aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debianutils/Config.in9
-rw-r--r--debianutils/run_parts.c219
-rw-r--r--include/usage.h19
3 files changed, 115 insertions, 132 deletions
diff --git a/debianutils/Config.in b/debianutils/Config.in
index 3d85999ff..c49197666 100644
--- a/debianutils/Config.in
+++ b/debianutils/Config.in
@@ -53,6 +53,15 @@ config FEATURE_RUN_PARTS_LONG_OPTIONS
53 help 53 help
54 Support long options for the run-parts applet. 54 Support long options for the run-parts applet.
55 55
56config FEATURE_RUN_PARTS_FANCY
57 bool "Support additional arguments"
58 default n
59 depends on RUN_PARTS
60 help
61 Support additional options:
62 -l --list print the names of the all matching files (not
63 limited to executables), but don't actually run them.
64
56config START_STOP_DAEMON 65config START_STOP_DAEMON
57 bool "start-stop-daemon" 66 bool "start-stop-daemon"
58 default y 67 default y
diff --git a/debianutils/run_parts.c b/debianutils/run_parts.c
index a864a0505..aa449c410 100644
--- a/debianutils/run_parts.c
+++ b/debianutils/run_parts.c
@@ -2,8 +2,10 @@
2/* 2/*
3 * Mini run-parts implementation for busybox 3 * Mini run-parts implementation for busybox
4 * 4 *
5 * Copyright (C) 2007 Bernhard Fischer
5 * 6 *
6 * Copyright (C) 2001 by Emanuele Aina <emanuele.aina@tiscali.it> 7 * Based on a older version that was in busybox which was 1k big..
8 * Copyright (C) 2001 by Emanuele Aina <emanuele.aina@tiscali.it>
7 * 9 *
8 * Based on the Debian run-parts program, version 1.15 10 * Based on the Debian run-parts program, version 1.15
9 * Copyright (C) 1996 Jeff Noxon <jeff@router.patch.net>, 11 * Copyright (C) 1996 Jeff Noxon <jeff@router.patch.net>,
@@ -25,168 +27,125 @@
25 * execute them. 27 * execute them.
26 * -a ARG argument. Pass ARG as an argument the program executed. It can 28 * -a ARG argument. Pass ARG as an argument the program executed. It can
27 * be repeated to pass multiple arguments. 29 * be repeated to pass multiple arguments.
28 * -u MASK umask. Set the umask of the program executed to MASK. */ 30 * -u MASK umask. Set the umask of the program executed to MASK.
29
30/* TODO
31 * done - convert calls to error in perror... and remove error()
32 * done - convert malloc/realloc to their x... counterparts
33 * done - remove catch_sigchld
34 * done - use bb's concat_path_file()
35 * done - declare run_parts_main() as extern and any other function as static?
36 */ 31 */
37 32
33
38#include "busybox.h" 34#include "busybox.h"
39#include <getopt.h> 35#include <getopt.h>
40 36
37#if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS
41static const struct option runparts_long_options[] = { 38static const struct option runparts_long_options[] = {
42 { "test", 0, NULL, 't' },
43 { "umask", 1, NULL, 'u' },
44 { "arg", 1, NULL, 'a' }, 39 { "arg", 1, NULL, 'a' },
40 { "umask", 1, NULL, 'u' },
41 { "test", 0, NULL, 't' },
42#if ENABLE_FEATURE_RUN_PARTS_FANCY
43 { "list", 0, NULL, 'l' },
44//XXX:TODO: { "reverse", 0, NULL, 'r' },
45//XXX:TODO: { "verbose", 0, NULL, 'v' },
46#endif
45 { 0, 0, 0, 0 } 47 { 0, 0, 0, 0 }
46}; 48};
49#endif
50
51struct globals {
52 char *cmd[10]; /* merely arbitrary arg count */
53 smalluint mode;
54};
55#define G (*(struct globals*)&bb_common_bufsiz1)
47 56
48/* valid_name */ 57/* valid_name */
49/* True or false? Is this a valid filename (upper/lower alpha, digits, 58/* True or false? Is this a valid filename (upper/lower alpha, digits,
50 * underscores, and hyphens only?) 59 * underscores, and hyphens only?)
51 */ 60 */
52static int valid_name(const struct dirent *d) 61static bool invalid_name(const char *c)
53{ 62{
54 const char *c = d->d_name;
55
56 while (*c) { 63 while (*c) {
57 if (!isalnum(*c) && (*c != '_') && (*c != '-')) { 64 if (!isalnum(*c) && (*c != '_') && (*c != '-' && (*c != '/'))) {
58 return 0; 65 return 1;
59 } 66 }
60 ++c; 67 ++c;
61 } 68 }
62 return 1; 69 return 0;
63} 70}
64 71#define RUN_PARTS_OPT_a (1<<0)
65/* test mode = 1 is the same as official run_parts 72#define RUN_PARTS_OPT_u (1<<1)
66 * test_mode = 2 means to fail silently on missing directories 73#define RUN_PARTS_OPT_t (1<<2)
67 */ 74#if ENABLE_FEATURE_RUN_PARTS_FANCY
68static int run_parts(char **args, const unsigned char test_mode) 75#define RUN_PARTS_OPT_l (1<<3)
69{
70 struct dirent **namelist = 0;
71 struct stat st;
72 char *filename;
73 char *arg0 = args[0];
74 int entries;
75 int i;
76 int exitstatus = 0;
77
78#if __GNUC__
79 /* Avoid longjmp clobbering */
80 (void) &i;
81 (void) &exitstatus;
82#endif 76#endif
83 /* scandir() isn't POSIX, but it makes things easy. */
84 entries = scandir(arg0, &namelist, valid_name, alphasort);
85
86 if (entries == -1) {
87 if (test_mode & 2) {
88 return 2;
89 }
90 bb_perror_msg_and_die("cannot open '%s'", arg0);
91 }
92 77
93 for (i = 0; i < entries; i++) { 78#define test_mode (G.mode & RUN_PARTS_OPT_t)
94 filename = concat_path_file(arg0, namelist[i]->d_name); 79#if ENABLE_FEATURE_RUN_PARTS_FANCY
95 80#define list_mode (G.mode & RUN_PARTS_OPT_l)
96 xstat(filename, &st); 81#else
97 if (S_ISREG(st.st_mode) && !access(filename, X_OK)) { 82#define list_mode (0)
98 if (test_mode) { 83#endif
99 puts(filename); 84static int act(const char *file, struct stat *statbuf, void *args, int depth)
100 } else { 85{
101 /* exec_errno is common vfork variable */ 86 int ret;
102 volatile int exec_errno = 0;
103 int result;
104 int pid;
105 87
106 if ((pid = vfork()) < 0) { 88 if (depth == 1)
107 bb_perror_msg_and_die("failed to fork"); 89 return TRUE;
108 } else if (!pid) {
109 args[0] = filename;
110 execve(filename, args, environ);
111 exec_errno = errno;
112 _exit(1);
113 }
114 90
115 waitpid(pid, &result, 0); 91 if (depth == 2 &&
116 if (exec_errno) { 92 ((!list_mode && access(file, X_OK)) ||
117 errno = exec_errno; 93 invalid_name(file) ||
118 bb_perror_msg("failed to exec %s", filename); 94 !(statbuf->st_mode & (S_IFREG | S_IFLNK))) )
119 exitstatus = 1; 95 return SKIP;
120 }
121 if (WIFEXITED(result) && WEXITSTATUS(result)) {
122 bb_perror_msg("%s exited with return code %d", filename, WEXITSTATUS(result));
123 exitstatus = 1;
124 } else if (WIFSIGNALED(result)) {
125 bb_perror_msg("%s exited because of uncaught signal %d", filename, WTERMSIG(result));
126 exitstatus = 1;
127 }
128 }
129 } else if (!S_ISDIR(st.st_mode)) {
130 bb_error_msg("component %s is not an executable plain file", filename);
131 exitstatus = 1;
132 }
133 96
134 free(namelist[i]); 97 if (test_mode || list_mode) {
135 free(filename); 98 puts(file);
99 return TRUE;
136 } 100 }
137 free(namelist); 101 G.cmd[0] = (char*)file;
138 102 ret = wait4pid(spawn(G.cmd));
139 return exitstatus; 103 if (ret < 0) {
104 bb_error_msg("failed to exec %s", *G.cmd);
105 } else if (ret > 0) {
106 bb_perror_msg("%s exited with return code %d", *G.cmd, ret);
107 }
108 return !ret;
140} 109}
141 110
142
143/* run_parts_main */
144/* Process options */
145int run_parts_main(int argc, char **argv); 111int run_parts_main(int argc, char **argv);
146int run_parts_main(int argc, char **argv) 112int run_parts_main(int argc, char **argv)
147{ 113{
148 char **args = xmalloc(2 * sizeof(char *)); 114 char *umask_p;
149 unsigned char test_mode = 0; 115 llist_t *arg_list = NULL;
150 unsigned short argcount = 1; 116 unsigned tmp;
151 int opt;
152 117
153 umask(022); 118 umask(022);
154
155 while ((opt = getopt_long(argc, argv, "tu:a:",
156 runparts_long_options, NULL)) > 0)
157 {
158 switch (opt) {
159 /* Enable test mode */
160 case 't':
161 test_mode++;
162 break;
163 /* Set the umask of the programs executed */
164 case 'u':
165 /* Check and set the umask of the program executed. As stated in the original
166 * run-parts, the octal conversion in libc is not foolproof; it will take the
167 * 8 and 9 digits under some circumstances. We'll just have to live with it.
168 */
169 umask(xstrtoul_range(optarg, 8, 0, 07777));
170 break;
171 /* Pass an argument to the programs */
172 case 'a':
173 /* Add an argument to the commands that we will call.
174 * Called once for every argument. */
175 args = xrealloc(args, (argcount + 2) * (sizeof(char *)));
176 args[argcount++] = optarg;
177 break;
178 default:
179 bb_show_usage();
180 }
181 }
182
183 /* We require exactly one argument: the directory name */ 119 /* We require exactly one argument: the directory name */
184 if (optind != (argc - 1)) { 120 opt_complementary = "=1:a::";
185 bb_show_usage(); 121#if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS
122 applet_long_options = runparts_long_options;
123#endif
124 tmp = getopt32(argc, argv, "a:u:t"USE_FEATURE_RUN_PARTS_FANCY("l"), &arg_list, &umask_p);
125 G.mode = tmp &~ (RUN_PARTS_OPT_a|RUN_PARTS_OPT_u);
126 if (tmp & RUN_PARTS_OPT_u) {
127 /* Check and set the umask of the program executed.
128 * As stated in the original run-parts, the octal conversion in
129 * libc is not foolproof; it will take the 8 and 9 digits under
130 * some circumstances. We'll just have to live with it.
131 */
132 umask(xstrtoul_range(umask_p, 8, 0, 07777));
186 } 133 }
187 134//XXX: FIXME: reverse the list before handing it over to the user.
188 args[0] = argv[optind]; 135//XXX: FIXME: The common case seems to be to use the order given by the user
189 args[argcount] = 0; 136 arg_list = llist_rev(arg_list); /* XXX: getopt32 appends them */
190 137 G.cmd[0] = (char*)"";
191 return run_parts(args, test_mode); 138 for (tmp = 1; arg_list; arg_list = arg_list->link, tmp++)
139 G.cmd[tmp] = arg_list->data;
140 if (!recursive_action(argv[argc - 1],
141 TRUE, /* recurse */
142 TRUE, /* follow links */
143 FALSE, /* depth first */
144 act, /* file action */
145 act, /* dir action */
146 NULL, /* user data */
147 1 /* depth */
148 ))
149 return EXIT_FAILURE;
150 return EXIT_SUCCESS;
192} 151}
diff --git a/include/usage.h b/include/usage.h
index d948c6a03..a19bcf7c2 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -2760,13 +2760,28 @@
2760 " -l, --range=RNG Levelrange" \ 2760 " -l, --range=RNG Levelrange" \
2761 2761
2762#define run_parts_trivial_usage \ 2762#define run_parts_trivial_usage \
2763 "[-t] [-a ARG] [-u MASK] DIRECTORY" 2763 "[-t] "USE_FEATURE_RUN_PARTS_FANCY("[-l] ")"[-a ARG] [-u MASK] DIRECTORY"
2764#define run_parts_full_usage \ 2764#define run_parts_full_usage \
2765 "Run a bunch of scripts in a directory" \ 2765 "Run a bunch of scripts in a directory" \
2766 "\n\nOptions:\n" \ 2766 "\n\nOptions:\n" \
2767 " -t Prints what would be run, but does not actually run anything\n" \ 2767 " -t Prints what would be run, but does not actually run anything\n" \
2768 " -a ARG Pass ARG as an argument for every program invoked\n" \ 2768 " -a ARG Pass ARG as an argument for every program invoked\n" \
2769 " -u MASK Set the umask to MASK before executing every program" 2769 " -u MASK Set the umask to MASK before executing every program" \
2770USE_FEATURE_RUN_PARTS_FANCY("\n -l Prints names of all matching files even when they are not executable")
2771
2772#define run_parts_example_usage \
2773 "$ run-parts -a start /etc/init.d\n" \
2774 "$ run-parts -a stop=now /etc/init.d\n\n" \
2775 "Let's assume you have a script foo/dosomething:\n" \
2776 "#!/bin/sh\n" \
2777 "for i in $*; do eval $i; done ; unset i\n" \
2778 "case \"$1\" in\n" \
2779 "start*) echo starting something ;;\n" \
2780 "stop*) set -x ; shutdown -h $stop ;;\n" \
2781 "esac\n\n" \
2782 "Running this yields:\n" \
2783 "$run-parts -a stop=+4m foo/\n" \
2784 "+ shutdown -h +4m"
2770 2785
2771#define runlevel_trivial_usage \ 2786#define runlevel_trivial_usage \
2772 "[utmp]" 2787 "[utmp]"