]> granicus.if.org Git - fcron/commitdiff
refactored suspend handling code into suspend.[ch]
authorThibault Godouet <fcron@free.fr>
Sun, 12 Jun 2016 10:25:59 +0000 (11:25 +0100)
committerThibault Godouet <fcron@free.fr>
Sun, 12 Jun 2016 10:25:59 +0000 (11:25 +0100)
Also applied code style.

Makefile.in
fcron.c
fcrondyn_svr.c
fcrondyn_svr.h
log.h
select.c
select.h
suspend.c [new file with mode: 0644]
suspend.h [new file with mode: 0644]

index 5349819b765736fd592bdcaa72dbc686028b70c9..d264f6c596b917ba9c5c1182b10428fcc53497e9 100644 (file)
@@ -75,7 +75,7 @@ CFLAGS += $(OPTIM) $(OPTION) $(DEFS) $(CPPFLAGS)
 ifeq ($(FCRONDYN), 1)
 LIBOBJS := $(LIBOBJS)
 endif
-OBJSD := fcron.o cl.o subs.o mem.o save.o temp_file.o log.o database.o job.o conf.o u_list.o exe_list.o lavg_list.o env_list.o fcronconf.o filesubs.o select.o fcrondyn_svr.o $(LIBOBJS)
+OBJSD := fcron.o cl.o subs.o mem.o save.o temp_file.o log.o database.o job.o conf.o u_list.o exe_list.o lavg_list.o env_list.o fcronconf.o filesubs.o select.o fcrondyn_svr.o suspend.o $(LIBOBJS)
 OBJSTAB := fcrontab.o cl.o subs.o mem.o save.o temp_file.o  log.o fileconf.o allow.o read_string.o u_list.o env_list.o fcronconf.o filesubs.o
 OBJSDYN := fcrondyn.o subs.o mem.o log.o allow.o read_string.o fcronconf.o filesubs.o
 OBJCONV := convert-fcrontab.o cl.o subs.o mem.o save.o log.o u_list.o env_list.o fcronconf.o filesubs.o
diff --git a/fcron.c b/fcron.c
index 7f4599830c22a21d6f1b0d5e52b8ad0ac94a65c2..e476340d14972b8cc7c82d934af9912974c78eb8 100644 (file)
--- a/fcron.c
+++ b/fcron.c
@@ -30,6 +30,7 @@
 #include "temp_file.h"
 #include "fcronconf.h"
 #include "select.h"
+#include "suspend.h"
 #ifdef FCRONDYN
 #include "fcrondyn_svr.h"
 #endif
@@ -37,6 +38,7 @@
 
 void main_loop(void);
 void check_signal(void);
+void reset_sig_cont(void);
 void info(void);
 void usage(void);
 void print_schedule(void);
@@ -46,8 +48,6 @@ RETSIGTYPE sigchild_handler(int x);
 RETSIGTYPE sigusr1_handler(int x);
 RETSIGTYPE sigusr2_handler(int x);
 RETSIGTYPE sigcont_handler(int x);
-long int read_suspend_duration(time_t slept_from);
-void check_suspend(time_t slept_from, time_t nwt);
 int parseopt(int argc, char *argv[]);
 void get_lock(void);
 int is_system_reboot(void);
@@ -533,161 +533,6 @@ sigcont_handler(int x)
     sig_cont = 1;
 }
 
