- rewrite run-parts
authorBernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Wed, 28 Mar 2007 20:35:13 +0000 (20:35 -0000)
committerBernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Wed, 28 Mar 2007 20:35:13 +0000 (20:35 -0000)
   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
debianutils/run_parts.c
include/usage.h

index 3d85999..c491976 100644 (file)
@@ -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
index a864a05..aa449c4 100644 (file)
@@ -2,8 +2,10 @@
 /*
  * Mini run-parts implementation for busybox
  *
+ * Copyright (C) 2007 Bernhard Fischer
  *
- * Copyright (C) 2001 by Emanuele Aina <emanuele.aina@tiscali.it>
+ * Based on a older version that was in busybox which was 1k big..
+ *   Copyright (C) 2001 by Emanuele Aina <emanuele.aina@tiscali.it>
  *
  * Based on the Debian run-parts program, version 1.15
  *   Copyright (C) 1996 Jeff Noxon <jeff@router.patch.net>,
  *                             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;
 }
index d948c6a..a19bcf7 100644 (file)
        "       -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]"