Package: debianutils
Version: 5.23.2
Severity: wishlist
Tags: patch
Dear Maintainer,
I'm having troubles monitoring what happens during the cron.daily process.
While a --time option (see bug#282553) might help, I got the feeling that
after some trials I'd have to tweak it further. So, for maximum flexibility,
I turned to -A and -B which allow a user-supplied utility to be called
after or before each script execution. Such utility can log the time, but also
examine various other things.
I add a patch for run-parts.c and run-parts.8.
-- System Information:
Distributor ID: Devuan
Description: Devuan GNU/Linux 6 (excalibur)
Release: 6
Codename: excalibur
Architecture: x86_64
Kernel: Linux 6.12.73+deb13-amd64 (SMP w/8 CPU threads; PREEMPT)
Locale: LANG=en_IE.UTF-8, LC_CTYPE=en_IE.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/bash
Init: sysvinit (via /sbin/init)
Versions of packages debianutils depends on:
ii libc6 2.41-12+deb13u2
debianutils recommends no packages.
debianutils suggests no packages.
-- no debconf information
Add optional arguments -A, --after and -B, --before for greater control.
--- a/run-parts.8
+++ b/run-parts.8
@@ -11,7 +11,8 @@
.SH SYNOPSIS
.B run\-parts
[\-\-test] [\-\-verbose] [\-\-debug] [\-\-report] [\-\-lsbsysinit]
[\-\-regex=RE]
-[\-\-umask=umask] [\-\-arg=argument] [\-\-exit\-on\-error] [\-\-help]
+[\-\-umask=umask] [\-\-arg=argument] [\-\-after=PROG] [\-\-before=PROG]
+[\-\-exit\-on\-error] [\-\-help]
[\-\-version] [\-\-list] [\-\-reverse] [\-\-] DIRECTORY [DIRECTORY ...]
.PP
.B run\-parts
@@ -109,6 +110,24 @@
.B \-\-arg
once for each argument you want passed.
.TP
+.BI "\-A, \-\-after=" program
+run
+.I program
+after each script runs. The program will be called with three
+arguments: script name, exit status, and total amount written to
+the output and error streams. The last argument will always be 0
+unless the \-\-report option is also specified. If this option is
+used multiple times, only the last one will take effect. If the
+called program exits with an error code, it won't be called again.
+.TP
+.BI "\-B, \-\-before=" program
+run
+.I program
+before running each script. The program will be called with one
+argument: the script name. If this option is used multiple
+times, only the last one will take effect. If the called program
+exits with an error code, it won't be called again.
+.TP
.B "\-\-"
specifies that this is the end of the options. Any filename after
.B "\-\-"
--- a/run-parts.c
+++ b/run-parts.c
@@ -31,6 +31,7 @@
#include <signal.h>
#include <sys/time.h>
#include <regex.h>
+#include <inttypes.h>
#define RUNPARTS_NORMAL 0
#define RUNPARTS_ERE 1
@@ -56,6 +57,8 @@
char *custom_ere;
regex_t hierre, tradre, excsre, classicalre, customre;
+char *run_before, *run_after;
+
static void catch_signals();
static void restore_signals();
@@ -108,6 +111,8 @@
" --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"
+ " -A, --after=PROG run PROG after each script runs.\n"
+ " -B, --before=PROG run PROG before running each script.\n"
" -V, --version output version information and exit.\n"
" -h, --help display this help and exit.\n");
exit(0);
@@ -182,12 +187,79 @@
return retval;
}
+/* wait for call return */
+static void wait_for_call(pid_t pid, char *progname, char **run_what)
+{
+ int result, rtc = 0, r;
+ r = waitpid(pid, &result, 0);
+
+ if (r == -1) {
+ error("waitpid: %s", strerror(errno));
+ exit(1);
+ }
+
+ if (WIFEXITED(result) && WEXITSTATUS(result)) {
+ error("%s %s exited with return code %d", *run_what, progname,
+ rtc = WEXITSTATUS(result));
+ }
+ else if (WIFSIGNALED(result)) {
+ error("%s exited because of uncaught signal %d", *run_what,
+ WTERMSIG(result));
+ rtc = 1;
+ }
+
+ if (rtc)
+ *run_what = NULL;
+}
+
+/* run run_before */
+static void call_run_before(char *progname)
+{
+ pid_t pid = fork();
+ if (pid < 0) {
+ error("failed to fork: %s", strerror(errno));
+ exit(1);
+ }
+ else if (!pid) {
+ restore_signals();
+ char *argv[3] = {run_before, progname, NULL};
+ execvp(run_before, argv);
+ error("failed to exec %s: %s", run_before, strerror(errno));
+ exit(1);
+ }
+
+ wait_for_call(pid, progname, &run_before);
+}
+
+/* run run_after */
+static void call_run_after(char *progname, uint64_t tot_wrote)
+{
+ pid_t pid = fork();
+ if (pid < 0) {
+ error("failed to fork: %s", strerror(errno));
+ exit(1);
+ }
+ else if (!pid) {
+ char wrote[32], status[32];
+ restore_signals();
+ snprintf(status, sizeof status, "%d", exitstatus);
+ snprintf(wrote, sizeof wrote, "%" PRIu64, tot_wrote);
+ char *argv[5] = {run_after, progname, status, wrote, NULL};
+ execvp(run_after, argv);
+ error("failed to exec %s: %s", run_after, strerror(errno));
+ exit(1);
+ }
+
+ wait_for_call(pid, progname, &run_after);
+}
+
/* Execute a file */
void run_part(char *progname)
{
int result, waited;
int pid, r;
int pout[2], perr[2];
+ uint64_t tot_wrote = 0;
waited = 0;
@@ -195,6 +267,10 @@
error("pipe: %s", strerror(errno));
exit(1);
}
+
+ if (run_before)
+ call_run_before(progname);
+
if ((pid = fork()) < 0) {
error("failed to fork: %s", strerror(errno));
exit(1);
@@ -303,6 +379,7 @@
fflush(stdout);
printflag = 1;
}
+ tot_wrote += c;
ignored = write(STDOUT_FILENO, buf, c);
}
else if (c == 0) {
@@ -323,6 +400,7 @@
fflush(stderr);
printflag = 1;
}
+ tot_wrote += c;
ignored = write(STDERR_FILENO, buf, c);
}
else if (c == 0) {
@@ -368,6 +446,9 @@
WTERMSIG(result));
exitstatus = 1;
}
+
+ if (run_after)
+ call_run_after(progname, tot_wrote);
}
static void handle_signal(int s)
@@ -709,10 +790,12 @@
{"stdin", 0, &stdin_mode, 1},
{"exit-on-error", 0, &exit_on_error_mode, 1},
{"new-session", 0, &new_session_mode, 1},
+ {"after", 1, 0, 'A'},
+ {"before", 1, 0, 'B'},
{0, 0, 0, 0}
};
- c = getopt_long(argc, argv, "u:ha:vV", long_options, &option_index);
+ c = getopt_long(argc, argv, "u:ha:A:B:vV", long_options, &option_index);
if (c == EOF)
break;
switch (c) {
@@ -736,6 +819,12 @@
case 'd':
debug_mode = 1;
break;
+ case 'A':
+ run_after = optarg;
+ break;
+ case 'B':
+ run_before = optarg;
+ break;
case 'V':
version();
break;