This source file includes following definitions.
- check_open
- free_orphan
- check_orphans
- find_orphan
- add_orphan
- process_crontab
- cluster_host_is_local
- check_inotify_database
- overwrite_database
- load_database
- link_user
- unlink_user
- find_user
- not_a_crontab
- max_mtime
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 #include "config.h"
31
32 #include <dirent.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <limits.h>
36 #include <pwd.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <unistd.h>
42
43 #ifdef WITH_INOTIFY
44 # include <sys/inotify.h>
45 #endif
46
47 #include "funcs.h"
48 #include "globals.h"
49 #include "pathnames.h"
50
51 #define TMAX(a,b) ((a)>(b)?(a):(b))
52 #define TMIN(a,b) ((a)<(b)?(a):(b))
53
54
55 #define EVENT_SIZE (sizeof (struct inotify_event))
56
57
58 #define BUF_LEN (1024 * (EVENT_SIZE + 16))
59
60 static void overwrite_database(cron_db *, cron_db *);
61
62 static void process_crontab(const char *, const char *,
63 const char *, cron_db *, cron_db *);
64
65 static int not_a_crontab(DIR_T * dp);
66
67
68 static void max_mtime(const char *dir_name, struct stat *max_st);
69
70
71 static int
72 check_open(const char *tabname, const char *uname,
73 struct passwd *pw, time_t * mtime) {
74 struct stat statbuf;
75 int crontab_fd;
76 pid_t pid = getpid();
77
78 if ((crontab_fd =
79 open(tabname, O_RDONLY | O_NONBLOCK, 0)) == -1) {
80 log_it(uname, pid, "CAN'T OPEN", tabname, errno);
81 return (-1);
82 }
83 if (fstat(crontab_fd, &statbuf) < OK) {
84 log_it(uname, pid, "STAT FAILED", tabname, errno);
85 close(crontab_fd);
86 return (-1);
87 }
88 *mtime = statbuf.st_mtime;
89 if (PermitAnyCrontab == 0) {
90 if (!S_ISREG(statbuf.st_mode)) {
91 log_it(uname, pid, "NOT REGULAR", tabname, 0);
92 close(crontab_fd);
93 return (-1);
94 }
95 if ((statbuf.st_mode & 07533) != 0400) {
96 log_it(uname, pid, "BAD FILE MODE", tabname, 0);
97 close(crontab_fd);
98 return (-1);
99 }
100 if (statbuf.st_uid != ROOT_UID && (pw == NULL ||
101 statbuf.st_uid != pw->pw_uid ||
102 strcmp(uname, pw->pw_name) != 0)) {
103 log_it(uname, pid, "WRONG FILE OWNER", tabname, 0);
104 close(crontab_fd);
105 return (-1);
106 }
107 if (pw && statbuf.st_nlink != 1) {
108 log_it(uname, pid, "BAD LINK COUNT", tabname, 0);
109 close(crontab_fd);
110 return (-1);
111 }
112 }
113 return (crontab_fd);
114 }
115
116 static orphan *orphans;
117
118 static void
119 free_orphan(orphan *o) {
120 free(o->tabname);
121 free(o->fname);
122 free(o->uname);
123 free(o);
124 }
125
126 void
127 check_orphans(cron_db *db) {
128 orphan *prev_orphan = NULL;
129 orphan *o = orphans;
130
131 while (o != NULL) {
132 if (getpwnam(o->uname) != NULL) {
133 orphan *next = o->next;
134
135 if (prev_orphan == NULL) {
136 orphans = next;
137 } else {
138 prev_orphan->next = next;
139 }
140
141 process_crontab(o->uname, o->fname, o->tabname,
142 db, NULL);
143
144
145 if (prev_orphan == NULL && orphans != next) {
146 prev_orphan = orphans;
147 }
148 free_orphan(o);
149 o = next;
150 } else {
151 prev_orphan = o;
152 o = o->next;
153 }
154 }
155 }
156
157 static int
158 find_orphan(const char *uname, const char *fname, const char *tabname) {
159 orphan *o;
160
161 for (o = orphans; o != NULL; o = o->next) {
162 if (uname && o->uname) {
163 if (strcmp(uname, o->uname) != 0)
164 continue;
165 } else if (uname != o->uname)
166 continue;
167
168 if (fname && o->fname) {
169 if (strcmp(fname, o->fname) != 0)
170 continue;
171 } else if (fname != o->fname)
172 continue;
173
174 if (tabname && o->tabname) {
175 if (strcmp(tabname, o->tabname) != 0)
176 continue;
177 } else if (tabname != o->tabname)
178 continue;
179 return 1;
180 }
181
182 return 0;
183 }
184
185 static void
186 add_orphan(const char *uname, const char *fname, const char *tabname) {
187 orphan *o;
188
189 if (find_orphan(uname, fname, tabname))
190 return;
191
192 o = calloc(1, sizeof(*o));
193 if (o == NULL)
194 return;
195
196 if (uname)
197 if ((o->uname=strdup(uname)) == NULL)
198 goto cleanup;
199
200 if (fname)
201 if ((o->fname=strdup(fname)) == NULL)
202 goto cleanup;
203
204 if (tabname)
205 if ((o->tabname=strdup(tabname)) == NULL)
206 goto cleanup;
207
208 o->next = orphans;
209 orphans = o;
210 return;
211
212 cleanup:
213 free_orphan(o);
214 }
215
216 static void
217 process_crontab(const char *uname, const char *fname, const char *tabname,
218 cron_db * new_db, cron_db * old_db) {
219 struct passwd *pw = NULL;
220 int crontab_fd = -1;
221 user *u = NULL;
222 time_t mtime;
223 int crond_crontab = (fname == NULL) && (strcmp(tabname, SYSCRONTAB) != 0);
224
225 if (fname == NULL) {
226
227
228 fname = "*system*";
229 }
230 else if ((pw = getpwnam(uname)) == NULL) {
231
232
233 log_it(uname, getpid(), "ORPHAN", "no passwd entry", 0);
234 add_orphan(uname, fname, tabname);
235
236 goto next_crontab;
237 }
238
239 if ((crontab_fd = check_open(tabname, uname, pw, &mtime)) == -1)
240 goto next_crontab;
241
242 mtime = TMIN(new_db->mtime, mtime);
243
244 Debug(DLOAD, ("\t%s:", fname));
245
246 if (old_db != NULL)
247 u = find_user(old_db, fname, crond_crontab ? tabname : NULL);
248
249 if (u != NULL) {
250
251
252
253 if (u->mtime == mtime) {
254 Debug(DLOAD, (" [no change, using old data]"));
255 unlink_user(old_db, u);
256 link_user(new_db, u);
257 goto next_crontab;
258 }
259
260
261
262
263
264
265
266
267 Debug(DLOAD, (" [delete old data]"));
268 unlink_user(old_db, u);
269 free_user(u);
270 log_it(fname, getpid(), "RELOAD", tabname, 0);
271 }
272
273 u = load_user(crontab_fd, pw, uname, fname, tabname);
274 crontab_fd = -1;
275 if (u != NULL) {
276 u->mtime = mtime;
277 link_user(new_db, u);
278 }
279
280 next_crontab:
281 if (crontab_fd != -1) {
282 Debug(DLOAD, (" [done]\n"));
283 close(crontab_fd);
284 }
285 }
286
287 static int
288 cluster_host_is_local(void)
289 {
290 char filename[NAME_MAX+1];
291 int is_local;
292 FILE *f;
293 char hostname[MAXHOSTNAMELEN], myhostname[MAXHOSTNAMELEN];
294
295 if (!EnableClustering)
296 return (1);
297
298
299
300
301
302
303
304
305
306
307
308
309
310 is_local = 0;
311 if (glue_strings(filename, sizeof filename, SPOOL_DIR, CRON_HOSTNAME, '/')) {
312 if ((f = fopen(filename, "r"))) {
313
314 if (EOF != get_string(hostname, MAXHOSTNAMELEN, f, "\n") &&
315 gethostname(myhostname, MAXHOSTNAMELEN) == 0) {
316 is_local = (strcmp(myhostname, hostname) == 0);
317 } else {
318 Debug(DLOAD, ("cluster: hostname comparison error\n"));
319 }
320
321 fclose(f);
322 } else {
323 Debug(DLOAD, ("cluster: file %s not found\n", filename));
324 }
325 }
326
327 return (is_local);
328 }
329
330 #if defined WITH_INOTIFY
331 void check_inotify_database(cron_db * old_db) {
332 cron_db new_db;
333 DIR_T *dp;
334 DIR *dir;
335 struct timeval tv;
336 fd_set rfds;
337 int retval;
338 char buf[BUF_LEN];
339 pid_t pid = getpid();
340 tv.tv_sec = 0;
341 tv.tv_usec = 0;
342
343 FD_ZERO(&rfds);
344 FD_SET(old_db->ifd, &rfds);
345
346 retval = select(old_db->ifd + 1, &rfds, NULL, NULL, &tv);
347 if (retval == -1) {
348 if (errno != EINTR)
349 log_it("CRON", pid, "INOTIFY", "select failed", errno);
350 return;
351 }
352 else if (FD_ISSET(old_db->ifd, &rfds)) {
353 new_db.head = new_db.tail = NULL;
354 new_db.ifd = old_db->ifd;
355 new_db.mtime = time(NULL) - 1;
356 while ((retval = (int)read(old_db->ifd, buf, sizeof (buf))) == -1 &&
357 errno == EINTR) ;
358
359 if (retval == 0) {
360
361 errno = ENOMEM;
362 }
363
364 if (retval <= 0) {
365 log_it("CRON", pid, "INOTIFY", "read failed", errno);
366
367
368
369 (void) exit(ERROR_EXIT);
370 }
371
372
373
374
375 set_cron_watched(old_db->ifd);
376
377
378 #if defined ENABLE_SYSCRONTAB
379 process_crontab("root", NULL, SYSCRONTAB, &new_db, old_db);
380 #endif
381
382 if (!(dir = opendir(SYS_CROND_DIR))) {
383 log_it("CRON", pid, "OPENDIR FAILED", SYS_CROND_DIR, errno);
384 }
385 else {
386 while (NULL != (dp = readdir(dir))) {
387 char tabname[NAME_MAX + 1];
388
389 if (not_a_crontab(dp))
390 continue;
391
392 if (!glue_strings(tabname, sizeof tabname, SYS_CROND_DIR,
393 dp->d_name, '/'))
394 continue;
395 process_crontab("root", NULL, tabname, &new_db, old_db);
396 }
397 closedir(dir);
398 }
399
400 if (!(dir = opendir(SPOOL_DIR))) {
401 log_it("CRON", pid, "OPENDIR FAILED", SPOOL_DIR, errno);
402 }
403 else {
404 while (NULL != (dp = readdir(dir))) {
405 char fname[NAME_MAX + 1], tabname[NAME_MAX + 1];
406
407 if (not_a_crontab(dp))
408 continue;
409
410 strncpy(fname, dp->d_name, NAME_MAX);
411
412 if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR,
413 dp->d_name, '/'))
414 continue;
415 process_crontab(fname, fname, tabname, &new_db, old_db);
416 }
417 closedir(dir);
418 }
419
420
421
422
423
424 endpwent();
425 }
426 else {
427
428 return;
429 }
430
431 overwrite_database(old_db, &new_db);
432 Debug(DLOAD, ("check_inotify_database is done\n"));
433 }
434 #endif
435
436 static void overwrite_database(cron_db * old_db, cron_db * new_db) {
437 user *u, *nu;
438
439
440 Debug(DLOAD, ("unlinking old database:\n"));
441 for (u = old_db->head; u != NULL; u = nu) {
442 Debug(DLOAD, ("\t%s\n", u->name));
443 nu = u->next;
444 unlink_user(old_db, u);
445 free_user(u);
446 }
447
448
449
450 *old_db = *new_db;
451 }
452
453 int load_database(cron_db * old_db) {
454 struct stat statbuf, syscron_stat, crond_stat;
455 cron_db new_db;
456 DIR_T *dp;
457 DIR *dir;
458 pid_t pid = getpid();
459 int is_local = 0;
460 time_t now;
461
462 Debug(DLOAD, ("[%ld] load_database()\n", (long) pid));
463
464 now = time(NULL);
465
466
467
468
469
470 if (stat(SPOOL_DIR, &statbuf) < OK) {
471 log_it("CRON", pid, "STAT FAILED", SPOOL_DIR, errno);
472 statbuf.st_mtime = 0;
473 }
474 else {
475
476
477
478
479 max_mtime(SPOOL_DIR, &statbuf);
480 }
481
482 if (stat(SYS_CROND_DIR, &crond_stat) < OK) {
483 log_it("CRON", pid, "STAT FAILED", SYS_CROND_DIR, errno);
484 crond_stat.st_mtime = 0;
485 }
486 else {
487 max_mtime(SYS_CROND_DIR, &crond_stat);
488 }
489
490 #if defined ENABLE_SYSCRONTAB
491
492
493 if (stat(SYSCRONTAB, &syscron_stat) < OK)
494 syscron_stat.st_mtime = 0;
495 #endif
496
497
498
499
500
501
502
503
504
505
506
507
508 if (old_db->mtime == TMIN(now - 1, TMAX(crond_stat.st_mtime,
509 TMAX(statbuf.st_mtime, syscron_stat.st_mtime)))
510 ) {
511 Debug(DLOAD, ("[%ld] spool dir mtime unch, no load needed.\n",
512 (long) pid));
513 return 0;
514 }
515
516
517
518
519
520
521 new_db.mtime = now - 1;
522 new_db.head = new_db.tail = NULL;
523 #if defined WITH_INOTIFY
524 new_db.ifd = old_db->ifd;
525 #endif
526
527 #if defined ENABLE_SYSCRONTAB
528 if (syscron_stat.st_mtime)
529 process_crontab("root", NULL, SYSCRONTAB, &new_db, old_db);
530 #endif
531
532 if (!(dir = opendir(SYS_CROND_DIR))) {
533 log_it("CRON", pid, "OPENDIR FAILED", SYS_CROND_DIR, errno);
534 }
535 else {
536 while (NULL != (dp = readdir(dir))) {
537 char tabname[NAME_MAX + 1];
538
539 if (not_a_crontab(dp))
540 continue;
541
542 if (!glue_strings(tabname, sizeof tabname, SYS_CROND_DIR,
543 dp->d_name, '/'))
544 continue;
545
546 process_crontab("root", NULL, tabname, &new_db, old_db);
547 }
548 closedir(dir);
549 }
550
551
552
553
554
555
556 if (!(dir = opendir(SPOOL_DIR))) {
557 log_it("CRON", pid, "OPENDIR FAILED", SPOOL_DIR, errno);
558 }
559 else {
560
561 is_local = cluster_host_is_local();
562
563 while (is_local && NULL != (dp = readdir(dir))) {
564 char fname[NAME_MAX + 1], tabname[NAME_MAX + 1];
565
566 if (not_a_crontab(dp))
567 continue;
568
569 strncpy(fname, dp->d_name, NAME_MAX);
570
571 if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, fname, '/'))
572 continue;
573
574 process_crontab(fname, fname, tabname, &new_db, old_db);
575 }
576 closedir(dir);
577 }
578
579
580
581
582
583 endpwent();
584
585 overwrite_database(old_db, &new_db);
586 Debug(DLOAD, ("load_database is done\n"));
587 return 1;
588 }
589
590 void link_user(cron_db * db, user * u) {
591 if (db->head == NULL)
592 db->head = u;
593 if (db->tail)
594 db->tail->next = u;
595 u->prev = db->tail;
596 u->next = NULL;
597 db->tail = u;
598 }
599
600 void unlink_user(cron_db * db, user * u) {
601 if (u->prev == NULL)
602 db->head = u->next;
603 else
604 u->prev->next = u->next;
605
606 if (u->next == NULL)
607 db->tail = u->prev;
608 else
609 u->next->prev = u->prev;
610 }
611
612 user *find_user(cron_db * db, const char *name, const char *tabname) {
613 user *u;
614
615 for (u = db->head; u != NULL; u = u->next)
616 if ((strcmp(u->name, name) == 0)
617 && ((tabname == NULL)
618 || (strcmp(tabname, u->tabname) == 0)
619 )
620 )
621 break;
622 return (u);
623 }
624
625 static int not_a_crontab(DIR_T * dp) {
626 size_t len;
627
628
629
630
631
632
633 if (dp->d_name[0] == '.')
634 return (1);
635
636
637 if (dp->d_name[0] == '#')
638 return (1);
639
640
641 if (0 == strcmp(dp->d_name, CRON_HOSTNAME))
642 return(1);
643
644 len = strlen(dp->d_name);
645
646 if (len >= NAME_MAX || len == 0)
647 return (1);
648
649 if (dp->d_name[len - 1] == '~')
650 return (1);
651
652 if ((len > 8) && (strncmp(dp->d_name + len - 8, ".rpmsave", 8) == 0))
653 return (1);
654 if ((len > 8) && (strncmp(dp->d_name + len - 8, ".rpmorig", 8) == 0))
655 return (1);
656 if ((len > 7) && (strncmp(dp->d_name + len - 7, ".rpmnew", 7) == 0))
657 return (1);
658
659 return (0);
660 }
661
662 static void max_mtime(const char *dir_name, struct stat *max_st) {
663 DIR *dir;
664 DIR_T *dp;
665 struct stat st;
666
667 if (!(dir = opendir(dir_name))) {
668 max_st->st_mtime = 0;
669 return;
670 }
671
672 while (NULL != (dp = readdir(dir))) {
673 char tabname[NAME_MAX + 1];
674
675 if ( not_a_crontab ( dp ) && strcmp(dp->d_name, CRON_HOSTNAME) != 0)
676 continue;
677
678 if (!glue_strings(tabname, sizeof tabname, dir_name, dp->d_name, '/'))
679 continue;
680
681 if (stat(tabname, &st) < OK)
682 continue;
683
684 if (st.st_mtime > max_st->st_mtime)
685 max_st->st_mtime = st.st_mtime;
686 }
687 closedir(dir);
688 }