Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/miscutils/crond.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 815 by niro, Sat Sep 1 22:45:15 2007 UTC revision 816 by niro, Fri Apr 24 18:33:46 2009 UTC
# Line 5  Line 5 
5   * run as root, but NOT setuid root   * run as root, but NOT setuid root
6   *   *
7   * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)   * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
8     * (version 2.3.2)
9   * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002   * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
10   *   *
11   * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.   * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
12   */   */
13    
14  #define VERSION "2.3.2"  #include "libbb.h"
15    #include <syslog.h>
16    
17  #include "busybox.h"  /* glibc frees previous setenv'ed value when we do next setenv()
18  #include <sys/syslog.h>   * of the same variable. uclibc does not do this! */
19    #if (defined(__GLIBC__) && !defined(__UCLIBC__)) /* || OTHER_SAFE_LIBC... */
20    #define SETENV_LEAKS 0
21    #else
22    #define SETENV_LEAKS 1
23    #endif
24    
 #define arysize(ary)    (sizeof(ary)/sizeof((ary)[0]))  
25    
26  #ifndef CRONTABS  #ifndef CRONTABS
27  #define CRONTABS        "/var/spool/cron/crontabs"  #define CRONTABS        "/var/spool/cron/crontabs"
# Line 24  Line 30 
30  #define TMPDIR          "/var/spool/cron"  #define TMPDIR          "/var/spool/cron"
31  #endif  #endif
32  #ifndef SENDMAIL  #ifndef SENDMAIL
33  #define SENDMAIL        "/usr/sbin/sendmail"  #define SENDMAIL        "sendmail"
34  #endif  #endif
35  #ifndef SENDMAIL_ARGS  #ifndef SENDMAIL_ARGS
36  #define SENDMAIL_ARGS   "-ti", "oem"  #define SENDMAIL_ARGS   "-ti", "oem"
# Line 36  Line 42 
42  #define MAXLINES        256 /* max lines in non-root crontabs */  #define MAXLINES        256 /* max lines in non-root crontabs */
43  #endif  #endif
44    
45    
46  typedef struct CronFile {  typedef struct CronFile {
47   struct CronFile *cf_Next;   struct CronFile *cf_Next;
48   struct CronLine *cf_LineBase;   struct CronLine *cf_LineBase;
49   char *cf_User; /* username                     */   char *cf_User;                  /* username                     */
50   int cf_Ready; /* bool: one or more jobs ready */   smallint cf_Ready;              /* bool: one or more jobs ready */
51   int cf_Running; /* bool: one or more jobs running */   smallint cf_Running;            /* bool: one or more jobs running */
52   int cf_Deleted; /* marked for deletion, ignore  */   smallint cf_Deleted;            /* marked for deletion, ignore  */
53  } CronFile;  } CronFile;
54    
55  typedef struct CronLine {  typedef struct CronLine {
56   struct CronLine *cl_Next;   struct CronLine *cl_Next;
57   char *cl_Shell; /* shell command                        */   char *cl_Shell;         /* shell command                        */
58   pid_t cl_Pid; /* running pid, 0, or armed (-1)        */   pid_t cl_Pid;           /* running pid, 0, or armed (-1)        */
59   int cl_MailFlag; /* running pid is for mail              */  #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
60   int cl_MailPos; /* 'empty file' size                    */   int cl_MailPos;         /* 'empty file' size                    */
61   char cl_Mins[60]; /* 0-59                                 */   smallint cl_MailFlag;   /* running pid is for mail              */
62   char cl_Hrs[24]; /* 0-23                                 */   char *cl_MailTo; /* whom to mail results */
63   char cl_Days[32]; /* 1-31                                 */  #endif
64   char cl_Mons[12]; /* 0-11                                 */   /* ordered by size, not in natural order. makes code smaller: */
65   char cl_Dow[7]; /* 0-6, beginning sunday                */   char cl_Dow[7];         /* 0-6, beginning sunday                */
66     char cl_Mons[12];       /* 0-11                                 */
67     char cl_Hrs[24];        /* 0-23                                 */
68     char cl_Days[32];       /* 1-31                                 */
69     char cl_Mins[60];       /* 0-59                                 */
70  } CronLine;  } CronLine;
71    
 #define RUN_RANOUT      1  
 #define RUN_RUNNING     2  
 #define RUN_FAILED      3  
72    
73  #define DaemonUid 0  #define DaemonUid 0
74    
75  #if ENABLE_DEBUG_CROND_OPTION  
76  static unsigned DebugOpt;  enum {
77     OPT_l = (1 << 0),
78     OPT_L = (1 << 1),
79     OPT_f = (1 << 2),
80     OPT_b = (1 << 3),
81     OPT_S = (1 << 4),
82     OPT_c = (1 << 5),
83     OPT_d = (1 << 6) * ENABLE_FEATURE_CROND_D,
84    };
85    #if ENABLE_FEATURE_CROND_D
86    #define DebugOpt (option_mask32 & OPT_d)
87    #else
88    #define DebugOpt 0
89  #endif  #endif
90    
 static unsigned LogLevel = 8;  
 static const char *LogFile;  
 static const char *CDir = CRONTABS;  
91    
92  static void startlogger(void);  struct globals {
93     unsigned LogLevel; /* = 8; */
94     const char *LogFile;
95     const char *CDir; /* = CRONTABS; */
96     CronFile *FileBase;
97    #if SETENV_LEAKS
98     char *env_var_user;
99     char *env_var_home;
100    #endif
101    };
102    #define G (*(struct globals*)&bb_common_bufsiz1)
103    #define LogLevel           (G.LogLevel               )
104    #define LogFile            (G.LogFile                )
105    #define CDir               (G.CDir                   )
106    #define FileBase           (G.FileBase               )
107    #define env_var_user       (G.env_var_user           )
108    #define env_var_home       (G.env_var_home           )
109    #define INIT_G() do { \
110     LogLevel = 8; \
111     CDir = CRONTABS; \
112    } while (0)
113    
114    
115  static void CheckUpdates(void);  static void CheckUpdates(void);
116  static void SynchronizeDir(void);  static void SynchronizeDir(void);
117  static int TestJobs(time_t t1, time_t t2);  static int TestJobs(time_t t1, time_t t2);
118  static void RunJobs(void);  static void RunJobs(void);
119  static int CheckJobs(void);  static int CheckJobs(void);
120    static void RunJob(const char *user, CronLine *line);
 static void RunJob(const char *user, CronLine * line);  
   
