busybox/miscutils/makedevs.c
<<
>>
Prefs
   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.2 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
  41//applet:IF_MAKEDEVS(APPLET_NOEXEC(makedevs, makedevs, BB_DIR_SBIN, BB_SUID_DROP, makedevs))
  42
  43//kbuild:lib-$(CONFIG_MAKEDEVS) += makedevs.o
  44
  45//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"
  76//usage:       "<name> <type> <mode> <uid> <gid> <major> <minor> <start> <inc> <count>\n"
  77//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
 105#include "libbb.h"
 106
 107#if ENABLE_FEATURE_MAKEDEVS_LEAF
 108/*
 109makedevs NAME TYPE MAJOR MINOR FIRST LAST [s]
 110TYPEs:
 111b       Block device
 112c       Character device
 113f       FIFO
 114
 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*/
 121int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 122int makedevs_main(int argc, char **argv)
 123{
 124        mode_t mode;
 125        char *basedev, *type, *nodname, *buf;
 126        int Smajor, Sminor, S, E;
 127
 128        if (argc < 7 || argv[1][0] == '-')
 129                bb_show_usage();
 130
 131        basedev = argv[1];
 132        buf = xasprintf("%s%u", argv[1], (unsigned)-1);
 133        type = argv[2];
 134        Smajor = xatoi_positive(argv[3]);
 135        Sminor = xatoi_positive(argv[4]);
 136        S = xatoi_positive(argv[5]);
 137        E = xatoi_positive(argv[6]);
 138        nodname = argv[7] ? basedev : buf;
 139
 140        mode = 0660;
 141        switch (type[0]) {
 142        case 'c':
 143                mode |= S_IFCHR;
 144                break;
 145        case 'b':
 146                mode |= S_IFBLK;
 147                break;
 148        case 'f':
 149                mode |= S_IFIFO;
 150                break;
 151        default:
 152                bb_show_usage();
 153        }
 154
 155        while (S <= E) {
 156                sprintf(buf, "%s%u", basedev, S);
 157
 158                /* if mode != S_IFCHR and != S_IFBLK,
 159                 * third param in mknod() ignored */
 160                if (mknod(nodname, mode, makedev(Smajor, Sminor)) != 0
 161                 && errno != EEXIST
 162                ) {
 163                        bb_perror_msg("can't create '%s'", nodname);
 164                }
 165
 166                /*if (nodname == basedev)*/ /* ex. /dev/hda - to /dev/hda1 ... */
 167                        nodname = buf;
 168                S++;
 169                Sminor++;
 170        }
 171
 172        return 0;
 173}
 174
 175#elif ENABLE_FEATURE_MAKEDEVS_TABLE
 176
 177/* Licensed under GPLv2 or later, see file LICENSE in this source tree. */
 178
 179int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 180int makedevs_main(int argc UNUSED_PARAM, char **argv)
 181{
 182        parser_t *parser;
 183        char *line = (char *)"-";
 184        int ret = EXIT_SUCCESS;
 185
 186        getopt32(argv, "^" "d:" "\0" "=1", &line);
 187        argv += optind;
 188
 189        xchdir(*argv); /* ensure root dir exists */
 190
 191        umask(0);
 192
 193        printf("rootdir=%s\ntable=", *argv);
 194        if (NOT_LONE_DASH(line)) {
 195                printf("'%s'\n", line);
 196        } else {
 197                puts("<stdin>");
 198        }
 199
 200        parser = config_open(line);
 201        while (config_read(parser, &line, 1, 1, "# \t", PARSE_NORMAL)) {
 202                int linenum;
 203                char type;
 204                unsigned mode = 0755;
 205                unsigned major = 0;
 206                unsigned minor = 0;
 207                unsigned count = 0;
 208                unsigned increment = 0;
 209                unsigned start = 0;
 210                char user[41];
 211                char group[41];
 212                char *full_name;
 213                int name_len;
 214                uid_t uid;
 215                gid_t gid;
 216
 217                linenum = parser->lineno;
 218
 219                if ((1 > sscanf(line, "%*s%n %c %o %40s %40s %u %u %u %u %u",
 220                                        &name_len, &type, &mode, user, group,
 221                                        &major, &minor, &start, &increment, &count))
 222                 || ((unsigned)(major | minor | start | count | increment) > 255)
 223                ) {
 224                        bb_error_msg("invalid line %d: '%s'", linenum, line);
 225                        ret = EXIT_FAILURE;
 226                        continue;
 227                }
 228
 229                gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid();
 230                uid = (*user) ? get_ug_id(user, xuname2uid) : getuid();
 231                line[name_len] = '\0';
 232                full_name = line;
 233                /* We are already in the right root dir,
 234                 * so make absolute paths relative */
 235                if ('/' == full_name[0])
 236                        full_name++;
 237
 238                if (type == 'd') {
 239                        bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR);
 240                        if (chown(full_name, uid, gid) == -1) {
 241 chown_fail:
 242                                bb_perror_msg("line %d: can't chown %s", linenum, full_name);
 243                                ret = EXIT_FAILURE;
 244                                continue;
 245                        }
 246                        if (chmod(full_name, mode) < 0) {
 247 chmod_fail:
 248                                bb_perror_msg("line %d: can't chmod %s", linenum, full_name);
 249                                ret = EXIT_FAILURE;
 250                                continue;
 251                        }
 252                } else if (type == 'f') {
 253                        struct stat st;
 254                        if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) {
 255                                bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name);
 256                                ret = EXIT_FAILURE;
 257                                continue;
 258                        }
 259                        if (chown(full_name, uid, gid) < 0)
 260                                goto chown_fail;
 261                        if (chmod(full_name, mode) < 0)
 262                                goto chmod_fail;
 263                } else {
 264                        unsigned i;
 265
 266                        if (type == 'p') {
 267                                mode |= S_IFIFO;
 268                        } else if (type == 'c') {
 269                                mode |= S_IFCHR;
 270                        } else if (type == 'b') {
 271                                mode |= S_IFBLK;
 272                        } else {
 273                                bb_error_msg("line %d: unsupported file type %c", linenum, type);
 274                                ret = EXIT_FAILURE;
 275                                continue;
 276                        }
 277
 278                        if (count != 0)
 279                                count--;
 280                        for (i = 0; i <= count; i++) {
 281                                dev_t rdev;
 282                                char *nameN = full_name;
 283                                if (count != 0)
 284                                        nameN = xasprintf("%s%u", full_name, start + i);
 285                                rdev = makedev(major, minor + i * increment);
 286                                if (mknod(nameN, mode, rdev) != 0
 287                                 && errno != EEXIST
 288                                ) {
 289                                        bb_perror_msg("line %d: can't create node %s", linenum, nameN);
 290                                        ret = EXIT_FAILURE;
 291                                } else if (chown(nameN, uid, gid) < 0) {
 292                                        bb_perror_msg("line %d: can't chown %s", linenum, nameN);
 293                                        ret = EXIT_FAILURE;
 294                                } else if (chmod(nameN, mode) < 0) {
 295                                        bb_perror_msg("line %d: can't chmod %s", linenum, nameN);
 296                                        ret = EXIT_FAILURE;
 297                                }
 298                                if (count != 0)
 299                                        free(nameN);
 300                        }
 301                }
 302        }
 303        if (ENABLE_FEATURE_CLEAN_UP)
 304                config_close(parser);
 305
 306        return ret;
 307}
 308
 309#else
 310# error makedevs configuration error, either leaf or table must be selected
 311#endif
 312