SVN: rc-scripts/trunk: Makefile.am configure.ac man/Makefile.am man/run-parts.8 run-parts run-parts....

glen glen at pld-linux.org
Fri Oct 18 18:32:33 CEST 2013


Author: glen
Date: Fri Oct 18 18:32:33 2013
New Revision: 12724

Added:
   rc-scripts/trunk/man/run-parts.8
   rc-scripts/trunk/run-parts.sh
      - copied unchanged from rev 12723, rc-scripts/trunk/run-parts
   rc-scripts/trunk/src/run-parts.c
Removed:
   rc-scripts/trunk/run-parts
Modified:
   rc-scripts/trunk/Makefile.am
   rc-scripts/trunk/configure.ac
   rc-scripts/trunk/man/Makefile.am
   rc-scripts/trunk/src/Makefile.am
Log:
add run-parts from debianutils 4.4

switch to debian one, it's more complete and at least maintained
basic usage without args works without modifications.

old script preserved as run-parts.sh in case need quickly to revert to previous script



Modified: rc-scripts/trunk/Makefile.am
==============================================================================
--- rc-scripts/trunk/Makefile.am	(original)
+++ rc-scripts/trunk/Makefile.am	Fri Oct 18 18:32:33 2013
@@ -20,7 +20,7 @@
 	lang.sh	\
 	lang.csh
 
-bin_SCRIPTS = run-parts
+bin_SCRIPTS = run-parts.sh
 sbin_SCRIPTS = \
 	setsysfont \
 	hwprofile \

Modified: rc-scripts/trunk/configure.ac
==============================================================================
--- rc-scripts/trunk/configure.ac	(original)
+++ rc-scripts/trunk/configure.ac	Fri Oct 18 18:32:33 2013
@@ -92,7 +92,7 @@
   DPKG_C_GCC_ATTRIBUTE(format...,format,[char *y, ...],[format(printf,1,2)],PRINTFFORMAT,[Define if printf-format argument lists a la GCC are available.]))
 
 AC_CHECK_TYPE(ptrdiff_t,int)
-AC_CHECK_HEADERS([stddef.h sys/capability.h])
+AC_CHECK_HEADERS([stddef.h sys/capability.h getopt.h])
 
 dnl Output
 AC_SUBST(BASHSCRIPTS)

Modified: rc-scripts/trunk/man/Makefile.am
==============================================================================
--- rc-scripts/trunk/man/Makefile.am	(original)
+++ rc-scripts/trunk/man/Makefile.am	Fri Oct 18 18:32:33 2013
@@ -12,6 +12,7 @@
 	netreport.1 \
 	ppp-watch.8 \
 	usernetctl.8 \
+	run-parts.8 \
 	usleep.1 \
 	crypttab.5 \
 	start-stop-daemon.8