121  #if ENABLE_FEATURE_CROND_CALL_SENDMAIL  #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
122  static void EndJob(const char *user, CronLine * line);  static void EndJob(const char *user, CronLine *line);
123  #else  #else
124  #define EndJob(user, line)  line->cl_Pid = 0  #define EndJob(user, line)  ((line)->cl_Pid = 0)
125  #endif  #endif
   
126  static void DeleteFile(const char *userName);  static void DeleteFile(const char *userName);
127    
 static CronFile *FileBase;  
128    
129    #define LVL5  "\x05"
130    #define LVL7  "\x07"
131    #define LVL8  "\x08"
132    #define LVL9  "\x09"
133    #define WARN9 "\x49"
134    #define DIE9  "\xc9"
135    /* level >= 20 is "error" */
136    #define ERR20 "\x14"
137    
138  static void crondlog(const char *ctl, ...)  static void crondlog(const char *ctl, ...)
139  {  {
140   va_list va;   va_list va;
141   const char *fmt;   int level = (ctl[0] & 0x1f);
  int level = (int) (ctl[0] & 0xf);  
  int type = level == 20 ?  
  LOG_ERR : ((ctl[0] & 0100) ? LOG_WARNING : LOG_NOTICE);  
   
142    
143   va_start(va, ctl);   va_start(va, ctl);
144   fmt = ctl + 1;   if (level >= (int)LogLevel) {
145   if (level >= LogLevel) {   /* Debug mode: all to (non-redirected) stderr, */
146     /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */
147  #if ENABLE_DEBUG_CROND_OPTION   if (!DebugOpt && LogFile) {
148   if (DebugOpt) {   /* Otherwise (log to file): we reopen log file at every write: */
149   vfprintf(stderr, fmt, va);   int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
150   } else   if (logfd >= 0)
151  #endif   xmove_fd(logfd, STDERR_FILENO);
  if (LogFile == 0) {  
  vsyslog(type, fmt, va);  
  } else {  
  int logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);  
  if (logfd >= 0) {  
  vdprintf(logfd, fmt, va);  
  close(logfd);  
 #if ENABLE_DEBUG_CROND_OPTION  
  } else {  
  bb_perror_msg("can't open log file");  
 #endif  
  }  
152   }   }
153    // TODO: ERR -> error, WARN -> warning, LVL -> info
154     bb_verror_msg(ctl + 1, va, /* strerr: */ NULL);
155   }   }
156   va_end(va);   va_end(va);
157   if (ctl[0] & 0200) {   if (ctl[0] & 0x80)
158   exit(20);   exit(20);
  }  
159  }  }
160    
161  int crond_main(int ac, char **av)  int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
162    int crond_main(int argc UNUSED_PARAM, char **argv)
163  {  {
164   unsigned opt;   unsigned opt;
  char *lopt, *Lopt, *copt;  
   
 #if ENABLE_DEBUG_CROND_OPTION  
  char *dopt;  
165    
166   opt_complementary = "f-b:b-f:S-L:L-S:d-l";   INIT_G();
 #else  
  opt_complementary = "f-b:b-f:S-L:L-S";  
 #endif  
167    
168   opterr = 0; /* disable getopt 'errors' message. */   /* "-b after -f is ignored", and so on for every pair a-b */
169   opt = getopt32(ac, av, "l:L:fbSc:"   opt_complementary = "f-b:b-f:S-L:L-S" USE_FEATURE_CROND_D(":d-l")
170  #if ENABLE_DEBUG_CROND_OPTION   ":l+:d+"; /* -l and -d have numeric param */
171   "d:"   opt = getopt32(argv, "l:L:fbSc:" USE_FEATURE_CROND_D("d:"),
172  #endif   &LogLevel, &LogFile, &CDir
173   , &lopt, &Lopt, &copt   USE_FEATURE_CROND_D(,&LogLevel));
174  #if ENABLE_DEBUG_CROND_OPTION   /* both -d N and -l N set the same variable: LogLevel */
175   , &dopt  
176  #endif   if (!(opt & OPT_f)) {
177   );   /* close stdin, stdout, stderr.
178   if (opt & 1) {   * close unused descriptors - don't need them. */
179   LogLevel = xatou(lopt);   bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
180   }   }
  if (opt & 2) {  
  if (*Lopt != 0) {  
  LogFile = Lopt;  
  }  
  }  
  if (opt & 32) {  
  if (*copt != 0) {  
  CDir = copt;  
  }  
  }  
 #if ENABLE_DEBUG_CROND_OPTION  
  if (opt & 64) {  
  DebugOpt = xatou(dopt);  
  LogLevel = 0;  
  }  
 #endif  
   
  /*  
  * change directory  
  */  
