diff -ru cron-3.0pl1.orig/cron.c cron-3.0pl1/cron.c --- cron-3.0pl1.orig/cron.c 2023-06-23 16:23:46.000000000 +0100 +++ cron-3.0pl1/cron.c 2023-06-23 15:33:22.223916352 +0100 @@ -279,6 +279,9 @@ register user *u; register entry *e; int rbfd; + pid_t pid; + int delay = 10, attempts = 5, attempt; + char msg[256]; /* Run on actual reboot, rather than cron restart */ if (access(REBOOT_FILE, F_OK) == 0) { @@ -300,11 +303,46 @@ for (u = db->head; u != NULL; u = u->next) { for (e = u->crontab; e != NULL; e = e->next) { if (e->flags & WHEN_REBOOT) { - job_add(e, u); + reboot_job_add(e, u); } } } - (void) job_runqueue(); + if (reboot_job_runqueue()) + return; + + /* We only get here if a reboot job could not be started. */ + /* We fork and retry failed jobs in the background for 5 minutes before giving up. */ + /* (Failed means we didn't get as far as execing it. If the job itself could not run, we do not retry it. */ + pid = fork(); + + switch(pid) { + case -1: + log_it("CRON", getpid(),"error", "fork failed in run_reboot_jobs"); + return; + case 0: + break; + default: + return; + } + for (attempt=2; attempt <= attempts; attempt++) { + time_t t = time(NULL); + time_t target = t + delay; + while (t < target) + { + sleep(target - t); + t = time(NULL); + } + delay *= 2; + sprintf(msg, "retry reboot jobs, attempt=%i/%i", attempt, attempts); + log_it("CRON", getpid(),"INFO", msg); + if (reboot_job_runqueue()) { + log_it("CRON", getpid(),"INFO", "reboot jobs all started"); + exit(0); + } + } + sprintf(msg, "giving up on reboot jobs after %i attempts", attempts); + log_it("CRON", getpid(),"INFO", msg); + exit(0); } diff -ru cron-3.0pl1.orig/cron.h cron-3.0pl1/cron.h --- cron-3.0pl1.orig/cron.h 2023-06-23 16:23:46.000000000 +0100 +++ cron-3.0pl1/cron.h 2023-06-23 14:13:52.111182149 +0100 @@ -229,7 +229,7 @@ open_logfile __P((void)), sigpipe_func __P((void)), job_add __P((entry *, user *)), - do_command __P((entry *, user *)), + reboot_job_add __P((entry *, user *)), link_user __P((cron_db *, user *)), unlink_user __P((cron_db *, user *)), free_user __P((user *)), @@ -238,11 +238,13 @@ free_entry __P((entry *)), acquire_daemonlock __P((int)), skip_comments __P((FILE *)), - log_it __P((char *, int, char *, char *)), + log_it __P((const char *, int, const char *, const char *)), log_close __P((void)), check_orphans __P((cron_db *)); int job_runqueue __P((void)), + reboot_job_runqueue __P((void)), + do_command __P((entry *, user *)), set_debug_flags __P((char *)), get_char __P((FILE *)), get_string __P((char *, int, FILE *, char *)), diff -ru cron-3.0pl1.orig/do_command.c cron-3.0pl1/do_command.c --- cron-3.0pl1.orig/do_command.c 2023-06-23 16:23:46.000000000 +0100 +++ cron-3.0pl1/do_command.c 2023-06-23 16:12:24.424553470 +0100 @@ -25,6 +25,7 @@ #include #include #include +#include #if defined(sequent) # include #endif @@ -50,7 +51,7 @@ #endif -static void child_process __P((entry *, user *)), +static void child_process __P((entry *, user *, int)), do_univ __P((user *)); static int safe_p(const char *, const char *); @@ -80,11 +81,14 @@ return jobenv; } -void +int do_command(e, u) entry *e; user *u; { + int status_pipe[2]; + char status = 1; + Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n", getpid(), e->cmd, u->name, e->uid, e->gid)) @@ -95,22 +99,52 @@ * vfork() is unsuitable, since we have much to do, and the parent * needs to be able to run off and fork other processes. */ + if (pipe(status_pipe)) + { + log_it("CRON",getpid(),"error","can't create pipe for status"); + return FALSE; + } + + if (fcntl(status_pipe[WRITE_PIPE], F_SETFD, FD_CLOEXEC) == -1) { + log_it("CRON",getpid(),"error","can't set close on exec for status pipe"); + close(status_pipe[READ_PIPE]); + close(status_pipe[WRITE_PIPE]); + return FALSE; + } + switch (fork()) { case -1: log_it("CRON",getpid(),"error","can't fork"); + close(status_pipe[READ_PIPE]); + close(status_pipe[WRITE_PIPE]); break; case 0: /* child process */ + close(status_pipe[READ_PIPE]); acquire_daemonlock(1); - child_process(e, u); + child_process(e, u, status_pipe[WRITE_PIPE]); Debug(DPROC, ("[%d] child process done, exiting\n", getpid())) _exit(OK_EXIT); break; default: + close(status_pipe[WRITE_PIPE]); /* parent process */ break; } - Debug(DPROC, ("[%d] main process returning to work\n", getpid())) + + /* If the grandchild got as far as execing the process, we'll read a NIL byte from the pipe. */ + /* If we don't get anything (the error case), status will be 1 (set above) */ + while (read(status_pipe[READ_PIPE], &status, 1) == -1) + { + if (errno != EINTR) + { + log_it("CRON",getpid(),"error","can't read from status pipe"); + + } + } + + Debug(DPROC, ("[%d] main process returning to work\n", getpid())); + return status == 0; /* Return True if we ran the job */ } @@ -123,9 +157,11 @@ */ static void -child_process(e, u) +child_process(e, u, status_fd) entry *e; user *u; + int status_fd; + { int stdin_pipe[2]; FILE *tmpout; @@ -136,7 +172,8 @@ #if defined(USE_PAM) int retcode = 0; #endif - + char *home_dir; + Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd)) /* mark ourselves as different to PS command watchers by upshifting @@ -172,7 +209,12 @@ /* create a pipe to talk to our future child */ - pipe(stdin_pipe); /* child's stdin */ + if (pipe(stdin_pipe)) /* child's stdin */ + { + log_it("CRON", getpid(), "error", "can't create pipe for stdin"); + exit(ERROR_EXIT); + } + /* child's stdout */ if ((tmpout = tmpfile()) == NULL) { log_it("CRON", getpid(), "error", "create tmpfile"); @@ -247,7 +289,10 @@ */ if ((log_level & CRON_LOG_JOBSTART) && ! (log_level & CRON_LOG_JOBPID)) { char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); - log_it(usernm, getpid(), "CMD", x); + if (x) + log_it(usernm, getpid(), "CMD", x); + else + fprintf(stderr, "%s: Run OUT OF MEMORY while %s\n", ProgramName, __FUNCTION__); free(x); } /* nothing to log from now on. close the log files. @@ -311,7 +356,14 @@ log_it("CRON",getpid(),"error",msg); exit(ERROR_EXIT); } - chdir(env_get("HOME", e->envp)); + home_dir = env_get("HOME", e->envp); + if (chdir(home_dir)) { + char msg[256]; + snprintf(msg, 256, "do_command:chdir(%s) failed: %s", + home_dir, strerror(errno)); + log_it("CRON",getpid(),"error",msg); + exit(ERROR_EXIT); + } /* exec the command. */ @@ -345,6 +397,7 @@ } } #endif + write(status_fd, "", 1); /* Indicate to do_command that we got as far as starting the job */ execle(shell, shell, "-c", e->cmd, (char *)0, jobenv); fprintf(stderr, "%s: execle: %s\n", shell, strerror(errno)); _exit(ERROR_EXIT); @@ -373,10 +426,11 @@ Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid())) - /* close the end of the pipe that will only be referenced in the + /* close the end of the pipes that will only be referenced in the * grandchild process... */ close(stdin_pipe[READ_PIPE]); + close(status_fd); /* * write, to the pipe connected to child's stdin, any input specified diff -ru cron-3.0pl1.orig/job.c cron-3.0pl1/job.c --- cron-3.0pl1.orig/job.c 2023-06-23 16:23:46.000000000 +0100 +++ cron-3.0pl1/job.c 2023-06-23 14:20:15.417238487 +0100 @@ -32,6 +32,9 @@ static job *jhead = NULL, *jtail = NULL; +job *rjhead = NULL; +job **rjtail = &rjhead; + void job_add(e, u) @@ -73,3 +76,49 @@ jhead = jtail = NULL; return run; } + + +void +reboot_job_add(e, u) + register entry *e; + register user *u; +{ + register job *j; + + /* if already on queue, keep going */ + for (j=rjhead; j; j=j->next) + if (j->e == e && j->u == u) { return; } + + /* build a job queue element */ + if ((j = (job*)malloc(sizeof(job))) == NULL) + return; + j->next = NULL; + j->e = e; + j->u = u; + + /* add it to the tail */ + *rjtail = j; + rjtail = &j->next; +} + + +int +reboot_job_runqueue() +{ + job *j, *jn; + job *new_rjhead = NULL; + job **new_rjtail = &new_rjhead; + + for (j=rjhead; j; j=jn) { + jn = j->next; + if (do_command(j->e, j->u)) { + free(j); + } else { + *new_rjtail = j; + new_rjtail = &j->next; + } + } + rjhead = new_rjhead; + rjtail = new_rjtail; + return rjhead == NULL; +} diff -ru cron-3.0pl1.orig/misc.c cron-3.0pl1/misc.c --- cron-3.0pl1.orig/misc.c 2023-06-23 16:23:46.000000000 +0100 +++ cron-3.0pl1/misc.c 2023-06-23 14:13:30.559291380 +0100 @@ -524,10 +524,10 @@ void log_it(username, xpid, event, detail) - char *username; - int xpid; - char *event; - char *detail; + const char *username; + int xpid; + const char *event; + const char *detail; { PID_T pid = xpid; #if defined(LOG_FILE)