diff -up cronie-1.1/src/cron.c.inotify-fixes cronie-1.1/src/cron.c --- cronie-1.1/src/cron.c.inotify-fixes 2008-06-18 22:49:02.000000000 +0200 +++ cronie-1.1/src/cron.c 2008-06-18 23:15:15.000000000 +0200 @@ -47,48 +47,56 @@ static int timeRunning, virtualTime, c static long GMToff; #if defined WITH_INOTIFY -int wd1, wd2, wd3, wd4; -void -set_cron_watched(int fd) { - pid_t pid = getpid(); - - wd1 = inotify_add_watch(fd, CRONDIR, IN_MODIFY | IN_DELETE | IN_CREATE | IN_ATTRIB); - if (wd1 < 0) - log_it("CRON", pid, "This directory can't be watched", CRONDIR, errno); +#define NUM_WATCHES 3 - wd2 = inotify_add_watch(fd, RH_CROND_DIR, IN_MODIFY | IN_DELETE | IN_CREATE | IN_ATTRIB); - if (wd2 < 0) - log_it("CRON", pid, "This directory can't be watched", RH_CROND_DIR, errno); +int wd[NUM_WATCHES]; +const char *watchpaths[NUM_WATCHES] = { SPOOL_DIR, RH_CROND_DIR, SYSCRONTAB }; - wd3 = inotify_add_watch(fd, SYSCRONTAB, IN_MODIFY | IN_DELETE | IN_CREATE | IN_ATTRIB); - if (wd3 < 0) - log_it("CRON", pid, "This file can't be watched", SYSCRONTAB, errno); +void +set_cron_unwatched(int fd) { + int i; + + for (i = 0; i < sizeof(wd)/sizeof(wd[0]); ++i) { + if (wd[i] < 0) { + inotify_rm_watch(fd, wd[i]); + wd[i] = -1; + } + } +} - wd4 = inotify_add_watch(fd, "/var/spool/cron/", IN_MODIFY | IN_DELETE | IN_CREATE | IN_ATTRIB); - if (wd4 < 0) - log_it("CRON", pid, "This directory can't be watched", "/var/spool/cron", errno); +void +set_cron_watched(int fd) { + pid_t pid = getpid(); + int i; - if (wd1 <0 || wd2<0 || wd3<0 || wd4<0) { + if (fd < 0) { inotify_enabled = 0; - log_it("CRON", pid, "INFO", "running without inotify support", 0); + return; } - else - inotify_enabled = 1; -} -void -set_cron_unwatched(int fd) { - int ret1, ret2, ret3, ret4; + for (i = 0; i < sizeof(wd)/sizeof(wd[0]); ++i) { + int w; + + w = inotify_add_watch(fd, watchpaths[i], + IN_CLOSE_WRITE | IN_ATTRIB | IN_MOVED_TO | IN_MOVED_FROM | IN_MOVE_SELF | IN_DELETE); + if (w < 0) { + if (wd[i] != -1) { + log_it("CRON", pid, "This directory or file can't be watched", watchpaths[i], errno); + log_it("CRON", pid, "INFO", "running without inotify support", 0); + } + inotify_enabled = 0; + set_cron_unwatched(fd); + return; + } + wd[i] = w; + } - if (wd1 >= 0) - ret1 = inotify_rm_watch(fd, wd1); - if (wd2 >= 0) - ret2 = inotify_rm_watch(fd, wd2); - if (wd3 >= 0) - ret3 = inotify_rm_watch(fd, wd3); - if (wd4 >= 0) - ret4 = inotify_rm_watch(fd, wd4); + if (!inotify_enabled) { + log_it("CRON", pid, "INFO", "running with inotify support", 0); + } + + inotify_enabled = 1; } #endif @@ -110,12 +118,8 @@ main(int argc, char *argv[]) { int fd; char *cs; pid_t pid = getpid(); - #if defined WITH_INOTIFY - int fildes; - fildes = inotify_init(); - if (fildes < 0) - log_it("CRON", pid, "INFO", "Inotify init failed", errno); + int i; #endif ProgramName = argv[0]; @@ -146,9 +150,6 @@ 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) { @@ -191,10 +192,7 @@ main(int argc, char *argv[]) { if (fd != STDERR) (void) close(fd); } - if (inotify_enabled) - log_it("CRON", getpid(), "STARTUP INOTIFY", PACKAGE_VERSION, 0); - else - log_it("CRON", getpid(), "STARTUP", PACKAGE_VERSION, 0); + log_it("CRON", getpid(), "STARTUP", PACKAGE_VERSION, 0); break; default: /* parent process should just die */ @@ -202,18 +200,28 @@ main(int argc, char *argv[]) { } } + pid = getpid(); acquire_daemonlock(0); database.head = NULL; database.tail = NULL; - if (inotify_enabled) { + database.mtime = (time_t) 0; + + load_database(&database); + #if defined WITH_INOTIFY - load_inotify_database(&database, fildes); -#endif - } - else { - database.mtime = (time_t) 0; - load_database(&database); + for (i = 0; i < sizeof(wd)/sizeof(wd[0]); ++i) { + /* initialize to negative number other than -1 + * so an eventual error is reported for the first time + */ + wd[i] = -2; } + + fd = inotify_init(); + if (fd < 0) + log_it("CRON", pid, "INFO", "Inotify init failed", errno); + set_cron_watched(fd); +#endif + set_time(TRUE); run_reboot_jobs(&database); timeRunning = virtualTime = clockTime; @@ -246,12 +254,17 @@ main(int argc, char *argv[]) { timeDiff = timeRunning - virtualTime; if (inotify_enabled) { #if defined WITH_INOTIFY - check_inotify_database(&database, fildes); + check_inotify_database(&database, fd); #endif } - else + else { load_database(&database); - +#if defined WITH_INOTIFY + /* try reinstating the watches */ + set_cron_watched(fd); +#endif + } + /* shortcut for the most common case */ if (timeDiff == 1) { virtualTime = timeRunning; @@ -275,7 +288,7 @@ main(int argc, char *argv[]) { * minute until caught up. */ Debug(DSCH, ("[%ld], normal case %d minutes to go\n", - (long)getpid(), timeDiff)) + (long)pid, timeDiff)) do { if (job_runqueue()) sleep(10); @@ -297,7 +310,7 @@ main(int argc, char *argv[]) { * housekeeping. */ Debug(DSCH, ("[%ld], DST begins %d minutes to go\n", - (long)getpid(), timeDiff)) + (long)pid, timeDiff)) /* run wildcard jobs for current minute */ find_jobs(timeRunning, &database, TRUE, FALSE); @@ -323,7 +336,7 @@ main(int argc, char *argv[]) { * change until we are caught up. */ Debug(DSCH, ("[%ld], DST ends %d minutes to go\n", - (long)getpid(), timeDiff)) + (long)pid, timeDiff)) find_jobs(timeRunning, &database, TRUE, FALSE); break; default: @@ -332,7 +345,7 @@ main(int argc, char *argv[]) { * jump virtual time, and run everything */ Debug(DSCH, ("[%ld], clock jumped\n", - (long)getpid())) + (long)pid)) virtualTime = timeRunning; find_jobs(timeRunning, &database, TRUE, TRUE); } @@ -353,14 +366,13 @@ main(int argc, char *argv[]) { sigchld_reaper(); } } - /* here stay ifdef, because some of the watches can be used even - * if inotify is disabled - */ + #if defined WITH_INOTIFY - set_cron_unwatched(fildes); + if (inotify_enabled) + set_cron_unwatched(fd); - if (fildes >= 0 && close(fildes) < 0) - log_it("CRON", pid, "INFO", "Inotify can't remove watches", errno); + if (fd >= 0 && close(fd) < 0) + log_it("CRON", pid, "INFO", "Inotify close failed", errno); #endif } diff -up cronie-1.1/src/database.c.inotify-fixes cronie-1.1/src/database.c --- cronie-1.1/src/database.c.inotify-fixes 2008-06-18 22:49:02.000000000 +0200 +++ cronie-1.1/src/database.c 2008-06-18 22:49:02.000000000 +0200 @@ -32,14 +32,10 @@ /* 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 +void overwrite_database(cron_db *, cron_db *); static void process_crontab(const char *, const char *, - const char *, struct stat *, + const char *, cron_db *, cron_db *); static int not_a_crontab( DIR_T *dp ); @@ -48,10 +44,9 @@ 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 int check_open(const char *tabname, const char *fname, const char *uname, - struct passwd *pw) { + struct passwd *pw, time_t *mtime) { struct stat statbuf; int crontab_fd; pid_t pid = getpid(); @@ -60,12 +55,13 @@ check_open(const char *tabname, const ch log_it(uname, pid, "CAN'T OPEN", tabname, errno); return(-1); } + if (fstat(crontab_fd, &statbuf) < OK) { + log_it(uname, pid, "STAT FAILED", tabname, errno); + close(crontab_fd); + return(-1); + } + *mtime = statbuf.st_mtime; if (PermitAnyCrontab == 0) { - if (fstat(crontab_fd, &statbuf) < OK) { - log_it(uname, pid, "STAT FAILED", tabname, errno); - close(crontab_fd); - return(-1); - } if (!S_ISREG(statbuf.st_mode)) { log_it(uname, pid, "NOT REGULAR", tabname, 0); close(crontab_fd); @@ -92,12 +88,12 @@ check_open(const char *tabname, const ch } 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) { +process_crontab(const char *uname, const char *fname, const char *tabname, + cron_db *new_db, cron_db *old_db) { struct passwd *pw = NULL; int crontab_fd = -1; user *u; - struct stat statbuf; + time_t mtime; int crond_crontab = (fname == NULL) && (strcmp(tabname, SYSCRONTAB) != 0); if (fname == NULL) { @@ -111,43 +107,42 @@ process_inotify_crontab(const char *unam goto next_crontab; } + if ((crontab_fd = check_open(tabname, fname, uname, pw, &mtime)) == -1) + goto next_crontab; + 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. Check permission when -p ISN'T used. */ - if (u == NULL) { - if ((crontab_fd = check_open(tabname, fname, uname, pw)) == -1) - goto next_crontab; - } - else { /* second and other runs */ + u = find_user(old_db, fname, crond_crontab ? tabname : NULL ); /* find user in old_db */ + + 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) { + * in, then we can just use our existing entry. + */ + if (u->mtime == mtime) { 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... - */ - if ((crontab_fd = check_open(tabname, fname, uname, pw)) == -1) - 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)) + log_it(fname, getpid(), "RELOAD", tabname, 0); } - u = load_user(crontab_fd, pw, uname, fname, tabname); /* touch the disk */ - if (u != NULL) + + u = load_user(crontab_fd, pw, uname, fname, tabname); /* read the file */ + if (u != NULL) { + u->mtime = mtime; link_user(new_db, u); + } next_crontab: if (crontab_fd != -1) { @@ -156,57 +151,7 @@ process_inotify_crontab(const char *unam } } -void -load_inotify_database(cron_db *old_db, int fd) { - cron_db new_db; - DIR_T *dp; - DIR *dir; - pid_t pid = getpid(); - - 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", pid, "OPENDIR FAILED", RH_CROND_DIR, errno); - (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))) { - log_it("CRON", pid, "OPENDIR FAILED", SPOOL_DIR, errno); - (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); -} - +#if defined WITH_INOTIFY void check_inotify_database(cron_db *old_db, int fd) { cron_db new_db; @@ -218,7 +163,7 @@ check_inotify_database(cron_db *old_db, char buf[BUF_LEN]; pid_t pid = getpid(); - time.tv_sec = 1; + time.tv_sec = 0; time.tv_usec = 0; FD_ZERO(&rfds); @@ -226,110 +171,83 @@ check_inotify_database(cron_db *old_db, retval = select(fd + 1, &rfds, NULL, NULL, &time); if (retval == -1) { - log_it("CRON", pid, "INOTIFY", "select failed", errno); + if (errno != EINTR) + log_it("CRON", pid, "INOTIFY", "select failed", errno); + return; } else if (FD_ISSET(fd, &rfds)) { new_db.head = new_db.tail = NULL; - if (read(fd, buf, sizeof(buf)) == -1) - log_it("CRON", pid, "INOTIFY", "read failed", errno); - process_inotify_crontab("root", NULL, SYSCRONTAB, &new_db, old_db, fd, RELOAD); + while ((retval=read(fd, buf, sizeof(buf))) == -1 && errno == EINTR); - if (!(dir = opendir(RH_CROND_DIR))) { - log_it("CRON", pid, "OPENDIR FAILED", RH_CROND_DIR, errno); - (void) exit(ERROR_EXIT); + if (retval == 0) { + /* this should not happen as the buffer is large enough */ + errno = ENOMEM; } - 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))) { - log_it("CRON", pid, "OPENDIR FAILED", SPOOL_DIR, errno); + if (retval <= 0) { + log_it("CRON", pid, "INOTIFY", "read failed", errno); + /* something fatal must have occured we have no other reasonable + * way how to handle this failure than exit. + */ (void) exit(ERROR_EXIT); } - while (NULL != (dp = readdir(dir))) { - char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1]; + + /* we must reinstate the watches here - TODO reinstate only watches + * which get IN_IGNORED event + */ + set_cron_watched(fd); - if (not_a_crontab(dp)) - continue; + /* TODO: parse the events and read only affected files */ - strncpy(fname, dp->d_name, MAXNAMLEN); + process_crontab("root", NULL, SYSCRONTAB, &new_db, old_db); - 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", pid, "OPENDIR FAILED", RH_CROND_DIR, errno); - (void) exit(ERROR_EXIT); + } else { + 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_crontab("root", NULL, tabname, &new_db, old_db); + } + closedir(dir); } - 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))) { log_it("CRON", pid, "OPENDIR FAILED", SPOOL_DIR, errno); - (void) exit(ERROR_EXIT); - } + } else { + while (NULL != (dp = readdir(dir))) { + char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1]; - while (NULL != (dp = readdir(dir))) { - char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1]; + if (not_a_crontab(dp)) + continue; - if (not_a_crontab(dp)) - continue; + strncpy(fname, dp->d_name, MAXNAMLEN); - 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); + if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, dp->d_name, '/')) + continue; + process_crontab(fname, fname, tabname, &new_db, old_db); + } + closedir(dir); } - 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); + /* if we don't do this, then when our children eventually call + * getpwnam() in do_command.c's child_process to verify MAILTO=, + * they will screw us up (and v-v). + */ + endpwent(); + } + else { + /* just return as no db reload is needed */ + return; } - /* overwrite the database control block with the new one. - */ - *old_db = new_db; - Debug(DLOAD, ("load_database is done\n")) + overwrite_database(old_db, &new_db); + Debug(DLOAD, ("check_inotify_database is done\n")) } /*void @@ -358,12 +276,29 @@ read_dir(char *dir_name, cron_db *new_db #endif void +overwrite_database(cron_db *old_db, cron_db *new_db) { + 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; +} + +void load_database(cron_db *old_db) { struct stat statbuf, syscron_stat, crond_stat; cron_db new_db; DIR_T *dp; DIR *dir; - user *u, *nu; pid_t pid = getpid(); Debug(DLOAD, ("[%ld] load_database()\n", (long)pid)) @@ -374,22 +309,22 @@ load_database(cron_db *old_db) { */ if (stat(SPOOL_DIR, &statbuf) < OK) { log_it("CRON", pid, "STAT FAILED", SPOOL_DIR, errno); - (void) exit(ERROR_EXIT); + statbuf.st_mtime = 0; + } else { + /* As pointed out in Red Hat bugzilla 198019, with modern Linux it + * is possible to modify a file without modifying the mtime of the + * containing directory. Hence, we must check the mtime of each file: + */ + max_mtime(SPOOL_DIR, &statbuf); } - - /* As pointed out in Red Hat bugzilla 198019, with modern Linux it - * is possible to modify a file without modifying the mtime of the - * containing directory. Hence, we must check the mtime of each file: - */ - max_mtime(SPOOL_DIR, &statbuf); if (stat(RH_CROND_DIR, &crond_stat) < OK) { log_it("CRON", pid, "STAT FAILED", RH_CROND_DIR, errno); - (void) exit(ERROR_EXIT); + crond_stat.st_mtime = 0; + } else { + max_mtime(RH_CROND_DIR, &crond_stat); } - max_mtime(RH_CROND_DIR, &crond_stat); - /* track system crontab file */ if (stat(SYSCRONTAB, &syscron_stat) < OK) @@ -420,27 +355,24 @@ load_database(cron_db *old_db) { new_db.head = new_db.tail = NULL; if (syscron_stat.st_mtime) - process_crontab("root", NULL, SYSCRONTAB, &syscron_stat, - &new_db, old_db); + process_crontab("root", NULL, SYSCRONTAB, &new_db, old_db); if (!(dir = opendir(RH_CROND_DIR))) { log_it("CRON", pid, "OPENDIR FAILED", RH_CROND_DIR, errno); - (void) exit(ERROR_EXIT); - } - - while (NULL != (dp = readdir(dir))) { - char tabname[MAXNAMLEN+1]; + } else { + while (NULL != (dp = readdir(dir))) { + char tabname[MAXNAMLEN+1]; - if ( not_a_crontab( dp ) ) - continue; + if ( not_a_crontab( dp ) ) + continue; - if (!glue_strings(tabname, sizeof tabname, RH_CROND_DIR, dp->d_name, '/')) - continue; /* XXX log? */ + if (!glue_strings(tabname, sizeof tabname, RH_CROND_DIR, dp->d_name, '/')) + continue; /* XXX log? */ - process_crontab("root", NULL, tabname, - &crond_stat, &new_db, old_db); + process_crontab("root", NULL, tabname, &new_db, old_db); + } + closedir(dir); } - closedir(dir); /* we used to keep this dir open all the time, for the sake of * efficiency. however, we need to close it in every fork, and @@ -449,24 +381,22 @@ load_database(cron_db *old_db) { if (!(dir = opendir(SPOOL_DIR))) { log_it("CRON", pid, "OPENDIR FAILED", SPOOL_DIR, errno); - (void) exit(ERROR_EXIT); - } - - while (NULL != (dp = readdir(dir))) { - char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1]; + } else { + while (NULL != (dp = readdir(dir))) { + char fname[MAXNAMLEN+1], tabname[MAXNAMLEN+1]; - if ( not_a_crontab( dp ) ) - continue; + if ( not_a_crontab( dp ) ) + continue; - strncpy(fname, dp->d_name, MAXNAMLEN); + strncpy(fname, dp->d_name, MAXNAMLEN); - if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, fname, '/')) - continue; /* XXX log? */ + if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, fname, '/')) + continue; /* XXX log? */ - process_crontab(fname, fname, tabname, - &statbuf, &new_db, old_db); + process_crontab(fname, fname, tabname, &new_db, old_db); + } + closedir(dir); } - closedir(dir); /* if we don't do this, then when our children eventually call * getpwnam() in do_command.c's child_process to verify MAILTO=, @@ -474,19 +404,7 @@ load_database(cron_db *old_db) { */ endpwent(); - /* 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; + overwrite_database(old_db, &new_db); Debug(DLOAD, ("load_database is done\n")) } @@ -527,100 +445,6 @@ find_user(cron_db *db, const char *name, return (u); } -static void -process_crontab(const char *uname, const char *fname, const char *tabname, - struct stat *statbuf, cron_db *new_db, cron_db *old_db) -{ - struct passwd *pw = NULL; - int crontab_fd = OK - 1; - user *u; - int crond_crontab = (fname == NULL) && (strcmp(tabname, SYSCRONTAB) != 0); - pid_t pid = getpid(); - - 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(fname, pid, "ORPHAN", "no passwd entry", 0); - goto next_crontab; - } - - if ((crontab_fd = open(tabname, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0)) < OK) { - /* crontab not accessible? - */ - log_it(fname, pid, "CAN'T OPEN", tabname, errno); - goto next_crontab; - } - - if (fstat(crontab_fd, statbuf) < OK) { - log_it(fname, pid, "FSTAT FAILED", tabname, errno); - goto next_crontab; - } - - if ( PermitAnyCrontab == 0 ) - { - if (!S_ISREG(statbuf->st_mode)) { - log_it(fname, pid, "NOT REGULAR", tabname, 0); - goto next_crontab; - } - if ((statbuf->st_mode & 07533) != 0400) { - log_it(fname, pid, "BAD FILE MODE", tabname, 0); - goto next_crontab; - } - if (statbuf->st_uid != ROOT_UID && (pw == NULL || - statbuf->st_uid != pw->pw_uid || strcmp(uname, pw->pw_name) != 0)) { - log_it(fname, pid, "WRONG FILE OWNER", tabname, 0); - goto next_crontab; - } - if (pw && statbuf->st_nlink != 1) { - log_it(fname, pid, "BAD LINK COUNT", tabname, 0); - goto next_crontab; - } - } - - Debug(DLOAD, ("\t%s:", fname)) - - u = find_user(old_db, fname, crond_crontab ? tabname : NULL ); - - if (u != NULL) { - /* if crontab has not changed since we last read it - * in, then we can just use our existing entry. - */ - if (u->mtime == statbuf->st_mtime) { - 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); - log_it(fname, pid, "RELOAD", tabname, 0); - } - u = load_user(crontab_fd, pw, uname, fname, tabname); - if (u != NULL) { - u->mtime = statbuf->st_mtime; - link_user(new_db, u); - } - - next_crontab: - if (crontab_fd >= OK) { - Debug(DLOAD, (" [done]\n")) - close(crontab_fd); - } -} - static int not_a_crontab( DIR_T *dp ) { int len; @@ -660,11 +484,10 @@ static void max_mtime( char *dir_name, s DIR * dir; DIR_T *dp; struct stat st; - pid_t pid = getpid(); if (!(dir = opendir(dir_name))) { - log_it("CRON", pid, "OPENDIR FAILED", dir_name, errno); - (void) exit(ERROR_EXIT); + max_st->st_mtime = 0; + return; } while (NULL != (dp = readdir(dir)))