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