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