-long int
-read_suspend_duration(time_t slept_from)
-  /* Return the amount of time the system was suspended (to mem or disk),
-   * as read from suspendfile.
-   * Return 0 on error.
-   *
-   * The idea is that:
-   * 1) the OS sends the STOP signal to the main fcron process when suspending
-   * 2) the OS writes the suspend duration (as a string) into suspendfile,
-   *    and then sends the CONT signal to the main fcron process when resuming.
-   *
-   * The main reason to do it this way instead of killing fcron and restarting
-   * it on resume is to better handle jobs that may already be running.
-   * (e.g. don't run them again when the machine resumes) */
-{
-    int fd = -1;
-    char buf[TERM_LEN];
-    int read_len = 0;
-    long int suspend_duration = 0;      /* default value to return on error */
-    struct stat s;
-
-    if (sig_cont <= 0) {
-        /* signal not raised -- do nothing */
-        return 0;
-    }
-
-    /* the signal CONT was raised: reset the signal and check the suspendfile */
-    sig_cont = 0;
-
-    fd = open(suspendfile, O_RDONLY | O_NONBLOCK);
-    if (fd == -1) {
-        /* If the file doesn't exist, then we assume the user/system
-         * did a manual 'kill -STOP' / 'kill -CONT' and doesn't intend
-         * for fcron to account for any suspend time.
-         * This is not considered as an error. */
-        if (errno != ENOENT) {
-            error_e("Could not open suspend file '%s'", suspendfile);
-        }
-        goto cleanup_return;
-    }
-
-    /* check the file is a 'normal' file (e.g. not a link) and only writable
-     * by root -- don't allow attacker to affect job schedules,
-     * or delete the suspendfile */
-    if (fstat(fd, &s) < 0) {
-        error_e("could not fstat() suspend file '%s'", suspendfile);
-        goto cleanup_return;
-    }
-    if (!S_ISREG(s.st_mode) || s.st_nlink != 1) {
-        error_e("suspend file %s is not a regular file", suspendfile);
-        goto cleanup_return;
-    }
-
-    if (s.st_mode & S_IWOTH || s.st_uid != rootuid || s.st_gid != rootgid) {
-        error("suspend file %s must be owned by %s:%s and not writable by"
-              " others.", suspendfile, ROOTNAME, ROOTGROUP);
-        goto cleanup_return;
-    }
-
-    /* read the content of the suspendfile into the buffer */
-    read_len = read(fd, buf, sizeof(buf) - 1);
-    if (read_len < 0) {
-        /* we have to run this immediately or errno may be changed */
-        error_e("Could not read suspend file '%s'", suspendfile);
-        goto unlink_cleanup_return;
-    }
-    if (read_len < 0) {
-        goto unlink_cleanup_return;
-    }
-    buf[read_len] = '\0';
-
-    errno = 0;
-    suspend_duration = strtol(buf, NULL, 10);
-    if (errno != 0) {
-        error_e("Count not parse suspend duration '%s'", buf);
-        suspend_duration = 0;
-        goto unlink_cleanup_return;
-    }
-    else if (suspend_duration < 0) {
-        warn("Read negative suspend_duration (%ld): ignoring.");
-        suspend_duration = 0;
-        goto unlink_cleanup_return;
-    }
-    else {
-        debug("Read suspend_duration of '%ld' from suspend file '%s'",
-              suspend_duration, suspendfile);
-
-        if (now < slept_from + suspend_duration) {
-            long int time_slept = now - slept_from;
-
-            /* we can have a couple of seconds more due to rounding up,
-             * but anything more should be an invalid value in suspendfile */
-            explain("Suspend duration %lds in suspend file '%s' is longer than "
-                    "we slept.  This could be due to rounding. "
-                    "Reverting to time slept %lds.",
-                    suspend_duration, suspendfile, time_slept);
-            suspend_duration = time_slept;
-        }
-    }
-
- unlink_cleanup_return:
-    if (unlink(suspendfile) < 0) {
-        warn_e("Could not remove suspend file '%s'", suspendfile);
-        return 0;
-    }
-
- cleanup_return:
-    if (fd >= 0 && xclose(&fd) < 0) {
-        warn_e("Could not xclose() suspend file '%s'", suspendfile);
-    }
-
-#ifdef HAVE_SIGNAL
-    signal(SIGCONT, sigcont_handler);
-    siginterrupt(SIGCONT, 0);
-#endif
-
-    return suspend_duration;
-
-}
-
-void
-check_suspend(time_t slept_from, time_t nwt)
-    /* Check if the machine was suspended (to mem or disk), and if so
-     * reschedule jobs accordingly */
-{
-    long int suspend_duration;  /* amount of time the system was suspended */
-    long int time_diff;         /* estimate of suspend_duration (as fallback) */
-
-    suspend_duration = read_suspend_duration(slept_from);
-
-    /* Also check if there was an unaccounted sleep duration, in case
-     * the OS is not configured to let fcron properly know about suspends
-     * via suspendfile.
-     * This is not perfect as we may miss some suspend time if fcron
-     * is woken up before the timer expiry, e.g. due to a signal
-     * or activity on a socket (fcrondyn).
-     * NOTE: the +5 second is arbitrary -- just a way to make sure
-     * we don't get any false positive.  If the suspend or hibernate
-     * is very short it seems fine to simply ignore it anyway */
-    time_diff = now - nwt;
-    if (suspend_duration <= 0 && time_diff > 5) {
-        suspend_duration = time_diff;
-    }
-
-    if (suspend_duration > 0) {
-        long int actual_sleep = now - slept_from;
-        long int scheduled_sleep = nwt - slept_from;
-        explain("suspend/hibernate detected: we woke up after %lus"
-                " instead of %lus. The system was suspended for %lus.",
-                actual_sleep, scheduled_sleep, suspend_duration);
-        reschedule_all_on_resume(suspend_duration);
-    }
-}
-
-
 int
 main(int argc, char **argv)
 {
@@ -899,6 +744,7 @@ check_signal()
         siginterrupt(SIGCHLD, 0);
 #endif
     }