181    
182   xchdir(CDir);   if (!DebugOpt && LogFile == NULL) {
183   signal(SIGHUP, SIG_IGN); /* hmm.. but, if kill -HUP original   /* logging to syslog */
184   * version - his died. ;(   openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
185   */   logmode = LOGMODE_SYSLOG;
  /*  
  * close stdin and stdout, stderr.  
  * close unused descriptors -  don't need.  
  * optional detach from controlling terminal  
  */  
   
  if (!(opt & 4)) {  
 #ifdef BB_NOMMU  
  /* reexec for vfork() do continue parent */  
  vfork_daemon_rexec(1, 0, ac, av, "-f");  
 #else  
  xdaemon(1, 0);  
 #endif  
186   }   }
187    
188   (void) startlogger(); /* need if syslog mode selected */   xchdir(CDir);
189     //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
190   /*   xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */
191   * main loop - synchronize to 1 second after the minute, minimum sleep   crondlog(LVL9 "crond (busybox "BB_VER") started, log level %d", LogLevel);
  *             of 1 second.  
  */  
   
  crondlog("\011%s " VERSION " dillon, started, log level %d\n",  
  applet_name, LogLevel);  
   
192   SynchronizeDir();   SynchronizeDir();
193    
194     /* main loop - synchronize to 1 second after the minute, minimum sleep
195     * of 1 second. */
196   {   {
197   time_t t1 = time(NULL);   time_t t1 = time(NULL);
198   time_t t2;   time_t t2;
199   long dt;   long dt;
200   int rescan = 60;   int rescan = 60;
201   short sleep_time = 60;   int sleep_time = 60;
202    
203     write_pidfile("/var/run/crond.pid");
204   for (;;) {   for (;;) {
205   sleep((sleep_time + 1) - (short) (time(NULL) % sleep_time));   sleep((sleep_time + 1) - (time(NULL) % sleep_time));
206    
207   t2 = time(NULL);   t2 = time(NULL);
208   dt = t2 - t1;   dt = (long)t2 - (long)t1;
209    
210   /*   /*
211   * The file 'cron.update' is checked to determine new cron   * The file 'cron.update' is checked to determine new cron
# Line 238  int crond_main(int ac, char **av) Line 223  int crond_main(int ac, char **av)
223   * when running jobs, the inequality used is greater but not   * when running jobs, the inequality used is greater but not
224   * equal to t1, and less then or equal to t2.   * equal to t1, and less then or equal to t2.
225   */   */
   
226   if (--rescan == 0) {   if (--rescan == 0) {
227   rescan = 60;   rescan = 60;
228   SynchronizeDir();   SynchronizeDir();
229   }   }
230   CheckUpdates();   CheckUpdates();
 #if ENABLE_DEBUG_CROND_OPTION  
231   if (DebugOpt)   if (DebugOpt)
232   crondlog("\005Wakeup dt=%d\n", dt);   crondlog(LVL5 "wakeup dt=%ld", dt);
 #endif  
233   if (dt < -60 * 60 || dt > 60 * 60) {   if (dt < -60 * 60 || dt > 60 * 60) {
234   t1 = t2;   crondlog(WARN9 "time disparity of %d minutes detected", dt / 60);
  crondlog("\111time disparity of %d minutes detected\n", dt / 60);  
235   } else if (dt > 0) {   } else if (dt > 0) {
236   TestJobs(t1, t2);   TestJobs(t1, t2);
237   RunJobs();   RunJobs();
# Line 260  int crond_main(int ac, char **av) Line 241  int crond_main(int ac, char **av)
241   } else {   } else {
242   sleep_time = 60;   sleep_time = 60;
243   }   }
  t1 = t2;  
244   }   }
245     t1 = t2;
246   }   }
247   }   }
248   return 0; /* not reached */   return 0; /* not reached */
249  }  }
250    
251  static int ChangeUser(const char *user)  #if SETENV_LEAKS
252    /* We set environment *before* vfork (because we want to use vfork),
253     * so we cannot use setenv() - repeated calls to setenv() may leak memory!
254     * Using putenv(), and freeing memory after unsetenv() won't leak */
255    static void safe_setenv4(char **pvar_val, const char *var, const char *val /*, int len*/)
256  {  {
257   struct passwd *pas;   const int len = 4; /* both var names are 4 char long */
258   const char *err_msg;   char *var_val = *pvar_val;
   
  /*  
  * Obtain password entry and change privileges  
  */  
  pas = getpwnam(user);  
  if (pas == 0) {  
  crondlog("\011failed to get uid for %s", user);  
  return -1;  
  }  
  setenv("USER", pas->pw_name, 1);  
  setenv("HOME", pas->pw_dir, 1);  
  setenv("SHELL", DEFAULT_SHELL, 1);  
259    
260   /*   if (var_val) {
261   * Change running state to the user in question   var_val[len] = '\0'; /* nuke '=' */
262   */   unsetenv(var_val);
263   err_msg = change_identity_e2str(pas);   free(var_val);
  if (err_msg) {  
  crondlog("\011%s for user %s", err_msg, user);  
  return -1;  
  }  
  if (chdir(pas->pw_dir) < 0) {  
  crondlog("\011chdir failed: %s: %m", pas->pw_dir);  
  if (chdir(TMPDIR) < 0) {  
  crondlog("\011chdir failed: %s: %m", TMPDIR);  
  return -1;  
  }  
264   }   }
265   return pas->pw_uid;   *pvar_val = xasprintf("%s=%s", var, val);
266     putenv(*pvar_val);
267  }  }
268    #endif
269    
270  static void startlogger(void)  static void SetEnv(struct passwd *pas)
271  {  {
272   if (LogFile == 0) {  #if SETENV_LEAKS
273   openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);   safe_setenv4(&env_var_user, "USER", pas->pw_name);
274   }   safe_setenv4(&env_var_home, "HOME", pas->pw_dir);
275  #if ENABLE_DEBUG_CROND_OPTION   /* if we want to set user's shell instead: */
276   else { /* test logfile */   /*safe_setenv(env_var_user, "SHELL", pas->pw_shell, 5);*/
277   int logfd;  #else
278     xsetenv("USER", pas->pw_name);
279     xsetenv("HOME", pas->pw_dir);
280    #endif
281     /* currently, we use constant one: */
282     /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */
283    }
284    
285   if ((logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600)) >= 0) {  static void ChangeUser(struct passwd *pas)
286   close(logfd);  {
287   } else {   /* careful: we're after vfork! */
288   bb_perror_msg("failed to open log file '%s': ", LogFile);   change_identity(pas); /* - initgroups, setgid, setuid */
289     if (chdir(pas->pw_dir) < 0) {
290     crondlog(LVL9 "can't chdir(%s)", pas->pw_dir);
291     if (chdir(TMPDIR) < 0) {
292     crondlog(DIE9 "can't chdir(%s)", TMPDIR); /* exits */
293   }   }
294   }   }
 #endif  
295  }  }
296    
297    static const char DowAry[] ALIGN1 =
298     "sun""mon""tue""wed""thu""fri""sat"
299     /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */
300    ;
301    
302  static const char *const DowAry[] = {  static const char MonAry[] ALIGN1 =
303   "sun",   "jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec"
304   "mon",   /* "Jan""Feb""Mar""Apr""May""Jun""Jul""Aug""Sep""Oct""Nov""Dec" */
305   "tue",  ;
  "wed",  
  "thu",  
  "fri",  
  "sat",  
   
  "Sun",  
  "Mon",  
  "Tue",  
  "Wed",  
  "Thu",  
  "Fri",  
  "Sat",  
  NULL  
 };  
