- rewrite run-parts text data bss dec hex filename 1029 0 0 1029 405 debianutils/run_parts.o-old 478 0 0 478 1de debianutils/run_parts.o-new-bare 600 0 0 600 258 debianutils/run_parts.o-new-full
No binary (build failed)
—
(log yok)
| @@ -53,6 +53,15 @@ config FEATURE_RUN_PARTS_LONG_OPTIONS | |
| help | |
| Support long options for the run-parts applet. | |
| + | config FEATURE_RUN_PARTS_FANCY |
| + | bool "Support additional arguments" |
| + | default n |
| + | depends on RUN_PARTS |
| + | help |
| + | Support additional options: |
| + | -l --list print the names of the all matching files (not |
| + | limited to executables), but don't actually run them. |
| + | |
| config START_STOP_DAEMON | |
| bool "start-stop-daemon" | |
| default y | |
| @@ -2,8 +2,10 @@ | |
| /* | |
| * Mini run-parts implementation for busybox | |
| * | |
| + | * Copyright (C) 2007 Bernhard Fischer |
| * | |
| - | * Copyright (C) 2001 by Emanuele Aina <[email protected]> |
| + | * Based on a older version that was in busybox which was 1k big.. |
| + | * Copyright (C) 2001 by Emanuele Aina <[email protected]> |
| * | |
| * Based on the Debian run-parts program, version 1.15 | |
| * Copyright (C) 1996 Jeff Noxon <[email protected]>, | |
| @@ -25,168 +27,125 @@ | |
| * execute them. | |
| * -a ARG argument. Pass ARG as an argument the program executed. It can | |
| * be repeated to pass multiple arguments. | |
| - | * -u MASK umask. Set the umask of the program executed to MASK. */ |
| - | |
| - | /* TODO |
| - | * done - convert calls to error in perror... and remove error() |
| - | * done - convert malloc/realloc to their x... counterparts |
| - | * done - remove catch_sigchld |
| - | * done - use bb's concat_path_file() |
| - | * done - declare run_parts_main() as extern and any other function as static? |
| + | * -u MASK umask. Set the umask of the program executed to MASK. |
| */ | |
| + | |
| #include "busybox.h" | |
| #include <getopt.h> | |
| + | #if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS |
| static const struct option runparts_long_options[] = { | |
| - | { "test", 0, NULL, 't' }, |
| - | { "umask", 1, NULL, 'u' }, |
| { "arg", 1, NULL, 'a' }, | |
| + | { "umask", 1, NULL, 'u' }, |
| + | { "test", 0, NULL, 't' }, |
| + | #if ENABLE_FEATURE_RUN_PARTS_FANCY |
| + | { "list", 0, NULL, 'l' }, |
| + | //XXX:TODO: { "reverse", 0, NULL, 'r' }, |
| + | //XXX:TODO: { "verbose", 0, NULL, 'v' }, |
| + | #endif |
| { 0, 0, 0, 0 } | |
| }; | |
| + | #endif |
| + | |
| + | struct globals { |
| + | char *cmd[10]; /* merely arbitrary arg count */ |
| + | smalluint mode; |
| + | }; |
| + | #define G (*(struct globals*)&bb_common_bufsiz1) |
| /* valid_name */ | |
| /* True or false? Is this a valid filename (upper/lower alpha, digits, | |
| * underscores, and hyphens only?) | |
| */ | |
| - | static int valid_name(const struct dirent *d) |
| + | static bool invalid_name(const char *c) |
| { | |
| - | const char *c = d->d_name; |
| - | |
| while (*c) { | |
| - | if (!isalnum(*c) && (*c != '_') && (*c != '-')) { |
| - | return 0; |
| + | if (!isalnum(*c) && (*c != '_') && (*c != '-' && (*c != '/'))) { |
| + | return 1; |
| } | |
| ++c; | |
| } | |
| - | return 1; |
| + | return 0; |
| } | |
| - | |
| - | /* test mode = 1 is the same as official run_parts |
| - | * test_mode = 2 means to fail silently on missing directories |
| - | */ |
| - | static int run_parts(char **args, const unsigned char test_mode) |
| - | { |
| - | struct dirent **namelist = 0; |
| - | struct stat st; |
| - | char *filename; |
| - | char *arg0 = args[0]; |
| - | int entries; |
| - | int i; |
| - | int exitstatus = 0; |
| - | |
| - | #if __GNUC__ |
| - | /* Avoid longjmp clobbering */ |
| - | (void) &i; |
| - | (void) &exitstatus; |
| + | #define RUN_PARTS_OPT_a (1<<0) |
| + | #define RUN_PARTS_OPT_u (1<<1) |
| + | #define RUN_PARTS_OPT_t (1<<2) |
| + | #if ENABLE_FEATURE_RUN_PARTS_FANCY |
| + | #define RUN_PARTS_OPT_l (1<<3) |
| #endif | |
| - | /* scandir() isn't POSIX, but it makes things easy. */ |
| - | entries = scandir(arg0, &namelist, valid_name, alphasort); |
| - | |
| - | if (entries == -1) { |
| - | if (test_mode & 2) { |
| - | return 2; |
| - | } |
| - | bb_perror_msg_and_die("cannot open '%s'", arg0); |
| - | } |
| - | for (i = 0; i < entries; i++) { |
| - | filename = concat_path_file(arg0, namelist[i]->d_name); |
| - | |
| - | xstat(filename, &st); |
| - | if (S_ISREG(st.st_mode) && !access(filename, X_OK)) { |
| - | if (test_mode) { |
| - | puts(filename); |
| - | } else { |
| - | /* exec_errno is common vfork variable */ |
| - | volatile int exec_errno = 0; |
| - | int result; |
| - | int pid; |
| + | #define test_mode (G.mode & RUN_PARTS_OPT_t) |
| + | #if ENABLE_FEATURE_RUN_PARTS_FANCY |
| + | #define list_mode (G.mode & RUN_PARTS_OPT_l) |
| + | #else |
| + | #define list_mode (0) |
| + | #endif |
| + | static int act(const char *file, struct stat *statbuf, void *args, int depth) |
| + | { |
| + | int ret; |
| - | if ((pid = vfork()) < 0) { |
| - | bb_perror_msg_and_die("failed to fork"); |
| - | } else if (!pid) { |
| - | args[0] = filename; |
| - | execve(filename, args, environ); |
| - | exec_errno = errno; |
| - | _exit(1); |
| - | } |
| + | if (depth == 1) |
| + | return TRUE; |
| - | waitpid(pid, &result, 0); |
| - | if (exec_errno) { |
| - | errno = exec_errno; |
| - | bb_perror_msg("failed to exec %s", filename); |
| - | exitstatus = 1; |
| - | } |
| - | if (WIFEXITED(result) && WEXITSTATUS(result)) { |
| - | bb_perror_msg("%s exited with return code %d", filename, WEXITSTATUS(result)); |
| - | exitstatus = 1; |
| - | } else if (WIFSIGNALED(result)) { |
| - | bb_perror_msg("%s exited because of uncaught signal %d", filename, WTERMSIG(result)); |
| - | exitstatus = 1; |
| - | } |
| - | } |
| - | } else if (!S_ISDIR(st.st_mode)) { |
| - | bb_error_msg("component %s is not an executable plain file", filename); |
| - | exitstatus = 1; |
| - | } |
| + | if (depth == 2 && |
| + | ((!list_mode && access(file, X_OK)) || |
| + | invalid_name(file) || |
| + | !(statbuf->st_mode & (S_IFREG | S_IFLNK))) ) |
| + | return SKIP; |
| - | free(namelist[i]); |
| - | free(filename); |
| + | if (test_mode || list_mode) { |
| + | puts(file); |
| + | return TRUE; |
| } | |
| - | free(namelist); |
| - | |
| - | return exitstatus; |
| + | G.cmd[0] = (char*)file; |
| + | ret = wait4pid(spawn(G.cmd)); |
| + | if (ret < 0) { |
| + | bb_error_msg("failed to exec %s", *G.cmd); |
| + | } else if (ret > 0) { |
| + | bb_perror_msg("%s exited with return code %d", *G.cmd, ret); |
| + | } |
| + | return !ret; |
| } | |
| - | |
| - | /* run_parts_main */ |
| - | /* Process options */ |
| int run_parts_main(int argc, char **argv); | |
| int run_parts_main(int argc, char **argv) | |
| { | |
| - | char **args = xmalloc(2 * sizeof(char *)); |
| - | unsigned char test_mode = 0; |
| - | unsigned short argcount = 1; |
| - | int opt; |
| + | char *umask_p; |
| + | llist_t *arg_list = NULL; |
| + | unsigned tmp; |
| umask(022); | |
| - | |
| - | while ((opt = getopt_long(argc, argv, "tu:a:", |
| - | runparts_long_options, NULL)) > 0) |
| - | { |
| - | switch (opt) { |
| - | /* Enable test mode */ |
| - | case 't': |
| - | test_mode++; |
| - | break; |
| - | /* Set the umask of the programs executed */ |
| - | case 'u': |
| - | /* Check and set the umask of the program executed. As stated in the original |
| - | * run-parts, the octal conversion in libc is not foolproof; it will take the |
| - | * 8 and 9 digits under some circumstances. We'll just have to live with it. |
| - | */ |
| - | umask(xstrtoul_range(optarg, 8, 0, 07777)); |
| - | break; |
| - | /* Pass an argument to the programs */ |
| - | case 'a': |
| - | /* Add an argument to the commands that we will call. |
| - | * Called once for every argument. */ |
| - | args = xrealloc(args, (argcount + 2) * (sizeof(char *))); |
| - | args[argcount++] = optarg; |
| - | break; |
| - | default: |
| - | bb_show_usage(); |
| - | } |
| - | } |
| - | |
| /* We require exactly one argument: the directory name */ | |
| - | if (optind != (argc - 1)) { |
| - | bb_show_usage(); |
| + | opt_complementary = "=1:a::"; |
| + | #if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS |
| + | applet_long_options = runparts_long_options; |
| + | #endif |
| + | tmp = getopt32(argc, argv, "a:u:t"USE_FEATURE_RUN_PARTS_FANCY("l"), &arg_list, &umask_p); |
| + | G.mode = tmp &~ (RUN_PARTS_OPT_a|RUN_PARTS_OPT_u); |
| + | if (tmp & RUN_PARTS_OPT_u) { |
| + | /* Check and set the umask of the program executed. |
| + | * As stated in the original run-parts, the octal conversion in |
| + | * libc is not foolproof; it will take the 8 and 9 digits under |
| + | * some circumstances. We'll just have to live with it. |
| + | */ |
| + | umask(xstrtoul_range(umask_p, 8, 0, 07777)); |
| } | |
| - | |
| - | args[0] = argv[optind]; |
| - | args[argcount] = 0; |
| - | |
| - | return run_parts(args, test_mode); |
| + | //XXX: FIXME: reverse the list before handing it over to the user. |
| + | //XXX: FIXME: The common case seems to be to use the order given by the user |
| + | arg_list = llist_rev(arg_list); /* XXX: getopt32 appends them */ |
| + | G.cmd[0] = (char*)""; |
| + | for (tmp = 1; arg_list; arg_list = arg_list->link, tmp++) |
| + | G.cmd[tmp] = arg_list->data; |
| + | if (!recursive_action(argv[argc - 1], |
| + | TRUE, /* recurse */ |
| + | TRUE, /* follow links */ |
| + | FALSE, /* depth first */ |
| + | act, /* file action */ |
| + | act, /* dir action */ |
| + | NULL, /* user data */ |
| + | 1 /* depth */ |
| + | )) |
| + | return EXIT_FAILURE; |
| + | return EXIT_SUCCESS; |
| } | |
| @@ -2760,13 +2760,28 @@ | |
| " -l, --range=RNG Levelrange" \ | |
| #define run_parts_trivial_usage \ | |
| - | "[-t] [-a ARG] [-u MASK] DIRECTORY" |
| + | "[-t] "USE_FEATURE_RUN_PARTS_FANCY("[-l] ")"[-a ARG] [-u MASK] DIRECTORY" |
| #define run_parts_full_usage \ | |
| "Run a bunch of scripts in a directory" \ | |
| "\n\nOptions:\n" \ | |
| " -t Prints what would be run, but does not actually run anything\n" \ | |
| " -a ARG Pass ARG as an argument for every program invoked\n" \ | |
| - | " -u MASK Set the umask to MASK before executing every program" |
| + | " -u MASK Set the umask to MASK before executing every program" \ |
| + | USE_FEATURE_RUN_PARTS_FANCY("\n -l Prints names of all matching files even when they are not executable") |
| + | |
| + | #define run_parts_example_usage \ |
| + | "$ run-parts -a start /etc/init.d\n" \ |
| + | "$ run-parts -a stop=now /etc/init.d\n\n" \ |
| + | "Let's assume you have a script foo/dosomething:\n" \ |
| + | "#!/bin/sh\n" \ |
| + | "for i in $*; do eval $i; done ; unset i\n" \ |
| + | "case \"$1\" in\n" \ |
| + | "start*) echo starting something ;;\n" \ |
| + | "stop*) set -x ; shutdown -h $stop ;;\n" \ |
| + | "esac\n\n" \ |
| + | "Running this yields:\n" \ |
| + | "$run-parts -a stop=+4m foo/\n" \ |
| + | "+ shutdown -h +4m" |
| #define runlevel_trivial_usage \ | |
| "[utmp]" | |