From b7cffd4bedf14770a7096a11a6e46cc497ff37c6 Mon Sep 17 00:00:00 2001 From: Bernhard Reutner-Fischer Date: Wed, 28 Mar 2007 20:35:13 +0000 Subject: [PATCH] - 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 bare, i.e. without long opts and fancy stuff ./scripts/bloat-o-meter bb_old busybox_unstripped function old new delta act - 215 +215 run_parts_main 216 201 -15 valid_name 50 - -50 runparts_long_options 64 - -64 .rodata 124323 124163 -160 run_parts 513 - -513 ------------------------------------------------------------------------------ (add/remove: 1/3 grow/shrink: 0/2 up/down: 215/-802) Total: -587 bytes --- debianutils/Config.in | 9 ++ debianutils/run_parts.c | 223 ++++++++++++++++------------------------ include/usage.h | 19 +++- 3 files changed, 117 insertions(+), 134 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 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 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 @@ /* * Mini run-parts implementation for busybox * + * Copyright (C) 2007 Bernhard Fischer * - * Copyright (C) 2001 by Emanuele Aina + * Based on a older version that was in busybox which was 1k big.. + * Copyright (C) 2001 by Emanuele Aina * * Based on the Debian run-parts program, version 1.15 * Copyright (C) 1996 Jeff Noxon , @@ -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 +#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); +#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 (depth == 1) + return TRUE; + + if (depth == 2 && + ((!list_mode && access(file, X_OK)) || + invalid_name(file) || + !(statbuf->st_mode & (S_IFREG | S_IFLNK))) ) + return SKIP; + + if (test_mode || list_mode) { + puts(file); + return TRUE; } - - 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; - - 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); - } - - 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; - } - - free(namelist[i]); - free(filename); + 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); } - free(namelist); - - return exitstatus; + 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; } 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 @@ " -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]"