306    
307  static const char *const MonAry[] = {  static void ParseField(char *user, char *ary, int modvalue, int off,
308   "jan",   const char *names, char *ptr)
309   "feb",  /* 'names' is a pointer to a set of 3-char abbreviations */
  "mar",  
  "apr",  
  "may",  
  "jun",  
  "jul",  
  "aug",  
  "sep",  
  "oct",  
  "nov",  
  "dec",  
   
  "Jan",  
  "Feb",  
  "Mar",  
  "Apr",  
  "May",  
  "Jun",  
  "Jul",  
  "Aug",  
  "Sep",  
  "Oct",  
  "Nov",  
  "Dec",  
  NULL  
 };  
   
 static char *ParseField(char *user, char *ary, int modvalue, int off,  
  const char *const *names, char *ptr)  
310  {  {
311   char *base = ptr;   char *base = ptr;
312   int n1 = -1;   int n1 = -1;
313   int n2 = -1;   int n2 = -1;
314    
315   if (base == NULL) {   // this can't happen due to config_read()
316   return NULL;   /*if (base == NULL)
317   }   return;*/
318    
319   while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {   while (1) {
320   int skip = 0;   int skip = 0;
321    
322   /* Handle numeric digit or symbol or '*' */   /* Handle numeric digit or symbol or '*' */
   
323   if (*ptr == '*') {   if (*ptr == '*') {
324   n1 = 0; /* everything will be filled */   n1 = 0; /* everything will be filled */
325   n2 = modvalue - 1;   n2 = modvalue - 1;
326   skip = 1;   skip = 1;
327   ++ptr;   ++ptr;
328   } else if (*ptr >= '0' && *ptr <= '9') {   } else if (isdigit(*ptr)) {
329   if (n1 < 0) {   if (n1 < 0) {
330   n1 = strtol(ptr, &ptr, 10) + off;   n1 = strtol(ptr, &ptr, 10) + off;
331   } else {   } else {
# Line 400  static char *ParseField(char *user, char Line 335  static char *ParseField(char *user, char
335   } else if (names) {   } else if (names) {
336   int i;   int i;
337    
338   for (i = 0; names[i]; ++i) {   for (i = 0; names[i]; i += 3) {
339   if (strncmp(ptr, names[i], strlen(names[i])) == 0) {   /* was using strncmp before... */
340     if (strncasecmp(ptr, &names[i], 3) == 0) {
341     ptr += 3;
342     if (n1 < 0) {
343     n1 = i / 3;
344     } else {
345     n2 = i / 3;
346     }
347     skip = 1;
348   break;   break;
349   }   }
350   }   }
  if (names[i]) {  
  ptr += strlen(names[i]);  
  if (n1 < 0) {  
  n1 = i;  
  } else {  
  n2 = i;  
  }  
  skip = 1;  
  }  
351   }   }
352    
353   /* handle optional range '-' */   /* handle optional range '-' */
   
354   if (skip == 0) {   if (skip == 0) {
355   crondlog("\111failed user %s parsing %s\n", user, base);   goto err;
  return NULL;  
356   }   }
357   if (*ptr == '-' && n2 < 0) {   if (*ptr == '-' && n2 < 0) {
358   ++ptr;   ++ptr;
# Line 431  static char *ParseField(char *user, char Line 363  static char *ParseField(char *user, char
363   * collapse single-value ranges, handle skipmark, and fill   * collapse single-value ranges, handle skipmark, and fill
364   * in the character array appropriately.   * in the character array appropriately.
365   */   */
   
366   if (n2 < 0) {   if (n2 < 0) {
367   n2 = n1;   n2 = n1;
368   }   }
369   if (*ptr == '/') {   if (*ptr == '/') {
370   skip = strtol(ptr + 1, &ptr, 10);   skip = strtol(ptr + 1, &ptr, 10);
371   }   }
372    
373   /*   /*
374   * fill array, using a failsafe is the easiest way to prevent   * fill array, using a failsafe is the easiest way to prevent
375   * an endless loop   * an endless loop
376   */   */
   
377   {   {
378   int s0 = 1;   int s0 = 1;
379   int failsafe = 1024;   int failsafe = 1024;
# Line 455  static char *ParseField(char *user, char Line 386  static char *ParseField(char *user, char
386   ary[n1 % modvalue] = 1;   ary[n1 % modvalue] = 1;
387   s0 = skip;   s0 = skip;
388   }   }
389   }   if (--failsafe == 0) {
390   while (n1 != n2 && --failsafe);   goto err;
391     }
392     } while (n1 != n2);
393    
  if (failsafe == 0) {  
  crondlog("\111failed user %s parsing %s\n", user, base);  
  return NULL;  
  }  
394   }   }
395   if (*ptr != ',') {   if (*ptr != ',') {
396   break;   break;
# Line 471  static char *ParseField(char *user, char Line 400  static char *ParseField(char *user, char
400   n2 = -1;   n2 = -1;
401   }   }
402    
403   if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {   if (*ptr) {
404   crondlog("\111failed user %s parsing %s\n", user, base);   err:
405   return NULL;   crondlog(WARN9 "user %s: parse error at %s", user, base);
406     return;
407   }   }
408    
409   while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') {   if (DebugOpt && (LogLevel <= 5)) { /* like LVL5 */
410   ++ptr;   /* can't use crondlog, it inserts '\n' */
  }  
 #if ENABLE_DEBUG_CROND_OPTION  
  if (DebugOpt) {  
411   int i;   int i;
412     for (i = 0; i < modvalue; ++i)
413   for (i = 0; i < modvalue; ++i) {   fprintf(stderr, "%d", (unsigned char)ary[i]);
414   crondlog("\005%d", ary[i]);   fputc('\n', stderr);
  }  
  crondlog("\005\n");  
415   }   }
 #endif  
   
  return ptr;  
416  }  }
417    
418  static void FixDayDow(CronLine * line)  static void FixDayDow(CronLine *line)
419  {  {
420   int i;   unsigned i;
421   int weekUsed = 0;   int weekUsed = 0;
422   int daysUsed = 0;   int daysUsed = 0;
423    
424   for (i = 0; i < (int)(arysize(line->cl_Dow)); ++i) {   for (i = 0; i < ARRAY_SIZE(line->cl_Dow); ++i) {
425   if (line->cl_Dow[i] == 0) {   if (line->cl_Dow[i] == 0) {
426   weekUsed = 1;   weekUsed = 1;
427   break;   break;
428   }   }
429   }   }
430   for (i = 0; i < (int)(arysize(line->cl_Days)); ++i) {   for (i = 0; i < ARRAY_SIZE(line->cl_Days); ++i) {
431   if (line->cl_Days[i] == 0) {   if (line->cl_Days[i] == 0) {
432   daysUsed = 1;   daysUsed = 1;
433   break;   break;
434   }   }
435   }   }
436   if (weekUsed && !daysUsed) {   if (weekUsed != daysUsed) {
437   memset(line->cl_Days, 0, sizeof(line->cl_Days));   if (weekUsed)
438   }   memset(line->cl_Days, 0, sizeof(line->cl_Days));
439   if (daysUsed && !weekUsed) {   else /* daysUsed */
440   memset(line->cl_Dow, 0, sizeof(line->cl_Dow));   memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
441   }   }
442  }  }
443    
   
   
444  static void SynchronizeFile(const char *fileName)  static void SynchronizeFile(const char *fileName)
445  {  {
446   int maxEntries = MAXLINES;   struct parser_t *parser;
447     struct stat sbuf;
448   int maxLines;   int maxLines;
449   char buf[1024];   char *tokens[6];
450    #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
451   if (strcmp(fileName, "root") == 0) {   char *mailTo = NULL;
452   maxEntries = 65535;  #endif
  }  
  maxLines = maxEntries * 10;  
   
  if (fileName) {  
  FILE *fi;  
   
  DeleteFile(fileName);  
   
  fi = fopen(fileName, "r");  
  if (fi != NULL) {  
  struct stat sbuf;  
   
  if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {  
  CronFile *file = xzalloc(sizeof(CronFile));  
  CronLine **pline;  
   
  file->cf_User = strdup(fileName);  
  pline = &file->cf_LineBase;  
453    
454   while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {   if (!fileName)
455   CronLine line;   return;
  char *ptr;  
456    
457   trim(buf);   DeleteFile(fileName);
458   if (buf[0] == 0 || buf[0] == '#') {   parser = config_open(fileName);
459   continue;   if (!parser)
460   }   return;
  if (--maxEntries == 0) {  
  break;  
  }  
  memset(&line, 0, sizeof(line));  
461    
462  #if ENABLE_DEBUG_CROND_OPTION   maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES;
  if (DebugOpt) {  
  crondlog("\111User %s Entry %s\n", fileName, buf);  
  }  
 #endif  
463    
464   /* parse date ranges */   if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
465   ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf);   CronFile *file = xzalloc(sizeof(CronFile));
466   ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr);   CronLine **pline;
467   ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr);   int n;
  ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr);  
  ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr);  
   
  /* check failure */  
  if (ptr == NULL) {  
  continue;  
  }  
468    
469   /*   file->cf_User = xstrdup(fileName);
470   * fix days and dow - if one is not * and the other   pline = &file->cf_LineBase;
  * is *, the other is set to 0, and vise-versa  
  */  
471    
472   FixDayDow(&line);   while (1) {
473     CronLine *line;
474    
475   *pline = xzalloc(sizeof(CronLine));   if (!--maxLines)
476   **pline = line;   break;
477     n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY);
478     if (!n)
479     break;
480    
481   /* copy command */   if (DebugOpt)
482   (*pline)->cl_Shell = strdup(ptr);   crondlog(LVL5 "user:%s entry:%s", fileName, parser->data);
483    
484  #if ENABLE_DEBUG_CROND_OPTION   /* check if line is setting MAILTO= */
485   if (DebugOpt) {   if (0 == strncmp(tokens[0], "MAILTO=", 7)) {
486   crondlog("\111    Command %s\n", ptr);  #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
487   }   free(mailTo);
488     mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL;
489    #endif /* otherwise just ignore such lines */
490     continue;
491     }
492     /* check if a minimum of tokens is specified */
493     if (n < 6)
494     continue;
495     *pline = line = xzalloc(sizeof(*line));
496     /* parse date ranges */
497     ParseField(file->cf_User, line->cl_Mins, 60, 0, NULL, tokens[0]);
498     ParseField(file->cf_User, line->cl_Hrs, 24, 0, NULL, tokens[1]);
499     ParseField(file->cf_User, line->cl_Days, 32, 0, NULL, tokens[2]);
500     ParseField(file->cf_User, line->cl_Mons, 12, -1, MonAry, tokens[3]);
501     ParseField(file->cf_User, line->cl_Dow, 7, 0, DowAry, tokens[4]);
502     /*
503     * fix days and dow - if one is not "*" and the other
504     * is "*", the other is set to 0, and vise-versa
505     */
506     FixDayDow(line);
507    #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
508     /* copy mailto (can be NULL) */
509     line->cl_MailTo = xstrdup(mailTo);
510  #endif  #endif
511     /* copy command */
512     line->cl_Shell = xstrdup(tokens[5]);
513     if (DebugOpt) {
514     crondlog(LVL5 " command:%s", tokens[5]);
515     }
516     pline = &line->cl_Next;
517    //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]);
518     }
519     *pline = NULL;
520    
521   pline = &((*pline)->cl_Next);   file->cf_Next = FileBase;
522   }   FileBase = file;
  *pline = NULL;  
   
  file->cf_Next = FileBase;  
  FileBase = file;  
