1/* vi: set sw=4 ts=4: */
2/*
3 * public domain -- Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
4 *
5 * makedevs
6 * Make ranges of device files quickly.
7 * known bugs: can't deal with alpha ranges
8 */
9
10#include "libbb.h"
11
12#if ENABLE_FEATURE_MAKEDEVS_LEAF
13/*
14makedevs NAME TYPE MAJOR MINOR FIRST LAST [s]
15TYPEs:
16b       Block device
17c       Character device
18f       FIFO
19
20FIRST..LAST specify numbers appended to NAME.
21If 's' is the last argument, the base device is created as well.
22Examples:
23        makedevs /dev/ttyS c 4 66 2 63   ->  ttyS2-ttyS63
24        makedevs /dev/hda b 3 0 0 8 s    ->  hda,hda1-hda8
25*/
26int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
27int makedevs_main(int argc, char **argv)
28{
29	mode_t mode;
30	char *basedev, *type, *nodname, *buf;
31	int Smajor, Sminor, S, E;
32
33	if (argc < 7 || argv[1][0] == '-')
34		bb_show_usage();
35
36	basedev = argv[1];
37	buf = xasprintf("%s%u", argv[1], (unsigned)-1);
38	type = argv[2];
39	Smajor = xatoi_u(argv[3]);
40	Sminor = xatoi_u(argv[4]);
41	S = xatoi_u(argv[5]);
42	E = xatoi_u(argv[6]);
43	nodname = argv[7] ? basedev : buf;
44
45	mode = 0660;
46	switch (type[0]) {
47	case 'c':
48		mode |= S_IFCHR;
49		break;
50	case 'b':
51		mode |= S_IFBLK;
52		break;
53	case 'f':
54		mode |= S_IFIFO;
55		break;
56	default:
57		bb_show_usage();
58	}
59
60	while (S <= E) {
61		sprintf(buf, "%s%u", basedev, S);
62
63		/* if mode != S_IFCHR and != S_IFBLK,
64		 * third param in mknod() ignored */
65		if (mknod(nodname, mode, makedev(Smajor, Sminor)))
66			bb_perror_msg("can't create '%s'", nodname);
67
68		/*if (nodname == basedev)*/ /* ex. /dev/hda - to /dev/hda1 ... */
69			nodname = buf;
70		S++;
71		Sminor++;
72	}
73
74	return 0;
75}
76
77#elif ENABLE_FEATURE_MAKEDEVS_TABLE
78
79/* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. */
80
81int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
82int makedevs_main(int argc UNUSED_PARAM, char **argv)
83{
84	parser_t *parser;
85	char *line = (char *)"-";
86	int ret = EXIT_SUCCESS;
87
88	opt_complementary = "=1"; /* exactly one param */
89	getopt32(argv, "d:", &line);
90	argv += optind;
91
92	xchdir(*argv); /* ensure root dir exists */
93
94	umask(0);
95
96	printf("rootdir=%s\ntable=", *argv);
97	if (NOT_LONE_DASH(line)) {
98		printf("'%s'\n", line);
99	} else {
100		puts("<stdin>");
101	}
102
103	parser = config_open(line);
104	while (config_read(parser, &line, 1, 1, "# \t", PARSE_NORMAL)) {
105		int linenum;
106		char type;
107		unsigned mode = 0755;
108		unsigned major = 0;
109		unsigned minor = 0;
110		unsigned count = 0;
111		unsigned increment = 0;
112		unsigned start = 0;
113		char name[41];
114		char user[41];
115		char group[41];
116		char *full_name = name;
117		uid_t uid;
118		gid_t gid;
119
120		linenum = parser->lineno;
121
122		if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u",
123					name, &type, &mode, user, group,
124					&major,	&minor, &start, &increment, &count))
125		 || ((unsigned)(major | minor | start | count | increment) > 255)
126		) {
127			bb_error_msg("invalid line %d: '%s'", linenum, line);
128			ret = EXIT_FAILURE;
129			continue;
130		}
131
132		gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid();
133		uid = (*user) ? get_ug_id(user, xuname2uid) : getuid();
134		/* We are already in the right root dir,
135		 * so make absolute paths relative */
136		if ('/' == *full_name)
137			full_name++;
138
139		if (type == 'd') {
140			bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR);
141			if (chown(full_name, uid, gid) == -1) {
142 chown_fail:
143				bb_perror_msg("line %d: can't chown %s", linenum, full_name);
144				ret = EXIT_FAILURE;
145				continue;
146			}
147			if (chmod(full_name, mode) < 0) {
148 chmod_fail:
149				bb_perror_msg("line %d: can't chmod %s", linenum, full_name);
150				ret = EXIT_FAILURE;
151				continue;
152			}
153		} else if (type == 'f') {
154			struct stat st;
155			if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) {
156				bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name);
157				ret = EXIT_FAILURE;
158				continue;
159			}
160			if (chown(full_name, uid, gid) < 0)
161				goto chown_fail;
162			if (chmod(full_name, mode) < 0)
163				goto chmod_fail;
164		} else {
165			dev_t rdev;
166			unsigned i;
167			char *full_name_inc;
168
169			if (type == 'p') {
170				mode |= S_IFIFO;
171			} else if (type == 'c') {
172				mode |= S_IFCHR;
173			} else if (type == 'b') {
174				mode |= S_IFBLK;
175			} else {
176				bb_error_msg("line %d: unsupported file type %c", linenum, type);
177				ret = EXIT_FAILURE;
178				continue;
179			}
180
181			full_name_inc = xmalloc(strlen(full_name) + sizeof(int)*3 + 2);
182			if (count)
183				count--;
184			for (i = start; i <= start + count; i++) {
185				sprintf(full_name_inc, count ? "%s%u" : "%s", full_name, i);
186				rdev = makedev(major, minor + (i - start) * increment);
187				if (mknod(full_name_inc, mode, rdev) < 0) {
188					bb_perror_msg("line %d: can't create node %s", linenum, full_name_inc);
189					ret = EXIT_FAILURE;
190				} else if (chown(full_name_inc, uid, gid) < 0) {
191					bb_perror_msg("line %d: can't chown %s", linenum, full_name_inc);
192					ret = EXIT_FAILURE;
193				} else if (chmod(full_name_inc, mode) < 0) {
194					bb_perror_msg("line %d: can't chmod %s", linenum, full_name_inc);
195					ret = EXIT_FAILURE;
196				}
197			}
198			free(full_name_inc);
199		}
200	}
201	if (ENABLE_FEATURE_CLEAN_UP)
202		config_close(parser);
203
204	return ret;
205}
206
207#else
208# error makedevs configuration error, either leaf or table must be selected
209#endif
210