source: MondoRescue/branches/stable/mindi-busybox/miscutils/devfsd.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 12 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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