Added: rc-scripts/trunk/man/run-parts.8
==============================================================================
--- (empty file)
+++ rc-scripts/trunk/man/run-parts.8	Fri Oct 18 18:32:33 2013
@@ -0,0 +1,130 @@
+.\" Hey, Emacs!  This is an -*- nroff -*- source file.
+.\" Build-from-directory and this manpage are Copyright 1994 by Ian Jackson.
+.\" Changes to this manpage are Copyright 1996 by Jeff Noxon.
+.\" More
+.\"
+.\" This is free software; see the GNU General Public Licence version 2
+.\" or later for copying conditions.  There is NO warranty.
+.TH RUN\-PARTS 8 "27 Jun 2012" "Debian"
+.SH NAME
+run\-parts \- run scripts or programs in a directory
+.SH SYNOPSIS
+.PP
+.B run\-parts
+[\-\-test] [\-\-verbose] [\-\-report] [\-\-lsbsysinit] [\-\-regex=RE]
+[\-\-umask=umask] [\-\-arg=argument] [\-\-exit\-on\-error] [\-\-help]
+[\-\-version] [\-\-list] [\-\-reverse] [\-\-] DIRECTORY
+.PP
+.B run\-parts
+\-V
+.SH DESCRIPTION
+.PP
+.B run\-parts
+runs all the executable files named within constraints described below, found
+in directory
+.IR directory .
+Other files and directories are silently ignored.
+
+If neither the \-\-lsbsysinit option nor the \-\-regex option is given
+then the names must consist entirely of ASCII upper- and lower-case
+letters, ASCII digits, ASCII underscores, and ASCII minus-hyphens.
+
+If the \-\-lsbsysinit option is given, then the names must not end
+in .dpkg\-old  or .dpkg\-dist or .dpkg\-new or .dpkg\-tmp, and must
+belong to one or more of the following namespaces: the
+LANANA-assigned namespace (^[a\-z0\-9]+$); the LSB hierarchical and
+reserved namespaces (^_?([a\-z0\-9_.]+\-)+[a\-z0\-9]+$);
+and the Debian cron script namespace (^[a\-zA-Z0\-9_\-]+$).
+
+If the \-\-regex option is given, the names must match the custom
+extended regular expression specified as that option's argument.
+
+Files are run in the lexical sort order (according to the C/POSIX
+locale character collation rules) of their names unless the
+\-\-reverse option is given, in which case they are run in the
+opposite order.
+
+.SH OPTIONS
+.TP
+.B \-\-test
+print the names of the scripts which would be run, but don't actually run
+them.
+.TP
+.B \-\-list
+print the names of the all matching files (not limited to executables),
+but don't actually run them. This option cannot be used with --test.
+.TP
+.B \-v, \-\-verbose
+print the name of each script to stderr before running.
+.TP
+.B \-\-report
+similar to
+.BR \-\-verbose ,
+but only prints the name of scripts which produce output.  The script's name is printed to whichever of stdout or stderr the script first produces output on.
+.TP
+.B \-\-reverse
+reverse the scripts' execution order.
+.TP
+.B \-\-exit\-on\-error
+exit as soon as a script returns with a non-zero exit code.
+.TP
+.B \-\-lsbsysinit
+use LSB namespaces instead of classical behavior.
+.TP
+.B \-\-new\-session
+run each script in a separate process session.  If you use this option,
+killing run-parts will not kill the currently running script, it will
+run until completion.
+.TP
+.BI \-\-regex= RE
+validate filenames against custom extended regular expression
+.IR RE .
+See the EXAMPLES section for an example.
+.TP
+.BI "\-u, \-\-umask=" umask
+sets the umask to
+.I umask
+before running the scripts.
+.I umask
+should be specified in octal.  By default the umask is set to 022.
+.TP
+.BI "\-a, \-\-arg=" argument
+pass
+.I argument
+to the scripts.  Use
+.B --arg
+once for each argument you want passed.
+.TP
+.B "\-\-"
+specifies that this is the end of the options.  Any filename after
+.B "\-\-"
+will be not be interpreted as an option even if it starts with a
+hyphen.
+.TP
+.B \-h, \-\-help
+display usage information and exit.
+.TP
+.B \-V, \-\-version
+display version and copyright and exit.
+
+.SH EXAMPLES
+.P
+Print the names of all files in /etc that start with `p' and end with `d':
+.P
+run-parts \-\-list \-\-regex \[aq]^p.*d$\[aq] /etc
+
+.SH COPYRIGHT
+.P
+Copyright (C) 1994 Ian Jackson.
+.P
+Copyright (C) 1996 Jeff Noxon.
+.P
+Copyright (C) 1996, 1997, 1998 Guy Maor
+.P
+Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Clint Adams
+
+.B run\-parts
+is free software; see the GNU General Public License version 2 or
+later for copying conditions.  There is
+.I no
+warranty.

Modified: rc-scripts/trunk/src/Makefile.am
==============================================================================
--- rc-scripts/trunk/src/Makefile.am	(original)
+++ rc-scripts/trunk/src/Makefile.am	Fri Oct 18 18:32:33 2013
@@ -9,6 +9,7 @@
 	doexec \
 	ipcalc \
 	usleep \
+	run-parts \
 	resolvesymlink
 	
 sbin_PROGRAMS =	\
@@ -64,3 +65,5 @@
 start_stop_daemon_LDADD = -lcap
 
 setuidgid_SOURCES = setuidgid.c
