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