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