272 lines
6.6 KiB
C
272 lines
6.6 KiB
C
/* Copyright 1988,1990 by Paul Vixie
|
|
* All rights reserved
|
|
*
|
|
* Distribute freely, except: don't remove my name from the source or
|
|
* documentation (don't take credit for my work), mark your changes (don't
|
|
* get me blamed for your possible bugs), don't alter or remove this
|
|
* notice. May be sold if buildable source is provided to buyer. No
|
|
* warrantee of any kind, express or implied, is included with this
|
|
* software; use at your own risk, responsibility for damages (if any) to
|
|
* anyone resulting from the use of this software rests entirely with the
|
|
* user.
|
|
*
|
|
* Send bug reports, bug fixes, enhancements, requests, flames, etc., and
|
|
* I'll try to keep a version up to date. I can be reached as follows:
|
|
* Paul Vixie, 329 Noe Street, San Francisco, CA, 94114, (415) 864-7013,
|
|
* paul@vixie.sf.ca.us || {hoptoad,pacbell,decwrl,crash}!vixie!paul
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char rcsid[] = "$Id: database.c,v 1.4 1993/12/15 16:58:01 jtc Exp $";
|
|
#endif
|
|
|
|
|
|
#include "cron.h"
|
|
#include <pwd.h>
|
|
#if defined(__NetBSD__)
|
|
# include <sys/file.h>
|
|
# include <dirent.h>
|
|
# define direct dirent
|
|
#elif defined(BSD)
|
|
# include <sys/file.h>
|
|
# include <sys/dir.h>
|
|
#elif defined(ATT)
|
|
# include <sys/file.h>
|
|
# include <ndir.h>
|
|
# include <fcntl.h>
|
|
#endif
|
|
|
|
|
|
extern void perror(), exit();
|
|
|
|
|
|
void
|
|
load_database(old_db)
|
|
cron_db *old_db;
|
|
{
|
|
extern void link_user(), unlink_user(), free_user();
|
|
extern user *load_user(), *find_user();
|
|
extern char *env_get();
|
|
|
|
DIR *dir;
|
|
struct stat statbuf;
|
|
struct direct *dp;
|
|
cron_db new_db;
|
|
user *u;
|
|
|
|
Debug(DLOAD, ("[%d] load_database()\n", getpid()))
|
|
|
|
/* before we start loading any data, do a stat on SPOOL_DIR
|
|
* so that if anything changes as of this moment (i.e., before we've
|
|
* cached any of the database), we'll see the changes next time.
|
|
*/
|
|
if (stat(SPOOL_DIR, &statbuf) < OK)
|
|
{
|
|
log_it("CROND", getpid(), "STAT FAILED", SPOOL_DIR);
|
|
(void) exit(ERROR_EXIT);
|
|
}
|
|
|
|
/* if spooldir's mtime has not changed, we don't need to fiddle with
|
|
* the database. Note that if /etc/passwd changes (like, someone's
|
|
* UID/GID/HOME/SHELL, we won't see it. Maybe we should
|
|
* keep an mtime for the passwd file? HINT
|
|
*
|
|
* Note that old_db->mtime is initialized to 0 in main(), and
|
|
* so is guaranteed to be different than the stat() mtime the first
|
|
* time this function is called.
|
|
*/
|
|
if (old_db->mtime == statbuf.st_mtime)
|
|
{
|
|
Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n",
|
|
getpid()))
|
|
return;
|
|
}
|
|
|
|
/* 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
|
|
* we fork a lot more often than the mtime of the dir changes.
|
|
*/
|
|
if (!(dir = opendir(SPOOL_DIR)))
|
|
{
|
|
log_it("CROND", getpid(), "OPENDIR FAILED", SPOOL_DIR);
|
|
(void) exit(ERROR_EXIT);
|
|
}
|
|
|
|
/* something's different. make a new database, moving unchanged
|
|
* elements from the old database, reloading elements that have
|
|
* actually changed. Whatever is left in the old database when
|
|
* we're done is chaff -- crontabs that disappeared.
|
|
*/
|
|
new_db.mtime = statbuf.st_mtime;
|
|
new_db.head = new_db.tail = NULL;
|
|
|
|
while (NULL != (dp = readdir(dir)))
|
|
{
|
|
extern struct passwd *getpwnam();
|
|
struct passwd *pw;
|
|
int crontab_fd = -1;
|
|
char fname[MAXNAMLEN+1],
|
|
tabname[MAXNAMLEN+1];
|
|
|
|
(void) strncpy(fname, dp->d_name, (int) dp->d_namlen);
|
|
fname[dp->d_namlen] = '\0';
|
|
|
|
/* avoid file names beginning with ".". this is good
|
|
* because we would otherwise waste two guaranteed calls
|
|
* to getpwnam() for . and .., and also because user names
|
|
* starting with a period are just too nasty to consider.
|
|
*/
|
|
if (fname[0] == '.')
|
|
goto next_crontab;
|
|
|
|
if (NULL == (pw = getpwnam(fname)))
|
|
{
|
|
/* file doesn't have a user in passwd file.
|
|
*/
|
|
log_it(fname, getpid(), "ORPHAN", "no passwd entry");
|
|
goto next_crontab;
|
|
}
|
|
|
|
sprintf(tabname, CRON_TAB(fname));
|
|
if ((crontab_fd = open(tabname, O_RDONLY, 0)) < OK)
|
|
{
|
|
/* crontab not accessible?
|
|
*/
|
|
log_it(fname, getpid(), "CAN'T OPEN", tabname);
|
|
goto next_crontab;
|
|
}
|
|
|
|
if (fstat(crontab_fd, &statbuf) < OK)
|
|
{
|
|
log_it(fname, getpid(), "FSTAT FAILED", tabname);
|
|
goto next_crontab;
|
|
}
|
|
|
|
Debug(DLOAD, ("\t%s:", fname))
|
|
u = find_user(old_db, fname);
|
|
if (u != NULL)
|
|
{
|
|
/* if crontab has not changed since we last read it
|
|
* in, then we can just use our existing entry.
|
|
* note that we do not check for changes in the
|
|
* passwd entry (uid, home dir, etc). HINT
|
|
*/
|
|
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);
|
|
}
|
|
u = load_user(
|
|
crontab_fd,
|
|
pw->pw_name,
|
|
pw->pw_uid,
|
|
pw->pw_gid,
|
|
pw->pw_dir,
|
|
pw->pw_shell
|
|
);
|
|
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);
|
|
}
|
|
}
|
|
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=,
|
|
* they will screw us up (and v-v).
|
|
*
|
|
* (this was lots of fun to find...)
|
|
*/
|
|
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 = u->next)
|
|
{
|
|
Debug(DLOAD, ("\t%s\n", env_get(USERENV, u->envp)))
|
|
unlink_user(old_db, u);
|
|
free_user(u);
|
|
}
|
|
|
|
/* overwrite the database control block with the new one.
|
|
*/
|
|
Debug(DLOAD, ("installing new database\n"))
|
|
#if defined(BSD)
|
|
/* BSD has structure assignments */
|
|
*old_db = new_db;
|
|
#endif
|
|
#if defined(ATT)
|
|
/* ATT, well, I don't know. Use memcpy(). */
|
|
memcpy(old_db, &new_db, sizeof(cron_db));
|
|
#endif
|
|
Debug(DLOAD, ("load_database is done\n"))
|
|
}
|
|
|
|
|
|
void
|
|
link_user(db, u)
|
|
cron_db *db;
|
|
user *u;
|
|
{
|
|
if (db->head == NULL)
|
|
db->head = u;
|
|
if (db->tail)
|
|
db->tail->next = u;
|
|
u->prev = db->tail;
|
|
u->next = NULL;
|
|
db->tail = u;
|
|
}
|
|
|
|
|
|
void
|
|
unlink_user(db, u)
|
|
cron_db *db;
|
|
user *u;
|
|
{
|
|
if (u->prev == NULL)
|
|
db->head = u->next;
|
|
else
|
|
u->prev->next = u->next;
|
|
|
|
if (u->next == NULL)
|
|
db->tail = u->prev;
|
|
else
|
|
u->next->prev = u->prev;
|
|
}
|
|
|
|
|
|
user *
|
|
find_user(db, name)
|
|
cron_db *db;
|
|
char *name;
|
|
{
|
|
char *env_get();
|
|
user *u;
|
|
|
|
for (u = db->head; u != NULL; u = u->next)
|
|
if (!strcmp(env_get(USERENV, u->envp), name))
|
|
break;
|
|
return u;
|
|
}
|