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