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