blob: 7915b860ac4ede6a76b66ecc5fe349bef63f1745 [file] [log] [blame]
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001/*
2 * crond -d[#] -c <crondir> -f -b
3 *
4 * run as root, but NOT setuid root
5 *
6 * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
7 * May be distributed under the GNU General Public License
8 *
9 * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002 to be used in busybox
10 */
11
12#define VERSION "2.3.2"
13
14#undef FEATURE_DEBUG_OPT
15
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <stdarg.h>
20#include <string.h>
21#include <errno.h>
22#include <time.h>
23#include <dirent.h>
24#include <fcntl.h>
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000025#include <unistd.h>
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000026#include <syslog.h>
27#include <signal.h>
28#include <getopt.h>
29#include <sys/ioctl.h>
30#include <sys/wait.h>
31#include <sys/stat.h>
32#include <sys/resource.h>
33
34#include "busybox.h"
35
36#define arysize(ary) (sizeof(ary)/sizeof((ary)[0]))
37
38#ifndef CRONTABS
39#define CRONTABS "/var/spool/cron/crontabs"
40#endif
41#ifndef TMPDIR
42#define TMPDIR "/var/spool/cron"
43#endif
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000044#ifndef SENDMAIL
45#define SENDMAIL "/usr/sbin/sendmail"
46#endif
47#ifndef SENDMAIL_ARGS
Eric Andersen35e643b2003-07-28 07:40:39 +000048#define SENDMAIL_ARGS "-ti", "oem"
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000049#endif
50#ifndef CRONUPDATE
51#define CRONUPDATE "cron.update"
52#endif
53#ifndef MAXLINES
54#define MAXLINES 256 /* max lines in non-root crontabs */
55#endif
56
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000057typedef struct CronFile {
58 struct CronFile *cf_Next;
59 struct CronLine *cf_LineBase;
60 char *cf_User; /* username */
61 int cf_Ready; /* bool: one or more jobs ready */
62 int cf_Running; /* bool: one or more jobs running */
63 int cf_Deleted; /* marked for deletion, ignore */
64} CronFile;
65
66typedef struct CronLine {
67 struct CronLine *cl_Next;
68 char *cl_Shell; /* shell command */
Eric Andersen35e643b2003-07-28 07:40:39 +000069 pid_t cl_Pid; /* running pid, 0, or armed (-1) */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000070 int cl_MailFlag; /* running pid is for mail */
71 int cl_MailPos; /* 'empty file' size */
72 char cl_Mins[60]; /* 0-59 */
73 char cl_Hrs[24]; /* 0-23 */
74 char cl_Days[32]; /* 1-31 */
75 char cl_Mons[12]; /* 0-11 */
76 char cl_Dow[7]; /* 0-6, beginning sunday */
77} CronLine;
78
79#define RUN_RANOUT 1
80#define RUN_RUNNING 2
81#define RUN_FAILED 3
82
83#define DaemonUid 0
84
85#ifdef FEATURE_DEBUG_OPT
86static short DebugOpt;
87#endif
88
89static short LogLevel = 8;
Eric Andersen35e643b2003-07-28 07:40:39 +000090static const char *LogFile;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000091static const char *CDir = CRONTABS;
92
Eric Andersenf6f7bfb2002-10-22 12:24:59 +000093static void startlogger(void);
94
95static void CheckUpdates(void);
96static void SynchronizeDir(void);
97static int TestJobs(time_t t1, time_t t2);
98static void RunJobs(void);
99static int CheckJobs(void);
Eric Andersen35e643b2003-07-28 07:40:39 +0000100
101static void RunJob(const char *user, CronLine *line);
102#ifdef CONFIG_FEATURE_CROND_CALL_SENDMAIL
103static void EndJob(const char *user, CronLine *line);
104#else
105#define EndJob(user, line) line->cl_Pid = 0
106#endif
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000107
108static void DeleteFile(const char *userName);
109
110static CronFile *FileBase;
111
112
Eric Andersen35e643b2003-07-28 07:40:39 +0000113static void
Eric Andersen9b476612003-07-28 09:37:56 +0000114crondlog(const char *ctl, ...)
Eric Andersen35e643b2003-07-28 07:40:39 +0000115{
116 va_list va;
117 int level = (int)(ctl[0] & 0xf);
118 int type = level == 20 ?
119 LOG_ERR : ((ctl[0] & 0100) ? LOG_WARNING : LOG_NOTICE);
120
121
122 va_start(va, ctl);
123 if (level >= LogLevel) {
124
125#ifdef FEATURE_DEBUG_OPT
126 if (DebugOpt) vfprintf(stderr, ctl, va);
127 else
128#endif
129 if (LogFile == 0) vsyslog(type, ctl, va);
130 else {
131 int logfd;
132
133 if ((logfd = open(LogFile, O_WRONLY|O_CREAT|O_APPEND, 600)) >= 0) {
134 vdprintf(logfd, ctl, va);
135 close(logfd);
136#ifdef FEATURE_DEBUG_OPT
137 } else {
138 bb_perror_msg("Can't open log file");
139#endif
140 }
141 }
142 }
143 va_end(va);
144 if(ctl[0] & 0200)
145 exit(20);
146}
147
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000148int
149crond_main(int ac, char **av)
150{
Eric Andersen8876fb22003-06-20 09:01:58 +0000151 unsigned long opt;
152 char *lopt, *Lopt, *copt;
153#ifdef FEATURE_DEBUG_OPT
154 char *dopt;
155 bb_opt_complementaly = "f-b:b-f:S-L:L-S:d-l";
156#else
157 bb_opt_complementaly = "f-b:b-f:S-L:L-S";
158#endif
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000159
160 opterr = 0; /* disable getopt 'errors' message.*/
Eric Andersen8876fb22003-06-20 09:01:58 +0000161 opt = bb_getopt_ulflags(ac, av, "l:L:fbSc:"
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000162#ifdef FEATURE_DEBUG_OPT
163 "d:"
164#endif
Eric Andersen8876fb22003-06-20 09:01:58 +0000165 , &lopt, &Lopt, &copt
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000166#ifdef FEATURE_DEBUG_OPT
Eric Andersen8876fb22003-06-20 09:01:58 +0000167 , &dopt
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000168#endif
Eric Andersen8876fb22003-06-20 09:01:58 +0000169 );
170 if(opt & 1)
171 LogLevel = atoi(lopt);
Eric Andersen35e643b2003-07-28 07:40:39 +0000172 if(opt & 2)
Eric Andersen8876fb22003-06-20 09:01:58 +0000173 if (*Lopt != 0) LogFile = Lopt;
Eric Andersen8876fb22003-06-20 09:01:58 +0000174 if(opt & 32) {
175 if (*copt != 0) CDir = copt;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000176 }
Eric Andersen8876fb22003-06-20 09:01:58 +0000177#ifdef FEATURE_DEBUG_OPT
178 if(opt & 64) {
179 DebugOpt = atoi(dopt);
180 LogLevel = 0;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000181 }
Eric Andersen8876fb22003-06-20 09:01:58 +0000182#endif
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000183
184 /*
185 * change directory
186 */
187
188 if (chdir(CDir) != 0)
Eric Andersen35e643b2003-07-28 07:40:39 +0000189 bb_perror_msg_and_die("%s", CDir);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000190
Eric Andersen8876fb22003-06-20 09:01:58 +0000191 signal(SIGHUP,SIG_IGN); /* hmm.. but, if kill -HUP original
192 * version - his died. ;(
193 */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000194 /*
195 * close stdin and stdout, stderr.
196 * close unused descriptors - don't need.
197 * optional detach from controlling terminal
198 */
199
Eric Andersen35e643b2003-07-28 07:40:39 +0000200 if (!(opt & 4)) {
201 if(daemon(1, 0) < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000202 bb_perror_msg_and_die("daemon");
Eric Andersen68d4a852003-07-28 09:31:28 +0000203 }
Eric Andersen35e643b2003-07-28 07:40:39 +0000204#if defined(__uClinux__)
Eric Andersen68d4a852003-07-28 09:31:28 +0000205 else {
Eric Andersen35e643b2003-07-28 07:40:39 +0000206 /* reexec for vfork() do continue parent */
207 vfork_daemon_rexec(ac, av, "-f");
208 }
209#endif /* uClinux */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000210 }
211
212 (void)startlogger(); /* need if syslog mode selected */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000213
214 /*
215 * main loop - synchronize to 1 second after the minute, minimum sleep
216 * of 1 second.
217 */
218
Eric Andersen9b476612003-07-28 09:37:56 +0000219 crondlog("\011%s " VERSION " dillon, started, log level %d\n", bb_applet_name,
Eric Andersen35e643b2003-07-28 07:40:39 +0000220 LogLevel);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000221
222 SynchronizeDir();
223
224 {
225 time_t t1 = time(NULL);
226 time_t t2;
227 long dt;
228 short rescan = 60;
229 short sleep_time = 60;
230
231 for (;;) {
232 sleep((sleep_time + 1) - (short)(time(NULL) % sleep_time));
233
234 t2 = time(NULL);
235 dt = t2 - t1;
236
237 /*
238 * The file 'cron.update' is checked to determine new cron
239 * jobs. The directory is rescanned once an hour to deal
240 * with any screwups.
241 *
242 * check for disparity. Disparities over an hour either way
243 * result in resynchronization. A reverse-indexed disparity
244 * less then an hour causes us to effectively sleep until we
245 * match the original time (i.e. no re-execution of jobs that
246 * have just been run). A forward-indexed disparity less then
247 * an hour causes intermediate jobs to be run, but only once
248 * in the worst case.
249 *
250 * when running jobs, the inequality used is greater but not
251 * equal to t1, and less then or equal to t2.
252 */
253
254 if (--rescan == 0) {
255 rescan = 60;
256 SynchronizeDir();
257 }
258 CheckUpdates();
259#ifdef FEATURE_DEBUG_OPT
260 if (DebugOpt)
Eric Andersen9b476612003-07-28 09:37:56 +0000261 crondlog("\005Wakeup dt=%d\n", dt);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000262#endif
263 if (dt < -60*60 || dt > 60*60) {
264 t1 = t2;
Eric Andersen9b476612003-07-28 09:37:56 +0000265 crondlog("\111time disparity of %d minutes detected\n", dt / 60);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000266 } else if (dt > 0) {
267 TestJobs(t1, t2);
268 RunJobs();
269 sleep(5);
270 if (CheckJobs() > 0)
271 sleep_time = 10;
272 else
273 sleep_time = 60;
274 t1 = t2;
275 }
276 }
277 }
278 /* not reached */
279}
280
281
Eric Andersen35e643b2003-07-28 07:40:39 +0000282#if defined(FEATURE_DEBUG_OPT) || defined(CONFIG_FEATURE_CROND_CALL_SENDMAIL)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000283/*
Eric Andersen35e643b2003-07-28 07:40:39 +0000284 write to temp file..
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000285*/
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000286static void
287fdprintf(int fd, const char *ctl, ...)
288{
289 va_list va;
290
291 va_start(va, ctl);
292 vdprintf(fd, ctl, va);
293 va_end(va);
294}
Eric Andersen35e643b2003-07-28 07:40:39 +0000295#endif
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000296
297
298static int
Eric Andersen35e643b2003-07-28 07:40:39 +0000299ChangeUser(const char *user)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000300{
301 struct passwd *pas;
Glenn L McGrath99bd5ad2003-09-03 12:18:42 +0000302 const char *err_msg;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000303
304 /*
305 * Obtain password entry and change privilages
306 */
307
308 if ((pas = getpwnam(user)) == 0) {
Eric Andersen9b476612003-07-28 09:37:56 +0000309 crondlog("\011failed to get uid for %s", user);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000310 return(-1);
311 }
312 setenv("USER", pas->pw_name, 1);
313 setenv("HOME", pas->pw_dir, 1);
Glenn L McGrathdc4e75e2003-09-02 02:36:18 +0000314 setenv("SHELL", DEFAULT_SHELL, 1);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000315
316 /*
317 * Change running state to the user in question
318 */
Glenn L McGrath99bd5ad2003-09-03 12:18:42 +0000319 err_msg = change_identity_e2str(pas);
320 if (err_msg) {
321 crondlog("\011%s for user %s", err_msg, user);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000322 return(-1);
323 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000324 if (chdir(pas->pw_dir) < 0) {
Eric Andersen9b476612003-07-28 09:37:56 +0000325 crondlog("\011chdir failed: %s: %m", pas->pw_dir);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000326 if (chdir(TMPDIR) < 0) {
Eric Andersen9b476612003-07-28 09:37:56 +0000327 crondlog("\011chdir failed: %s: %m", TMPDIR);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000328 return(-1);
329 }
330 }
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000331 return(pas->pw_uid);
332}
333
334static void
335startlogger(void)
336{
Eric Andersen35e643b2003-07-28 07:40:39 +0000337 if (LogFile == 0)
338 openlog(bb_applet_name, LOG_CONS|LOG_PID, LOG_CRON);
339#ifdef FEATURE_DEBUG_OPT
340 else { /* test logfile */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000341 int logfd;
342
Eric Andersen35e643b2003-07-28 07:40:39 +0000343 if ((logfd = open(LogFile, O_WRONLY|O_CREAT|O_APPEND, 600)) >= 0)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000344 close(logfd);
345 else
Eric Andersen35e643b2003-07-28 07:40:39 +0000346 bb_perror_msg("Failed to open log file '%s' reason", LogFile);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000347 }
Eric Andersen35e643b2003-07-28 07:40:39 +0000348#endif
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000349}
350
351
352static const char * const DowAry[] = {
353 "sun",
354 "mon",
355 "tue",
356 "wed",
357 "thu",
358 "fri",
359 "sat",
360
361 "Sun",
362 "Mon",
363 "Tue",
364 "Wed",
365 "Thu",
366 "Fri",
367 "Sat",
368 NULL
369};
370
371static const char * const MonAry[] = {
372 "jan",
373 "feb",
374 "mar",
375 "apr",
376 "may",
377 "jun",
378 "jul",
379 "aug",
380 "sep",
381 "oct",
382 "nov",
383 "dec",
384
385 "Jan",
386 "Feb",
387 "Mar",
388 "Apr",
389 "May",
390 "Jun",
391 "Jul",
392 "Aug",
393 "Sep",
394 "Oct",
395 "Nov",
396 "Dec",
397 NULL
398};
399
400static char *
401ParseField(char *user, char *ary, int modvalue, int off,
402 const char * const *names, char *ptr)
403{
404 char *base = ptr;
405 int n1 = -1;
406 int n2 = -1;
407
408 if (base == NULL)
409 return(NULL);
410
411 while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
412 int skip = 0;
413
414 /*
415 * Handle numeric digit or symbol or '*'
416 */
417
418 if (*ptr == '*') {
419 n1 = 0; /* everything will be filled */
420 n2 = modvalue - 1;
421 skip = 1;
422 ++ptr;
423 } else if (*ptr >= '0' && *ptr <= '9') {
424 if (n1 < 0)
425 n1 = strtol(ptr, &ptr, 10) + off;
426 else
427 n2 = strtol(ptr, &ptr, 10) + off;
428 skip = 1;
429 } else if (names) {
430 int i;
431
432 for (i = 0; names[i]; ++i) {
433 if (strncmp(ptr, names[i], strlen(names[i])) == 0) {
434 break;
435 }
436 }
437 if (names[i]) {
438 ptr += strlen(names[i]);
439 if (n1 < 0)
440 n1 = i;
441 else
442 n2 = i;
443 skip = 1;
444 }
445 }
446
447 /*
448 * handle optional range '-'
449 */
450
451 if (skip == 0) {
Eric Andersen9b476612003-07-28 09:37:56 +0000452 crondlog("\111failed user %s parsing %s\n", user, base);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000453 return(NULL);
454 }
455 if (*ptr == '-' && n2 < 0) {
456 ++ptr;
457 continue;
458 }
459
460 /*
461 * collapse single-value ranges, handle skipmark, and fill
462 * in the character array appropriately.
463 */
464
465 if (n2 < 0)
466 n2 = n1;
467
468 if (*ptr == '/')
469 skip = strtol(ptr + 1, &ptr, 10);
470
471 /*
472 * fill array, using a failsafe is the easiest way to prevent
473 * an endless loop
474 */
475
476 {
477 int s0 = 1;
478 int failsafe = 1024;
479
480 --n1;
481 do {
482 n1 = (n1 + 1) % modvalue;
483
484 if (--s0 == 0) {
485 ary[n1 % modvalue] = 1;
486 s0 = skip;
487 }
488 } while (n1 != n2 && --failsafe);
489
490 if (failsafe == 0) {
Eric Andersen9b476612003-07-28 09:37:56 +0000491 crondlog("\111failed user %s parsing %s\n", user, base);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000492 return(NULL);
493 }
494 }
495 if (*ptr != ',')
496 break;
497 ++ptr;
498 n1 = -1;
499 n2 = -1;
500 }
501
502 if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
Eric Andersen9b476612003-07-28 09:37:56 +0000503 crondlog("\111failed user %s parsing %s\n", user, base);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000504 return(NULL);
505 }
506
507 while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
508 ++ptr;
509
510#ifdef FEATURE_DEBUG_OPT
511 if (DebugOpt) {
512 int i;
513
514 for (i = 0; i < modvalue; ++i)
Eric Andersen9b476612003-07-28 09:37:56 +0000515 crondlog("\005%d", ary[i]);
516 crondlog("\005\n");
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000517 }
518#endif
519
520 return(ptr);
521}
522
523static void
524FixDayDow(CronLine *line)
525{
526 short i;
527 short weekUsed = 0;
528 short daysUsed = 0;
529
530 for (i = 0; i < arysize(line->cl_Dow); ++i) {
531 if (line->cl_Dow[i] == 0) {
532 weekUsed = 1;
533 break;
534 }
535 }
536 for (i = 0; i < arysize(line->cl_Days); ++i) {
537 if (line->cl_Days[i] == 0) {
538 daysUsed = 1;
539 break;
540 }
541 }
542 if (weekUsed && !daysUsed) {
543 memset(line->cl_Days, 0, sizeof(line->cl_Days));
544 }
545 if (daysUsed && !weekUsed) {
546 memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
547 }
548}
549
550
551
552static void
553SynchronizeFile(const char *fileName)
554{
555 int maxEntries = MAXLINES;
556 int maxLines;
557 char buf[1024];
558
559 if (strcmp(fileName, "root") == 0)
560 maxEntries = 65535;
561 maxLines = maxEntries * 10;
562
563 if (fileName) {
564 FILE *fi;
565
566 DeleteFile(fileName);
567
568 if ((fi = fopen(fileName, "r")) != NULL) {
569 struct stat sbuf;
570
571 if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
572 CronFile *file = calloc(1, sizeof(CronFile));
573 CronLine **pline;
574
575 file->cf_User = strdup(fileName);
576 pline = &file->cf_LineBase;
577
578 while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {
579 CronLine line;
580 char *ptr;
581
582 if (buf[0])
583 buf[strlen(buf)-1] = 0;
584
585 if (buf[0] == 0 || buf[0] == '#' || buf[0] == ' ' || buf[0] == '\t')
586 continue;
587
588 if (--maxEntries == 0)
589 break;
590
591 memset(&line, 0, sizeof(line));
592
593#ifdef FEATURE_DEBUG_OPT
594 if (DebugOpt)
Eric Andersen9b476612003-07-28 09:37:56 +0000595 crondlog("\111User %s Entry %s\n", fileName, buf);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000596#endif
597
598 /*
599 * parse date ranges
600 */
601
602 ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf);
603 ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr);
604 ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr);
605 ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr);
606 ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr);
607
608 /*
609 * check failure
610 */
611
612 if (ptr == NULL)
613 continue;
614
615 /*
616 * fix days and dow - if one is not * and the other
617 * is *, the other is set to 0, and vise-versa
618 */
619
620 FixDayDow(&line);
621
622 *pline = calloc(1, sizeof(CronLine));
623 **pline = line;
624
625 /*
626 * copy command
627 */
628
629 (*pline)->cl_Shell = strdup(ptr);
630
631#ifdef FEATURE_DEBUG_OPT
632 if (DebugOpt) {
Eric Andersen9b476612003-07-28 09:37:56 +0000633 crondlog("\111 Command %s\n", ptr);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000634 }
635#endif
636
637 pline = &((*pline)->cl_Next);
638 }
639 *pline = NULL;
640
641 file->cf_Next = FileBase;
642 FileBase = file;
643
644 if (maxLines == 0 || maxEntries == 0)
Eric Andersen9b476612003-07-28 09:37:56 +0000645 crondlog("\111Maximum number of lines reached for user %s\n", fileName);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000646 }
647 fclose(fi);
648 }
649 }
650}
651
652static void
653CheckUpdates(void)
654{
655 FILE *fi;
656 char buf[256];
657
658 if ((fi = fopen(CRONUPDATE, "r")) != NULL) {
659 remove(CRONUPDATE);
660 while (fgets(buf, sizeof(buf), fi) != NULL) {
661 SynchronizeFile(strtok(buf, " \t\r\n"));
662 }
663 fclose(fi);
664 }
665}
666
667static void
668SynchronizeDir(void)
669{
670 /*
Eric Andersen35e643b2003-07-28 07:40:39 +0000671 * Attempt to delete the database.
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000672 */
673
674 for (;;) {
675 CronFile *file;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000676
677 for (file = FileBase; file && file->cf_Deleted; file = file->cf_Next)
678 ;
679 if (file == NULL)
680 break;
Eric Andersen35e643b2003-07-28 07:40:39 +0000681 DeleteFile(file->cf_User);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000682 }
683
684 /*
685 * Remove cron update file
686 *
687 * Re-chdir, in case directory was renamed & deleted, or otherwise
688 * screwed up.
689 *
690 * scan directory and add associated users
691 */
692
693 remove(CRONUPDATE);
694 if (chdir(CDir) < 0) {
Eric Andersen9b476612003-07-28 09:37:56 +0000695 crondlog("\311unable to find %s\n", CDir);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000696 }
697 {
698 DIR *dir;
699 struct dirent *den;
700
701 if ((dir = opendir("."))) {
702 while ((den = readdir(dir))) {
703 if (strchr(den->d_name, '.') != NULL)
704 continue;
705 if (getpwnam(den->d_name))
706 SynchronizeFile(den->d_name);
707 else
Eric Andersen9b476612003-07-28 09:37:56 +0000708 crondlog("\007ignoring %s\n", den->d_name);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000709 }
710 closedir(dir);
711 } else {
Eric Andersen9b476612003-07-28 09:37:56 +0000712 crondlog("\311Unable to open current dir!\n");
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000713 }
714 }
715}
716
717
718/*
719 * DeleteFile() - delete user database
720 *
721 * Note: multiple entries for same user may exist if we were unable to
722 * completely delete a database due to running processes.
723 */
724
725static void
726DeleteFile(const char *userName)
727{
728 CronFile **pfile = &FileBase;
729 CronFile *file;
730
731 while ((file = *pfile) != NULL) {
732 if (strcmp(userName, file->cf_User) == 0) {
733 CronLine **pline = &file->cf_LineBase;
734 CronLine *line;
735
736 file->cf_Running = 0;
737 file->cf_Deleted = 1;
738
739 while ((line = *pline) != NULL) {
740 if (line->cl_Pid > 0) {
741 file->cf_Running = 1;
742 pline = &line->cl_Next;
743 } else {
744 *pline = line->cl_Next;
745 free(line->cl_Shell);
746 free(line);
747 }
748 }
749 if (file->cf_Running == 0) {
750 *pfile = file->cf_Next;
751 free(file->cf_User);
752 free(file);
753 } else {
754 pfile = &file->cf_Next;
755 }
756 } else {
757 pfile = &file->cf_Next;
758 }
759 }
760}
761
762/*
763 * TestJobs()
764 *
765 * determine which jobs need to be run. Under normal conditions, the
766 * period is about a minute (one scan). Worst case it will be one
767 * hour (60 scans).
768 */
769
770static int
771TestJobs(time_t t1, time_t t2)
772{
773 short nJobs = 0;
774 time_t t;
775
776 /*
777 * Find jobs > t1 and <= t2
778 */
779
780 for (t = t1 - t1 % 60; t <= t2; t += 60) {
781 if (t > t1) {
782 struct tm *tp = localtime(&t);
783 CronFile *file;
784 CronLine *line;
785
786 for (file = FileBase; file; file = file->cf_Next) {
787#ifdef FEATURE_DEBUG_OPT
788 if (DebugOpt)
Eric Andersen9b476612003-07-28 09:37:56 +0000789 crondlog("\005FILE %s:\n", file->cf_User);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000790#endif
791 if (file->cf_Deleted)
792 continue;
793 for (line = file->cf_LineBase; line; line = line->cl_Next) {
794#ifdef FEATURE_DEBUG_OPT
795 if (DebugOpt)
Eric Andersen9b476612003-07-28 09:37:56 +0000796 crondlog("\005 LINE %s\n", line->cl_Shell);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000797#endif
798 if (line->cl_Mins[tp->tm_min] &&
799 line->cl_Hrs[tp->tm_hour] &&
800 (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday]) &&
801 line->cl_Mons[tp->tm_mon]
802 ) {
803#ifdef FEATURE_DEBUG_OPT
804 if (DebugOpt)
Eric Andersen9b476612003-07-28 09:37:56 +0000805 crondlog("\005 JobToDo: %d %s\n", line->cl_Pid, line->cl_Shell);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000806#endif
807 if (line->cl_Pid > 0) {
Eric Andersen9b476612003-07-28 09:37:56 +0000808 crondlog("\010 process already running: %s %s\n",
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000809 file->cf_User,
810 line->cl_Shell
811 );
812 } else if (line->cl_Pid == 0) {
813 line->cl_Pid = -1;
814 file->cf_Ready = 1;
815 ++nJobs;
816 }
817 }
818 }
819 }
820 }
821 }
822 return(nJobs);
823}
824
825static void
826RunJobs(void)
827{
828 CronFile *file;
829 CronLine *line;
830
831 for (file = FileBase; file; file = file->cf_Next) {
832 if (file->cf_Ready) {
833 file->cf_Ready = 0;
834
835 for (line = file->cf_LineBase; line; line = line->cl_Next) {
836 if (line->cl_Pid < 0) {
837
Eric Andersen35e643b2003-07-28 07:40:39 +0000838 RunJob(file->cf_User, line);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000839
Eric Andersen9b476612003-07-28 09:37:56 +0000840 crondlog("\010USER %s pid %3d cmd %s\n",
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000841 file->cf_User,
842 line->cl_Pid,
843 line->cl_Shell
844 );
845 if (line->cl_Pid < 0)
846 file->cf_Ready = 1;
847 else if (line->cl_Pid > 0)
848 file->cf_Running = 1;
849 }
850 }
851 }
852 }
853}
854
855/*
856 * CheckJobs() - check for job completion
857 *
858 * Check for job completion, return number of jobs still running after
859 * all done.
860 */
861
862static int
863CheckJobs(void)
864{
865 CronFile *file;
866 CronLine *line;
867 int nStillRunning = 0;
868
869 for (file = FileBase; file; file = file->cf_Next) {
870 if (file->cf_Running) {
871 file->cf_Running = 0;
872
873 for (line = file->cf_LineBase; line; line = line->cl_Next) {
874 if (line->cl_Pid > 0) {
875 int status;
876 int r = wait4(line->cl_Pid, &status, WNOHANG, NULL);
877
878 if (r < 0 || r == line->cl_Pid) {
Eric Andersen35e643b2003-07-28 07:40:39 +0000879 EndJob(file->cf_User, line);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000880 if (line->cl_Pid)
881 file->cf_Running = 1;
882 } else if (r == 0) {
883 file->cf_Running = 1;
884 }
885 }
886 }
887 }
888 nStillRunning += file->cf_Running;
889 }
890 return(nStillRunning);
891}
892
893
Eric Andersen35e643b2003-07-28 07:40:39 +0000894#ifdef CONFIG_FEATURE_CROND_CALL_SENDMAIL
895static void
896ForkJob(const char *user, CronLine *line, int mailFd,
897 const char *prog, const char *cmd, const char *arg, const char *mailf)
898{
899 /*
900 * Fork as the user in question and run program
901 */
902 pid_t pid = fork();
903
904 line->cl_Pid = pid;
905 if (pid == 0) {
906 /*
907 * CHILD
908 */
909
910 /*
911 * Change running state to the user in question
912 */
913
914 if (ChangeUser(user) < 0)
915 exit(0);
916
917#ifdef FEATURE_DEBUG_OPT
918 if (DebugOpt)
Eric Andersen9b476612003-07-28 09:37:56 +0000919 crondlog("\005Child Running %s\n", prog);
Eric Andersen35e643b2003-07-28 07:40:39 +0000920#endif
921
922 if (mailFd >= 0) {
923 dup2(mailFd, mailf != NULL);
924 dup2((mailf ? mailFd : 1), 2);
925 close(mailFd);
926 }
927 execl(prog, prog, cmd, arg, NULL);
Eric Andersen9b476612003-07-28 09:37:56 +0000928 crondlog("\024unable to exec, user %s cmd %s %s %s\n", user,
Eric Andersen35e643b2003-07-28 07:40:39 +0000929 prog, cmd, arg);
930 if(mailf)
931 fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
932 exit(0);
933 } else if (pid < 0) {
934 /*
935 * FORK FAILED
936 */
Eric Andersen9b476612003-07-28 09:37:56 +0000937 crondlog("\024couldn't fork, user %s\n", user);
Eric Andersen35e643b2003-07-28 07:40:39 +0000938 line->cl_Pid = 0;
939 if(mailf)
940 remove(mailf);
941 } else if(mailf) {
942 /*
943 * PARENT, FORK SUCCESS
944 *
945 * rename mail-file based on pid of process
946 */
947 char mailFile2[128];
948
949 snprintf(mailFile2, sizeof(mailFile2), TMPDIR "/cron.%s.%d",
950 user, pid);
951 rename(mailf, mailFile2);
952 }
953 /*
954 * Close the mail file descriptor.. we can't just leave it open in
955 * a structure, closing it later, because we might run out of descriptors
956 */
957
958 if (mailFd >= 0)
959 close(mailFd);
960}
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000961
962static void
Eric Andersen35e643b2003-07-28 07:40:39 +0000963RunJob(const char *user, CronLine *line)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000964{
965 char mailFile[128];
966 int mailFd;
967
968 line->cl_Pid = 0;
969 line->cl_MailFlag = 0;
970
971 /*
972 * open mail file - owner root so nobody can screw with it.
973 */
974
975 snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d",
Eric Andersen35e643b2003-07-28 07:40:39 +0000976 user, getpid());
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000977 mailFd = open(mailFile, O_CREAT|O_TRUNC|O_WRONLY|O_EXCL|O_APPEND, 0600);
978
979 if (mailFd >= 0) {
980 line->cl_MailFlag = 1;
Eric Andersen35e643b2003-07-28 07:40:39 +0000981 fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", user,
982 line->cl_Shell);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000983 line->cl_MailPos = lseek(mailFd, 0, 1);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000984 } else {
Eric Andersen9b476612003-07-28 09:37:56 +0000985 crondlog("\024unable to create mail file user %s file %s, output to /dev/null\n",
Eric Andersen35e643b2003-07-28 07:40:39 +0000986 user, mailFile);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000987 }
988
Glenn L McGrathdc4e75e2003-09-02 02:36:18 +0000989 ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000990}
991
992/*
993 * EndJob - called when job terminates and when mail terminates
994 */
995
996static void
Eric Andersen35e643b2003-07-28 07:40:39 +0000997EndJob(const char *user, CronLine *line)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +0000998{
999 int mailFd;
1000 char mailFile[128];
1001 struct stat sbuf;
1002
1003 /*
1004 * No job
1005 */
1006
1007 if (line->cl_Pid <= 0) {
1008 line->cl_Pid = 0;
1009 return;
1010 }
1011
1012 /*
1013 * End of job and no mail file
1014 * End of sendmail job
1015 */
1016
1017 snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d",
Eric Andersen35e643b2003-07-28 07:40:39 +00001018 user, line->cl_Pid);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001019 line->cl_Pid = 0;
1020
1021 if (line->cl_MailFlag != 1)
1022 return;
1023
1024 line->cl_MailFlag = 0;
1025
1026 /*
1027 * End of primary job - check for mail file. If size has increased and
1028 * the file is still valid, we sendmail it.
1029 */
1030
1031 mailFd = open(mailFile, O_RDONLY);
1032 remove(mailFile);
1033 if (mailFd < 0) {
1034 return;
1035 }
1036
1037 if (fstat(mailFd, &sbuf) < 0 ||
1038 sbuf.st_uid != DaemonUid ||
1039 sbuf.st_nlink != 0 ||
1040 sbuf.st_size == line->cl_MailPos ||
1041 !S_ISREG(sbuf.st_mode)
1042 ) {
1043 close(mailFd);
1044 return;
1045 }
Eric Andersen35e643b2003-07-28 07:40:39 +00001046 ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
1047}
1048#else
1049/* crond whithout sendmail */
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001050
Eric Andersen35e643b2003-07-28 07:40:39 +00001051static void
1052RunJob(const char *user, CronLine *line)
1053{
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001054 /*
Eric Andersen35e643b2003-07-28 07:40:39 +00001055 * Fork as the user in question and run program
1056 */
1057 pid_t pid = fork();
1058
1059 if (pid == 0) {
1060 /*
1061 * CHILD
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001062 */
1063
1064 /*
Eric Andersen35e643b2003-07-28 07:40:39 +00001065 * Change running state to the user in question
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001066 */
1067
Eric Andersen35e643b2003-07-28 07:40:39 +00001068 if (ChangeUser(user) < 0)
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001069 exit(0);
1070
Eric Andersen35e643b2003-07-28 07:40:39 +00001071#ifdef FEATURE_DEBUG_OPT
1072 if (DebugOpt)
Glenn L McGrathdc4e75e2003-09-02 02:36:18 +00001073 crondlog("\005Child Running %s\n", DEFAULT_SHELL);
Eric Andersen35e643b2003-07-28 07:40:39 +00001074#endif
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001075
Glenn L McGrathdc4e75e2003-09-02 02:36:18 +00001076 execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL);
Eric Andersen9b476612003-07-28 09:37:56 +00001077 crondlog("\024unable to exec, user %s cmd %s -c %s\n", user,
Glenn L McGrathdc4e75e2003-09-02 02:36:18 +00001078 DEFAULT_SHELL, line->cl_Shell);
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001079 exit(0);
Eric Andersen35e643b2003-07-28 07:40:39 +00001080 } else if (pid < 0) {
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001081 /*
Eric Andersen35e643b2003-07-28 07:40:39 +00001082 * FORK FAILED
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001083 */
Eric Andersen9b476612003-07-28 09:37:56 +00001084 crondlog("\024couldn't fork, user %s\n", user);
Eric Andersen35e643b2003-07-28 07:40:39 +00001085 pid = 0;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001086 }
Eric Andersen35e643b2003-07-28 07:40:39 +00001087 line->cl_Pid = pid;
Eric Andersenf6f7bfb2002-10-22 12:24:59 +00001088}
Eric Andersen35e643b2003-07-28 07:40:39 +00001089#endif /* CONFIG_FEATURE_CROND_CALL_SENDMAIL */