blob: f436b08f839404afdee03aaedbe7f2514d5f3c2b [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Bernhard Reutner-Fischerd9cf7ac2006-04-12 18:39:58 +00002/*
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 */
Denys Vlasenkofb4da162016-11-22 23:14:24 +01009//config:config MAKEDEVS
10//config: bool "makedevs"
11//config: default y
12//config: help
13//config: 'makedevs' is a utility used to create a batch of devices with
14//config: one command.
15//config:
16//config: There are two choices for command line behaviour, the interface
17//config: as used by LEAF/Linux Router Project, or a device table file.
18//config:
19//config: 'leaf' is traditionally what busybox follows, it allows multiple
20//config: devices of a particluar type to be created per command.
21//config: e.g. /dev/hda[0-9]
22//config: Device properties are passed as command line arguments.
23//config:
24//config: 'table' reads device properties from a file or stdin, allowing
25//config: a batch of unrelated devices to be made with one command.
26//config: User/group names are allowed as an alternative to uid/gid.
27//config:
28//config:choice
29//config: prompt "Choose makedevs behaviour"
30//config: depends on MAKEDEVS
31//config: default FEATURE_MAKEDEVS_TABLE
32//config:
33//config:config FEATURE_MAKEDEVS_LEAF
34//config: bool "leaf"
35//config:
36//config:config FEATURE_MAKEDEVS_TABLE
37//config: bool "table"
38//config:
39//config:endchoice
Eric Andersen3d925622005-06-09 10:16:02 +000040
Denys Vlasenkof88e3bf2016-11-22 23:54:17 +010041//applet:IF_MAKEDEVS(APPLET(makedevs, BB_DIR_SBIN, BB_SUID_DROP))
42
43//kbuild:lib-$(CONFIG_MAKEDEVS) += makedevs.o
44
Pere Orga5bc8c002011-04-11 03:29:49 +020045//usage:#if ENABLE_FEATURE_MAKEDEVS_LEAF
46//usage:#define makedevs_trivial_usage
47//usage: "NAME TYPE MAJOR MINOR FIRST LAST [s]"
48//usage:#define makedevs_full_usage "\n\n"
49//usage: "Create a range of block or character special files"
50//usage: "\n"
51//usage: "\nTYPE is:"
52//usage: "\n b Block device"
53//usage: "\n c Character device"
54//usage: "\n f FIFO, MAJOR and MINOR are ignored"
55//usage: "\n"
56//usage: "\nFIRST..LAST specify numbers appended to NAME."
57//usage: "\nIf 's' is the last argument, the base device is created as well."
58//usage: "\n"
59//usage: "\nExamples:"
60//usage: "\n makedevs /dev/ttyS c 4 66 2 63 -> ttyS2-ttyS63"
61//usage: "\n makedevs /dev/hda b 3 0 0 8 s -> hda,hda1-hda8"
62//usage:
63//usage:#define makedevs_example_usage
64//usage: "# makedevs /dev/ttyS c 4 66 2 63\n"
65//usage: "[creates ttyS2-ttyS63]\n"
66//usage: "# makedevs /dev/hda b 3 0 0 8 s\n"
67//usage: "[creates hda,hda1-hda8]\n"
68//usage:#endif
69//usage:
70//usage:#if ENABLE_FEATURE_MAKEDEVS_TABLE
71//usage:#define makedevs_trivial_usage
72//usage: "[-d device_table] rootdir"
73//usage:#define makedevs_full_usage "\n\n"
74//usage: "Create a range of special files as specified in a device table.\n"
75//usage: "Device table entries take the form of:\n"
Bernhard Reutner-Fischer14f55532011-05-30 23:00:47 +020076//usage: "<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n"
Pere Orga5bc8c002011-04-11 03:29:49 +020077//usage: "Where name is the file name, type can be one of:\n"
78//usage: " f Regular file\n"
79//usage: " d Directory\n"
80//usage: " c Character device\n"
81//usage: " b Block device\n"
82//usage: " p Fifo (named pipe)\n"
83//usage: "uid is the user id for the target file, gid is the group id for the\n"
84//usage: "target file. The rest of the entries (major, minor, etc) apply to\n"
85//usage: "to device special files. A '-' may be used for blank entries."
86//usage:
87//usage:#define makedevs_example_usage
88//usage: "For example:\n"
89//usage: "<name> <type> <mode><uid><gid><major><minor><start><inc><count>\n"
90//usage: "/dev d 755 0 0 - - - - -\n"
91//usage: "/dev/console c 666 0 0 5 1 - - -\n"
92//usage: "/dev/null c 666 0 0 1 3 0 0 -\n"
93//usage: "/dev/zero c 666 0 0 1 5 0 0 -\n"
94//usage: "/dev/hda b 640 0 0 3 0 0 0 -\n"
95//usage: "/dev/hda b 640 0 0 3 1 1 1 15\n\n"
96//usage: "Will Produce:\n"
97//usage: "/dev\n"
98//usage: "/dev/console\n"
99//usage: "/dev/null\n"
100//usage: "/dev/zero\n"
101//usage: "/dev/hda\n"
102//usage: "/dev/hda[0-15]\n"
103//usage:#endif
104
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000105#include "libbb.h"
Eric Andersen3d925622005-06-09 10:16:02 +0000106
Denis Vlasenkoe3241842007-08-13 10:36:25 +0000107#if ENABLE_FEATURE_MAKEDEVS_LEAF
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000108/*
109makedevs NAME TYPE MAJOR MINOR FIRST LAST [s]
110TYPEs:
111b Block device
112c Character device
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000113f FIFO
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000114
115FIRST..LAST specify numbers appended to NAME.
116If 's' is the last argument, the base device is created as well.
117Examples:
118 makedevs /dev/ttyS c 4 66 2 63 -> ttyS2-ttyS63
119 makedevs /dev/hda b 3 0 0 8 s -> hda,hda1-hda8
120*/
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000121int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000122int makedevs_main(int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000123{
Eric Andersen36006022002-06-08 12:44:17 +0000124 mode_t mode;
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000125 char *basedev, *type, *nodname, *buf;
Eric Andersen4f807a82004-07-26 09:11:12 +0000126 int Smajor, Sminor, S, E;
Eric Andersencc8ed391999-10-05 16:24:54 +0000127
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000128 if (argc < 7 || argv[1][0] == '-')
Manuel Novoa III cad53642003-03-19 09:13:01 +0000129 bb_show_usage();
Erik Andersen5e1189e2000-04-15 16:34:54 +0000130
Eric Andersen19732c62002-04-13 14:26:44 +0000131 basedev = argv[1];
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000132 buf = xasprintf("%s%u", argv[1], (unsigned)-1);
Eric Andersen19732c62002-04-13 14:26:44 +0000133 type = argv[2];
Denys Vlasenko77832482010-08-12 14:14:45 +0200134 Smajor = xatoi_positive(argv[3]);
135 Sminor = xatoi_positive(argv[4]);
136 S = xatoi_positive(argv[5]);
137 E = xatoi_positive(argv[6]);
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000138 nodname = argv[7] ? basedev : buf;
Eric Andersen36006022002-06-08 12:44:17 +0000139
140 mode = 0660;
Eric Andersencc8ed391999-10-05 16:24:54 +0000141 switch (type[0]) {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000142 case 'c':
Eric Andersen36006022002-06-08 12:44:17 +0000143 mode |= S_IFCHR;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000144 break;
145 case 'b':
Eric Andersen36006022002-06-08 12:44:17 +0000146 mode |= S_IFBLK;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000147 break;
148 case 'f':
Eric Andersen36006022002-06-08 12:44:17 +0000149 mode |= S_IFIFO;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000150 break;
151 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000152 bb_show_usage();
Erik Andersene49d5ec2000-02-08 19:58:47 +0000153 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000154
Erik Andersene49d5ec2000-02-08 19:58:47 +0000155 while (S <= E) {
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000156 sprintf(buf, "%s%u", basedev, S);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000157
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000158 /* if mode != S_IFCHR and != S_IFBLK,
159 * third param in mknod() ignored */
Denys Vlasenko2c769c62016-11-27 23:27:54 +0100160 if (mknod(nodname, mode, makedev(Smajor, Sminor)) != 0
161 && errno != EEXIST
162 ) {
Denys Vlasenko651a2692010-03-23 16:25:17 +0100163 bb_perror_msg("can't create '%s'", nodname);
Denys Vlasenko2c769c62016-11-27 23:27:54 +0100164 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000165
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000166 /*if (nodname == basedev)*/ /* ex. /dev/hda - to /dev/hda1 ... */
Eric Andersen36006022002-06-08 12:44:17 +0000167 nodname = buf;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000168 S++;
169 Sminor++;
Eric Andersencc8ed391999-10-05 16:24:54 +0000170 }
171
Erik Andersene49d5ec2000-02-08 19:58:47 +0000172 return 0;
Eric Andersencc8ed391999-10-05 16:24:54 +0000173}
174
Denis Vlasenkoe3241842007-08-13 10:36:25 +0000175#elif ENABLE_FEATURE_MAKEDEVS_TABLE
Eric Andersen3d925622005-06-09 10:16:02 +0000176
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +0200177/* Licensed under GPLv2 or later, see file LICENSE in this source tree. */
Eric Andersencc8ed391999-10-05 16:24:54 +0000178
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000179int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000180int makedevs_main(int argc UNUSED_PARAM, char **argv)
Eric Andersen3d925622005-06-09 10:16:02 +0000181{
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000182 parser_t *parser;
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000183 char *line = (char *)"-";
Eric Andersen3d925622005-06-09 10:16:02 +0000184 int ret = EXIT_SUCCESS;
Eric Andersencc8ed391999-10-05 16:24:54 +0000185
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000186 opt_complementary = "=1"; /* exactly one param */
Denis Vlasenkofe7cd642007-08-18 15:32:12 +0000187 getopt32(argv, "d:", &line);
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000188 argv += optind;
Eric Andersene8614db2005-07-18 09:28:36 +0000189
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000190 xchdir(*argv); /* ensure root dir exists */
Eric Andersen3d925622005-06-09 10:16:02 +0000191
192 umask(0);
193
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000194 printf("rootdir=%s\ntable=", *argv);
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000195 if (NOT_LONE_DASH(line)) {
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000196 printf("'%s'\n", line);
Eric Andersene8614db2005-07-18 09:28:36 +0000197 } else {
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000198 puts("<stdin>");
Eric Andersene8614db2005-07-18 09:28:36 +0000199 }
200
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000201 parser = config_open(line);
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000202 while (config_read(parser, &line, 1, 1, "# \t", PARSE_NORMAL)) {
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000203 int linenum;
Eric Andersen3d925622005-06-09 10:16:02 +0000204 char type;
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000205 unsigned mode = 0755;
206 unsigned major = 0;
207 unsigned minor = 0;
208 unsigned count = 0;
209 unsigned increment = 0;
210 unsigned start = 0;
Eric Andersen3d925622005-06-09 10:16:02 +0000211 char user[41];
212 char group[41];
Denys Vlasenko3532e602017-07-06 02:04:32 +0200213 char *full_name;
214 int name_len;
Eric Andersen3d925622005-06-09 10:16:02 +0000215 uid_t uid;
216 gid_t gid;
217
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000218 linenum = parser->lineno;
Eric Andersene8614db2005-07-18 09:28:36 +0000219
Denys Vlasenko3532e602017-07-06 02:04:32 +0200220 if ((1 > sscanf(line, "%*s%n %c %o %40s %40s %u %u %u %u %u",
221 &name_len, &type, &mode, user, group,
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +0200222 &major, &minor, &start, &increment, &count))
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000223 || ((unsigned)(major | minor | start | count | increment) > 255)
224 ) {
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000225 bb_error_msg("invalid line %d: '%s'", linenum, line);
Eric Andersen3d925622005-06-09 10:16:02 +0000226 ret = EXIT_FAILURE;
227 continue;
228 }
Bernhard Reutner-Fischer17329742005-12-21 15:07:30 +0000229
Denis Vlasenko9a44c4f2006-12-28 05:44:47 +0000230 gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid();
231 uid = (*user) ? get_ug_id(user, xuname2uid) : getuid();
Denys Vlasenko3532e602017-07-06 02:04:32 +0200232 line[name_len] = '\0';
233 full_name = line;
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000234 /* We are already in the right root dir,
235 * so make absolute paths relative */
Denys Vlasenko3532e602017-07-06 02:04:32 +0200236 if ('/' == full_name[0])
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000237 full_name++;
Eric Andersen3d925622005-06-09 10:16:02 +0000238
239 if (type == 'd') {
Eric Andersene8614db2005-07-18 09:28:36 +0000240 bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR);
Eric Andersen3d925622005-06-09 10:16:02 +0000241 if (chown(full_name, uid, gid) == -1) {
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000242 chown_fail:
243 bb_perror_msg("line %d: can't chown %s", linenum, full_name);
Eric Andersen3d925622005-06-09 10:16:02 +0000244 ret = EXIT_FAILURE;
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000245 continue;
Eric Andersen3d925622005-06-09 10:16:02 +0000246 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +0000247 if (chmod(full_name, mode) < 0) {
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000248 chmod_fail:
249 bb_perror_msg("line %d: can't chmod %s", linenum, full_name);
Eric Andersen90161c92005-07-18 22:40:59 +0000250 ret = EXIT_FAILURE;
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000251 continue;
Eric Andersen90161c92005-07-18 22:40:59 +0000252 }
253 } else if (type == 'f') {
254 struct stat st;
255 if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) {
256 bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name);
257 ret = EXIT_FAILURE;
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000258 continue;
Eric Andersen90161c92005-07-18 22:40:59 +0000259 }
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000260 if (chown(full_name, uid, gid) < 0)
261 goto chown_fail;
262 if (chmod(full_name, mode) < 0)
263 goto chmod_fail;
Denis Vlasenkoe3241842007-08-13 10:36:25 +0000264 } else {
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000265 unsigned i;
Eric Andersen3d925622005-06-09 10:16:02 +0000266
267 if (type == 'p') {
268 mode |= S_IFIFO;
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000269 } else if (type == 'c') {
Eric Andersen3d925622005-06-09 10:16:02 +0000270 mode |= S_IFCHR;
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000271 } else if (type == 'b') {
Eric Andersen3d925622005-06-09 10:16:02 +0000272 mode |= S_IFBLK;
273 } else {
Eric Andersen2ada89b2005-07-18 09:45:35 +0000274 bb_error_msg("line %d: unsupported file type %c", linenum, type);
Eric Andersen3d925622005-06-09 10:16:02 +0000275 ret = EXIT_FAILURE;
Denis Vlasenkob2dc9132008-08-03 22:14:02 +0000276 continue;
Eric Andersen3d925622005-06-09 10:16:02 +0000277 }
278
Denys Vlasenko3c9688e2017-07-06 02:17:24 +0200279 if (count != 0)
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000280 count--;
Denys Vlasenko3c9688e2017-07-06 02:17:24 +0200281 for (i = 0; i <= count; i++) {
282 dev_t rdev;
283 char *nameN = full_name;
284 if (count != 0)
285 nameN = xasprintf("%s%u", full_name, start + i);
286 rdev = makedev(major, minor + i * increment);
287 if (mknod(nameN, mode, rdev) != 0
Denys Vlasenko1d3a04a2016-11-28 01:22:57 +0100288 && errno != EEXIST
Denys Vlasenko2c769c62016-11-27 23:27:54 +0100289 ) {
Denys Vlasenko3c9688e2017-07-06 02:17:24 +0200290 bb_perror_msg("line %d: can't create node %s", linenum, nameN);
Eric Andersen3d925622005-06-09 10:16:02 +0000291 ret = EXIT_FAILURE;
Denys Vlasenko3c9688e2017-07-06 02:17:24 +0200292 } else if (chown(nameN, uid, gid) < 0) {
293 bb_perror_msg("line %d: can't chown %s", linenum, nameN);
Eric Andersen3d925622005-06-09 10:16:02 +0000294 ret = EXIT_FAILURE;
Denys Vlasenko3c9688e2017-07-06 02:17:24 +0200295 } else if (chmod(nameN, mode) < 0) {
296 bb_perror_msg("line %d: can't chmod %s", linenum, nameN);
Eric Andersen90161c92005-07-18 22:40:59 +0000297 ret = EXIT_FAILURE;
298 }
Denys Vlasenko3c9688e2017-07-06 02:17:24 +0200299 if (count != 0)
300 free(nameN);
Eric Andersen3d925622005-06-09 10:16:02 +0000301 }
302 }
Eric Andersen3d925622005-06-09 10:16:02 +0000303 }
Denis Vlasenkoe1fa8172008-08-01 02:15:05 +0000304 if (ENABLE_FEATURE_CLEAN_UP)
305 config_close(parser);
Eric Andersen3d925622005-06-09 10:16:02 +0000306
Bernhard Reutner-Fischer56fbd212006-04-24 17:41:29 +0000307 return ret;
Eric Andersencc8ed391999-10-05 16:24:54 +0000308}
Eric Andersene8614db2005-07-18 09:28:36 +0000309
Eric Andersen3d925622005-06-09 10:16:02 +0000310#else
Bernhard Reutner-Fischer771b1862006-03-24 14:30:05 +0000311# error makedevs configuration error, either leaf or table must be selected
Eric Andersen3d925622005-06-09 10:16:02 +0000312#endif