source: MondoRescue/branches/stable/mindi-busybox/miscutils/devfsd.c@ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 16 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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