move remaining help text from include/usage.src.h
[oweals/busybox.git] / miscutils / crond.c
index 28722563c2bbcde5e982c5091fe10a47c9a90786..014016fb63c16f3ab00fa6e03c0dae69977b8f2d 100644 (file)
@@ -8,9 +8,22 @@
  * (version 2.3.2)
  * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
+//usage:#define crond_trivial_usage
+//usage:       "-fbS -l N " IF_FEATURE_CROND_D("-d N ") "-L LOGFILE -c DIR"
+//usage:#define crond_full_usage "\n\n"
+//usage:       "       -f      Foreground"
+//usage:     "\n       -b      Background (default)"
+//usage:     "\n       -S      Log to syslog (default)"
+//usage:     "\n       -l      Set log level. 0 is the most verbose, default 8"
+//usage:       IF_FEATURE_CROND_D(
+//usage:     "\n       -d      Set log level, log to stderr"
+//usage:       )
+//usage:     "\n       -L      Log to file"
+//usage:     "\n       -c      Working dir"
+
 #include "libbb.h"
 #include <syslog.h>
 
 # define SENDMAIL       "sendmail"
 #endif
 #ifndef SENDMAIL_ARGS
-# define SENDMAIL_ARGS  "-ti", NULL
+# define SENDMAIL_ARGS  "-ti"
 #endif
 #ifndef CRONUPDATE
 # define CRONUPDATE     "cron.update"
 #endif
 #ifndef MAXLINES
-# define MAXLINES       256    /* max lines in non-root crontabs */
+# define MAXLINES       256  /* max lines in non-root crontabs */
 #endif
 
 
@@ -53,9 +66,8 @@ typedef struct CronLine {
        char *cl_cmd;                   /* shell command */
        pid_t cl_pid;                   /* >0:running, <0:needs to be started in this minute, 0:dormant */
 #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
-       int cl_empty_mail_size;         /* size of mail header only */
+       int cl_empty_mail_size;         /* size of mail header only, 0 if no mailfile */
        char *cl_mailto;                /* whom to mail results, may be NULL */
-       smallint cl_mail_result;        /* mail file is created, need to send it on completion */
 #endif
        /* ordered by size, not in natural order. makes code smaller: */
        char cl_Dow[7];                 /* 0-6, beginning sunday */
@@ -124,7 +136,7 @@ static void crondlog(const char *ctl, ...)
                /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */
                if (!DebugOpt && G.log_filename) {
                        /* Otherwise (log to file): we reopen log file at every write: */
-                       int logfd = open3_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
+                       int logfd = open_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND);
                        if (logfd >= 0)
                                xmove_fd(logfd, STDERR_FILENO);
                }
