2 devfsd implementation for busybox
4 Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
6 Busybox version is based on some previous work and ideas
7 Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
11 Main file for devfsd (devfs daemon for Linux).
13 Copyright (C) 1998-2002 Richard Gooch
17 Header file for devfsd (devfs daemon for Linux).
19 Copyright (C) 1998-2000 Richard Gooch
23 Compatibility name file for devfsd (build compatibility names).
25 Copyright (C) 1998-2002 Richard Gooch
29 This code provides Borne Shell-like expression expansion.
31 Copyright (C) 1997-1999 Richard Gooch
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.
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.
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.
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.
62 #include <sys/types.h>
64 #include <sys/ioctl.h>
65 #include <sys/socket.h>
73 #include <sys/sysmacros.h>
76 /* Various defines taken from linux/major.h */
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 */
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 */
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
133 /* for get_uid_gid() */
137 /* for msg_logger(), do_ioctl(),
138 fork_and_execute() */
142 /* for dir_operation() */
145 #define READ_CONFIG 2
147 /* Update only after changing code to reflect new protocol */
148 #define DEVFSD_PROTOCOL_REVISION_DAEMON 5
150 /* Compile-time check */
151 #if DEVFSD_PROTOCOL_REVISION_KERNEL != DEVFSD_PROTOCOL_REVISION_DAEMON
152 #error protocol version mismatch. Update your kernel headers
155 #define AC_PERMISSIONS 0
158 #define AC_MFUNCTION 3 /* not supported by busybox */
159 #define AC_CFUNCTION 4 /* not supported by busybox */
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
169 struct permissions_type
178 char *argv[MAX_ARGS + 1]; /* argv[0] must always be the programme */
184 const char *destination;
193 struct config_entry_struct
195 struct action_type action;
199 struct permissions_type permissions;
200 struct execute_type execute;
201 struct copy_type copy;
204 struct config_entry_struct *next;
207 struct get_variable_info
209 const struct devfsd_notify_struct *info;
211 char devpath[STRING_LENGTH];
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);
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 *);
248 /* busybox functions */
249 #if defined(CONFIG_DEVFSD_VERBOSE) || defined(CONFIG_DEBUG)
250 static void msg_logger(int die, int pri, const char * fmt, ... );
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);
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
269 } initial_symlinks[] =
271 {"/proc/self/fd", "fd"},
278 static struct event_type
280 unsigned int type; /* The DEVFSD_NOTIFY_* value */
281 const char *config_name; /* The name used in the config file */
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"},
295 /* busybox functions and messages */
297 extern void xregcomp(regex_t * preg, const char *regex, int cflags);
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";
306 #if defined(CONFIG_DEVFSD_VERBOSE) || defined(CONFIG_DEBUG)
307 static void msg_logger(int die, int pri, const char * fmt, ... )
312 if (access ("/dev/log", F_OK) == 0)
314 openlog(bb_applet_name, 0, LOG_DAEMON);
315 vsyslog( pri , fmt , ap);
321 bb_verror_msg(fmt, ap);
328 static void do_ioctl(int die, int fd, int request, unsigned long event_mask_flag)
330 #ifdef CONFIG_DEVFSD_VERBOSE
331 if (ioctl (fd, request, event_mask_flag) == -1)
332 msg_logger(die, LOG_ERR, "ioctl(): %m\n");
334 if (ioctl (fd, request, event_mask_flag) == -1)
339 static void fork_and_execute(int die, char *arg0, char **arg )
347 /* Parent: Error : die or return */
348 #ifdef CONFIG_DEVFSD_VERBOSE
349 msg_logger(die, LOG_ERR,(char *) bb_msg_memory_exhausted);
356 /* Parent : ok : return or exit */
364 /* Child : if arg0 != NULL do execvp */
368 #ifdef CONFIG_DEVFSD_VERBOSE
369 msg_logger(DIE, LOG_ERR, "execvp(): %s: %m\n", arg0);
376 static void safe_memcpy( char *dest, const char *src, int len)
378 memcpy (dest , src , len );
382 static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, char *ptr)
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);
396 static unsigned int scan_dev_name(const char *d, unsigned int n, char *ptr)
398 if(d[0]=='s' && d[1]=='c' && d[2]=='s' && d[3]=='i' && d[4]=='/')
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' &&
404 return scan_dev_name_common(d, n, 0, ptr);
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')
409 return scan_dev_name_common(d, n, 4, ptr);
411 else if(d[0]=='s' && d[1]=='b' && d[2]=='p' && d[3]=='/')
415 else if(d[0]=='v' && d[1]=='c' && d[2]=='c' && d[3]=='/')
419 else if(d[0]=='p' && d[1]=='t' && d[2]=='y' && d[3]=='/')
426 /* Public functions follow */
428 int devfsd_main (int argc, char **argv)
430 int print_version = FALSE;
431 #ifdef CONFIG_DEVFSD_FG_NP
432 int do_daemon = TRUE;
433 int no_polling = FALSE;
436 int fd, proto_rev, count;
437 unsigned long event_mask = 0;
438 struct sigaction new_action;
439 struct initial_symlink_struct *curr;
444 for (count = 2; count < argc; ++count)
446 if(argv[count][0] == '-')
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 */
453 else if(argv[count][1]=='n' && argv[count][2]=='p' && !argv[count][3]) /* -np */
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';
465 mount_point = argv[1];
467 if (chdir (mount_point) != 0)
468 #ifdef CONFIG_DEVFSD_VERBOSE
469 bb_error_msg_and_die( " %s: %m", mount_point);
474 fd = bb_xopen (".devfsd", O_RDONLY);
476 if (fcntl (fd, F_SETFD, FD_CLOEXEC) != 0)
477 #ifdef CONFIG_DEVFSD_VERBOSE
478 bb_error_msg( "FD_CLOEXEC");
483 do_ioctl(DIE, fd, DEVFSDIOC_GET_PROTO_REV,(int )&proto_rev);
485 /*setup initial entries */
486 for (curr = initial_symlinks; curr->dest != NULL; ++curr)
487 symlink (curr->dest, curr->name);
489 /* NB: The check for CONFIG_FILE is done in read_config_file() */
491 if ( print_version || (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev) )
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 */
500 /* Tell kernel we are special (i.e. we get to see hidden entries) */
501 do_ioctl(DIE, fd, DEVFSDIOC_SET_EVENT_MASK, 0);
503 sigemptyset (&new_action.sa_mask);
504 new_action.sa_flags = 0;
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()");
515 bb_printf("%s v%s started for %s\n",bb_applet_name, DEVFSD_VERSION, mount_point);
517 /* Set umask so that mknod(2), open(2) and mkdir(2) have complete control over permissions */
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
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
535 setpgid (0, 0); /* Become process group leader */
540 do_scan = do_servicing (fd, event_mask);
543 read_config_file (CONFIG_FILE, FALSE, &event_mask);
545 dir_operation(SERVICE,mount_point,0,NULL);
547 } /* End Function main */
550 /* Private functions follow */
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.
563 char buf[STRING_LENGTH];
567 msg_logger( NO_DIE, LOG_INFO, "read_config_file(): %s\n", path);
569 if (stat (path, &statbuf) != 0 || statbuf.st_size == 0 )
570 goto read_config_file_err;
572 if ( S_ISDIR (statbuf.st_mode) )
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';
578 dir_operation(READ_CONFIG, path, 0, event_mask);
582 if ( ( fp = fopen (path, "r") ) != NULL )
584 while (fgets (buf, STRING_LENGTH, fp) != NULL)
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.
592 /*buf[strlen (buf) - 1] = '\0';*/
593 /* Skip whitespace */
594 for (line = buf; isspace (*line); ++line)
596 if (line[0] == '\0' || line[0] == '#' )
598 process_config_line (line, event_mask);
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);
607 if(optional == 0 && errno == ENOENT)
611 } /* End Function read_config_file */
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.
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];
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 };
636 msg_logger( NO_DIE, LOG_INFO, "process_config_line()\n");
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",
642 p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
644 i = compare_string_array(options, when );
655 goto process_config_line_err;
657 /* "INCLUDE" & "OPTIONAL_INCLUDE" */
658 if( i == 1 || i == 2 )
660 st_expr_expand (name, STRING_LENGTH, name, get_variable, NULL );
662 msg_logger( NO_DIE, LOG_INFO, "%sinclude: %s\n",(toupper (when[0]) == 'I') ? "": "optional_", name);
664 read_config_file (name, (toupper (when[0]) == 'I') ? FALSE : TRUE, event_mask);
670 dir_operation(RESTORE,name, strlen (name),NULL);
674 goto process_config_line_err;
676 new = xmalloc (sizeof *new);
677 memset (new, 0, sizeof *new);
679 for (count = 0; event_types[count].config_name != NULL; ++count)
681 if (strcasecmp (when, event_types[count].config_name) != 0)
683 new->action.when = event_types[count].type;
686 if (event_types[count].config_name == NULL)
689 goto process_config_line_err;
692 i = compare_string_array(options, what );
696 case 4: /* "PERMISSIONS" */
697 new->action.what = AC_PERMISSIONS;
698 /* Get user and group */
699 if ( ( ptr = strchr (p[0], '.') ) == NULL )
702 goto process_config_line_err; /*"missing '.' in UID.GID */
706 new->u.permissions.uid = get_uid_gid (UID, p[0]);
707 new->u.permissions.gid = get_uid_gid (GID, ptr);
709 new->u.permissions.mode = get_mode (p[1]);
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;
719 case 6: /* EXECUTE */
720 new->action.what = AC_EXECUTE;
723 for (count = 0; count < num_args; ++count)
724 new->u.execute.argv[count] = bb_xstrdup (p[count]);
726 new->u.execute.argv[num_args] = NULL;
729 new->action.what = AC_COPY;
732 goto process_config_line_err; /* missing path and function in line */
734 new->u.copy.source = bb_xstrdup (p[0]);
735 new->u.copy.destination = bb_xstrdup (p[1]);
739 case 9: /* MKOLDCOMPAT */
741 case 10: /* MKNEWCOMPAT */
743 case 11:/* RMOLDCOMPAT */
745 case 12: /* RMNEWCOMPAT */
751 new->action.what = i - 2;
755 goto process_config_line_err;
759 xregcomp( &new->preg, name, REG_EXTENDED);
761 *event_mask |= 1 << new->action.when;
763 if (first_config == NULL)
766 last_config->next = new;
769 process_config_line_err:
770 #ifdef CONFIG_DEVFSD_VERBOSE
771 msg_logger( DIE, LOG_ERR, bb_msg_bad_config, msg , line);
775 } /* End Function process_config_line */
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.
785 struct devfsd_notify_struct info;
786 unsigned long tmp_event_mask;
789 msg_logger( NO_DIE, LOG_INFO, "do_servicing()\n");
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)
797 bytes = read (fd, (char *) &info, sizeof info);
799 break; /* Must test for this first */
801 continue; /* Yes, the order is important */
804 service_name (&info);
808 int c_sighup = caught_sighup;
810 caught_signal = FALSE;
811 caught_sighup = FALSE;
814 #ifdef CONFIG_DEVFSD_VERBOSE
815 msg_logger( NO_DIE, LOG_ERR, "read error on control file: %m\n");
817 /* This is to shut up a compiler warning */
819 } /* End Function do_servicing */
821 static void service_name (const struct devfsd_notify_struct *info)
822 /* [SUMMARY] Service a single devfs change.
823 <info> The devfs change.
828 regmatch_t mbuf[MAX_SUBEXPR];
829 struct config_entry_struct *entry;
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);
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]=='t' &&
843 info->devname[4]=='c' && info->devname[5]=='t' &&
844 info->devname[6]=='l' && !info->devname[7])))
846 for (entry = first_config; entry != NULL; entry = entry->next)
848 /* First check if action matches the type, then check if name matches */
849 if (info->type != entry->action.when || regexec (&entry->preg, info->devname, MAX_SUBEXPR, mbuf, 0) != 0 )
851 for (n = 0; (n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n)
854 msg_logger( NO_DIE, LOG_INFO, "service_name(): action.what %d\n", entry->action.what);
856 switch (entry->action.what)
859 action_permissions (info, entry);
861 #ifdef CONFIG_DEVFSD_MODLOAD
863 action_modload (info, entry);
867 action_execute (info, entry, mbuf, n);
870 action_copy (info, entry, mbuf, n);
879 action_compat (info, entry->action.what);
882 #ifdef CONFIG_DEVFSD_VERBOSE
883 msg_logger( DIE, LOG_ERR, "Unknown action\n");
890 } /* End Function service_name */
892 static void action_permissions (const struct devfsd_notify_struct *info,
893 const struct config_entry_struct *entry)
894 /* [SUMMARY] Update permissions for a device entry.
895 <info> The devfs change.
896 <entry> The config file entry.
903 msg_logger( NO_DIE, LOG_INFO, "action_permission()\n");
906 if ( stat (info->devname, &statbuf) != 0 ||
907 chmod (info->devname,(statbuf.st_mode & S_IFMT) | (entry->u.permissions.mode & ~S_IFMT)) != 0 ||
908 chown (info->devname, entry->u.permissions.uid, entry->u.permissions.gid) != 0)
910 #ifdef CONFIG_DEVFSD_VERBOSE
911 msg_logger( NO_DIE, LOG_ERR, "chmod() or chown(): %s: %m\n",info->devname);
916 } /* End Function action_permissions */
918 #ifdef CONFIG_DEVFSD_MODLOAD
919 static void action_modload (const struct devfsd_notify_struct *info,
920 const struct config_entry_struct *entry)
921 /* [SUMMARY] Load a module.
922 <info> The devfs change.
923 <entry> The config file entry.
928 char device[STRING_LENGTH];
931 argv[1] = MODPROBE_SWITCH_1; /* "-k" */
932 argv[2] = MODPROBE_SWITCH_2; /* "-C" */
933 argv[3] = CONFIG_MODULES_DEVFS;
937 snprintf (device, sizeof (device), "/dev/%s", info->devname);
939 msg_logger( NO_DIE, LOG_INFO, "action_modload():%s %s %s %s %s\n",argv[0],argv[1],argv[2],argv[3],argv[4]);
941 fork_and_execute(DIE, argv[0], argv);
942 } /* End Function action_modload */
945 static void action_execute (const struct devfsd_notify_struct *info,
946 const struct config_entry_struct *entry,
947 const regmatch_t *regexpr, unsigned int numexpr)
948 /* [SUMMARY] Execute a programme.
949 <info> The devfs change.
950 <entry> The config file entry.
951 <regexpr> The number of subexpression (start, end) offsets within the
953 <numexpr> The number of elements within <<regexpr>>.
958 struct get_variable_info gv_info;
959 char *argv[MAX_ARGS + 1];
960 char largv[MAX_ARGS + 1][STRING_LENGTH];
968 gv_info.devname = info->devname;
969 snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
970 for (count = 0; entry->u.execute.argv[count] != NULL; ++count)
972 expand_expression (largv[count], STRING_LENGTH,
973 entry->u.execute.argv[count],
974 get_variable, &gv_info,
975 gv_info.devname, regexpr, numexpr );
976 argv[count] = largv[count];
982 for(i=0;argv[i]!=NULL;i++) /* argv[i] < MAX_ARGS + 1 */
985 if( (strlen(buff)+ 1 + strlen(argv[i])) >= 512)
987 strcat(buff,argv[i]);
990 msg_logger( NO_DIE, LOG_INFO, "action_execute(): %s",buff);
993 fork_and_execute(NO_DIE, argv[0], argv);
994 } /* End Function action_execute */
997 static void action_copy (const struct devfsd_notify_struct *info,
998 const struct config_entry_struct *entry,
999 const regmatch_t *regexpr, unsigned int numexpr)
1000 /* [SUMMARY] Copy permissions.
1001 <info> The devfs change.
1002 <entry> The config file entry.
1003 <regexpr> This list of subexpression (start, end) offsets within the
1005 <numexpr> The number of elements in <<regexpr>>.
1010 struct get_variable_info gv_info;
1011 struct stat source_stat, dest_stat;
1012 char source[STRING_LENGTH], destination[STRING_LENGTH];
1013 dest_stat.st_mode = 0;
1016 msg_logger( NO_DIE, LOG_INFO, "action_copy()\n");
1019 if ( (info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK (info->mode) )
1021 gv_info.info = info;
1022 gv_info.devname = info->devname;
1024 snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
1025 expand_expression (source, STRING_LENGTH, entry->u.copy.source,
1026 get_variable, &gv_info, gv_info.devname,
1029 expand_expression (destination, STRING_LENGTH, entry->u.copy.destination,
1030 get_variable, &gv_info, gv_info.devname,
1033 if ( !make_dir_tree (destination) || lstat (source, &source_stat) != 0)
1035 lstat (destination, &dest_stat);
1036 new_mode = source_stat.st_mode & ~S_ISVTX;
1037 if (info->type == DEVFSD_NOTIFY_CREATE)
1038 new_mode |= S_ISVTX;
1039 else if ( (info->type == DEVFSD_NOTIFY_CHANGE) && (dest_stat.st_mode & S_ISVTX) )
1040 new_mode |= S_ISVTX;
1042 if ( !copy_inode (destination, &dest_stat, new_mode, source, &source_stat) && (errno != EEXIST))
1043 msg_logger( NO_DIE, LOG_ERR, "copy_inode(): %s to %s: %m\n", source, destination);
1045 copy_inode (destination, &dest_stat, new_mode, source, &source_stat);
1048 } /* End Function action_copy */
1050 static void action_compat (const struct devfsd_notify_struct *info, unsigned int action)
1051 /* [SUMMARY] Process a compatibility request.
1052 <info> The devfs change.
1053 <action> The action to take.
1057 const char *compat_name = NULL;
1058 const char *dest_name = info->devname;
1060 char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH];
1061 int mode, host, bus, target, lun;
1064 /* 1 to 5 "scsi/" , 6 to 9 "ide/host" */
1065 const char *fmt[] = { NULL ,
1066 "sg/c%db%dt%du%d", /* scsi/generic */
1067 "sd/c%db%dt%du%d", /* scsi/disc */
1068 "sr/c%db%dt%du%d", /* scsi/cd */
1069 "sd/c%db%dt%du%dp%d", /* scsi/part */
1070 "st/c%db%dt%du%dm%d%c", /* scsi/mt */
1071 "ide/hd/c%db%dt%du%d", /* ide/host/disc */
1072 "ide/cd/c%db%dt%du%d", /* ide/host/cd */
1073 "ide/hd/c%db%dt%du%dp%d", /* ide/host/part */
1074 "ide/mt/c%db%dt%du%d%s", /* ide/host/mt */
1077 /* First construct compatibility name */
1080 case AC_MKOLDCOMPAT:
1081 case AC_RMOLDCOMPAT:
1082 compat_name = get_old_name (info->devname, info->namelen, compat_buf, info->major, info->minor);
1084 case AC_MKNEWCOMPAT:
1085 case AC_RMNEWCOMPAT:
1086 ptr = strrchr (info->devname, '/') + 1;
1087 i=scan_dev_name(info->devname, info->namelen, ptr);
1090 msg_logger( NO_DIE, LOG_INFO, "action_compat(): scan_dev_name() returned %d\n", i);
1097 sscanf (info->devname +((i<6)?5:4), "host%d/bus%d/target%d/lun%d/", &host, &bus, &target, &lun);
1098 snprintf (dest_buf, sizeof (dest_buf), "../%s", info->devname + ((i>5)?4:0));
1099 dest_name = dest_buf;
1100 compat_name = compat_buf;
1103 /* 1 == scsi/generic 2 == scsi/disc 3 == scsi/cd 6 == ide/host/disc 7 == ide/host/cd */
1104 if( i == 1 || i == 2 || i == 3 || i == 6 || i ==7 )
1105 sprintf ( compat_buf, fmt[i], host, bus, target, lun);
1107 /* 4 == scsi/part 8 == ide/host/part */
1108 if( i == 4 || i == 8)
1109 sprintf ( compat_buf, fmt[i], host, bus, target, lun, atoi (ptr + 4) );
1114 rewind_ = info->devname[info->namelen - 1];
1118 if(ptr[2] == 'l' /*108*/ || ptr[2] == 'm'/*109*/)
1119 mode = ptr[2] - 107; /* 1 or 2 */
1122 sprintf (compat_buf, fmt [i], host, bus, target, lun, mode, rewind_);
1125 /* 9 == ide/host/mt */
1127 snprintf (compat_buf, sizeof (compat_buf), fmt[i], host, bus, target, lun, ptr + 2);
1129 } /* switch(action) */
1131 if(compat_name == NULL )
1135 msg_logger( NO_DIE, LOG_INFO, "action_compat(): %s\n", compat_name);
1138 /* Now decide what to do with it */
1141 case AC_MKOLDCOMPAT:
1142 case AC_MKNEWCOMPAT:
1143 mksymlink (dest_name, compat_name);
1145 case AC_RMOLDCOMPAT:
1146 case AC_RMNEWCOMPAT:
1148 if (unlink (compat_name) != 0)
1149 msg_logger( NO_DIE, LOG_ERR, "unlink(): %s: %m\n", compat_name);
1151 unlink (compat_name);
1155 } /* switch(action) */
1156 } /* End Function action_compat */
1158 static void restore(char *spath, struct stat source_stat, int rootlen)
1160 char dpath[STRING_LENGTH];
1161 struct stat dest_stat;
1164 msg_logger( NO_DIE, LOG_INFO, "restore()\n");
1167 dest_stat.st_mode = 0;
1168 snprintf (dpath, sizeof dpath, "%s%s", mount_point, spath + rootlen);
1169 lstat (dpath, &dest_stat);
1171 if ( S_ISLNK (source_stat.st_mode) || (source_stat.st_mode & S_ISVTX) )
1172 copy_inode (dpath, &dest_stat, (source_stat.st_mode & ~S_ISVTX) , spath, &source_stat);
1174 if ( S_ISDIR (source_stat.st_mode) )
1175 dir_operation(RESTORE, spath, rootlen,NULL);
1179 static int copy_inode (const char *destpath, const struct stat *dest_stat,
1181 const char *sourcepath, const struct stat *source_stat)
1182 /* [SUMMARY] Copy an inode.
1183 <destpath> The destination path. An existing inode may be deleted.
1184 <dest_stat> The destination stat(2) information.
1185 <new_mode> The desired new mode for the destination.
1186 <sourcepath> The source path.
1187 <source_stat> The source stat(2) information.
1188 [RETURNS] TRUE on success, else FALSE.
1191 int source_len, dest_len;
1192 char source_link[STRING_LENGTH], dest_link[STRING_LENGTH];
1194 struct sockaddr_un un_addr;
1195 char symlink_val[STRING_LENGTH];
1198 msg_logger( NO_DIE, LOG_INFO, "copy_inode()\n");
1201 if ( (source_stat->st_mode & S_IFMT) == (dest_stat->st_mode & S_IFMT) )
1204 if ( S_ISLNK (source_stat->st_mode) )
1206 if (( source_len = readlink (sourcepath, source_link, STRING_LENGTH - 1) ) < 0 ||
1207 ( dest_len = readlink (destpath , dest_link , STRING_LENGTH - 1) ) < 0 )
1209 source_link[source_len] = '\0';
1210 dest_link[dest_len] = '\0';
1211 if ( (source_len != dest_len) || (strcmp (source_link, dest_link) != 0) )
1214 symlink (source_link, destpath);
1217 } /* Else not a symlink */
1218 chmod (destpath, new_mode & ~S_IFMT);
1219 chown (destpath, source_stat->st_uid, source_stat->st_gid);
1222 /* Different types: unlink and create */
1224 switch (source_stat->st_mode & S_IFMT)
1227 if ( ( fd = socket (AF_UNIX, SOCK_STREAM, 0) ) < 0 )
1229 un_addr.sun_family = AF_UNIX;
1230 snprintf (un_addr.sun_path, sizeof (un_addr.sun_path), "%s", destpath);
1231 val = bind (fd, (struct sockaddr *) &un_addr, (int) sizeof un_addr);
1233 if (val != 0 || chmod (destpath, new_mode & ~S_IFMT) != 0)
1237 if ( ( val = readlink (sourcepath, symlink_val, STRING_LENGTH - 1) ) < 0 )
1239 symlink_val[val] = '\0';
1240 if (symlink (symlink_val, destpath) == 0)
1244 if ( ( fd = open (destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT) ) < 0 )
1247 if (chmod (destpath, new_mode & ~S_IFMT) != 0)
1253 if (mknod (destpath, new_mode, source_stat->st_rdev) != 0)
1257 if (mkdir (destpath, new_mode & ~S_IFMT) != 0)
1260 if (chown (destpath, source_stat->st_uid, source_stat->st_gid) == 0)
1265 } /* End Function copy_inode */
1267 static void free_config ()
1268 /* [SUMMARY] Free the configuration information.
1272 struct config_entry_struct *c_entry;
1276 msg_logger( NO_DIE, LOG_INFO, "free_config()\n");
1279 for (c_entry = first_config; c_entry != NULL; c_entry = next)
1283 next = c_entry->next;
1284 regfree (&c_entry->preg);
1285 if (c_entry->action.what == AC_EXECUTE)
1287 for (count = 0; count < MAX_ARGS; ++count)
1289 if (c_entry->u.execute.argv[count] == NULL)
1291 free (c_entry->u.execute.argv[count]);
1296 first_config = NULL;
1298 } /* End Function free_config */
1300 static int get_uid_gid (int flag, const char *string)
1301 /* [SUMMARY] Convert a string to a UID or GID value.
1302 <flag> "UID" or "GID".
1303 <string> The string.
1304 [RETURNS] The UID or GID value.
1307 struct passwd *pw_ent;
1308 struct group *grp_ent;
1309 #ifdef CONFIG_DEVFSD_VERBOSE
1314 msg_logger( NO_DIE, LOG_INFO, "get_uid_gid()\n");
1317 if(flag != UID && flag != GID )
1318 msg_logger( DIE, LOG_ERR,"get_uid_gid(): flag != UID && flag != GID\n");
1321 if ( isdigit (string[0]) || ( (string[0] == '-') && isdigit (string[1]) ) )
1322 return atoi (string);
1324 if ( flag == UID && ( pw_ent = getpwnam (string) ) != NULL )
1325 return (pw_ent->pw_uid);
1327 if ( flag == GID && ( grp_ent = getgrnam (string) ) != NULL )
1328 return (grp_ent->gr_gid);
1329 #ifdef CONFIG_DEVFSD_VERBOSE
1333 msg_logger( NO_DIE, LOG_ERR,"unknown %s: %s, defaulting to %cID=0\n", msg, string, msg[0] - 32);
1336 }/* End Function get_uid_gid */
1338 static mode_t get_mode (const char *string)
1339 /* [SUMMARY] Convert a string to a mode value.
1340 <string> The string.
1341 [RETURNS] The mode value.
1347 msg_logger( NO_DIE, LOG_INFO, "get_mode()\n");
1350 if ( isdigit (string[0]) )
1351 return strtoul (string, NULL, 8);
1352 if (strlen (string) != 9)
1353 #ifdef CONFIG_DEVFSD_VERBOSE
1354 msg_logger( DIE, LOG_ERR, "bad mode: %s\n", string);
1362 if(string[0]=='r'||string[0]=='w'||string[0]=='x')
1368 } /* End Function get_mode */
1370 static void signal_handler (int sig)
1373 msg_logger( NO_DIE, LOG_INFO, "signal_handler()\n");
1376 caught_signal = TRUE;
1378 caught_sighup = TRUE;
1379 #ifdef CONFIG_DEVFSD_VERBOSE
1380 msg_logger( NO_DIE, LOG_INFO, "Caught %s\n",(sig == SIGHUP)?"SIGHUP" : "SIGUSR1");
1382 } /* End Function signal_handler */
1384 static const char *get_variable (const char *variable, void *info)
1386 struct get_variable_info *gv_info = info;
1387 static char hostname[STRING_LENGTH], sbuf[STRING_LENGTH];
1388 const char *field_names[] = { "hostname", "mntpt", "devpath", "devname",
1389 "uid", "gid", "mode", hostname, mount_point,
1390 gv_info->devpath, gv_info->devname, 0 };
1393 msg_logger( NO_DIE, LOG_INFO, "get_variable()\n");
1396 if (gethostname (hostname, STRING_LENGTH - 1) != 0)
1397 #ifdef CONFIG_DEVFSD_VERBOSE
1398 msg_logger( DIE, LOG_ERR, "gethostname(): %m\n");
1402 /* Here on error we should do exit(RV_SYS_ERROR), instead we do exit(EXIT_FAILURE) */
1403 hostname[STRING_LENGTH - 1] = '\0';
1405 /* compare_string_array returns i>=0 */
1406 i=compare_string_array(field_names, variable);
1408 if ( i > 6 && (i > 1 && gv_info == NULL))
1410 if( i >= 0 || i <= 3)
1413 msg_logger( NO_DIE, LOG_INFO, "get_variable(): i=%d %s\n",i ,field_names[i+7]);
1415 return(field_names[i+7]);
1419 sprintf (sbuf, "%u", gv_info->info->uid);
1421 sprintf (sbuf, "%u", gv_info->info->gid);
1423 sprintf (sbuf, "%o", gv_info->info->mode);
1425 msg_logger( NO_DIE, LOG_INFO, "get_variable(): %s\n", sbuf);
1428 } /* End Function get_variable */
1430 static void service(struct stat statbuf, char *path)
1432 struct devfsd_notify_struct info;
1435 msg_logger( NO_DIE, LOG_INFO, "service()\n");
1438 memset (&info, 0, sizeof info);
1439 info.type = DEVFSD_NOTIFY_REGISTERED;
1440 info.mode = statbuf.st_mode;
1441 info.major = major (statbuf.st_rdev);
1442 info.minor = minor (statbuf.st_rdev);
1443 info.uid = statbuf.st_uid;
1444 info.gid = statbuf.st_gid;
1445 snprintf (info.devname, sizeof (info.devname), "%s", path + strlen (mount_point) + 1);
1446 info.namelen = strlen (info.devname);
1447 service_name (&info);
1448 if ( S_ISDIR (statbuf.st_mode) )
1449 dir_operation(SERVICE,path,0,NULL);
1452 static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask)
1453 /* [SUMMARY] Scan a directory tree and generate register events on leaf nodes.
1454 <flag> To choose which function to perform
1455 <dp> The directory pointer. This is closed upon completion.
1456 <dir_name> The name of the directory.
1457 <rootlen> string length parameter.
1461 struct stat statbuf;
1464 char path[STRING_LENGTH];
1468 msg_logger( NO_DIE, LOG_INFO, "dir_operation()\n");
1471 if((dp = opendir( dir_name))==NULL)
1474 msg_logger( NO_DIE, LOG_ERR, "opendir(): %s: %m\n", dir_name);
1479 while ( (de = readdir (dp) ) != NULL )
1482 if(de->d_name && *de->d_name == '.' && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])))
1484 snprintf (path, sizeof (path), "%s/%s", dir_name, de->d_name);
1486 msg_logger( NO_DIE, LOG_ERR, "dir_operation(): %s\n", path);
1489 if (lstat (path, &statbuf) != 0)
1492 msg_logger( NO_DIE, LOG_ERR, "%s: %m\n", path);
1499 service(statbuf,path);
1502 restore(path, statbuf, var);
1505 read_config_file (path, var, event_mask);
1510 } /* End Function do_scan_and_service */
1512 static int mksymlink (const char *oldpath, const char *newpath)
1513 /* [SUMMARY] Create a symlink, creating intervening directories as required.
1514 <oldpath> The string contained in the symlink.
1515 <newpath> The name of the new symlink.
1516 [RETURNS] 0 on success, else -1.
1520 msg_logger( NO_DIE, LOG_INFO, "mksymlink()\n", newpath);
1524 if ( !make_dir_tree (newpath) )
1527 if (symlink (oldpath, newpath) != 0)
1529 if (errno != EEXIST)
1532 msg_logger( NO_DIE, LOG_ERR, "mksymlink(): %s to %s: %m\n", oldpath, newpath);
1538 } /* End Function mksymlink */
1541 static int make_dir_tree (const char *path)
1542 /* [SUMMARY] Creating intervening directories for a path as required.
1543 <path> The full pathname (including the leaf node).
1544 [RETURNS] TRUE on success, else FALSE.
1548 msg_logger( NO_DIE, LOG_INFO, "make_dir_tree()\n");
1550 if (bb_make_directory( dirname((char *)path), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ,FILEUTILS_RECUR )==-1)
1553 msg_logger( NO_DIE, LOG_ERR, "make_dir_tree(): %s: %m\n", path);
1558 } /* End Function make_dir_tree */
1560 static int expand_expression(char *output, unsigned int outsize,
1562 const char *(*get_variable_func)(const char *variable, void *info),
1564 const char *devname,
1565 const regmatch_t *ex, unsigned int numexp)
1566 /* [SUMMARY] Expand environment variables and regular subexpressions in string.
1567 <output> The output expanded expression is written here.
1568 <length> The size of the output buffer.
1569 <input> The input expression. This may equal <<output>>.
1570 <get_variable> A function which will be used to get variable values. If
1571 this returns NULL, the environment is searched instead. If this is NULL,
1572 only the environment is searched.
1573 <info> An arbitrary pointer passed to <<get_variable>>.
1574 <devname> Device name; specifically, this is the string that contains all
1575 of the regular subexpressions.
1576 <ex> Array of start / end offsets into info->devname for each subexpression
1577 <numexp> Number of regular subexpressions found in <<devname>>.
1578 [RETURNS] TRUE on success, else FALSE.
1581 char temp[STRING_LENGTH];
1584 msg_logger( NO_DIE, LOG_INFO, "expand_expression()\n");
1587 if ( !st_expr_expand (temp, STRING_LENGTH, input, get_variable_func, info) )
1589 expand_regexp (output, outsize, temp, devname, ex, numexp);
1591 } /* End Function expand_expression */
1593 static void expand_regexp (char *output, size_t outsize, const char *input,
1594 const char *devname,
1595 const regmatch_t *ex, unsigned int numex )
1596 /* [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9.
1597 <output> The output expanded expression is written here.
1598 <outsize> The size of the output buffer.
1599 <input> The input expression. This may NOT equal <<output>>, because
1600 supporting that would require yet another string-copy. However, it's not
1601 hard to write a simple wrapper function to add this functionality for those
1602 few cases that need it.
1603 <devname> Device name; specifically, this is the string that contains all
1604 of the regular subexpressions.
1605 <ex> An array of start and end offsets into <<devname>>, one for each
1607 <numex> Number of subexpressions in the offset-array <<ex>>.
1611 const char last_exp = '0' - 1 + numex;
1615 msg_logger( NO_DIE, LOG_INFO, "expand_regexp()\n");
1618 /* Guarantee NULL termination by writing an explicit '\0' character into
1619 the very last byte */
1621 output[--outsize] = '\0';
1622 /* Copy the input string into the output buffer, replacing '\\' with '\'
1623 and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x
1624 codes are deleted */
1625 while ( (c != '\0') && (outsize != 0) )
1635 if ((c >= '0') && (c <= last_exp))
1637 const regmatch_t *subexp = ex + (c - '0');
1638 unsigned int sublen = subexp->rm_eo - subexp->rm_so;
1640 /* Range checking */
1641 if (sublen > outsize)
1643 strncpy (output, devname + subexp->rm_so, sublen);
1654 } /* End Function expand_regexp */
1657 /* from compat_name.c */
1659 struct translate_struct
1661 char *match; /* The string to match to (up to length) */
1662 char *format; /* Format of output, "%s" takes data past match string,
1663 NULL is effectively "%s" (just more efficient) */
1666 static struct translate_struct translate_table[] =
1669 {"printers/", "lp%s"},
1671 {"parports/", "parport%s"},
1674 {"loop/", "loop%s"},
1675 {"floppy/", "fd%s"},
1677 {"md/", "md%s"}, /* Meta-devices */
1681 {"pg/", "pg%s"}, /* Parallel port generic ATAPI interface*/
1683 {"staliomem/", "staliomem%s"}, /* Stallion serial driver control */
1684 {"tts/E", "ttyE%s"}, /* Stallion serial driver */
1685 {"cua/E", "cue%s"}, /* Stallion serial driver callout */
1686 {"tts/R", "ttyR%s"}, /* Rocketport serial driver */
1687 {"cua/R", "cur%s"}, /* Rocketport serial driver callout */
1688 {"ip2/", "ip2%s"}, /* Computone serial driver control */
1689 {"tts/F", "ttyF%s"}, /* Computone serial driver */
1690 {"cua/F", "cuf%s"}, /* Computone serial driver callout */
1691 {"tts/C", "ttyC%s"}, /* Cyclades serial driver */
1692 {"cua/C", "cub%s"}, /* Cyclades serial driver callout */
1693 {"tts/", "ttyS%s"}, /* Generic serial: must be after others */
1694 {"cua/", "cua%s"}, /* Generic serial: must be after others */
1695 {"input/js", "js%s"}, /* Joystick driver */
1699 const char *get_old_name (const char *devname, unsigned int namelen,
1700 char *buffer, unsigned int major, unsigned int minor)
1701 /* [SUMMARY] Translate a kernel-supplied name into an old name.
1702 <devname> The device name provided by the kernel.
1703 <namelen> The length of the name.
1704 <buffer> A buffer that may be used. This should be at least 128 bytes long.
1705 <major> The major number for the device.
1706 <minor> The minor number for the device.
1707 [RETURNS] A pointer to the old name if known, else NULL.
1710 const char *compat_name = NULL;
1712 struct translate_struct *trans;
1719 /* 1 to 5 "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */
1720 const char *fmt[] = { NULL ,
1721 "sg%u", /* scsi/generic */
1722 NULL, /* scsi/disc */
1723 "sr%u", /* scsi/cd */
1724 NULL, /* scsi/part */
1725 "nst%u%c", /* scsi/mt */
1726 "hd%c" , /* ide/host/disc */
1727 "hd%c" , /* ide/host/cd */
1728 "hd%c%s", /* ide/host/part */
1729 "%sht%d", /* ide/host/mt */
1730 "sbpcd%u", /* sbp/ */
1732 "%cty%c%c", /* pty/ */
1736 msg_logger( NO_DIE, LOG_INFO, "get_old_name()\n");
1739 for (trans = translate_table; trans->match != NULL; ++trans)
1741 len = strlen (trans->match);
1743 if (strncmp (devname, trans->match, len) == 0)
1745 if (trans->format == NULL)
1746 return (devname + len);
1747 sprintf (buffer, trans->format, devname + len);
1752 ptr = (strrchr (devname, '/') + 1);
1753 i = scan_dev_name(devname, namelen, ptr);
1755 if( i > 0 && i < 13)
1756 compat_name = buffer;
1761 msg_logger( NO_DIE, LOG_INFO, "get_old_name(): scan_dev_name() returned %d\n", i);
1764 /* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */
1765 if( i == 1 || i == 3 || i == 10 )
1766 sprintf (buffer, fmt[i], minor);
1768 /* 2 ==scsi/disc, 4 == scsi/part */
1769 if( i == 2 || i == 4)
1770 compat_name = write_old_sd_name (buffer, major, minor,((i == 2)?"":(ptr + 4)));
1778 sprintf (buffer, fmt[i], minor & 0x1f, mode);
1779 if (devname[namelen - 1] != 'n')
1782 /* 6 == ide/host/disc, 7 == ide/host/cd, 8 == ide/host/part */
1783 if( i == 6 || i == 7 || i == 8 )
1784 sprintf (buffer, fmt[i] , get_old_ide_name (major, minor), ptr + 4); /* last arg should be ignored for i == 6 or i== 7 */
1786 /* 9 == ide/host/mt */
1788 sprintf (buffer, fmt[i], ptr + 2, minor & 0x7f);
1793 sprintf (buffer, fmt[i], devname + 4);
1794 if (buffer[3] == '0')
1800 pty1 = "pqrstuvwxyzabcde";
1801 pty2 = "0123456789abcdef";
1802 indexx = atoi (devname + 5);
1803 sprintf (buffer, fmt[i], (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]);
1806 if(compat_name!=NULL)
1807 msg_logger( NO_DIE, LOG_INFO, "get_old_name(): compat_name %s\n", compat_name);
1809 return (compat_name);
1810 } /* End Function get_old_name */
1812 static char get_old_ide_name (unsigned int major, unsigned int minor)
1813 /* [SUMMARY] Get the old IDE name for a device.
1814 <major> The major number for the device.
1815 <minor> The minor number for the device.
1816 [RETURNS] The drive letter.
1819 char letter='y'; /* 121 */
1820 char c='a'; /* 97 */
1824 msg_logger( NO_DIE, LOG_INFO, "get_old_ide_name()\n");
1827 /* I hope it works like the previous code as it saves a few bytes. Tito ;P */
1829 if( i==IDE0_MAJOR || i==IDE1_MAJOR || i==IDE2_MAJOR ||
1830 i==IDE3_MAJOR || i==IDE4_MAJOR || i==IDE5_MAJOR ||
1831 i==IDE6_MAJOR || i==IDE7_MAJOR || i==IDE8_MAJOR ||
1842 } while(i<=IDE9_MAJOR);
1847 } /* End Function get_old_ide_name */
1849 static char *write_old_sd_name (char *buffer,
1850 unsigned int major, unsigned int minor,
1852 /* [SUMMARY] Write the old SCSI disc name to a buffer.
1853 <buffer> The buffer to write to.
1854 <major> The major number for the device.
1855 <minor> The minor number for the device.
1856 <part> The partition string. Must be "" for a whole-disc entry.
1857 [RETURNS] A pointer to the buffer on success, else NULL.
1860 unsigned int disc_index;
1863 msg_logger( NO_DIE, LOG_INFO, "write_old_sd_name()\n");
1868 sprintf (buffer, "sd%c%s", 'a' + (minor >> 4), part);
1871 if ( (major > 64) && (major < 72) )
1873 disc_index = ( (major - 64) << 4 ) + (minor >> 4);
1874 if (disc_index < 26)
1875 sprintf (buffer, "sd%c%s", 'a' + disc_index, part);
1877 sprintf (buffer, "sd%c%c%s", 'a' + (disc_index / 26) - 1, 'a' + disc_index % 26,part);
1881 } /* End Function write_old_sd_name */
1886 /*EXPERIMENTAL_FUNCTION*/
1888 int st_expr_expand (char *output, unsigned int length, const char *input,
1889 const char *(*get_variable_func) (const char *variable,
1892 /* [SUMMARY] Expand an expression using Borne Shell-like unquoted rules.
1893 <output> The output expanded expression is written here.
1894 <length> The size of the output buffer.
1895 <input> The input expression. This may equal <<output>>.
1896 <get_variable> A function which will be used to get variable values. If
1897 this returns NULL, the environment is searched instead. If this is NULL,
1898 only the environment is searched.
1899 <info> An arbitrary pointer passed to <<get_variable>>.
1900 [RETURNS] TRUE on success, else FALSE.
1905 unsigned int out_pos = 0;
1908 struct passwd *pwent;
1909 char buffer[BUFFER_SIZE], tmp[STRING_LENGTH];
1912 msg_logger( NO_DIE, LOG_INFO, "st_expr_expand()\n");
1915 if (length > BUFFER_SIZE)
1916 length = BUFFER_SIZE;
1917 for (; TRUE; ++input)
1919 switch (ch = *input)
1922 /* Variable expansion */
1923 input = expand_variable (buffer, length, &out_pos, ++input, get_variable_func, info);
1928 /* Home directory expansion */
1930 if ( isspace (ch) || (ch == '/') || (ch == '\0') )
1932 /* User's own home directory: leave separator for next time */
1933 if ( ( env = getenv ("HOME") ) == NULL )
1935 #ifdef CONFIG_DEVFSD_VERBOSE
1936 msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, "HOME");
1941 if (len + out_pos >= length)
1942 goto st_expr_expand_out;
1943 memcpy (buffer + out_pos, env, len + 1);
1947 /* Someone else's home directory */
1948 for (ptr = ++input; !isspace (ch) && (ch != '/') && (ch != '\0'); ch = *++ptr)
1951 if (len >= sizeof tmp)
1952 goto st_expr_expand_out;
1953 safe_memcpy (tmp, input, len);
1955 if ( ( pwent = getpwnam (tmp) ) == NULL )
1957 #ifdef CONFIG_DEVFSD_VERBOSE
1958 msg_logger( NO_DIE, LOG_INFO, "no pwent for: %s\n", tmp);
1962 len = strlen (pwent->pw_dir);
1963 if (len + out_pos >= length)
1964 goto st_expr_expand_out;
1965 memcpy (buffer + out_pos, pwent->pw_dir, len + 1);
1971 if (out_pos >= length)
1972 goto st_expr_expand_out;
1973 buffer[out_pos++] = ch;
1976 memcpy (output, buffer, out_pos);
1985 #ifdef CONFIG_DEVFSD_VERBOSE
1986 msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
1989 } /* End Function st_expr_expand */
1992 /* Private functions follow */
1994 static const char *expand_variable (char *buffer, unsigned int length,
1995 unsigned int *out_pos, const char *input,
1996 const char *(*func) (const char *variable,
1999 /* [SUMMARY] Expand a variable.
2000 <buffer> The buffer to write to.
2001 <length> The length of the output buffer.
2002 <out_pos> The current output position. This is updated.
2003 <input> A pointer to the input character pointer.
2004 <func> A function which will be used to get variable values. If this
2005 returns NULL, the environment is searched instead. If this is NULL, only
2006 the environment is searched.
2007 <info> An arbitrary pointer passed to <<func>>.
2008 <errfp> Diagnostic messages are written here.
2009 [RETURNS] A pointer to the end of this subexpression on success, else NULL.
2014 unsigned int open_braces;
2015 const char *env, *ptr;
2016 char tmp[STRING_LENGTH];
2019 msg_logger( NO_DIE, LOG_INFO, "expand_variable()\n");
2025 /* Special case for "$$": PID */
2026 sprintf ( tmp, "%d", (int) getpid () );
2028 if (len + *out_pos >= length)
2029 goto expand_variable_out;
2031 memcpy (buffer + *out_pos, tmp, len + 1);
2035 /* Ordinary variable expansion, possibly in braces */
2038 /* Simple variable expansion */
2039 for (ptr = input; isalnum (ch) || (ch == '_') || (ch == ':');ch = *++ptr)
2042 if (len >= sizeof tmp)
2043 goto expand_variable_out;
2045 safe_memcpy (tmp, input, len);
2047 if ( ( env = get_variable_v2 (tmp, func, info) ) == NULL )
2049 #ifdef CONFIG_DEVFSD_VERBOSE
2050 msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, tmp);
2055 if (len + *out_pos >= length)
2056 goto expand_variable_out;
2058 memcpy (buffer + *out_pos, env, len + 1);
2062 /* Variable in braces: check for ':' tricks */
2064 for (ptr = input; isalnum (ch) || (ch == '_'); ch = *++ptr)
2068 /* Must be simple variable expansion with "${var}" */
2070 if (len >= sizeof tmp)
2071 goto expand_variable_out;
2073 safe_memcpy (tmp, input, len);
2074 ptr = expand_variable (buffer, length, out_pos, tmp, func, info );
2077 return (input + len);
2079 if (ch != ':' || ptr[1] != '-' )
2081 #ifdef CONFIG_DEVFSD_VERBOSE
2082 msg_logger( NO_DIE, LOG_INFO,"illegal char in var name\n");
2086 /* It's that handy "${var:-word}" expression. Check if var is defined */
2088 if (len >= sizeof tmp)
2089 goto expand_variable_out;
2091 safe_memcpy (tmp, input, len);
2092 /* Move input pointer to ':' */
2094 /* First skip to closing brace, taking note of nested expressions */
2097 for (open_braces = 1; open_braces > 0; ch = *++ptr)
2108 #ifdef CONFIG_DEVFSD_VERBOSE
2109 msg_logger( NO_DIE, LOG_INFO,"\"}\" not found in: %s\n", input);
2117 /* At this point ptr should point to closing brace of "${var:-word}" */
2118 if ( ( env = get_variable_v2 (tmp, func, info) ) != NULL )
2120 /* Found environment variable, so skip the input to the closing brace
2121 and return the variable */
2124 if (len + *out_pos >= length)
2125 goto expand_variable_out;
2127 memcpy (buffer + *out_pos, env, len + 1);
2131 /* Environment variable was not found, so process word. Advance input
2132 pointer to start of word in "${var:-word}" */
2135 if (len >= sizeof tmp)
2136 goto expand_variable_out;
2138 safe_memcpy (tmp, input, len);
2140 if ( !st_expr_expand (tmp, STRING_LENGTH, tmp, func, info ) )
2143 if (len + *out_pos >= length)
2144 goto expand_variable_out;
2146 memcpy (buffer + *out_pos, tmp, len + 1);
2149 expand_variable_out:
2150 #ifdef CONFIG_DEVFSD_VERBOSE
2151 msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
2154 } /* End Function expand_variable */
2157 static const char *get_variable_v2 (const char *variable,
2158 const char *(*func) (const char *variable, void *info),
2160 /* [SUMMARY] Get a variable from the environment or .
2161 <variable> The variable name.
2162 <func> A function which will be used to get the variable. If this returns
2163 NULL, the environment is searched instead. If this is NULL, only the
2164 environment is searched.
2165 [RETURNS] The value of the variable on success, else NULL.
2171 msg_logger( NO_DIE, LOG_INFO, "get_variable_v2()\n");
2176 value = (*func) (variable, info);
2180 return getenv (variable);
2181 } /* End Function get_variable */