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