523    
524   if (maxLines == 0 || maxEntries == 0) {   if (maxLines == 0) {
525   crondlog("\111Maximum number of lines reached for user %s\n", fileName);   crondlog(WARN9 "user %s: too many lines", fileName);
  }  
  }  
  fclose(fi);  
526   }   }
527   }   }
528     config_close(parser);
529  }  }
530    
531  static void CheckUpdates(void)  static void CheckUpdates(void)
# Line 619  static void CheckUpdates(void) Line 533  static void CheckUpdates(void)
533   FILE *fi;   FILE *fi;
534   char buf[256];   char buf[256];
535    
536   fi = fopen(CRONUPDATE, "r");   fi = fopen_for_read(CRONUPDATE);
537   if (fi != NULL) {   if (fi != NULL) {
538   remove(CRONUPDATE);   unlink(CRONUPDATE);
539   while (fgets(buf, sizeof(buf), fi) != NULL) {   while (fgets(buf, sizeof(buf), fi) != NULL) {
540     /* use first word only */
541   SynchronizeFile(strtok(buf, " \t\r\n"));   SynchronizeFile(strtok(buf, " \t\r\n"));
542   }   }
543   fclose(fi);   fclose(fi);
# Line 631  static void CheckUpdates(void) Line 546  static void CheckUpdates(void)
546    
547  static void SynchronizeDir(void)  static void SynchronizeDir(void)
548  {  {
549     CronFile *file;
550   /* Attempt to delete the database. */   /* Attempt to delete the database. */
551     again:
552   for (;;) {   for (file = FileBase; file; file = file->cf_Next) {
553   CronFile *file;   if (!file->cf_Deleted) {
554     DeleteFile(file->cf_User);
555   for (file = FileBase; file && file->cf_Deleted; file = file->cf_Next);   goto again;
  if (file == NULL) {  
  break;  
556   }   }
  DeleteFile(file->cf_User);  
557   }   }
558    
559   /*   /*
# Line 651  static void SynchronizeDir(void) Line 564  static void SynchronizeDir(void)
564   *   *
565   * scan directory and add associated users   * scan directory and add associated users
566   */   */
567     unlink(CRONUPDATE);
  remove(CRONUPDATE);  
568   if (chdir(CDir) < 0) {   if (chdir(CDir) < 0) {
569   crondlog("\311cannot find %s\n", CDir);   crondlog(DIE9 "can't chdir(%s)", CDir);
570   }   }
571   {   {
572   DIR *dir = opendir(".");   DIR *dir = opendir(".");
573   struct dirent *den;   struct dirent *den;
574    
575   if (dir) {   if (!dir)
576   while ((den = readdir(dir))) {   crondlog(DIE9 "can't chdir(%s)", "."); /* exits */
577   if (strchr(den->d_name, '.') != NULL) {   while ((den = readdir(dir)) != NULL) {
578   continue;   if (strchr(den->d_name, '.') != NULL) {
579   }   continue;
580   if (getpwnam(den->d_name)) {   }
581   SynchronizeFile(den->d_name);   if (getpwnam(den->d_name)) {
582   } else {   SynchronizeFile(den->d_name);
583   crondlog("\007ignoring %s\n", den->d_name);   } else {
584   }   crondlog(LVL7 "ignoring %s", den->d_name);
585   }   }
  closedir(dir);  
  } else {  
  crondlog("\311cannot open current dir!\n");  
586   }   }
587     closedir(dir);
588   }   }
589  }  }
590    
   
591  /*  /*
592   *  DeleteFile() - delete user database   *  DeleteFile() - delete user database
593   *   *
594   *  Note: multiple entries for same user may exist if we were unable to   *  Note: multiple entries for same user may exist if we were unable to
595   *  completely delete a database due to running processes.   *  completely delete a database due to running processes.
596   */   */
   
597  static void DeleteFile(const char *userName)  static void DeleteFile(const char *userName)
598  {  {
599   CronFile **pfile = &FileBase;   CronFile **pfile = &FileBase;
# Line 729  static void DeleteFile(const char *userN Line 637  static void DeleteFile(const char *userN
637   * period is about a minute (one scan).  Worst case it will be one   * period is about a minute (one scan).  Worst case it will be one
638   * hour (60 scans).   * hour (60 scans).
639   */   */
   
640  static int TestJobs(time_t t1, time_t t2)  static int TestJobs(time_t t1, time_t t2)
641  {  {
642   int nJobs = 0;   int nJobs = 0;
# Line 738  static int TestJobs(time_t t1, time_t t2 Line 645  static int TestJobs(time_t t1, time_t t2
645   /* Find jobs > t1 and <= t2 */   /* Find jobs > t1 and <= t2 */
646    
647   for (t = t1 - t1 % 60; t <= t2; t += 60) {   for (t = t1 - t1 % 60; t <= t2; t += 60) {
648   if (t > t1) {   struct tm *tp;
649   struct tm *tp = localtime(&t);   CronFile *file;
650   CronFile *file;   CronLine *line;
651   CronLine *line;  
652     if (t <= t1)
653     continue;
654    
655   for (file = FileBase; file; file = file->cf_Next) {   tp = localtime(&t);
656  #if ENABLE_DEBUG_CROND_OPTION   for (file = FileBase; file; file = file->cf_Next) {
657     if (DebugOpt)
658     crondlog(LVL5 "file %s:", file->cf_User);
659     if (file->cf_Deleted)
660     continue;
661     for (line = file->cf_LineBase; line; line = line->cl_Next) {
662   if (DebugOpt)   if (DebugOpt)
663   crondlog("\005FILE %s:\n", file->cf_User);   crondlog(LVL5 " line %s", line->cl_Shell);
664  #endif   if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour]
665   if (file->cf_Deleted)   && (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday])
666   continue;   && line->cl_Mons[tp->tm_mon]
667   for (line = file->cf_LineBase; line; line = line->cl_Next) {   ) {
668  #if ENABLE_DEBUG_CROND_OPTION   if (DebugOpt) {
669   if (DebugOpt)   crondlog(LVL5 " job: %d %s",
670   crondlog("\005    LINE %s\n", line->cl_Shell);   (int)line->cl_Pid, line->cl_Shell);
671  #endif   }
672   if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour] &&   if (line->cl_Pid > 0) {
673   (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday])   crondlog(LVL8 "user %s: process already running: %s",
674   && line->cl_Mons[tp->tm_mon]) {   file->cf_User, line->cl_Shell);
675  #if ENABLE_DEBUG_CROND_OPTION   } else if (line->cl_Pid == 0) {
676   if (DebugOpt) {   line->cl_Pid = -1;
677   crondlog("\005    JobToDo: %d %s\n",   file->cf_Ready = 1;
678   line->cl_Pid, line->cl_Shell);   ++nJobs;
  }  
 #endif  
  if (line->cl_Pid > 0) {  
  crondlog("\010    process already running: %s %s\n",  
  file->cf_User, line->cl_Shell);  
  } else if (line->cl_Pid == 0) {  
  line->cl_Pid = -1;  
  file->cf_Ready = 1;  
  ++nJobs;  
  }  
679   }   }
680   }   }
681   }   }
# Line 786  static void RunJobs(void) Line 690  static void RunJobs(void)
690   CronLine *line;   CronLine *line;
691    
692   for (file = FileBase; file; file = file->cf_Next) {   for (file = FileBase; file; file = file->cf_Next) {
693   if (file->cf_Ready) {   if (!file->cf_Ready)
694   file->cf_Ready = 0;   continue;
   
  for (line = file->cf_LineBase; line; line = line->cl_Next) {  
  if (line->cl_Pid < 0) {  
   
  RunJob(file->cf_User, line);  
695    
696   crondlog("\010USER %s pid %3d cmd %s\n",   file->cf_Ready = 0;
697   file->cf_User, line->cl_Pid, line->cl_Shell);   for (line = file->cf_LineBase; line; line = line->cl_Next) {
698   if (line->cl_Pid < 0) {   if (line->cl_Pid >= 0)
699   file->cf_Ready = 1;   continue;
700   }  
701   else if (line->cl_Pid > 0) {   RunJob(file->cf_User, line);
702   file->cf_Running = 1;   crondlog(LVL8 "USER %s pid %3d cmd %s",
703   }   file->cf_User, (int)line->cl_Pid, line->cl_Shell);
704   }   if (line->cl_Pid < 0) {
705     file->cf_Ready = 1;
706     } else if (line->cl_Pid > 0) {
707     file->cf_Running = 1;
708   }   }
709   }   }
710   }   }
# Line 814  static void RunJobs(void) Line 716  static void RunJobs(void)
716   * Check for job completion, return number of jobs still running after   * Check for job completion, return number of jobs still running after
717   * all done.   * all done.
718   */   */
   
719  static int CheckJobs(void)  static int CheckJobs(void)
720  {  {
721   CronFile *file;   CronFile *file;
# Line 826  static int CheckJobs(void) Line 727  static int CheckJobs(void)
727   file->cf_Running = 0;   file->cf_Running = 0;
728    
729   for (line = file->cf_LineBase; line; line = line->cl_Next) {   for (line = file->cf_LineBase; line; line = line->cl_Next) {
730   if (line->cl_Pid > 0) {   int status, r;
731   int status;   if (line->cl_Pid <= 0)
732   int r = wait4(line->cl_Pid, &status, WNOHANG, NULL);   continue;
733    
734   if (r < 0 || r == line->cl_Pid) {   r = waitpid(line->cl_Pid, &status, WNOHANG);
735   EndJob(file->cf_User, line);   if (r < 0 || r == line->cl_Pid) {
736   if (line->cl_Pid) {   EndJob(file->cf_User, line);
737   file->cf_Running = 1;   if (line->cl_Pid) {
  }  
  } else if (r == 0) {  
738   file->cf_Running = 1;   file->cf_Running = 1;
739   }   }
740     } else if (r == 0) {
741     file->cf_Running = 1;
742   }   }
743   }   }
744   }   }
# Line 846  static int CheckJobs(void) Line 747  static int CheckJobs(void)
747   return nStillRunning;   return nStillRunning;
748  }  }
749    
   
750  #if ENABLE_FEATURE_CROND_CALL_SENDMAIL  #if ENABLE_FEATURE_CROND_CALL_SENDMAIL
751    
752    // TODO: sendmail should be _run-time_ option, not compile-time!
753    
754  static void  static void
755  ForkJob(const char *user, CronLine * line, int mailFd,  ForkJob(const char *user, CronLine *line, int mailFd,
756   const char *prog, const char *cmd, const char *arg, const char *mailf)   const char *prog, const char *cmd, const char *arg,
757     const char *mail_filename)
758  {  {
759   /* Fork as the user in question and run program */   struct passwd *pas;
760   pid_t pid = fork();   pid_t pid;
761    
762   line->cl_Pid = pid;   /* prepare things before vfork */
763     pas = getpwnam(user);
764     if (!pas) {
765     crondlog(LVL9 "can't get uid for %s", user);
766     goto err;
767     }
768     SetEnv(pas);
769    
770     pid = vfork();
771   if (pid == 0) {   if (pid == 0) {
772   /* CHILD */   /* CHILD */
773     /* change running state to the user in question */
774   /* Change running state to the user in question */   ChangeUser(pas);
   
  if (ChangeUser(user) < 0) {  
  exit(0);  
  }  
 #if ENABLE_DEBUG_CROND_OPTION  
775   if (DebugOpt) {   if (DebugOpt) {
776   crondlog("\005Child Running %s\n", prog);   crondlog(LVL5 "child running %s", prog);
777   }   }
 #endif  
   
778   if (mailFd >= 0) {   if (mailFd >= 0) {
779   dup2(mailFd, mailf != NULL);   xmove_fd(mailFd, mail_filename ? 1 : 0);
780   dup2((mailf ? mailFd : 1), 2);   dup2(1, 2);
781   close(mailFd);   }
782   }   /* crond 3.0pl1-100 puts tasks in separate process groups */
783   execl(prog, prog, cmd, arg, NULL);   bb_setpgrp();
784   crondlog("\024cannot exec, user %s cmd %s %s %s\n", user, prog, cmd, arg);   execlp(prog, prog, cmd, arg, NULL);
785   if (mailf) {   crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, prog, cmd, arg);
786     if (mail_filename) {
787   fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);   fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
788   }   }
789   exit(0);   _exit(EXIT_SUCCESS);
790   } else if (pid < 0) {   }
791    
792     line->cl_Pid = pid;
793     if (pid < 0) {
794   /* FORK FAILED */   /* FORK FAILED */
795   crondlog("\024cannot fork, user %s\n", user);   crondlog(ERR20 "can't vfork");
796     err:
797   line->cl_Pid = 0;   line->cl_Pid = 0;
798   if (mailf) {   if (mail_filename) {
799   remove(mailf);   unlink(mail_filename);
800   }   }
801   } else if (mailf) {   } else if (mail_filename) {
802   /* PARENT, FORK SUCCESS   /* PARENT, FORK SUCCESS
803   * rename mail-file based on pid of process   * rename mail-file based on pid of process
804   */   */
805   char mailFile2[128];   char mailFile2[128];
806    
807   snprintf(mailFile2, sizeof(mailFile2), TMPDIR "/cron.%s.%d", user, pid);   snprintf(mailFile2, sizeof(mailFile2), "%s/cron.%s.%d", TMPDIR, user, pid);
808   rename(mailf, mailFile2);   rename(mail_filename, mailFile2); // TODO: xrename?
809   }   }
810    
811   /*   /*
812   * Close the mail file descriptor.. we can't just leave it open in   * Close the mail file descriptor.. we can't just leave it open in
813   * a structure, closing it later, because we might run out of descriptors   * a structure, closing it later, because we might run out of descriptors
814   */   */
   
815   if (mailFd >= 0) {   if (mailFd >= 0) {
816   close(mailFd);   close(mailFd);
817   }   }
818  }  }
819    
820  static void RunJob(const char *user, CronLine * line)  static void RunJob(const char *user, CronLine *line)
821  {  {
822   char mailFile[128];   char mailFile[128];
823   int mailFd;   int mailFd = -1;
824    
825   line->cl_Pid = 0;   line->cl_Pid = 0;
826   line->cl_MailFlag = 0;   line->cl_MailFlag = 0;
827    
828   /* open mail file - owner root so nobody can screw with it. */   if (line->cl_MailTo) {
829     /* open mail file - owner root so nobody can screw with it. */
830     snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid());
831     mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
832    
833   snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, getpid());   if (mailFd >= 0) {
834   mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);   line->cl_MailFlag = 1;
835     fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", line->cl_MailTo,
836   if (mailFd >= 0) {   line->cl_Shell);
837   line->cl_MailFlag = 1;   line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR);
838   fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", user,   } else {
839   line->cl_Shell);   crondlog(ERR20 "cannot create mail file %s for user %s, "
840   line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR);   "discarding output", mailFile, user);
841   } else {   }
  crondlog("\024cannot create mail file user %s file %s, output to /dev/null\n", user, mailFile);  
