blob: 3ec4b9d10ee9cc172ae8cb86011712e93d711e08 [file] [log] [blame]
Eric Andersen7d682902001-10-31 10:59:29 +00001/* 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 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 * 02111-1307 USA
27 *
28 */
29
30/* This is my first attempt to write a program in C (well, this is my first
31 * attempt to write a program! :-) . */
32
33/* This piece of code is heavily based on the original version of run-parts,
34 * taken from debian-utils. I've only removed the long options and a the
35 * report mode. As the original run-parts support only long options, I've
36 * broken compatibility because the BusyBox policy doesn't allow them.
37 * The supported options are:
38 * -t test. Print the name of the files to be executed, without
39 * execute them.
40 * -a ARG argument. Pass ARG as an argument the program executed. It can
41 * be repeated to pass multiple arguments.
42 * -u MASK umask. Set the umask of the program executed to MASK. */
43
44/* TODO
45 * done - convert calls to error in perror... and remove error()
46 * done - convert malloc/realloc to their x... counterparts
47 * done - remove catch_sigchld
Eric Andersen7d682902001-10-31 10:59:29 +000048 * done - use bb's concat_path_file()
Eric Andersenfb74a452001-12-18 14:06:03 +000049 * done - declare run_parts_main() as extern and any other function as static?
50 */
Eric Andersen7d682902001-10-31 10:59:29 +000051
52#include <stdio.h>
53#include <stdarg.h>
54#include <stdlib.h>
55/* #include <sys/types.h> */
56#include <sys/wait.h>
57#include <dirent.h>
58#include <sys/stat.h>
59#include <unistd.h>
60#include <getopt.h>
61#include <string.h>
62#include <errno.h>
63#include <ctype.h>
Eric Andersen7d682902001-10-31 10:59:29 +000064
65#include "busybox.h"
66
Eric Andersenfb74a452001-12-18 14:06:03 +000067static int test_mode = 0;
68static int exitstatus = 0;
Eric Andersen7d682902001-10-31 10:59:29 +000069
Eric Andersenfb74a452001-12-18 14:06:03 +000070static int argcount = 0, argsize = 0;
71static char **args = 0;
Eric Andersen7d682902001-10-31 10:59:29 +000072
73
74/* set_umask */
75/* Check and set the umask of the program executed. As stated in the original
76 * run-parts, the octal conversion in libc is not foolproof; it will take the
77 * 8 and 9 digits under some circumstances. We'll just have to live with it.
78 */
79
Eric Andersenfb74a452001-12-18 14:06:03 +000080static void set_umask (void)
Eric Andersen7d682902001-10-31 10:59:29 +000081{
82 int mask, result;
83
84 /*TODO
85 * We must substitute sscanf, according to bb's style guide? */
86 result = sscanf (optarg, "%o", &mask);
87 if ((result != 1) || (mask > 07777) || (mask < 0)) {
88 perror_msg_and_die ("bad umask value");
89 }
90
91 umask (mask);
92}
93
94/* add_argument */
95/* Add an argument to the commands that we will call. Called once for
96 every argument. */
Eric Andersenfb74a452001-12-18 14:06:03 +000097static void add_argument (char *newarg)
Eric Andersen7d682902001-10-31 10:59:29 +000098{
99 if (argcount+1 >= argsize) {
100 argsize = argsize ? argsize*2 : 4;
101 /*TODO if we convert to xrealloc we lose the verbose error message */
102 args = realloc(args, argsize * (sizeof(char*)));
103 if (!args) {
Eric Andersenfb74a452001-12-18 14:06:03 +0000104 perror_msg_and_die ("failed to reallocate memory for arguments");
Eric Andersen7d682902001-10-31 10:59:29 +0000105 }
106 }
107 args[argcount++] = newarg;
108 args[argcount] = 0;
109}
110
111/* valid_name */
112/* True or false? Is this a valid filename (upper/lower alpha, digits,
113 * underscores, and hyphens only?)
114 */
115
Eric Andersenfb74a452001-12-18 14:06:03 +0000116static int valid_name (const struct dirent *d)
Eric Andersen7d682902001-10-31 10:59:29 +0000117{
118 char *c = d->d_name;
119 while (*c) {
120 if (!isalnum(*c) && *c!='_' && *c!='-') {
121 return 0;
122 }
123 ++c;
124 }
125 return 1;
126}
127
128
129/* run_part */
130/* Execute a file */
131
Eric Andersenfb74a452001-12-18 14:06:03 +0000132static void run_part (char *progname)
Eric Andersen7d682902001-10-31 10:59:29 +0000133{
134 int result;
135 int pid;
136
137
138 if ((pid=fork()) < 0) {
Eric Andersenfb74a452001-12-18 14:06:03 +0000139 perror_msg_and_die ("failed to fork");
Eric Andersen7d682902001-10-31 10:59:29 +0000140 }
141 else if (!pid) {
142 args[0] = progname;
143 execv (progname, args);
Eric Andersenfb74a452001-12-18 14:06:03 +0000144 perror_msg_and_die ("failed to exec %s", progname);
Eric Andersen7d682902001-10-31 10:59:29 +0000145 }
146
147 if (0) {
148
149 } else {
150
151 waitpid(pid, &result, 0);
152 }
153
154 if (WIFEXITED (result) && WEXITSTATUS(result)) {
155 perror_msg ("%s exited with return code %d", progname, WEXITSTATUS(result));
156 exitstatus = 1;
157 }
158 else if (WIFSIGNALED (result)) {
159 perror_msg ("%s exited because of uncaught signal %d", progname,
160 WTERMSIG(result));
161 exitstatus = 1;
162 }
163}
164
165/* run_parts */
166/* Find the parts to run & call run_part() */
167
Eric Andersenfb74a452001-12-18 14:06:03 +0000168static void run_parts (char *dir_name)
Eric Andersen7d682902001-10-31 10:59:29 +0000169{
Eric Andersenfb74a452001-12-18 14:06:03 +0000170 struct dirent **namelist = 0;
171 char *filename;
172 int entries, i;
Eric Andersen7d682902001-10-31 10:59:29 +0000173 struct stat st;
174
Eric Andersen7d682902001-10-31 10:59:29 +0000175 /* -- */
176
177 /* scandir() isn't POSIX, but it makes things easy. */
178 entries = scandir (dir_name, &namelist, valid_name, alphasort);
179
180 if (entries < 0) {
Eric Andersenfb74a452001-12-18 14:06:03 +0000181 perror_msg_and_die ("failed to open directory %s", dir_name);
Eric Andersen7d682902001-10-31 10:59:29 +0000182 }
183
184 for (i = 0; i < entries; i++) {
185
Eric Andersen7d682902001-10-31 10:59:29 +0000186 /* -- */
187
188 filename = concat_path_file (dir_name, namelist[i]->d_name);
189
Eric Andersenfb74a452001-12-18 14:06:03 +0000190 if (stat (filename, &st) < 0) {
191 perror_msg_and_die ("failed to stat component %s", filename);
Eric Andersen7d682902001-10-31 10:59:29 +0000192 }
193 if (S_ISREG(st.st_mode) && !access (filename, X_OK)) {
194 if (test_mode)
195 printf ("run-parts would run %s\n", filename);
196 else {
197 run_part (filename);
198 }
199 }
Eric Andersenfb74a452001-12-18 14:06:03 +0000200
Eric Andersen7d682902001-10-31 10:59:29 +0000201 else if (!S_ISDIR(st.st_mode)) {
Eric Andersenfb74a452001-12-18 14:06:03 +0000202 error_msg ("component %s is not an executable plain file",
Eric Andersen7d682902001-10-31 10:59:29 +0000203 filename);
204 exitstatus = 1;
205 }
206
207 free (namelist[i]);
Eric Andersenfb74a452001-12-18 14:06:03 +0000208 free (filename);
Eric Andersen7d682902001-10-31 10:59:29 +0000209 }
210 free (namelist);
Eric Andersen7d682902001-10-31 10:59:29 +0000211}
212
213/* run_parts_main */
214/* Process options */
215int run_parts_main (int argc, char *argv[])
216{
217 umask (022);
218 add_argument(0);
219
220 for (;;) {
221 int c;
222
223 opterr = 0;
224 c = getopt(argc, argv, "tu:a:");
225
226 if (c == EOF)
227 break;
228 switch (c) {
229 case 't': /* Enable test mode */
230 test_mode = 1;
231 break;
232 case 'u': /* Set the umask of the programs executed */
233 set_umask ();
234 break;
235 case 'a': /* Pass an argument to the programs */
236 add_argument (optarg);
237 break;
238 default:
239 show_usage();
240 }
241 }
242
243 /* We require exactly one argument: the directory name */
244 if (optind != (argc - 1)) {
245 show_usage();
246 }
247
248 run_parts (argv[optind]);
249
250 return exitstatus;
251}