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