cronie/n_option.patch
Ondřej Pohořelský eb373fda1e Add support for -n option in crontab entries
Resolves: RHEL-5372
2023-11-10 12:00:55 +01:00

206 lines
5.8 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From ce7d5bf0a43d147f8502e6424cd523b56adf5599 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Poho=C5=99elsk=C3=BD?= <opohorel@redhat.com>
Date: Mon, 26 Sep 2022 16:51:41 +0200
Subject: [PATCH] Add -n option for crontab entries
The -n option causes no mail to be sent
when the command finishes successfully.
These kind of options are already supported in *BSD;
in fact, this is a port of a patch from NetBSD [1].
This was requested in [2].
[1]: NetBSD/src@666eac5
[2]: https://bugzilla.redhat.com/show_bug.cgi?id=1591763
---
src/do_command.c | 32 +++++++++++++++++++++++++++++---
src/entry.c | 31 +++++++++++++++++++++++++++++++
src/funcs.h | 1 +
src/popen.c | 33 ++++++++++++++++++++++++++++++---
src/structs.h | 1 +
5 files changed, 92 insertions(+), 6 deletions(-)
diff --git a/src/do_command.c b/src/do_command.c
index 87f996f..6a3886a 100644
--- a/src/do_command.c
+++ b/src/do_command.c
@@ -94,6 +94,7 @@ static int child_process(entry * e, char **jobenv) {
char mailfrom_expanded[MAX_EMAILSTR];
int children = 0;
pid_t pid = getpid();
+ pid_t jobpid = -1;
struct sigaction sa;
/* Ignore SIGPIPE as we will be writing to pipes and do not want to terminate
@@ -199,7 +200,7 @@ static int child_process(entry * e, char **jobenv) {
/* fork again, this time so we can exec the user's command.
*/
- switch (fork()) {
+ switch (jobpid = fork()) {
case -1:
log_it("CRON", pid, "CAN'T FORK", "child_process", errno);
return ERROR_EXIT;
@@ -552,10 +553,35 @@ static int child_process(entry * e, char **jobenv) {
}
#endif
}
- /* only close pipe if we opened it -- i.e., we're
- * mailing...
+ /* if -n option was specified, abort the sending
+ * now when we read all of the command output
+ * and thus can wait for it's exit status
*/
+ if (mail && e->flags & MAIL_WHEN_ERR) {
+ int jobstatus = -1;
+ if (jobpid > 0) {
+ while (waitpid(jobpid, &jobstatus, WNOHANG) == -1) {
+ if (errno == EINTR) continue;
+ log_it("CRON", getpid(), "error", "invalid job pid", errno);
+ break;
+ }
+ } else {
+ log_it("CRON", getpid(), "error", "invalid job pid", 0);
+ }
+ /* if everything went well, -n is set, and we have mail,
+ * we won't be mailing so shoot the messenger!
+ */
+ if (WIFEXITED(jobstatus) && WEXITSTATUS(jobstatus) == EXIT_SUCCESS) {
+ Debug(DPROC, ("[%ld] aborting pipe to mail\n", (long)getpid()));
+ status = cron_pabort(mail);
+ mail = NULL;
+ }
+ }
+
+ /* only close pipe if we opened it -- i.e., we're (still)
+ * mailing...
+ */
if (mail) {
Debug(DPROC, ("[%ld] closing pipe to mail\n", (long) getpid()));
/* Note: the pclose will probably see
diff --git a/src/entry.c b/src/entry.c
index bb7cb62..9e199fe 100644
--- a/src/entry.c
+++ b/src/entry.c
@@ -417,6 +417,37 @@ entry *load_entry(FILE * file, void (*error_func) (), struct passwd *pw,
Debug(DPARS, ("load_entry()...about to parse command\n"));
+ /* If the first character of the command is '-', it is a cron option. */
+ ch = get_char(file);
+ while (ch == '-') {
+ switch (ch = get_char(file)) {
+ case 'n':
+ /* only allow user to set the option once */
+ if ((e->flags & MAIL_WHEN_ERR) == MAIL_WHEN_ERR) {
+ ecode = e_option;
+ goto eof;
+ }
+ e->flags |= MAIL_WHEN_ERR;
+ break;
+
+ default:
+ ecode = e_option;
+ goto eof;
+ }
+
+ ch = get_char(file);
+ if (ch != '\t' && ch != ' ') {
+ ecode = e_option;
+ goto eof;
+ }
+ Skip_Blanks(ch, file);
+ if (ch == EOF || ch == '\n') {
+ ecode = e_cmd;
+ goto eof;
+ }
+ }
+ unget_char(ch, file);
+
/* Everything up to the next \n or EOF is part of the command...
* too bad we don't know in advance how long it will be, since we
* need to malloc a string for it... so, we limit it to MAX_COMMAND.
diff --git a/src/funcs.h b/src/funcs.h
index dea737e..427e027 100644
--- a/src/funcs.h
+++ b/src/funcs.h
@@ -67,6 +67,7 @@ int load_database(cron_db *),
swap_uids_back(void),
load_env(char *, FILE *),
env_set_from_environ(char ***envpp),
+ cron_pabort(FILE *),
cron_pclose(FILE *),
glue_strings(char *, size_t, const char *, const char *, char),
strcmp_until(const char *, const char *, char),
diff --git a/src/popen.c b/src/popen.c
index 4397264..3043eb6 100644
--- a/src/popen.c
+++ b/src/popen.c
@@ -167,7 +167,7 @@ FILE *cron_popen(char *program, const char *type, struct passwd *pw, char **jobe
return (iop);
}
-int cron_pclose(FILE * iop) {
+static int cron_finalize(FILE * iop, int sig) {
int fdes;
sigset_t oset, nset;
WAIT_T stat_loc;
@@ -180,7 +180,12 @@ int cron_pclose(FILE * iop) {
fdes = fileno(iop);
if (pids == NULL || fdes >= fds || pids[fdes] == 0L)
return (-1);
- (void) fclose(iop);
+
+ if (!sig) {
+ (void) fclose(iop);
+ } else if (kill(pids[fdes], sig) == -1) {
+ return -1;
+ }
sigemptyset(&nset);
sigaddset(&nset, SIGINT);
@@ -189,6 +194,28 @@ int cron_pclose(FILE * iop) {
(void) sigprocmask(SIG_BLOCK, &nset, &oset);
while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1) ;
(void) sigprocmask(SIG_SETMASK, &oset, NULL);
+
+ if (sig) {
+ (void) fclose(iop);
+ }
pids[fdes] = 0;
- return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
+
+ if (pid < 0) {
+ return pid;
+ }
+
+ if (WIFEXITED(stat_loc)) {
+ return WEXITSTATUS(stat_loc);
+ } else {
+ return WTERMSIG(stat_loc);
+ }
+}
+
+int cron_pclose(FILE * iop) {
+ return cron_finalize(iop, 0);
+}
+
+int cron_pabort(FILE * iop) {
+ int esig = cron_finalize(iop, SIGKILL);
+ return esig == SIGKILL ? 0 : esig;
}
diff --git a/src/structs.h b/src/structs.h
index 6d3c15b..d930da5 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -48,6 +48,7 @@ typedef struct _entry {
#define DOW_STAR 0x08
#define WHEN_REBOOT 0x10
#define DONT_LOG 0x20
+#define MAIL_WHEN_ERR 0x40
} entry;
/* the crontab database will be a list of the