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_DEBUG_MASK _IOW(DEVFSD_IOCTL_BASE, 4, int)
97 #define DEVFSD_NOTIFY_REGISTERED 0
98 #define DEVFSD_NOTIFY_UNREGISTERED 1
99 #define DEVFSD_NOTIFY_ASYNC_OPEN 2
100 #define DEVFSD_NOTIFY_CLOSE 3
101 #define DEVFSD_NOTIFY_LOOKUP 4
102 #define DEVFSD_NOTIFY_CHANGE 5
103 #define DEVFSD_NOTIFY_CREATE 6
104 #define DEVFSD_NOTIFY_DELETE 7
105 #define DEVFS_PATHLEN 1024 /* Never change this otherwise the
106 binary interface will change */
107 struct devfsd_notify_struct
108 { /* Use native C types to ensure same types in kernel and user space */
109 unsigned int type; /* DEVFSD_NOTIFY_* value */
110 unsigned int mode; /* Mode of the inode or device entry */
111 unsigned int major; /* Major number of device entry */
112 unsigned int minor; /* Minor number of device entry */
113 unsigned int uid; /* Uid of process, inode or device entry */
114 unsigned int gid; /* Gid of process, inode or device entry */
115 unsigned int overrun_count; /* Number of lost events */
116 unsigned int namelen; /* Number of characters not including '\0' */
117 /* The device name MUST come last */
118 char devname[DEVFS_PATHLEN]; /* This will be '\0' terminated */
123 /* These are now in Config.in */
124 /* define this if you want to have more output on stderr and syslog at the same time */
125 /*#define CONFIG_DEVFSD_VERBOSE*/
126 /* define this if you want to have the function names in output */
127 /*#define CONFIG_DEVFSD_DEBUG*/
129 #define BUFFER_SIZE 16384
130 #define DEVFSD_VERSION "1.3.25"
131 #define CONFIG_FILE "/etc/devfsd.conf"
132 #define MODPROBE "/sbin/modprobe"
133 #define CONFIG_MODULES_DEVFS "/etc/modules.devfs"
134 #define MAX_ARGS (6 + 1)
135 #define MAX_SUBEXPR 10
136 #define STRING_LENGTH 255
138 /* for get_uid_gid() */
142 /* for msg_logger(), do_ioctl(),
143 fork_and_execute() */
147 /* for dir_operation() */
150 #define READ_CONFIG 2
152 /* Update only after changing code to reflect new protocol */
153 #define DEVFSD_PROTOCOL_REVISION_DAEMON 5
155 /* Compile-time check */
156 #if DEVFSD_PROTOCOL_REVISION_KERNEL != DEVFSD_PROTOCOL_REVISION_DAEMON
157 #error protocol version mismatch. Update your kernel headers
160 #define AC_PERMISSIONS 0
163 #define AC_MFUNCTION 3 /* not supported by busybox */
164 #define AC_CFUNCTION 4 /* not supported by busybox */
167 #define AC_MKOLDCOMPAT 7
168 #define AC_MKNEWCOMPAT 8
169 #define AC_RMOLDCOMPAT 9
170 #define AC_RMNEWCOMPAT 10
171 #define AC_RESTORE 11
174 struct permissions_type
183 char *argv[MAX_ARGS + 1]; /* argv[0] must always be the programme */
189 const char *destination;
198 struct config_entry_struct
200 struct action_type action;
204 struct permissions_type permissions;
205 struct execute_type execute;
206 struct copy_type copy;
209 struct config_entry_struct *next;
212 struct get_variable_info
214 const struct devfsd_notify_struct *info;
216 char devpath[STRING_LENGTH];
219 static void dir_operation(int , const char * , int, unsigned long* );
220 static void service(struct stat statbuf, char *path);
221 static int st_expr_expand(char *, unsigned, const char *, const char *(*) (const char *, void *), void *);
222 static const char *get_old_name(const char *, unsigned, char *, unsigned, unsigned);
223 static int mksymlink (const char *oldpath, const char *newpath);
224 static void read_config_file (const char *path, int optional, unsigned long *event_mask);
225 static void process_config_line (const char *, unsigned long *);
226 static int do_servicing (int, unsigned long);
227 static void service_name (const struct devfsd_notify_struct *);
228 static void action_permissions (const struct devfsd_notify_struct *, const struct config_entry_struct *);
229 static void action_execute (const struct devfsd_notify_struct *, const struct config_entry_struct *,
230 const regmatch_t *, unsigned);
231 #ifdef CONFIG_DEVFSD_MODLOAD
232 static void action_modload (const struct devfsd_notify_struct *info, const struct config_entry_struct *entry);
234 static void action_copy (const struct devfsd_notify_struct *, const struct config_entry_struct *,
235 const regmatch_t *, unsigned);
236 static void action_compat (const struct devfsd_notify_struct *, unsigned);
237 static void free_config (void);
238 static void restore(char *spath, struct stat source_stat, int rootlen);
239 static int copy_inode (const char *, const struct stat *, mode_t, const char *, const struct stat *);
240 static mode_t get_mode (const char *);
241 static void signal_handler (int);
242 static const char *get_variable (const char *, void *);
243 static int make_dir_tree (const char *);
244 static int expand_expression(char *, unsigned, const char *, const char *(*)(const char *, void *), void *,
245 const char *, const regmatch_t *, unsigned );
246 static void expand_regexp (char *, size_t, const char *, const char *, const regmatch_t *, unsigned );
247 static const char *expand_variable( char *, unsigned, unsigned *, const char *,
248 const char *(*) (const char *, void *), void * );
249 static const char *get_variable_v2(const char *, const char *(*) (const char *, void *), void *);
250 static char get_old_ide_name (unsigned , unsigned);
251 static char *write_old_sd_name (char *, unsigned, unsigned, char *);
253 /* busybox functions */
254 static void msg_logger(int die, int pri, const char * fmt, ... );
255 static void do_ioctl(int die, int fd, int request, unsigned long event_mask_flag);
256 static void fork_and_execute(int die, char *arg0, char **arg );
257 static int get_uid_gid ( int, const char *);
258 static void safe_memcpy( char * dest, const char * src, int len);
260 /* Structs and vars */
261 static struct config_entry_struct *first_config = NULL;
262 static struct config_entry_struct *last_config = NULL;
263 static const char *mount_point = NULL;
264 static volatile int caught_signal = FALSE;
265 static volatile int caught_sighup = FALSE;
266 static struct initial_symlink_struct
270 } initial_symlinks[] =
272 {"/proc/self/fd", "fd"},
279 static struct event_type
281 unsigned int type; /* The DEVFSD_NOTIFY_* value */
282 const char *config_name; /* The name used in the config file */
285 {DEVFSD_NOTIFY_REGISTERED, "REGISTER"},
286 {DEVFSD_NOTIFY_UNREGISTERED, "UNREGISTER"},
287 {DEVFSD_NOTIFY_ASYNC_OPEN, "ASYNC_OPEN"},
288 {DEVFSD_NOTIFY_CLOSE, "CLOSE"},
289 {DEVFSD_NOTIFY_LOOKUP, "LOOKUP"},
290 {DEVFSD_NOTIFY_CHANGE, "CHANGE"},
291 {DEVFSD_NOTIFY_CREATE, "CREATE"},
292 {DEVFSD_NOTIFY_DELETE, "DELETE"},
296 /* busybox functions and messages */
298 extern void xregcomp(regex_t * preg, const char *regex, int cflags);
300 const char * const bb_msg_bad_config = "bad %s config file: %s\n";
301 const char * const bb_msg_proto_rev = "protocol revision";
302 #ifdef CONFIG_DEVFSD_VERBOSE
303 const char * const bb_msg_small_buffer = "buffer too small\n";
304 const char * const bb_msg_variable_not_found= "variable: %s not found\n";
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);
318 #ifndef CONFIG_DEVFSD_VERBOSE
321 bb_verror_msg(fmt, ap);
328 static void do_ioctl(int die, int fd, int request, unsigned long event_mask_flag)
330 if (ioctl (fd, request, event_mask_flag) != 0)
331 msg_logger(die, LOG_ERR, "ioctl(): %m\n");
334 static void fork_and_execute(int die, char *arg0, char **arg )
342 /* Parent: Error : die or return */
343 msg_logger(die, LOG_ERR,(char *) bb_msg_memory_exhausted);
346 /* Parent : ok : return or exit */
354 /* Child : if arg0 != NULL do execvp */
358 msg_logger(DIE, LOG_ERR, "execvp(): %s: %m\n", arg0);
362 static void safe_memcpy( char *dest, const char *src, int len)
364 memcpy (dest , src , len );
368 /* Public functions follow */
370 int devfsd_main (int argc, char **argv)
372 int print_version = FALSE;
373 int do_daemon = TRUE;
374 int no_polling = FALSE;
375 int fd, proto_rev, count;
376 unsigned long event_mask = 0;
377 struct sigaction new_action;
378 struct initial_symlink_struct *curr;
383 for (count = 2; count < argc; ++count)
385 if (strcmp (argv[count], "-v") == 0)
386 print_version = TRUE;
387 else if (strcmp (argv[count], "-fg") == 0)
389 else if (strcmp (argv[count], "-np") == 0)
395 mount_point = argv[1];
397 if (chdir (mount_point) != 0)
398 bb_error_msg_and_die( " %s: %m", mount_point);
400 fd = bb_xopen (".devfsd", O_RDONLY);
402 if (fcntl (fd, F_SETFD, FD_CLOEXEC) != 0)
403 bb_error_msg( "FD_CLOEXEC");
405 do_ioctl(DIE, fd, DEVFSDIOC_GET_PROTO_REV,(int )&proto_rev);
407 /*setup initial entries */
408 for (curr = initial_symlinks; curr->dest != NULL; ++curr)
409 symlink (curr->dest, curr->name);
411 /* NB: The check for CONFIG_FILE is done in read_config_file() */
413 if ( print_version || (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev) )
415 bb_printf( "%s v%s\nDaemon %s:\t%d\nKernel-side %s:\t%d\n",
416 bb_applet_name,DEVFSD_VERSION,bb_msg_proto_rev,
417 DEVFSD_PROTOCOL_REVISION_DAEMON,bb_msg_proto_rev, proto_rev);
418 if (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)
419 bb_error_msg_and_die( "%s mismatch!",bb_msg_proto_rev);
420 exit(EXIT_SUCCESS); /* -v */
422 /* Tell kernel we are special (i.e. we get to see hidden entries) */
423 do_ioctl(DIE, fd, DEVFSDIOC_SET_EVENT_MASK, 0);
425 sigemptyset (&new_action.sa_mask);
426 new_action.sa_flags = 0;
428 /* Set up SIGHUP and SIGUSR1 handlers */
429 new_action.sa_handler = signal_handler;
430 if (sigaction (SIGHUP, &new_action, NULL) != 0 || sigaction (SIGUSR1, &new_action, NULL) != 0 )
431 bb_error_msg_and_die( "sigaction()");
433 bb_printf("%s v%s started for %s\n",bb_applet_name, DEVFSD_VERSION, mount_point);
435 /* Set umask so that mknod(2), open(2) and mkdir(2) have complete control over permissions */
437 read_config_file (CONFIG_FILE, FALSE, &event_mask);
438 /* Do the scan before forking, so that boot scripts see the finished product */
439 dir_operation(SERVICE,mount_point,0,NULL);
444 /* Release so that the child can grab it */
445 do_ioctl(DIE, fd, DEVFSDIOC_RELEASE_EVENT_QUEUE, 0);
446 fork_and_execute(DIE, NULL, NULL);
447 setsid (); /* Prevent hangups and become pgrp leader */
450 setpgid (0, 0); /* Become process group leader */
454 int do_scan = do_servicing (fd, event_mask);
457 read_config_file (CONFIG_FILE, FALSE, &event_mask);
459 dir_operation(SERVICE,mount_point,0,NULL);
461 } /* End Function main */
464 /* Private functions follow */
466 static void read_config_file (const char *path, int optional, unsigned long *event_mask)
467 /* [SUMMARY] Read a configuration database.
468 <path> The path to read the database from. If this is a directory, all
469 entries in that directory will be read (except hidden entries).
470 <optional> If TRUE, the routine will silently ignore a missing config file.
471 <event_mask> The event mask is written here. This is not initialised.
477 char buf[STRING_LENGTH];
480 #ifdef CONFIG_DEVFSD_DEBUG
481 msg_logger( NO_DIE, LOG_INFO, "read_config_file()\n");
483 if (stat (path, &statbuf) != 0 || statbuf.st_size == 0 )
484 goto read_config_file_err;
486 if ( S_ISDIR (statbuf.st_mode) )
488 dir_operation(READ_CONFIG, path, 0, event_mask);
492 if ( ( fp = fopen (path, "r") ) != NULL )
494 while (fgets (buf, STRING_LENGTH, fp) != NULL)
496 /* GETS(3) Linux Programmer's Manual
497 fgets() reads in at most one less than size characters from stream and
498 stores them into the buffer pointed to by s. Reading stops after an
499 EOF or a newline. If a newline is read, it is stored into the buffer.
500 A '\0' is stored after the last character in the buffer.
502 /*buf[strlen (buf) - 1] = '\0';*/
503 /* Skip whitespace */
504 for (line = buf; isspace (*line); ++line)
506 if (line[0] == '\0' || line[0] == '#' )
508 process_config_line (line, event_mask);
513 read_config_file_err:
514 #ifdef CONFIG_DEVFSD_VERBOSE
515 msg_logger(((optional == 0 ) && (errno == ENOENT))? DIE : NO_DIE, LOG_ERR, "read config file: %s: %m\n", path);
518 } /* End Function read_config_file */
520 static void process_config_line (const char *line, unsigned long *event_mask)
521 /* [SUMMARY] Process a line from a configuration file.
522 <line> The configuration line.
523 <event_mask> The event mask is written here. This is not initialised.
528 struct config_entry_struct *new;
529 char p[MAX_ARGS][STRING_LENGTH];
530 char when[STRING_LENGTH], what[STRING_LENGTH];
531 char name[STRING_LENGTH];
535 #ifdef CONFIG_DEVFSD_DEBUG
536 msg_logger( NO_DIE, LOG_INFO, "process_config_line()\n");
539 for (count = 0; count < MAX_ARGS; ++count) p[count][0] = '\0';
540 num_args = sscanf (line, "%s %s %s %s %s %s %s %s %s %s",
542 p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
543 if (strcasecmp (when, "CLEAR_CONFIG") == 0)
550 goto process_config_line_err;
552 if ( (strcasecmp (when, "INCLUDE") == 0) ||
553 (strcasecmp (when, "OPTIONAL_INCLUDE") == 0) )
555 st_expr_expand (name, STRING_LENGTH, name, get_variable, NULL );
556 #ifdef CONFIG_DEVFSD_VERBOSE
557 msg_logger( NO_DIE, LOG_INFO, "%sinclude: %s\n",(toupper (when[0]) == 'I') ? "": "optional_", name);
559 read_config_file (name, (toupper (when[0]) == 'I') ? FALSE : TRUE, event_mask);
562 if (strcasecmp (when, "RESTORE") == 0)
564 dir_operation(RESTORE,name, strlen (name),NULL);
568 goto process_config_line_err;
570 new = xmalloc (sizeof *new);
571 memset (new, 0, sizeof *new);
573 for (count = 0; event_types[count].config_name != NULL; ++count)
575 if (strcasecmp (when, event_types[count].config_name) != 0)
577 new->action.when = event_types[count].type;
580 if (event_types[count].config_name == NULL)
583 goto process_config_line_err;
586 if (strcasecmp (what, "PERMISSIONS") == 0)
588 new->action.what = AC_PERMISSIONS;
589 /* Get user and group */
590 if ( ( ptr = strchr (p[0], '.') ) == NULL )
593 goto process_config_line_err; /*"missing '.' in UID.GID */
597 new->u.permissions.uid = get_uid_gid (UID, p[0]);
598 new->u.permissions.gid = get_uid_gid (GID, ptr);
600 new->u.permissions.mode = get_mode (p[1]);
602 #ifdef CONFIG_DEVFSD_MODLOAD
603 else if (strcasecmp (what, "MODLOAD") == 0)
604 /*This action will pass "/dev/$devname" (i.e. "/dev/" prefixed to
605 the device name) to the module loading facility. In addition,
606 the /etc/modules.devfs configuration file is used.*/
608 new->action.what = AC_MODLOAD;
610 else if (strcasecmp (what, "EXECUTE") == 0)
612 new->action.what = AC_EXECUTE;
615 for (count = 0; count < num_args; ++count)
616 new->u.execute.argv[count] = bb_xstrdup (p[count]);
618 new->u.execute.argv[num_args] = NULL;
620 else if (strcasecmp (what, "COPY") == 0)
622 new->action.what = AC_COPY;
625 goto process_config_line_err; /* missing path and function in line */
627 new->u.copy.source = bb_xstrdup (p[0]);
628 new->u.copy.destination = bb_xstrdup (p[1]);
630 else if (strcasecmp (what, "IGNORE") == 0)
631 new->action.what = AC_IGNORE;
632 else if (strcasecmp (what, "MKOLDCOMPAT") == 0)
633 new->action.what = AC_MKOLDCOMPAT;
634 else if (strcasecmp (what, "MKNEWCOMPAT") == 0)
635 new->action.what = AC_MKNEWCOMPAT;
636 else if (strcasecmp (what, "RMOLDCOMPAT") == 0)
637 new->action.what = AC_RMOLDCOMPAT;
638 else if (strcasecmp (what, "RMNEWCOMPAT") == 0)
639 new->action.what = AC_RMNEWCOMPAT;
643 goto process_config_line_err;
646 xregcomp( &new->preg, name, REG_EXTENDED);
648 *event_mask |= 1 << new->action.when;
650 if (first_config == NULL)
653 last_config->next = new;
656 process_config_line_err:
657 msg_logger( DIE, LOG_ERR, bb_msg_bad_config, msg , line);
658 } /* End Function process_config_line */
660 static int do_servicing (int fd, unsigned long event_mask)
661 /* [SUMMARY] Service devfs changes until a signal is received.
662 <fd> The open control file.
663 <event_mask> The event mask.
664 [RETURNS] TRUE if SIGHUP was caught, else FALSE.
668 struct devfsd_notify_struct info;
669 unsigned long tmp_event_mask;
671 #ifdef CONFIG_DEVFSD_DEBUG
672 msg_logger( NO_DIE, LOG_INFO, "do_servicing()\n");
674 /* Tell devfs what events we care about */
675 tmp_event_mask = event_mask;
676 do_ioctl(DIE, fd, DEVFSDIOC_SET_EVENT_MASK, tmp_event_mask);
677 while (!caught_signal)
680 bytes = read (fd, (char *) &info, sizeof info);
682 break; /* Must test for this first */
684 continue; /* Yes, the order is important */
687 service_name (&info);
691 int c_sighup = caught_sighup;
693 caught_signal = FALSE;
694 caught_sighup = FALSE;
697 #ifdef CONFIG_DEVFSD_VERBOSE
698 msg_logger( NO_DIE, LOG_ERR, "read error on control file: %m\n");
700 /* This is to shut up a compiler warning */
702 } /* End Function do_servicing */
704 static void service_name (const struct devfsd_notify_struct *info)
705 /* [SUMMARY] Service a single devfs change.
706 <info> The devfs change.
711 regmatch_t mbuf[MAX_SUBEXPR];
712 struct config_entry_struct *entry;
714 #ifdef CONFIG_DEVFSD_DEBUG
715 msg_logger( NO_DIE, LOG_INFO, "service_name()\n");
717 #ifdef CONFIG_DEVFSD_VERBOSE
718 if (info->overrun_count > 0)
719 msg_logger( NO_DIE, LOG_ERR, "lost %u events\n", info->overrun_count);
722 /* Discard lookups on "/dev/log" and "/dev/initctl" */
723 if (info->type == DEVFSD_NOTIFY_LOOKUP &&
724 (( (strcmp (info->devname, "log") == 0) ||
725 (strcmp (info->devname, "initctl") == 0))))
727 for (entry = first_config; entry != NULL; entry = entry->next)
729 /* First check if action matches the type, then check if name matches */
730 if (info->type != entry->action.when || regexec (&entry->preg, info->devname, MAX_SUBEXPR, mbuf, 0) != 0 )
732 for (n = 0; (n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n)
734 switch (entry->action.what)
737 #ifdef CONFIG_DEVFSD_DEBUG
738 msg_logger( NO_DIE, LOG_INFO, "AC_PERMISSIONS\n");
740 action_permissions (info, entry);
742 #ifdef CONFIG_DEVFSD_MODLOAD
744 #ifdef CONFIG_DEVFSD_DEBUG
745 msg_logger( NO_DIE, LOG_INFO, "AC_MODLOAD\n");
747 action_modload (info, entry);
751 #ifdef CONFIG_DEVFSD_DEBUG
752 msg_logger( NO_DIE, LOG_INFO, "AC_EXECUTE\n");
754 action_execute (info, entry, mbuf, n);
757 #ifdef CONFIG_DEVFSD_DEBUG
758 msg_logger( NO_DIE, LOG_INFO, "AC_COPY\n");
760 action_copy (info, entry, mbuf, n);
763 #ifdef CONFIG_DEVFSD_DEBUG
764 msg_logger( NO_DIE, LOG_INFO, "AC_IGNORE\n");
772 #ifdef CONFIG_DEVFSD_DEBUG
773 msg_logger( NO_DIE, LOG_INFO, "AC_COMPAT\n");
775 action_compat (info, entry->action.what);
778 msg_logger( DIE, LOG_ERR, "Unknown action\n");
782 } /* End Function service_name */
784 static void action_permissions (const struct devfsd_notify_struct *info,
785 const struct config_entry_struct *entry)
786 /* [SUMMARY] Update permissions for a device entry.
787 <info> The devfs change.
788 <entry> The config file entry.
794 #ifdef CONFIG_DEVFSD_DEBUG
795 msg_logger( NO_DIE, LOG_INFO, "action_permission()\n");
798 if ( stat (info->devname, &statbuf) != 0 ||
799 chmod (info->devname,(statbuf.st_mode & S_IFMT) | (entry->u.permissions.mode & ~S_IFMT)) != 0 ||
800 chown (info->devname, entry->u.permissions.uid, entry->u.permissions.gid) != 0)
802 #ifdef CONFIG_DEVFSD_VERBOSE
803 msg_logger( NO_DIE, LOG_ERR, "chmod() or chown(): %s: %m\n",info->devname);
808 } /* End Function action_permissions */
810 #ifdef CONFIG_DEVFSD_MODLOAD
811 static void action_modload (const struct devfsd_notify_struct *info,
812 const struct config_entry_struct *entry)
813 /* [SUMMARY] Load a module.
814 <info> The devfs change.
815 <entry> The config file entry.
820 char device[STRING_LENGTH];
825 argv[3] = CONFIG_MODULES_DEVFS;
828 #ifdef CONFIG_DEVFSD_DEBUG
829 msg_logger( NO_DIE, LOG_INFO, "action_modload()\n");
831 snprintf (device, sizeof (device), "/dev/%s", info->devname);
832 fork_and_execute(DIE, argv[0], argv);
833 } /* End Function action_modload */
836 static void action_execute (const struct devfsd_notify_struct *info,
837 const struct config_entry_struct *entry,
838 const regmatch_t *regexpr, unsigned int numexpr)
839 /* [SUMMARY] Execute a programme.
840 <info> The devfs change.
841 <entry> The config file entry.
842 <regexpr> The number of subexpression (start, end) offsets within the
844 <numexpr> The number of elements within <<regexpr>>.
849 struct get_variable_info gv_info;
850 char *argv[MAX_ARGS + 1];
851 char largv[MAX_ARGS + 1][STRING_LENGTH];
853 #ifdef CONFIG_DEVFSD_DEBUG
857 msg_logger( NO_DIE, LOG_INFO, "action_execute()\n");
861 gv_info.devname = info->devname;
862 snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
863 for (count = 0; entry->u.execute.argv[count] != NULL; ++count)
865 expand_expression (largv[count], STRING_LENGTH,
866 entry->u.execute.argv[count],
867 get_variable, &gv_info,
868 gv_info.devname, regexpr, numexpr );
869 argv[count] = largv[count];
873 #ifdef CONFIG_DEVFSD_DEBUG
875 for(i=0;argv[i]!=NULL;i++) /* argv[i] < MAX_ARGS + 1 */
878 if( (strlen(buff)+ 1 + strlen(argv[i])) >= 1024)
880 strcat(buff,argv[i]);
883 msg_logger( NO_DIE, LOG_INFO, "action_execute(): %s",buff);
886 fork_and_execute(NO_DIE, argv[0], argv);
887 } /* End Function action_execute */
890 static void action_copy (const struct devfsd_notify_struct *info,
891 const struct config_entry_struct *entry,
892 const regmatch_t *regexpr, unsigned int numexpr)
893 /* [SUMMARY] Copy permissions.
894 <info> The devfs change.
895 <entry> The config file entry.
896 <regexpr> This list of subexpression (start, end) offsets within the
898 <numexpr> The number of elements in <<regexpr>>.
903 struct get_variable_info gv_info;
904 struct stat source_stat, dest_stat;
905 char source[STRING_LENGTH], destination[STRING_LENGTH];
906 dest_stat.st_mode = 0;
908 #ifdef CONFIG_DEVFSD_DEBUG
909 msg_logger( NO_DIE, LOG_INFO, "action_copy()\n");
912 if ( (info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK (info->mode) )
915 gv_info.devname = info->devname;
916 snprintf (gv_info.devpath, sizeof (gv_info.devpath), "%s/%s", mount_point, info->devname);
918 expand_expression (source, STRING_LENGTH, entry->u.copy.source,
919 get_variable, &gv_info, gv_info.devname,
922 expand_expression (destination, STRING_LENGTH, entry->u.copy.destination,
923 get_variable, &gv_info, gv_info.devname,
926 if ( !make_dir_tree (destination) || lstat (source, &source_stat) != 0)
928 lstat (destination, &dest_stat);
929 new_mode = source_stat.st_mode & ~S_ISVTX;
930 if (info->type == DEVFSD_NOTIFY_CREATE)
932 else if ( (info->type == DEVFSD_NOTIFY_CHANGE) && (dest_stat.st_mode & S_ISVTX) )
934 #ifdef CONFIG_DEVFSD_VERBOSE
935 if ( !copy_inode (destination, &dest_stat, new_mode, source, &source_stat) )
936 msg_logger( NO_DIE, LOG_ERR, "copy_inode(): %s to %s: %m\n", source, destination);
938 copy_inode (destination, &dest_stat, new_mode, source, &source_stat);
941 } /* End Function action_copy */
943 static void action_compat (const struct devfsd_notify_struct *info,
945 /* [SUMMARY] Process a compatibility request.
946 <info> The devfs change.
947 <action> The action to take.
951 const char *compat_name = NULL;
952 const char *dest_name = info->devname;
954 char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH];
956 #ifdef CONFIG_DEVFSD_DEBUG
957 msg_logger( NO_DIE, LOG_INFO, "action_compat()\n");
960 /* First construct compatibility name */
965 compat_name = get_old_name (info->devname, info->namelen, compat_buf, info->major, info->minor);
969 if (strncmp (info->devname, "scsi/", 5) == 0)
971 int mode, host, bus, target, lun;
973 sscanf (info->devname + 5, "host%d/bus%d/target%d/lun%d/",
974 &host, &bus, &target, &lun);
975 compat_name = compat_buf;
976 snprintf (dest_buf, sizeof (dest_buf), "../%s", info->devname);
977 dest_name = dest_buf;
978 if (strncmp (ptr = (strrchr (info->devname, '/') + 1), "mt", 2)== 0)
980 char rewind_ = info->devname[info->namelen - 1];
1000 sprintf (compat_buf, "st/c%db%dt%du%dm%d%c", host, bus, target, lun, mode, rewind_);
1002 else if (strcmp (info->devname + info->namelen - 7,"generic") == 0)
1003 sprintf (compat_buf, "sg/c%db%dt%du%d",
1004 host, bus, target, lun);
1005 else if (strcmp (info->devname + info->namelen - 2, "cd") == 0)
1006 sprintf (compat_buf, "sr/c%db%dt%du%d",
1007 host, bus, target, lun);
1008 else if (strcmp (info->devname + info->namelen - 4, "disc") == 0)
1009 sprintf (compat_buf, "sd/c%db%dt%du%d",
1010 host, bus, target, lun);
1011 else if (strncmp (ptr = (strrchr (info->devname, '/') + 1), "part", 4) == 0)
1012 sprintf ( compat_buf, "sd/c%db%dt%du%dp%d",
1013 host, bus, target, lun, atoi (ptr + 4) );
1014 else compat_name = NULL;
1016 else if (strncmp (info->devname, "ide/host", 8) == 0)
1018 int host, bus, target, lun;
1020 sscanf (info->devname + 4, "host%d/bus%d/target%d/lun%d/",
1021 &host, &bus, &target, &lun);
1022 compat_name = compat_buf;
1023 snprintf (dest_buf, sizeof (dest_buf), "../%s", info->devname + 4);
1024 dest_name = dest_buf;
1025 if (strcmp (info->devname + info->namelen - 4, "disc") == 0)
1026 sprintf (compat_buf, "ide/hd/c%db%dt%du%d",
1027 host, bus, target, lun);
1028 else if (strncmp (ptr = (strrchr (info->devname, '/') + 1), "part", 4) == 0)
1029 sprintf ( compat_buf, "ide/hd/c%db%dt%du%dp%d",
1030 host, bus, target, lun, atoi (ptr + 4) );
1031 else if (strcmp (info->devname + info->namelen - 2, "cd") == 0)
1032 sprintf (compat_buf, "ide/cd/c%db%dt%du%d",
1033 host, bus, target,lun);
1034 else if (strncmp (ptr = (strrchr (info->devname, '/') + 1), "mt", 2) == 0)
1035 snprintf (compat_buf, sizeof (compat_buf), "ide/mt/c%db%dt%du%d%s",
1036 host, bus, target, lun, ptr + 2);
1037 else compat_name = NULL;
1041 if (compat_name == NULL)
1043 /* Now decide what to do with it */
1046 case AC_MKOLDCOMPAT:
1047 case AC_MKNEWCOMPAT:
1048 mksymlink (dest_name, compat_name);
1050 case AC_RMOLDCOMPAT:
1051 case AC_RMNEWCOMPAT:
1052 #ifdef CONFIG_DEVFSD_VERBOSE
1053 if (unlink (compat_name) != 0)
1054 msg_logger( NO_DIE, LOG_ERR, "unlink(): %s: %m\n", compat_name);
1056 unlink (compat_name);
1060 } /* End Function action_compat */
1062 static void restore(char *spath, struct stat source_stat, int rootlen)
1064 char dpath[STRING_LENGTH];
1065 struct stat dest_stat;
1067 #ifdef CONFIG_DEVFSD_DEBUG
1068 msg_logger( NO_DIE, LOG_INFO, "restore()\n");
1071 dest_stat.st_mode = 0;
1072 snprintf (dpath, sizeof dpath, "%s%s", mount_point, spath + rootlen);
1073 lstat (dpath, &dest_stat);
1075 if ( S_ISLNK (source_stat.st_mode) || (source_stat.st_mode & S_ISVTX) )
1076 copy_inode (dpath, &dest_stat, (source_stat.st_mode & ~S_ISVTX) , spath, &source_stat);
1078 if ( S_ISDIR (source_stat.st_mode) )
1079 dir_operation(RESTORE, spath, rootlen,NULL);
1083 static int copy_inode (const char *destpath, const struct stat *dest_stat,
1085 const char *sourcepath, const struct stat *source_stat)
1086 /* [SUMMARY] Copy an inode.
1087 <destpath> The destination path. An existing inode may be deleted.
1088 <dest_stat> The destination stat(2) information.
1089 <new_mode> The desired new mode for the destination.
1090 <sourcepath> The source path.
1091 <source_stat> The source stat(2) information.
1092 [RETURNS] TRUE on success, else FALSE.
1096 #ifdef CONFIG_DEVFSD_DEBUG
1097 msg_logger( NO_DIE, LOG_INFO, "copy_inode()\n");
1100 if ( (source_stat->st_mode & S_IFMT) == (dest_stat->st_mode & S_IFMT) )
1103 if ( S_ISLNK (source_stat->st_mode) )
1105 int source_len, dest_len;
1106 char source_link[STRING_LENGTH], dest_link[STRING_LENGTH];
1109 if (( source_len = readlink (sourcepath, source_link, STRING_LENGTH - 1) ) < 0 ||
1110 ( dest_len = readlink (destpath , dest_link , STRING_LENGTH - 1) ) < 0 )
1112 source_link[source_len] = '\0';
1113 dest_link[dest_len] = '\0';
1114 if ( (source_len != dest_len) || (strcmp (source_link, dest_link) != 0) )
1117 symlink (source_link, destpath);
1120 } /* Else not a symlink */
1121 chmod (destpath, new_mode & ~S_IFMT);
1122 chown (destpath, source_stat->st_uid, source_stat->st_gid);
1125 /* Different types: unlink and create */
1127 switch (source_stat->st_mode & S_IFMT)
1130 struct sockaddr_un un_addr;
1131 char symlink_val[STRING_LENGTH];
1134 if ( ( fd = socket (AF_UNIX, SOCK_STREAM, 0) ) < 0 )
1136 un_addr.sun_family = AF_UNIX;
1137 snprintf (un_addr.sun_path, sizeof (un_addr.sun_path), "%s", destpath);
1138 val = bind (fd, (struct sockaddr *) &un_addr, (int) sizeof un_addr);
1140 if (val != 0 || chmod (destpath, new_mode & ~S_IFMT) != 0)
1144 if ( ( val = readlink (sourcepath, symlink_val, STRING_LENGTH - 1) ) < 0 )
1146 symlink_val[val] = '\0';
1147 if (symlink (symlink_val, destpath) == 0)
1151 if ( ( fd = open (destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT) ) < 0 )
1154 if (chmod (destpath, new_mode & ~S_IFMT) != 0)
1160 if (mknod (destpath, new_mode, source_stat->st_rdev) != 0)
1164 if (mkdir (destpath, new_mode & ~S_IFMT) != 0)
1167 if (chown (destpath, source_stat->st_uid, source_stat->st_gid) == 0)
1172 } /* End Function copy_inode */
1174 static void free_config ()
1175 /* [SUMMARY] Free the configuration information.
1179 struct config_entry_struct *c_entry;
1182 #ifdef CONFIG_DEVFSD_DEBUG
1183 msg_logger( NO_DIE, LOG_INFO, "free_config()\n");
1186 for (c_entry = first_config; c_entry != NULL; c_entry = next)
1190 next = c_entry->next;
1191 regfree (&c_entry->preg);
1192 if (c_entry->action.what == AC_EXECUTE)
1194 for (count = 0; count < MAX_ARGS; ++count)
1196 if (c_entry->u.execute.argv[count] == NULL)
1198 free (c_entry->u.execute.argv[count]);
1203 first_config = NULL;
1205 } /* End Function free_config */
1207 static int get_uid_gid (int flag, const char *string)
1208 /* [SUMMARY] Convert a string to a UID or GID value.
1209 <flag> "UID" or "GID".
1210 <string> The string.
1211 [RETURNS] The UID or GID value.
1214 struct passwd *pw_ent;
1215 struct group *grp_ent;
1218 #ifdef CONFIG_DEVFSD_DEBUG
1219 msg_logger( NO_DIE, LOG_INFO, "get_uid_gid()\n");
1222 if(flag != UID && flag != GID )
1223 msg_logger( DIE, LOG_ERR,"get_uid_gid(): flag != UID && flag != GID\n");
1226 if ( isdigit (string[0]) || ( (string[0] == '-') && isdigit (string[1]) ) )
1227 return atoi (string);
1229 if ( flag == UID && ( pw_ent = getpwnam (string) ) != NULL )
1230 return (pw_ent->pw_uid);
1232 if ( flag == GID && ( grp_ent = getgrnam (string) ) != NULL )
1233 return (grp_ent->gr_gid);
1237 msg_logger( NO_DIE, LOG_ERR,"unknown %s: %s, defaulting to %cID=0\n", msg, string, msg[0] - 32);
1239 }/* End Function get_uid_gid */
1241 static mode_t get_mode (const char *string)
1242 /* [SUMMARY] Convert a string to a mode value.
1243 <string> The string.
1244 [RETURNS] The mode value.
1249 #ifdef CONFIG_DEVFSD_DEBUG
1250 msg_logger( NO_DIE, LOG_INFO, "get_mode()\n");
1253 if ( isdigit (string[0]) )
1254 return strtoul (string, NULL, 8);
1255 if (strlen (string) != 9)
1256 msg_logger( DIE, LOG_ERR, "bad mode: %s\n", string);
1261 if(string[0]=='r'||string[0]=='w'||string[0]=='x')
1267 } /* End Function get_mode */
1269 static void signal_handler (int sig)
1271 #ifdef CONFIG_DEVFSD_DEBUG
1272 msg_logger( NO_DIE, LOG_INFO, "signal_handler()\n");
1275 caught_signal = TRUE;
1277 caught_sighup = TRUE;
1278 #ifdef CONFIG_DEVFSD_VERBOSE
1279 msg_logger( NO_DIE, LOG_INFO, "Caught %s\n",(sig == SIGHUP)?"SIGHUP" : "SIGUSR1");
1281 } /* End Function signal_handler */
1283 static const char *get_variable (const char *variable, void *info)
1285 struct get_variable_info *gv_info = info;
1286 static char hostname[STRING_LENGTH], sbuf[STRING_LENGTH];
1288 #ifdef CONFIG_DEVFSD_DEBUG
1289 msg_logger( NO_DIE, LOG_INFO, "get_variable()\n");
1292 if (gethostname (hostname, STRING_LENGTH - 1) != 0)
1293 msg_logger( DIE, LOG_ERR, "gethostname(): %m\n");
1294 /* Here on error we should do exit(RV_SYS_ERROR), instead we do exit(EXIT_FAILURE) */
1295 hostname[STRING_LENGTH - 1] = '\0';
1296 if (strcmp (variable, "hostname") == 0)
1298 if (strcmp (variable, "mntpnt") == 0)
1299 return (mount_point);
1300 if (gv_info == NULL)
1302 if (strcmp (variable, "devpath") == 0)
1303 return (gv_info->devpath);
1304 if (strcmp (variable, "devname") == 0)
1305 return (gv_info->devname);
1306 if (strcmp (variable, "mode") == 0)
1308 sprintf (sbuf, "%o", gv_info->info->mode);
1311 if (strcmp (variable, "uid") == 0)
1313 sprintf (sbuf, "%u", gv_info->info->uid);
1316 if (strcmp (variable, "gid") == 0)
1318 sprintf (sbuf, "%u", gv_info->info->gid);
1322 } /* End Function get_variable */
1324 static void service(struct stat statbuf, char *path)
1326 struct devfsd_notify_struct info;
1328 #ifdef CONFIG_DEVFSD_DEBUG
1329 msg_logger( NO_DIE, LOG_INFO, "service()\n");
1332 memset (&info, 0, sizeof info);
1333 info.type = DEVFSD_NOTIFY_REGISTERED;
1334 info.mode = statbuf.st_mode;
1335 info.major = major (statbuf.st_rdev);
1336 info.minor = minor (statbuf.st_rdev);
1337 info.uid = statbuf.st_uid;
1338 info.gid = statbuf.st_gid;
1339 snprintf (info.devname, sizeof (info.devname), "%s", path + strlen (mount_point) + 1);
1340 info.namelen = strlen (info.devname);
1341 service_name (&info);
1342 if ( S_ISDIR (statbuf.st_mode) )
1343 dir_operation(SERVICE,path,0,NULL);
1346 static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask)
1347 /* [SUMMARY] Scan a directory tree and generate register events on leaf nodes.
1348 <flag> To choose which function to perform
1349 <dp> The directory pointer. This is closed upon completion.
1350 <dir_name> The name of the directory.
1351 <rootlen> string length parameter.
1355 struct stat statbuf;
1358 char path[STRING_LENGTH];
1361 #ifdef CONFIG_DEVFSD_DEBUG
1362 msg_logger( NO_DIE, LOG_INFO, "dir_operation()\n");
1365 if((dp = opendir( dir_name))==NULL)
1367 msg_logger( NO_DIE, LOG_ERR, "opendir(): %s: %m\n", dir_name);
1371 while ( (de = readdir (dp) ) != NULL )
1374 if ( (strcmp (de->d_name, ".") == 0) || (strcmp (de->d_name, "..") == 0) )
1376 snprintf (path, sizeof (path), "%s%s%s", dir_name,(last_char_is(dir_name,'/')==NULL)?"/":"", de->d_name);
1378 if (lstat (path, &statbuf) != 0)
1380 #ifdef CONFIG_DEVFSD_VERBOSE
1381 msg_logger( NO_DIE, LOG_ERR, "%s: %m\n", path);
1388 service(statbuf,path);
1391 restore(path, statbuf, var);
1394 read_config_file (path, var, event_mask);
1399 } /* End Function do_scan_and_service */
1401 static int mksymlink (const char *oldpath, const char *newpath)
1402 /* [SUMMARY] Create a symlink, creating intervening directories as required.
1403 <oldpath> The string contained in the symlink.
1404 <newpath> The name of the new symlink.
1405 [RETURNS] 0 on success, else -1.
1408 #ifdef CONFIG_DEVFSD_DEBUG
1409 msg_logger( NO_DIE, LOG_INFO, "mksymlink()\n", newpath);
1413 if ( !make_dir_tree (newpath) )
1416 if (symlink (oldpath, newpath) != 0)
1418 #ifdef CONFIG_DEVFSD_VERBOSE
1419 msg_logger( NO_DIE, LOG_ERR, "symlink(): %s to %s: %m\n", oldpath, newpath);
1421 if (errno != EEXIST)
1425 } /* End Function mksymlink */
1428 static int make_dir_tree (const char *path)
1429 /* [SUMMARY] Creating intervening directories for a path as required.
1430 <path> The full pathname (including the leaf node).
1431 [RETURNS] TRUE on success, else FALSE.
1434 #ifdef CONFIG_DEVFSD_DEBUG
1435 msg_logger( NO_DIE, LOG_INFO, "make_dir_tree()\n");
1437 if (bb_make_directory( dirname((char *)path), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ,FILEUTILS_RECUR )==-1)
1439 #ifdef CONFIG_DEVFSD_VERBOSE
1440 msg_logger( NO_DIE, LOG_ERR, "make_dir_tree(): %s: %m\n", path);
1445 } /* End Function make_dir_tree */
1447 static int expand_expression(char *output, unsigned int outsize,
1449 const char *(*get_variable_func)(const char *variable, void *info),
1451 const char *devname,
1452 const regmatch_t *ex, unsigned int numexp)
1453 /* [SUMMARY] Expand enviroment variables and regular subexpressions in string.
1454 <output> The output expanded expression is written here.
1455 <length> The size of the output buffer.
1456 <input> The input expression. This may equal <<output>>.
1457 <get_variable> A function which will be used to get variable values. If
1458 this returns NULL, the environment is searched instead. If this is NULL,
1459 only the environment is searched.
1460 <info> An arbitrary pointer passed to <<get_variable>>.
1461 <devname> Device name; specifically, this is the string that contains all
1462 of the regular subexpressions.
1463 <ex> Array of start / end offsets into info->devname for each subexpression
1464 <numexp> Number of regular subexpressions found in <<devname>>.
1465 [RETURNS] TRUE on success, else FALSE.
1468 char temp[STRING_LENGTH];
1470 #ifdef CONFIG_DEVFSD_DEBUG
1471 msg_logger( NO_DIE, LOG_INFO, "expand_expression()\n");
1474 if ( !st_expr_expand (temp, STRING_LENGTH, input, get_variable_func, info) )
1476 expand_regexp (output, outsize, temp, devname, ex, numexp);
1478 } /* End Function expand_expression */
1480 static void expand_regexp (char *output, size_t outsize, const char *input,
1481 const char *devname,
1482 const regmatch_t *ex, unsigned int numex )
1483 /* [SUMMARY] Expand all occurrences of the regular subexpressions \0 to \9.
1484 <output> The output expanded expression is written here.
1485 <outsize> The size of the output buffer.
1486 <input> The input expression. This may NOT equal <<output>>, because
1487 supporting that would require yet another string-copy. However, it's not
1488 hard to write a simple wrapper function to add this functionality for those
1489 few cases that need it.
1490 <devname> Device name; specifically, this is the string that contains all
1491 of the regular subexpressions.
1492 <ex> An array of start and end offsets into <<devname>>, one for each
1494 <numex> Number of subexpressions in the offset-array <<ex>>.
1498 const char last_exp = '0' - 1 + numex;
1501 #ifdef CONFIG_DEVFSD_DEBUG
1502 msg_logger( NO_DIE, LOG_INFO, "expand_regexp()\n");
1505 /* Guarantee NULL termination by writing an explicit '\0' character into
1506 the very last byte */
1508 output[--outsize] = '\0';
1509 /* Copy the input string into the output buffer, replacing '\\' with '\'
1510 and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x
1511 codes are deleted */
1512 while ( (c != '\0') && (outsize != 0) )
1522 if ((c >= '0') && (c <= last_exp))
1524 const regmatch_t *subexp = ex + (c - '0');
1525 unsigned int sublen = subexp->rm_eo - subexp->rm_so;
1527 /* Range checking */
1528 if (sublen > outsize)
1530 strncpy (output, devname + subexp->rm_so, sublen);
1541 } /* End Function expand_regexp */
1544 /* from compat_name.c */
1546 struct translate_struct
1548 char *match; /* The string to match to (up to length) */
1549 char *format; /* Format of output, "%s" takes data past match string,
1550 NULL is effectively "%s" (just more efficient) */
1553 static struct translate_struct translate_table[] =
1556 {"printers/", "lp%s"},
1558 {"parports/", "parport%s"},
1561 {"loop/", "loop%s"},
1562 {"floppy/", "fd%s"},
1564 {"md/", "md%s"}, /* Meta-devices */
1568 {"pg/", "pg%s"}, /* Parallel port generic ATAPI interface*/
1570 {"staliomem/", "staliomem%s"}, /* Stallion serial driver control */
1571 {"tts/E", "ttyE%s"}, /* Stallion serial driver */
1572 {"cua/E", "cue%s"}, /* Stallion serial driver callout */
1573 {"tts/R", "ttyR%s"}, /* Rocketport serial driver */
1574 {"cua/R", "cur%s"}, /* Rocketport serial driver callout */
1575 {"ip2/", "ip2%s"}, /* Computone serial driver control */
1576 {"tts/F", "ttyF%s"}, /* Computone serial driver */
1577 {"cua/F", "cuf%s"}, /* Computone serial driver callout */
1578 {"tts/C", "ttyC%s"}, /* Cyclades serial driver */
1579 {"cua/C", "cub%s"}, /* Cyclades serial driver callout */
1580 {"tts/", "ttyS%s"}, /* Generic serial: must be after others */
1581 {"cua/", "cua%s"}, /* Generic serial: must be after others */
1582 {"input/js", "js%s"}, /* Joystick driver */
1586 const char *get_old_name (const char *devname, unsigned int namelen,
1587 char *buffer, unsigned int major, unsigned int minor)
1588 /* [SUMMARY] Translate a kernel-supplied name into an old name.
1589 <devname> The device name provided by the kernel.
1590 <namelen> The length of the name.
1591 <buffer> A buffer that may be used. This should be at least 128 bytes long.
1592 <major> The major number for the device.
1593 <minor> The minor number for the device.
1594 [RETURNS] A pointer to the old name if known, else NULL.
1597 const char *compat_name = NULL;
1599 struct translate_struct *trans;
1601 #ifdef CONFIG_DEVFSD_DEBUG
1602 msg_logger( NO_DIE, LOG_INFO, "get_old_name()\n");
1605 for (trans = translate_table; trans->match != NULL; ++trans)
1607 size_t len = strlen (trans->match);
1609 if (strncmp (devname, trans->match, len) == 0)
1611 if (trans->format == NULL)
1612 return (devname + len);
1613 sprintf (buffer, trans->format, devname + len);
1617 if (strncmp (devname, "sbp/", 4) == 0)
1619 sprintf (buffer, "sbpcd%u", minor);
1620 compat_name = buffer;
1622 else if (strncmp (devname, "scsi/", 5) == 0)
1623 { /* All SCSI devices */
1624 if (strcmp (devname + namelen - 7, "generic") == 0)
1626 sprintf (buffer, "sg%u", minor);
1627 compat_name = buffer;
1629 else if (strncmp (ptr = (strrchr (devname, '/') + 1), "mt", 2) == 0)
1635 sprintf (buffer, "nst%u%c", minor & 0x1f, mode);
1636 compat_name = buffer;
1637 if (devname[namelen - 1] != 'n')
1640 else if (strcmp (devname + namelen - 2, "cd") == 0)
1642 sprintf (buffer, "sr%u", minor);
1643 compat_name = buffer;
1645 else if (strcmp (devname + namelen - 4, "disc") == 0)
1646 compat_name = write_old_sd_name (buffer, major, minor, "");
1647 else if (strncmp (ptr = (strrchr (devname, '/') + 1), "part", 4) == 0)
1648 compat_name = write_old_sd_name (buffer, major, minor, ptr + 4);
1649 return (compat_name);
1651 else if (strncmp (devname, "ide/host", 8) == 0)
1652 { /* All IDE devices */
1653 if (strncmp (ptr = (strrchr (devname, '/') + 1), "mt", 2) == 0)
1655 sprintf (buffer, "%sht%d", ptr + 2, minor & 0x7f);
1656 compat_name = buffer;
1658 else if (strcmp (devname + namelen - 4, "disc") == 0)
1660 sprintf ( buffer, "hd%c", get_old_ide_name (major, minor) );
1661 compat_name = buffer;
1663 else if (strncmp (ptr = (strrchr (devname, '/') + 1), "part", 4) == 0)
1665 sprintf (buffer, "hd%c%s", get_old_ide_name (major, minor), ptr + 4);
1666 compat_name = buffer;
1668 else if (strcmp (devname + namelen - 2, "cd") == 0)
1670 sprintf ( buffer, "hd%c", get_old_ide_name (major, minor) );
1671 compat_name = buffer;
1673 return (compat_name);
1675 else if (strncmp (devname, "vcc/", 4) == 0)
1677 sprintf (buffer, "vcs%s", devname + 4);
1678 if (buffer[3] == '0')
1680 compat_name = buffer;
1682 else if (strncmp (devname, "pty/", 4) == 0)
1684 int indexx = atoi (devname + 5);
1685 const char *pty1 = "pqrstuvwxyzabcde";
1686 const char *pty2 = "0123456789abcdef";
1688 sprintf (buffer, "%cty%c%c", (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]);
1689 compat_name = buffer;
1691 return (compat_name);
1692 } /* End Function get_old_name */
1694 static char get_old_ide_name (unsigned int major, unsigned int minor)
1695 /* [SUMMARY] Get the old IDE name for a device.
1696 <major> The major number for the device.
1697 <minor> The minor number for the device.
1698 [RETURNS] The drive letter.
1701 char letter='y'; /* 121 */
1702 char c='a'; /* 97 */
1705 #ifdef CONFIG_DEVFSD_DEBUG
1706 msg_logger( NO_DIE, LOG_INFO, "get_old_ide_name()\n");
1709 /* I hope it works like the previous code as it saves a few bytes. Tito ;P */
1711 if( i==IDE0_MAJOR || i==IDE1_MAJOR || i==IDE2_MAJOR ||
1712 i==IDE3_MAJOR || i==IDE4_MAJOR || i==IDE5_MAJOR ||
1713 i==IDE6_MAJOR || i==IDE7_MAJOR || i==IDE8_MAJOR ||
1724 } while(i<=IDE9_MAJOR);
1729 } /* End Function get_old_ide_name */
1731 static char *write_old_sd_name (char *buffer,
1732 unsigned int major, unsigned int minor,
1734 /* [SUMMARY] Write the old SCSI disc name to a buffer.
1735 <buffer> The buffer to write to.
1736 <major> The major number for the device.
1737 <minor> The minor number for the device.
1738 <part> The partition string. Must be "" for a whole-disc entry.
1739 [RETURNS] A pointer to the buffer on success, else NULL.
1742 unsigned int disc_index;
1744 #ifdef CONFIG_DEVFSD_DEBUG
1745 msg_logger( NO_DIE, LOG_INFO, "write_old_sd_name()\n");
1750 sprintf (buffer, "sd%c%s", 'a' + (minor >> 4), part);
1753 if ( (major > 64) && (major < 72) )
1755 disc_index = ( (major - 64) << 4 ) + (minor >> 4);
1756 if (disc_index < 26)
1757 sprintf (buffer, "sd%c%s", 'a' + disc_index, part);
1759 sprintf (buffer, "sd%c%c%s", 'a' + (disc_index / 26) - 1, 'a' + disc_index % 26,part);
1763 } /* End Function write_old_sd_name */
1768 /*EXPERIMENTAL_FUNCTION*/
1770 int st_expr_expand (char *output, unsigned int length, const char *input,
1771 const char *(*get_variable_func) (const char *variable,
1774 /* [SUMMARY] Expand an expression using Borne Shell-like unquoted rules.
1775 <output> The output expanded expression is written here.
1776 <length> The size of the output buffer.
1777 <input> The input expression. This may equal <<output>>.
1778 <get_variable> A function which will be used to get variable values. If
1779 this returns NULL, the environment is searched instead. If this is NULL,
1780 only the environment is searched.
1781 <info> An arbitrary pointer passed to <<get_variable>>.
1782 [RETURNS] TRUE on success, else FALSE.
1787 unsigned int out_pos = 0;
1790 struct passwd *pwent;
1791 char buffer[BUFFER_SIZE], tmp[STRING_LENGTH];
1793 #ifdef CONFIG_DEVFSD_DEBUG
1794 msg_logger( NO_DIE, LOG_INFO, "st_expr_expand()\n");
1797 if (length > BUFFER_SIZE)
1798 length = BUFFER_SIZE;
1799 for (; TRUE; ++input)
1801 switch (ch = *input)
1804 /* Variable expansion */
1805 input = expand_variable (buffer, length, &out_pos, ++input, get_variable_func, info);
1810 /* Home directory expansion */
1812 if ( isspace (ch) || (ch == '/') || (ch == '\0') )
1814 /* User's own home directory: leave separator for next time */
1815 if ( ( env = getenv ("HOME") ) == NULL )
1817 #ifdef CONFIG_DEVFSD_VERBOSE
1818 msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, "HOME");
1823 if (len + out_pos >= length)
1824 goto st_expr_expand_out;
1825 memcpy (buffer + out_pos, env, len + 1);
1829 /* Someone else's home directory */
1830 for (ptr = ++input; !isspace (ch) && (ch != '/') && (ch != '\0'); ch = *++ptr)
1833 if (len >= sizeof tmp)
1834 goto st_expr_expand_out;
1835 safe_memcpy (tmp, input, len);
1837 if ( ( pwent = getpwnam (tmp) ) == NULL )
1839 #ifdef CONFIG_DEVFSD_VERBOSE
1840 msg_logger( NO_DIE, LOG_INFO, "getpwnam(): %s\n", tmp);
1844 len = strlen (pwent->pw_dir);
1845 if (len + out_pos >= length)
1846 goto st_expr_expand_out;
1847 memcpy (buffer + out_pos, pwent->pw_dir, len + 1);
1853 if (out_pos >= length)
1854 goto st_expr_expand_out;
1855 buffer[out_pos++] = ch;
1858 memcpy (output, buffer, out_pos);
1867 #ifdef CONFIG_DEVFSD_VERBOSE
1868 msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
1871 } /* End Function st_expr_expand */
1874 /* Private functions follow */
1876 static const char *expand_variable (char *buffer, unsigned int length,
1877 unsigned int *out_pos, const char *input,
1878 const char *(*func) (const char *variable,
1881 /* [SUMMARY] Expand a variable.
1882 <buffer> The buffer to write to.
1883 <length> The length of the output buffer.
1884 <out_pos> The current output position. This is updated.
1885 <input> A pointer to the input character pointer.
1886 <func> A function which will be used to get variable values. If this
1887 returns NULL, the environment is searched instead. If this is NULL, only
1888 the environment is searched.
1889 <info> An arbitrary pointer passed to <<func>>.
1890 <errfp> Diagnostic messages are written here.
1891 [RETURNS] A pointer to the end of this subexpression on success, else NULL.
1896 unsigned int open_braces;
1897 const char *env, *ptr;
1898 char tmp[STRING_LENGTH];
1900 #ifdef CONFIG_DEVFSD_DEBUG
1901 msg_logger( NO_DIE, LOG_INFO, "expand_variable()\n");
1907 /* Special case for "$$": PID */
1908 sprintf ( tmp, "%d", (int) getpid () );
1910 if (len + *out_pos >= length)
1911 goto expand_variable_out;
1913 memcpy (buffer + *out_pos, tmp, len + 1);
1917 /* Ordinary variable expansion, possibly in braces */
1920 /* Simple variable expansion */
1921 for (ptr = input; isalnum (ch) || (ch == '_') || (ch == ':');ch = *++ptr)
1924 if (len >= sizeof tmp)
1925 goto expand_variable_out;
1927 safe_memcpy (tmp, input, len);
1929 if ( ( env = get_variable_v2 (tmp, func, info) ) == NULL )
1931 #ifdef CONFIG_DEVFSD_VERBOSE
1932 msg_logger( NO_DIE, LOG_INFO, bb_msg_variable_not_found, tmp);
1937 if (len + *out_pos >= length)
1938 goto expand_variable_out;
1940 memcpy (buffer + *out_pos, env, len + 1);
1944 /* Variable in braces: check for ':' tricks */
1946 for (ptr = input; isalnum (ch) || (ch == '_'); ch = *++ptr)
1950 /* Must be simple variable expansion with "${var}" */
1952 if (len >= sizeof tmp)
1953 goto expand_variable_out;
1955 safe_memcpy (tmp, input, len);
1956 ptr = expand_variable (buffer, length, out_pos, tmp, func, info );
1959 return (input + len);
1961 if (ch != ':' || ptr[1] != '-' )
1963 #ifdef CONFIG_DEVFSD_VERBOSE
1964 msg_logger( NO_DIE, LOG_INFO,"illegal char in var name\n");
1968 /* It's that handy "${var:-word}" expression. Check if var is defined */
1970 if (len >= sizeof tmp)
1971 goto expand_variable_out;
1973 safe_memcpy (tmp, input, len);
1974 /* Move input pointer to ':' */
1976 /* First skip to closing brace, taking note of nested expressions */
1979 for (open_braces = 1; open_braces > 0; ch = *++ptr)
1990 #ifdef CONFIG_DEVFSD_VERBOSE
1991 msg_logger( NO_DIE, LOG_INFO,"\"}\" not found in: %s\n", input);
1999 /* At this point ptr should point to closing brace of "${var:-word}" */
2000 if ( ( env = get_variable_v2 (tmp, func, info) ) != NULL )
2002 /* Found environment variable, so skip the input to the closing brace
2003 and return the variable */
2006 if (len + *out_pos >= length)
2007 goto expand_variable_out;
2009 memcpy (buffer + *out_pos, env, len + 1);
2013 /* Environment variable was not found, so process word. Advance input
2014 pointer to start of word in "${var:-word}" */
2017 if (len >= sizeof tmp)
2018 goto expand_variable_out;
2020 safe_memcpy (tmp, input, len);
2022 if ( !st_expr_expand (tmp, STRING_LENGTH, tmp, func, info ) )
2025 if (len + *out_pos >= length)
2026 goto expand_variable_out;
2028 memcpy (buffer + *out_pos, tmp, len + 1);
2031 expand_variable_out:
2032 #ifdef CONFIG_DEVFSD_VERBOSE
2033 msg_logger( NO_DIE, LOG_INFO, bb_msg_small_buffer);
2036 } /* End Function expand_variable */
2039 static const char *get_variable_v2 (const char *variable,
2040 const char *(*func) (const char *variable, void *info),
2042 /* [SUMMARY] Get a variable from the environment or .
2043 <variable> The variable name.
2044 <func> A function which will be used to get the variable. If this returns
2045 NULL, the environment is searched instead. If this is NULL, only the
2046 environment is searched.
2047 [RETURNS] The value of the variable on success, else NULL.
2052 #ifdef CONFIG_DEVFSD_DEBUG
2053 msg_logger( NO_DIE, LOG_INFO, "get_variable_v2()\n");
2058 value = (*func) (variable, info);
2062 return getenv (variable);
2063 } /* End Function get_variable */