+
+run_parts_CPPFLAGS = -include config.h

Added: rc-scripts/trunk/src/run-parts.c
==============================================================================
--- (empty file)
+++ rc-scripts/trunk/src/run-parts.c	Fri Oct 18 18:32:33 2013
@@ -0,0 +1,647 @@
+/* run-parts: run a bunch of scripts in a directory
+ *
+ * Debian run-parts program
+ * Copyright (C) 1996 Jeff Noxon <jeff at router.patch.net>,
+ * Copyright (C) 1996-1999 Guy Maor <maor at debian.org>
+ * Copyright (C) 2002-2012 Clint Adams <clint at debian.org>
+ *
+ * This is free software; see the GNU General Public License version 2
+ * or later for copying conditions.  There is NO warranty.
+ *
+ * Based on run-parts.pl version 0.2, Copyright (C) 1994 Ian Jackson.
+ *
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif /* HAVE_GETOPT_H */
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <regex.h>
+
+#define RUNPARTS_NORMAL 0
+#define RUNPARTS_ERE 1
+#define RUNPARTS_LSBSYSINIT 100
+
+int test_mode = 0;
+int list_mode = 0;
+int verbose_mode = 0;
+int report_mode = 0;
+int reverse_mode = 0;
+int exitstatus = 0;
+int regex_mode = 0;
+int exit_on_error_mode = 0;
+int new_session_mode = 0;
+
+int argcount = 0, argsize = 0;
+char **args = 0;
+
+char *custom_ere;
+regex_t hierre, tradre, excsre, classicalre, customre;
+
+static void catch_signals();
+static void restore_signals();
+
+static char* regex_get_error(int errcode, regex_t *compiled);
+static void  regex_compile_pattern(void);
+static void  regex_clean(void);
+
+void error(char *format, ...)
+{
+  va_list ap;
+
+  fprintf(stderr, "run-parts: ");
+
+  va_start(ap, format);
+  vfprintf(stderr, format, ap);
+  va_end(ap);
+
+  fprintf(stderr, "\n");
+}
+
+
+void version()
+{
+  fprintf(stderr, "Debian run-parts program, version " PACKAGE_VERSION
+	  "\nCopyright (C) 1994 Ian Jackson, Copyright (C) 1996 Jeff Noxon.\n"
+	  "Copyright (C) 1996,1997,1998,1999 Guy Maor\n"
+	  "Copyright (C) 2002-2012 Clint Adams\n"
+	  "This is free software; see the GNU General Public License version 2\n"
+	  "or later for copying conditions.  There is NO warranty.\n");
+  exit(0);
+}
+
+
+void usage()
+{
+  fprintf(stderr, "Usage: run-parts [OPTION]... DIRECTORY\n"
+	  "      --test          print script names which would run, but don't run them.\n"
+	  "      --list          print names of all valid files (can not be used with\n"
+	  "                      --test)\n"
+	  "  -v, --verbose       print script names before running them.\n"
+	  "      --report        print script names if they produce output.\n"
+	  "      --reverse       reverse execution order of scripts.\n"
+	  "      --exit-on-error exit as soon as a script returns with a non-zero exit\n"
+	  "                      code.\n"
+	  "      --lsbsysinit    validate filenames based on LSB sysinit specs.\n"
+	  "      --new-session   run each script in a separate process session\n"
+	  "      --regex=PATTERN validate filenames based on POSIX ERE pattern PATTERN.\n"
+	  "  -u, --umask=UMASK   sets umask to UMASK (octal), default is 022.\n"
+	  "  -a, --arg=ARGUMENT  pass ARGUMENT to scripts, use once for each argument.\n"
+	  "  -V, --version       output version information and exit.\n"
+	  "  -h, --help          display this help and exit.\n");
+  exit(0);
+}
+
+
+/* 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.
+ */
+void set_umask()
+{
+  int mask, result;
+
+  result = sscanf(optarg, "%o", &mask);
+  if ((result != 1) || (mask > 07777) || (mask < 0)) {
+    error("bad umask value");
+    exit(1);
+  }
+
+  umask(mask);
+}
+
+/* Add an argument to the commands that we will call.  Called once for
+   every argument. */
+void add_argument(char *newarg)
+{
+  if (argcount + 1 >= argsize) {
+    argsize = argsize ? argsize * 2 : 4;
+    args = realloc(args, argsize * (sizeof(char *)));
+    if (!args) {
+      error("failed to reallocate memory for arguments: %s", strerror(errno));
+      exit(1);
+    }
+  }
+  args[argcount++] = newarg;
+  args[argcount] = 0;
+}
+
+/* True or false? Is this a valid filename? */
+int valid_name(const struct dirent *d)
+{
+    char         *s;
+    unsigned int  retval;
+
+    s = (char *)&(d->d_name);
+
+    if (regex_mode == RUNPARTS_ERE)
+        retval = !regexec(&customre, s, 0, NULL, 0);
+
+    else if (regex_mode == RUNPARTS_LSBSYSINIT) {
+
+        if (!regexec(&hierre, s, 0, NULL, 0))
+            retval = regexec(&excsre, s, 0, NULL, 0);
+
+	else
+            retval = !regexec(&tradre, s, 0, NULL, 0);
+
+    } else
+        retval = !regexec(&classicalre, s, 0, NULL, 0);
+
+    return retval;
+}
+
+/* Execute a file */
+void run_part(char *progname)
+{
+  int result, waited;
+  int pid, r;
+  int pout[2], perr[2];
+
+  waited = 0;
+
+  if (report_mode && (pipe(pout) || pipe(perr))) {
+    error("pipe: %s", strerror(errno));
+    exit(1);
+  }
+  if ((pid = fork()) < 0) {
+    error("failed to fork: %s", strerror(errno));
+    exit(1);
+  }
+  else if (!pid) {
+    restore_signals();
+    if (new_session_mode)
+      setsid();
+    if (report_mode) {
+      if (dup2(pout[1], STDOUT_FILENO) == -1 ||
+	  dup2(perr[1], STDERR_FILENO) == -1) {
+	error("dup2: %s", strerror(errno));
+	exit(1);
+      }
+      close(pout[0]);
+      close(perr[0]);
+      close(pout[1]);
+      close(perr[1]);
+    }
+    args[0] = progname;
+    execv(progname, args);
+    error("failed to exec %s: %s", progname, strerror(errno));
+    exit(1);
+  }
+
+  if (report_mode) {
+    fd_set set;
+    sigset_t tempmask;
+    struct timespec zero_timeout;
+    struct timespec *the_timeout;
+    int max, printflag;
+    ssize_t c;
+    char buf[4096];
+
+    sigemptyset(&tempmask);
+    sigprocmask(0, NULL, &tempmask);
+    sigdelset(&tempmask, SIGCHLD);
+
+    memset(&zero_timeout, 0, sizeof(zero_timeout));
+    the_timeout = NULL;
+
+    close(pout[1]);
+    close(perr[1]);
+    max = pout[0] > perr[0] ? pout[0] + 1 : perr[0] + 1;
+    printflag = 0;
+
+    while (pout[0] >= 0 || perr[0] >= 0) {
+      if (!waited) {
+        r = waitpid(pid, &result, WNOHANG);
+        if (r == -1) {
+          error("waitpid: %s", strerror(errno));
+          exit(1);
+        }
+        if (r != 0 && (WIFEXITED(result) || WIFSIGNALED(result))) {
+          /* If the process dies, set a zero timeout. Rarely, some processes
+           * leak file descriptors (e.g., by starting a naughty daemon).
+           * select() would wait forever since the pipes wouldn't close.
+           * We loop, with a zero timeout, until there's no data left, then
+           * give up. This shouldn't affect non-leaky processes. */
+          waited = 1;
+          the_timeout = &zero_timeout;
+        }
+      }
+
+      FD_ZERO(&set);
+      if (pout[0] >= 0)
+        FD_SET(pout[0], &set);
+      if (perr[0] >= 0)
+        FD_SET(perr[0], &set);
+      r = pselect(max, &set, 0, 0, the_timeout, &tempmask);
+
+      if (r < 0) {
+        if (errno == EINTR)
+            continue;
+
+        error("select: %s", strerror(errno));
+        exit(1);
+      }
+      else if (r > 0) {
+	/* If STDOUT or STDERR get closed / full, we still run to completion
+	 * (and just ignore that we can't output process output any more).
+	 * Perhaps we should instead kill the child process we are running
+	 * if that happens.
+	 * For now partial writes are not retried to complete - that can
+	 * and should be done, but needs care to ensure that we don't hang
+	 * if the fd doesn't accept more data ever - or we need to decide that
+	 * waiting is the appropriate thing to do.
+	 */
+	int ignored;
+	if (pout[0] >= 0 && FD_ISSET(pout[0], &set)) {
+	  c = read(pout[0], buf, sizeof(buf));
+	  if (c > 0) {
+	    if (!printflag) {
+	      printf("%s:\n", progname);
+	      fflush(stdout);
+	      printflag = 1;
+	    }
+	    ignored = write(STDOUT_FILENO, buf, c);
+	  }
+	  else if (c == 0) {
+	    close(pout[0]);
+	    pout[0] = -1;
+	  }
+	  else if (c < 0) {
+	    close(pout[0]);
+	    pout[0] = -1;
+	    error("failed to read from stdout pipe: %s", strerror (errno)); 
+	  }
+	}
+	if (perr[0] >= 0 && FD_ISSET(perr[0], &set)) {
+	  c = read(perr[0], buf, sizeof(buf));
+	  if (c > 0) {
+	    if (!printflag) {
+	      fprintf(stderr, "%s:\n", progname);
+	      fflush(stderr);
+	      printflag = 1;
+	    }
+	    ignored = write(STDERR_FILENO, buf, c);
+	  }
+	  else if (c == 0) {
+	    close(perr[0]);
+	    perr[0] = -1;
+	  }
+	  else if (c < 0) {
+	    close(perr[0]);
+	    perr[0] = -1;
+	    error("failed to read from error pipe: %s", strerror (errno)); 
+	  }
+	}
+      }
+      else if (r == 0 && waited) {
+        /* Zero timeout, no data left. */
+        close(perr[0]);
+        perr[0] = -1;
+        close(pout[0]);
+        pout[0] = -1;
+      }
+      else {
+	/* assert(FALSE): select was called with infinite timeout, so
+	   it either returns successfully or is interrupted */
+      }				/*if */
+    }				/*while */
+  }
+
+  if (!waited) {
+    r = waitpid(pid, &result, 0);
+
+    if (r == -1) {
+		  error("waitpid: %s", strerror(errno));
+			exit(1);
+    }
+  }
+
+  if (WIFEXITED(result) && WEXITSTATUS(result)) {
+    error("%s exited with return code %d", progname, WEXITSTATUS(result));
+    exitstatus = 1;
+  }
+  else if (WIFSIGNALED(result)) {
+    error("%s exited because of uncaught signal %d", progname,
+	  WTERMSIG(result));
+    exitstatus = 1;
+  }
+}
+
+static void handle_signal(int s)
+{
+    /* Do nothing */
+}
+
+/* Catch SIGCHLD with an empty function to interrupt select() */
+static void catch_signals()
+{
+    struct sigaction act;
+    sigset_t set;
+
+    memset(&act, 0, sizeof(act));
+    act.sa_handler = handle_signal;
+    act.sa_flags = SA_NOCLDSTOP;
+    sigaction(SIGCHLD, &act, NULL);
+
+    sigemptyset(&set);
+    sigaddset(&set, SIGCHLD);
+    sigprocmask(SIG_BLOCK, &set, NULL);
+}
+
+/* Unblock signals before execing a child */
+static void restore_signals()
+{
+    sigset_t set;
+    sigemptyset(&set);
+    sigaddset(&set, SIGCHLD);
+    sigprocmask(SIG_UNBLOCK, &set, NULL);
+}
+
+/* Find the parts to run & call run_part() */
+void run_parts(char *dirname)
+{
+  struct dirent **namelist;
+  char *filename;
+  size_t filename_length, dirname_length;
+  int entries, i, result;
+  struct stat st;
+
+  /* dirname + "/" */
+  dirname_length = strlen(dirname) + 1;
+  /* dirname + "/" + ".." + "\0" (This will save one realloc.) */
+  filename_length = dirname_length + 2 + 1;
+  if (!(filename = malloc(filename_length))) {
+    error("failed to allocate memory for path: %s", strerror(errno));
+    exit(1);
+  }
+  strcpy(filename, dirname);
+  strcat(filename, "/");
+
+  /* scandir() isn't POSIX, but it makes things easy. */
+  entries = scandir(dirname, &namelist, valid_name, alphasort);
+  if (entries < 0) {
+    error("failed to open directory %s: %s", dirname, strerror(errno));
+    exit(1);
+  }
+
+  i = reverse_mode ? 0 : entries;
+  for (i = reverse_mode ? (entries - 1) : 0;
+       reverse_mode ? (i >= 0) : (i < entries); reverse_mode ? i-- : i++) {
+    if (filename_length < dirname_length + strlen(namelist[i]->d_name) + 1) {
+      filename_length = dirname_length + strlen(namelist[i]->d_name) + 1;
+      if (!(filename = realloc(filename, filename_length))) {
+	error("failed to reallocate memory for path: %s", strerror(errno));
+	exit(1);
+      }
+    }
+    strcpy(filename + dirname_length, namelist[i]->d_name);
+
+    strcpy(filename, dirname);
+    strcat(filename, "/");
+    strcat(filename, namelist[i]->d_name);
+
+    result = stat(filename, &st);
+    if (result < 0) {
+      error("failed to stat component %s: %s", filename, strerror(errno));
+      if (exit_on_error_mode) {
+        exit(1);
+      }
+      else
+        continue;
+    }
+
+    if (S_ISREG(st.st_mode)) {
+      if (!access(filename, X_OK)) {
+	if (test_mode) {
+	  printf("%s\n", filename);
+	}
+	else if (list_mode) {
+	  if (!access(filename, R_OK))
+	    printf("%s\n", filename);
+	}
+	else {
+	  if (verbose_mode)
+	    if (argcount) {
+	      char **a = args;
+
+	      fprintf(stderr, "run-parts: executing %s", filename);
+	      while(*(++a))
+		fprintf(stderr, " %s", *a);
+	      fprintf(stderr, "\n");
+	    } else {
+	      fprintf(stderr, "run-parts: executing %s\n", filename);
+	    }
+	  run_part(filename);
+	  if (exitstatus != 0 && exit_on_error_mode) return;
+	}
+      }
+      else if (!access(filename, R_OK)) {
+	if (list_mode) {
+	  printf("%s\n", filename);
+	}
+      }
+      else if (S_ISLNK(st.st_mode)) {
+	if (!list_mode) {
+	  error("run-parts: component %s is a broken symbolic link\n",filename);
+	  exitstatus = 1;
+	}
+      }
+    }
+    else if (!S_ISDIR(st.st_mode)) {
+      if (!list_mode) {
+	error("run-parts: component %s is not an executable plain file\n",
+	       filename);
+	exitstatus = 1;
+      }
+    }
+
+    free(namelist[i]);
+  }
+  free(namelist);
+  free(filename);
+}
+
+/* Process options */
+int main(int argc, char *argv[])
+{
+  custom_ere = NULL;
+  umask(022);
+  add_argument(0);
+
+  for (;;) {
+    int c;
+    int option_index = 0;
+
+    static struct option long_options[] = {
+      {"test", 0, &test_mode, 1},
+      {"list", 0, &list_mode, 1},
+      {"verbose", 0, 0, 'v'},
+      {"report", 0, &report_mode, 1},
+      {"reverse", 0, &reverse_mode, 1},
+      {"umask", 1, 0, 'u'},
+      {"arg", 1, 0, 'a'},
+      {"help", 0, 0, 'h'},
+      {"version", 0, 0, 'V'},
+      {"lsbsysinit", 0, &regex_mode, RUNPARTS_LSBSYSINIT},
+      {"regex", 1, &regex_mode, RUNPARTS_ERE},
<<diff output has been trimmed to 500 lines, 148 line(s) remained.>>


More information about the pld-cvs-commit mailing list