1: /* Copyright 1988,1990,1993,1994 by Paul Vixie 2: * All rights reserved 3: * 4: * Distribute freely, except: don't remove my name from the source or 5: * documentation (don't take credit for my work), mark your changes (don't 6: * get me blamed for your possible bugs), don't alter or remove this 7: * notice. May be sold if buildable source is provided to buyer. No 8: * warrantee of any kind, express or implied, is included with this 9: * software; use at your own risk, responsibility for damages (if any) to 10: * anyone resulting from the use of this software rests entirely with the 11: * user. 12: * 13: * Send bug reports, bug fixes, enhancements, requests, flames, etc., and 14: * I'll try to keep a version up to date. I can be reached as follows: 15: * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul 16: */ 17: 18: #if !defined(lint) && !defined(LINT) 19: static char rcsid[] = "$Id: database.c,v 2.8 1994/01/15 20:43:43 vixie Exp $"; 20: #endif 21: 22: /* vix 26jan87 [RCS has the log] 23: */ 24: 25: 26: #include "cron.h" 27: #include <fcntl.h> 28: #include <sys/stat.h> 29: #include <sys/file.h> 30: 31: 32: #define TMAX(a,b) ((a)>(b)?(a):(b)) 33: 34: 35: static void process_crontab __P((char *, char *, char *, 36: struct stat *, 37: cron_db *, cron_db *)); 38: 39: 40: void 41: load_database(old_db) 42: cron_db *old_db; 43: { 44: DIR *dir; 45: struct stat statbuf; 46: struct stat syscron_stat; 47: register DIR_T *dp; 48: cron_db new_db; 49: user *u, *nu; 50: 51: Debug(DLOAD, ("[%d] load_database()\n", getpid())) 52: 53: /* before we start loading any data, do a stat on SPOOL_DIR 54: * so that if anything changes as of this moment (i.e., before we've 55: * cached any of the database), we'll see the changes next time. 56: */ 57: if (stat(SPOOL_DIR, &statbuf) < OK) { 58: log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR); 59: (void) exit(ERROR_EXIT); 60: } 61: 62: /* track system crontab file 63: */ 64: if (stat(SYSCRONTAB, &syscron_stat) < OK) 65: syscron_stat.st_mtime = 0; 66: 67: /* if spooldir's mtime has not changed, we don't need to fiddle with 68: * the database. 69: * 70: * Note that old_db->mtime is initialized to 0 in main(), and 71: * so is guaranteed to be different than the stat() mtime the first 72: * time this function is called. 73: */ 74: if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime)) { 75: Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n", 76: getpid())) 77: return; 78: } 79: 80: /* something's different. make a new database, moving unchanged 81: * elements from the old database, reloading elements that have 82: * actually changed. Whatever is left in the old database when 83: * we're done is chaff -- crontabs that disappeared. 84: */ 85: new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime); 86: new_db.head = new_db.tail = NULL; 87: 88: if (syscron_stat.st_mtime) { 89: process_crontab("root", "*system*", 90: SYSCRONTAB, &syscron_stat, 91: &new_db, old_db); 92: } 93: 94: /* we used to keep this dir open all the time, for the sake of 95: * efficiency. however, we need to close it in every fork, and 96: * we fork a lot more often than the mtime of the dir changes. 97: */ 98: if (!(dir = opendir(SPOOL_DIR))) { 99: log_it("CRON", getpid(), "OPENDIR FAILED", SPOOL_DIR); 100: (void) exit(ERROR_EXIT); 101: } 102: 103: while (NULL != (dp = readdir(dir))) { 104: char fname[MAXNAMLEN+1], 105: tabname[MAXNAMLEN+1]; 106: 107: /* avoid file names beginning with ".". this is good 108: * because we would otherwise waste two guaranteed calls 109: * to getpwnam() for . and .., and also because user names 110: * starting with a period are just too nasty to consider. 111: */ 112: if (dp->d_name[0] == '.') 113: continue; 114: 115: (void) strcpy(fname, dp->d_name); 116: sprintf(tabname, CRON_TAB(fname)); 117: 118: process_crontab(fname, fname, tabname, 119: &statbuf, &new_db, old_db); 120: } 121: closedir(dir); 122: 123: /* if we don't do this, then when our children eventually call 124: * getpwnam() in do_command.c's child_process to verify MAILTO=, 125: * they will screw us up (and v-v). 126: */ 127: endpwent(); 128: 129: /* whatever's left in the old database is now junk. 130: */ 131: Debug(DLOAD, ("unlinking old database:\n")) 132: for (u = old_db->head; u != NULL; u = nu) { 133: Debug(DLOAD, ("\t%s\n", u->name)) 134: nu = u->next; 135: unlink_user(old_db, u); 136: free_user(u); 137: } 138: 139: /* overwrite the database control block with the new one. 140: */ 141: *old_db = new_db; 142: Debug(DLOAD, ("load_database is done\n")) 143: } 144: 145: 146: void 147: link_user(db, u) 148: register cron_db *db; 149: register user *u; 150: { 151: if (db->head == NULL) 152: db->head = u; 153: if (db->tail) 154: db->tail->next = u; 155: u->prev = db->tail; 156: u->next = NULL; 157: db->tail = u; 158: } 159: 160: 161: void 162: unlink_user(db, u) 163: register cron_db *db; 164: register user *u; 165: { 166: if (u->prev == NULL) 167: db->head = u->next; 168: else 169: u->prev->next = u->next; 170: 171: if (u->next == NULL) 172: db->tail = u->prev; 173: else 174: u->next->prev = u->prev; 175: } 176: 177: 178: user * 179: find_user(db, name) 180: cron_db *db; 181: register char *name; 182: { 183: char *env_get(); 184: register user *u; 185: 186: for (u = db->head; u != NULL; u = u->next) 187: if (!strcmp(u->name, name)) 188: break; 189: return u; 190: } 191: 192: 193: static void 194: process_crontab(uname, fname, tabname, statbuf, new_db, old_db) 195: char *uname; 196: char *fname; 197: char *tabname; 198: struct stat *statbuf; 199: cron_db *new_db; 200: cron_db *old_db; 201: { 202: struct passwd *pw = NULL; 203: int crontab_fd = OK - 1; 204: register user *u; 205: 206: if (strcmp(fname, "*system*") && !(pw = getpwnam(uname))) { 207: /* file doesn't have a user in passwd file. 208: */ 209: log_it(fname, getpid(), "ORPHAN", "no passwd entry"); 210: goto next_crontab; 211: } 212: 213: if ((crontab_fd = open(tabname, O_RDONLY, 0)) < OK) { 214: /* crontab not accessible? 215: */ 216: log_it(fname, getpid(), "CAN'T OPEN", tabname); 217: goto next_crontab; 218: } 219: 220: if (fstat(crontab_fd, statbuf) < OK) { 221: log_it(fname, getpid(), "FSTAT FAILED", tabname); 222: goto next_crontab; 223: } 224: 225: Debug(DLOAD, ("\t%s:", fname)) 226: u = find_user(old_db, fname); 227: if (u != NULL) { 228: /* if crontab has not changed since we last read it 229: * in, then we can just use our existing entry. 230: */ 231: if (u->mtime == statbuf->st_mtime) { 232: Debug(DLOAD, (" [no change, using old data]")) 233: unlink_user(old_db, u); 234: link_user(new_db, u); 235: goto next_crontab; 236: } 237: 238: /* before we fall through to the code that will reload 239: * the user, let's deallocate and unlink the user in 240: * the old database. This is more a point of memory 241: * efficiency than anything else, since all leftover 242: * users will be deleted from the old database when 243: * we finish with the crontab... 244: */ 245: Debug(DLOAD, (" [delete old data]")) 246: unlink_user(old_db, u); 247: free_user(u); 248: log_it(fname, getpid(), "RELOAD", tabname); 249: } 250: u = load_user(crontab_fd, pw, fname); 251: if (u != NULL) { 252: u->mtime = statbuf->st_mtime; 253: link_user(new_db, u); 254: } 255: 256: next_crontab: 257: if (crontab_fd >= OK) { 258: Debug(DLOAD, (" [done]\n")) 259: close(crontab_fd); 260: } 261: }