source: MondoRescue/branches/stable/mindi-busybox/miscutils/crond.c

Last change on this file was 1770, checked in by Bruno Cornec, 16 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File size: 20.1 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * crond -d[#] -c <crondir> -f -b
4 *
5 * run as root, but NOT setuid root
6 *
7 * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
8 * (version 2.3.2)
9 * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
10 *
11 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
12 */
13
14#include <sys/syslog.h>
15#include "libbb.h"
16
17#ifndef CRONTABS
18#define CRONTABS "/var/spool/cron/crontabs"
19#endif
20#ifndef TMPDIR
21#define TMPDIR "/var/spool/cron"
22#endif
23#ifndef SENDMAIL
24#define SENDMAIL "sendmail"
25#endif
26#ifndef SENDMAIL_ARGS
27#define SENDMAIL_ARGS "-ti", "oem"
28#endif
29#ifndef CRONUPDATE
30#define CRONUPDATE "cron.update"
31#endif
32#ifndef MAXLINES
33#define MAXLINES 256 /* max lines in non-root crontabs */
34#endif
35
36typedef struct CronFile {
37 struct CronFile *cf_Next;
38 struct CronLine *cf_LineBase;
39 char *cf_User; /* username */
40 int cf_Ready; /* bool: one or more jobs ready */
41 int cf_Running; /* bool: one or more jobs running */
42 int cf_Deleted; /* marked for deletion, ignore */
43} CronFile;
44
45typedef struct CronLine {
46 struct CronLine *cl_Next;
47 char *cl_Shell; /* shell command */
48 pid_t cl_Pid; /* running pid, 0, or armed (-1) */
49 int cl_MailFlag; /* running pid is for mail */
50 int cl_MailPos; /* 'empty file' size */
51 char cl_Mins[60]; /* 0-59 */
52 char cl_Hrs[24]; /* 0-23 */
53 char cl_Days[32]; /* 1-31 */
54 char cl_Mons[12]; /* 0-11 */
55 char cl_Dow[7]; /* 0-6, beginning sunday */
56} CronLine;
57
58#define RUN_RANOUT 1
59#define RUN_RUNNING 2
60#define RUN_FAILED 3
61
62#define DaemonUid 0
63
64#if ENABLE_DEBUG_CROND_OPTION
65static unsigned DebugOpt;
66#endif
67
68static unsigned LogLevel = 8;
69static const char *LogFile;
70static const char *CDir = CRONTABS;
71
72static void startlogger(void);
73
74static void CheckUpdates(void);
75static void SynchronizeDir(void);
76static int TestJobs(time_t t1, time_t t2);
77static void RunJobs(void);
78static int CheckJobs(void);
79
80static void RunJob(const char *user, CronLine * line);
81
82#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
83static void EndJob(const char *user, CronLine * line);
84#else
85#define EndJob(user, line) line->cl_Pid = 0
86#endif
87
88static void DeleteFile(const char *userName);
89
90static CronFile *FileBase;
91
92
93static void crondlog(const char *ctl, ...)
94{
95 va_list va;
96 const char *fmt;
97 int level = (int) (ctl[0] & 0xf);
98 int type = level == 20 ?
99 LOG_ERR : ((ctl[0] & 0100) ? LOG_WARNING : LOG_NOTICE);
100
101
102 va_start(va, ctl);
103 fmt = ctl + 1;
104 if (level >= LogLevel) {
105
106#if ENABLE_DEBUG_CROND_OPTION
107 if (DebugOpt) {
108 vfprintf(stderr, fmt, va);
109 } else
110#endif
111 if (LogFile == 0) {
112 vsyslog(type, fmt, va);
113 } else {
114#if !ENABLE_DEBUG_CROND_OPTION
115 int logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
116#else
117 int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
118#endif
119 if (logfd >= 0) {
120 vdprintf(logfd, fmt, va);
121 close(logfd);
122 }
123 }
124 }
125 va_end(va);
126 if (ctl[0] & 0200) {
127 exit(20);
128 }
129}
130
131int crond_main(int ac, char **av);
132int crond_main(int ac, char **av)
133{
134 unsigned opt;
135 char *lopt, *Lopt, *copt;
136 USE_DEBUG_CROND_OPTION(char *dopt;)
137
138 opt_complementary = "f-b:b-f:S-L:L-S" USE_DEBUG_CROND_OPTION(":d-l");
139 opterr = 0; /* disable getopt 'errors' message. */
140 opt = getopt32(av, "l:L:fbSc:" USE_DEBUG_CROND_OPTION("d:"),
141 &lopt, &Lopt, &copt USE_DEBUG_CROND_OPTION(, &dopt));
142 if (opt & 1) /* -l */
143 LogLevel = xatou(lopt);
144 if (opt & 2) /* -L */
145 if (*Lopt)
146 LogFile = Lopt;
147 if (opt & 32) /* -c */
148 if (*copt)
149 CDir = copt;
150#if ENABLE_DEBUG_CROND_OPTION
151 if (opt & 64) { /* -d */
152 DebugOpt = xatou(dopt);
153 LogLevel = 0;
154 }
155#endif
156
157 /* close stdin and stdout, stderr.
158 * close unused descriptors - don't need.
159 * optional detach from controlling terminal
160 */
161 if (!(opt & 4))
162 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, av);
163
164 xchdir(CDir);
165 signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
166
167 startlogger(); /* need if syslog mode selected */
168
169 /*
170 * main loop - synchronize to 1 second after the minute, minimum sleep
171 * of 1 second.
172 */
173 crondlog("\011%s " BB_VER " started, log level %d\n",
174 applet_name, LogLevel);
175
176 SynchronizeDir();
177
178 {
179 time_t t1 = time(NULL);
180 time_t t2;
181 long dt;
182 int rescan = 60;
183 short sleep_time = 60;
184
185 write_pidfile("/var/run/crond.pid");
186 for (;;) {
187 sleep((sleep_time + 1) - (short) (time(NULL) % sleep_time));
188
189 t2 = time(NULL);
190 dt = t2 - t1;
191
192 /*
193 * The file 'cron.update' is checked to determine new cron
194 * jobs. The directory is rescanned once an hour to deal
195 * with any screwups.
196 *
197 * check for disparity. Disparities over an hour either way
198 * result in resynchronization. A reverse-indexed disparity
199 * less then an hour causes us to effectively sleep until we
200 * match the original time (i.e. no re-execution of jobs that
201 * have just been run). A forward-indexed disparity less then
202 * an hour causes intermediate jobs to be run, but only once
203 * in the worst case.
204 *
205 * when running jobs, the inequality used is greater but not
206 * equal to t1, and less then or equal to t2.
207 */
208
209 if (--rescan == 0) {
210 rescan = 60;
211 SynchronizeDir();
212 }
213 CheckUpdates();
214#if ENABLE_DEBUG_CROND_OPTION
215 if (DebugOpt)
216 crondlog("\005Wakeup dt=%d\n", dt);
217#endif
218 if (dt < -60 * 60 || dt > 60 * 60) {
219 t1 = t2;
220 crondlog("\111time disparity of %d minutes detected\n", dt / 60);
221 } else if (dt > 0) {
222 TestJobs(t1, t2);
223 RunJobs();
224 sleep(5);
225 if (CheckJobs() > 0) {
226 sleep_time = 10;
227 } else {
228 sleep_time = 60;
229 }
230 t1 = t2;
231 }
232 }
233 }
234 return 0; /* not reached */
235}
236
237static int ChangeUser(const char *user)
238{
239 struct passwd *pas;
240 const char *err_msg;
241
242 /*
243 * Obtain password entry and change privileges
244 */
245 pas = getpwnam(user);
246 if (pas == 0) {
247 crondlog("\011failed to get uid for %s", user);
248 return -1;
249 }
250 setenv("USER", pas->pw_name, 1);
251 setenv("HOME", pas->pw_dir, 1);
252 setenv("SHELL", DEFAULT_SHELL, 1);
253
254 /*
255 * Change running state to the user in question
256 */
257 err_msg = change_identity_e2str(pas);
258 if (err_msg) {
259 crondlog("\011%s for user %s", err_msg, user);
260 return -1;
261 }
262 if (chdir(pas->pw_dir) < 0) {
263 crondlog("\011chdir failed: %s: %m", pas->pw_dir);
264 if (chdir(TMPDIR) < 0) {
265 crondlog("\011chdir failed: %s: %m", TMPDIR);
266 return -1;
267 }
268 }
269 return pas->pw_uid;
270}
271
272static void startlogger(void)
273{
274 if (LogFile == 0) {
275 openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
276 }
277#if ENABLE_DEBUG_CROND_OPTION
278 else { /* test logfile */
279 int logfd;
280
281 logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600);
282 if (logfd >= 0) {
283 close(logfd);
284 }
285 }
286#endif
287}
288
289
290static const char DowAry[] ALIGN1 =
291 "sun""mon""tue""wed""thu""fri""sat"
292 /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */
293;
294
295static const char MonAry[] ALIGN1 =
296 "jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec"
297 /* "Jan""Feb""Mar""Apr""May""Jun""Jul""Aug""Sep""Oct""Nov""Dec" */
298;
299
300static char *ParseField(char *user, char *ary, int modvalue, int off,
301 const char *names, char *ptr)
302/* 'names' is a pointer to a set of 3-char abbreviations */
303{
304 char *base = ptr;
305 int n1 = -1;
306 int n2 = -1;
307
308 if (base == NULL) {
309 return NULL;
310 }
311
312 while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
313 int skip = 0;
314
315 /* Handle numeric digit or symbol or '*' */
316
317 if (*ptr == '*') {
318 n1 = 0; /* everything will be filled */
319 n2 = modvalue - 1;
320 skip = 1;
321 ++ptr;
322 } else if (*ptr >= '0' && *ptr <= '9') {
323 if (n1 < 0) {
324 n1 = strtol(ptr, &ptr, 10) + off;
325 } else {
326 n2 = strtol(ptr, &ptr, 10) + off;
327 }
328 skip = 1;
329 } else if (names) {
330 int i;
331
332 for (i = 0; names[i]; i += 3) {
333 /* was using strncmp before... */
334 if (strncasecmp(ptr, &names[i], 3) == 0) {
335 ptr += 3;
336 if (n1 < 0) {
337 n1 = i / 3;
338 } else {
339 n2 = i / 3;
340 }
341 skip = 1;
342 break;
343 }
344 }
345 }
346
347 /* handle optional range '-' */
348
349 if (skip == 0) {
350 crondlog("\111failed user %s parsing %s\n", user, base);
351 return NULL;
352 }
353 if (*ptr == '-' && n2 < 0) {
354 ++ptr;
355 continue;
356 }
357
358 /*
359 * collapse single-value ranges, handle skipmark, and fill
360 * in the character array appropriately.
361 */
362
363 if (n2 < 0) {
364 n2 = n1;
365 }
366 if (*ptr == '/') {
367 skip = strtol(ptr + 1, &ptr, 10);
368 }
369 /*
370 * fill array, using a failsafe is the easiest way to prevent
371 * an endless loop
372 */
373
374 {
375 int s0 = 1;
376 int failsafe = 1024;
377
378 --n1;
379 do {
380 n1 = (n1 + 1) % modvalue;
381
382 if (--s0 == 0) {
383 ary[n1 % modvalue] = 1;
384 s0 = skip;
385 }
386 }
387 while (n1 != n2 && --failsafe);
388
389 if (failsafe == 0) {
390 crondlog("\111failed user %s parsing %s\n", user, base);
391 return NULL;
392 }
393 }
394 if (*ptr != ',') {
395 break;
396 }
397 ++ptr;
398 n1 = -1;
399 n2 = -1;
400 }
401
402 if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
403 crondlog("\111failed user %s parsing %s\n", user, base);
404 return NULL;
405 }
406
407 while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') {
408 ++ptr;
409 }
410#if ENABLE_DEBUG_CROND_OPTION
411 if (DebugOpt) {
412 int i;
413
414 for (i = 0; i < modvalue; ++i) {
415 crondlog("\005%d", ary[i]);
416 }
417 crondlog("\005\n");
418 }
419#endif
420
421 return ptr;
422}
423
424static void FixDayDow(CronLine * line)
425{
426 int i;
427 int weekUsed = 0;
428 int daysUsed = 0;
429
430 for (i = 0; i < (int)(ARRAY_SIZE(line->cl_Dow)); ++i) {
431 if (line->cl_Dow[i] == 0) {
432 weekUsed = 1;
433 break;
434 }
435 }
436 for (i = 0; i < (int)(ARRAY_SIZE(line->cl_Days)); ++i) {
437 if (line->cl_Days[i] == 0) {
438 daysUsed = 1;
439 break;
440 }
441 }
442 if (weekUsed && !daysUsed) {
443 memset(line->cl_Days, 0, sizeof(line->cl_Days));
444 }
445 if (daysUsed && !weekUsed) {
446 memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
447 }
448}
449
450
451
452static void SynchronizeFile(const char *fileName)
453{
454 int maxEntries = MAXLINES;
455 int maxLines;
456 char buf[1024];
457
458 if (strcmp(fileName, "root") == 0) {
459 maxEntries = 65535;
460 }
461 maxLines = maxEntries * 10;
462
463 if (fileName) {
464 FILE *fi;
465
466 DeleteFile(fileName);
467
468 fi = fopen(fileName, "r");
469 if (fi != NULL) {
470 struct stat sbuf;
471
472 if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
473 CronFile *file = xzalloc(sizeof(CronFile));
474 CronLine **pline;
475
476 file->cf_User = strdup(fileName);
477 pline = &file->cf_LineBase;
478
479 while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {
480 CronLine line;
481 char *ptr;
482
483 trim(buf);
484 if (buf[0] == 0 || buf[0] == '#') {
485 continue;
486 }
487 if (--maxEntries == 0) {
488 break;
489 }
490 memset(&line, 0, sizeof(line));
491
492#if ENABLE_DEBUG_CROND_OPTION
493 if (DebugOpt) {
494 crondlog("\111User %s Entry %s\n", fileName, buf);
495 }
496#endif
497
498 /* parse date ranges */
499 ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf);
500 ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr);
501 ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr);
502 ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr);
503 ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr);
504
505 /* check failure */
506 if (ptr == NULL) {
507 continue;
508 }
509
510 /*
511 * fix days and dow - if one is not * and the other
512 * is *, the other is set to 0, and vise-versa
513 */
514
515 FixDayDow(&line);
516
517 *pline = xzalloc(sizeof(CronLine));
518 **pline = line;
519
520 /* copy command */
521 (*pline)->cl_Shell = strdup(ptr);
522
523#if ENABLE_DEBUG_CROND_OPTION
524 if (DebugOpt) {
525 crondlog("\111 Command %s\n", ptr);
526 }
527#endif
528
529 pline = &((*pline)->cl_Next);
530 }
531 *pline = NULL;
532
533 file->cf_Next = FileBase;
534 FileBase = file;
535
536 if (maxLines == 0 || maxEntries == 0) {
537 crondlog("\111Maximum number of lines reached for user %s\n", fileName);
538 }
539 }
540 fclose(fi);
541 }
542 }
543}
544
545static void CheckUpdates(void)
546{
547 FILE *fi;
548 char buf[256];
549
550 fi = fopen(CRONUPDATE, "r");
551 if (fi != NULL) {
552 remove(CRONUPDATE);
553 while (fgets(buf, sizeof(buf), fi) != NULL) {
554 SynchronizeFile(strtok(buf, " \t\r\n"));
555 }
556 fclose(fi);
557 }
558}
559
560static void SynchronizeDir(void)
561{
562 /* Attempt to delete the database. */
563
564 for (;;) {
565 CronFile *file;
566
567 for (file = FileBase; file && file->cf_Deleted; file = file->cf_Next);
568 if (file == NULL) {
569 break;
570 }
571 DeleteFile(file->cf_User);
572 }
573
574 /*
575 * Remove cron update file
576 *
577 * Re-chdir, in case directory was renamed & deleted, or otherwise
578 * screwed up.
579 *
580 * scan directory and add associated users
581 */
582
583 remove(CRONUPDATE);
584 if (chdir(CDir) < 0) {
585 crondlog("\311cannot find %s\n", CDir);
586 }
587 {
588 DIR *dir = opendir(".");
589 struct dirent *den;
590
591 if (dir) {
592 while ((den = readdir(dir))) {
593 if (strchr(den->d_name, '.') != NULL) {
594 continue;
595 }
596 if (getpwnam(den->d_name)) {
597 SynchronizeFile(den->d_name);
598 } else {
599 crondlog("\007ignoring %s\n", den->d_name);
600 }
601 }
602 closedir(dir);
603 } else {
604 crondlog("\311cannot open current dir!\n");
605 }
606 }
607}
608
609
610/*
611 * DeleteFile() - delete user database
612 *
613 * Note: multiple entries for same user may exist if we were unable to
614 * completely delete a database due to running processes.
615 */
616
617static void DeleteFile(const char *userName)
618{
619 CronFile **pfile = &FileBase;
620 CronFile *file;
621
622 while ((file = *pfile) != NULL) {
623 if (strcmp(userName, file->cf_User) == 0) {
624 CronLine **pline = &file->cf_LineBase;
625 CronLine *line;
626
627 file->cf_Running = 0;
628 file->cf_Deleted = 1;
629
630 while ((line = *pline) != NULL) {
631 if (line->cl_Pid > 0) {
632 file->cf_Running = 1;
633 pline = &line->cl_Next;
634 } else {
635 *pline = line->cl_Next;
636 free(line->cl_Shell);
637 free(line);
638 }
639 }
640 if (file->cf_Running == 0) {
641 *pfile = file->cf_Next;
642 free(file->cf_User);
643 free(file);
644 } else {
645 pfile = &file->cf_Next;
646 }
647 } else {
648 pfile = &file->cf_Next;
649 }
650 }
651}
652
653/*
654 * TestJobs()
655 *
656 * determine which jobs need to be run. Under normal conditions, the
657 * period is about a minute (one scan). Worst case it will be one
658 * hour (60 scans).
659 */
660
661static int TestJobs(time_t t1, time_t t2)
662{
663 int nJobs = 0;
664 time_t t;
665
666 /* Find jobs > t1 and <= t2 */
667
668 for (t = t1 - t1 % 60; t <= t2; t += 60) {
669 if (t > t1) {
670 struct tm *tp = localtime(&t);
671 CronFile *file;
672 CronLine *line;
673
674 for (file = FileBase; file; file = file->cf_Next) {
675#if ENABLE_DEBUG_CROND_OPTION
676 if (DebugOpt)
677 crondlog("\005FILE %s:\n", file->cf_User);
678#endif
679 if (file->cf_Deleted)
680 continue;
681 for (line = file->cf_LineBase; line; line = line->cl_Next) {
682#if ENABLE_DEBUG_CROND_OPTION
683 if (DebugOpt)
684 crondlog("\005 LINE %s\n", line->cl_Shell);
685#endif
686 if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour] &&
687 (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday])
688 && line->cl_Mons[tp->tm_mon]) {
689#if ENABLE_DEBUG_CROND_OPTION
690 if (DebugOpt) {
691 crondlog("\005 JobToDo: %d %s\n",
692 line->cl_Pid, line->cl_Shell);
693 }
694#endif
695 if (line->cl_Pid > 0) {
696 crondlog("\010 process already running: %s %s\n",
697 file->cf_User, line->cl_Shell);
698 } else if (line->cl_Pid == 0) {
699 line->cl_Pid = -1;
700 file->cf_Ready = 1;
701 ++nJobs;
702 }
703 }
704 }
705 }
706 }
707 }
708 return nJobs;
709}
710
711static void RunJobs(void)
712{
713 CronFile *file;
714 CronLine *line;
715
716 for (file = FileBase; file; file = file->cf_Next) {
717 if (file->cf_Ready) {
718 file->cf_Ready = 0;
719
720 for (line = file->cf_LineBase; line; line = line->cl_Next) {
721 if (line->cl_Pid < 0) {
722
723 RunJob(file->cf_User, line);
724
725 crondlog("\010USER %s pid %3d cmd %s\n",
726 file->cf_User, line->cl_Pid, line->cl_Shell);
727 if (line->cl_Pid < 0) {
728 file->cf_Ready = 1;
729 }
730 else if (line->cl_Pid > 0) {
731 file->cf_Running = 1;
732 }
733 }
734 }
735 }
736 }
737}
738
739/*
740 * CheckJobs() - check for job completion
741 *
742 * Check for job completion, return number of jobs still running after
743 * all done.
744 */
745
746static int CheckJobs(void)
747{
748 CronFile *file;
749 CronLine *line;
750 int nStillRunning = 0;
751
752 for (file = FileBase; file; file = file->cf_Next) {
753 if (file->cf_Running) {
754 file->cf_Running = 0;
755
756 for (line = file->cf_LineBase; line; line = line->cl_Next) {
757 if (line->cl_Pid > 0) {
758 int status;
759 int r = wait4(line->cl_Pid, &status, WNOHANG, NULL);
760
761 if (r < 0 || r == line->cl_Pid) {
762 EndJob(file->cf_User, line);
763 if (line->cl_Pid) {
764 file->cf_Running = 1;
765 }
766 } else if (r == 0) {
767 file->cf_Running = 1;
768 }
769 }
770 }
771 }
772 nStillRunning += file->cf_Running;
773 }
774 return nStillRunning;
775}
776
777
778#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
779static void
780ForkJob(const char *user, CronLine * line, int mailFd,
781 const char *prog, const char *cmd, const char *arg, const char *mailf)
782{
783 /* Fork as the user in question and run program */
784 pid_t pid = fork();
785
786 line->cl_Pid = pid;
787 if (pid == 0) {
788 /* CHILD */
789
790 /* Change running state to the user in question */
791
792 if (ChangeUser(user) < 0) {
793 exit(0);
794 }
795#if ENABLE_DEBUG_CROND_OPTION
796 if (DebugOpt) {
797 crondlog("\005Child Running %s\n", prog);
798 }
799#endif
800
801 if (mailFd >= 0) {
802 dup2(mailFd, mailf != NULL);
803 dup2((mailf ? mailFd : 1), 2);
804 close(mailFd);
805 }
806 execl(prog, prog, cmd, arg, NULL);
807 crondlog("\024cannot exec, user %s cmd %s %s %s\n", user, prog, cmd, arg);
808 if (mailf) {
809 fdprintf(1, "Exec failed: %s -c %s\n", prog, arg);
810 }
811 exit(0);
812 } else if (pid < 0) {
813 /* FORK FAILED */
814 crondlog("\024cannot fork, user %s\n", user);
815 line->cl_Pid = 0;
816 if (mailf) {
817 remove(mailf);
818 }
819 } else if (mailf) {
820 /* PARENT, FORK SUCCESS
821 * rename mail-file based on pid of process
822 */
823 char mailFile2[128];
824
825 snprintf(mailFile2, sizeof(mailFile2), TMPDIR "/cron.%s.%d", user, pid);
826 rename(mailf, mailFile2);
827 }
828 /*
829 * Close the mail file descriptor.. we can't just leave it open in
830 * a structure, closing it later, because we might run out of descriptors
831 */
832
833 if (mailFd >= 0) {
834 close(mailFd);
835 }
836}
837
838static void RunJob(const char *user, CronLine * line)
839{
840 char mailFile[128];
841 int mailFd;
842
843 line->cl_Pid = 0;
844 line->cl_MailFlag = 0;
845
846 /* open mail file - owner root so nobody can screw with it. */
847
848 snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, getpid());
849 mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600);
850
851 if (mailFd >= 0) {
852 line->cl_MailFlag = 1;
853 fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", user,
854 line->cl_Shell);
855 line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR);
856 } else {
857 crondlog("\024cannot create mail file user %s file %s, output to /dev/null\n", user, mailFile);
858 }
859
860 ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
861}
862
863/*
864 * EndJob - called when job terminates and when mail terminates
865 */
866
867static void EndJob(const char *user, CronLine * line)
868{
869 int mailFd;
870 char mailFile[128];
871 struct stat sbuf;
872
873 /* No job */
874
875 if (line->cl_Pid <= 0) {
876 line->cl_Pid = 0;
877 return;
878 }
879
880 /*
881 * End of job and no mail file
882 * End of sendmail job
883 */
884
885 snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, line->cl_Pid);
886 line->cl_Pid = 0;
887
888 if (line->cl_MailFlag != 1) {
889 return;
890 }
891 line->cl_MailFlag = 0;
892
893 /*
894 * End of primary job - check for mail file. If size has increased and
895 * the file is still valid, we sendmail it.
896 */
897
898 mailFd = open(mailFile, O_RDONLY);
899 remove(mailFile);
900 if (mailFd < 0) {
901 return;
902 }
903
904 if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid
905 || sbuf.st_nlink != 0 || sbuf.st_size == line->cl_MailPos
906 || !S_ISREG(sbuf.st_mode)
907 ) {
908 close(mailFd);
909 return;
910 }
911 ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
912}
913#else
914/* crond without sendmail */
915
916static void RunJob(const char *user, CronLine * line)
917{
918 /* Fork as the user in question and run program */
919 pid_t pid = fork();
920
921 if (pid == 0) {
922 /* CHILD */
923
924 /* Change running state to the user in question */
925
926 if (ChangeUser(user) < 0) {
927 exit(0);
928 }
929#if ENABLE_DEBUG_CROND_OPTION
930 if (DebugOpt) {
931 crondlog("\005Child Running %s\n", DEFAULT_SHELL);
932 }
933#endif
934
935 execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL);
936 crondlog("\024cannot exec, user %s cmd %s -c %s\n", user,
937 DEFAULT_SHELL, line->cl_Shell);
938 exit(0);
939 } else if (pid < 0) {
940 /* FORK FAILED */
941 crondlog("\024cannot, user %s\n", user);
942 pid = 0;
943 }
944 line->cl_Pid = pid;
945}
946#endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */
Note: See TracBrowser for help on using the repository browser.