]> granicus.if.org Git - cronie/commitdiff
Rewrited inotify support.
authorMarcela Mašláňová <mmaslano@redhat.com>
Fri, 14 Mar 2008 14:46:01 +0000 (15:46 +0100)
committerMarcela Mašláňová <mmaslano@redhat.com>
Fri, 14 Mar 2008 14:46:01 +0000 (15:46 +0100)
configure.ac
src/cron.c
src/cron.h
src/crontab.c
src/database.c
src/externs.h
src/funcs.h
src/structs.h

index ebacb1ab675a60650d9edb7d2b123dba4a2d9119..052f2943727969427214ad0da33489cb1f3c8ef0 100644 (file)
@@ -23,7 +23,8 @@ AC_CHECK_HEADERS( \
         selinux/selinux.h \
         stddef.h \
         stdint.h \
-               sys/audit.h \
+        sys/audit.h \
+        sys/inotify.h \
         sys/stat.h \
         sys/stream.h \
         sys/stropts.h \
@@ -129,7 +130,18 @@ AC_SUBST(DAEMON_GROUPNAME)
 #              AC_SUBST(initdir)
 #      fi ]
 #)
-                                                        
+
+# Check whether inotify is accepted
+AC_ARG_WITH(inotify,
+   [AS_HELP_STRING([--with-inotify], [ Enable inotify support])],
+   [ if test "x$withval" != "xno" ; then
+       AC_DEFINE(WITH_INOTIFY,1,[Define if you want inotify support.])
+       AC_CHECK_HEADER([sys/inotify.h], , AC_MSG_ERROR(Inotify support requires sys/inotify.h header))
+       AC_CHECK_FUNCS(inotify_init inotify_add_watch)
+     fi
+   ]
+)
+                                                    
 # Check whether user wants SELinux support
 SELINUX_MSG="no"
 LIBSELINUX=""
index 1d34d5645ea012c3b212ce2cd8ee6b147b7a8b26..759505addb777bb93d1080e90f69affaceb90d65 100644 (file)
@@ -27,6 +27,12 @@ static char rcsid[] = "$Id: cron.c,v 1.12 2004/01/23 18:56:42 vixie Exp $";
 
 #include <cron.h>
 
+#if defined WITH_INOTIFY
+int inotify_enabled;
+#else
+#define inotify_enabled 0
+#endif
+
 enum timejump { negative, small, medium, large };
 
 static void    usage(void),
@@ -44,6 +50,48 @@ static       volatile sig_atomic_t   got_sighup, got_sigchld;
 static int                     timeRunning, virtualTime, clockTime;
 static long                    GMToff;
 
+#if defined WITH_INOTIFY
+int wd1, wd2, wd3, wd4;
+
+void
+set_cron_watched(int fd) {
+    int ret1, ret2, ret3;
+
+    wd1 = inotify_add_watch(fd, CRONDIR, IN_MODIFY | IN_DELETE | IN_CREATE);
+    if (wd1 < 0) 
+        log_it("CRON",getpid(),"This directory can't be watched",strerror(errno));
+
+    wd2 = inotify_add_watch(fd, RH_CROND_DIR, IN_MODIFY | IN_DELETE | IN_CREATE);
+    if (wd2 < 0) 
+        log_it("CRON",getpid(),"This directory can't be watched",strerror(errno));
+
+    wd3 = inotify_add_watch(fd, SYSCRONTAB, IN_MODIFY | IN_DELETE | IN_CREATE);
+    if (wd3 < 0) 
+        log_it("CRON",getpid(),"This file can't be watched ",strerror(errno));
+
+    wd4 = inotify_add_watch(fd, "/var/spool/cron/", IN_MODIFY | IN_DELETE | IN_CREATE);
+    if (wd4 < 0)
+        log_it("CRON",getpid(),"This file can't be watched ",strerror(errno));
+
+    if (wd1 <0 || wd2<0 || wd3<0 || wd4<0) {
+               inotify_enabled = 0;
+        syslog(LOG_INFO, "CRON (%s) ERROR: run without inotify support");
+    }
+    else
+       inotify_enabled = 1;
+}
+
+void
+set_cron_unwatched(int fd) {
+    int ret1, ret2, ret3, ret4;
+
+    ret1 = inotify_rm_watch(fd, wd1);
+    ret2 = inotify_rm_watch(fd, wd2);
+    ret3 = inotify_rm_watch(fd, wd3);
+    ret4 = inotify_rm_watch(fd, wd4);
+}
+#endif
+
 static void
 usage(void) {
        const char **dflags;
@@ -62,6 +110,13 @@ main(int argc, char *argv[]) {
        int fd;
        char *cs;
 
+#if defined WITH_INOTIFY
+       int fildes;
+       fildes = inotify_init();
+       if (fildes < 0)
+               perror ("inotify_init");
+#endif
+
        ProgramName = argv[0];
 
        setlocale(LC_ALL, "");
@@ -90,6 +145,9 @@ main(int argc, char *argv[]) {
 
        acquire_daemonlock(0);
        set_cron_uid();
+#if defined WITH_INOTIFY
+       set_cron_watched(fildes);
+#endif
        set_cron_cwd();
 
        if (putenv("PATH="_PATH_DEFPATH) < 0) {
@@ -132,7 +190,14 @@ main(int argc, char *argv[]) {
                                if (fd != STDERR)
                                        (void) close(fd);
                        }
-                       log_it("CRON",getpid(),"STARTUP",PACKAGE_VERSION);
+                       if (inotify_enabled) {
+#if defined WITH_INOTIFY
+                               log_it("CRON",getpid(),"STARTUP INOTIFY",PACKAGE_VERSION);
+#endif
+                       }
+                       else {
+                               log_it("CRON",getpid(),"STARTUP",PACKAGE_VERSION);
+                       }
                        break;
                default:
                        /* parent process should just die */
@@ -143,8 +208,15 @@ main(int argc, char *argv[]) {
        acquire_daemonlock(0);
        database.head = NULL;
        database.tail = NULL;
-       database.mtime = (time_t) 0;
-       load_database(&database);
+       if (inotify_enabled) {
+#if defined WITH_INOTIFY
+               load_inotify_database(&database, fildes);
+#endif
+       }
+       else {
+               database.mtime = (time_t) 0;
+               load_database(&database);
+       }
        set_time(TRUE);
        run_reboot_jobs(&database);
        timeRunning = virtualTime = clockTime;
@@ -175,8 +247,14 @@ main(int argc, char *argv[]) {
                 * clock.  Classify the change into one of 4 cases.
                 */
                timeDiff = timeRunning - virtualTime;
-               
-               load_database(&database);
+               if (inotify_enabled) {
+#if defined WITH_INOTIFY
+                       check_inotify_database(&database, fildes);
+#endif
+               }
+               else
+                       load_database(&database);
+
                /* shortcut for the most common case */
                if (timeDiff == 1) {
                        virtualTime = timeRunning;
@@ -205,8 +283,7 @@ main(int argc, char *argv[]) {
                                        if (job_runqueue())
                                                sleep(10);
                                        virtualTime++;
-                                       find_jobs(virtualTime, &database,
-                                           TRUE, TRUE);
+                                       find_jobs(virtualTime, &database, TRUE, TRUE);
                                } while (virtualTime < timeRunning);
                                break;
 
@@ -270,6 +347,7 @@ main(int argc, char *argv[]) {
                /* Check to see if we received a signal while running jobs. */
                if (got_sighup) {
                        got_sighup = 0;
+               if (!inotify_enabled)
                        database.mtime = (time_t) 0;
                        log_close();
                }
@@ -278,6 +356,14 @@ main(int argc, char *argv[]) {
                        sigchld_reaper();
                }
        }
+#if defined WITH_INOTIFY
+       set_cron_unwatched(fildes);
+
+       int ret;
+       ret = close(fildes);
+       if (ret)
+       perror ("close");
+#endif
 }
 
 static void
@@ -327,7 +413,6 @@ find_jobs(int vtime, cron_db *db, int doWild, int doNonWild) {
        Debug(DSCH, ("[%ld] tick(%d,%d,%d,%d,%d) %s %s\n",
                     (long)getpid(), minute, hour, dom, month, dow,
                     doWild?" ":"No wildcard",doNonWild?" ":"Wildcard only"))
-
        /* the dom/dow situation is odd.  '* * 1,15 * Sun' will run on the
         * first and fifteenth AND every Sunday;  '* * * * Sun' will run *only*
         * on Sundays;  '* * 1,15 * *' will run *only* the 1st and 15th.  this
@@ -339,9 +424,9 @@ find_jobs(int vtime, cron_db *db, int doWild, int doNonWild) {
                        Debug(DSCH|DEXT, ("user [%s:%ld:%ld:...] cmd=\"%s\"\n",
                            e->pwd->pw_name, (long)e->pwd->pw_uid,
                            (long)e->pwd->pw_gid, e->cmd))
-
                        job_tz = env_get("CRON_TZ", e->envp);
                        maketime(job_tz, orig_tz);
+                       /* here we test whether time is NOW */
                        if (bit_test(e->minute, minute) &&
                            bit_test(e->hour, hour) &&
                            bit_test(e->month, month) &&
@@ -353,7 +438,7 @@ find_jobs(int vtime, cron_db *db, int doWild, int doNonWild) {
                                if ((doNonWild &&
                                    !(e->flags & (MIN_STAR|HR_STAR))) || 
                                    (doWild && (e->flags & (MIN_STAR|HR_STAR))))
-                                       job_add(e, u);
+                                       job_add(e, u);  /*will add job, if it isn't in queue already for NOW.*/
                        }
                }
        }
@@ -409,7 +494,8 @@ cron_sleep(int target, cron_db *db) {
                 */
                if (got_sighup) {
                        got_sighup = 0;
-                       db->mtime = (time_t) 0;
+                       if (!inotify_enabled)
+                               db->mtime = (time_t) 0;
                        log_close();
                }
                if (got_sigchld) {
index 7bc553c1954437cef3841b5da81b70479ec50f13..6ed3de16af411c8b7ca2d9725f391597a18bd4d1 100644 (file)
 #include <security/pam_appl.h>
 #endif
 
+#ifdef WITH_INOTIFY
+#include <sys/inotify.h>
+#endif
+
 #include "pathnames.h"
 #include "macros.h"
 #include "structs.h"
index 874464c68592a4af94cfea844b5e64dd6e74728d..66fc657bab1439dd361c9df7a8bc6700ae1b61e4 100644 (file)
@@ -94,7 +94,7 @@ usage(const char *msg) {
 int
 main(int argc, char *argv[]) {
        int exitstatus;
-
+       
        Pid = getpid();
        ProgramName = argv[0];
 
@@ -530,8 +530,11 @@ edit_cmd(void) {
        }
        (void)signal(SIGHUP, SIG_DFL);
        (void)signal(SIGINT, SIG_DFL);
-       (void)signal(SIGQUIT, SIG_DFL);      
+       (void)signal(SIGQUIT, SIG_DFL);
 
+       /* lstat doesn't make any harm, because 
+        * the file is stat'ed only when crontab is touched
+        */
        if (lstat(Filename, &statbuf) < 0) {
                perror("lstat");
                goto fatal;
index 10a0290165bbe98473559823362a90231ff7c5a5..06197837968cf87b59b2f3211c8d7cb57dc943ff 100644 (file)
@@ -30,6 +30,18 @@ static char rcsid[] = "$Id: database.c,v 1.7 2004/01/23 18:56:42 vixie Exp $";
 
 #define TMAX(a,b) ((a)>(b)?(a):(b))
 
+/* size of the event structure, not counting name */
+#define EVENT_SIZE  (sizeof (struct inotify_event))
+
+/* reasonable guess as to size of 1024 events */
+#define BUF_LEN        (1024 * (EVENT_SIZE + 16))
+
+#if defined WITH_INOTIFY
+/* state say if we change the crontable */
+#define RELOAD         1
+void unlink_inotify_database(cron_db *, cron_db , int);
+#endif
+
 static void            process_crontab(const char *, const char *,
                                        const char *, struct stat *,
                                        cron_db *, cron_db *);
@@ -40,6 +52,275 @@ static int not_a_crontab( DIR_T *dp );
 static void max_mtime( char *dir_name, struct stat *max_st ); 
 /* record max mtime of any file under dir_name in max_st */
 
+#if defined WITH_INOTIFY
+void
+process_inotify_crontab(const char *uname, const char *fname, const char *tabname,
+       cron_db *new_db, cron_db *old_db, int fd, int state) {
+       struct passwd *pw = NULL;
+       int crontab_fd = OK - 1;
+       user *u;
+       int crond_crontab = (fname == NULL) && (strcmp(tabname, SYSCRONTAB) != 0);
+
+       if (fname == NULL) {
+               /* must be set to something for logging purposes.
+               */
+               fname = "*system*";
+       } else if ((pw = getpwnam(uname)) == NULL) {
+               /* file doesn't have a user in passwd file.
+               */
+               log_it("CRON", getpid(), "ORPHAN", "no passwd entry");
+               goto next_crontab;
+       }
+
+/* need to rewrite this part because of statbuf related to stat'ing */
+/*
+       if (PermitAnyCrontab == 0) {
+       }
+*/
+       Debug(DLOAD, ("\t%s:", fname))
+       u = find_user(old_db, fname, crond_crontab ? tabname : NULL );  /* goes only through database in memory */
+
+       /* in first run is database empty and we need jump this section */
+       if (u != NULL) {
+               /* if crontab has not changed since we last read it
+               * in, then we can just use our existing entry.
+               */
+               /* 6 because we want string reload or none */
+               if (state != RELOAD) {
+                       Debug(DLOAD, (" [no change, using old data]"))
+                       unlink_user(old_db, u);
+                       link_user(new_db, u);
+                       goto next_crontab;
+               }
+               /* before we fall through to the code that will reload
+               * the user, let's deallocate and unlink the user in
+               * the old database.  This is more a point of memory
+               * efficiency than anything else, since all leftover
+               * users will be deleted from the old database when
+               * we finish with the crontab...
+               */
+
+               Debug(DLOAD, (" [delete old data]"))
+               unlink_user(old_db, u);
+               free_user(u);
+               Debug(DSCH, ("RELOAD %s\n", tabname))
+       }
+       if ((crontab_fd = open(tabname, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0)) < OK) {
+               log_it("CRON", getpid(), "CAN'T OPEN", tabname);
+               goto next_crontab;
+       }
+
+       u = load_user(crontab_fd, pw, uname, fname, tabname);   /* touch the disk */
+       if (u != NULL)
+               link_user(new_db, u);
+
+  next_crontab:
+       if (crontab_fd >= OK) {
+               Debug(DLOAD, (" [done]\n"))
+               close(crontab_fd);
+       }
+}
+
+void
+load_inotify_database(cron_db *old_db, int fd) {
+       cron_db new_db;
+       DIR_T *dp;
+       DIR *dir;
+
+       new_db.head = new_db.tail = NULL;
+       process_inotify_crontab("root", NULL, SYSCRONTAB, &new_db, old_db, fd, RELOAD);
+
+       /* RH_CROND_DIR /etc/cron.d */
+       if (!(dir = opendir(RH_CROND_DIR))) {
+               log_it("CRON", getpid(), "OPENDIR FAILED", RH_CROND_DIR);
+               (void) exit(ERROR_EXIT);
+       }
+       while (NULL != (dp = readdir(dir))) {
+               char tabname[MAXNAMLEN+1];
+
+               if (not_a_crontab(dp))
+                       continue;
+
+               if (!glue_strings(tabname, sizeof tabname, RH_CROND_DIR, dp->d_name, '/'))
+                       continue;
+               
+               process_inotify_crontab("root", NULL, tabname, &new_db, old_db, fd, RELOAD);
+       }
+       closedir(dir);
+       /* SPOOL_DIR */
+       if (!(dir = opendir(SPOOL_DIR))) {
+               syslog(LOG_INFO, "CRON: OPENDIR FAILED %s", SPOOL_DIR);
+               (void) exit(ERROR_EXIT);
+       }
+
+       while (NULL != (dp = readdir(dir))) {
+               char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1];
+
+               if (not_a_crontab(dp))
+                       continue;
+
+               strncpy(fname, dp->d_name, MAXNAMLEN);
+
+               if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, fname, '/'))
+                       continue;
+
+               process_inotify_crontab(fname, fname, tabname, &new_db, old_db, fd, RELOAD);
+       }
+       closedir(dir);
+
+       unlink_inotify_database(old_db, new_db, fd);
+}
+
+void
+check_inotify_database(cron_db *old_db, int fd) {
+       cron_db new_db;
+       DIR_T *dp;
+       DIR *dir;
+       struct timeval time;
+       fd_set rfds;
+       int retval = 0;
+       char buf[BUF_LEN];
+
+       time.tv_sec = 1;
+       time.tv_usec = 0;
+
+       FD_ZERO(&rfds);
+       FD_SET(fd, &rfds);
+
+       retval = select(fd + 1, &rfds, NULL, NULL, &time);
+       if (retval == -1) {
+               perror("select()");
+               syslog(LOG_INFO, "CRON: select failed: %s", strerror(errno));
+       }
+       else if (FD_ISSET(fd, &rfds)) {
+               new_db.head = new_db.tail = NULL;
+               if (read(fd, buf, sizeof(buf)) == -1)
+                       log_it("CRON", getpid(), "reading problem",buf);
+               process_inotify_crontab("root", NULL, SYSCRONTAB, &new_db, old_db, fd, RELOAD);
+
+               if (!(dir = opendir(RH_CROND_DIR))) {
+                       log_it("CRON", getpid(), "OPENDIR FAILED", RH_CROND_DIR);
+                       (void) exit(ERROR_EXIT);
+               }
+               while (NULL != (dp = readdir(dir))) {
+                       char tabname[MAXNAMLEN+1];
+
+                       if (not_a_crontab(dp))
+                               continue;
+
+                       if (!glue_strings(tabname, sizeof tabname, RH_CROND_DIR, dp->d_name, '/'))
+                               continue;
+                       process_inotify_crontab("root", NULL, tabname, &new_db, old_db, fd, RELOAD);
+               }
+               closedir(dir);
+
+               if (!(dir = opendir(SPOOL_DIR))) {
+                       syslog(LOG_INFO, "CRON: OPENDIR FAILED %s", SPOOL_DIR);
+                       (void) exit(ERROR_EXIT);
+               }
+               while (NULL != (dp = readdir(dir))) {
+                       char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1];
+
+                       if (not_a_crontab(dp))
+                               continue;
+
+                       strncpy(fname, dp->d_name, MAXNAMLEN);
+
+                       if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, dp->d_name, '/'))
+                               continue;
+                       process_inotify_crontab(fname, fname, tabname, &new_db, old_db, fd, RELOAD);
+               }
+               closedir(dir);
+       }
+       else {
+               new_db.head = new_db.tail = NULL;
+               process_inotify_crontab("root", NULL, SYSCRONTAB, &new_db, old_db, fd, !RELOAD);
+               if (!(dir = opendir(RH_CROND_DIR))) {
+                       log_it("CRON", getpid(), "OPENDIR FAILED", RH_CROND_DIR);
+                       (void) exit(ERROR_EXIT);
+               }
+
+               while (NULL != (dp = readdir(dir))) {
+                       char tabname[MAXNAMLEN+1];
+
+                       if (not_a_crontab(dp))
+                               continue;
+
+                       if (!glue_strings(tabname, sizeof tabname, RH_CROND_DIR, dp->d_name, '/'))
+                               continue;
+                       process_inotify_crontab("root", NULL, tabname, &new_db, old_db, fd, !RELOAD);
+               }
+               closedir(dir);
+
+               if (!(dir = opendir(SPOOL_DIR))) {
+                       syslog(LOG_INFO, "CRON: OPENDIR FAILED %s", SPOOL_DIR);
+                       (void) exit(ERROR_EXIT);
+               }
+
+               while (NULL != (dp = readdir(dir))) {
+                       char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1];
+
+                       if (not_a_crontab(dp))
+                               continue;
+
+                       strncpy(fname, dp->d_name, MAXNAMLEN);
+
+                       if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, fname, '/'))
+                               continue;
+
+                       process_inotify_crontab(fname, fname, tabname, &new_db, old_db, fd, !RELOAD);
+               }
+               closedir(dir);
+       }
+       FD_CLR(fd, &rfds);
+
+       unlink_inotify_database(old_db, new_db, fd);
+}
+
+void
+unlink_inotify_database(cron_db *old_db, cron_db new_db, int fd) {
+       user *u, *nu;
+       /* whatever's left in the old database is now junk.
+       */
+       Debug(DLOAD, ("unlinking old database:\n"))
+       for (u = old_db->head;  u != NULL;  u = nu) {
+               Debug(DLOAD, ("\t%s\n", u->name))
+               nu = u->next;
+               unlink_user(old_db, u);
+               free_user(u);
+       }
+
+       /* overwrite the database control block with the new one.
+       */
+       *old_db = new_db;
+       Debug(DLOAD, ("load_database is done\n"))
+}
+
+/*void
+read_dir(char *dir_name, cron_db *new_db, cron_db *old_db, int fd, int state) {
+       DIR *dir;
+       DIR_T *dp;
+
+       if (!(dir = opendir(dir_name))) {
+               syslog(LOG_INFO, "CRON: OPENDIR FAILED %s", dir_name);
+               (void) exit(ERROR_EXIT);
+       }
+
+       while (NULL != (dp = readdir(dir))) {
+               char tabname[MAXNAMLEN+1];
+
+           if (not_a_crontab(dp))
+                       continue;
+               
+               if (!glue_strings(tabname, sizeof tabname, dir_name, dp->d_name, '/'))
+                       continue;
+               process_inotify_crontab("root", NULL, tabname, &new_db, old_db, fd, state);
+       }
+       closedir(dir);
+}
+*/
+#endif
+
 void
 load_database(cron_db *old_db) {
        struct stat statbuf, syscron_stat, crond_stat;
index 8d8fd94f740702da7b88d14c378c3ff25ec7e822..89aed4d52b4e6d7fd1bcd9f2551b743b4206f496 100644 (file)
@@ -27,6 +27,7 @@
 #include <sys/wait.h>
 #include <sys/fcntl.h>
 #include <sys/file.h>
+/* stat is used even, when --with-inotify */
 #include <sys/stat.h>
 
 #include <bitstring.h>
index fe54990c482d874d859f5f72f48ab022227442f3..bc82639ac350aa6c0028211b15015af27689c4dc 100644 (file)
@@ -26,7 +26,6 @@
 
 void           set_cron_uid(void),
                set_cron_cwd(void),
-               load_database(cron_db *),
                open_logfile(void),
                sigpipe_func(void),
                job_add(entry *, user *),
@@ -41,6 +40,13 @@ void         set_cron_uid(void),
                skip_comments(FILE *),
                log_it(const char *, int, const char *, const char *),
                log_close(void);
+#if defined WITH_INOTIFY
+void   load_inotify_database(cron_db *, int ),
+               set_cron_watched(int ),
+               set_cron_unwatched(int ),
+               check_inotify_database(cron_db *, int );
+#endif
+void   load_database(cron_db *);
 
 int            job_runqueue(void),
                set_debug_flags(const char *),
index b309bf8f4b9404adbb4d60e804f4b8e37cfc1416..b1b3c3773c6e14effad39504a5701151d426538a 100644 (file)
@@ -51,10 +51,10 @@ typedef     struct _entry {
 typedef        struct _user {
        struct _user    *next, *prev;   /* links */
        char            *name;
-        char            *tabname;       /* /etc/cron.d/ file name or NULL */
+       char            *tabname;       /* /etc/cron.d/ file name or NULL */
        time_t          mtime;          /* last modtime of crontab */
        entry           *crontab;       /* this person's crontab */
-        security_context_t scontext;    /* SELinux security context */
+       security_context_t      scontext;    /* SELinux security context */
 } user;
 
 typedef        struct _cron_db {