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