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 sbuf[sizeof(int)*3 + 2]; /* sign and NUL */
1146        static char *hostname;
1147
1148        struct get_variable_info *gv_info = info;
1149        const char *field_names[] = {
1150                        "hostname", "mntpt", "devpath", "devname",
1151                        "uid", "gid", "mode", hostname, mount_point,
1152                        gv_info->devpath, gv_info->devname, NULL
1153        };
1154        int i;
1155
1156        if (!hostname)
1157                hostname = safe_gethostname();
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                sprintf(sbuf, "%u", gv_info->info->uid);
1168        else if (i == 5)
1169                sprintf(sbuf, "%u", gv_info->info->gid);
1170        else if (i == 6)
1171                sprintf(sbuf, "%o", gv_info->info->mode);
1172        return sbuf;
1173}   /*  End Function get_variable  */
1174
1175static void service(struct stat statbuf, char *path)
1176{
1177        struct devfsd_notify_struct info;
1178
1179        memset(&info, 0, sizeof info);
1180        info.type = DEVFSD_NOTIFY_REGISTERED;
1181        info.mode = statbuf.st_mode;
1182        info.major = major(statbuf.st_rdev);
1183        info.minor = minor(statbuf.st_rdev);
1184        info.uid = statbuf.st_uid;
1185        info.gid = statbuf.st_gid;
1186        snprintf(info.devname, sizeof(info.devname), "%s", path + strlen(mount_point) + 1);
1187        info.namelen = strlen(info.devname);
1188        service_name(&info);
1189        if (S_ISDIR(statbuf.st_mode))
1190                dir_operation(SERVICE, path, 0, NULL);
1191}
1192
1193static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask)
1194/*  [SUMMARY] Scan a directory tree and generate register events on leaf nodes.
1195        <flag> To choose which function to perform
1196        <dp> The directory pointer. This is closed upon completion.
1197    <dir_name> The name of the directory.
1198        <rootlen> string length parameter.
1199    [RETURNS] Nothing.
1200*/
1201{
1202        struct stat statbuf;
1203        DIR *dp;
1204        struct dirent *de;
1205        char *path;
1206
1207        dp = warn_opendir(dir_name);
1208        if (dp == NULL)
1209                return;
1210
1211        while ((de = readdir(dp)) != NULL) {
1212
1213                if (de->d_name && DOT_OR_DOTDOT(de->d_name))
1214                        continue;
1215                path = concat_path_file(dir_name, de->d_name);
1216                if (lstat(path, &statbuf) == 0) {
1217                        switch (type) {
1218                                case SERVICE:
1219                                        service(statbuf, path);
1220                                        break;
1221                                case RESTORE:
1222                                        restore(path, statbuf, var);
1223                                        break;
1224                                case READ_CONFIG:
1225                                        read_config_file(path, var, event_mask);
1226                                        break;
1227                        }
1228                }
1229                free(path);
1230        }
1231        closedir(dp);
1232}   /*  End Function do_scan_and_service  */
1233
1234static int mksymlink(const char *oldpath, const char *newpath)
1235/*  [SUMMARY] Create a symlink, creating intervening directories as required.
1236    <oldpath> The string contained in the symlink.
1237    <newpath> The name of the new symlink.
1238    [RETURNS] 0 on success, else -1.
1239*/
1240{
1241        if (!make_dir_tree(newpath))
1242                return -1;
1243
1244        if (symlink(oldpath, newpath) != 0) {
1245                if (errno != EEXIST)
1246                        return -1;
1247        }
1248        return 0;
1249}   /*  End Function mksymlink  */
1250
1251
1252static int make_dir_tree(const char *path)
1253/*  [SUMMARY] Creating intervening directories for a path as required.
1254    <path> The full pathname(including the leaf node).
1255    [RETURNS] TRUE on success, else FALSE.
1256*/
1257{
1258        if (bb_make_directory(dirname((char *)path), -1, FILEUTILS_RECUR) == -1)
1259                return FALSE;
1260        return TRUE;
1261} /*  End Function make_dir_tree  */
1262
1263static int expand_expression(char *output, unsigned int outsize,
1264                        const char *input,
1265                        const char *(*get_variable_func)(const char *variable, void *info),
1266                        void *info,
1267                        const char *devname,
1268                        const regmatch_t *ex, unsigned int numexp)
1269/*  [SUMMARY] Expand environment variables and regular subexpressions in string.
1270    <output> The output expanded expression is written here.
1271    <length> The size of the output buffer.
1272    <input> The input expression. This may equal <<output>>.
1273    <get_variable> A function which will be used to get variable values. If
1274    this returns NULL, the environment is searched instead. If this is NULL,
1275    only the environment is searched.
1276    <info> An arbitrary pointer passed to <<get_variable>>.
1277    <devname> Device name; specifically, this is the string that contains all
1278    of the regular subexpressions.
1279    <ex> Array of start / end offsets into info->devname for each subexpression
1280    <numexp> Number of regular subexpressions found in <<devname>>.
1281    [RETURNS] TRUE on success, else FALSE.
1282*/
1283{
1284        char temp[STRING_LENGTH];
1285
1286        if (!st_expr_expand(temp, STRING_LENGTH, input, get_variable_func, info))
1287                return FALSE;
1288        expand_regexp(output, outsize, temp, devname, ex, numexp);
1289        return TRUE;
1290}   /*  End Function expand_expression  */
1291
1292static void expand_regexp(char *output, size_t outsize, const char *input,
1293                        const char *devname,
1294                        const regmatch_t *ex, unsigned int numex)
1295/*  [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9.
1296    <output> The output expanded expression is written here.
1297    <outsize> The size of the output buffer.
1298    <input> The input expression. This may NOT equal <<output>>, because
1299    supporting that would require yet another string-copy. However, it's not
1300    hard to write a simple wrapper function to add this functionality for those
1301    few cases that need it.
1302    <devname> Device name; specifically, this is the string that contains all
1303    of the regular subexpressions.
1304    <ex> An array of start and end offsets into <<devname>>, one for each
1305    subexpression
1306    <numex> Number of subexpressions in the offset-array <<ex>>.
1307    [RETURNS] Nothing.
1308*/
1309{
1310        const char last_exp = '0' - 1 + numex;
1311        int c = -1;
1312
1313        /*  Guarantee NULL termination by writing an explicit '\0' character into
1314        the very last byte  */
1315        if (outsize)
1316                output[--outsize] = '\0';
1317        /*  Copy the input string into the output buffer, replacing '\\' with '\'
1318        and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x
1319        codes are deleted  */
1320        while ((c != '\0') && (outsize != 0)) {
1321                c = *input;
1322                ++input;
1323                if (c == '\\') {
1324                        c = *input;
1325                        ++input;
1326                        if (c != '\\') {
1327                                if ((c >= '0') && (c <= last_exp)) {
1328                                        const regmatch_t *subexp = ex + (c - '0');
1329                                        unsigned int sublen = subexp->rm_eo - subexp->rm_so;
1330
1331                                        /*  Range checking  */
1332                                        if (sublen > outsize)
1333                                                sublen = outsize;
1334                                        strncpy(output, devname + subexp->rm_so, sublen);
1335                                        output += sublen;
1336                                        outsize -= sublen;
1337                                }
1338                                continue;
1339                        }
1340                }
1341                *output = c;
1342                ++output;
1343                --outsize;
1344        } /* while */
1345}   /*  End Function expand_regexp  */
1346
1347
1348/* from compat_name.c */
1349
1350struct translate_struct {
1351        const char *match;    /*  The string to match to(up to length)                */
1352        const char *format;   /*  Format of output, "%s" takes data past match string,
1353                        NULL is effectively "%s"(just more efficient)       */
1354};
1355
1356static struct translate_struct translate_table[] =
1357{
1358        {"sound/",     NULL},
1359        {"printers/",  "lp%s"},
1360        {"v4l/",       NULL},
1361        {"parports/",  "parport%s"},
1362        {"fb/",        "fb%s"},
1363        {"netlink/",   NULL},
1364        {"loop/",      "loop%s"},
1365        {"floppy/",    "fd%s"},
1366        {"rd/",        "ram%s"},
1367        {"md/",        "md%s"},         /*  Meta-devices                         */
1368        {"vc/",        "tty%s"},
1369        {"misc/",      NULL},
1370        {"isdn/",      NULL},
1371        {"pg/",        "pg%s"},         /*  Parallel port generic ATAPI interface*/
1372        {"i2c/",       "i2c-%s"},
1373        {"staliomem/", "staliomem%s"},  /*  Stallion serial driver control       */
1374        {"tts/E",      "ttyE%s"},       /*  Stallion serial driver               */
1375        {"cua/E",      "cue%s"},        /*  Stallion serial driver callout       */
1376        {"tts/R",      "ttyR%s"},       /*  Rocketport serial driver             */
1377        {"cua/R",      "cur%s"},        /*  Rocketport serial driver callout     */
1378        {"ip2/",       "ip2%s"},        /*  Computone serial driver control      */
1379        {"tts/F",      "ttyF%s"},       /*  Computone serial driver              */
1380        {"cua/F",      "cuf%s"},        /*  Computone serial driver callout      */
1381        {"tts/C",      "ttyC%s"},       /*  Cyclades serial driver               */
1382        {"cua/C",      "cub%s"},        /*  Cyclades serial driver callout       */
1383        {"tts/",       "ttyS%s"},       /*  Generic serial: must be after others */
1384        {"cua/",       "cua%s"},        /*  Generic serial: must be after others */
1385        {"input/js",   "js%s"},         /*  Joystick driver                      */
1386        {NULL,         NULL}
1387};
1388
1389const char *get_old_name(const char *devname, unsigned int namelen,
1390                        char *buffer, unsigned int major, unsigned int minor)
1391/*  [SUMMARY] Translate a kernel-supplied name into an old name.
1392    <devname> The device name provided by the kernel.
1393    <namelen> The length of the name.
1394    <buffer> A buffer that may be used. This should be at least 128 bytes long.
1395    <major> The major number for the device.
1396    <minor> The minor number for the device.
1397    [RETURNS] A pointer to the old name if known, else NULL.
1398*/
1399{
1400        const char *compat_name = NULL;
1401        const char *ptr;
1402        struct translate_struct *trans;
1403        unsigned int i;
1404        char mode;
1405        int indexx;
1406        const char *pty1;
1407        const char *pty2;
1408        size_t len;
1409        /* 1 to 5  "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */
1410        static const char *const fmt[] = {
1411                NULL ,
1412                "sg%u",                 /* scsi/generic */
1413                NULL,                   /* scsi/disc */
1414                "sr%u",                 /* scsi/cd */
1415                NULL,                   /* scsi/part */
1416                "nst%u%c",              /* scsi/mt */
1417                "hd%c"  ,               /* ide/host/disc */
1418                "hd%c"  ,               /* ide/host/cd */
1419                "hd%c%s",               /* ide/host/part */
1420                "%sht%d",               /* ide/host/mt */
1421                "sbpcd%u",              /* sbp/ */
1422                "vcs%s",                /* vcc/ */
1423                "%cty%c%c",             /* pty/ */
1424                NULL
1425        };
1426
1427        for (trans = translate_table; trans->match != NULL; ++trans) {
1428                len = strlen(trans->match);
1429
1430                if (strncmp(devname, trans->match, len) == 0) {
1431                        if (trans->format == NULL)
1432                                return devname + len;
1433                        sprintf(buffer, trans->format, devname + len);
1434                        return buffer;
1435                }
1436        }
1437
1438        ptr = bb_basename(devname);
1439        i = scan_dev_name(devname, namelen, ptr);
1440
1441        if (i > 0 && i < 13)
1442                compat_name = buffer;
1443        else
1444                return NULL;
1445
1446        /* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */
1447        if (i == 1 || i == 3 || i == 10)
1448                sprintf(buffer, fmt[i], minor);
1449
1450        /* 2 ==scsi/disc, 4 == scsi/part */
1451        if (i == 2 || i == 4)
1452                compat_name = write_old_sd_name(buffer, major, minor, ((i == 2) ? "" : (ptr + 4)));
1453
1454        /* 5 == scsi/mt */
1455        if (i == 5) {
1456                mode = ptr[2];
1457                if (mode == 'n')
1458                        mode = '\0';
1459                sprintf(buffer, fmt[i], minor & 0x1f, mode);
1460                if (devname[namelen - 1] != 'n')
1461                        ++compat_name;
1462        }
1463        /* 6 == ide/host/disc, 7 == ide/host/cd, 8 == ide/host/part */
1464        if (i == 6 || i == 7 || i == 8)
1465                /* last arg should be ignored for i == 6 or i== 7 */
1466                sprintf(buffer, fmt[i] , get_old_ide_name(major, minor), ptr + 4);
1467
1468        /* 9 ==  ide/host/mt */
1469        if (i == 9)
1470                sprintf(buffer, fmt[i], ptr + 2, minor & 0x7f);
1471
1472        /*  11 == vcc/ */
1473        if (i == 11) {
1474                sprintf(buffer, fmt[i], devname + 4);
1475                if (buffer[3] == '0')
1476                        buffer[3] = '\0';
1477        }
1478        /* 12 ==  pty/ */
1479        if (i == 12) {
1480                pty1 = "pqrstuvwxyzabcde";
1481                pty2 = "0123456789abcdef";
1482                indexx = atoi(devname + 5);
1483                sprintf(buffer, fmt[i], (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]);
1484        }
1485        return compat_name;
1486}   /*  End Function get_old_name  */
1487
1488static char get_old_ide_name(unsigned int major, unsigned int minor)
1489/*  [SUMMARY] Get the old IDE name for a device.
1490    <major> The major number for the device.
1491    <minor> The minor number for the device.
1492    [RETURNS] The drive letter.
1493*/
1494{
1495        char letter = 'y';      /* 121 */
1496        char c = 'a';           /*  97 */
1497        int i = IDE0_MAJOR;
1498
1499        /* I hope it works like the previous code as it saves a few bytes. Tito ;P */
1500        do {
1501                if (i == IDE0_MAJOR || i == IDE1_MAJOR || i == IDE2_MAJOR
1502                 || i == IDE3_MAJOR || i == IDE4_MAJOR || i == IDE5_MAJOR
1503                 || i == IDE6_MAJOR || i == IDE7_MAJOR || i == IDE8_MAJOR
1504                 || i == IDE9_MAJOR
1505                ) {
1506                        if ((unsigned int)i == major) {
1507                                letter = c;
1508                                break;
1509                        }
1510                        c += 2;
1511                }
1512                i++;
1513        } while (i <= IDE9_MAJOR);
1514
1515        if (minor > 63)
1516                ++letter;
1517        return letter;
1518}   /*  End Function get_old_ide_name  */
1519
1520static char *write_old_sd_name(char *buffer,
1521                                unsigned int major, unsigned int minor,
1522                                const char *part)
1523/*  [SUMMARY] Write the old SCSI disc name to a buffer.
1524    <buffer> The buffer to write to.
1525    <major> The major number for the device.
1526    <minor> The minor number for the device.
1527    <part> The partition string. Must be "" for a whole-disc entry.
1528    [RETURNS] A pointer to the buffer on success, else NULL.
1529*/
1530{
1531        unsigned int disc_index;
1532
1533        if (major == 8) {
1534                sprintf(buffer, "sd%c%s", 'a' + (minor >> 4), part);
1535                return buffer;
1536        }
1537        if ((major > 64) && (major < 72)) {
1538                disc_index = ((major - 64) << 4) +(minor >> 4);
1539                if (disc_index < 26)
1540                        sprintf(buffer, "sd%c%s", 'a' + disc_index, part);
1541                else
1542                        sprintf(buffer, "sd%c%c%s", 'a' +(disc_index / 26) - 1, 'a' + disc_index % 26, part);
1543                return buffer;
1544        }
1545        return NULL;
1546}   /*  End Function write_old_sd_name  */
1547
1548
1549/*  expression.c */
1550
1551/*EXPERIMENTAL_FUNCTION*/
1552
1553int st_expr_expand(char *output, unsigned int length, const char *input,
1554                const char *(*get_variable_func)(const char *variable,
1555                                                void *info),
1556                void *info)
1557/*  [SUMMARY] Expand an expression using Borne Shell-like unquoted rules.
1558    <output> The output expanded expression is written here.
1559    <length> The size of the output buffer.
1560    <input> The input expression. This may equal <<output>>.
1561    <get_variable> A function which will be used to get variable values. If
1562    this returns NULL, the environment is searched instead. If this is NULL,
1563    only the environment is searched.
1564    <info> An arbitrary pointer passed to <<get_variable>>.
1565    [RETURNS] TRUE on success, else FALSE.
1566*/
1567{
1568        char ch;
1569        unsigned int len;
1570        unsigned int out_pos = 0;
1571        const char *env;
1572        const char *ptr;
1573        struct passwd *pwent;
1574        char buffer[BUFFER_SIZE], tmp[STRING_LENGTH];
1575
1576        if (length > BUFFER_SIZE)
1577                length = BUFFER_SIZE;
1578        for (; TRUE; ++input) {
1579                switch (ch = *input) {
1580                        case '$':
1581                                /*  Variable expansion  */
1582                                input = expand_variable(buffer, length, &out_pos, ++input, get_variable_func, info);
1583                                if (input == NULL)
1584                                        return FALSE;
1585                                break;
1586                        case '~':
1587                                /*  Home directory expansion  */
1588                                ch = input[1];
1589                                if (isspace(ch) ||(ch == '/') ||(ch == '\0')) {
1590                                        /* User's own home directory: leave separator for next time */
1591                                        env = getenv("HOME");
1592                                        if (env == NULL) {
1593                                                info_logger(LOG_INFO, bb_msg_variable_not_found, "HOME");
1594                                                return FALSE;
1595                                        }
1596                                        len = strlen(env);
1597                                        if (len + out_pos >= length)
1598                                                goto st_expr_expand_out;
1599                                        memcpy(buffer + out_pos, env, len + 1);
1600                                        out_pos += len;
1601                                        continue;
1602                                }
1603                                /*  Someone else's home directory  */
1604                                for (ptr = ++input; !isspace(ch) && (ch != '/') && (ch != '\0'); ch = *++ptr)
1605                                        /* VOID */;
1606                                len = ptr - input;
1607                                if (len >= sizeof tmp)
1608                                        goto st_expr_expand_out;
1609                                safe_memcpy(tmp, input, len);
1610                                input = ptr - 1;
1611                                pwent = getpwnam(tmp);
1612                                if (pwent == NULL) {
1613                                        info_logger(LOG_INFO, "no pwent for: %s", tmp);
1614                                        return FALSE;
1615                                }
1616                                len = strlen(pwent->pw_dir);
1617                                if (len + out_pos >= length)
1618                                        goto st_expr_expand_out;
1619                                memcpy(buffer + out_pos, pwent->pw_dir, len + 1);
1620                                out_pos += len;
1621                                break;
1622                        case '\0':
1623                        /* Falltrough */
1624                        default:
1625                                if (out_pos >= length)
1626                                        goto st_expr_expand_out;
1627                                buffer[out_pos++] = ch;
1628                                if (ch == '\0') {
1629                                        memcpy(output, buffer, out_pos);
1630                                        return TRUE;
1631                                }
1632                                break;
1633                        /* esac */
1634                }
1635        }
1636        return FALSE;
1637st_expr_expand_out:
1638        info_logger(LOG_INFO, bb_msg_small_buffer);
1639        return FALSE;
1640}   /*  End Function st_expr_expand  */
1641
1642
1643/*  Private functions follow  */
1644
1645static const char *expand_variable(char *buffer, unsigned int length,
1646                                unsigned int *out_pos, const char *input,
1647                                const char *(*func)(const char *variable,
1648                                                        void *info),
1649                                void *info)
1650/*  [SUMMARY] Expand a variable.
1651    <buffer> The buffer to write to.
1652    <length> The length of the output buffer.
1653    <out_pos> The current output position. This is updated.
1654    <input> A pointer to the input character pointer.
1655    <func> A function which will be used to get variable values. If this
1656    returns NULL, the environment is searched instead. If this is NULL, only
1657    the environment is searched.
1658    <info> An arbitrary pointer passed to <<func>>.
1659    <errfp> Diagnostic messages are written here.
1660    [RETURNS] A pointer to the end of this subexpression on success, else NULL.
1661*/
1662{
1663        char ch;
1664        int len;
1665        unsigned int open_braces;
1666        const char *env, *ptr;
1667        char tmp[STRING_LENGTH];
1668
1669        ch = input[0];
1670        if (ch == '$') {
1671                /*  Special case for "$$": PID  */
1672                sprintf(tmp, "%d", (int) getpid());
1673                len = strlen(tmp);
1674                if (len + *out_pos >= length)
1675                        goto expand_variable_out;
1676
1677                memcpy(buffer + *out_pos, tmp, len + 1);
1678                out_pos += len;
1679                return input;
1680        }
1681        /*  Ordinary variable expansion, possibly in braces  */
1682        if (ch != '{') {
1683                /*  Simple variable expansion  */
1684                for (ptr = input; isalnum(ch) || (ch == '_') || (ch == ':'); ch = *++ptr)
1685                        /* VOID */;
1686                len = ptr - input;
1687                if ((size_t)len >= sizeof tmp)
1688                        goto expand_variable_out;
1689
1690                safe_memcpy(tmp, input, len);
1691                input = ptr - 1;
1692                env = get_variable_v2(tmp, func, info);
1693                if (env == NULL) {
1694                        info_logger(LOG_INFO, bb_msg_variable_not_found, tmp);
1695                        return NULL;
1696                }
1697                len = strlen(env);
1698                if (len + *out_pos >= length)
1699                        goto expand_variable_out;
1700
1701                memcpy(buffer + *out_pos, env, len + 1);
1702                *out_pos += len;
1703                return input;
1704        }
1705        /*  Variable in braces: check for ':' tricks  */
1706        ch = *++input;
1707        for (ptr = input; isalnum(ch) || (ch == '_'); ch = *++ptr)
1708                /* VOID */;
1709        if (ch == '}') {
1710                /*  Must be simple variable expansion with "${var}"  */
1711                len = ptr - input;
1712                if ((size_t)len >= sizeof tmp)
1713                        goto expand_variable_out;
1714
1715                safe_memcpy(tmp, input, len);
1716                ptr = expand_variable(buffer, length, out_pos, tmp, func, info);
1717                if (ptr == NULL)
1718                        return NULL;
1719                return input + len;
1720        }
1721        if (ch != ':' || ptr[1] != '-') {
1722                info_logger(LOG_INFO, "illegal char in var name");
1723                return NULL;
1724        }
1725        /*  It's that handy "${var:-word}" expression. Check if var is defined  */
1726        len = ptr - input;
1727        if ((size_t)len >= sizeof tmp)
1728                goto expand_variable_out;
1729
1730        safe_memcpy(tmp, input, len);
1731        /*  Move input pointer to ':'  */
1732        input = ptr;
1733        /*  First skip to closing brace, taking note of nested expressions  */
1734        ptr += 2;
1735        ch = ptr[0];
1736        for (open_braces = 1; open_braces > 0; ch = *++ptr) {
1737                switch (ch) {
1738                        case '{':
1739                                ++open_braces;
1740                                break;
1741                        case '}':
1742                                --open_braces;
1743                                break;
1744                        case '\0':
1745                                info_logger(LOG_INFO, "\"}\" not found in: %s", input);
1746                                return NULL;
1747                        default:
1748                                break;
1749                }
1750        }
1751        --ptr;
1752        /*  At this point ptr should point to closing brace of "${var:-word}"  */
1753        env = get_variable_v2(tmp, func, info);
1754        if (env != NULL) {
1755                /*  Found environment variable, so skip the input to the closing brace
1756                        and return the variable  */
1757                input = ptr;
1758                len = strlen(env);
1759                if (len + *out_pos >= length)
1760                        goto expand_variable_out;
1761
1762                memcpy(buffer + *out_pos, env, len + 1);
1763                *out_pos += len;
1764                return input;
1765        }
1766        /*  Environment variable was not found, so process word. Advance input
1767        pointer to start of word in "${var:-word}"  */
1768        input += 2;
1769        len = ptr - input;
1770        if ((size_t)len >= sizeof tmp)
1771                goto expand_variable_out;
1772
1773        safe_memcpy(tmp, input, len);
1774        input = ptr;
1775        if (!st_expr_expand(tmp, STRING_LENGTH, tmp, func, info))
1776                return NULL;
1777        len = strlen(tmp);
1778        if (len + *out_pos >= length)
1779                goto expand_variable_out;
1780
1781        memcpy(buffer + *out_pos, tmp, len + 1);
1782        *out_pos += len;
1783        return input;
1784expand_variable_out:
1785        info_logger(LOG_INFO, bb_msg_small_buffer);
1786        return NULL;
1787}   /*  End Function expand_variable  */
1788
1789
1790static const char *get_variable_v2(const char *variable,
1791                                const char *(*func)(const char *variable, void *info),
1792                                void *info)
1793/*  [SUMMARY] Get a variable from the environment or .
1794    <variable> The variable name.
1795    <func> A function which will be used to get the variable. If this returns
1796    NULL, the environment is searched instead. If this is NULL, only the
1797    environment is searched.
1798    [RETURNS] The value of the variable on success, else NULL.
1799*/
1800{
1801        const char *value;
1802
1803        if (func != NULL) {
1804                value = (*func)(variable, info);
1805                if (value != NULL)
1806                        return value;
1807        }
1808        return getenv(variable);
1809}   /*  End Function get_variable  */
1810
1811/* END OF CODE */
1812