]> granicus.if.org Git - fcron/blob - fcron.c
refactored suspend handling code into suspend.[ch]
[fcron] / fcron.c
1 /*
2  * FCRON - periodic command scheduler 
3  *
4  *  Copyright 2000-2014 Thibault Godouet <fcron@free.fr>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  * 
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  *  The GNU General Public License can also be found in the file
21  *  `LICENSE' that comes with the fcron source distribution.
22  */
23
24
25 #include "fcron.h"
26
27 #include "database.h"
28 #include "conf.h"
29 #include "job.h"
30 #include "temp_file.h"
31 #include "fcronconf.h"
32 #include "select.h"
33 #include "suspend.h"
34 #ifdef FCRONDYN
35 #include "fcrondyn_svr.h"
36 #endif
37
38
39 void main_loop(void);
40 void check_signal(void);
41 void reset_sig_cont(void);
42 void info(void);
43 void usage(void);
44 void print_schedule(void);
45 RETSIGTYPE sighup_handler(int x);
46 RETSIGTYPE sigterm_handler(int x);
47 RETSIGTYPE sigchild_handler(int x);
48 RETSIGTYPE sigusr1_handler(int x);
49 RETSIGTYPE sigusr2_handler(int x);
50 RETSIGTYPE sigcont_handler(int x);
51 int parseopt(int argc, char *argv[]);
52 void get_lock(void);
53 int is_system_reboot(void);
54 void create_spooldir(char *dir);
55
56 /* command line options */
57
58 #ifdef FOREGROUND
59 char foreground = 1;            /* set to 1 when we are on foreground, else 0 */
60 #else
61 char foreground = 0;            /* set to 1 when we are on foreground, else 0 */
62 #endif
63
64 time_t first_sleep = FIRST_SLEEP;
65 const time_t min_sleep_usec = 1000;     /* 1ms -- we won't sleep for less than this time */
66 time_t save_time = SAVE;
67 char once = 0;                  /* set to 1 if fcron shall return immediately after running
68                                  * all jobs that are due at the time when fcron is started */
69
70 /* Get the default locale character set for the mail
71  * "Content-Type: ...; charset=" header */
72 char default_mail_charset[TERM_LEN] = "";
73
74 /* used in temp_file() : create it in current dir (normally spool dir) */
75 char *tmp_path = "";
76
77 /* process identity */
78 pid_t daemon_pid;
79 mode_t saved_umask;             /* default root umask */
80 char *prog_name = NULL;
81 char *orig_tz_envvar = NULL;
82
83 /* uid/gid of user/group root 
84  * (we don't use the static UID or GID as we ask for user and group names
85  * in the configure script) */
86 uid_t rootuid = 0;
87 gid_t rootgid = 0;
88
89 /* have we got a signal ? */
90 char sig_conf = 0;              /* is 1 when we got a SIGHUP, 2 for a SIGUSR1 */
91 char sig_chld = 0;              /* is 1 when we got a SIGCHLD */
92 char sig_debug = 0;             /* is 1 when we got a SIGUSR2 */
93 char sig_cont = 0;              /* is 1 when we got a SIGCONT */
94
95 /* jobs database */
96 struct cf_t *file_base;         /* point to the first file of the list */
97 struct job_t *queue_base;       /* ordered list of normal jobs to be run */
98 unsigned long int next_id;      /* id for next line to enter database */
99
100 struct cl_t **serial_array;     /* ordered list of job to be run one by one */
101 short int serial_array_size;    /* size of serial_array */
102 short int serial_array_index;   /* the index of the first job */
103 short int serial_num;           /* number of job being queued */
104 short int serial_running;       /* number of running serial jobs */
105
106 /* do not run more than this number of serial job simultaneously */
107 short int serial_max_running = SERIAL_MAX_RUNNING;
108 short int serial_queue_max = SERIAL_QUEUE_MAX;
109
110 lavg_list_t *lavg_list;         /* jobs waiting for a given system load value */
111 short int lavg_queue_max = LAVG_QUEUE_MAX;
112 short int lavg_serial_running;  /* number of serialized lavg job being running */
113
114 exe_list_t *exe_list;           /* jobs which are executed */
115
116 time_t now;                     /* the current time */
117
118 #ifdef HAVE_LIBPAM
119 pam_handle_t *pamh = NULL;
120 const struct pam_conv apamconv = { NULL };
121 #endif
122
123 void
124 info(void)
125     /* print some informations about this program :
126      * version, license */
127 {
128     fprintf(stderr,
129             "fcron " VERSION_QUOTED " - periodic command scheduler\n"
130             "Copyright " COPYRIGHT_QUOTED " Thibault Godouet <fcron@free.fr>\n"
131             "This program is free software distributed WITHOUT ANY WARRANTY.\n"
132             "See the GNU General Public License for more details.\n");
133
134     exit(EXIT_OK);
135
136 }
137
138
139 void
140 usage(void)
141   /*  print a help message about command line options and exit */
142 {
143     fprintf(stderr, "\nfcron " VERSION_QUOTED "\n\n"
144             "fcron [-d] [-f] [-b]\n"
145             "fcron -h\n"
146             "  -s t   --savetime t     Save fcrontabs on disk every t sec.\n"
147             "  -l t   --firstsleep t   Sets the initial delay before any job is executed"
148             ",\n                          default to %d seconds.\n"
149             "  -m n   --maxserial n    Set to n the max number of running serial jobs.\n"
150             "  -c f   --configfile f   Make fcron use config file f.\n"
151             "  -n d   --newspooldir d  Create d as a new spool directory.\n"
152             "  -f     --foreground     Stay in foreground.\n"
153             "  -b     --background     Go to background.\n"
154             "  -y     --nosyslog       Don't log to syslog at all.\n"
155             "  -p     --logfilepath    If set, log to the file given as argument.\n"
156             "  -o     --once           Execute all jobs that need to be run, wait for "
157             "them,\n                          then return. Sets firstsleep to 0.\n"
158             "                          Especially useful with -f and -y.\n"
159             "  -d     --debug          Set Debug mode.\n"
160             "  -h     --help           Show this help message.\n"
161             "  -V     --version        Display version & infos about fcron.\n",
162             FIRST_SLEEP);
163
164     exit(EXIT_ERR);
165 }
166
167
168 void
169 print_schedule(void)
170     /* print the current schedule on syslog */
171 {
172     cf_t *cf;
173     cl_t *cl;
174     struct tm *ftime;
175
176     explain("Printing schedule ...");
177     for (cf = file_base; cf; cf = cf->cf_next) {
178         explain(" File %s", cf->cf_user);
179         for (cl = cf->cf_line_base; cl; cl = cl->cl_next) {
180             ftime = localtime(&(cl->cl_nextexe));
181             explain("  cmd '%s' next exec %04d-%02d-%02d wday:%d %02d:%02d"
182                     " (system time)",
183                     cl->cl_shell, (ftime->tm_year + 1900), (ftime->tm_mon + 1),
184                     ftime->tm_mday, ftime->tm_wday, ftime->tm_hour,
185                     ftime->tm_min);
186
187         }
188     }
189     explain("... end of printing schedule.");
190 }
191
192
193 void
194 xexit(int exit_value)
195     /* exit after having freed memory and removed lock file */
196 {
197     cf_t *f = NULL;
198
199     now = my_time();
200
201     /* we save all files now and after having waiting for all
202      * job being executed because we might get a SIGKILL
203      * if we don't exit quickly */
204     save_file(NULL);
205
206 #ifdef FCRONDYN
207     fcrondyn_socket_close(NULL);
208 #endif
209
210     f = file_base;
211     while (f != NULL) {
212         if (f->cf_running > 0) {
213             /* */
214             debug("waiting jobs for %s ...", f->cf_user);
215             /* */
216             wait_all(&f->cf_running);
217             save_file(f);
218         }
219         delete_file(f->cf_user);
220
221         /* delete_file remove the f file from the list :
222          * next file to remove is now pointed by file_base. */
223         f = file_base;
224     }
225
226     remove(pidfile);
227
228     exe_list_destroy(exe_list);
229     lavg_list_destroy(lavg_list);
230     free_conf();
231
232     Free_safe(orig_tz_envvar);
233
234     explain("Exiting with code %d", exit_value);
235     exit(exit_value);
236
237 }
238
239 void
240 get_lock()
241     /* check if another fcron daemon is running with the same config (-c option) :
242      * in this case, die. if not, write our pid to /var/run/fcron.pid in order to lock,
243      * and to permit fcrontab to read our pid and signal us */
244 {
245     int otherpid = 0;
246     FILE *daemon_lockfp = NULL;
247     int fd;
248
249     if (((fd = open(pidfile, O_RDWR | O_CREAT, 0644)) == -1)
250         || ((daemon_lockfp = fdopen(fd, "r+"))) == NULL)
251         die_e("can't open or create %s", pidfile);
252
253 #ifdef HAVE_FLOCK
254     if (flock(fd, LOCK_EX | LOCK_NB) != 0)
255 #else                           /* HAVE_FLOCK */
256     if (lockf(fileno(daemon_lockfp), F_TLOCK, 0) != 0)
257 #endif                          /* ! HAVE_FLOCK */
258     {
259         if (fscanf(daemon_lockfp, "%d", &otherpid) >= 1)
260             die_e("can't lock %s, running daemon's pid may be %d", pidfile,
261                   otherpid);
262         else
263             die_e("can't lock %s, and unable to read running"
264                   " daemon's pid", pidfile);
265     }
266
267     fcntl(fd, F_SETFD, 1);
268
269     rewind(daemon_lockfp);
270     fprintf(daemon_lockfp, "%d\n", (int)daemon_pid);
271     fflush(daemon_lockfp);
272     if (ftruncate(fileno(daemon_lockfp), ftell(daemon_lockfp)) < 0)
273         error_e
274             ("Unable to ftruncate(fileno(daemon_lockfp), ftell(daemon_lockfp))");
275
276     /* abandon fd and daemon_lockfp even though the file is open. we need to
277      * keep it open and locked, but we don't need the handles elsewhere.
278      */
279
280 }
281
282 int
283 is_system_startup(void)
284 {
285     int reboot = 0;
286
287     /* lock exist - skip reboot jobs */
288     if (access(REBOOT_LOCK, F_OK) == 0) {
289         explain("@reboot jobs will only be run at computer's startup.");
290         /* don't run @reboot jobs */
291         return 0;
292     }
293     /* lock doesn't exist - create lock, run reboot jobs */
294     if ((reboot = creat(REBOOT_LOCK, S_IRUSR & S_IWUSR)) < 0)
295         error_e("Can't create lock for reboot jobs.");
296     else
297         xclose_check(&reboot, REBOOT_LOCK);
298
299     /* run @reboot jobs */
300     return 1;
301 }
302
303
304 int
305 parseopt(int argc, char *argv[])
306   /* set options */
307 {
308
309     int c;
310     int i;
311
312 #ifdef HAVE_GETOPT_LONG
313     static struct option opt[] = {
314         {"debug", 0, NULL, 'd'},
315         {"foreground", 0, NULL, 'f'},
316         {"background", 0, NULL, 'b'},
317         {"nosyslog", 0, NULL, 'y'},
318         {"logfilepath", 1, NULL, 'p'},
319         {"help", 0, NULL, 'h'},
320         {"version", 0, NULL, 'V'},
321         {"once", 0, NULL, 'o'},
322         {"savetime", 1, NULL, 's'},
323         {"firstsleep", 1, NULL, 'l'},
324         {"maxserial", 1, NULL, 'm'},
325         {"configfile", 1, NULL, 'c'},
326         {"newspooldir", 1, NULL, 'n'},
327         {"queuelen", 1, NULL, 'q'},
328         {0, 0, 0, 0}
329     };
330 #endif                          /* HAVE_GETOPT_LONG */
331
332     extern char *optarg;
333     extern int optind, opterr, optopt;
334
335     /* constants and variables defined by command line */
336
337     while (1) {
338 #ifdef HAVE_GETOPT_LONG
339         c = getopt_long(argc, argv, "dfbyp:hVos:l:m:c:n:q:", opt, NULL);
340 #else
341         c = getopt(argc, argv, "dfbyp:hVos:l:m:c:n:q:");
342 #endif                          /* HAVE_GETOPT_LONG */
343         if (c == EOF)
344             break;
345         switch ((char)c) {
346
347         case 'V':
348             info();
349             break;
350
351         case 'h':
352             usage();
353             break;
354
355         case 'd':
356             debug_opt = 1;
357             break;
358
359         case 'f':
360             foreground = 1;
361             break;
362
363         case 'b':
364             foreground = 0;
365             break;
366
367         case 'y':
368             dosyslog = 0;
369             break;
370
371         case 'p':
372             logfile_path = strdup2(optarg);
373             break;
374
375         case 'o':
376             once = 1;
377             first_sleep = 0;
378             break;
379
380         case 's':
381             if ((save_time = strtol(optarg, NULL, 10)) < 60
382                 || save_time >= TIME_T_MAX)
383                 die("Save time can only be set between 60 and %d.", TIME_T_MAX);
384             break;
385
386         case 'l':
387             if ((first_sleep = strtol(optarg, NULL, 10)) < 0
388                 || first_sleep >= TIME_T_MAX)
389                 die("First sleep can only be set between 0 and %d.",
390                     TIME_T_MAX);
391             break;
392
393         case 'm':
394             if ((serial_max_running = strtol(optarg, NULL, 10)) <= 0
395                 || serial_max_running >= SHRT_MAX)
396                 die("Max running can only be set between 1 and %d.", SHRT_MAX);
397             break;
398
399         case 'c':
400             Set(fcronconf, optarg);
401             break;
402
403         case 'n':
404             create_spooldir(optarg);
405             break;
406
407         case 'q':
408             if ((lavg_queue_max = serial_queue_max =
409                  strtol(optarg, NULL, 10)) < 5 || serial_queue_max >= SHRT_MAX)
410                 die("Queue length can only be set between 5 and %d.", SHRT_MAX);
411             break;
412
413         case ':':
414             error("(parseopt) Missing parameter");
415             usage();
416
417         case '?':
418             usage();
419
420         default:
421             warn("(parseopt) Warning: getopt returned %c", c);
422         }
423     }
424
425     if (optind < argc) {
426         for (i = optind; i <= argc; i++)
427             error("Unknown argument \"%s\"", argv[i]);
428         usage();
429     }
430
431     return OK;
432
433 }
434
435 void
436 create_spooldir(char *dir)
437     /* create a new spool dir for fcron : set correctly its mode and owner */
438 {
439     int dir_fd = -1;
440     struct stat st;
441     uid_t useruid = get_user_uid_safe(USERNAME);
442     gid_t usergid = get_group_gid_safe(GROUPNAME);
443
444     if (mkdir(dir, 0) != 0 && errno != EEXIST)
445         die_e("Cannot create dir %s", dir);
446
447     if ((dir_fd = open(dir, 0)) < 0)
448         die_e("Cannot open dir %s", dir);
449
450     if (fstat(dir_fd, &st) != 0) {
451         xclose_check(&dir_fd, "spooldir");
452         die_e("Cannot fstat %s", dir);
453     }
454
455     if (!S_ISDIR(st.st_mode)) {
456         xclose_check(&dir_fd, "spooldir");
457         die("%s exists and is not a directory", dir);
458     }
459
460     if (fchown(dir_fd, useruid, usergid) != 0) {
461         xclose_check(&dir_fd, "spooldir");
462         die_e("Cannot fchown dir %s to %s:%s", dir, USERNAME, GROUPNAME);
463     }
464
465     if (fchmod
466         (dir_fd,
467          S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP) != 0) {
468         xclose_check(&dir_fd, "spooldir");
469         die_e("Cannot change dir %s's mode to 770", dir);
470     }
471
472     xclose_check(&dir_fd, "spooldir");
473
474     exit(EXIT_OK);
475
476 }
477
478
479 RETSIGTYPE
480 sigterm_handler(int x)
481   /* exit safely */
482 {
483     debug("");
484     explain("SIGTERM signal received");
485     xexit(EXIT_OK);
486 }
487
488 RETSIGTYPE
489 sighup_handler(int x)
490   /* update configuration */
491 {
492     /* we don't call the synchronize_dir() function directly,
493      * because it may cause some problems if this signal
494      * is not received during the sleep
495      */
496     sig_conf = 1;
497 }
498
499 RETSIGTYPE
500 sigchild_handler(int x)
501   /* call wait_chld() to take care of finished jobs */
502 {
503
504     sig_chld = 1;
505
506 }
507
508
509 RETSIGTYPE
510 sigusr1_handler(int x)
511   /* reload all configurations */
512 {
513     /* we don't call the synchronize_dir() function directly,
514      * because it may cause some problems if this signal
515      * is not received during the sleep
516      */
517     sig_conf = 2;
518 }
519
520
521 RETSIGTYPE
522 sigusr2_handler(int x)
523   /* print schedule and switch on/off debug mode */
524 {
525     sig_debug = 1;
526 }
527
528 RETSIGTYPE
529 sigcont_handler(int x)
530   /* used to notify fcron of a system resume after suspend.
531    * However this signal could also be received in other cases. */
532 {
533     sig_cont = 1;
534 }
535
536 int
537 main(int argc, char **argv)
538 {
539     char *codeset = NULL;
540
541     rootuid = get_user_uid_safe(ROOTNAME);
542     rootgid = get_group_gid_safe(ROOTGROUP);
543
544     /* we set it to 022 in order to get a pidfile readable by fcrontab
545      * (will be set to 066 later) */
546     saved_umask = umask(022);
547
548     /* parse options */
549
550     if (strrchr(argv[0], '/') == NULL)
551         prog_name = argv[0];
552     else
553         prog_name = strrchr(argv[0], '/') + 1;
554
555     {
556         uid_t daemon_uid;
557         if ((daemon_uid = getuid()) != rootuid)
558             die("Fcron must be executed as root");
559     }
560
561     /* we have to set daemon_pid before the fork because it's
562      * used in die() and die_e() functions */
563     daemon_pid = getpid();
564
565     /* save the value of the TZ env variable (used for option timezone) */
566     orig_tz_envvar = strdup2(getenv("TZ"));
567
568     parseopt(argc, argv);
569
570     /* read fcron.conf and update global parameters */
571     read_conf();
572
573     /* initialize the logs before we become a daemon */
574     xopenlog();
575
576     /* change directory */
577
578     if (chdir(fcrontabs) != 0)
579         die_e("Could not change dir to %s", fcrontabs);
580
581     /* Get the default locale character set for the mail
582      * "Content-Type: ...; charset=" header */
583     setlocale(LC_ALL, "");      /* set locale to system defaults or to
584                                  * that specified by any  LC_* env vars */
585     /* Except that "US-ASCII" is preferred to "ANSI_x3.4-1968" in MIME,
586      * even though "ANSI_x3.4-1968" is the official charset name. */
587     if ((codeset = nl_langinfo(CODESET)) != 0L &&
588         strcmp(codeset, "ANSI_x3.4-1968") != 0)
589         strncpy(default_mail_charset, codeset, sizeof(default_mail_charset));
590     else
591         strcpy(default_mail_charset, "US-ASCII");
592
593     if (freopen("/dev/null", "r", stdin) == NULL)
594         error_e("Could not open /dev/null as stdin");
595
596     if (foreground == 0) {
597
598         /* close stdout and stderr.
599          * close unused descriptors
600          * optional detach from controlling terminal */
601
602         int fd;
603         pid_t pid;
604
605         switch (pid = fork()) {
606         case -1:
607             die_e("fork");
608             break;
609         case 0:
610             /* child */
611             break;
612         default:
613             /* parent */
614 /*          printf("%s[%d] " VERSION_QUOTED " : started.\n", */
615 /*                 prog_name, pid); */
616             exit(0);
617         }
618
619         daemon_pid = getpid();
620
621         if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
622 #ifndef _HPUX_SOURCE
623             ioctl(fd, TIOCNOTTY, 0);
624 #endif
625             xclose_check(&fd, "/dev/tty");
626         }
627
628         if (freopen("/dev/null", "w", stdout) == NULL)
629             error_e("Could not open /dev/null as stdout");
630         if (freopen("/dev/null", "w", stderr) == NULL)
631             error_e("Could not open /dev/null as stderr");
632
633         /* close most other open fds */
634         xcloselog();
635         for (fd = 3; fd < 250; fd++)
636             /* don't use xclose_check() as we do expect most of them to fail */
637             (void)close(fd);
638
639         /* finally, create a new session */
640         if (setsid() == -1)
641             error("Could not setsid()");
642
643     }
644
645     /* check if another fcron daemon is running, create pid file and lock it */
646     get_lock();
647
648     /* this program belongs to root : we set default permission mode
649      * to  600 for security reasons, but we reset them to the saved
650      * umask just before we run a job */
651     umask(066);
652
653     explain("%s[%d] " VERSION_QUOTED " started", prog_name, daemon_pid);
654
655 #ifdef HAVE_SIGNAL
656     /* FIXME: check for errors */
657     signal(SIGTERM, sigterm_handler);
658     signal(SIGHUP, sighup_handler);
659     siginterrupt(SIGHUP, 0);
660     signal(SIGCHLD, sigchild_handler);
661     siginterrupt(SIGCHLD, 0);
662     signal(SIGUSR1, sigusr1_handler);
663     siginterrupt(SIGUSR1, 0);
664     signal(SIGUSR2, sigusr2_handler);
665     siginterrupt(SIGUSR2, 0);
666     signal(SIGCONT, sigcont_handler);
667     siginterrupt(SIGCONT, 0);
668     /* we don't want SIGPIPE to kill fcron, and don't need to handle it as when ignored
669      * write() on a pipe closed at the other end will return EPIPE */
670     signal(SIGPIPE, SIG_IGN);
671 #elif HAVE_SIGSET
672     /* FIXME: check for errors */
673     sigset(SIGTERM, sigterm_handler);
674     sigset(SIGHUP, sighup_handler);
675     sigset(SIGCHLD, sigchild_handler);
676     sigset(SIGUSR1, sigusr1_handler);
677     sigset(SIGUSR2, sigusr2_handler);
678     sigset(SIGCONT, sigcont_handler);
679     sigset(SIGPIPE, SIG_IGN);
680 #endif
681
682     /* initialize job database */
683     next_id = 0;
684
685     /* initialize exe_array */
686     exe_list = exe_list_init();
687
688     /* initialize serial_array */
689     serial_running = 0;
690     serial_array_index = 0;
691     serial_num = 0;
692     serial_array_size = SERIAL_INITIAL_SIZE;
693     serial_array =
694         alloc_safe(serial_array_size * sizeof(cl_t *), "serial_array");
695
696     /* initialize lavg_array */
697     lavg_list = lavg_list_init();
698     lavg_list->max_entries = lavg_queue_max;
699     lavg_serial_running = 0;
700
701     /* initialize random number generator :
702      * WARNING : easy to guess !!! */
703     /* we use the hostname and tv_usec in order to get different seeds
704      * on two different machines starting fcron at the same moment */
705     {
706         char hostname[50];
707         int i;
708         unsigned int seed;
709 #ifdef HAVE_GETTIMEOFDAY
710         struct timeval tv;      /* we use usec field to get more precision */
711         gettimeofday(&tv, NULL);
712         seed = ((unsigned int)tv.tv_usec) ^ ((unsigned int)tv.tv_sec);
713 #else
714         seed = (unsigned int)time(NULL);
715 #endif
716         gethostname(hostname, sizeof(hostname));
717
718         for (i = 0; i < sizeof(hostname) - sizeof(seed); i += sizeof(seed))
719             seed ^= (unsigned int)*(hostname + i);
720
721         srand(seed);
722     }
723
724     main_loop();
725
726     /* never reached */
727     return EXIT_OK;
728 }
729
730
731 void
732 check_signal()
733     /* check if a signal has been received and handle it */
734 {
735
736     /* we reinstall the signal handler functions here and not directly in the handlers,
737      * as it is not supported on some systems (HP-UX) and makes fcron crash */
738
739     if (sig_chld > 0) {
740         wait_chld();
741         sig_chld = 0;
742 #ifdef HAVE_SIGNAL
743         (void)signal(SIGCHLD, sigchild_handler);
744         siginterrupt(SIGCHLD, 0);
745 #endif
746     }
747
748     if (sig_conf > 0) {
749
750         if (sig_conf == 1) {
751             /* update configuration */
752             synchronize_dir(".", 0);
753             sig_conf = 0;
754 #ifdef HAVE_SIGNAL
755             signal(SIGHUP, sighup_handler);
756             siginterrupt(SIGHUP, 0);
757 #endif
758         }
759         else {
760             /* reload all configuration */
761             reload_all(".");
762             sig_conf = 0;
763 #ifdef HAVE_SIGNAL
764             signal(SIGUSR1, sigusr1_handler);
765             siginterrupt(SIGUSR1, 0);
766 #endif
767         }
768
769     }
770
771     if (sig_debug > 0) {
772         print_schedule();
773         debug_opt = (debug_opt > 0) ? 0 : 1;
774         explain("debug_opt = %d", debug_opt);
775         sig_debug = 0;
776 #ifdef HAVE_SIGNAL
777         signal(SIGUSR2, sigusr2_handler);
778         siginterrupt(SIGUSR2, 0);
779 #endif
780     }
781
782 }
783
784 void
785 reset_sig_cont(void)
786    /* reinitialize the SIGCONT handler if it was raised */
787 {
788     if (sig_cont > 0) {
789
790         sig_cont = 0;
791 #ifdef HAVE_SIGNAL
792         signal(SIGCONT, sigcont_handler);
793         siginterrupt(SIGCONT, 0);
794 #endif
795     }
796 }
797
798 #ifdef HAVE_GETTIMEOFDAY
799 #define debug_print_tstamp(where_str) \
800         { \
801         if (debug_opt) { \
802             gettimeofday(&now_tv, NULL); \
803             debug(where_str ": now=%ld now_tv sec=%ld usec=%ld", now, now_tv.tv_sec, \
804                   now_tv.tv_usec); \
805         } \
806     }
807 #else
808 #define debug_print_tstamp(where_str) { ; }
809 #endif
810
811 void
812 main_loop()
813   /* main loop - get the time to sleep until next job execution,
814    *             sleep, and then test all jobs and execute if needed. */
815 {
816     time_t save;                /* time remaining until next save */
817     time_t slept_from;          /* time it was when we went into sleep */
818     time_t nwt;                 /* next wake time */
819 #ifdef HAVE_GETTIMEOFDAY
820     struct select_instance main_select;
821     struct timeval now_tv;      /* we use usec field to get more precision */
822     struct timeval sleep_tv;    /* we use usec field to get more precision */
823 #endif
824
825     debug("Entering main loop");
826
827 #ifdef HAVE_GETTIMEOFDAY
828     select_init(&main_select);
829 #ifdef FCRONDYN
830     fcrondyn_socket_init(&main_select);
831 #endif
832 #endif
833
834     now = my_time();
835
836     synchronize_dir(".", is_system_startup());
837
838     /* synchronize save with jobs execution */
839     save = now + save_time;
840
841     if (serial_num > 0 || once) {
842         nwt = now + first_sleep;
843     }
844     else {
845         /* force first execution after first_sleep sec : execution of jobs
846          * during system boot time is not what we want */
847         nwt = tmax(next_wake_time(save), now + first_sleep);
848     }
849     debug("initial wake time : %s", ctime(&nwt));
850
851     for (;;) {
852
853         /* remember when we started to sleep -- this is to detect suspend/hibernate */
854         slept_from = time(NULL);
855
856 #ifdef HAVE_GETTIMEOFDAY
857         gettimeofday(&now_tv, NULL);
858         debug("now gettimeofday tv_sec=%ld, tv_usec=%ld %s", now_tv.tv_sec,
859               now_tv.tv_usec, ctime(&nwt));
860
861         if (nwt <= now_tv.tv_sec) {
862             sleep_tv.tv_sec = 0;
863             sleep_tv.tv_usec = 0;
864         }
865         else {
866             /* compensate for any time spent in the loop,
867              * so as we wake up exactly at the beginning of the second */
868             sleep_tv.tv_sec = nwt - now_tv.tv_sec - 1;
869             /* we set tv_usec to slightly more than necessary so as we don't wake
870              * up too early (e.g. due to rounding to the system clock granularity),
871              * in which case we would have to go back to sleep again */
872             sleep_tv.tv_usec = 1000000 + min_sleep_usec - now_tv.tv_usec;
873         }
874
875         if (sleep_tv.tv_usec > 999999) {
876             /* On some systems (BSD, etc), tv_usec cannot be greater than 999999 */
877             sleep_tv.tv_usec = 999999;
878         }
879         else if (sleep_tv.tv_sec == 0 && sleep_tv.tv_usec < min_sleep_usec) {
880             /* to prevent any infinite loop, sleep at least 1ms */
881             debug
882                 ("We'll sleep for a tiny bit to avoid any risk of infinite loop");
883             sleep_tv.tv_usec = min_sleep_usec;
884         }
885         debug("nwt=%s, sleep sec=%ld, usec=%ld", ctime(&nwt), sleep_tv.tv_sec,
886               sleep_tv.tv_usec);
887         select_call(&main_select, &sleep_tv);
888 #else                           /* HAVE_GETTIMEOFDAY */
889         if (nwt - now > 0) {
890             sleep(nwt - now);
891         }
892 #endif                          /* HAVE_GETTIMEOFDAY */
893
894         debug("\n");
895         now = my_time();
896         debug_print_tstamp("just woke up")
897
898             check_signal();
899         check_suspend(slept_from, nwt, &sig_cont);
900         reset_sig_cont();
901         debug_print_tstamp("after check_signal and suspend")
902
903             test_jobs();
904         debug_print_tstamp("after test_jobs")
905
906             while (serial_num > 0 && serial_running < serial_max_running) {
907             run_serial_job();
908         }
909
910         if (once) {
911             explain("Running with option once : exiting ... ");
912             xexit(EXIT_OK);
913         }
914
915         if (save <= now) {
916             save = now + save_time;
917             /* save all files */
918             save_file(NULL);
919         }
920         debug_print_tstamp("after save")
921 #if defined(FCRONDYN) && defined(HAVE_GETTIMEOFDAY)
922             /* check if there's a new connection, a new command to answer, etc ... */
923             /* we do that *after* other checks, to avoid Denial Of Service attacks */
924             fcrondyn_socket_check(&main_select);
925 #endif
926
927         nwt = check_lavg(save);
928         debug_print_tstamp("after check_lavg")
929             debug("next wake time : %s", ctime(&nwt));
930
931         check_signal();
932
933     }
934
935 }