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 #ifdef CONFIG_DEVFSD_VERBOSE
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 #ifdef CONFIG_DEVFSD_VERBOSE
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]=='c' &&
843 info->devname[4]=='t' && info->devname[5]=='l' && !info->devname[6])))
845 for (entry = first_config; entry != NULL; entry = entry->next)
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 )
850 for (n = 0; (n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n)
853 msg_logger( NO_DIE, LOG_INFO, "service_name(): action.what %d\n", entry->action.what);
855 switch (entry->action.what)
858 action_permissions (info, entry);
860 #ifdef CONFIG_DEVFSD_MODLOAD
862 action_modload (info, entry);
866 action_execute (info, entry, mbuf, n);
869 action_copy (info, entry, mbuf, n);
878 action_compat (info, entry->action.what);
881 #ifdef CONFIG_DEVFSD_VERBOSE
882 msg_logger( DIE, LOG_ERR, "Unknown action\n");
889 } /* End Function service_name */
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.
902 msg_logger( NO_DIE, LOG_INFO, "action_permission()\n");
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)
909 #ifdef CONFIG_DEVFSD_VERBOSE
910 msg_logger( NO_DIE, LOG_ERR, "chmod() or chown(): %s: %m\n",info->devname);
915 } /* End Function action_permissions */
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.
927 char device[STRING_LENGTH];
930 argv[1] = MODPROBE_SWITCH_1; /* "-k" */
931 argv[2] = MODPROBE_SWITCH_2; /* "-C" */
932 argv[3] = CONFIG_MODULES_DEVFS;
936 snprintf (device, sizeof (device), "/dev/%s", info->devname);
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]);
940 fork_and_execute(DIE, argv[0], argv);
941 } /* End Function action_modload */
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
952 <numexpr> The number of elements within <<regexpr>>.
957 struct get_variable_info gv_info;
958 char *argv[MAX_ARGS + 1];
959 char largv[MAX_ARGS + 1][STRING_LENGTH];
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)
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];
981 for(i=0;argv[i]!=NULL;i++) /* argv[i] < MAX_ARGS + 1 */
984 if( (strlen(buff)+ 1 + strlen(argv[i])) >= 512)
986 strcat(buff,argv[i]);
989 msg_logger( NO_DIE, LOG_INFO, "action_execute(): %s",buff);
992 fork_and_execute(NO_DIE, argv[0], argv);
993 } /* End Function action_execute */
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
1004 <numexpr> The number of elements in <<regexpr>>.
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;
1015 msg_logger( NO_DIE, LOG_INFO, "action_copy()\n");
1018 if ( (info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK (info->mode) )
1020 gv_info.info = info;
1021 gv_info.devname = info->devname;
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,
1028 expand_expression (destination, STRING_LENGTH, entry->u.copy.destination,
1029 get_variable, &gv_info, gv_info.devname,
1032 if ( !make_dir_tree (destination) || lstat (source, &source_stat) != 0)
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;
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);
1044 copy_inode (destination, &dest_stat, new_mode, source, &source_stat);
1047 } /* End Function action_copy */
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.
1056 const char *compat_name = NULL;
1057 const char *dest_name = info->devname;
1059 char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH];
1060 int mode, host, bus, target, lun;
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 */
1076 /* First construct compatibility name */
1079 case AC_MKOLDCOMPAT:
1080 case AC_RMOLDCOMPAT:
1081 compat_name = get_old_name (info->devname, info->namelen, compat_buf, info->major, info->minor);
1083 case AC_MKNEWCOMPAT:
1084 case AC_RMNEWCOMPAT:
1085 ptr = strrchr (info->devname, '/') + 1;
1086 i=scan_dev_name(info->devname, info->namelen, ptr);
1089 msg_logger( NO_DIE, LOG_INFO, "action_compat(): scan_dev_name() returned %d\n", i);
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;
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);
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) );
1113 rewind_ = info->devname[info->namelen - 1];
1117 if(ptr[2] == 'l' /*108*/ || ptr[2] == 'm'/*109*/)
1118 mode = ptr[2] - 107; /* 1 or 2 */
1121 sprintf (compat_buf, fmt [i], host, bus, target, lun, mode, rewind_);
1124 /* 9 == ide/host/mt */
1126 snprintf (compat_buf, sizeof (compat_buf), fmt[i], host, bus, target, lun, ptr + 2);
1128 } /* switch(action) */
1130 if(compat_name == NULL )
1134 msg_logger( NO_DIE, LOG_INFO, "action_compat(): %s\n", compat_name);
1137 /* Now decide what to do with it */
1140 case AC_MKOLDCOMPAT:
1141 case AC_MKNEWCOMPAT:
1142 mksymlink (dest_name, compat_name);
1144 case AC_RMOLDCOMPAT:
1145 case AC_RMNEWCOMPAT:
1147 if (unlink (compat_name) != 0)
1148 msg_logger( NO_DIE, LOG_ERR, "unlink(): %s: %m\n", compat_name);
1150 unlink (compat_name);
1154 } /* switch(action) */
1155 } /* End Function action_compat */
1157 static void restore(char *spath, struct stat source_stat, int rootlen)
1159 char dpath[STRING_LENGTH];
1160 struct stat dest_stat;
1163 msg_logger( NO_DIE, LOG_INFO, "restore()\n");
1166 dest_stat.st_mode = 0;
1167 snprintf (dpath, sizeof dpath, "%s%s", mount_point, spath + rootlen);
1168 lstat (dpath, &dest_stat);
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);
1173 if ( S_ISDIR (source_stat.st_mode) )
1174 dir_operation(RESTORE, spath, rootlen,NULL);
1178 static int copy_inode (const char *destpath, const struct stat *dest_stat,
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.
1190 int source_len, dest_len;
1191 char source_link[STRING_LENGTH], dest_link[STRING_LENGTH];
1193 struct sockaddr_un un_addr;
1194 char symlink_val[STRING_LENGTH];
1197 msg_logger( NO_DIE, LOG_INFO, "copy_inode()\n");
1200 if ( (source_stat->st_mode & S_IFMT) == (dest_stat->st_mode & S_IFMT) )
1203 if ( S_ISLNK (source_stat->st_mode) )
1205 if (( source_len = readlink (sourcepath, source_link, STRING_LENGTH - 1) ) < 0 ||
1206 ( dest_len = readlink (destpath , dest_link , STRING_LENGTH - 1) ) < 0 )
1208 source_link[source_len] = '\0';
1209 dest_link[dest_len] = '\0';
1210 if ( (source_len != dest_len) || (strcmp (source_link, dest_link) != 0) )
1213 symlink (source_link, destpath);
1216 } /* Else not a symlink */
1217 chmod (destpath, new_mode & ~S_IFMT);
1218 chown (destpath, source_stat->st_uid, source_stat->st_gid);
1221 /* Different types: unlink and create */
1223 switch (source_stat->st_mode & S_IFMT)
1226 if ( ( fd = socket (AF_UNIX, SOCK_STREAM, 0) ) < 0 )
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);
1232 if (val != 0 || chmod (destpath, new_mode & ~S_IFMT) != 0)
1236 if ( ( val = readlink (sourcepath, symlink_val, STRING_LENGTH - 1) ) < 0 )
1238 symlink_val[val] = '\0';
1239 if (symlink (symlink_val, destpath) == 0)
1243 if ( ( fd = open (destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT) ) < 0 )
1246 if (chmod (destpath, new_mode & ~S_IFMT) != 0)
1252 if (mknod (destpath, new_mode, source_stat->st_rdev) != 0)
1256 if (mkdir (destpath, new_mode & ~S_IFMT) != 0)
1259 if (chown (destpath, source_stat->st_uid, source_stat->st_gid) == 0)
1264 } /* End Function copy_inode */
1266 static void free_config ()
1267 /* [SUMMARY] Free the configuration information.
1271 struct config_entry_struct *c_entry;
1275 msg_logger( NO_DIE, LOG_INFO, "free_config()\n");
1278 for (c_entry = first_config; c_entry != NULL; c_entry = next)
1282 next = c_entry->next;
1283 regfree (&c_entry->preg);
1284 if (c_entry->action.what == AC_EXECUTE)
1286 for (count = 0; count < MAX_ARGS; ++count)
1288 if (c_entry->u.execute.argv[count] == NULL)
1290 free (c_entry->u.execute.argv[count]);
1295 first_config = NULL;
1297 } /* End Function free_config */
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.
1306 struct passwd *pw_ent;
1307 struct group *grp_ent;
1308 #ifdef CONFIG_DEVFSD_VERBOSE
1313 msg_logger( NO_DIE, LOG_INFO, "get_uid_gid()\n");
1316 if(flag != UID && flag != GID )
1317 msg_logger( DIE, LOG_ERR,"get_uid_gid(): flag != UID && flag != GID\n");
1320 if ( isdigit (string[0]) || ( (string[0] == '-') && isdigit (string[1]) ) )
1321 return atoi (string);
1323 if ( flag == UID && ( pw_ent = getpwnam (string) ) != NULL )
1324 return (pw_ent->pw_uid);
1326 if ( flag == GID && ( grp_ent = getgrnam (string) ) != NULL )
1327 return (grp_ent->gr_gid);
1328 #ifdef CONFIG_DEVFSD_VERBOSE
1332 msg_logger( NO_DIE, LOG_ERR,"unknown %s: %s, defaulting to %cID=0\n", msg, string, msg[0] - 32);
1335 }/* End Function get_uid_gid */
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.
1346 msg_logger( NO_DIE, LOG_INFO, "get_mode()\n");
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);
1361 if(string[0]=='r'||string[0]=='w'||string[0]=='x')
1367 } /* End Function get_mode */
1369 static void signal_handler (int sig)
1372 msg_logger( NO_DIE, LOG_INFO, "signal_handler()\n");
1375 caught_signal = TRUE;
1377 caught_sighup = TRUE;
1378 #ifdef CONFIG_DEVFSD_VERBOSE
1379 msg_logger( NO_DIE, LOG_INFO, "Caught %s\n",(sig == SIGHUP)?"SIGHUP" : "SIGUSR1");
1381 } /* End Function signal_handler */
1383 static const char *get_variable (const char *variable, void *info)
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 };
1392 msg_logger( NO_DIE, LOG_INFO, "get_variable()\n");
1395 if (gethostname (hostname, STRING_LENGTH - 1) != 0)
1396 #ifdef CONFIG_DEVFSD_VERBOSE
1397 msg_logger( DIE, LOG_ERR, "gethostname(): %m\n");
1401 /* Here on error we should do exit(RV_SYS_ERROR), instead we do exit(EXIT_FAILURE) */
1402 hostname[STRING_LENGTH - 1] = '\0';
1404 /* compare_string_array returns i>=0 */
1405 i=compare_string_array(field_names, variable);
1407 if ( i > 6 && (i > 1 && gv_info == NULL))
1409 if( i >= 0 || i <= 3)
1412 msg_logger( NO_DIE, LOG_INFO, "get_variable(): i=%d %s\n",i ,field_names[i+7]);
1414 return(field_names[i+7]);
1418 sprintf (sbuf, "%u", gv_info->info->uid);
1420 sprintf (sbuf, "%u", gv_info->info->gid);
1422 sprintf (sbuf, "%o", gv_info->info->mode);
1424 msg_logger( NO_DIE, LOG_INFO, "get_variable(): %s\n", sbuf);
1427 } /* End Function get_variable */
1429 static void service(struct stat statbuf, char *path)
1431 struct devfsd_notify_struct info;
1434 msg_logger( NO_DIE, LOG_INFO, "service()\n");
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);
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.
1460 struct stat statbuf;
1463 char path[STRING_LENGTH];
1467 msg_logger( NO_DIE, LOG_INFO, "dir_operation()\n");
1470 if((dp = opendir( dir_name))==NULL)
1473 msg_logger( NO_DIE, LOG_ERR, "opendir(): %s: %m\n", dir_name);
1478 while ( (de = readdir (dp) ) != NULL )
1481 if(de->d_name && *de->d_name == '.' && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])))
1483 snprintf (path, sizeof (path), "%s/%s", dir_name, de->d_name);
1485 msg_logger( NO_DIE, LOG_ERR, "dir_operation(): %s\n", path);
1488 if (lstat (path, &statbuf) != 0)
1491 msg_logger( NO_DIE, LOG_ERR, "%s: %m\n", path);
1498 service(statbuf,path);
1501 restore(path, statbuf, var);
1504 read_config_file (path, var, event_mask);
1509 } /* End Function do_scan_and_service */
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.
1519 msg_logger( NO_DIE, LOG_INFO, "mksymlink()\n", newpath);
1523 if ( !make_dir_tree (newpath) )
1526 if (symlink (oldpath, newpath) != 0)
1528 if (errno != EEXIST)
1531 msg_logger( NO_DIE, LOG_ERR, "mksymlink(): %s to %s: %m\n", oldpath, newpath);
1537 } /* End Function mksymlink */
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.
1547 msg_logger( NO_DIE, LOG_INFO, "make_dir_tree()\n");
1549 if (bb_make_directory( dirname((char *)path), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ,FILEUTILS_RECUR )==-1)
1552 msg_logger( NO_DIE, LOG_ERR, "make_dir_tree(): %s: %m\n", path);
1557 } /* End Function make_dir_tree */
1559 static int expand_expression(char *output, unsigned int outsize,
1561 const char *(*get_variable_func)(const char *variable, 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.
1580 char temp[STRING_LENGTH];
1583 msg_logger( NO_DIE, LOG_INFO, "expand_expression()\n");
1586 if ( !st_expr_expand (temp, STRING_LENGTH, input, get_variable_func, info) )
1588 expand_regexp (output, outsize, temp, devname, ex, numexp);
1590 } /* End Function expand_expression */
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
1606 <numex> Number of subexpressions in the offset-array <<ex>>.
1610 const char last_exp = '0' - 1 + numex;
1614 msg_logger( NO_DIE, LOG_INFO, "expand_regexp()\n");
1617 /* Guarantee NULL termination by writing an explicit '\0' character into
1618 the very last byte */
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) )
1634 if ((c >= '0') && (c <= last_exp))
1636 const regmatch_t *subexp = ex + (c - '0');
1637 unsigned int sublen = subexp->rm_eo - subexp->rm_so;
1639 /* Range checking */
1640 if (sublen > outsize)
1642 strncpy (output, devname + subexp->rm_so, sublen);
1653 } /* End Function expand_regexp */
1656 /* from compat_name.c */
1658 struct translate_struct
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) */
1665 static struct translate_struct translate_table[] =
1668 {"printers/", "lp%s"},
1670 {"parports/", "parport%s"},
1673 {"loop/", "loop%s"},
1674 {"floppy/", "fd%s"},
1676 {"md/", "md%s"}, /* Meta-devices */
1680 {"pg/", "pg%s"}, /* Parallel port generic ATAPI interface*/
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 */
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.
1709 const char *compat_name = NULL;
1711 struct translate_struct *trans;
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/ */
1731 "%cty%c%c", /* pty/ */
1735 msg_logger( NO_DIE, LOG_INFO, "get_old_name()\n");
1738 for (trans = translate_table; trans->match != NULL; ++trans)
1740 len = strlen (trans->match);
1742 if (strncmp (devname, trans->match, len) == 0)
1744 if (trans->format == NULL)
1745 return (devname + len);
1746 sprintf (buffer, trans->format, devname + len);
1751 ptr = (strrchr (devname, '/') + 1);
1752 i = scan_dev_name(devname, namelen, ptr);
1754 if( i > 0 && i < 13)
1755 compat_name = buffer;
1760 msg_logger( NO_DIE, LOG_INFO, "get_old_name(): scan_dev_name() returned %d\n", i);
1763 /* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */
1764 if( i == 1 || i == 3 || i == 10 )
1765 sprintf (buffer, fmt[i], minor);
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)));
1777 sprintf (buffer, fmt[i], minor & 0x1f, mode);
1778 if (devname[namelen - 1] != 'n')
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 */
1785 /* 9 == ide/host/mt */
1787 sprintf (buffer, fmt[i], ptr + 2, minor & 0x7f);
1792 sprintf (buffer, fmt[i], devname + 4);
1793 if (buffer[3] == '0')
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]);
1805 if(compat_name!=NULL)
1806 msg_logger( NO_DIE, LOG_INFO, "get_old_name(): compat_name %s\n", compat_name);
1808 return (compat_name);
1809 } /* End Function get_old_name */
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.
1818 char letter='y'; /* 121 */
1819 char c='a'; /* 97 */
1823 msg_logger( NO_DIE, LOG_INFO, "get_old_ide_name()\n");
1826 /* I hope it works like the previous code as it saves a few bytes. Tito ;P */
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 ||
1841 } while(i<=IDE9_MAJOR);
1846 } /* End Function get_old_ide_name */
1848 static char *write_old_sd_name (char *buffer,
1849 unsigned int major, unsigned int minor,
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.
1859 unsigned int disc_index;
1862 msg_logger( NO_DIE, LOG_INFO, "write_old_sd_name()\n");
1867 sprintf (buffer, "sd%c%s", 'a' + (minor >> 4), part);
1870 if ( (major > 64) && (major < 72) )
1872 disc_index = ( (major - 64) << 4 ) + (minor >> 4);
1873 if (disc_index < 26)
1874 sprintf (buffer, "sd%c%s", 'a' + disc_index, part);
1876 sprintf (buffer, "sd%c%c%s", 'a' + (disc_index / 26) - 1, 'a' + disc_index % 26,part);
1880 } /* End Function write_old_sd_name */
1885 /*EXPERIMENTAL_FUNCTION*/
1887 int st_expr_expand (char *output, unsigned int length, const char *input,
1888 const char *(*get_variable_func) (const char *variable,
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.
1904 unsigned int out_pos = 0;
1907 struct passwd *pwent;
1908 char buffer[BUFFER_SIZE], tmp[STRING_LENGTH];
1911 msg_logger( NO_DIE, LOG_INFO, "st_expr_expand()\n");
1914 if (length > BUFFER_SIZE)
1915 length = BUFFER_SIZE;
1916 for (; TRUE; ++input)
1918 switch (ch = *input)
1921 /* Variable expansion */
1922 input = expand_variable (buffer, length, &out_pos, ++input, get_variable_func, info);
1927 /* Home directory expansion */
1929 if ( isspace (ch) || (ch == '/') || (ch == '\0') )
1931 /* User's own home directory: leave separator for next time */
1932 if ( ( env = getenv ("HOME") ) == NULL )
1934 #ifdef CONFIG_DEVFSD_VERBOSE
1935 msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, "HOME");
1940 if (len + out_pos >= length)
1941 goto st_expr_expand_out;
1942 memcpy (buffer + out_pos, env, len + 1);
1946 /* Someone else's home directory */
1947 for (ptr = ++input; !isspace (ch) && (ch != '/') && (ch != '\0'); ch = *++ptr)
1950 if (len >= sizeof tmp)
1951 goto st_expr_expand_out;
1952 safe_memcpy (tmp, input, len);
1954 if ( ( pwent = getpwnam (tmp) ) == NULL )
1956 #ifdef CONFIG_DEVFSD_VERBOSE
1957 msg_logger( NO_DIE, LOG_INFO, "no pwent for: %s\n", tmp);
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);
1970 if (out_pos >= length)
1971 goto st_expr_expand_out;
1972 buffer[out_pos++] = ch;
1975 memcpy (output, buffer, out_pos);
1984 #ifdef CONFIG_DEVFSD_VERBOSE
1985 msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
1988 } /* End Function st_expr_expand */
1991 /* Private functions follow */
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,
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.
2013 unsigned int open_braces;
2014 const char *env, *ptr;
2015 char tmp[STRING_LENGTH];
2018 msg_logger( NO_DIE, LOG_INFO, "expand_variable()\n");
2024 /* Special case for "$$": PID */
2025 sprintf ( tmp, "%d", (int) getpid () );
2027 if (len + *out_pos >= length)
2028 goto expand_variable_out;
2030 memcpy (buffer + *out_pos, tmp, len + 1);
2034 /* Ordinary variable expansion, possibly in braces */
2037 /* Simple variable expansion */
2038 for (ptr = input; isalnum (ch) || (ch == '_') || (ch == ':');ch = *++ptr)
2041 if (len >= sizeof tmp)
2042 goto expand_variable_out;
2044 safe_memcpy (tmp, input, len);
2046 if ( ( env = get_variable_v2 (tmp, func, info) ) == NULL )
2048 #ifdef CONFIG_DEVFSD_VERBOSE
2049 msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, tmp);
2054 if (len + *out_pos >= length)
2055 goto expand_variable_out;
2057 memcpy (buffer + *out_pos, env, len + 1);
2061 /* Variable in braces: check for ':' tricks */
2063 for (ptr = input; isalnum (ch) || (ch == '_'); ch = *++ptr)
2067 /* Must be simple variable expansion with "${var}" */
2069 if (len >= sizeof tmp)
2070 goto expand_variable_out;
2072 safe_memcpy (tmp, input, len);
2073 ptr = expand_variable (buffer, length, out_pos, tmp, func, info );
2076 return (input + len);
2078 if (ch != ':' || ptr[1] != '-' )
2080 #ifdef CONFIG_DEVFSD_VERBOSE
2081 msg_logger( NO_DIE, LOG_INFO,"illegal char in var name\n");
2085 /* It's that handy "${var:-word}" expression. Check if var is defined */
2087 if (len >= sizeof tmp)
2088 goto expand_variable_out;
2090 safe_memcpy (tmp, input, len);
2091 /* Move input pointer to ':' */
2093 /* First skip to closing brace, taking note of nested expressions */
2096 for (open_braces = 1; open_braces > 0; ch = *++ptr)
2107 #ifdef CONFIG_DEVFSD_VERBOSE
2108 msg_logger( NO_DIE, LOG_INFO,"\"}\" not found in: %s\n", input);
2116 /* At this point ptr should point to closing brace of "${var:-word}" */
2117 if ( ( env = get_variable_v2 (tmp, func, info) ) != NULL )
2119 /* Found environment variable, so skip the input to the closing brace
2120 and return the variable */
2123 if (len + *out_pos >= length)
2124 goto expand_variable_out;
2126 memcpy (buffer + *out_pos, env, len + 1);
2130 /* Environment variable was not found, so process word. Advance input
2131 pointer to start of word in "${var:-word}" */
2134 if (len >= sizeof tmp)
2135 goto expand_variable_out;
2137 safe_memcpy (tmp, input, len);
2139 if ( !st_expr_expand (tmp, STRING_LENGTH, tmp, func, info ) )
2142 if (len + *out_pos >= length)
2143 goto expand_variable_out;
2145 memcpy (buffer + *out_pos, tmp, len + 1);
2148 expand_variable_out:
2149 #ifdef CONFIG_DEVFSD_VERBOSE
2150 msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
2153 } /* End Function expand_variable */
2156 static const char *get_variable_v2 (const char *variable,
2157 const char *(*func) (const char *variable, 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.
2170 msg_logger( NO_DIE, LOG_INFO, "get_variable_v2()\n");
2175 value = (*func) (variable, info);
2179 return getenv (variable);
2180 } /* End Function get_variable */