aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2010-07-08 04:07:15 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2010-07-08 04:07:15 +0200
commit4a09aefae2523bf242039e45c9f85bd1f35b72ad (patch)
tree16f529eb0b832445c588893fefbacd938d4a8437
parent9388b4e72051b031b84a1345cc763757dbd9a699 (diff)
downloadbusybox-w32-4a09aefae2523bf242039e45c9f85bd1f35b72ad.tar.gz
busybox-w32-4a09aefae2523bf242039e45c9f85bd1f35b72ad.tar.bz2
busybox-w32-4a09aefae2523bf242039e45c9f85bd1f35b72ad.zip
crond: reorder functions to follow usual order: "main last"
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--miscutils/crond.c452
1 files changed, 221 insertions, 231 deletions
diff --git a/miscutils/crond.c b/miscutils/crond.c
index 49ecf745e..06d72ced9 100644
--- a/miscutils/crond.c
+++ b/miscutils/crond.c
@@ -17,25 +17,25 @@
17/* glibc frees previous setenv'ed value when we do next setenv() 17/* glibc frees previous setenv'ed value when we do next setenv()
18 * of the same variable. uclibc does not do this! */ 18 * of the same variable. uclibc does not do this! */
19#if (defined(__GLIBC__) && !defined(__UCLIBC__)) /* || OTHER_SAFE_LIBC... */ 19#if (defined(__GLIBC__) && !defined(__UCLIBC__)) /* || OTHER_SAFE_LIBC... */
20#define SETENV_LEAKS 0 20# define SETENV_LEAKS 0
21#else 21#else
22#define SETENV_LEAKS 1 22# define SETENV_LEAKS 1
23#endif 23#endif
24 24
25 25
26#define TMPDIR CONFIG_FEATURE_CROND_DIR 26#define TMPDIR CONFIG_FEATURE_CROND_DIR
27#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs" 27#define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs"
28#ifndef SENDMAIL 28#ifndef SENDMAIL
29#define SENDMAIL "sendmail" 29# define SENDMAIL "sendmail"
30#endif 30#endif
31#ifndef SENDMAIL_ARGS 31#ifndef SENDMAIL_ARGS
32#define SENDMAIL_ARGS "-ti", NULL 32# define SENDMAIL_ARGS "-ti", NULL
33#endif 33#endif
34#ifndef CRONUPDATE 34#ifndef CRONUPDATE
35#define CRONUPDATE "cron.update" 35# define CRONUPDATE "cron.update"
36#endif 36#endif
37#ifndef MAXLINES 37#ifndef MAXLINES
38#define MAXLINES 256 /* max lines in non-root crontabs */ 38# define MAXLINES 256 /* max lines in non-root crontabs */
39#endif 39#endif
40 40
41 41
@@ -108,20 +108,6 @@ struct globals {
108} while (0) 108} while (0)
109 109
110 110
111static void CheckUpdates(void);
112static void SynchronizeDir(void);
113static int TestJobs(time_t t1, time_t t2);
114static void RunJobs(void);
115static int CheckJobs(void);
116static void RunJob(const char *user, CronLine *line);
117#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
118static void EndJob(const char *user, CronLine *line);
119#else
120#define EndJob(user, line) ((line)->cl_Pid = 0)
121#endif
122static void DeleteFile(const char *userName);
123
124
125/* 0 is the most verbose, default 8 */ 111/* 0 is the most verbose, default 8 */
126#define LVL5 "\x05" 112#define LVL5 "\x05"
127#define LVL7 "\x07" 113#define LVL7 "\x07"
@@ -163,101 +149,6 @@ static void crondlog(const char *ctl, ...)
163 exit(20); 149 exit(20);
164} 150}
165 151
166int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
167int crond_main(int argc UNUSED_PARAM, char **argv)
168{
169 time_t t2;
170 int rescan;
171 int sleep_time;
172 unsigned opts;
173
174 INIT_G();
175
176 /* "-b after -f is ignored", and so on for every pair a-b */
177 opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l")
178 ":l+:d+"; /* -l and -d have numeric param */
179 opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"),
180 &LogLevel, &LogFile, &CDir
181 IF_FEATURE_CROND_D(,&LogLevel));
182 /* both -d N and -l N set the same variable: LogLevel */
183
184 if (!(opts & OPT_f)) {
185 /* close stdin, stdout, stderr.
186 * close unused descriptors - don't need them. */
187 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
188 }
189
190 if (!(opts & OPT_d) && LogFile == NULL) {
191 /* logging to syslog */
192 openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
193 logmode = LOGMODE_SYSLOG;
194 }
195
196 xchdir(CDir);
197 //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
198 xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */
199 crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", LogLevel);
200 SynchronizeDir();
201 write_pidfile("/var/run/crond.pid");
202
203 /* main loop - synchronize to 1 second after the minute, minimum sleep
204 * of 1 second. */
205 t2 = time(NULL);
206 rescan = 60;
207 sleep_time = 60;
208 for (;;) {
209 time_t t1;
210 long dt;
211
212 t1 = t2;
213 sleep((sleep_time + 1) - (time(NULL) % sleep_time));
214
215 t2 = time(NULL);
216 dt = (long)t2 - (long)t1;
217
218 /*
219 * The file 'cron.update' is checked to determine new cron
220 * jobs. The directory is rescanned once an hour to deal
221 * with any screwups.
222 *
223 * Check for time jump. Disparities over an hour either way
224 * result in resynchronization. A negative disparity
225 * less than an hour causes us to effectively sleep until we
226 * match the original time (i.e. no re-execution of jobs that
227 * have just been run). A positive disparity less than
228 * an hour causes intermediate jobs to be run, but only once
229 * in the worst case.
230 *
231 * When running jobs, the inequality used is greater but not
232 * equal to t1, and less then or equal to t2.
233 */
234 if (--rescan == 0) {
235 rescan = 60;
236 SynchronizeDir();
237 }
238 CheckUpdates();
239 if (DebugOpt)
240 crondlog(LVL5 "wakeup dt=%ld", dt);
241 if (dt < -60 * 60 || dt > 60 * 60) {
242 crondlog(WARN9 "time disparity of %ld minutes detected", dt / 60);
243 /* and we do not run any jobs in this case */
244 } else if (dt > 0) {
245 /* Usual case: time advances forwad, as expected */
246 TestJobs(t1, t2);
247 RunJobs();
248 sleep(5);
249 if (CheckJobs() > 0) {
250 sleep_time = 10;
251 } else {
252 sleep_time = 60;
253 }
254 }
255 /* else: time jumped back, do not run any jobs */
256 } /* for (;;) */
257
258 return 0; /* not reached */
259}
260
261#if SETENV_LEAKS 152#if SETENV_LEAKS
262/* We set environment *before* vfork (because we want to use vfork), 153/* We set environment *before* vfork (because we want to use vfork),
263 * so we cannot use setenv() - repeated calls to setenv() may leak memory! 154 * so we cannot use setenv() - repeated calls to setenv() may leak memory!
@@ -452,6 +343,48 @@ static void FixDayDow(CronLine *line)
452 } 343 }
453} 344}
454 345
346/*
347 * DeleteFile() - delete user database
348 *
349 * Note: multiple entries for same user may exist if we were unable to
350 * completely delete a database due to running processes.
351 */
352static void DeleteFile(const char *userName)
353{
354 CronFile **pfile = &FileBase;
355 CronFile *file;
356
357 while ((file = *pfile) != NULL) {
358 if (strcmp(userName, file->cf_User) == 0) {
359 CronLine **pline = &file->cf_LineBase;
360 CronLine *line;
361
362 file->cf_Running = 0;
363 file->cf_Deleted = 1;
364
365 while ((line = *pline) != NULL) {
366 if (line->cl_Pid > 0) {
367 file->cf_Running = 1;
368 pline = &line->cl_Next;
369 } else {
370 *pline = line->cl_Next;
371 free(line->cl_Shell);
372 free(line);
373 }
374 }
375 if (file->cf_Running == 0) {
376 *pfile = file->cf_Next;
377 free(file->cf_User);
378 free(file);
379 } else {
380 pfile = &file->cf_Next;
381 }
382 } else {
383 pfile = &file->cf_Next;
384 }
385 }
386}
387
455static void SynchronizeFile(const char *fileName) 388static void SynchronizeFile(const char *fileName)
456{ 389{
457 struct parser_t *parser; 390 struct parser_t *parser;
@@ -600,51 +533,7 @@ static void SynchronizeDir(void)
600} 533}
601 534
602/* 535/*
603 * DeleteFile() - delete user database 536 * Determine which jobs need to be run. Under normal conditions, the
604 *
605 * Note: multiple entries for same user may exist if we were unable to
606 * completely delete a database due to running processes.
607 */
608static void DeleteFile(const char *userName)
609{
610 CronFile **pfile = &FileBase;
611 CronFile *file;
612
613 while ((file = *pfile) != NULL) {
614 if (strcmp(userName, file->cf_User) == 0) {
615 CronLine **pline = &file->cf_LineBase;
616 CronLine *line;
617
618 file->cf_Running = 0;
619 file->cf_Deleted = 1;
620
621 while ((line = *pline) != NULL) {
622 if (line->cl_Pid > 0) {
623 file->cf_Running = 1;
624 pline = &line->cl_Next;
625 } else {
626 *pline = line->cl_Next;
627 free(line->cl_Shell);
628 free(line);
629 }
630 }
631 if (file->cf_Running == 0) {
632 *pfile = file->cf_Next;
633 free(file->cf_User);
634 free(file);
635 } else {
636 pfile = &file->cf_Next;
637 }
638 } else {
639 pfile = &file->cf_Next;
640 }
641 }
642}
643
644/*
645 * TestJobs()
646 *
647 * determine which jobs need to be run. Under normal conditions, the
648 * period is about a minute (one scan). Worst case it will be one 537 * period is about a minute (one scan). Worst case it will be one
649 * hour (60 scans). 538 * hour (60 scans).
650 */ 539 */
@@ -695,35 +584,63 @@ static int TestJobs(time_t t1, time_t t2)
695 return nJobs; 584 return nJobs;
696} 585}
697 586
698static void RunJobs(void) 587#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
588static void
589ForkJob(const char *user, CronLine *line, int mailFd,
590 const char *prog, const char *cmd, const char *arg,
591 const char *mail_filename);
592/*
593 * EndJob - called when job terminates and when mail terminates
594 */
595static void EndJob(const char *user, CronLine *line)
699{ 596{
700 CronFile *file; 597 int mailFd;
701 CronLine *line; 598 char mailFile[128];
599 struct stat sbuf;
702 600
703 for (file = FileBase; file; file = file->cf_Next) { 601 /* No job */
704 if (!file->cf_Ready) 602 if (line->cl_Pid <= 0) {
705 continue; 603 line->cl_Pid = 0;
604 return;
605 }
706 606
707 file->cf_Ready = 0; 607 /*
708 for (line = file->cf_LineBase; line; line = line->cl_Next) { 608 * End of job and no mail file
709 if (line->cl_Pid >= 0) 609 * End of sendmail job
710 continue; 610 */
611 snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, line->cl_Pid);
612 line->cl_Pid = 0;
711 613
712 RunJob(file->cf_User, line); 614 if (line->cl_MailFlag == 0) {
713 crondlog(LVL8 "USER %s pid %3d cmd %s", 615 return;
714 file->cf_User, (int)line->cl_Pid, line->cl_Shell);
715 if (line->cl_Pid < 0) {
716 file->cf_Ready = 1;
717 } else if (line->cl_Pid > 0) {
718 file->cf_Running = 1;
719 }
720 }
721 } 616 }
617 line->cl_MailFlag = 0;
618
619 /*
620 * End of primary job - check for mail file. If size has increased and
621 * the file is still valid, we sendmail it.
622 */
623 mailFd = open(mailFile, O_RDONLY);
624 unlink(mailFile);
625 if (mailFd < 0) {
626 return;
627 }
628
629 if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid
630 || sbuf.st_nlink != 0 || sbuf.st_size == line->cl_MailPos
631 || !S_ISREG(sbuf.st_mode)
632 ) {
633 close(mailFd);
634 return;
635 }
636 if (line->cl_MailTo)
637 ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
722} 638}
639#else
640# define EndJob(user, line) ((line)->cl_Pid = 0)
641#endif
723 642
724/* 643/*
725 * CheckJobs() - check for job completion
726 *
727 * Check for job completion, return number of jobs still running after 644 * Check for job completion, return number of jobs still running after
728 * all done. 645 * all done.
729 */ 646 */
@@ -855,55 +772,7 @@ static void RunJob(const char *user, CronLine *line)
855 ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile); 772 ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile);
856} 773}
857 774
858/* 775#else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */
859 * EndJob - called when job terminates and when mail terminates
860 */
861static void EndJob(const char *user, CronLine *line)
862{
863 int mailFd;
864 char mailFile[128];
865 struct stat sbuf;
866
867 /* No job */
868 if (line->cl_Pid <= 0) {
869 line->cl_Pid = 0;
870 return;
871 }
872
873 /*
874 * End of job and no mail file
875 * End of sendmail job
876 */
877 snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, line->cl_Pid);
878 line->cl_Pid = 0;
879
880 if (line->cl_MailFlag == 0) {
881 return;
882 }
883 line->cl_MailFlag = 0;
884
885 /*
886 * End of primary job - check for mail file. If size has increased and
887 * the file is still valid, we sendmail it.
888 */
889 mailFd = open(mailFile, O_RDONLY);
890 unlink(mailFile);
891 if (mailFd < 0) {
892 return;
893 }
894
895 if (fstat(mailFd, &sbuf) < 0 || sbuf.st_uid != DaemonUid
896 || sbuf.st_nlink != 0 || sbuf.st_size == line->cl_MailPos
897 || !S_ISREG(sbuf.st_mode)
898 ) {
899 close(mailFd);
900 return;
901 }
902 if (line->cl_MailTo)
903 ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL);
904}
905
906#else /* crond without sendmail */
907 776
908static void RunJob(const char *user, CronLine *line) 777static void RunJob(const char *user, CronLine *line)
909{ 778{
@@ -943,4 +812,125 @@ static void RunJob(const char *user, CronLine *line)
943 line->cl_Pid = pid; 812 line->cl_Pid = pid;
944} 813}
945 814
946#endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */ 815#endif /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */
816
817static void RunJobs(void)
818{
819 CronFile *file;
820 CronLine *line;
821
822 for (file = FileBase; file; file = file->cf_Next) {
823 if (!file->cf_Ready)
824 continue;
825
826 file->cf_Ready = 0;
827 for (line = file->cf_LineBase; line; line = line->cl_Next) {
828 if (line->cl_Pid >= 0)
829 continue;
830
831 RunJob(file->cf_User, line);
832 crondlog(LVL8 "USER %s pid %3d cmd %s",
833 file->cf_User, (int)line->cl_Pid, line->cl_Shell);
834 if (line->cl_Pid < 0) {
835 file->cf_Ready = 1;
836 } else if (line->cl_Pid > 0) {
837 file->cf_Running = 1;
838 }
839 }
840 }
841}
842
843int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
844int crond_main(int argc UNUSED_PARAM, char **argv)
845{
846 time_t t2;
847 int rescan;
848 int sleep_time;
849 unsigned opts;
850
851 INIT_G();
852
853 /* "-b after -f is ignored", and so on for every pair a-b */
854 opt_complementary = "f-b:b-f:S-L:L-S" IF_FEATURE_CROND_D(":d-l")
855 ":l+:d+"; /* -l and -d have numeric param */
856 opts = getopt32(argv, "l:L:fbSc:" IF_FEATURE_CROND_D("d:"),
857 &LogLevel, &LogFile, &CDir
858 IF_FEATURE_CROND_D(,&LogLevel));
859 /* both -d N and -l N set the same variable: LogLevel */
860
861 if (!(opts & OPT_f)) {
862 /* close stdin, stdout, stderr.
863 * close unused descriptors - don't need them. */
864 bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
865 }
866
867 if (!(opts & OPT_d) && LogFile == NULL) {
868 /* logging to syslog */
869 openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON);
870 logmode = LOGMODE_SYSLOG;
871 }
872
873 xchdir(CDir);
874 //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */
875 xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */
876 crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", LogLevel);
877 SynchronizeDir();
878 write_pidfile("/var/run/crond.pid");
879
880 /* main loop - synchronize to 1 second after the minute, minimum sleep
881 * of 1 second. */
882 t2 = time(NULL);
883 rescan = 60;
884 sleep_time = 60;
885 for (;;) {
886 time_t t1;
887 long dt;
888
889 t1 = t2;
890 sleep((sleep_time + 1) - (time(NULL) % sleep_time));
891
892 t2 = time(NULL);
893 dt = (long)t2 - (long)t1;
894
895 /*
896 * The file 'cron.update' is checked to determine new cron
897 * jobs. The directory is rescanned once an hour to deal
898 * with any screwups.
899 *
900 * Check for time jump. Disparities over an hour either way
901 * result in resynchronization. A negative disparity
902 * less than an hour causes us to effectively sleep until we
903 * match the original time (i.e. no re-execution of jobs that
904 * have just been run). A positive disparity less than
905 * an hour causes intermediate jobs to be run, but only once
906 * in the worst case.
907 *
908 * When running jobs, the inequality used is greater but not
909 * equal to t1, and less then or equal to t2.
910 */
911 if (--rescan == 0) {
912 rescan = 60;
913 SynchronizeDir();
914 }
915 CheckUpdates();
916 if (DebugOpt)
917 crondlog(LVL5 "wakeup dt=%ld", dt);
918 if (dt < -60 * 60 || dt > 60 * 60) {
919 crondlog(WARN9 "time disparity of %ld minutes detected", dt / 60);
920 /* and we do not run any jobs in this case */
921 } else if (dt > 0) {
922 /* Usual case: time advances forwad, as expected */
923 TestJobs(t1, t2);
924 RunJobs();
925 sleep(5);
926 if (CheckJobs() > 0) {
927 sleep_time = 10;
928 } else {
929 sleep_time = 60;
930 }
931 }
932 /* else: time jumped back, do not run any jobs */
933 } /* for (;;) */
934
935 return 0; /* not reached */
936}