diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-07-08 15:37:10 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-07-08 15:37:10 +0200 |
commit | 45963c873c4eb8d95ba327c9e21d5188d14f1fa4 (patch) | |
tree | ca7943a7ac914387b8e615eb38480ebf43e5cb5d | |
parent | dfc870fab711b3ee85341f8cd8e7c8e0799d2da7 (diff) | |
download | busybox-w32-45963c873c4eb8d95ba327c9e21d5188d14f1fa4.tar.gz busybox-w32-45963c873c4eb8d95ba327c9e21d5188d14f1fa4.tar.bz2 busybox-w32-45963c873c4eb8d95ba327c9e21d5188d14f1fa4.zip |
crond: rename HumpBack names. Note two TODOs/FIXMEs
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | miscutils/crond.c | 633 |
1 files changed, 322 insertions, 311 deletions
diff --git a/miscutils/crond.c b/miscutils/crond.c index 481830f4d..28722563c 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c | |||
@@ -40,33 +40,33 @@ | |||
40 | 40 | ||
41 | 41 | ||
42 | typedef struct CronFile { | 42 | typedef struct CronFile { |
43 | struct CronFile *cf_Next; | 43 | struct CronFile *cf_next; |
44 | struct CronLine *cf_LineBase; | 44 | struct CronLine *cf_lines; |
45 | char *cf_User; /* username */ | 45 | char *cf_username; |
46 | smallint cf_Ready; /* bool: one or more jobs ready */ | 46 | smallint cf_wants_starting; /* bool: one or more jobs ready */ |
47 | smallint cf_Running; /* bool: one or more jobs running */ | 47 | smallint cf_has_running; /* bool: one or more jobs running */ |
48 | smallint cf_Deleted; /* marked for deletion, ignore */ | 48 | smallint cf_deleted; /* marked for deletion (but still has running jobs) */ |
49 | } CronFile; | 49 | } CronFile; |
50 | 50 | ||
51 | typedef struct CronLine { | 51 | typedef struct CronLine { |
52 | struct CronLine *cl_Next; | 52 | struct CronLine *cl_next; |
53 | char *cl_Shell; /* shell command */ | 53 | char *cl_cmd; /* shell command */ |
54 | pid_t cl_Pid; /* running pid, 0, or armed (-1) */ | 54 | pid_t cl_pid; /* >0:running, <0:needs to be started in this minute, 0:dormant */ |
55 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL | 55 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL |
56 | int cl_MailPos; /* 'empty file' size */ | 56 | int cl_empty_mail_size; /* size of mail header only */ |
57 | char *cl_MailTo; /* whom to mail results */ | 57 | char *cl_mailto; /* whom to mail results, may be NULL */ |
58 | smallint cl_MailFlag; /* running pid is for mail */ | 58 | smallint cl_mail_result; /* mail file is created, need to send it on completion */ |
59 | #endif | 59 | #endif |
60 | /* ordered by size, not in natural order. makes code smaller: */ | 60 | /* ordered by size, not in natural order. makes code smaller: */ |
61 | char cl_Dow[7]; /* 0-6, beginning sunday */ | 61 | char cl_Dow[7]; /* 0-6, beginning sunday */ |
62 | char cl_Mons[12]; /* 0-11 */ | 62 | char cl_Mons[12]; /* 0-11 */ |
63 | char cl_Hrs[24]; /* 0-23 */ | 63 | char cl_Hrs[24]; /* 0-23 */ |
64 | char cl_Days[32]; /* 1-31 */ | 64 | char cl_Days[32]; /* 1-31 */ |
65 | char cl_Mins[60]; /* 0-59 */ | 65 | char cl_Mins[60]; /* 0-59 */ |
66 | } CronLine; | 66 | } CronLine; |
67 | 67 | ||
68 | 68 | ||
69 | #define DaemonUid 0 | 69 | #define DAEMON_UID 0 |
70 | 70 | ||
71 | 71 | ||
72 | enum { | 72 | enum { |
@@ -79,18 +79,18 @@ enum { | |||
79 | OPT_d = (1 << 6) * ENABLE_FEATURE_CROND_D, | 79 | OPT_d = (1 << 6) * ENABLE_FEATURE_CROND_D, |
80 | }; | 80 | }; |
81 | #if ENABLE_FEATURE_CROND_D | 81 | #if ENABLE_FEATURE_CROND_D |
82 | #define DebugOpt (option_mask32 & OPT_d) | 82 | # define DebugOpt (option_mask32 & OPT_d) |
83 | #else | 83 | #else |
84 | #define DebugOpt 0 | 84 | # define DebugOpt 0 |
85 | #endif | 85 | #endif |
86 | 86 | ||
87 | 87 | ||
88 | struct globals { | 88 | struct globals { |
89 | unsigned LogLevel; /* = 8; */ | 89 | unsigned log_level; /* = 8; */ |
90 | time_t CDir_mtime; | 90 | time_t crontab_dir_mtime; |
91 | const char *LogFile; | 91 | const char *log_filename; |
92 | const char *CDir; /* = CRONTABS; */ | 92 | const char *crontab_dir_name; /* = CRONTABS; */ |
93 | CronFile *FileBase; | 93 | CronFile *cron_files; |
94 | #if SETENV_LEAKS | 94 | #if SETENV_LEAKS |
95 | char *env_var_user; | 95 | char *env_var_user; |
96 | char *env_var_home; | 96 | char *env_var_home; |
@@ -98,8 +98,8 @@ struct globals { | |||
98 | } FIX_ALIASING; | 98 | } FIX_ALIASING; |
99 | #define G (*(struct globals*)&bb_common_bufsiz1) | 99 | #define G (*(struct globals*)&bb_common_bufsiz1) |
100 | #define INIT_G() do { \ | 100 | #define INIT_G() do { \ |
101 | G.LogLevel = 8; \ | 101 | G.log_level = 8; \ |
102 | G.CDir = CRONTABS; \ | 102 | G.crontab_dir_name = CRONTABS; \ |
103 | } while (0) | 103 | } while (0) |
104 | 104 | ||
105 | 105 | ||
@@ -119,12 +119,12 @@ static void crondlog(const char *ctl, ...) | |||
119 | int level = (ctl[0] & 0x1f); | 119 | int level = (ctl[0] & 0x1f); |
120 | 120 | ||
121 | va_start(va, ctl); | 121 | va_start(va, ctl); |
122 | if (level >= (int)G.LogLevel) { | 122 | if (level >= (int)G.log_level) { |
123 | /* Debug mode: all to (non-redirected) stderr, */ | 123 | /* Debug mode: all to (non-redirected) stderr, */ |
124 | /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */ | 124 | /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */ |
125 | if (!DebugOpt && G.LogFile) { | 125 | if (!DebugOpt && G.log_filename) { |
126 | /* Otherwise (log to file): we reopen log file at every write: */ | 126 | /* Otherwise (log to file): we reopen log file at every write: */ |
127 | int logfd = open3_or_warn(G.LogFile, O_WRONLY | O_CREAT | O_APPEND, 0666); | 127 | int logfd = open3_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND, 0666); |
128 | if (logfd >= 0) | 128 | if (logfd >= 0) |
129 | xmove_fd(logfd, STDERR_FILENO); | 129 | xmove_fd(logfd, STDERR_FILENO); |
130 | } | 130 | } |
@@ -144,49 +144,6 @@ static void crondlog(const char *ctl, ...) | |||
144 | exit(20); | 144 | exit(20); |
145 | } | 145 | } |
146 | 146 | ||
147 | #if SETENV_LEAKS | ||
148 | /* We set environment *before* vfork (because we want to use vfork), | ||
149 | * so we cannot use setenv() - repeated calls to setenv() may leak memory! | ||
150 | * Using putenv(), and freeing memory after unsetenv() won't leak */ | ||
151 | static void safe_setenv(char **pvar_val, const char *var, const char *val) | ||
152 | { | ||
153 | char *var_val = *pvar_val; | ||
154 | |||
155 | if (var_val) { | ||
156 | bb_unsetenv_and_free(var_val); | ||
157 | } | ||
158 | *pvar_val = xasprintf("%s=%s", var, val); | ||
159 | putenv(*pvar_val); | ||
160 | } | ||
161 | #endif | ||
162 | |||
163 | static void SetEnv(struct passwd *pas) | ||
164 | { | ||
165 | #if SETENV_LEAKS | ||
166 | safe_setenv(&G.env_var_user, "USER", pas->pw_name); | ||
167 | safe_setenv(&G.env_var_home, "HOME", pas->pw_dir); | ||
168 | /* if we want to set user's shell instead: */ | ||
169 | /*safe_setenv(G.env_var_shell, "SHELL", pas->pw_shell);*/ | ||
170 | #else | ||
171 | xsetenv("USER", pas->pw_name); | ||
172 | xsetenv("HOME", pas->pw_dir); | ||
173 | #endif | ||
174 | /* currently, we use constant one: */ | ||
175 | /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */ | ||
176 | } | ||
177 | |||
178 | static void ChangeUser(struct passwd *pas) | ||
179 | { | ||
180 | /* careful: we're after vfork! */ | ||
181 | change_identity(pas); /* - initgroups, setgid, setuid */ | ||
182 | if (chdir(pas->pw_dir) < 0) { | ||
183 | crondlog(WARN9 "chdir(%s)", pas->pw_dir); | ||
184 | if (chdir(TMPDIR) < 0) { | ||
185 | crondlog(DIE9 "chdir(%s)", TMPDIR); /* exits */ | ||
186 | } | ||
187 | } | ||
188 | } | ||
189 | |||
190 | static const char DowAry[] ALIGN1 = | 147 | static const char DowAry[] ALIGN1 = |
191 | "sun""mon""tue""wed""thu""fri""sat" | 148 | "sun""mon""tue""wed""thu""fri""sat" |
192 | /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */ | 149 | /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */ |
@@ -303,7 +260,7 @@ static void ParseField(char *user, char *ary, int modvalue, int off, | |||
303 | return; | 260 | return; |
304 | } | 261 | } |
305 | 262 | ||
306 | if (DebugOpt && (G.LogLevel <= 5)) { /* like LVL5 */ | 263 | if (DebugOpt && (G.log_level <= 5)) { /* like LVL5 */ |
307 | /* can't use crondlog, it inserts '\n' */ | 264 | /* can't use crondlog, it inserts '\n' */ |
308 | int i; | 265 | int i; |
309 | for (i = 0; i < modvalue; ++i) | 266 | for (i = 0; i < modvalue; ++i) |
@@ -339,48 +296,57 @@ static void FixDayDow(CronLine *line) | |||
339 | } | 296 | } |
340 | 297 | ||
341 | /* | 298 | /* |
342 | * DeleteFile() - delete user database | 299 | * delete_cronfile() - delete user database |
343 | * | 300 | * |
344 | * Note: multiple entries for same user may exist if we were unable to | 301 | * Note: multiple entries for same user may exist if we were unable to |
345 | * completely delete a database due to running processes. | 302 | * completely delete a database due to running processes. |
346 | */ | 303 | */ |
347 | static void DeleteFile(const char *userName) | 304 | //FIXME: we will start a new job even if the old job is running |
305 | //if crontab was reloaded: crond thinks that "new" job is different from "old" | ||
306 | //even if they are in fact completely the same. Example | ||
307 | //Crontab was: | ||
308 | // 0-59 * * * job1 | ||
309 | // 0-59 * * * long_running_job2 | ||
310 | //User edits crontab to: | ||
311 | // 0-59 * * * job1_updated | ||
312 | // 0-59 * * * long_running_job2 | ||
313 | //Bug: crond can now start another long_running_job2 even if old one | ||
314 | //is still running. | ||
315 | static void delete_cronfile(const char *userName) | ||
348 | { | 316 | { |
349 | CronFile **pfile = &G.FileBase; | 317 | CronFile **pfile = &G.cron_files; |
350 | CronFile *file; | 318 | CronFile *file; |
351 | 319 | ||
352 | while ((file = *pfile) != NULL) { | 320 | while ((file = *pfile) != NULL) { |
353 | if (strcmp(userName, file->cf_User) == 0) { | 321 | if (strcmp(userName, file->cf_username) == 0) { |
354 | CronLine **pline = &file->cf_LineBase; | 322 | CronLine **pline = &file->cf_lines; |
355 | CronLine *line; | 323 | CronLine *line; |
356 | 324 | ||
357 | file->cf_Running = 0; | 325 | file->cf_has_running = 0; |
358 | file->cf_Deleted = 1; | 326 | file->cf_deleted = 1; |
359 | 327 | ||
360 | while ((line = *pline) != NULL) { | 328 | while ((line = *pline) != NULL) { |
361 | if (line->cl_Pid > 0) { | 329 | if (line->cl_pid > 0) { |
362 | file->cf_Running = 1; | 330 | file->cf_has_running = 1; |
363 | pline = &line->cl_Next; | 331 | pline = &line->cl_next; |
364 | } else { | 332 | } else { |
365 | *pline = line->cl_Next; | 333 | *pline = line->cl_next; |
366 | free(line->cl_Shell); | 334 | free(line->cl_cmd); |
367 | free(line); | 335 | free(line); |
368 | } | 336 | } |
369 | } | 337 | } |
370 | if (file->cf_Running == 0) { | 338 | if (file->cf_has_running == 0) { |
371 | *pfile = file->cf_Next; | 339 | *pfile = file->cf_next; |
372 | free(file->cf_User); | 340 | free(file->cf_username); |
373 | free(file); | 341 | free(file); |
374 | } else { | 342 | continue; |
375 | pfile = &file->cf_Next; | ||
376 | } | 343 | } |
377 | } else { | ||
378 | pfile = &file->cf_Next; | ||
379 | } | 344 | } |
345 | pfile = &file->cf_next; | ||
380 | } | 346 | } |
381 | } | 347 | } |
382 | 348 | ||
383 | static void SynchronizeFile(const char *fileName) | 349 | static void load_crontab(const char *fileName) |
384 | { | 350 | { |
385 | struct parser_t *parser; | 351 | struct parser_t *parser; |
386 | struct stat sbuf; | 352 | struct stat sbuf; |
@@ -390,23 +356,26 @@ static void SynchronizeFile(const char *fileName) | |||
390 | char *mailTo = NULL; | 356 | char *mailTo = NULL; |
391 | #endif | 357 | #endif |
392 | 358 | ||
393 | if (!fileName) | 359 | delete_cronfile(fileName); |
360 | |||
361 | if (!getpwnam(fileName)) { | ||
362 | crondlog(LVL7 "ignoring file '%s' (no such user)", fileName); | ||
394 | return; | 363 | return; |
364 | } | ||
395 | 365 | ||
396 | DeleteFile(fileName); | ||
397 | parser = config_open(fileName); | 366 | parser = config_open(fileName); |
398 | if (!parser) | 367 | if (!parser) |
399 | return; | 368 | return; |
400 | 369 | ||
401 | maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES; | 370 | maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES; |
402 | 371 | ||
403 | if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DaemonUid) { | 372 | if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DAEMON_UID) { |
404 | CronFile *file = xzalloc(sizeof(CronFile)); | 373 | CronFile *file = xzalloc(sizeof(CronFile)); |
405 | CronLine **pline; | 374 | CronLine **pline; |
406 | int n; | 375 | int n; |
407 | 376 | ||
408 | file->cf_User = xstrdup(fileName); | 377 | file->cf_username = xstrdup(fileName); |
409 | pline = &file->cf_LineBase; | 378 | pline = &file->cf_lines; |
410 | 379 | ||
411 | while (1) { | 380 | while (1) { |
412 | CronLine *line; | 381 | CronLine *line; |
@@ -433,11 +402,11 @@ static void SynchronizeFile(const char *fileName) | |||
433 | continue; | 402 | continue; |
434 | *pline = line = xzalloc(sizeof(*line)); | 403 | *pline = line = xzalloc(sizeof(*line)); |
435 | /* parse date ranges */ | 404 | /* parse date ranges */ |
436 | ParseField(file->cf_User, line->cl_Mins, 60, 0, NULL, tokens[0]); | 405 | ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]); |
437 | ParseField(file->cf_User, line->cl_Hrs, 24, 0, NULL, tokens[1]); | 406 | ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]); |
438 | ParseField(file->cf_User, line->cl_Days, 32, 0, NULL, tokens[2]); | 407 | ParseField(file->cf_username, line->cl_Days, 32, 0, NULL, tokens[2]); |
439 | ParseField(file->cf_User, line->cl_Mons, 12, -1, MonAry, tokens[3]); | 408 | ParseField(file->cf_username, line->cl_Mons, 12, -1, MonAry, tokens[3]); |
440 | ParseField(file->cf_User, line->cl_Dow, 7, 0, DowAry, tokens[4]); | 409 | ParseField(file->cf_username, line->cl_Dow, 7, 0, DowAry, tokens[4]); |
441 | /* | 410 | /* |
442 | * fix days and dow - if one is not "*" and the other | 411 | * fix days and dow - if one is not "*" and the other |
443 | * is "*", the other is set to 0, and vise-versa | 412 | * is "*", the other is set to 0, and vise-versa |
@@ -445,20 +414,20 @@ static void SynchronizeFile(const char *fileName) | |||
445 | FixDayDow(line); | 414 | FixDayDow(line); |
446 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL | 415 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL |
447 | /* copy mailto (can be NULL) */ | 416 | /* copy mailto (can be NULL) */ |
448 | line->cl_MailTo = xstrdup(mailTo); | 417 | line->cl_mailto = xstrdup(mailTo); |
449 | #endif | 418 | #endif |
450 | /* copy command */ | 419 | /* copy command */ |
451 | line->cl_Shell = xstrdup(tokens[5]); | 420 | line->cl_cmd = xstrdup(tokens[5]); |
452 | if (DebugOpt) { | 421 | if (DebugOpt) { |
453 | crondlog(LVL5 " command:%s", tokens[5]); | 422 | crondlog(LVL5 " command:%s", tokens[5]); |
454 | } | 423 | } |
455 | pline = &line->cl_Next; | 424 | pline = &line->cl_next; |
456 | //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]); | 425 | //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]); |
457 | } | 426 | } |
458 | *pline = NULL; | 427 | *pline = NULL; |
459 | 428 | ||
460 | file->cf_Next = G.FileBase; | 429 | file->cf_next = G.cron_files; |
461 | G.FileBase = file; | 430 | G.cron_files = file; |
462 | 431 | ||
463 | if (maxLines == 0) { | 432 | if (maxLines == 0) { |
464 | crondlog(WARN9 "user %s: too many lines", fileName); | 433 | crondlog(WARN9 "user %s: too many lines", fileName); |
@@ -467,7 +436,7 @@ static void SynchronizeFile(const char *fileName) | |||
467 | config_close(parser); | 436 | config_close(parser); |
468 | } | 437 | } |
469 | 438 | ||
470 | static void CheckUpdates(void) | 439 | static void process_cron_update_file(void) |
471 | { | 440 | { |
472 | FILE *fi; | 441 | FILE *fi; |
473 | char buf[256]; | 442 | char buf[256]; |
@@ -477,36 +446,34 @@ static void CheckUpdates(void) | |||
477 | unlink(CRONUPDATE); | 446 | unlink(CRONUPDATE); |
478 | while (fgets(buf, sizeof(buf), fi) != NULL) { | 447 | while (fgets(buf, sizeof(buf), fi) != NULL) { |
479 | /* use first word only */ | 448 | /* use first word only */ |
480 | SynchronizeFile(strtok(buf, " \t\r\n")); | 449 | skip_non_whitespace(buf)[0] = '\0'; |
450 | load_crontab(buf); | ||
481 | } | 451 | } |
482 | fclose(fi); | 452 | fclose(fi); |
483 | } | 453 | } |
484 | } | 454 | } |
485 | 455 | ||
486 | static void SynchronizeDir(void) | 456 | static void rescan_crontab_dir(void) |
487 | { | 457 | { |
488 | CronFile *file; | 458 | CronFile *file; |
489 | /* Attempt to delete the database. */ | 459 | |
460 | /* Delete all files until we only have ones with running jobs (or none) */ | ||
490 | again: | 461 | again: |
491 | for (file = G.FileBase; file; file = file->cf_Next) { | 462 | for (file = G.cron_files; file; file = file->cf_next) { |
492 | if (!file->cf_Deleted) { | 463 | if (!file->cf_deleted) { |
493 | DeleteFile(file->cf_User); | 464 | delete_cronfile(file->cf_username); |
494 | goto again; | 465 | goto again; |
495 | } | 466 | } |
496 | } | 467 | } |
497 | 468 | ||
498 | /* | 469 | /* Remove cron update file */ |
499 | * Remove cron update file | ||
500 | * | ||
501 | * Re-chdir, in case directory was renamed & deleted, or otherwise | ||
502 | * screwed up. | ||
503 | * | ||
504 | * scan directory and add associated users | ||
505 | */ | ||
506 | unlink(CRONUPDATE); | 470 | unlink(CRONUPDATE); |
507 | if (chdir(G.CDir) < 0) { | 471 | /* Re-chdir, in case directory was renamed & deleted */ |
508 | crondlog(DIE9 "chdir(%s)", G.CDir); | 472 | if (chdir(G.crontab_dir_name) < 0) { |
473 | crondlog(DIE9 "chdir(%s)", G.crontab_dir_name); | ||
509 | } | 474 | } |
475 | |||
476 | /* Scan directory and add associated users */ | ||
510 | { | 477 | { |
511 | DIR *dir = opendir("."); | 478 | DIR *dir = opendir("."); |
512 | struct dirent *den; | 479 | struct dirent *den; |
@@ -517,162 +484,62 @@ static void SynchronizeDir(void) | |||
517 | if (strchr(den->d_name, '.') != NULL) { | 484 | if (strchr(den->d_name, '.') != NULL) { |
518 | continue; | 485 | continue; |
519 | } | 486 | } |
520 | if (getpwnam(den->d_name)) { | 487 | load_crontab(den->d_name); |
521 | SynchronizeFile(den->d_name); | ||
522 | } else { | ||
523 | crondlog(LVL7 "ignoring %s", den->d_name); | ||
524 | } | ||
525 | } | 488 | } |
526 | closedir(dir); | 489 | closedir(dir); |
527 | } | 490 | } |
528 | } | 491 | } |
529 | 492 | ||
530 | /* | 493 | #if SETENV_LEAKS |
531 | * Determine which jobs need to be run. Under normal conditions, the | 494 | /* We set environment *before* vfork (because we want to use vfork), |
532 | * period is about a minute (one scan). Worst case it will be one | 495 | * so we cannot use setenv() - repeated calls to setenv() may leak memory! |
533 | * hour (60 scans). | 496 | * Using putenv(), and freeing memory after unsetenv() won't leak */ |
534 | */ | 497 | static void safe_setenv(char **pvar_val, const char *var, const char *val) |
535 | static void TestJobs(time_t t1, time_t t2) | ||
536 | { | 498 | { |
537 | time_t t; | 499 | char *var_val = *pvar_val; |
538 | |||
539 | /* Find jobs > t1 and <= t2 */ | ||
540 | |||
541 | for (t = t1 - t1 % 60; t <= t2; t += 60) { | ||
542 | struct tm *ptm; | ||
543 | CronFile *file; | ||
544 | CronLine *line; | ||
545 | |||
546 | if (t <= t1) | ||
547 | continue; | ||
548 | 500 | ||
549 | ptm = localtime(&t); | 501 | if (var_val) { |
550 | for (file = G.FileBase; file; file = file->cf_Next) { | 502 | bb_unsetenv_and_free(var_val); |
551 | if (DebugOpt) | ||
552 | crondlog(LVL5 "file %s:", file->cf_User); | ||
553 | if (file->cf_Deleted) | ||
554 | continue; | ||
555 | for (line = file->cf_LineBase; line; line = line->cl_Next) { | ||
556 | if (DebugOpt) | ||
557 | crondlog(LVL5 " line %s", line->cl_Shell); | ||
558 | if (line->cl_Mins[ptm->tm_min] && line->cl_Hrs[ptm->tm_hour] | ||
559 | && (line->cl_Days[ptm->tm_mday] || line->cl_Dow[ptm->tm_wday]) | ||
560 | && line->cl_Mons[ptm->tm_mon] | ||
561 | ) { | ||
562 | if (DebugOpt) { | ||
563 | crondlog(LVL5 " job: %d %s", | ||
564 | (int)line->cl_Pid, line->cl_Shell); | ||
565 | } | ||
566 | if (line->cl_Pid > 0) { | ||
567 | crondlog(LVL8 "user %s: process already running: %s", | ||
568 | file->cf_User, line->cl_Shell); | ||
569 | } else if (line->cl_Pid == 0) { | ||
570 | line->cl_Pid = -1; | ||
571 | file->cf_Ready = 1; | ||
572 | } | ||
573 | } | ||
574 | } | ||
575 | } | ||
576 | } | 503 | } |
504 | *pvar_val = xasprintf("%s=%s", var, val); | ||
505 | putenv(*pvar_val); | ||
577 | } | 506 | } |
507 | #endif | ||
578 | 508 | ||
579 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL | 509 | static void set_env_vars(struct passwd *pas) |
580 | static void | ||
581 | ForkJob(const char *user, CronLine *line, int mailFd, | ||
582 | const char *prog, const char *cmd, const char *arg, | ||
583 | const char *mail_filename); | ||
584 | /* | ||
585 | * EndJob - called when job terminates and when mail terminates | ||
586 | */ | ||
587 | static void EndJob(const char *user, CronLine *line) | ||
588 | { | 510 | { |
589 | int mailFd; | 511 | #if SETENV_LEAKS |
590 | char mailFile[128]; | 512 | safe_setenv(&G.env_var_user, "USER", pas->pw_name); |
591 | struct stat sbuf; | 513 | safe_setenv(&G.env_var_home, "HOME", pas->pw_dir); |
592 | 514 | /* if we want to set user's shell instead: */ | |
593 | /* No job */ | 515 | /*safe_setenv(G.env_var_shell, "SHELL", pas->pw_shell);*/ |
594 | if (line->cl_Pid <= 0) { | ||
595 | line->cl_Pid = 0; | ||
596 | return; | ||
597 | } | ||
598 | |||
599 | /* | ||
600 | * End of job and no mail file | ||
601 | * End of sendmail job | ||
602 | */ | ||
603 | snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, line->cl_Pid); | ||
604 | line->cl_Pid = 0; | ||
605 | |||
606 | if (line->cl_MailFlag == 0) { | ||
607 | return; | ||
608 | } | ||
609 | line->cl_MailFlag = 0; | ||
610 | |||
611 | /* | ||
612 | * End of primary job - check for mail file. If size has increased and | ||
613 | * the file is still valid, we sendmail it. | ||
614 | */ | ||
615 | mailFd = open(mailFile, O_RDONLY); | ||
616 | unlink(mailFile); | ||
617 | if (mailFd < 0) { | ||
618 | return; | ||
619 | } | ||
620 | |||
621 | if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid | ||
622 | || sbuf.st_nlink != 0 || sbuf.st_size == line->cl_MailPos | ||
623 | || !S_ISREG(sbuf.st_mode) | ||
624 | ) { | ||
625 | close(mailFd); | ||
626 | return; | ||
627 | } | ||
628 | if (line->cl_MailTo) | ||
629 | ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL); | ||
630 | } | ||
631 | #else | 516 | #else |
632 | # define EndJob(user, line) ((line)->cl_Pid = 0) | 517 | xsetenv("USER", pas->pw_name); |
518 | xsetenv("HOME", pas->pw_dir); | ||
633 | #endif | 519 | #endif |
520 | /* currently, we use constant one: */ | ||
521 | /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */ | ||
522 | } | ||
634 | 523 | ||
635 | /* | 524 | static void change_user(struct passwd *pas) |
636 | * Check for job completion, return number of jobs still running after | ||
637 | * all done. | ||
638 | */ | ||
639 | static int CheckJobs(void) | ||
640 | { | 525 | { |
641 | CronFile *file; | 526 | /* careful: we're after vfork! */ |
642 | CronLine *line; | 527 | change_identity(pas); /* - initgroups, setgid, setuid */ |
643 | int nStillRunning = 0; | 528 | if (chdir(pas->pw_dir) < 0) { |
644 | 529 | crondlog(WARN9 "chdir(%s)", pas->pw_dir); | |
645 | for (file = G.FileBase; file; file = file->cf_Next) { | 530 | if (chdir(TMPDIR) < 0) { |
646 | if (file->cf_Running) { | 531 | crondlog(DIE9 "chdir(%s)", TMPDIR); /* exits */ |
647 | file->cf_Running = 0; | ||
648 | |||
649 | for (line = file->cf_LineBase; line; line = line->cl_Next) { | ||
650 | int status, r; | ||
651 | if (line->cl_Pid <= 0) | ||
652 | continue; | ||
653 | |||
654 | r = waitpid(line->cl_Pid, &status, WNOHANG); | ||
655 | if (r < 0 || r == line->cl_Pid) { | ||
656 | EndJob(file->cf_User, line); | ||
657 | if (line->cl_Pid) { | ||
658 | file->cf_Running = 1; | ||
659 | } | ||
660 | } else if (r == 0) { | ||
661 | file->cf_Running = 1; | ||
662 | } | ||
663 | } | ||
664 | } | 532 | } |
665 | nStillRunning += file->cf_Running; | ||
666 | } | 533 | } |
667 | return nStillRunning; | ||
668 | } | 534 | } |
669 | 535 | ||
670 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL | ||
671 | |||
672 | // TODO: sendmail should be _run-time_ option, not compile-time! | 536 | // TODO: sendmail should be _run-time_ option, not compile-time! |
537 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL | ||
673 | 538 | ||
539 | //TODO: return pid (and stop passing line); | ||
540 | // stop passing mail_filename here but process it in caller | ||
674 | static void | 541 | static void |
675 | ForkJob(const char *user, CronLine *line, int mailFd, | 542 | fork_job(const char *user, CronLine *line, int mailFd, |
676 | const char *prog, const char *cmd, const char *arg, | 543 | const char *prog, const char *cmd, const char *arg, |
677 | const char *mail_filename) | 544 | const char *mail_filename) |
678 | { | 545 | { |
@@ -685,13 +552,13 @@ ForkJob(const char *user, CronLine *line, int mailFd, | |||
685 | crondlog(WARN9 "can't get uid for %s", user); | 552 | crondlog(WARN9 "can't get uid for %s", user); |
686 | goto err; | 553 | goto err; |
687 | } | 554 | } |
688 | SetEnv(pas); | 555 | set_env_vars(pas); |
689 | 556 | ||
690 | pid = vfork(); | 557 | pid = vfork(); |
691 | if (pid == 0) { | 558 | if (pid == 0) { |
692 | /* CHILD */ | 559 | /* CHILD */ |
693 | /* change running state to the user in question */ | 560 | /* initgroups, setgid, setuid, and chdir to home or TMPDIR */ |
694 | ChangeUser(pas); | 561 | change_user(pas); |
695 | if (DebugOpt) { | 562 | if (DebugOpt) { |
696 | crondlog(LVL5 "child running %s", prog); | 563 | crondlog(LVL5 "child running %s", prog); |
697 | } | 564 | } |
@@ -709,12 +576,12 @@ ForkJob(const char *user, CronLine *line, int mailFd, | |||
709 | _exit(EXIT_SUCCESS); | 576 | _exit(EXIT_SUCCESS); |
710 | } | 577 | } |
711 | 578 | ||
712 | line->cl_Pid = pid; | 579 | line->cl_pid = pid; |
713 | if (pid < 0) { | 580 | if (pid < 0) { |
714 | /* FORK FAILED */ | 581 | /* FORK FAILED */ |
715 | crondlog(ERR20 "can't vfork"); | 582 | crondlog(ERR20 "can't vfork"); |
716 | err: | 583 | err: |
717 | line->cl_Pid = 0; | 584 | line->cl_pid = 0; |
718 | if (mail_filename) { | 585 | if (mail_filename) { |
719 | unlink(mail_filename); | 586 | unlink(mail_filename); |
720 | } | 587 | } |
@@ -737,62 +604,108 @@ ForkJob(const char *user, CronLine *line, int mailFd, | |||
737 | } | 604 | } |
738 | } | 605 | } |
739 | 606 | ||
740 | static void RunJob(const char *user, CronLine *line) | 607 | static void start_one_job(const char *user, CronLine *line) |
741 | { | 608 | { |
742 | char mailFile[128]; | 609 | char mailFile[128]; |
743 | int mailFd = -1; | 610 | int mailFd = -1; |
744 | 611 | ||
745 | line->cl_Pid = 0; | 612 | line->cl_pid = 0; |
746 | line->cl_MailFlag = 0; | 613 | line->cl_mail_result = 0; |
747 | 614 | ||
748 | if (line->cl_MailTo) { | 615 | if (line->cl_mailto) { |
749 | /* open mail file - owner root so nobody can screw with it. */ | 616 | /* Open mail file (owner is root so nobody can screw with it) */ |
750 | snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid()); | 617 | snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid()); |
751 | mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600); | 618 | mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600); |
752 | 619 | ||
753 | if (mailFd >= 0) { | 620 | if (mailFd >= 0) { |
754 | line->cl_MailFlag = 1; | 621 | line->cl_mail_result = 1; |
755 | fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", line->cl_MailTo, | 622 | fdprintf(mailFd, "To: %s\nSubject: cron: %s\n\n", line->cl_mailto, |
756 | line->cl_Shell); | 623 | line->cl_cmd); |
757 | line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR); | 624 | line->cl_empty_mail_size = lseek(mailFd, 0, SEEK_CUR); |
758 | } else { | 625 | } else { |
759 | crondlog(ERR20 "can't create mail file %s for user %s, " | 626 | crondlog(ERR20 "can't create mail file %s for user %s, " |
760 | "discarding output", mailFile, user); | 627 | "discarding output", mailFile, user); |
761 | } | 628 | } |
762 | } | 629 | } |
763 | 630 | ||
764 | ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile); | 631 | fork_job(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_cmd, mailFile); |
632 | } | ||
633 | |||
634 | /* | ||
635 | * process_finished_job - called when job terminates and when mail terminates | ||
636 | */ | ||
637 | static void process_finished_job(const char *user, CronLine *line) | ||
638 | { | ||
639 | pid_t pid; | ||
640 | int mailFd; | ||
641 | char mailFile[128]; | ||
642 | struct stat sbuf; | ||
643 | |||
644 | pid = line->cl_pid; | ||
645 | line->cl_pid = 0; | ||
646 | if (pid <= 0) { | ||
647 | /* No job */ | ||
648 | return; | ||
649 | } | ||
650 | if (line->cl_mail_result == 0) { | ||
651 | /* End of job and no mail file, or end of sendmail job */ | ||
652 | return; | ||
653 | } | ||
654 | line->cl_mail_result = 0; | ||
655 | |||
656 | /* | ||
657 | * End of primary job - check for mail file. | ||
658 | * If size has changed and the file is still valid, we send it. | ||
659 | */ | ||
660 | snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, (int)pid); | ||
661 | mailFd = open(mailFile, O_RDONLY); | ||
662 | unlink(mailFile); | ||
663 | if (mailFd < 0) { | ||
664 | return; | ||
665 | } | ||
666 | |||
667 | if (fstat(mailFd, &sbuf) < 0 | ||
668 | || sbuf.st_uid != DAEMON_UID | ||
669 | || sbuf.st_nlink != 0 | ||
670 | || sbuf.st_size == line->cl_empty_mail_size | ||
671 | || !S_ISREG(sbuf.st_mode) | ||
672 | ) { | ||
673 | close(mailFd); | ||
674 | return; | ||
675 | } | ||
676 | /* if (line->cl_mailto) - always true if cl_mail_result was true */ | ||
677 | fork_job(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL); | ||
765 | } | 678 | } |
766 | 679 | ||
767 | #else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */ | 680 | #else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */ |
768 | 681 | ||
769 | static void RunJob(const char *user, CronLine *line) | 682 | static void start_one_job(const char *user, CronLine *line) |
770 | { | 683 | { |
771 | struct passwd *pas; | 684 | struct passwd *pas; |
772 | pid_t pid; | 685 | pid_t pid; |
773 | 686 | ||
774 | /* prepare things before vfork */ | ||
775 | pas = getpwnam(user); | 687 | pas = getpwnam(user); |
776 | if (!pas) { | 688 | if (!pas) { |
777 | crondlog(WARN9 "can't get uid for %s", user); | 689 | crondlog(WARN9 "can't get uid for %s", user); |
778 | goto err; | 690 | goto err; |
779 | } | 691 | } |
780 | SetEnv(pas); | ||
781 | 692 | ||
782 | /* fork as the user in question and run program */ | 693 | /* Prepare things before vfork */ |
694 | set_env_vars(pas); | ||
695 | |||
696 | /* Fork as the user in question and run program */ | ||
783 | pid = vfork(); | 697 | pid = vfork(); |
784 | if (pid == 0) { | 698 | if (pid == 0) { |
785 | /* CHILD */ | 699 | /* CHILD */ |
786 | /* change running state to the user in question */ | 700 | /* initgroups, setgid, setuid, and chdir to home or TMPDIR */ |
787 | ChangeUser(pas); | 701 | change_user(pas); |
788 | if (DebugOpt) { | 702 | if (DebugOpt) { |
789 | crondlog(LVL5 "child running %s", DEFAULT_SHELL); | 703 | crondlog(LVL5 "child running %s", DEFAULT_SHELL); |
790 | } | 704 | } |
791 | /* crond 3.0pl1-100 puts tasks in separate process groups */ | 705 | /* crond 3.0pl1-100 puts tasks in separate process groups */ |
792 | bb_setpgrp(); | 706 | bb_setpgrp(); |
793 | execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, (char *) NULL); | 707 | execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_cmd, (char *) NULL); |
794 | crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, | 708 | crondlog(ERR20 "can't execute '%s' for user %s", DEFAULT_SHELL, user); |
795 | DEFAULT_SHELL, "-c", line->cl_Shell); | ||
796 | _exit(EXIT_SUCCESS); | 709 | _exit(EXIT_SUCCESS); |
797 | } | 710 | } |
798 | if (pid < 0) { | 711 | if (pid < 0) { |
@@ -801,35 +714,130 @@ static void RunJob(const char *user, CronLine *line) | |||
801 | err: | 714 | err: |
802 | pid = 0; | 715 | pid = 0; |
803 | } | 716 | } |
804 | line->cl_Pid = pid; | 717 | line->cl_pid = pid; |
805 | } | 718 | } |
806 | 719 | ||
720 | #define process_finished_job(user, line) ((line)->cl_pid = 0) | ||
721 | |||
807 | #endif /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */ | 722 | #endif /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */ |
808 | 723 | ||
809 | static void RunJobs(void) | 724 | /* |
725 | * Determine which jobs need to be run. Under normal conditions, the | ||
726 | * period is about a minute (one scan). Worst case it will be one | ||
727 | * hour (60 scans). | ||
728 | */ | ||
729 | static void flag_starting_jobs(time_t t1, time_t t2) | ||
730 | { | ||
731 | time_t t; | ||
732 | |||
733 | /* Find jobs > t1 and <= t2 */ | ||
734 | |||
735 | for (t = t1 - t1 % 60; t <= t2; t += 60) { | ||
736 | struct tm *ptm; | ||
737 | CronFile *file; | ||
738 | CronLine *line; | ||
739 | |||
740 | if (t <= t1) | ||
741 | continue; | ||
742 | |||
743 | ptm = localtime(&t); | ||
744 | for (file = G.cron_files; file; file = file->cf_next) { | ||
745 | if (DebugOpt) | ||
746 | crondlog(LVL5 "file %s:", file->cf_username); | ||
747 | if (file->cf_deleted) | ||
748 | continue; | ||
749 | for (line = file->cf_lines; line; line = line->cl_next) { | ||
750 | if (DebugOpt) | ||
751 | crondlog(LVL5 " line %s", line->cl_cmd); | ||
752 | if (line->cl_Mins[ptm->tm_min] | ||
753 | && line->cl_Hrs[ptm->tm_hour] | ||
754 | && (line->cl_Days[ptm->tm_mday] || line->cl_Dow[ptm->tm_wday]) | ||
755 | && line->cl_Mons[ptm->tm_mon] | ||
756 | ) { | ||
757 | if (DebugOpt) { | ||
758 | crondlog(LVL5 " job: %d %s", | ||
759 | (int)line->cl_pid, line->cl_cmd); | ||
760 | } | ||
761 | if (line->cl_pid > 0) { | ||
762 | crondlog(LVL8 "user %s: process already running: %s", | ||
763 | file->cf_username, line->cl_cmd); | ||
764 | } else if (line->cl_pid == 0) { | ||
765 | line->cl_pid = -1; | ||
766 | file->cf_wants_starting = 1; | ||
767 | } | ||
768 | } | ||
769 | } | ||
770 | } | ||
771 | } | ||
772 | } | ||
773 | |||
774 | static void start_jobs(void) | ||
810 | { | 775 | { |
811 | CronFile *file; | 776 | CronFile *file; |
812 | CronLine *line; | 777 | CronLine *line; |
813 | 778 | ||
814 | for (file = G.FileBase; file; file = file->cf_Next) { | 779 | for (file = G.cron_files; file; file = file->cf_next) { |
815 | if (!file->cf_Ready) | 780 | if (!file->cf_wants_starting) |
816 | continue; | 781 | continue; |
817 | 782 | ||
818 | file->cf_Ready = 0; | 783 | file->cf_wants_starting = 0; |
819 | for (line = file->cf_LineBase; line; line = line->cl_Next) { | 784 | for (line = file->cf_lines; line; line = line->cl_next) { |
820 | if (line->cl_Pid >= 0) | 785 | pid_t pid; |
786 | if (line->cl_pid >= 0) | ||
821 | continue; | 787 | continue; |
822 | 788 | ||
823 | RunJob(file->cf_User, line); | 789 | start_one_job(file->cf_username, line); |
790 | pid = line->cl_pid; | ||
824 | crondlog(LVL8 "USER %s pid %3d cmd %s", | 791 | crondlog(LVL8 "USER %s pid %3d cmd %s", |
825 | file->cf_User, (int)line->cl_Pid, line->cl_Shell); | 792 | file->cf_username, (int)pid, line->cl_cmd); |
826 | if (line->cl_Pid < 0) { | 793 | if (pid < 0) { |
827 | file->cf_Ready = 1; | 794 | file->cf_wants_starting = 1; |
828 | } else if (line->cl_Pid > 0) { | 795 | } |
829 | file->cf_Running = 1; | 796 | if (pid > 0) { |
797 | file->cf_has_running = 1; | ||
798 | } | ||
799 | } | ||
800 | } | ||
801 | } | ||
802 | |||
803 | /* | ||
804 | * Check for job completion, return number of jobs still running after | ||
805 | * all done. | ||
806 | */ | ||
807 | static int check_completions(void) | ||
808 | { | ||
809 | CronFile *file; | ||
810 | CronLine *line; | ||
811 | int num_still_running = 0; | ||
812 | |||
813 | for (file = G.cron_files; file; file = file->cf_next) { | ||
814 | if (!file->cf_has_running) | ||
815 | continue; | ||
816 | |||
817 | file->cf_has_running = 0; | ||
818 | for (line = file->cf_lines; line; line = line->cl_next) { | ||
819 | int r; | ||
820 | |||
821 | if (line->cl_pid <= 0) | ||
822 | continue; | ||
823 | |||
824 | r = waitpid(line->cl_pid, NULL, WNOHANG); | ||
825 | if (r < 0 || r == line->cl_pid) { | ||
826 | process_finished_job(file->cf_username, line); | ||
827 | if (line->cl_pid == 0) { | ||
828 | /* sendmail was not started for it */ | ||
829 | continue; | ||
830 | } | ||
831 | /* else: sendmail was started, job is still running, fall thru */ | ||
830 | } | 832 | } |
833 | /* else: r == 0: "process is still running" */ | ||
834 | file->cf_has_running = 1; | ||
831 | } | 835 | } |
836 | //FIXME: if !file->cf_has_running && file->deleted: delete it! | ||
837 | //otherwise deleted entries will stay forever, right? | ||
838 | num_still_running += file->cf_has_running; | ||
832 | } | 839 | } |
840 | return num_still_running; | ||
833 | } | 841 | } |
834 | 842 | ||
835 | int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 843 | int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
@@ -846,9 +854,9 @@ int crond_main(int argc UNUSED_PARAM, char **argv) | |||
846 | opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l") | 854 | opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l") |
847 | ":l+:d+"; /* -l and -d have numeric param */ | 855 | ":l+:d+"; /* -l and -d have numeric param */ |
848 | opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"), | 856 | opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"), |
849 | &G.LogLevel, &G.LogFile, &G.CDir | 857 | &G.log_level, &G.log_filename, &G.crontab_dir_name |
850 | IF_FEATURE_CROND_D(,&G.LogLevel)); | 858 | IF_FEATURE_CROND_D(,&G.log_level)); |
851 | /* both -d N and -l N set the same variable: G.LogLevel */ | 859 | /* both -d N and -l N set the same variable: G.log_level */ |
852 | 860 | ||
853 | if (!(opts & OPT_f)) { | 861 | if (!(opts & OPT_f)) { |
854 | /* close stdin, stdout, stderr. | 862 | /* close stdin, stdout, stderr. |
@@ -856,17 +864,17 @@ int crond_main(int argc UNUSED_PARAM, char **argv) | |||
856 | bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); | 864 | bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); |
857 | } | 865 | } |
858 | 866 | ||
859 | if (!(opts & OPT_d) && G.LogFile == NULL) { | 867 | if (!(opts & OPT_d) && G.log_filename == NULL) { |
860 | /* logging to syslog */ | 868 | /* logging to syslog */ |
861 | openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); | 869 | openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); |
862 | logmode = LOGMODE_SYSLOG; | 870 | logmode = LOGMODE_SYSLOG; |
863 | } | 871 | } |
864 | 872 | ||
865 | xchdir(G.CDir); | 873 | xchdir(G.crontab_dir_name); |
866 | //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ | 874 | //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ |
867 | xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */ | 875 | xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */ |
868 | crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", G.LogLevel); | 876 | crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", G.log_level); |
869 | SynchronizeDir(); | 877 | rescan_crontab_dir(); |
870 | write_pidfile("/var/run/crond.pid"); | 878 | write_pidfile("/var/run/crond.pid"); |
871 | 879 | ||
872 | /* Main loop */ | 880 | /* Main loop */ |
@@ -902,15 +910,17 @@ int crond_main(int argc UNUSED_PARAM, char **argv) | |||
902 | * When running jobs, the inequality used is greater but not | 910 | * When running jobs, the inequality used is greater but not |
903 | * equal to t1, and less then or equal to t2. | 911 | * equal to t1, and less then or equal to t2. |
904 | */ | 912 | */ |
905 | if (stat(G.CDir, &sbuf) == 0 && G.CDir_mtime != sbuf.st_mtime) { | 913 | if (stat(G.crontab_dir_name, &sbuf) != 0) |
906 | G.CDir_mtime = sbuf.st_mtime; | 914 | sbuf.st_mtime = 0; /* force update (once) if dir was deleted */ |
915 | if (G.crontab_dir_mtime != sbuf.st_mtime) { | ||
916 | G.crontab_dir_mtime = sbuf.st_mtime; | ||
907 | rescan = 1; | 917 | rescan = 1; |
908 | } | 918 | } |
909 | if (--rescan == 0) { | 919 | if (--rescan == 0) { |
910 | rescan = 60; | 920 | rescan = 60; |
911 | SynchronizeDir(); | 921 | rescan_crontab_dir(); |
912 | } | 922 | } |
913 | CheckUpdates(); | 923 | process_cron_update_file(); |
914 | if (DebugOpt) | 924 | if (DebugOpt) |
915 | crondlog(LVL5 "wakeup dt=%ld", dt); | 925 | crondlog(LVL5 "wakeup dt=%ld", dt); |
916 | if (dt < -60 * 60 || dt > 60 * 60) { | 926 | if (dt < -60 * 60 || dt > 60 * 60) { |
@@ -918,9 +928,10 @@ int crond_main(int argc UNUSED_PARAM, char **argv) | |||
918 | /* and we do not run any jobs in this case */ | 928 | /* and we do not run any jobs in this case */ |
919 | } else if (dt > 0) { | 929 | } else if (dt > 0) { |
920 | /* Usual case: time advances forward, as expected */ | 930 | /* Usual case: time advances forward, as expected */ |
921 | TestJobs(t1, t2); | 931 | flag_starting_jobs(t1, t2); |
922 | RunJobs(); | 932 | start_jobs(); |
923 | if (CheckJobs() > 0) { | 933 | if (check_completions() > 0) { |
934 | /* some jobs are still running */ | ||
924 | sleep_time = 10; | 935 | sleep_time = 10; |
925 | } else { | 936 | } else { |
926 | sleep_time = 60; | 937 | sleep_time = 60; |