makedevs.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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"
  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(makedevs, BB_DIR_SBIN, BB_SUID_DROP))
  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. int ret = EXIT_SUCCESS;
  169. opt_complementary = "=1"; /* exactly one param */
  170. getopt32(argv, "d:", &line);
  171. argv += optind;
  172. xchdir(*argv); /* ensure root dir exists */
  173. umask(0);
  174. printf("rootdir=%s\ntable=", *argv);
  175. if (NOT_LONE_DASH(line)) {
  176. printf("'%s'\n", line);
  177. } else {
  178. puts("<stdin>");
  179. }
  180. parser = config_open(line);
  181. while (config_read(parser, &line, 1, 1, "# \t", PARSE_NORMAL)) {
  182. int linenum;
  183. char type;
  184. unsigned mode = 0755;
  185. unsigned major = 0;
  186. unsigned minor = 0;
  187. unsigned count = 0;
  188. unsigned increment = 0;
  189. unsigned start = 0;
  190. char name[41];
  191. char user[41];
  192. char group[41];
  193. char *full_name = name;
  194. uid_t uid;
  195. gid_t gid;
  196. linenum = parser->lineno;
  197. if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u",
  198. name, &type, &mode, user, group,
  199. &major, &minor, &start, &increment, &count))
  200. || ((unsigned)(major | minor | start | count | increment) > 255)
  201. ) {
  202. bb_error_msg("invalid line %d: '%s'", linenum, line);
  203. ret = EXIT_FAILURE;
  204. continue;
  205. }
  206. gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid();
  207. uid = (*user) ? get_ug_id(user, xuname2uid) : getuid();
  208. /* We are already in the right root dir,
  209. * so make absolute paths relative */
  210. if ('/' == *full_name)
  211. full_name++;
  212. if (type == 'd') {
  213. bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR);
  214. if (chown(full_name, uid, gid) == -1) {
  215. chown_fail:
  216. bb_perror_msg("line %d: can't chown %s", linenum, full_name);
  217. ret = EXIT_FAILURE;
  218. continue;
  219. }
  220. if (chmod(full_name, mode) < 0) {
  221. chmod_fail:
  222. bb_perror_msg("line %d: can't chmod %s", linenum, full_name);
  223. ret = EXIT_FAILURE;
  224. continue;
  225. }
  226. } else if (type == 'f') {
  227. struct stat st;
  228. if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) {
  229. bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name);
  230. ret = EXIT_FAILURE;
  231. continue;
  232. }
  233. if (chown(full_name, uid, gid) < 0)
  234. goto chown_fail;
  235. if (chmod(full_name, mode) < 0)
  236. goto chmod_fail;
  237. } else {
  238. dev_t rdev;
  239. unsigned i;
  240. char *full_name_inc;
  241. if (type == 'p') {
  242. mode |= S_IFIFO;
  243. } else if (type == 'c') {
  244. mode |= S_IFCHR;
  245. } else if (type == 'b') {
  246. mode |= S_IFBLK;
  247. } else {
  248. bb_error_msg("line %d: unsupported file type %c", linenum, type);
  249. ret = EXIT_FAILURE;
  250. continue;
  251. }
  252. full_name_inc = xmalloc(strlen(full_name) + sizeof(int)*3 + 2);
  253. if (count)
  254. count--;
  255. for (i = start; i <= start + count; i++) {
  256. sprintf(full_name_inc, count ? "%s%u" : "%s", full_name, i);
  257. rdev = makedev(major, minor + (i - start) * increment);
  258. if (mknod(full_name_inc, mode, rdev) != 0
  259. && errno != EEXIST
  260. ) {
  261. bb_perror_msg("line %d: can't create node %s", linenum, full_name_inc);
  262. ret = EXIT_FAILURE;
  263. } else if (chown(full_name_inc, uid, gid) < 0) {
  264. bb_perror_msg("line %d: can't chown %s", linenum, full_name_inc);
  265. ret = EXIT_FAILURE;
  266. } else if (chmod(full_name_inc, mode) < 0) {
  267. bb_perror_msg("line %d: can't chmod %s", linenum, full_name_inc);
  268. ret = EXIT_FAILURE;
  269. }
  270. }
  271. free(full_name_inc);
  272. }
  273. }
  274. if (ENABLE_FEATURE_CLEAN_UP)
  275. config_close(parser);
  276. return ret;
  277. }
  278. #else
  279. # error makedevs configuration error, either leaf or table must be selected
  280. #endif