+
     if (sig_conf > 0) {
 
         if (sig_conf == 1) {
@@ -921,6 +767,7 @@ check_signal()
         }
 
     }
+
     if (sig_debug > 0) {
         print_schedule();
         debug_opt = (debug_opt > 0) ? 0 : 1;
@@ -934,6 +781,33 @@ check_signal()
 
 }
 
+void
+reset_sig_cont(void)
+   /* reinitialize the SIGCONT handler if it was raised */
+{
+    if (sig_cont > 0) {
+
+        sig_cont = 0;
+#ifdef HAVE_SIGNAL
+        signal(SIGCONT, sigcont_handler);
+        siginterrupt(SIGCONT, 0);
+#endif
+    }
+}
+
+#ifdef HAVE_GETTIMEOFDAY
+#define debug_print_tstamp(where_str) \
+        { \
+        if (debug_opt) { \
+            gettimeofday(&now_tv, NULL); \
+            debug(where_str ": now=%ld now_tv sec=%ld usec=%ld", now, now_tv.tv_sec, \
+                  now_tv.tv_usec); \
+        } \
+    }
+#else
+#define debug_print_tstamp(where_str) { ; }
+#endif
+
 void
 main_loop()
   /* main loop - get the time to sleep until next job execution,
@@ -1008,7 +882,6 @@ main_loop()
                 ("We'll sleep for a tiny bit to avoid any risk of infinite loop");
             sleep_tv.tv_usec = min_sleep_usec;
         }
-        /* note: read_set is set in socket.c */
         debug("nwt=%s, sleep sec=%ld, usec=%ld", ctime(&nwt), sleep_tv.tv_sec,
               sleep_tv.tv_usec);
         select_call(&main_select, &sleep_tv);
@@ -1020,37 +893,17 @@ main_loop()
 
         debug("\n");
         now = my_time();
-#ifdef HAVE_GETTIMEOFDAY
-        if (debug_opt) {
-            gettimeofday(&now_tv, NULL);
-            debug("now=%ld now_tv sec=%ld usec=%ld", now, now_tv.tv_sec,
-                  now_tv.tv_usec);
-        }
-#endif
+        debug_print_tstamp("just woke up")
 
-        debug("\n");
-
-        check_signal();
+            check_signal();
+        check_suspend(slept_from, nwt, &sig_cont);
+        reset_sig_cont();
+        debug_print_tstamp("after check_signal and suspend")
 
