Add support for -n option in crontab entries

Resolves: RHEL-5372
This commit is contained in:
Ondřej Pohořelský 2023-11-10 12:00:55 +01:00
parent 5bfb0297b0
commit eb373fda1e
2 changed files with 213 additions and 1 deletions

View File

@ -6,7 +6,7 @@
Summary: Cron daemon for executing programs at set times
Name: cronie
Version: 1.5.7
Release: 8%{?dist}
Release: 9%{?dist}
License: MIT and BSD and ISC and GPLv2+
URL: https://github.com/cronie-crond/cronie
Source0: https://github.com/cronie-crond/cronie/releases/download/cronie-%{version}/cronie-%{version}.tar.gz
@ -17,6 +17,9 @@ Patch: 0002-Add-random-within-range-operator.patch
Patch: 0003-get_number-Add-missing-NUL-termination-for-the-scann.patch
Patch: 0004-Fix-regression-in-handling-x-crontab-entries.patch
Patch: 0005-Fix-regression-in-handling-1-5-crontab-entries.patch
# Add support for `-n` option in crontab entries
# https://github.com/cronie-crond/cronie/commit/ce7d5bf0a43d147f8502e6424cd523b56adf5599
Patch: n_option.patch
Requires: dailyjobs
@ -211,6 +214,10 @@ exit 0
%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/cron.d/dailyjobs
%changelog
* Fri Nov 10 2023 Ondřej Pohořelský <opohorel@redhat.com> - 1.5.7-9
- Add support for `-n` option in crontab entries
- Resolves: RHEL-5372
* Mon Jul 11 2022 Jan Staněk <jstanek@redhat.com> - 1.5.7-8
- Set 'missingok' for /etc/cron.deny to not recreate it on update

205
n_option.patch Normal file
View File

@ -0,0 +1,205 @@
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