This source file includes following definitions.
- reset_watches
- set_cron_unwatched
- set_cron_watched
- handle_signals
- usage
- main
- run_reboot_jobs
- find_jobs
- set_time
- cron_sleep
- sighup_handler
- sigchld_handler
- sigintterm_handler
- sigchld_reaper
- parse_args
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 #include "config.h"
28
29 #define MAIN_PROGRAM
30
31 #include <errno.h>
32 #include <langinfo.h>
33 #include <locale.h>
34 #include <pwd.h>
35 #include <signal.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <unistd.h>
42 #include <sys/time.h>
43
44 #ifdef WITH_INOTIFY
45 # include <sys/inotify.h>
46 #endif
47
48 #ifdef HAVE_SYS_FCNTL_H
49 # include <sys/fcntl.h>
50 #endif
51
52 #include "cronie_common.h"
53 #include "funcs.h"
54 #include "globals.h"
55 #include "pathnames.h"
56
57 #if defined WITH_INOTIFY
58 int inotify_enabled;
59 #else
60 # define inotify_enabled 0
61 #endif
62
63 enum timejump { negative, small, medium, large };
64
65 static void usage(void) ATTRIBUTE_NORETURN,
66 run_reboot_jobs(cron_db *),
67 find_jobs(int, cron_db *, int, int, long),
68 set_time(int),
69 cron_sleep(int, cron_db *),
70 sigchld_handler(int),
71 sighup_handler(int),
72 sigchld_reaper(void), sigintterm_handler(int), parse_args(int c, char *v[]);
73
74 static volatile sig_atomic_t got_sighup, got_sigchld, got_sigintterm;
75 static int timeRunning, virtualTime, clockTime;
76 static long GMToff;
77 static int DisableInotify;
78
79 #if defined WITH_INOTIFY
80
81
82
83
84
85
86
87
88 # if defined ENABLE_SYSCRONTAB
89 # define NUM_WATCHES 3
90
91 int wd[NUM_WATCHES];
92 const char *watchpaths[NUM_WATCHES] = {SPOOL_DIR, SYS_CROND_DIR, SYSCRONTAB};
93 # else
94 # define NUM_WATCHES 2
95 int wd[NUM_WATCHES];
96 const char *watchpaths[NUM_WATCHES] = {SPOOL_DIR, SYS_CROND_DIR};
97 # endif
98
99 static void reset_watches(void) {
100 size_t i;
101
102 for (i = 0; i < sizeof (wd) / sizeof (wd[0]); ++i) {
103 wd[i] = -2;
104 }
105 }
106
107 void set_cron_unwatched(int fd) {
108 size_t i;
109
110 for (i = 0; i < sizeof (wd) / sizeof (wd[0]); ++i) {
111 if (wd[i] > 0) {
112 inotify_rm_watch(fd, wd[i]);
113 wd[i] = -1;
114 }
115 }
116 }
117
118 void set_cron_watched(int fd) {
119 pid_t pid = getpid();
120 size_t i;
121
122 if (fd < 0) {
123 inotify_enabled = 0;
124 return;
125 }
126
127 for (i = 0; i < sizeof (wd) / sizeof (wd[0]); ++i) {
128 int w;
129
130 w = inotify_add_watch(fd, watchpaths[i],
131 IN_CREATE | IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO |
132 IN_MOVED_FROM | IN_MOVE_SELF | IN_DELETE | IN_DELETE_SELF);
133 if (w < 0 && errno != ENOENT) {
134 if (wd[i] != -1) {
135 log_it("CRON", pid, "This directory or file can't be watched",
136 watchpaths[i], errno);
137 log_it("CRON", pid, "INFO", "running without inotify support",
138 0);
139 }
140 inotify_enabled = 0;
141 set_cron_unwatched(fd);
142 return;
143 }
144 wd[i] = w;
145 }
146
147 if (!inotify_enabled) {
148 log_it("CRON", pid, "INFO", "running with inotify support", 0);
149 }
150
151 inotify_enabled = 1;
152 }
153 #endif
154
155 static void handle_signals(cron_db * database) {
156 if (got_sighup) {
157 got_sighup = 0;
158 #if defined WITH_INOTIFY
159
160 if (inotify_enabled && (EnableClustering != 1)) {
161 set_cron_unwatched(database->ifd);
162 reset_watches();
163 inotify_enabled = 0;
164 }
165 #endif
166 database->mtime = (time_t) 0;
167 log_close();
168 }
169
170 if (got_sigchld) {
171 got_sigchld = 0;
172 sigchld_reaper();
173 }
174 }
175
176 static void usage(void) {
177 const char **dflags;
178
179 fprintf(stderr, "Usage:\n");
180 fprintf(stderr, " %s [options]\n", ProgramName);
181 fprintf(stderr, "\n");
182 fprintf(stderr, "Options:\n");
183 fprintf(stderr, " -h print this message \n");
184 fprintf(stderr, " -i deamon runs without inotify support\n");
185 fprintf(stderr, " -m <comm> off, or specify preferred client for sending mails\n");
186 fprintf(stderr, " -n run in foreground\n");
187 fprintf(stderr, " -p permit any crontab\n");
188 fprintf(stderr, " -P use PATH=\"%s\"\n", _PATH_DEFPATH);
189 fprintf(stderr, " -c enable clustering support\n");
190 fprintf(stderr, " -s log into syslog instead of sending mails\n");
191 fprintf(stderr, " -V print version and exit\n");
192 fprintf(stderr, " -x <flag> print debug information\n");
193 fprintf(stderr, "\n");
194 fprintf(stderr, "Debugging flags are: ");
195 for (dflags = DebugFlagNames; *dflags; dflags++)
196 fprintf(stderr, "%s%s", *dflags, dflags[1] ? "," : "\n");
197 exit(ERROR_EXIT);
198 }
199
200 int main(int argc, char *argv[]) {
201 struct sigaction sact;
202 cron_db database;
203 int fd;
204 char *cs;
205 pid_t pid = getpid();
206 long oldGMToff;
207 struct timeval tv;
208 struct timezone tz;
209 char buf[256];
210
211 if ((ProgramName=strrchr(argv[0], '/')) == NULL) {
212 ProgramName = argv[0];
213 }
214 else {
215 ++ProgramName;
216 }
217
218 MailCmd[0] = '\0';
219 cron_default_mail_charset[0] = '\0';
220
221 setlocale(LC_ALL, "");
222
223 #if defined(BSD)
224 setlinebuf(stdout);
225 setlinebuf(stderr);
226 #endif
227
228 SyslogOutput = 0;
229 NoFork = 0;
230 ChangePath = 1;
231 parse_args(argc, argv);
232
233 memset((char *) &sact, 0, sizeof sact);
234 sigemptyset(&sact.sa_mask);
235 sact.sa_flags = 0;
236 #ifdef SA_RESTART
237 sact.sa_flags |= SA_RESTART;
238 #endif
239 sact.sa_handler = sigchld_handler;
240 (void) sigaction(SIGCHLD, &sact, NULL);
241 sact.sa_handler = sighup_handler;
242 (void) sigaction(SIGHUP, &sact, NULL);
243 sact.sa_handler = sigintterm_handler;
244 (void) sigaction(SIGINT, &sact, NULL);
245 (void) sigaction(SIGTERM, &sact, NULL);
246
247 acquire_daemonlock(0);
248 set_cron_uid();
249 check_spool_dir();
250
251 if (ChangePath) {
252 if (setenv("PATH", _PATH_DEFPATH, 1) < 0) {
253 log_it("CRON", pid, "DEATH", "can't setenv PATH",
254 errno);
255 exit(1);
256 }
257 }
258
259
260
261
262 setlocale(LC_ALL, "");
263
264 if ((cs = nl_langinfo(CODESET)) != 0L)
265 strncpy(cron_default_mail_charset, cs, MAX_ENVSTR-1);
266 else
267 strcpy(cron_default_mail_charset, "US-ASCII");
268
269
270
271 if (DebugFlags) {
272 #if DEBUGGING
273 (void) fprintf(stderr, "[%ld] cron started\n", (long) getpid());
274 #endif
275 }
276 else if (NoFork == 0) {
277 switch (fork()) {
278 case -1:
279 log_it("CRON", pid, "DEATH", "can't fork", errno);
280 exit(0);
281 break;
282 case 0:
283
284 (void) setsid();
285 if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) >= 0) {
286 (void) dup2(fd, STDIN);
287 (void) dup2(fd, STDOUT);
288 (void) dup2(fd, STDERR);
289 if (fd != STDERR)
290 (void) close(fd);
291 }
292 break;
293 default:
294
295 _exit(0);
296 }
297 }
298
299 log_it("CRON", getpid(), "STARTUP", PACKAGE_VERSION, 0);
300
301 if (!SyslogOutput && MailCmd[0] == '\0' && access("/usr/sbin/sendmail", X_OK) != 0) {
302 SyslogOutput=1;
303 log_it("CRON", pid, "INFO","Syslog will be used instead of sendmail.", 0);
304 }
305
306 pid = getpid();
307
308
309 if (gettimeofday(&tv, &tz) != 0)
310 tv.tv_usec = 0;
311 srandom((unsigned int)(pid + tv.tv_usec));
312 RandomScale = (double)random() / (double)RAND_MAX;
313 snprintf(buf, sizeof(buf), "RANDOM_DELAY will be scaled with factor %d%% if used.", (int)(RandomScale*100));
314 log_it("CRON", pid, "INFO", buf, 0);
315
316 acquire_daemonlock(0);
317 database.head = NULL;
318 database.tail = NULL;
319 database.mtime = (time_t) 0;
320
321 load_database(&database);
322
323 fd = -1;
324 #if defined WITH_INOTIFY
325 if (DisableInotify || EnableClustering) {
326 log_it("CRON", getpid(), "No inotify - daemon runs with -i or -c option",
327 "", 0);
328 }
329 else {
330 reset_watches();
331 database.ifd = fd = inotify_init();
332 fcntl(fd, F_SETFD, FD_CLOEXEC);
333 if (fd < 0)
334 log_it("CRON", pid, "INFO", "Inotify init failed", errno);
335 set_cron_watched(fd);
336 }
337 #endif
338
339 set_time(TRUE);
340 run_reboot_jobs(&database);
341 timeRunning = virtualTime = clockTime;
342 oldGMToff = GMToff;
343
344
345
346
347
348
349
350
351
352
353
354 while (!got_sigintterm) {
355 int timeDiff;
356 enum timejump wakeupKind;
357
358
359 do {
360 cron_sleep(timeRunning + 1, &database);
361 set_time(FALSE);
362 } while (!got_sigintterm && clockTime == timeRunning);
363 if (got_sigintterm)
364 break;
365 timeRunning = clockTime;
366
367
368
369
370
371 timeDiff = timeRunning - virtualTime;
372 check_orphans(&database);
373 #if defined WITH_INOTIFY
374 if (inotify_enabled) {
375 check_inotify_database(&database);
376 }
377 else {
378 if (load_database(&database) && (EnableClustering != 1))
379
380 set_cron_watched(fd);
381 }
382 #else
383 load_database(&database);
384 #endif
385
386
387 if (timeDiff == 1) {
388 virtualTime = timeRunning;
389 oldGMToff = GMToff;
390 find_jobs(virtualTime, &database, TRUE, TRUE, oldGMToff);
391 }
392 else {
393 if (timeDiff > (3 * MINUTE_COUNT) || timeDiff < -(3 * MINUTE_COUNT))
394 wakeupKind = large;
395 else if (timeDiff > 5)
396 wakeupKind = medium;
397 else if (timeDiff > 0)
398 wakeupKind = small;
399 else
400 wakeupKind = negative;
401
402 switch (wakeupKind) {
403 case small:
404
405
406
407
408
409 Debug(DSCH, ("[%ld], normal case %d minutes to go\n",
410 (long) pid, timeDiff));
411 do {
412 if (job_runqueue())
413 sleep(10);
414 virtualTime++;
415 if (virtualTime >= timeRunning)
416
417 oldGMToff = GMToff;
418 find_jobs(virtualTime, &database, TRUE, TRUE, oldGMToff);
419 } while (virtualTime < timeRunning);
420 break;
421
422 case medium:
423
424
425
426
427
428
429
430
431
432
433
434 Debug(DSCH, ("[%ld], DST begins %d minutes to go\n",
435 (long) pid, timeDiff));
436
437 find_jobs(timeRunning, &database, TRUE, FALSE, GMToff);
438
439
440 do {
441 if (job_runqueue())
442 sleep(10);
443 virtualTime++;
444 if (virtualTime >= timeRunning)
445
446 oldGMToff = GMToff;
447 find_jobs(virtualTime, &database, FALSE, TRUE, oldGMToff);
448 set_time(FALSE);
449 } while (virtualTime < timeRunning && clockTime == timeRunning);
450 break;
451
452 case negative:
453
454
455
456
457
458
459
460
461 Debug(DSCH, ("[%ld], DST ends %d minutes to go\n",
462 (long) pid, timeDiff));
463 find_jobs(timeRunning, &database, TRUE, FALSE, GMToff);
464 break;
465 default:
466
467
468
469
470 Debug(DSCH, ("[%ld], clock jumped\n", (long) pid));
471 virtualTime = timeRunning;
472 oldGMToff = GMToff;
473 find_jobs(timeRunning, &database, TRUE, TRUE, GMToff);
474 }
475 }
476
477
478 job_runqueue();
479
480 handle_signals(&database);
481 }
482
483 #if defined WITH_INOTIFY
484 if (inotify_enabled && (EnableClustering != 1))
485 set_cron_unwatched(fd);
486
487 if (fd >= 0 && close(fd) < 0)
488 log_it("CRON", pid, "INFO", "Inotify close failed", errno);
489 #endif
490
491 log_it("CRON", pid, "INFO", "Shutting down", 0);
492
493 (void) unlink(_PATH_CRON_PID);
494
495 return 0;
496 }
497
498 static void run_reboot_jobs(cron_db * db) {
499 user *u;
500 entry *e;
501 int reboot;
502 pid_t pid = getpid();
503
504
505 if (access(REBOOT_LOCK, F_OK) == 0) {
506 log_it("CRON", pid, "INFO",
507 "@reboot jobs will be run at computer's startup.", 0);
508 return;
509 }
510
511 if ((reboot = creat(REBOOT_LOCK, S_IRUSR & S_IWUSR)) < 0)
512 log_it("CRON", pid, "INFO", "Can't create lock for reboot jobs.",
513 errno);
514 else
515 close(reboot);
516
517 for (u = db->head; u != NULL; u = u->next) {
518 for (e = u->crontab; e != NULL; e = e->next) {
519 if (e->flags & WHEN_REBOOT)
520 job_add(e, u);
521 }
522 }
523 (void) job_runqueue();
524 }
525
526 static void find_jobs(int vtime, cron_db * db, int doWild, int doNonWild, long vGMToff) {
527 char *orig_tz, *job_tz;
528 struct tm *tm;
529 int minute, hour, dom, month, dow;
530 user *u;
531 entry *e;
532
533
534
535
536
537
538
539
540 #define maketime(tz1, tz2) do { \
541 char *t = tz1; \
542 if (t != NULL && *t != '\0') { \
543 setenv("TZ", t, 1); \
544 tm = localtime(&virtualGMTSecond); \
545 } else { if ((tz2) != NULL) \
546 setenv("TZ", (tz2), 1); \
547 else \
548 unsetenv("TZ"); \
549 tm = gmtime(&virtualSecond); \
550 } \
551 minute = tm->tm_min -FIRST_MINUTE; \
552 hour = tm->tm_hour -FIRST_HOUR; \
553 dom = tm->tm_mday -FIRST_DOM; \
554 month = tm->tm_mon +1 -FIRST_MONTH; \
555 dow = tm->tm_wday -FIRST_DOW; \
556 } while (0)
557
558 orig_tz = getenv("TZ");
559
560
561
562
563
564
565
566 for (u = db->head; u != NULL; u = u->next) {
567 for (e = u->crontab; e != NULL; e = e->next) {
568 time_t virtualSecond = (time_t)(vtime - e->delay) * (time_t)SECONDS_PER_MINUTE;
569 time_t virtualGMTSecond = virtualSecond - vGMToff;
570 job_tz = env_get("CRON_TZ", e->envp);
571 maketime(job_tz, orig_tz);
572
573
574 if (bit_test(e->minute, minute) &&
575 bit_test(e->hour, hour) &&
576 bit_test(e->month, month) &&
577 (((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
578 ? (bit_test(e->dow, dow) && bit_test(e->dom, dom))
579 : (bit_test(e->dow, dow) || bit_test(e->dom, dom))
580 )
581 ) {
582 if (job_tz != NULL && vGMToff != GMToff)
583
584
585
586 continue;
587
588 if ((doNonWild &&
589 !(e->flags & (MIN_STAR | HR_STAR))) ||
590 (doWild && (e->flags & (MIN_STAR | HR_STAR))))
591 job_add(e, u);
592 }
593 }
594 }
595 if (orig_tz != NULL)
596 setenv("TZ", orig_tz, 1);
597 else
598 unsetenv("TZ");
599 }
600
601
602
603
604
605
606 static void set_time(int initialize) {
607 struct tm tm;
608 static int isdst;
609
610 StartTime = time(NULL);
611
612
613 tm = *localtime(&StartTime);
614 if (initialize || tm.tm_isdst != isdst) {
615 isdst = tm.tm_isdst;
616 GMToff = get_gmtoff(&StartTime, &tm);
617 Debug(DSCH, ("[%ld] GMToff=%ld\n", (long) getpid(), (long) GMToff));
618 }
619 clockTime = (int)((StartTime + GMToff) / (time_t) SECONDS_PER_MINUTE);
620 }
621
622
623
624
625 static void cron_sleep(int target, cron_db * db) {
626 time_t t1, t2;
627 int seconds_to_wait;
628
629 t1 = time(NULL) + GMToff;
630 seconds_to_wait = (int) (target * SECONDS_PER_MINUTE - t1) + 1;
631 Debug(DSCH, ("[%ld] Target time=%ld, sec-to-wait=%d\n",
632 (long) getpid(), (long) target * SECONDS_PER_MINUTE,
633 seconds_to_wait));
634
635 while (seconds_to_wait > 0 && seconds_to_wait < 65) {
636 sleep((unsigned int) seconds_to_wait);
637
638 if (got_sigintterm)
639 return;
640
641
642
643
644
645
646 handle_signals(db);
647
648 t2 = time(NULL) + GMToff;
649 seconds_to_wait -= (int) (t2 - t1);
650 t1 = t2;
651 }
652 }
653
654 static void sighup_handler(int x) {
655 got_sighup = 1;
656 }
657
658 static void sigchld_handler(int x) {
659 got_sigchld = 1;
660 }
661
662 static void sigintterm_handler(int x) {
663 got_sigintterm = 1;
664 }
665
666 static void sigchld_reaper(void) {
667 WAIT_T waiter;
668 PID_T pid;
669
670 do {
671 pid = waitpid(-1, &waiter, WNOHANG);
672 switch (pid) {
673 case -1:
674 if (errno == EINTR)
675 continue;
676 Debug(DPROC, ("[%ld] sigchld...no children\n", (long) getpid()));
677 break;
678 case 0:
679 Debug(DPROC, ("[%ld] sigchld...no dead kids\n", (long) getpid()));
680 break;
681 default:
682 Debug(DPROC,
683 ("[%ld] sigchld...pid #%ld died, stat=%d\n",
684 (long) getpid(), (long) pid, WEXITSTATUS(waiter)));
685 break;
686 }
687 } while (pid > 0);
688 }
689
690 static void parse_args(int argc, char *argv[]) {
691 int argch;
692
693 while (-1 != (argch = getopt(argc, argv, "hnpsiPx:m:cV"))) {
694 switch (argch) {
695 case 'x':
696 if (!set_debug_flags(optarg))
697 usage();
698 break;
699 case 'n':
700 NoFork = 1;
701 break;
702 case 'p':
703 PermitAnyCrontab = 1;
704 break;
705 case 's':
706 SyslogOutput = 1;
707 break;
708 case 'i':
709 DisableInotify = 1;
710 break;
711 case 'P':
712 ChangePath = 0;
713 break;
714 case 'm':
715 strncpy(MailCmd, optarg, MAX_COMMAND);
716 break;
717 case 'c':
718 EnableClustering = 1;
719 break;
720 case 'V':
721 puts(PACKAGE_STRING);
722 exit(EXIT_SUCCESS);
723 case 'h':
724 default:
725 usage();
726 break;
727 }
728 }
729 }