busybox/miscutils/devfsd.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   4 */
   5
   6/*
   7        devfsd implementation for busybox
   8
   9        Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
  10
  11        Busybox version is based on some previous work and ideas
  12        Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
  13
  14        devfsd.c
  15
  16        Main file for  devfsd  (devfs daemon for Linux).
  17
  18    Copyright (C) 1998-2002  Richard Gooch
  19
  20        devfsd.h
  21
  22    Header file for  devfsd  (devfs daemon for Linux).
  23
  24    Copyright (C) 1998-2000  Richard Gooch
  25
  26        compat_name.c
  27
  28    Compatibility name file for  devfsd  (build compatibility names).
  29
  30    Copyright (C) 1998-2002  Richard Gooch
  31
  32        expression.c
  33
  34    This code provides Borne Shell-like expression expansion.
  35
  36    Copyright (C) 1997-1999  Richard Gooch
  37
  38        This program is free software; you can redistribute it and/or modify
  39    it under the terms of the GNU General Public License as published by
  40    the Free Software Foundation; either version 2 of the License, or
  41    (at your option) any later version.
  42
  43    This program is distributed in the hope that it will be useful,
  44    but WITHOUT ANY WARRANTY; without even the implied warranty of
  45    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  46    GNU General Public License for more details.
  47
  48    You should have received a copy of the GNU General Public License
  49    along with this program; if not, write to the Free Software
  50    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  51
  52    Richard Gooch may be reached by email at  rgooch@atnf.csiro.au
  53    The postal address is:
  54      Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
  55*/
  56
  57//usage:#define devfsd_trivial_usage
  58//usage:       "mntpnt [-v]" IF_DEVFSD_FG_NP("[-fg][-np]")
  59//usage:#define devfsd_full_usage "\n\n"
  60//usage:       "Manage devfs permissions and old device name symlinks\n"
  61//usage:     "\n        mntpnt  The mount point where devfs is mounted"
  62//usage:     "\n        -v      Print the protocol version numbers for devfsd"
  63//usage:     "\n                and the kernel-side protocol version and exit"
  64//usage:        IF_DEVFSD_FG_NP(
  65//usage:     "\n        -fg     Run in foreground"
  66//usage:     "\n        -np     Exit after parsing the configuration file"
  67//usage:     "\n                and processing synthetic REGISTER events,"
  68//usage:     "\n                don't poll for events"
  69//usage:        )
  70
  71#include "libbb.h"
  72#include "xregex.h"
  73#include <syslog.h>
  74
  75#include <sys/un.h>
  76#include <sys/sysmacros.h>
  77
  78/* Various defines taken from linux/major.h */
  79#define IDE0_MAJOR      3
  80#define IDE1_MAJOR      22
  81#define IDE2_MAJOR      33
  82#define IDE3_MAJOR      34
  83#define IDE4_MAJOR      56
  84#define IDE5_MAJOR      57
  85#define IDE6_MAJOR      88
  86#define IDE7_MAJOR      89
  87#define IDE8_MAJOR      90
  88#define IDE9_MAJOR      91
  89
  90
  91/* Various defines taken from linux/devfs_fs.h */
  92#define DEVFSD_PROTOCOL_REVISION_KERNEL  5
  93#define DEVFSD_IOCTL_BASE       'd'
  94/*  These are the various ioctls  */
  95#define DEVFSDIOC_GET_PROTO_REV         _IOR(DEVFSD_IOCTL_BASE, 0, int)
  96#define DEVFSDIOC_SET_EVENT_MASK        _IOW(DEVFSD_IOCTL_BASE, 2, int)
  97#define DEVFSDIOC_RELEASE_EVENT_QUEUE   _IOW(DEVFSD_IOCTL_BASE, 3, int)
  98#define DEVFSDIOC_SET_CONFIG_DEBUG_MASK _IOW(DEVFSD_IOCTL_BASE, 4, int)
  99#define DEVFSD_NOTIFY_REGISTERED    0
 100#define DEVFSD_NOTIFY_UNREGISTERED  1
 101#define DEVFSD_NOTIFY_ASYNC_OPEN    2
 102#define DEVFSD_NOTIFY_CLOSE         3
 103#define DEVFSD_NOTIFY_LOOKUP        4
 104#define DEVFSD_NOTIFY_CHANGE        5
 105#define DEVFSD_NOTIFY_CREATE        6
 106#define DEVFSD_NOTIFY_DELETE        7
 107#define DEVFS_PATHLEN               1024
 108/*  Never change this otherwise the binary interface will change   */
 109
 110struct devfsd_notify_struct {
 111        /*  Use native C types to ensure same types in kernel and user space     */
 112        unsigned int type;           /*  DEVFSD_NOTIFY_* value                   */
 113        unsigned int mode;           /*  Mode of the inode or device entry       */
 114        unsigned int major;          /*  Major number of device entry            */
 115        unsigned int minor;          /*  Minor number of device entry            */
 116        unsigned int uid;            /*  Uid of process, inode or device entry   */
 117        unsigned int gid;            /*  Gid of process, inode or device entry   */
 118        unsigned int overrun_count;  /*  Number of lost events                   */
 119        unsigned int namelen;        /*  Number of characters not including '\0' */
 120        /*  The device name MUST come last                                       */
 121        char devname[DEVFS_PATHLEN]; /*  This will be '\0' terminated            */
 122};
 123
 124#define BUFFER_SIZE 16384
 125#define DEVFSD_VERSION "1.3.25"
 126#define CONFIG_FILE  "/etc/devfsd.conf"
 127#define MODPROBE                "/sbin/modprobe"
 128#define MODPROBE_SWITCH_1 "-k"
 129#define MODPROBE_SWITCH_2 "-C"
 130#define CONFIG_MODULES_DEVFS "/etc/modules.devfs"
 131#define MAX_ARGS     (6 + 1)
 132#define MAX_SUBEXPR  10
 133#define STRING_LENGTH 255
 134
 135/* for get_uid_gid() */
 136#define UID                     0
 137#define GID                     1
 138
 139/* fork_and_execute() */
 140# define DIE                    1
 141# define NO_DIE                 0
 142
 143/* for dir_operation() */
 144#define RESTORE         0
 145#define SERVICE         1
 146#define READ_CONFIG 2
 147
 148/*  Update only after changing code to reflect new protocol  */
 149#define DEVFSD_PROTOCOL_REVISION_DAEMON  5
 150
 151/*  Compile-time check  */
 152#if DEVFSD_PROTOCOL_REVISION_KERNEL != DEVFSD_PROTOCOL_REVISION_DAEMON
 153#error protocol version mismatch. Update your kernel headers
 154#endif
 155
 156#define AC_PERMISSIONS                          0
 157#define AC_MODLOAD                                      1
 158#define AC_EXECUTE                                      2
 159#define AC_MFUNCTION                            3       /* not supported by busybox */
 160#define AC_CFUNCTION                            4       /* not supported by busybox */
 161#define AC_COPY                                         5
 162#define AC_IGNORE                                       6
 163#define AC_MKOLDCOMPAT                          7
 164#define AC_MKNEWCOMPAT                          8
 165#define AC_RMOLDCOMPAT                          9
 166#define AC_RMNEWCOMPAT                          10
 167#define AC_RESTORE                                      11
 168
 169struct permissions_type {
 170        mode_t mode;
 171        uid_t uid;
 172        gid_t gid;
 173};
 174
 175struct execute_type {
 176        char *argv[MAX_ARGS + 1];  /*  argv[0] must always be the programme  */
 177};
 178
 179struct copy_type {
 180        const char *source;
 181        const char *destination;
 182};
 183
 184struct action_type {
 185        unsigned int what;
 186        unsigned int when;
 187};
 188
 189struct config_entry_struct {
 190        struct action_type action;
 191        regex_t preg;
 192        union
 193        {
 194        struct permissions_type permissions;
 195        struct execute_type execute;
 196        struct copy_type copy;
 197        }
 198        u;
 199        struct config_entry_struct *next;
 200};
 201
 202struct get_variable_info {
 203        const struct devfsd_notify_struct *info;
 204        const char *devname;
 205        char devpath[STRING_LENGTH];
 206};
 207
 208static void dir_operation(int , const char * , int,  unsigned long*);
 209static void service(struct stat statbuf, char *path);
 210static int st_expr_expand(char *, unsigned, const char *, const char *(*)(const char *, void *), void *);
 211static const char *get_old_name(const char *, unsigned, char *, unsigned, unsigned);
 212static int mksymlink(const char *oldpath, const char *newpath);
 213static void read_config_file(char *path, int optional, unsigned long *event_mask);
 214static void process_config_line(const char *, unsigned long *);
 215static int  do_servicing(int, unsigned long);
 216static void service_name(const struct devfsd_notify_struct *);
 217static void action_permissions(const struct devfsd_notify_struct *, const struct config_entry_struct *);
 218static void action_execute(const struct devfsd_notify_struct *, const struct config_entry_struct *,
 219                                                        const regmatch_t *, unsigned);
 220static void action_modload(const struct devfsd_notify_struct *info, const struct config_entry_struct *entry);
 221static void action_copy(const struct devfsd_notify_struct *, const struct config_entry_struct *,
 222                                                const regmatch_t *, unsigned);
 223static void action_compat(const struct devfsd_notify_struct *, unsigned);
 224static void free_config(void);
 225static void restore(char *spath, struct stat source_stat, int rootlen);
 226static int copy_inode(const char *, const struct stat *, mode_t, const char *, const struct stat *);
 227static mode_t get_mode(const char *);
 228static void signal_handler(int);
 229static const char *get_variable(const char *, void *);
 230static int make_dir_tree(const char *);
 231static int expand_expression(char *, unsigned, const char *, const char *(*)(const char *, void *), void *,
 232                                                        const char *, const regmatch_t *, unsigned);
 233static void expand_regexp(char *, size_t, const char *, const char *, const regmatch_t *, unsigned);
 234static const char *expand_variable(     char *, unsigned, unsigned *, const char *,
 235                                                                        const char *(*)(const char *, void *), void *);
 236static const char *get_variable_v2(const char *, const char *(*)(const char *, void *), void *);
 237static char get_old_ide_name(unsigned, unsigned);
 238static char *write_old_sd_name(char *, unsigned, unsigned, const char *);
 239
 240/* busybox functions */
 241static int get_uid_gid(int flag, const char *string);
 242static void safe_memcpy(char * dest, const char * src, int len);
 243static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, const char *ptr);
 244static unsigned int scan_dev_name(const char *d, unsigned int n, const char *ptr);
 245
 246/* Structs and vars */
 247static struct config_entry_struct *first_config = NULL;
 248static struct config_entry_struct *last_config = NULL;
 249static char *mount_point = NULL;
 250static volatile int caught_signal = FALSE;
 251static volatile int caught_sighup = FALSE;
 252static struct initial_symlink_struct {
 253        const char *dest;
 254        const char *name;
 255} initial_symlinks[] = {
 256        {"/proc/self/fd", "fd"},
 257        {"fd/0", "stdin"},
 258        {"fd/1", "stdout"},
 259        {"fd/2", "stderr"},
 260        {NULL, NULL},
 261};
 262
 263static struct event_type {
 264        unsigned int type;        /*  The DEVFSD_NOTIFY_* value                  */
 265        const char *config_name;  /*  The name used in the config file           */
 266} event_types[] = {
 267        {DEVFSD_NOTIFY_REGISTERED,   "REGISTER"},
 268        {DEVFSD_NOTIFY_UNREGISTERED, "UNREGISTER"},
 269        {DEVFSD_NOTIFY_ASYNC_OPEN,   "ASYNC_OPEN"},
 270        {DEVFSD_NOTIFY_CLOSE,        "CLOSE"},
 271        {DEVFSD_NOTIFY_LOOKUP,       "LOOKUP"},
 272        {DEVFSD_NOTIFY_CHANGE,       "CHANGE"},
 273        {DEVFSD_NOTIFY_CREATE,       "CREATE"},
 274        {DEVFSD_NOTIFY_DELETE,       "DELETE"},
 275        {0xffffffff,                 NULL}
 276};
 277
 278/* Busybox messages */
 279
 280static const char bb_msg_proto_rev[] ALIGN1          = "protocol revision";
 281static const char bb_msg_bad_config[] ALIGN1         = "bad %s config file: %s";
 282static const char bb_msg_small_buffer[] ALIGN1       = "buffer too small";
 283static const char bb_msg_variable_not_found[] ALIGN1 = "variable: %s not found";
 284
 285/* Busybox stuff */
 286#if ENABLE_DEVFSD_VERBOSE || ENABLE_DEBUG
 287#define info_logger(p, fmt, args...)                 bb_info_msg(fmt, ## args)
 288#define msg_logger(p, fmt, args...)                  bb_error_msg(fmt, ## args)
 289#define msg_logger_and_die(p, fmt, args...)          bb_error_msg_and_die(fmt, ## args)
 290#define error_logger(p, fmt, args...)                bb_perror_msg(fmt, ## args)
 291#define error_logger_and_die(p, fmt, args...)        bb_perror_msg_and_die(fmt, ## args)
 292#else
 293#define info_logger(p, fmt, args...)
 294#define msg_logger(p, fmt, args...)
 295#define msg_logger_and_die(p, fmt, args...)           exit(EXIT_FAILURE)
 296#define error_logger(p, fmt, args...)
 297#define error_logger_and_die(p, fmt, args...)         exit(EXIT_FAILURE)
 298#endif
 299
 300static void safe_memcpy(char *dest, const char *src, int len)
 301{
 302        memcpy(dest , src, len);
 303        dest[len] = '\0';
 304}
 305
 306static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, const char *ptr)
 307{
 308        if (d[n - 4] == 'd' && d[n - 3] == 'i' && d[n - 2] == 's' && d[n - 1] == 'c')
 309                return 2 + addendum;
 310        if (d[n - 2] == 'c' && d[n - 1] == 'd')
 311                return 3 + addendum;
 312        if (ptr[0] == 'p' && ptr[1] == 'a' && ptr[2] == 'r' && ptr[3] == 't')
 313                return 4 + addendum;
 314        if (ptr[n - 2] == 'm' && ptr[n - 1] == 't')
 315                return 5 + addendum;
 316        return 0;
 317}
 318
 319static unsigned int scan_dev_name(const char *d, unsigned int n, const char *ptr)
 320{
 321        if (d[0] == 's' && d[1] == 'c' && d[2] == 's' && d[3] == 'i' && d[4] == '/') {
 322                if (d[n - 7] == 'g' && d[n - 6] == 'e' && d[n - 5] == 'n'
 323                        && d[n - 4] == 'e' && d[n - 3] == 'r' && d[n - 2] == 'i' && d[n - 1] == 'c'
 324                )
 325                        return 1;
 326                return scan_dev_name_common(d, n, 0, ptr);
 327        }
 328        if (d[0] == 'i' && d[1] == 'd' && d[2] == 'e' && d[3] == '/'
 329                && d[4] == 'h' && d[5] == 'o' && d[6] == 's' && d[7] == 't'
 330        )
 331                return scan_dev_name_common(d, n, 4, ptr);
 332        if (d[0] == 's' && d[1] == 'b' && d[2] == 'p' && d[3] == '/')
 333                return 10;
 334        if (d[0] == 'v' && d[1] == 'c' && d[2] == 'c' && d[3] == '/')
 335                return 11;
 336        if (d[0] == 'p' && d[1] == 't' && d[2] == 'y' && d[3] == '/')
 337                return 12;
 338        return 0;
 339}
 340
 341/*  Public functions follow  */
 342
 343int devfsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 344int devfsd_main(int argc, char **argv)
 345{
 346        int print_version = FALSE;
 347        int do_daemon = TRUE;
 348        int no_polling = FALSE;
 349        int do_scan;
 350        int fd, proto_rev, count;
 351        unsigned long event_mask = 0;
 352        struct sigaction new_action;
 353        struct initial_symlink_struct *curr;
 354
 355        if (argc < 2)
 356                bb_show_usage();
 357
 358        for (count = 2; count < argc; ++count) {
 359                if (argv[count][0] == '-') {
 360                        if (argv[count][1] == 'v' && !argv[count][2]) /* -v */
 361                                print_version = TRUE;
 362                        else if (ENABLE_DEVFSD_FG_NP && argv[count][1] == 'f'
 363                         && argv[count][2] == 'g' && !argv[count][3]) /* -fg */
 364                                do_daemon = FALSE;
 365                        else if (ENABLE_DEVFSD_FG_NP && argv[count][1] == 'n'
 366                         && argv[count][2] == 'p' && !argv[count][3]) /* -np */
 367                                no_polling = TRUE;
 368                        else
 369                                bb_show_usage();
 370                }
 371        }
 372
 373        mount_point = bb_simplify_path(argv[1]);
 374
 375        xchdir(mount_point);
 376
 377        fd = xopen(".devfsd", O_RDONLY);
 378        close_on_exec_on(fd);
 379        xioctl(fd, DEVFSDIOC_GET_PROTO_REV, &proto_rev);
 380
 381        /*setup initial entries */
 382        for (curr = initial_symlinks; curr->dest != NULL; ++curr)
 383                symlink(curr->dest, curr->name);
 384
 385        /* NB: The check for CONFIG_FILE is done in read_config_file() */
 386
 387        if (print_version || (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)) {
 388                printf("%s v%s\nDaemon %s:\t%d\nKernel-side %s:\t%d\n",
 389                                applet_name, DEVFSD_VERSION, bb_msg_proto_rev,
 390                                DEVFSD_PROTOCOL_REVISION_DAEMON, bb_msg_proto_rev, proto_rev);
 391                if (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)
 392                        bb_error_msg_and_die("%s mismatch!", bb_msg_proto_rev);
 393                exit(EXIT_SUCCESS); /* -v */
 394        }
 395        /*  Tell kernel we are special(i.e. we get to see hidden entries)  */
 396        xioctl(fd, DEVFSDIOC_SET_EVENT_MASK, 0);
 397
 398        /*  Set up SIGHUP and SIGUSR1 handlers  */
 399        sigemptyset(&new_action.sa_mask);
 400        new_action.sa_flags = 0;
 401        new_action.sa_handler = signal_handler;
 402        sigaction_set(SIGHUP, &new_action);
 403        sigaction_set(SIGUSR1, &new_action);
 404
 405        printf("%s v%s started for %s\n", applet_name, DEVFSD_VERSION, mount_point);
 406
 407        /*  Set umask so that mknod(2), open(2) and mkdir(2) have complete control over permissions  */
 408        umask(0);
 409        read_config_file((char*)CONFIG_FILE, FALSE, &event_mask);
 410        /*  Do the scan before forking, so that boot scripts see the finished product  */
 411        dir_operation(SERVICE, mount_point, 0, NULL);
 412
 413        if (ENABLE_DEVFSD_FG_NP && no_polling)
 414                exit(EXIT_SUCCESS);
 415
 416        if (ENABLE_DEVFSD_VERBOSE || ENABLE_DEBUG)
 417                logmode = LOGMODE_BOTH;
 418        else if (do_daemon == TRUE)
 419                logmode = LOGMODE_SYSLOG;
 420        /* This is the default */
 421        /*else
 422                logmode = LOGMODE_STDIO; */
 423
 424        if (do_daemon) {
 425                /*  Release so that the child can grab it  */
 426                xioctl(fd, DEVFSDIOC_RELEASE_EVENT_QUEUE, 0);
 427                bb_daemonize_or_rexec(0, argv);
 428        } else if (ENABLE_DEVFSD_FG_NP) {
 429                setpgid(0, 0);  /*  Become process group leader                    */
 430        }
 431
 432        while (TRUE) {
 433                do_scan = do_servicing(fd, event_mask);
 434
 435                free_config();
 436                read_config_file((char*)CONFIG_FILE, FALSE, &event_mask);
 437                if (do_scan)
 438                        dir_operation(SERVICE, mount_point, 0, NULL);
 439        }
 440        if (ENABLE_FEATURE_CLEAN_UP) free(mount_point);
 441}   /*  End Function main  */
 442
 443
 444/*  Private functions follow  */
 445
 446static void read_config_file(char *path, int optional, unsigned long *event_mask)
 447/*  [SUMMARY] Read a configuration database.
 448    <path> The path to read the database from. If this is a directory, all
 449    entries in that directory will be read(except hidden entries).
 450    <optional> If TRUE, the routine will silently ignore a missing config file.
 451    <event_mask> The event mask is written here. This is not initialised.
 452    [RETURNS] Nothing.
 453*/
 454{
 455        struct stat statbuf;
 456        FILE *fp;
 457        char buf[STRING_LENGTH];
 458        char *line = NULL;
 459        char *p;
 460
 461        if (stat(path, &statbuf) == 0) {
 462                /* Don't read 0 length files: ignored */
 463                /*if (statbuf.st_size == 0)
 464                                return;*/
 465                if (S_ISDIR(statbuf.st_mode)) {
 466                        p = bb_simplify_path(path);
 467                        dir_operation(READ_CONFIG, p, 0, event_mask);
 468                        free(p);
 469                        return;
 470                }
 471                fp = fopen_for_read(path);
 472                if (fp != NULL) {
 473                        while (fgets(buf, STRING_LENGTH, fp) != NULL) {
 474                                /*  Skip whitespace  */
 475                                line = buf;
 476                                line = skip_whitespace(line);
 477                                if (line[0] == '\0' || line[0] == '#')
 478                                        continue;
 479                                process_config_line(line, event_mask);
 480                        }
 481                        fclose(fp);
 482                } else {
 483                        goto read_config_file_err;
 484                }
 485        } else {
 486read_config_file_err:
 487                if (optional == 0 && errno == ENOENT)
 488                        error_logger_and_die(LOG_ERR, "read config file: %s", path);
 489        }
 490}   /*  End Function read_config_file   */
 491
 492static void process_config_line(const char *line, unsigned long *event_mask)
 493/*  [SUMMARY] Process a line from a configuration file.
 494    <line> The configuration line.
 495    <event_mask> The event mask is written here. This is not initialised.
 496    [RETURNS] Nothing.
 497*/
 498{
 499        int  num_args, count;
 500        struct config_entry_struct *new;
 501        char p[MAX_ARGS][STRING_LENGTH];
 502        char when[STRING_LENGTH], what[STRING_LENGTH];
 503        char name[STRING_LENGTH];
 504        const char *msg = "";
 505        char *ptr;
 506        int i;
 507
 508        /* !!!! Only Uppercase Keywords in devsfd.conf */
 509        static const char options[] ALIGN1 =
 510                "CLEAR_CONFIG\0""INCLUDE\0""OPTIONAL_INCLUDE\0"
 511                "RESTORE\0""PERMISSIONS\0""MODLOAD\0""EXECUTE\0"
 512                "COPY\0""IGNORE\0""MKOLDCOMPAT\0""MKNEWCOMPAT\0"
 513                "RMOLDCOMPAT\0""RMNEWCOMPAT\0";
 514
 515        for (count = 0; count < MAX_ARGS; ++count)
 516                p[count][0] = '\0';
 517        num_args = sscanf(line, "%s %s %s %s %s %s %s %s %s %s",
 518                        when, name, what,
 519                        p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
 520
 521        i = index_in_strings(options, when);
 522
 523        /* "CLEAR_CONFIG" */
 524        if (i == 0) {
 525                free_config();
 526                *event_mask = 0;
 527                return;
 528        }
 529
 530        if (num_args < 2)
 531                goto process_config_line_err;
 532
 533        /* "INCLUDE" & "OPTIONAL_INCLUDE" */
 534        if (i == 1 || i == 2) {
 535                st_expr_expand(name, STRING_LENGTH, name, get_variable, NULL);
 536                info_logger(LOG_INFO, "%sinclude: %s", (toupper(when[0]) == 'I') ? "": "optional_", name);
 537                read_config_file(name, (toupper(when[0]) == 'I') ? FALSE : TRUE, event_mask);
 538                return;
 539        }
 540        /* "RESTORE" */
 541        if (i == 3) {
 542                dir_operation(RESTORE, name, strlen(name),NULL);
 543                return;
 544        }
 545        if (num_args < 3)
 546                goto process_config_line_err;
 547
 548        new = xzalloc(sizeof *new);
 549
 550        for (count = 0; event_types[count].config_name != NULL; ++count) {
 551                if (strcasecmp(when, event_types[count].config_name) != 0)
 552                        continue;
 553                new->action.when = event_types[count].type;
 554                break;
 555        }
 556        if (event_types[count].config_name == NULL) {
 557                msg = "WHEN in";
 558                goto process_config_line_err;
 559        }
 560
 561        i = index_in_strings(options, what);
 562
 563        switch (i) {
 564                case 4: /* "PERMISSIONS" */
 565                        new->action.what = AC_PERMISSIONS;
 566                        /*  Get user and group  */
 567                        ptr = strchr(p[0], '.');
 568                        if (ptr == NULL) {
 569                                msg = "UID.GID";
 570                                goto process_config_line_err; /*"missing '.' in UID.GID"*/
 571                        }
 572
 573                        *ptr++ = '\0';
 574                        new->u.permissions.uid = get_uid_gid(UID, p[0]);
 575                        new->u.permissions.gid = get_uid_gid(GID, ptr);
 576                        /*  Get mode  */
 577                        new->u.permissions.mode = get_mode(p[1]);
 578                        break;
 579                case 5: /*  MODLOAD */
 580                        /*This  action will pass "/dev/$devname"(i.e. "/dev/" prefixed to
 581                        the device name) to the module loading  facility.  In  addition,
 582                        the /etc/modules.devfs configuration file is used.*/
 583                        if (ENABLE_DEVFSD_MODLOAD)
 584                                new->action.what = AC_MODLOAD;
 585                        break;
 586                case 6: /* EXECUTE */
 587                        new->action.what = AC_EXECUTE;
 588                        num_args -= 3;
 589
 590                        for (count = 0; count < num_args; ++count)
 591                                new->u.execute.argv[count] = xstrdup(p[count]);
 592
 593                        new->u.execute.argv[num_args] = NULL;
 594                        break;
 595                case 7: /* COPY */
 596                        new->action.what = AC_COPY;
 597                        num_args -= 3;
 598                        if (num_args != 2)
 599                                goto process_config_line_err; /* missing path and function in line */
 600
 601                        new->u.copy.source = xstrdup(p[0]);
 602                        new->u.copy.destination = xstrdup(p[1]);
 603                        break;
 604                case 8: /* IGNORE */
 605                /* FALLTROUGH */
 606                case 9: /* MKOLDCOMPAT */
 607                /* FALLTROUGH */
 608                case 10: /* MKNEWCOMPAT */
 609                /* FALLTROUGH */
 610                case 11:/* RMOLDCOMPAT */
 611                /* FALLTROUGH */
 612                case 12: /* RMNEWCOMPAT */
 613                /*      AC_IGNORE                                       6
 614                        AC_MKOLDCOMPAT                          7
 615                        AC_MKNEWCOMPAT                          8
 616                        AC_RMOLDCOMPAT                          9
 617                        AC_RMNEWCOMPAT                          10*/
 618                        new->action.what = i - 2;
 619                        break;
 620                default:
 621                        msg = "WHAT in";
 622                        goto process_config_line_err;
 623                /*esac*/
 624        } /* switch (i) */
 625
 626        xregcomp(&new->preg, name, REG_EXTENDED);
 627
 628        *event_mask |= 1 << new->action.when;
 629        new->next = NULL;
 630        if (first_config == NULL)
 631                first_config = new;
 632        else
 633                last_config->next = new;
 634        last_config = new;
 635        return;
 636
 637 process_config_line_err:
 638        msg_logger_and_die(LOG_ERR, bb_msg_bad_config, msg , line);
 639}  /*  End Function process_config_line   */
 640
 641static int do_servicing(int fd, unsigned long event_mask)
 642/*  [SUMMARY] Service devfs changes until a signal is received.
 643    <fd> The open control file.
 644    <event_mask> The event mask.
 645    [RETURNS] TRUE if SIGHUP was caught, else FALSE.
 646*/
 647{
 648        ssize_t bytes;
 649        struct devfsd_notify_struct info;
 650
 651        /* (void*) cast is only in order to match prototype */
 652        xioctl(fd, DEVFSDIOC_SET_EVENT_MASK, (void*)event_mask);
 653        while (!caught_signal) {
 654                errno = 0;
 655                bytes = read(fd, (char *) &info, sizeof info);
 656                if (caught_signal)
 657                        break;      /*  Must test for this first     */
 658                if (errno == EINTR)
 659                        continue;  /*  Yes, the order is important  */
 660                if (bytes < 1)
 661                        break;
 662                service_name(&info);
 663        }
 664        if (caught_signal) {
 665                int c_sighup = caught_sighup;
 666
 667                caught_signal = FALSE;
 668                caught_sighup = FALSE;
 669                return c_sighup;
 670        }
 671        msg_logger_and_die(LOG_ERR, "read error on control file");
 672}   /*  End Function do_servicing  */
 673
 674static void service_name(const struct devfsd_notify_struct *info)
 675/*  [SUMMARY] Service a single devfs change.
 676    <info> The devfs change.
 677    [RETURNS] Nothing.
 678*/
 679{
 680        unsigned int n;
 681        regmatch_t mbuf[MAX_SUBEXPR];
 682        struct config_entry_struct *entry;
 683
 684        if (ENABLE_DEBUG && info->overrun_count > 0)
 685                msg_logger(LOG_ERR, "lost %u events", info->overrun_count);
 686
 687        /*  Discard lookups on "/dev/log" and "/dev/initctl"  */
 688        if (info->type == DEVFSD_NOTIFY_LOOKUP
 689                && ((info->devname[0] == 'l' && info->devname[1] == 'o'
 690                && info->devname[2] == 'g' && !info->devname[3])
 691                || (info->devname[0] == 'i' && info->devname[1] == 'n'
 692                && info->devname[2] == 'i' && info->devname[3] == 't'
 693                && info->devname[4] == 'c' && info->devname[5] == 't'
 694                && info->devname[6] == 'l' && !info->devname[7]))
 695        )
 696                return;
 697
 698        for (entry = first_config; entry != NULL; entry = entry->next) {
 699                /*  First check if action matches the type, then check if name matches */
 700                if (info->type != entry->action.when
 701                || regexec(&entry->preg, info->devname, MAX_SUBEXPR, mbuf, 0) != 0)
 702                        continue;
 703                for (n = 0;(n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n)
 704                        /* VOID */;
 705
 706                switch (entry->action.what) {
 707                        case AC_PERMISSIONS:
 708                                action_permissions(info, entry);
 709                                break;
 710                        case AC_MODLOAD:
 711                                if (ENABLE_DEVFSD_MODLOAD)
 712                                        action_modload(info, entry);
 713                                break;
 714                        case AC_EXECUTE:
 715                                action_execute(info, entry, mbuf, n);
 716                                break;
 717                        case AC_COPY:
 718                                action_copy(info, entry, mbuf, n);
 719                                break;
 720                        case AC_IGNORE:
 721                                return;
 722                                /*break;*/
 723                        case AC_MKOLDCOMPAT:
 724                        case AC_MKNEWCOMPAT:
 725                        case AC_RMOLDCOMPAT:
 726                        case AC_RMNEWCOMPAT:
 727                                action_compat(info, entry->action.what);
 728                                break;
 729                        default:
 730                                msg_logger_and_die(LOG_ERR, "Unknown action");
 731                }
 732        }
 733}   /*  End Function service_name  */
 734
 735static void action_permissions(const struct devfsd_notify_struct *info,
 736                                const struct config_entry_struct *entry)
 737/*  [SUMMARY] Update permissions for a device entry.
 738    <info> The devfs change.
 739    <entry> The config file entry.
 740    [RETURNS] Nothing.
 741*/
 742{
 743        struct stat statbuf;
 744
 745        if (stat(info->devname, &statbuf) != 0
 746         || chmod(info->devname, (statbuf.st_mode & S_IFMT) | (entry->u.permissions.mode & ~S_IFMT)) != 0
 747         || chown(info->devname, entry->u.permissions.uid, entry->u.permissions.gid) != 0
 748        )
 749                error_logger(LOG_ERR, "Can't chmod or chown: %s", info->devname);
 750}   /*  End Function action_permissions  */
 751
 752static void action_modload(const struct devfsd_notify_struct *info,
 753                        const struct config_entry_struct *entry UNUSED_PARAM)
 754/*  [SUMMARY] Load a module.
 755    <info> The devfs change.
 756    <entry> The config file entry.
 757    [RETURNS] Nothing.
 758*/
 759{
 760        char *argv[6];
 761
 762        argv[0] = (char*)MODPROBE;
 763        argv[1] = (char*)MODPROBE_SWITCH_1; /* "-k" */
 764        argv[2] = (char*)MODPROBE_SWITCH_2; /* "-C" */
 765        argv[3] = (char*)CONFIG_MODULES_DEVFS;
 766        argv[4] = concat_path_file("/dev", info->devname); /* device */
 767        argv[5] = NULL;
 768
 769        spawn_and_wait(argv);
 770        free(argv[4]);
 771}  /*  End Function action_modload  */
 772
 773static void action_execute(const struct devfsd_notify_struct *info,
 774                        const struct config_entry_struct *entry,
 775                        const regmatch_t *regexpr, unsigned int numexpr)
 776/*  [SUMMARY] Execute a programme.
 777    <info> The devfs change.
 778    <entry> The config file entry.
 779    <regexpr> The number of subexpression(start, end) offsets within the
 780    device name.
 781    <numexpr> The number of elements within <<regexpr>>.
 782    [RETURNS] Nothing.
 783*/
 784{
 785        unsigned int count;
 786        struct get_variable_info gv_info;
 787        char *argv[MAX_ARGS + 1];
 788        char largv[MAX_ARGS + 1][STRING_LENGTH];
 789
 790        gv_info.info = info;
 791        gv_info.devname = info->devname;
 792        snprintf(gv_info.devpath, sizeof(gv_info.devpath), "%s/%s", mount_point, info->devname);
 793        for (count = 0; entry->u.execute.argv[count] != NULL; ++count) {
 794                expand_expression(largv[count], STRING_LENGTH,
 795                                entry->u.execute.argv[count],
 796                                get_variable, &gv_info,
 797                                gv_info.devname, regexpr, numexpr);
 798                argv[count] = largv[count];
 799        }
 800        argv[count] = NULL;
 801        spawn_and_wait(argv);
 802}   /*  End Function action_execute  */
 803
 804
 805static void action_copy(const struct devfsd_notify_struct *info,
 806                        const struct config_entry_struct *entry,
 807                        const regmatch_t *regexpr, unsigned int numexpr)
 808/*  [SUMMARY] Copy permissions.
 809    <info> The devfs change.
 810    <entry> The config file entry.
 811    <regexpr> This list of subexpression(start, end) offsets within the
 812    device name.
 813    <numexpr> The number of elements in <<regexpr>>.
 814    [RETURNS] Nothing.
 815*/
 816{
 817        mode_t new_mode;
 818        struct get_variable_info gv_info;
 819        struct stat source_stat, dest_stat;
 820        char source[STRING_LENGTH], destination[STRING_LENGTH];
 821        int ret = 0;
 822
 823        dest_stat.st_mode = 0;
 824
 825        if ((info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK(info->mode))
 826                return;
 827        gv_info.info = info;
 828        gv_info.devname = info->devname;
 829
 830        snprintf(gv_info.devpath, sizeof(gv_info.devpath), "%s/%s", mount_point, info->devname);
 831        expand_expression(source, STRING_LENGTH, entry->u.copy.source,
 832                                get_variable, &gv_info, gv_info.devname,
 833                                regexpr, numexpr);
 834
 835        expand_expression(destination, STRING_LENGTH, entry->u.copy.destination,
 836                                get_variable, &gv_info, gv_info.devname,
 837                                regexpr, numexpr);
 838
 839        if (!make_dir_tree(destination) || lstat(source, &source_stat) != 0)
 840                        return;
 841        lstat(destination, &dest_stat);
 842        new_mode = source_stat.st_mode & ~S_ISVTX;
 843        if (info->type == DEVFSD_NOTIFY_CREATE)
 844                new_mode |= S_ISVTX;
 845        else if ((info->type == DEVFSD_NOTIFY_CHANGE) &&(dest_stat.st_mode & S_ISVTX))
 846                new_mode |= S_ISVTX;
 847        ret = copy_inode(destination, &dest_stat, new_mode, source, &source_stat);
 848        if (ENABLE_DEBUG && ret && (errno != EEXIST))
 849                error_logger(LOG_ERR, "copy_inode: %s to %s", source, destination);
 850}   /*  End Function action_copy  */
 851
 852static void action_compat(const struct devfsd_notify_struct *info, unsigned int action)
 853/*  [SUMMARY] Process a compatibility request.
 854    <info> The devfs change.
 855    <action> The action to take.
 856    [RETURNS] Nothing.
 857*/
 858{
 859        int ret;
 860        const char *compat_name = NULL;
 861        const char *dest_name = info->devname;
 862        const char *ptr;
 863        char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH];
 864        int mode, host, bus, target, lun;
 865        unsigned int i;
 866        char rewind_;
 867        /* 1 to 5  "scsi/" , 6 to 9 "ide/host" */
 868        static const char *const fmt[] = {
 869                NULL ,
 870                "sg/c%db%dt%du%d",              /* scsi/generic */
 871                "sd/c%db%dt%du%d",              /* scsi/disc */
 872                "sr/c%db%dt%du%d",              /* scsi/cd */
 873                "sd/c%db%dt%du%dp%d",           /* scsi/part */
 874                "st/c%db%dt%du%dm%d%c",         /* scsi/mt */
 875                "ide/hd/c%db%dt%du%d",          /* ide/host/disc */
 876                "ide/cd/c%db%dt%du%d",          /* ide/host/cd */
 877                "ide/hd/c%db%dt%du%dp%d",       /* ide/host/part */
 878                "ide/mt/c%db%dt%du%d%s",        /* ide/host/mt */
 879                NULL
 880        };
 881
 882        /*  First construct compatibility name  */
 883        switch (action) {
 884                case AC_MKOLDCOMPAT:
 885                case AC_RMOLDCOMPAT:
 886                        compat_name = get_old_name(info->devname, info->namelen, compat_buf, info->major, info->minor);
 887                        break;
 888                case AC_MKNEWCOMPAT:
 889                case AC_RMNEWCOMPAT:
 890                        ptr = bb_basename(info->devname);
 891                        i = scan_dev_name(info->devname, info->namelen, ptr);
 892
 893                        /* nothing found */
 894                        if (i == 0 || i > 9)
 895                                return;
 896
 897                        sscanf(info->devname + ((i < 6) ? 5 : 4), "host%d/bus%d/target%d/lun%d/", &host, &bus, &target, &lun);
 898                        snprintf(dest_buf, sizeof(dest_buf), "../%s", info->devname + (( i > 5) ? 4 : 0));
 899                        dest_name = dest_buf;
 900                        compat_name = compat_buf;
 901
 902
 903                        /* 1 == scsi/generic  2 == scsi/disc 3 == scsi/cd 6 == ide/host/disc 7 == ide/host/cd */
 904                        if (i == 1 || i == 2 || i == 3 || i == 6 || i ==7)
 905                                sprintf(compat_buf, fmt[i], host, bus, target, lun);
 906
 907                        /* 4 == scsi/part 8 == ide/host/part */
 908                        if (i == 4 || i == 8)
 909                                sprintf(compat_buf, fmt[i], host, bus, target, lun, atoi(ptr + 4));
 910
 911                        /* 5 == scsi/mt */
 912                        if (i == 5) {
 913                                rewind_ = info->devname[info->namelen - 1];
 914                                if (rewind_ != 'n')
 915                                        rewind_ = '\0';
 916                                mode=0;
 917                                if (ptr[2] ==  'l' /*108*/ || ptr[2] == 'm'/*109*/)
 918                                        mode = ptr[2] - 107; /* 1 or 2 */
 919                                if (ptr[2] ==  'a')
 920                                        mode = 3;
 921                                sprintf(compat_buf, fmt[i], host, bus, target, lun, mode, rewind_);
 922                        }
 923
 924                        /* 9 == ide/host/mt */
 925                        if (i ==  9)
 926                                snprintf(compat_buf, sizeof(compat_buf), fmt[i], host, bus, target, lun, ptr + 2);
 927                /* esac */
 928        } /* switch (action) */
 929
 930        if (compat_name == NULL)
 931                return;
 932
 933        /*  Now decide what to do with it  */
 934        switch (action) {
 935                case AC_MKOLDCOMPAT:
 936                case AC_MKNEWCOMPAT:
 937                        mksymlink(dest_name, compat_name);
 938                        break;
 939                case AC_RMOLDCOMPAT:
 940                case AC_RMNEWCOMPAT:
 941                        ret = unlink(compat_name);
 942                        if (ENABLE_DEBUG && ret)
 943                                error_logger(LOG_ERR, "unlink: %s", compat_name);
 944                        break;
 945                /*esac*/
 946        } /* switch (action) */
 947}   /*  End Function action_compat  */
 948
 949static void restore(char *spath, struct stat source_stat, int rootlen)
 950{
 951        char *dpath;
 952        struct stat dest_stat;
 953
 954        dest_stat.st_mode = 0;
 955        dpath = concat_path_file(mount_point, spath + rootlen);
 956        lstat(dpath, &dest_stat);
 957        free(dpath);
 958        if (S_ISLNK(source_stat.st_mode) || (source_stat.st_mode & S_ISVTX))
 959                copy_inode(dpath, &dest_stat, (source_stat.st_mode & ~S_ISVTX), spath, &source_stat);
 960
 961        if (S_ISDIR(source_stat.st_mode))
 962                dir_operation(RESTORE, spath, rootlen, NULL);
 963}
 964
 965
 966static int copy_inode(const char *destpath, const struct stat *dest_stat,
 967                        mode_t new_mode,
 968                        const char *sourcepath, const struct stat *source_stat)
 969/*  [SUMMARY] Copy an inode.
 970    <destpath> The destination path. An existing inode may be deleted.
 971    <dest_stat> The destination stat(2) information.
 972    <new_mode> The desired new mode for the destination.
 973    <sourcepath> The source path.
 974    <source_stat> The source stat(2) information.
 975    [RETURNS] TRUE on success, else FALSE.
 976*/
 977{
 978        int source_len, dest_len;
 979        char source_link[STRING_LENGTH], dest_link[STRING_LENGTH];
 980        int fd, val;
 981        struct sockaddr_un un_addr;
 982        char symlink_val[STRING_LENGTH];
 983
 984        if ((source_stat->st_mode & S_IFMT) ==(dest_stat->st_mode & S_IFMT)) {
 985                /*  Same type  */
 986                if (S_ISLNK(source_stat->st_mode)) {
 987                        source_len = readlink(sourcepath, source_link, STRING_LENGTH - 1);
 988                        if ((source_len < 0)
 989                         || (dest_len = readlink(destpath, dest_link, STRING_LENGTH - 1)) < 0
 990                        )
 991                                return FALSE;
 992                        source_link[source_len] = '\0';
 993                        dest_link[dest_len]     = '\0';
 994                        if ((source_len != dest_len) || (strcmp(source_link, dest_link) != 0)) {
 995                                unlink(destpath);
 996                                symlink(source_link, destpath);
 997                        }
 998                        return TRUE;
 999                }   /*  Else not a symlink  */
1000                chmod(destpath, new_mode & ~S_IFMT);
1001                chown(destpath, source_stat->st_uid, source_stat->st_gid);
1002                return TRUE;
1003        }
1004        /*  Different types: unlink and create  */
1005        unlink(destpath);
1006        switch (source_stat->st_mode & S_IFMT) {
1007                case S_IFSOCK:
1008                        fd = socket(AF_UNIX, SOCK_STREAM, 0);
1009                        if (fd < 0)
1010                                break;
1011                        un_addr.sun_family = AF_UNIX;
1012                        snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s", destpath);
1013                        val = bind(fd, (struct sockaddr *) &un_addr, (int) sizeof un_addr);
1014                        close(fd);
1015                        if (val != 0 || chmod(destpath, new_mode & ~S_IFMT) != 0)
1016                                break;
1017                        goto do_chown;
1018                case S_IFLNK:
1019                        val = readlink(sourcepath, symlink_val, STRING_LENGTH - 1);
1020                        if (val < 0)
1021                                break;
1022                        symlink_val[val] = '\0';
1023                        if (symlink(symlink_val, destpath) == 0)
1024                                return TRUE;
1025                        break;
1026                case S_IFREG:
1027                        fd = open(destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT);
1028                        if (fd < 0)
1029                                break;
1030                        close(fd);
1031                        if (chmod(destpath, new_mode & ~S_IFMT) != 0)
1032                                break;
1033                        goto do_chown;
1034                case S_IFBLK:
1035                case S_IFCHR:
1036                case S_IFIFO:
1037                        if (mknod(destpath, new_mode, source_stat->st_rdev) != 0)
1038                                break;
1039                        goto do_chown;
1040                case S_IFDIR:
1041                        if (mkdir(destpath, new_mode & ~S_IFMT) != 0)
1042                                break;
1043do_chown:
1044                        if (chown(destpath, source_stat->st_uid, source_stat->st_gid) == 0)
1045                                return TRUE;
1046                /*break;*/
1047        }
1048        return FALSE;
1049}   /*  End Function copy_inode  */
1050
1051static void free_config(void)
1052/*  [SUMMARY] Free the configuration information.
1053    [RETURNS] Nothing.
1054*/
1055{
1056        struct config_entry_struct *c_entry;
1057        void *next;
1058
1059        for (c_entry = first_config; c_entry != NULL; c_entry = next) {
1060                unsigned int count;
1061
1062                next = c_entry->next;
1063                regfree(&c_entry->preg);
1064                if (c_entry->action.what == AC_EXECUTE) {
1065                        for (count = 0; count < MAX_ARGS; ++count) {
1066                                if (c_entry->u.execute.argv[count] == NULL)
1067                                        break;
1068                                free(c_entry->u.execute.argv[count]);
1069                        }
1070                }
1071                free(c_entry);
1072        }
1073        first_config = NULL;
1074        last_config = NULL;
1075}   /*  End Function free_config  */
1076
1077static int get_uid_gid(int flag, const char *string)
1078/*  [SUMMARY] Convert a string to a UID or GID value.
1079        <flag> "UID" or "GID".
1080        <string> The string.
1081    [RETURNS] The UID or GID value.
1082*/
1083{
1084        struct passwd *pw_ent;
1085        struct group *grp_ent;
1086        const char *msg;
1087
1088        if (isdigit(string[0]) || ((string[0] == '-') && isdigit(string[1])))
1089                return atoi(string);
1090
1091        if (flag == UID && (pw_ent = getpwnam(string)) != NULL)
1092                return pw_ent->pw_uid;
1093
1094        if (ENABLE_DEVFSD_VERBOSE)
1095                msg = "user";
1096
1097        if (flag == GID) {
1098                if ((grp_ent = getgrnam(string)) != NULL)
1099                        return grp_ent->gr_gid;
1100                if (ENABLE_DEVFSD_VERBOSE)
1101                        msg = "group";
1102        }
1103
1104        if (ENABLE_DEVFSD_VERBOSE)
1105                msg_logger(LOG_ERR, "unknown %s: %s, defaulting to %cid=0",  msg, string, msg[0]);
1106        return 0;
1107}/*  End Function get_uid_gid  */
1108
1109static mode_t get_mode(const char *string)
1110/*  [SUMMARY] Convert a string to a mode value.
1111    <string> The string.
1112    [RETURNS] The mode value.
1113*/
1114{
1115        mode_t mode;
1116        int i;
1117
1118        if (isdigit(string[0]))
1119                return strtoul(string, NULL, 8);
1120        if (strlen(string) != 9)
1121                msg_logger_and_die(LOG_ERR, "bad mode: %s", string);
1122
1123        mode = 0;
1124        i = S_IRUSR;
1125        while (i > 0) {
1126                if (string[0] == 'r' || string[0] == 'w' || string[0] == 'x')
1127                        mode += i;
1128                i = i / 2;
1129                string++;
1130        }
1131        return mode;
1132}   /*  End Function get_mode  */
1133
1134static void signal_handler(int sig)
1135{
1136        caught_signal = TRUE;
1137        if (sig == SIGHUP)
1138                caught_sighup = TRUE;
1139
1140        info_logger(LOG_INFO, "Caught signal %d", sig);
1141}   /*  End Function signal_handler  */
1142
1143static const char *get_variable(const char *variable, void *info)
1144{
1145        static char *hostname;
1146
1147        struct get_variable_info *gv_info = info;
1148        const char *field_names[] = {
1149                        "hostname", "mntpt", "devpath", "devname", "uid", "gid", "mode",
1150                        NULL, mount_point, gv_info->devpath, gv_info->devname, NULL
1151        };
1152        int i;
1153
1154        if (!hostname)
1155                hostname = safe_gethostname();
1156        field_names[7] = hostname;
1157
1158        /* index_in_str_array returns i>=0  */
1159        i = index_in_str_array(field_names, variable);
1160
1161        if (i > 6 || i < 0 || (i > 1 && gv_info == NULL))
1162                return NULL;
1163        if (i >= 0 && i <= 3)
1164                return field_names[i + 7];
1165
1166        if (i == 4)
1167                return auto_string(xasprintf("%u", gv_info->info->uid));
1168        if (i == 5)
1169                return auto_string(xasprintf("%u", gv_info->info->gid));
1170        /* i == 6 */
1171        return auto_string(xasprintf("%o", gv_info->info->mode));
1172}   /*  End Function get_variable  */
1173
1174static void service(struct stat statbuf, char *path)
1175{
1176        struct devfsd_notify_struct info;
1177
1178        memset(&info, 0, sizeof info);
1179        info.type = DEVFSD_NOTIFY_REGISTERED;
1180        info.mode = statbuf.st_mode;
1181        info.major = major(statbuf.st_rdev);
1182        info.minor = minor(statbuf.st_rdev);
1183        info.uid = statbuf.st_uid;
1184        info.gid = statbuf.st_gid;
1185        snprintf(info.devname, sizeof(info.devname), "%s", path + strlen(mount_point) + 1);
1186        info.namelen = strlen(info.devname);
1187        service_name(&info);
1188        if (S_ISDIR(statbuf.st_mode))
1189                dir_operation(SERVICE, path, 0, NULL);
1190}
1191
1192static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask)
1193/*  [SUMMARY] Scan a directory tree and generate register events on leaf nodes.
1194        <flag> To choose which function to perform
1195        <dp> The directory pointer. This is closed upon completion.
1196    <dir_name> The name of the directory.
1197        <rootlen> string length parameter.
1198    [RETURNS] Nothing.
1199*/
1200{
1201        struct stat statbuf;
1202        DIR *dp;
1203        struct dirent *de;
1204        char *path;
1205
1206        dp = warn_opendir(dir_name);
1207        if (dp == NULL)
1208                return;
1209
1210        while ((de = readdir(dp)) != NULL) {
1211
1212                if (de->d_name && DOT_OR_DOTDOT(de->d_name))
1213                        continue;
1214                path = concat_path_file(dir_name, de->d_name);
1215                if (lstat(path, &statbuf) == 0) {
1216                        switch (type) {
1217                                case SERVICE:
1218                                        service(statbuf, path);
1219                                        break;
1220                                case RESTORE:
1221                                        restore(path, statbuf, var);
1222                                        break;
1223                                case READ_CONFIG:
1224                                        read_config_file(path, var, event_mask);
1225                                        break;
1226                        }
1227                }
1228                free(path);
1229        }
1230        closedir(dp);
1231}   /*  End Function do_scan_and_service  */
1232
1233static int mksymlink(const char *oldpath, const char *newpath)
1234/*  [SUMMARY] Create a symlink, creating intervening directories as required.
1235    <oldpath> The string contained in the symlink.
1236    <newpath> The name of the new symlink.
1237    [RETURNS] 0 on success, else -1.
1238*/
1239{
1240        if (!make_dir_tree(newpath))
1241                return -1;
1242
1243        if (symlink(oldpath, newpath) != 0) {
1244                if (errno != EEXIST)
1245                        return -1;
1246        }
1247        return 0;
1248}   /*  End Function mksymlink  */
1249
1250
1251static int make_dir_tree(const char *path)
1252/*  [SUMMARY] Creating intervening directories for a path as required.
1253    <path> The full pathname(including the leaf node).
1254    [RETURNS] TRUE on success, else FALSE.
1255*/
1256{
1257        if (bb_make_directory(dirname((char *)path), -1, FILEUTILS_RECUR) == -1)
1258                return FALSE;
1259        return TRUE;
1260} /*  End Function make_dir_tree  */
1261
1262static int expand_expression(char *output, unsigned int outsize,
1263                        const char *input,
1264                        const char *(*get_variable_func)(const char *variable, void *info),
1265                        void *info,
1266                        const char *devname,
1267                        const regmatch_t *ex, unsigned int numexp)
1268/*  [SUMMARY] Expand environment variables and regular subexpressions in string.
1269    <output> The output expanded expression is written here.
1270    <length> The size of the output buffer.
1271    <input> The input expression. This may equal <<output>>.
1272    <get_variable> A function which will be used to get variable values. If
1273    this returns NULL, the environment is searched instead. If this is NULL,
1274    only the environment is searched.
1275    <info> An arbitrary pointer passed to <<get_variable>>.
1276    <devname> Device name; specifically, this is the string that contains all
1277    of the regular subexpressions.
1278    <ex> Array of start / end offsets into info->devname for each subexpression
1279    <numexp> Number of regular subexpressions found in <<devname>>.
1280    [RETURNS] TRUE on success, else FALSE.
1281*/
1282{
1283        char temp[STRING_LENGTH];
1284
1285        if (!st_expr_expand(temp, STRING_LENGTH, input, get_variable_func, info))
1286                return FALSE;
1287        expand_regexp(output, outsize, temp, devname, ex, numexp);
1288        return TRUE;
1289}   /*  End Function expand_expression  */
1290
1291static void expand_regexp(char *output, size_t outsize, const char *input,
1292                        const char *devname,
1293                        const regmatch_t *ex, unsigned int numex)
1294/*  [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9.
1295    <output> The output expanded expression is written here.
1296    <outsize> The size of the output buffer.
1297    <input> The input expression. This may NOT equal <<output>>, because
1298    supporting that would require yet another string-copy. However, it's not
1299    hard to write a simple wrapper function to add this functionality for those
1300    few cases that need it.
1301    <devname> Device name; specifically, this is the string that contains all
1302    of the regular subexpressions.
1303    <ex> An array of start and end offsets into <<devname>>, one for each
1304    subexpression
1305    <numex> Number of subexpressions in the offset-array <<ex>>.
1306    [RETURNS] Nothing.
1307*/
1308{
1309        const char last_exp = '0' - 1 + numex;
1310        int c = -1;
1311
1312        /*  Guarantee NULL termination by writing an explicit '\0' character into
1313        the very last byte  */
1314        if (outsize)
1315                output[--outsize] = '\0';
1316        /*  Copy the input string into the output buffer, replacing '\\' with '\'
1317        and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x
1318        codes are deleted  */
1319        while ((c != '\0') && (outsize != 0)) {
1320                c = *input;
1321                ++input;
1322                if (c == '\\') {
1323                        c = *input;
1324                        ++input;
1325                        if (c != '\\') {
1326                                if ((c >= '0') && (c <= last_exp)) {
1327                                        const regmatch_t *subexp = ex + (c - '0');
1328                                        unsigned int sublen = subexp->rm_eo - subexp->rm_so;
1329
1330                                        /*  Range checking  */
1331                                        if (sublen > outsize)
1332                                                sublen = outsize;
1333                                        strncpy(output, devname + subexp->rm_so, sublen);
1334                                        output += sublen;
1335                                        outsize -= sublen;
1336                                }
1337                                continue;
1338                        }
1339                }
1340                *output = c;
1341                ++output;
1342                --outsize;
1343        } /* while */
1344}   /*  End Function expand_regexp  */
1345
1346
1347/* from compat_name.c */
1348
1349struct translate_struct {
1350        const char *match;    /*  The string to match to(up to length)                */
1351        const char *format;   /*  Format of output, "%s" takes data past match string,
1352                        NULL is effectively "%s"(just more efficient)       */
1353};
1354
1355static struct translate_struct translate_table[] =
1356{
1357        {"sound/",     NULL},
1358        {"printers/",  "lp%s"},
1359        {"v4l/",       NULL},
1360        {"parports/",  "parport%s"},
1361        {"fb/",        "fb%s"},
1362        {"netlink/",   NULL},
1363        {"loop/",      "loop%s"},
1364        {"floppy/",    "fd%s"},
1365        {"rd/",        "ram%s"},
1366        {"md/",        "md%s"},         /*  Meta-devices                         */
1367        {"vc/",        "tty%s"},
1368        {"misc/",      NULL},
1369        {"isdn/",      NULL},
1370        {"pg/",        "pg%s"},         /*  Parallel port generic ATAPI interface*/
1371        {"i2c/",       "i2c-%s"},
1372        {"staliomem/", "staliomem%s"},  /*  Stallion serial driver control       */
1373        {"tts/E",      "ttyE%s"},       /*  Stallion serial driver               */
1374        {"cua/E",      "cue%s"},        /*  Stallion serial driver callout       */
1375        {"tts/R",      "ttyR%s"},       /*  Rocketport serial driver             */
1376        {"cua/R",      "cur%s"},        /*  Rocketport serial driver callout     */
1377        {"ip2/",       "ip2%s"},        /*  Computone serial driver control      */
1378        {"tts/F",      "ttyF%s"},       /*  Computone serial driver              */
1379        {"cua/F",      "cuf%s"},        /*  Computone serial driver callout      */
1380        {"tts/C",      "ttyC%s"},       /*  Cyclades serial driver               */
1381        {"cua/C",      "cub%s"},        /*  Cyclades serial driver callout       */
1382        {"tts/",       "ttyS%s"},       /*  Generic serial: must be after others */
1383        {"cua/",       "cua%s"},        /*  Generic serial: must be after others */
1384        {"input/js",   "js%s"},         /*  Joystick driver                      */
1385        {NULL,         NULL}
1386};
1387
1388const char *get_old_name(const char *devname, unsigned int namelen,
1389                        char *buffer, unsigned int major, unsigned int minor)
1390/*  [SUMMARY] Translate a kernel-supplied name into an old name.
1391    <devname> The device name provided by the kernel.
1392    <namelen> The length of the name.
1393    <buffer> A buffer that may be used. This should be at least 128 bytes long.
1394    <major> The major number for the device.
1395    <minor> The minor number for the device.
1396    [RETURNS] A pointer to the old name if known, else NULL.
1397*/
1398{
1399        const char *compat_name = NULL;
1400        const char *ptr;
1401        struct translate_struct *trans;
1402        unsigned int i;
1403        char mode;
1404        int indexx;
1405        const char *pty1;
1406        const char *pty2;
1407        /* 1 to 5  "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */
1408        static const char *const fmt[] = {
1409                NULL ,
1410                "sg%u",                 /* scsi/generic */
1411                NULL,                   /* scsi/disc */
1412                "sr%u",                 /* scsi/cd */
1413                NULL,                   /* scsi/part */
1414                "nst%u%c",              /* scsi/mt */
1415                "hd%c"  ,               /* ide/host/disc */
1416                "hd%c"  ,               /* ide/host/cd */
1417                "hd%c%s",               /* ide/host/part */
1418                "%sht%d",               /* ide/host/mt */
1419                "sbpcd%u",              /* sbp/ */
1420                "vcs%s",                /* vcc/ */
1421                "%cty%c%c",             /* pty/ */
1422                NULL
1423        };
1424
1425        for (trans = translate_table; trans->match != NULL; ++trans) {
1426                char *after_match = is_prefixed_with(devname, trans->match);
1427                if (after_match) {
1428                        if (trans->format == NULL)
1429                                return after_match;
1430                        sprintf(buffer, trans->format, after_match);
1431                        return buffer;
1432                }
1433        }
1434
1435        ptr = bb_basename(devname);
1436        i = scan_dev_name(devname, namelen, ptr);
1437
1438        if (i > 0 && i < 13)
1439                compat_name = buffer;
1440        else
1441                return NULL;
1442
1443        /* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */
1444        if (i == 1 || i == 3 || i == 10)
1445                sprintf(buffer, fmt[i], minor);
1446
1447        /* 2 ==scsi/disc, 4 == scsi/part */
1448        if (i == 2 || i == 4)
1449                compat_name = write_old_sd_name(buffer, major, minor, ((i == 2) ? "" : (ptr + 4)));
1450
1451        /* 5 == scsi/mt */
1452        if (i == 5) {
1453                mode = ptr[2];
1454                if (mode == 'n')
1455                        mode = '\0';
1456                sprintf(buffer, fmt[i], minor & 0x1f, mode);
1457                if (devname[namelen - 1] != 'n')
1458                        ++compat_name;
1459        }
1460        /* 6 == ide/host/disc, 7 == ide/host/cd, 8 == ide/host/part */
1461        if (i == 6 || i == 7 || i == 8)
1462                /* last arg should be ignored for i == 6 or i== 7 */
1463                sprintf(buffer, fmt[i] , get_old_ide_name(major, minor), ptr + 4);
1464
1465        /* 9 ==  ide/host/mt */
1466        if (i == 9)
1467                sprintf(buffer, fmt[i], ptr + 2, minor & 0x7f);
1468
1469        /*  11 == vcc/ */
1470        if (i == 11) {
1471                sprintf(buffer, fmt[i], devname + 4);
1472                if (buffer[3] == '0')
1473                        buffer[3] = '\0';
1474        }
1475        /* 12 ==  pty/ */
1476        if (i == 12) {
1477                pty1 = "pqrstuvwxyzabcde";
1478                pty2 = "0123456789abcdef";
1479                indexx = atoi(devname + 5);
1480                sprintf(buffer, fmt[i], (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]);
1481        }
1482        return compat_name;
1483}   /*  End Function get_old_name  */
1484
1485static char get_old_ide_name(unsigned int major, unsigned int minor)
1486/*  [SUMMARY] Get the old IDE name for a device.
1487    <major> The major number for the device.
1488    <minor> The minor number for the device.
1489    [RETURNS] The drive letter.
1490*/
1491{
1492        char letter = 'y';      /* 121 */
1493        char c = 'a';           /*  97 */
1494        int i = IDE0_MAJOR;
1495
1496        /* I hope it works like the previous code as it saves a few bytes. Tito ;P */
1497        do {
1498                if (i == IDE0_MAJOR || i == IDE1_MAJOR || i == IDE2_MAJOR
1499                 || i == IDE3_MAJOR || i == IDE4_MAJOR || i == IDE5_MAJOR
1500                 || i == IDE6_MAJOR || i == IDE7_MAJOR || i == IDE8_MAJOR
1501                 || i == IDE9_MAJOR
1502                ) {
1503                        if ((unsigned int)i == major) {
1504                                letter = c;
1505                                break;
1506                        }
1507                        c += 2;
1508                }
1509                i++;
1510        } while (i <= IDE9_MAJOR);
1511
1512        if (minor > 63)
1513                ++letter;
1514        return letter;
1515}   /*  End Function get_old_ide_name  */
1516
1517static char *write_old_sd_name(char *buffer,
1518                                unsigned int major, unsigned int minor,
1519                                const char *part)
1520/*  [SUMMARY] Write the old SCSI disc name to a buffer.
1521    <buffer> The buffer to write to.
1522    <major> The major number for the device.
1523    <minor> The minor number for the device.
1524    <part> The partition string. Must be "" for a whole-disc entry.
1525    [RETURNS] A pointer to the buffer on success, else NULL.
1526*/
1527{
1528        unsigned int disc_index;
1529
1530        if (major == 8) {
1531                sprintf(buffer, "sd%c%s", 'a' + (minor >> 4), part);
1532                return buffer;
1533        }
1534        if ((major > 64) && (major < 72)) {
1535                disc_index = ((major - 64) << 4) +(minor >> 4);
1536                if (disc_index < 26)
1537                        sprintf(buffer, "sd%c%s", 'a' + disc_index, part);
1538                else
1539                        sprintf(buffer, "sd%c%c%s", 'a' +(disc_index / 26) - 1, 'a' + disc_index % 26, part);
1540                return buffer;
1541        }
1542        return NULL;
1543}   /*  End Function write_old_sd_name  */
1544
1545
1546/*  expression.c */
1547
1548/*EXPERIMENTAL_FUNCTION*/
1549
1550int st_expr_expand(char *output, unsigned int length, const char *input,
1551                const char *(*get_variable_func)(const char *variable,
1552                                                void *info),
1553                void *info)
1554/*  [SUMMARY] Expand an expression using Borne Shell-like unquoted rules.
1555    <output> The output expanded expression is written here.
1556    <length> The size of the output buffer.
1557    <input> The input expression. This may equal <<output>>.
1558    <get_variable> A function which will be used to get variable values. If
1559    this returns NULL, the environment is searched instead. If this is NULL,
1560    only the environment is searched.
1561    <info> An arbitrary pointer passed to <<get_variable>>.
1562    [RETURNS] TRUE on success, else FALSE.
1563*/
1564{
1565        char ch;
1566        unsigned int len;
1567        unsigned int out_pos = 0;
1568        const char *env;
1569        const char *ptr;
1570        struct passwd *pwent;
1571        char buffer[BUFFER_SIZE], tmp[STRING_LENGTH];
1572
1573        if (length > BUFFER_SIZE)
1574                length = BUFFER_SIZE;
1575        for (; TRUE; ++input) {
1576                switch (ch = *input) {
1577                        case '$':
1578                                /*  Variable expansion  */
1579                                input = expand_variable(buffer, length, &out_pos, ++input, get_variable_func, info);
1580                                if (input == NULL)
1581                                        return FALSE;
1582                                break;
1583                        case '~':
1584                                /*  Home directory expansion  */
1585                                ch = input[1];
1586                                if (isspace(ch) ||(ch == '/') ||(ch == '\0')) {
1587                                        /* User's own home directory: leave separator for next time */
1588                                        env = getenv("HOME");
1589                                        if (env == NULL) {
1590                                                info_logger(LOG_INFO, bb_msg_variable_not_found, "HOME");
1591                                                return FALSE;
1592                                        }
1593                                        len = strlen(env);
1594                                        if (len + out_pos >= length)
1595                                                goto st_expr_expand_out;
1596                                        memcpy(buffer + out_pos, env, len + 1);
1597                                        out_pos += len;
1598                                        continue;
1599                                }
1600                                /*  Someone else's home directory  */
1601                                for (ptr = ++input; !isspace(ch) && (ch != '/') && (ch != '\0'); ch = *++ptr)
1602                                        /* VOID */;
1603                                len = ptr - input;
1604                                if (len >= sizeof tmp)
1605                                        goto st_expr_expand_out;
1606                                safe_memcpy(tmp, input, len);
1607                                input = ptr - 1;
1608                                pwent = getpwnam(tmp);
1609                                if (pwent == NULL) {
1610                                        info_logger(LOG_INFO, "no pwent for: %s", tmp);
1611                                        return FALSE;
1612                                }
1613                                len = strlen(pwent->pw_dir);
1614                                if (len + out_pos >= length)
1615                                        goto st_expr_expand_out;
1616                                memcpy(buffer + out_pos, pwent->pw_dir, len + 1);
1617                                out_pos += len;
1618                                break;
1619                        case '\0':
1620                        /* Falltrough */
1621                        default:
1622                                if (out_pos >= length)
1623                                        goto st_expr_expand_out;
1624                                buffer[out_pos++] = ch;
1625                                if (ch == '\0') {
1626                                        memcpy(output, buffer, out_pos);
1627                                        return TRUE;
1628                                }
1629                                break;
1630                        /* esac */
1631                }
1632        }
1633        return FALSE;
1634st_expr_expand_out:
1635        info_logger(LOG_INFO, bb_msg_small_buffer);
1636        return FALSE;
1637}   /*  End Function st_expr_expand  */
1638
1639
1640/*  Private functions follow  */
1641
1642static const char *expand_variable(char *buffer, unsigned int length,
1643                                unsigned int *out_pos, const char *input,
1644                                const char *(*func)(const char *variable,
1645                                                        void *info),
1646                                void *info)
1647/*  [SUMMARY] Expand a variable.
1648    <buffer> The buffer to write to.
1649    <length> The length of the output buffer.
1650    <out_pos> The current output position. This is updated.
1651    <input> A pointer to the input character pointer.
1652    <func> A function which will be used to get variable values. If this
1653    returns NULL, the environment is searched instead. If this is NULL, only
1654    the environment is searched.
1655    <info> An arbitrary pointer passed to <<func>>.
1656    <errfp> Diagnostic messages are written here.
1657    [RETURNS] A pointer to the end of this subexpression on success, else NULL.
1658*/
1659{
1660        char ch;
1661        int len;
1662        unsigned int open_braces;
1663        const char *env, *ptr;
1664        char tmp[STRING_LENGTH];
1665
1666        ch = input[0];
1667        if (ch == '$') {
1668                /*  Special case for "$$": PID  */
1669                sprintf(tmp, "%d", (int) getpid());
1670                len = strlen(tmp);
1671                if (len + *out_pos >= length)
1672                        goto expand_variable_out;
1673
1674                memcpy(buffer + *out_pos, tmp, len + 1);
1675                out_pos += len;
1676                return input;
1677        }
1678        /*  Ordinary variable expansion, possibly in braces  */
1679        if (ch != '{') {
1680                /*  Simple variable expansion  */
1681                for (ptr = input; isalnum(ch) || (ch == '_') || (ch == ':'); ch = *++ptr)
1682                        /* VOID */;
1683                len = ptr - input;
1684                if ((size_t)len >= sizeof tmp)
1685                        goto expand_variable_out;
1686
1687                safe_memcpy(tmp, input, len);
1688                input = ptr - 1;
1689                env = get_variable_v2(tmp, func, info);
1690                if (env == NULL) {
1691                        info_logger(LOG_INFO, bb_msg_variable_not_found, tmp);
1692                        return NULL;
1693                }
1694                len = strlen(env);
1695                if (len + *out_pos >= length)
1696                        goto expand_variable_out;
1697
1698                memcpy(buffer + *out_pos, env, len + 1);
1699                *out_pos += len;
1700                return input;
1701        }
1702        /*  Variable in braces: check for ':' tricks  */
1703        ch = *++input;
1704        for (ptr = input; isalnum(ch) || (ch == '_'); ch = *++ptr)
1705                /* VOID */;
1706        if (ch == '}') {
1707                /*  Must be simple variable expansion with "${var}"  */
1708                len = ptr - input;
1709                if ((size_t)len >= sizeof tmp)
1710                        goto expand_variable_out;
1711
1712                safe_memcpy(tmp, input, len);
1713                ptr = expand_variable(buffer, length, out_pos, tmp, func, info);
1714                if (ptr == NULL)
1715                        return NULL;
1716                return input + len;
1717        }
1718        if (ch != ':' || ptr[1] != '-') {
1719                info_logger(LOG_INFO, "illegal char in var name");
1720                return NULL;
1721        }
1722        /*  It's that handy "${var:-word}" expression. Check if var is defined  */
1723        len = ptr - input;
1724        if ((size_t)len >= sizeof tmp)
1725                goto expand_variable_out;
1726
1727        safe_memcpy(tmp, input, len);
1728        /*  Move input pointer to ':'  */
1729        input = ptr;
1730        /*  First skip to closing brace, taking note of nested expressions  */
1731        ptr += 2;
1732        ch = ptr[0];
1733        for (open_braces = 1; open_braces > 0; ch = *++ptr) {
1734                switch (ch) {
1735                        case '{':
1736                                ++open_braces;
1737                                break;
1738                        case '}':
1739                                --open_braces;
1740                                break;
1741                        case '\0':
1742                                info_logger(LOG_INFO, "\"}\" not found in: %s", input);
1743                                return NULL;
1744                        default:
1745                                break;
1746                }
1747        }
1748        --ptr;
1749        /*  At this point ptr should point to closing brace of "${var:-word}"  */
1750        env = get_variable_v2(tmp, func, info);
1751        if (env != NULL) {
1752                /*  Found environment variable, so skip the input to the closing brace
1753                        and return the variable  */
1754                input = ptr;
1755                len = strlen(env);
1756                if (len + *out_pos >= length)
1757                        goto expand_variable_out;
1758
1759                memcpy(buffer + *out_pos, env, len + 1);
1760                *out_pos += len;
1761                return input;
1762        }
1763        /*  Environment variable was not found, so process word. Advance input
1764        pointer to start of word in "${var:-word}"  */
1765        input += 2;
1766        len = ptr - input;
1767        if ((size_t)len >= sizeof tmp)
1768                goto expand_variable_out;
1769
1770        safe_memcpy(tmp, input, len);
1771        input = ptr;
1772        if (!st_expr_expand(tmp, STRING_LENGTH, tmp, func, info))
1773                return NULL;
1774        len = strlen(tmp);
1775        if (len + *out_pos >= length)
1776                goto expand_variable_out;
1777
1778        memcpy(buffer + *out_pos, tmp, len + 1);
1779        *out_pos += len;
1780        return input;
1781expand_variable_out:
1782        info_logger(LOG_INFO, bb_msg_small_buffer);
1783        return NULL;
1784}   /*  End Function expand_variable  */
1785
1786
1787static const char *get_variable_v2(const char *variable,
1788                                const char *(*func)(const char *variable, void *info),
1789                                void *info)
1790/*  [SUMMARY] Get a variable from the environment or .
1791    <variable> The variable name.
1792    <func> A function which will be used to get the variable. If this returns
1793    NULL, the environment is searched instead. If this is NULL, only the
1794    environment is searched.
1795    [RETURNS] The value of the variable on success, else NULL.
1796*/
1797{
1798        const char *value;
1799
1800        if (func != NULL) {
1801                value = (*func)(variable, info);
1802                if (value != NULL)
1803                        return value;
1804        }
1805        return getenv(variable);
1806}   /*  End Function get_variable  */
1807
1808/* END OF CODE */
1809