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