842   }   }
843    
844   ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);   ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
# Line 935  static void RunJob(const char *user, Cro Line 847  static void RunJob(const char *user, Cro
847  /*  /*
848   * EndJob - called when job terminates and when mail terminates   * EndJob - called when job terminates and when mail terminates
849   */   */
850    static void EndJob(const char *user, CronLine *line)
 static void EndJob(const char *user, CronLine * line)  
851  {  {
852   int mailFd;   int mailFd;
853   char mailFile[128];   char mailFile[128];
854   struct stat sbuf;   struct stat sbuf;
855    
856   /* No job */   /* No job */
   
857   if (line->cl_Pid <= 0) {   if (line->cl_Pid <= 0) {
858   line->cl_Pid = 0;   line->cl_Pid = 0;
859   return;   return;
# Line 953  static void EndJob(const char *user, Cro Line 863  static void EndJob(const char *user, Cro
863   * End of job and no mail file   * End of job and no mail file
864   * End of sendmail job   * End of sendmail job
865   */   */
866     snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, line->cl_Pid);
  snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, line->cl_Pid);  
867   line->cl_Pid = 0;   line->cl_Pid = 0;
868    
869   if (line->cl_MailFlag != 1) {   if (line->cl_MailFlag == 0) {
870   return;   return;
871   }   }
872   line->cl_MailFlag = 0;   line->cl_MailFlag = 0;
# Line 966  static void EndJob(const char *user, Cro Line 875  static void EndJob(const char *user, Cro
875   * End of primary job - check for mail file.  If size has increased and   * End of primary job - check for mail file.  If size has increased and
876   * the file is still valid, we sendmail it.   * the file is still valid, we sendmail it.
877   */   */
   
878   mailFd = open(mailFile, O_RDONLY);   mailFd = open(mailFile, O_RDONLY);
879   remove(mailFile);   unlink(mailFile);
880   if (mailFd < 0) {   if (mailFd < 0) {
881   return;   return;
882   }   }
883    
884   if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid || sbuf.st_nlink != 0 ||   if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid
885   sbuf.st_size == line->cl_MailPos || !S_ISREG(sbuf.st_mode)) {   || sbuf.st_nlink != 0 || sbuf.st_size == line->cl_MailPos
886     || !S_ISREG(sbuf.st_mode)
887     ) {
888   close(mailFd);   close(mailFd);
889   return;   return;
890   }   }
891   ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);   if (line->cl_MailTo)
892     ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
893  }  }
 #else  
 /* crond without sendmail */  
