suppress warnings about easch <applet>_main() having
[oweals/busybox.git] / debianutils / run_parts.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini run-parts implementation for busybox
4  *
5  *
6  * Copyright (C) 2001 by Emanuele Aina <emanuele.aina@tiscali.it>
7  *
8  * Based on the Debian run-parts program, version 1.15
9  *   Copyright (C) 1996 Jeff Noxon <jeff@router.patch.net>,
10  *   Copyright (C) 1996-1999 Guy Maor <maor@debian.org>
11  *
12  *
13  * Licensed under GPL v2 or later, see file LICENSE in this tarball for details.
14  */
15
16 /* This is my first attempt to write a program in C (well, this is my first
17  * attempt to write a program! :-) . */
18
19 /* This piece of code is heavily based on the original version of run-parts,
20  * taken from debian-utils. I've only removed the long options and a the
21  * report mode. As the original run-parts support only long options, I've
22  * broken compatibility because the BusyBox policy doesn't allow them.
23  * The supported options are:
24  * -t                   test. Print the name of the files to be executed, without
25  *                              execute them.
26  * -a ARG               argument. Pass ARG as an argument the program executed. It can
27  *                              be repeated to pass multiple arguments.
28  * -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  */
37
38 #include "busybox.h"
39 #include <getopt.h>
40
41 static const struct option runparts_long_options[] = {
42         { "test",       0,      NULL,   't' },
43         { "umask",      1,      NULL,   'u' },
44         { "arg",        1,      NULL,   'a' },
45         { 0,            0,      0,      0   }
46 };
47
48 /* valid_name */
49 /* True or false? Is this a valid filename (upper/lower alpha, digits,
50  * underscores, and hyphens only?)
51  */
52 static int valid_name(const struct dirent *d)
53 {
54         const char *c = d->d_name;
55
56         while (*c) {
57                 if (!isalnum(*c) && (*c != '_') && (*c != '-')) {
58                         return 0;
59                 }
60                 ++c;
61         }
62         return 1;
63 }
64
65 /* test mode = 1 is the same as official run_parts
66  * test_mode = 2 means to fail silently on missing directories
67  */
68 static int run_parts(char **args, const unsigned char test_mode)
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
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
93         for (i = 0; i < entries; i++) {
94                 filename = concat_path_file(arg0, namelist[i]->d_name);
95
96                 xstat(filename, &st);
97                 if (S_ISREG(st.st_mode) && !access(filename, X_OK)) {
98                         if (test_mode) {
99                                 puts(filename);
100                         } else {
101                                 /* exec_errno is common vfork variable */
102                                 volatile int exec_errno = 0;
103                                 int result;
104                                 int pid;
105
106                                 if ((pid = vfork()) < 0) {
107                                         bb_perror_msg_and_die("failed to fork");
108                                 } else if (!pid) {
109                                         args[0] = filename;
110                                         execve(filename, args, environ);
111                                         exec_errno = errno;
112                                         _exit(1);
113                                 }
114
115                                 waitpid(pid, &result, 0);
116                                 if (exec_errno) {
117                                         errno = exec_errno;
118                                         bb_perror_msg("failed to exec %s", filename);
119                                         exitstatus = 1;
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
134                 free(namelist[i]);
135                 free(filename);
136         }
137         free(namelist);
138
139         return exitstatus;
140 }
141
142
143 /* run_parts_main */
144 /* Process options */
145 int run_parts_main(int argc, char **argv);
146 int run_parts_main(int argc, char **argv)
147 {
148         char **args = xmalloc(2 * sizeof(char *));
149         unsigned char test_mode = 0;
150         unsigned short argcount = 1;
151         int opt;
152
153         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 */
184         if (optind != (argc - 1)) {
185                 bb_show_usage();
186         }
187
188         args[0] = argv[optind];
189         args[argcount] = 0;
190
191         return run_parts(args, test_mode);
192 }