-        check_suspend(slept_from, nwt);
-#ifdef HAVE_GETTIMEOFDAY
-        if (debug_opt) {
-            gettimeofday(&now_tv, NULL);
-            debug("after check_signal: now_tv sec=%ld usec=%ld", now_tv.tv_sec,
-                  now_tv.tv_usec);
-        }
-#endif
+            test_jobs();
+        debug_print_tstamp("after test_jobs")
 
-        test_jobs();
-#ifdef HAVE_GETTIMEOFDAY
-        if (debug_opt) {
-            gettimeofday(&now_tv, NULL);
-            debug("after test_jobs: now_tv sec=%ld usec=%ld", now_tv.tv_sec,
-                  now_tv.tv_usec);
-        }
-#endif
-
-        while (serial_num > 0 && serial_running < serial_max_running) {
+            while (serial_num > 0 && serial_running < serial_max_running) {
             run_serial_job();
         }
 
@@ -1064,29 +917,16 @@ main_loop()
             /* save all files */
             save_file(NULL);
         }
-#ifdef HAVE_GETTIMEOFDAY
-        if (debug_opt) {
-            gettimeofday(&now_tv, NULL);
-            debug("after save: now_tv sec=%ld usec=%ld", now_tv.tv_sec,
-                  now_tv.tv_usec);
-        }
-#endif
-
+        debug_print_tstamp("after save")
 #if defined(FCRONDYN) && defined(HAVE_GETTIMEOFDAY)
-        /* check if there's a new connection, a new command to answer, etc ... */
-        /* we do that *after* other checks, to avoid Denial Of Service attacks */
-        fcrondyn_socket_check(&main_select);
+            /* check if there's a new connection, a new command to answer, etc ... */
+            /* we do that *after* other checks, to avoid Denial Of Service attacks */
+            fcrondyn_socket_check(&main_select);
 #endif
 
         nwt = check_lavg(save);
-#ifdef HAVE_GETTIMEOFDAY
-        if (debug_opt) {
-            gettimeofday(&now_tv, NULL);
-            debug("after check_lavg: now_tv sec=%ld usec=%ld", now_tv.tv_sec,
-                  now_tv.tv_usec);
-        }
-#endif
-        debug("next wake time : %s", ctime(&nwt));
+        debug_print_tstamp("after check_lavg")
+            debug("next wake time : %s", ctime(&nwt));
 
         check_signal();
 
index 497b69ef25060726e7cd336156836783340c448c..461ef5f712f36b1e0dd7dbeddd2276bdb19bf92e 100644 (file)
@@ -33,7 +33,7 @@
 
 
 void remove_connection(struct fcrondyn_cl **client,
-                       struct fcrondyn_cl *prev_client, select_instance *si);
+                       struct fcrondyn_cl *prev_client, select_instance * si);
 void exe_cmd(struct fcrondyn_cl *client);
 #ifdef SO_PEERCRED              /* linux */
 void auth_client_so_peercred(struct fcrondyn_cl *client);
@@ -104,7 +104,7 @@ char err_others_nallowed_str[] =
 
 
 void
-fcrondyn_socket_init(select_instance *si)
+fcrondyn_socket_init(select_instance * si)
     /* do everything needed to get a working listening socket */
 {
     struct sockaddr_un addr;
@@ -842,7 +842,8 @@ exe_cmd(struct fcrondyn_cl *client)
 }
 
 void
-remove_connection(struct fcrondyn_cl **client, struct fcrondyn_cl *prev_client, select_instance *si)
+remove_connection(struct fcrondyn_cl **client, struct fcrondyn_cl *prev_client,
+                  select_instance * si)
 /* close the connection, remove it from the list 
 and make client points to the next entry */
 {
@@ -866,7 +867,7 @@ and make client points to the next entry */
 }
 
 void
