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