894    
895  static void RunJob(const char *user, CronLine * line)  #else /* crond without sendmail */
896    
897    static void RunJob(const char *user, CronLine *line)
898  {  {
899   /* Fork as the user in question and run program */   struct passwd *pas;
900   pid_t pid = fork();   pid_t pid;
901    
902     /* prepare things before vfork */
903     pas = getpwnam(user);
904     if (!pas) {
905     crondlog(LVL9 "can't get uid for %s", user);
906     goto err;
907     }
908     SetEnv(pas);
909    
910     /* fork as the user in question and run program */
911     pid = vfork();
912   if (pid == 0) {   if (pid == 0) {
913   /* CHILD */   /* CHILD */
914     /* change running state to the user in question */
915   /* Change running state to the user in question */   ChangeUser(pas);
   
  if (ChangeUser(user) < 0) {  
  exit(0);  
  }  
 #if ENABLE_DEBUG_CROND_OPTION  
916   if (DebugOpt) {   if (DebugOpt) {
917   crondlog("\005Child Running %s\n", DEFAULT_SHELL);   crondlog(LVL5 "child running %s", DEFAULT_SHELL);
918   }   }
919  #endif   /* crond 3.0pl1-100 puts tasks in separate process groups */
920     bb_setpgrp();
921   execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL);   execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL);
922   crondlog("\024cannot exec, user %s cmd %s -c %s\n", user,   crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user,
923   DEFAULT_SHELL, line->cl_Shell);   DEFAULT_SHELL, "-c", line->cl_Shell);
924   exit(0);   _exit(EXIT_SUCCESS);
925   } else if (pid < 0) {   }
926     if (pid < 0) {
927   /* FORK FAILED */   /* FORK FAILED */
928   crondlog("\024cannot, user %s\n", user);   crondlog(ERR20 "can't vfork");
929     err:
930   pid = 0;   pid = 0;
931   }   }
932   line->cl_Pid = pid;   line->cl_Pid = pid;
933  }  }
934    
935  #endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */  #endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */

Legend:
Removed from v.815  
changed lines
  Added in v.816