-fcrondyn_socket_check(select_instance *si)
+fcrondyn_socket_check(select_instance * si)
     /* check for new connection, command, connection closed */
 {
     int fd = -1, avoid_fd = -1;
@@ -984,7 +985,7 @@ fcrondyn_socket_check(select_instance *si)
 
 
 void
-fcrondyn_socket_close(select_instance *si)
+fcrondyn_socket_close(select_instance * si)
     /* close connections, close socket, remove socket file.
      * If si is not NULL, then remove the fds from si's readfds */
 {
index a75f531f832a58081ad71028f58af410201fbb32..4e6e010e461642ad190ae5a5e24cf9e6d3ba4874 100644 (file)
@@ -35,9 +35,9 @@
 #endif
 
 /* functions prototypes */
-extern void fcrondyn_socket_init(select_instance *si);
-extern void fcrondyn_socket_check(select_instance *si);
-extern void fcrondyn_socket_close(select_instance *si);
+extern void fcrondyn_socket_init(select_instance * si);
+extern void fcrondyn_socket_check(select_instance * si);
+extern void fcrondyn_socket_close(select_instance * si);
 
 /* struct used by fcron : */
 typedef struct fcrondyn_cl {
diff --git a/log.h b/log.h
index f928a4d702384c9e6feccd4466c4c75b707a30c9..d9276a102f0bf898516580a68bd405842726040e 100644 (file)
--- a/log.h
+++ b/log.h
@@ -26,7 +26,7 @@
 #define __LOG_H__
 
 #ifdef HAVE_LIBPAM
-#   include "pam.h"
+#include "pam.h"
 #endif
 
 extern char debug_opt;
index 7cacfc83115382a2afdfcae292119de19b8e05b7..80607e7ef3c6324153c1563fe0aad55f393b8295 100644 (file)
--- a/select.c
+++ b/select.c
@@ -39,7 +39,7 @@ select_init(struct select_instance *si)
 }
 
 void
-select_add_read(select_instance *si, int fd)
+select_add_read(select_instance * si, int fd)
     /* Add a fd to the read set */
 {
     FD_SET(fd, &si->__readfds_master);
@@ -51,7 +51,7 @@ select_add_read(select_instance *si, int fd)
 }
 
 void
-select_rm_read(select_instance *si, int fd)
+select_rm_read(select_instance * si, int fd)
     /* remove a fd to the read set */
 {
     FD_CLR(fd, &si->__readfds_master);
@@ -59,9 +59,9 @@ select_rm_read(select_instance *si, int fd)
         /* fds in the fd_set may not be continuous, so we need
          * to check them all to find the new max */
         int i;
-        for (i=fd; i >= 0; i--) {
+        for (i = fd; i >= 0; i--) {
             if (FD_ISSET(i, &si->__readfds_master))
-            si->__fd_max = i;
+                si->__fd_max = i;
         }
     }
     debug("select: removed fd %d (fd_max=%d)", fd, si->__fd_max);
@@ -79,4 +79,3 @@ select_call(struct select_instance *si, struct timeval *timeout)
         die_e("select returned %d", errno);
     }
 }
-
index 21dedbd42ada758ba9e819540ded9147ed6161a2..b1c18cca6b431f59649af794a6bc9d0f6f970bd1 100644 (file)
--- a/select.h
+++ b/select.h
 #include "config.h"
 
 #ifdef HAVE_SYS_SELECT_H
-#   include <sys/select.h>
+#include <sys/select.h>
 #endif
 #ifdef HAVE_SYS_TYPES_H
-#   include <sys/types.h>
+#include <sys/types.h>
 #endif
 
 
 typedef struct select_instance {
     /* public (read-only) */
-    int retcode; /* return value of the last call of select() */
-    fd_set readfds; /* to check the status of fds after select_call */
+    int retcode;                /* return value of the last call of select() */
+    fd_set readfds;             /* to check the status of fds after select_call */
     /* private */
-    fd_set __readfds_master; /* select() modifies readfds, so we maintain a list in parallel */
+    fd_set __readfds_master;    /* select() modifies readfds, so we maintain a list in parallel */
     int __fd_max;
 } select_instance;
 
 /* functions prototypes */
 extern void select_init(struct select_instance *si);
-extern void select_add_read(select_instance *si, int fd);
-extern void select_rm_read(select_instance *si, int fd);
+extern void select_add_read(select_instance * si, int fd);
+extern void select_rm_read(select_instance * si, int fd);
 extern void select_call(struct select_instance *si, struct timeval *timeout);
 
 #endif                          /* __SELECT_H__ */
diff --git a/suspend.c b/suspend.c
new file mode 100644 (file)
index 0000000..1aedcb1
--- /dev/null
+++ b/suspend.c
@@ -0,0 +1,174 @@
+/*
+ * FCRON - periodic command scheduler 
+ *
+ *  Copyright 2000-2014 Thibault Godouet <fcron@free.fr>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * 
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ *  The GNU General Public License can also be found in the file
+ *  `LICENSE' that comes with the fcron source distribution.
+ */
+
+/* code to handle system suspend/hibernate and resume */
+
+#include "suspend.h"
+#include "global.h"
+#include "fcronconf.h"
+#include "fcron.h"
+#include "database.h"
+
+long int
+read_suspend_duration(time_t slept_from)
+  /* Return the amount of time the system was suspended (to mem or disk),
+   * as read from suspendfile.
+   * Return 0 on error.
+   *
+   * The idea is that:
+   * 1) the OS sends the STOP signal to the main fcron process when suspending
+   * 2) the OS writes the suspend duration (as a string) into suspendfile,
+   *    and then sends the CONT signal to the main fcron process when resuming.
+   *
+   * The main reason to do it this way instead of killing fcron and restarting
+   * it on resume is to better handle jobs that may already be running.
+   * (e.g. don't run them again when the machine resumes) */
+{
+    int fd = -1;
+    char buf[TERM_LEN];
+    int read_len = 0;
+    long int suspend_duration = 0;      /* default value to return on error */
+    struct stat s;
+
+    fd = open(suspendfile, O_RDONLY | O_NONBLOCK);
+    if (fd == -1) {
+        /* If the file doesn't exist, then we assume the user/system
+         * did a manual 'kill -STOP' / 'kill -CONT' and doesn't intend
+         * for fcron to account for any suspend time.
+         * This is not considered as an error. */
+        if (errno != ENOENT) {
+            error_e("Could not open suspend file '%s'", suspendfile);
+        }
+        goto cleanup_return;
+    }
+
+    /* check the file is a 'normal' file (e.g. not a link) and only writable
+     * by root -- don't allow attacker to affect job schedules,
+     * or delete the suspendfile */
+    if (fstat(fd, &s) < 0) {
+        error_e("could not fstat() suspend file '%s'", suspendfile);
+        goto cleanup_return;
+    }
+    if (!S_ISREG(s.st_mode) || s.st_nlink != 1) {
+        error_e("suspend file %s is not a regular file", suspendfile);
+        goto cleanup_return;
+    }
+
+    if (s.st_mode & S_IWOTH || s.st_uid != rootuid || s.st_gid != rootgid) {
+        error("suspend file %s must be owned by %s:%s and not writable by"
+              " others.", suspendfile, ROOTNAME, ROOTGROUP);
+        goto cleanup_return;
+    }
+
+    /* read the content of the suspendfile into the buffer */
+    read_len = read(fd, buf, sizeof(buf) - 1);
+    if (read_len < 0) {
+        /* we have to run this immediately or errno may be changed */
+        error_e("Could not read suspend file '%s'", suspendfile);
+        goto unlink_cleanup_return;
+    }
+    if (read_len < 0) {
+        goto unlink_cleanup_return;
+    }
+    buf[read_len] = '\0';
+
+    errno = 0;
+    suspend_duration = strtol(buf, NULL, 10);
+    if (errno != 0) {
+        error_e("Count not parse suspend duration '%s'", buf);
+        suspend_duration = 0;
+        goto unlink_cleanup_return;
+    }
+    else if (suspend_duration < 0) {
+        warn("Read negative suspend_duration (%ld): ignoring.");
+        suspend_duration = 0;
+        goto unlink_cleanup_return;
+    }
+    else {
+        debug("Read suspend_duration of '%ld' from suspend file '%s'",
+              suspend_duration, suspendfile);
+
+        if (now < slept_from + suspend_duration) {
+            long int time_slept = now - slept_from;
+
+            /* we can have a couple of seconds more due to rounding up,
+             * but anything more should be an invalid value in suspendfile */
+            explain("Suspend duration %lds in suspend file '%s' is longer than "
+                    "we slept.  This could be due to rounding. "
+                    "Reverting to time slept %lds.",
+                    suspend_duration, suspendfile, time_slept);
+            suspend_duration = time_slept;
+        }
+    }
+
+ unlink_cleanup_return:
+    if (unlink(suspendfile) < 0) {
+        warn_e("Could not remove suspend file '%s'", suspendfile);
+        return 0;
+    }
+
+ cleanup_return:
+    if (fd >= 0 && xclose(&fd) < 0) {
+        warn_e("Could not xclose() suspend file '%s'", suspendfile);
+    }
+
+    return suspend_duration;
+
+}
+
+void
+check_suspend(time_t slept_from, time_t nwt, char *sig_cont)
+    /* Check if the machine was suspended (to mem or disk), and if so
+     * reschedule jobs accordingly */
+{
+    long int suspend_duration;  /* amount of time the system was suspended */
+    long int time_diff;         /* estimate of suspend_duration (as fallback) */
+
+    if (*sig_cont > 0) {
+        /* the signal CONT was raised, check the suspendfile */
+        suspend_duration = read_suspend_duration(slept_from);
+    }
+
+    /* Also check if there was an unaccounted sleep duration, in case
+     * the OS is not configured to let fcron properly know about suspends
+     * via suspendfile.
+     * This is not perfect as we may miss some suspend time if fcron
+     * is woken up before the timer expiry, e.g. due to a signal
+     * or activity on a socket (fcrondyn).
+     * NOTE: the +5 second is arbitrary -- just a way to make sure
+     * we don't get any false positive.  If the suspend or hibernate
+     * is very short it seems fine to simply ignore it anyway */
+    time_diff = now - nwt;
+    if (suspend_duration <= 0 && time_diff > 5) {
+        suspend_duration = time_diff;
+    }
+
+    if (suspend_duration > 0) {
+        long int actual_sleep = now - slept_from;
+        long int scheduled_sleep = nwt - slept_from;
+        explain("suspend/hibernate detected: we woke up after %lus"
+                " instead of %lus. The system was suspended for %lus.",
+                actual_sleep, scheduled_sleep, suspend_duration);
+        reschedule_all_on_resume(suspend_duration);
+    }
+}
diff --git a/suspend.h b/suspend.h
new file mode 100644 (file)
index 0000000..247a657
--- /dev/null
+++ b/suspend.h
@@ -0,0 +1,36 @@
+/*
+ * FCRON - periodic command scheduler 
+ *
+ *  Copyright 2000-2014 Thibault Godouet <fcron@free.fr>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * 
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ *  The GNU General Public License can also be found in the file
+ *  `LICENSE' that comes with the fcron source distribution.
+ */
+
+/* code to handle system suspend/hibernate and resume */
+
+#ifndef __SUSPEND_H__
+#define __SUSPEND_H__
+
+#include "config.h"
+#include "global.h"
+
+/* functions prototypes */
+extern long int read_suspend_duration(time_t slept_from);
+extern void check_suspend(time_t slept_from, time_t nwt, char *sig_cont);
+
+#endif                          /* __SUSPEND_H__ */