makedevs.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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. //config:config MAKEDEVS
  10. //config: bool "makedevs (9.4 kb)"
  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
  40. //applet:IF_MAKEDEVS(APPLET_NOEXEC(makedevs, makedevs, BB_DIR_SBIN, BB_SUID_DROP, makedevs))
  41. //kbuild:lib-$(CONFIG_MAKEDEVS) += makedevs.o
  42. //usage:#if ENABLE_FEATURE_MAKEDEVS_LEAF
  43. //usage:#define makedevs_trivial_usage
  44. //usage: "NAME TYPE MAJOR MINOR FIRST LAST [s]"
  45. //usage:#define makedevs_full_usage "\n\n"
  46. //usage: "Create a range of block or character special files"
  47. //usage: "\n"
  48. //usage: "\nTYPE is:"
  49. //usage: "\n b Block device"
  50. //usage: "\n c Character device"
  51. //usage: "\n f FIFO, MAJOR and MINOR are ignored"
  52. //usage: "\n"
  53. //usage: "\nFIRST..LAST specify numbers appended to NAME."
  54. //usage: "\nIf 's' is the last argument, the base device is created as well."
  55. //usage: "\n"
  56. //usage: "\nExamples:"
  57. //usage: "\n makedevs /dev/ttyS c 4 66 2 63 -> ttyS2-ttyS63"
  58. //usage: "\n makedevs /dev/hda b 3 0 0 8 s -> hda,hda1-hda8"
  59. //usage:
  60. //usage:#define makedevs_example_usage
  61. //usage: "# makedevs /dev/ttyS c 4 66 2 63\n"
  62. //usage: "[creates ttyS2-ttyS63]\n"
  63. //usage: "# makedevs /dev/hda b 3 0 0 8 s\n"
  64. //usage: "[creates hda,hda1-hda8]\n"
  65. //usage:#endif
  66. //usage:
  67. //usage:#if ENABLE_FEATURE_MAKEDEVS_TABLE
  68. //usage:#define makedevs_trivial_usage
  69. //usage: "[-d device_table] rootdir"
  70. //usage:#define makedevs_full_usage "\n\n"
  71. //usage: "Create a range of special files as specified in a device table.\n"
  72. //usage: "Device table entries take the form of:\n"
  73. //usage: "<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n"
  74. //usage: "Where name is the file name, type can be one of:\n"
  75. //usage: " f Regular file\n"
  76. //usage: " d Directory\n"
  77. //usage: " c Character device\n"
  78. //usage: " b Block device\n"
  79. //usage: " p Fifo (named pipe)\n"
  80. //usage: "uid is the user id for the target file, gid is the group id for the\n"
  81. //usage: "target file. The rest of the entries (major, minor, etc) apply to\n"
  82. //usage: "to device special files. A '-' may be used for blank entries."
  83. //usage:
  84. //usage:#define makedevs_example_usage
  85. //usage: "For example:\n"
  86. //usage: "<name> <type> <mode><uid><gid><major><minor><start><inc><count>\n"
  87. //usage: "/dev d 755 0 0 - - - - -\n"
  88. //usage: "/dev/console c 666 0 0 5 1 - - -\n"
  89. //usage: "/dev/null c 666 0 0 1 3 0 0 -\n"
  90. //usage: "/dev/zero c 666 0 0 1 5 0 0 -\n"
  91. //usage: "/dev/hda b 640 0 0 3 0 0 0 -\n"
  92. //usage: "/dev/hda b 640 0 0 3 1 1 1 15\n\n"
  93. //usage: "Will Produce:\n"
  94. //usage: "/dev\n"
  95. //usage: "/dev/console\n"
  96. //usage: "/dev/null\n"
  97. //usage: "/dev/zero\n"
  98. //usage: "/dev/hda\n"
  99. //usage: "/dev/hda[0-15]\n"
  100. //usage:#endif
  101. #include "libbb.h"
  102. #if ENABLE_FEATURE_MAKEDEVS_LEAF
  103. /*
  104. makedevs NAME TYPE MAJOR MINOR FIRST LAST [s]
  105. TYPEs:
  106. b Block device
  107. c Character device
  108. f FIFO
  109. FIRST..LAST specify numbers appended to NAME.
  110. If 's' is the last argument, the base device is created as well.
  111. Examples:
  112. makedevs /dev/ttyS c 4 66 2 63 -> ttyS2-ttyS63
  113. makedevs /dev/hda b 3 0 0 8 s -> hda,hda1-hda8
  114. */
  115. int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  116. int makedevs_main(int argc, char **argv)
  117. {
  118. mode_t mode;
  119. char *basedev, *type, *nodname, *buf;
  120. int Smajor, Sminor, S, E;
  121. if (argc < 7 || argv[1][0] == '-')
  122. bb_show_usage();
  123. basedev = argv[1];
  124. buf = xasprintf("%s%u", argv[1], (unsigned)-1);
  125. type = argv[2];
  126. Smajor = xatoi_positive(argv[3]);
  127. Sminor = xatoi_positive(argv[4]);
  128. S = xatoi_positive(argv[5]);
  129. E = xatoi_positive(argv[6]);
  130. nodname = argv[7] ? basedev : buf;
  131. mode = 0660;
  132. switch (type[0]) {
  133. case 'c':
  134. mode |= S_IFCHR;
  135. break;
  136. case 'b':
  137. mode |= S_IFBLK;
  138. break;
  139. case 'f':
  140. mode |= S_IFIFO;
  141. break;
  142. default:
  143. bb_show_usage();
  144. }
  145. while (S <= E) {
  146. sprintf(buf, "%s%u", basedev, S);
  147. /* if mode != S_IFCHR and != S_IFBLK,
  148. * third param in mknod() ignored */
  149. if (mknod(nodname, mode, makedev(Smajor, Sminor)) != 0
  150. && errno != EEXIST
  151. ) {
  152. bb_perror_msg("can't create '%s'", nodname);
  153. }
  154. /*if (nodname == basedev)*/ /* ex. /dev/hda - to /dev/hda1 ... */
  155. nodname = buf;
  156. S++;
  157. Sminor++;
  158. }
  159. return 0;
  160. }
  161. #elif ENABLE_FEATURE_MAKEDEVS_TABLE
  162. /* Licensed under GPLv2 or later, see file LICENSE in this source tree. */
  163. int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  164. int makedevs_main(int argc UNUSED_PARAM, char **argv)
  165. {
  166. parser_t *parser;
  167. char *line = (char *)"-";
  168. exitcode_t ret = EXIT_SUCCESS;
  169. getopt32(argv, "^" "d:" "\0" "=1", &line);
  170. argv += optind;
  171. xchdir(*argv); /* ensure root dir exists */
  172. umask(0);
  173. printf("rootdir=%s\ntable=", *argv);
  174. if (NOT_LONE_DASH(line)) {
  175. printf("'%s'\n", line);
  176. } else {
  177. puts("<stdin>");
  178. }
  179. parser = config_open(line);
  180. while (config_read(parser, &line, 1, 1, "# \t", PARSE_NORMAL)) {
  181. int linenum;
  182. char type;
  183. unsigned mode = 0755;
  184. unsigned major = 0;
  185. unsigned minor = 0;
  186. unsigned count = 0;
  187. unsigned increment = 0;
  188. unsigned start = 0;
  189. char user[41];
  190. char group[41];
  191. char *full_name;
  192. int name_len;
  193. uid_t uid;
  194. gid_t gid;
  195. linenum = parser->lineno;
  196. if ((1 > sscanf(line, "%*s%n %c %o %40s %40s %u %u %u %u %u",
  197. &name_len, &type, &mode, user, group,
  198. &major, &minor, &start, &increment, &count))
  199. || ((unsigned)(major | minor | start | count | increment) > 255)
  200. ) {
  201. bb_error_msg("invalid line %d: '%s'", linenum, line);
  202. ret = EXIT_FAILURE;
  203. continue;
  204. }
  205. gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid();
  206. uid = (*user) ? get_ug_id(user, xuname2uid) : getuid();
  207. line[name_len] = '\0';
  208. full_name = line;
  209. /* We are already in the right root dir,
  210. * so make absolute paths relative */
  211. if ('/' == full_name[0])
  212. full_name++;
  213. if (type == 'd') {
  214. bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR);
  215. if (chown(full_name, uid, gid) == -1) {
  216. chown_fail:
  217. bb_perror_msg("line %d: can't chown %s", linenum, full_name);
  218. ret = EXIT_FAILURE;
  219. continue;
  220. }
  221. if (chmod(full_name, mode) < 0) {
  222. chmod_fail:
  223. bb_perror_msg("line %d: can't chmod %s", linenum, full_name);
  224. ret = EXIT_FAILURE;
  225. continue;
  226. }
  227. } else if (type == 'f') {
  228. struct stat st;
  229. if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) {
  230. bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name);
  231. ret = EXIT_FAILURE;
  232. continue;
  233. }
  234. if (chown(full_name, uid, gid) < 0)
  235. goto chown_fail;
  236. if (chmod(full_name, mode) < 0)
  237. goto chmod_fail;
  238. } else {
  239. unsigned i;
  240. if (type == 'p') {
  241. mode |= S_IFIFO;
  242. } else if (type == 'c') {
  243. mode |= S_IFCHR;
  244. } else if (type == 'b') {
  245. mode |= S_IFBLK;
  246. } else {
  247. bb_error_msg("line %d: unsupported file type %c", linenum, type);
  248. ret = EXIT_FAILURE;
  249. continue;
  250. }
  251. if (count != 0)
  252. count--;
  253. for (i = 0; i <= count; i++) {
  254. dev_t rdev;
  255. char *nameN = full_name;
  256. if (count != 0)
  257. nameN = xasprintf("%s%u", full_name, start + i);
  258. rdev = makedev(major, minor + i * increment);
  259. if (mknod(nameN, mode, rdev) != 0
  260. && errno != EEXIST
  261. ) {
  262. bb_perror_msg("line %d: can't create node %s", linenum, nameN);
  263. ret = EXIT_FAILURE;
  264. } else if (chown(nameN, uid, gid) < 0) {
  265. bb_perror_msg("line %d: can't chown %s", linenum, nameN);
  266. ret = EXIT_FAILURE;
  267. } else if (chmod(nameN, mode) < 0) {
  268. bb_perror_msg("line %d: can't chmod %s", linenum, nameN);
  269. ret = EXIT_FAILURE;
  270. }
  271. if (count != 0)
  272. free(nameN);
  273. }
  274. }
  275. }
  276. if (ENABLE_FEATURE_CLEAN_UP)
  277. config_close(parser);
  278. return ret;
  279. }
  280. #else
  281. # error makedevs configuration error, either leaf or table must be selected
  282. #endif