source: MondoRescue/branches/3.3/mindi-busybox/miscutils/devfsd.c@ 3647

Last change on this file since 3647 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

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