@@ -171,7 +183,7 @@ static void ParseField(char *user, char *ary, int modvalue, int off,
 
                /* Handle numeric digit or symbol or '*' */
                if (*ptr == '*') {
-                       n1 = 0;         /* everything will be filled */
+                       n1 = 0;  /* everything will be filled */
                        n2 = modvalue - 1;
                        skip = 1;
                        ++ptr;
@@ -244,7 +256,6 @@ static void ParseField(char *user, char *ary, int modvalue, int off,
                                        goto err;
                                }
                        } while (n1 != n2);
-
                }
                if (*ptr != ',') {
                        break;
@@ -305,13 +316,15 @@ static void FixDayDow(CronLine *line)
 //if crontab was reloaded: crond thinks that "new" job is different from "old"
 //even if they are in fact completely the same. Example
 //Crontab was:
-// 0-59 * * * job1
-// 0-59 * * * long_running_job2
+// 0-59 * * * job1
+// 0-59 * * * long_running_job2
 //User edits crontab to:
-// 0-59 * * * job1_updated
-// 0-59 * * * long_running_job2
+// 0-59 * * * job1_updated
+// 0-59 * * * long_running_job2
 //Bug: crond can now start another long_running_job2 even if old one
 //is still running.
+//OTOH most other versions of cron do not wait for job termination anyway,
+//they end up with multiple copies of jobs if they don't terminate soon enough.
 static void delete_cronfile(const char *userName)
 {
        CronFile **pfile = &G.cron_files;
@@ -536,13 +549,11 @@ static void change_user(struct passwd *pas)
 // TODO: sendmail should be _run-time_ option, not compile-time!
 #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
 
-//TODO: return pid (and stop passing line);
-// stop passing mail_filename here but process it in caller
-static void
-fork_job(const char *user, CronLine *line, int mailFd,
-               const char *prog, const char *cmd, const char *arg,
-               const char *mail_filename)
-{
+static pid_t
+fork_job(const char *user, int mailFd,
+               const char *prog,
+               const char *shell_cmd /* if NULL, we run sendmail */
+) {
        struct passwd *pas;
        pid_t pid;
 
@@ -563,37 +574,25 @@ fork_job(const char *user, CronLine *line, int mailFd,
                        crondlog(LVL5 "child running %s", prog);
                }
                if (mailFd >= 0) {
-                       xmove_fd(mailFd, mail_filename ? 1 : 0);
+                       xmove_fd(mailFd, shell_cmd ? 1 : 0);
                        dup2(1, 2);
                }
                /* crond 3.0pl1-100 puts tasks in separate process groups */
                bb_setpgrp();
-               execlp(prog, prog, cmd, arg, (char *) NULL);
-               crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, prog, cmd, arg);
-               if (mail_filename) {
-                       fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
+               execlp(prog, prog, (shell_cmd ? "-c" : SENDMAIL_ARGS), shell_cmd, (char *) NULL);
+               crondlog(ERR20 "can't execute '%s' for user %s", prog, user);
+               if (shell_cmd) {
+                       fdprintf(1, "Exec failed: %s -c %s\n", prog, shell_cmd);
                }
                _exit(EXIT_SUCCESS);
        }
 
-       line->cl_pid = pid;
        if (pid < 0) {
                /* FORK FAILED */
                crondlog(ERR20 "can't vfork");
  err:
-               line->cl_pid = 0;
-               if (mail_filename) {
-                       unlink(mail_filename);
-               }
-       } else {
-               /* PARENT, FORK SUCCESS */
-               if (mail_filename) {
-                       /* rename mail-file based on pid of process */
-                       char *mailFile2 = xasprintf("%s/cron.%s.%d", TMPDIR, user, (int)pid);
-                       rename(mail_filename, mailFile2); // TODO: xrename?
-                       free(mailFile2);
-               }
-       }
+               pid = 0;
+       } /* else: PARENT, FORK SUCCESS */
 
        /*
         * Close the mail file descriptor.. we can't just leave it open in
@@ -602,6 +601,7 @@ fork_job(const char *user, CronLine *line, int mailFd,
        if (mailFd >= 0) {
                close(mailFd);
        }
+       return pid;
 }
 
 static void start_one_job(const char *user, CronLine *line)
@@ -610,7 +610,7 @@ static void start_one_job(const char *user, CronLine *line)
        int mailFd = -1;
 
        line->cl_pid = 0;
-       line->cl_mail_result = 0;
+       line->cl_empty_mail_size = 0;
 
        if (line->cl_mailto) {
                /* Open mail file (owner is root so nobody can screw with it) */
@@ -618,7 +618,6 @@ static void start_one_job(const char *user, CronLine *line)
                mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
 
                if (mailFd >= 0) {
-                       line->cl_mail_result = 1;
                        fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", line->cl_mailto,
                                line->cl_cmd);
                        line->cl_empty_mail_size = lseek(mailFd, 0, SEEK_CUR);
@@ -628,7 +627,17 @@ static void start_one_job(const char *user, CronLine *line)
                }
        }
 
-       fork_job(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_cmd, mailFile);
+       line->cl_pid = fork_job(user, mailFd, DEFAULT_SHELL, line->cl_cmd);
+       if (mailFd >= 0) {
+               if (line->cl_pid <= 0) {
+                       unlink(mailFile);
+               } else {
+                       /* rename mail-file based on pid of process */
+                       char *mailFile2 = xasprintf("%s/cron.%s.%d", TMPDIR, user, (int)line->cl_pid);
+                       rename(mailFile, mailFile2); // TODO: xrename?
+                       free(mailFile2);
+               }
+       }
 }
 
 /*
@@ -647,11 +656,10 @@ static void process_finished_job(const char *user, CronLine *line)
                /* No job */
                return;
        }
-       if (line->cl_mail_result == 0) {
+       if (line->cl_empty_mail_size <= 0) {
                /* End of job and no mail file, or end of sendmail job */
                return;
        }
-       line->cl_mail_result = 0;
 
        /*
         * End of primary job - check for mail file.
@@ -673,8 +681,9 @@ static void process_finished_job(const char *user, CronLine *line)
                close(mailFd);
                return;
        }
-       /* if (line->cl_mailto) - always true if cl_mail_result was true */
-               fork_job(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
+       line->cl_empty_mail_size = 0;
+       /* if (line->cl_mailto) - always true if cl_empty_mail_size was nonzero */
+               line->cl_pid = fork_job(user, mailFd, SENDMAIL, NULL);
 }
 
 #else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */