diff options
author | Ron Yorston <rmy@pobox.com> | 2014-05-06 20:41:10 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2014-05-06 20:41:10 +0100 |
commit | d3bef66324a8ca5eed9ad7c15ead3a1cc9a9151e (patch) | |
tree | 4b364ba4b6b9e96c2629fe382fef0248d76833dd /miscutils | |
parent | 7905d97aeece18da362a5a1e066abff2d2e5c16b (diff) | |
parent | d257608a8429b64e1a04c7cb6d99975eeb2c3955 (diff) | |
download | busybox-w32-d3bef66324a8ca5eed9ad7c15ead3a1cc9a9151e.tar.gz busybox-w32-d3bef66324a8ca5eed9ad7c15ead3a1cc9a9151e.tar.bz2 busybox-w32-d3bef66324a8ca5eed9ad7c15ead3a1cc9a9151e.zip |
Merge branch 'busybox' into merge
Conflicts:
debianutils/which.c
editors/vi.c
libbb/executable.c
Diffstat (limited to 'miscutils')
-rw-r--r-- | miscutils/Config.src | 34 | ||||
-rw-r--r-- | miscutils/Kbuild.src | 1 | ||||
-rw-r--r-- | miscutils/adjtimex.c | 25 | ||||
-rw-r--r-- | miscutils/crond.c | 332 | ||||
-rw-r--r-- | miscutils/less.c | 38 |
5 files changed, 232 insertions, 198 deletions
diff --git a/miscutils/Config.src b/miscutils/Config.src index 1da9800bd..1b2a3ae9a 100644 --- a/miscutils/Config.src +++ b/miscutils/Config.src | |||
@@ -133,40 +133,6 @@ config CHRT | |||
133 | manipulate real-time attributes of a process. | 133 | manipulate real-time attributes of a process. |
134 | This requires sched_{g,s}etparam support in your libc. | 134 | This requires sched_{g,s}etparam support in your libc. |
135 | 135 | ||
136 | config CROND | ||
137 | bool "crond" | ||
138 | default y | ||
139 | select FEATURE_SYSLOG | ||
140 | help | ||
141 | Crond is a background daemon that parses individual crontab | ||
142 | files and executes commands on behalf of the users in question. | ||
143 | This is a port of dcron from slackware. It uses files of the | ||
144 | format /var/spool/cron/crontabs/<username> files, for example: | ||
145 | $ cat /var/spool/cron/crontabs/root | ||
146 | # Run daily cron jobs at 4:40 every day: | ||
147 | 40 4 * * * /etc/cron/daily > /dev/null 2>&1 | ||
148 | |||
149 | config FEATURE_CROND_D | ||
150 | bool "Support option -d to redirect output to stderr" | ||
151 | depends on CROND | ||
152 | default y | ||
153 | help | ||
154 | -d sets loglevel to 0 (most verbose) and directs all output to stderr. | ||
155 | |||
156 | config FEATURE_CROND_CALL_SENDMAIL | ||
157 | bool "Report command output via email (using sendmail)" | ||
158 | default y | ||
159 | depends on CROND | ||
160 | help | ||
161 | Command output will be sent to corresponding user via email. | ||
162 | |||
163 | config FEATURE_CROND_DIR | ||
164 | string "crond spool directory" | ||
165 | default "/var/spool/cron" | ||
166 | depends on CROND || CRONTAB | ||
167 | help | ||
168 | Location of crond spool. | ||
169 | |||
170 | config CRONTAB | 136 | config CRONTAB |
171 | bool "crontab" | 137 | bool "crontab" |
172 | default y | 138 | default y |
diff --git a/miscutils/Kbuild.src b/miscutils/Kbuild.src index 9e164f16e..8eaa82de9 100644 --- a/miscutils/Kbuild.src +++ b/miscutils/Kbuild.src | |||
@@ -12,7 +12,6 @@ lib-$(CONFIG_BBCONFIG) += bbconfig.o | |||
12 | lib-$(CONFIG_BEEP) += beep.o | 12 | lib-$(CONFIG_BEEP) += beep.o |
13 | lib-$(CONFIG_CHAT) += chat.o | 13 | lib-$(CONFIG_CHAT) += chat.o |
14 | lib-$(CONFIG_CHRT) += chrt.o | 14 | lib-$(CONFIG_CHRT) += chrt.o |
15 | lib-$(CONFIG_CROND) += crond.o | ||
16 | lib-$(CONFIG_CRONTAB) += crontab.o | 15 | lib-$(CONFIG_CRONTAB) += crontab.o |
17 | lib-$(CONFIG_DC) += dc.o | 16 | lib-$(CONFIG_DC) += dc.o |
18 | lib-$(CONFIG_DEVFSD) += devfsd.o | 17 | lib-$(CONFIG_DEVFSD) += devfsd.o |
diff --git a/miscutils/adjtimex.c b/miscutils/adjtimex.c index c8816e9e7..534364a69 100644 --- a/miscutils/adjtimex.c +++ b/miscutils/adjtimex.c | |||
@@ -14,12 +14,12 @@ | |||
14 | //usage:#define adjtimex_trivial_usage | 14 | //usage:#define adjtimex_trivial_usage |
15 | //usage: "[-q] [-o OFF] [-f FREQ] [-p TCONST] [-t TICK]" | 15 | //usage: "[-q] [-o OFF] [-f FREQ] [-p TCONST] [-t TICK]" |
16 | //usage:#define adjtimex_full_usage "\n\n" | 16 | //usage:#define adjtimex_full_usage "\n\n" |
17 | //usage: "Read and optionally set system timebase parameters. See adjtimex(2)\n" | 17 | //usage: "Read or set kernel time variables. See adjtimex(2)\n" |
18 | //usage: "\n -q Quiet" | 18 | //usage: "\n -q Quiet" |
19 | //usage: "\n -o OFF Time offset, microseconds" | 19 | //usage: "\n -o OFF Time offset, microseconds" |
20 | //usage: "\n -f FREQ Frequency adjust, integer kernel units (65536 is 1ppm)" | 20 | //usage: "\n -f FREQ Frequency adjust, integer kernel units (65536 is 1ppm)" |
21 | //usage: "\n (positive values make clock run faster)" | ||
22 | //usage: "\n -t TICK Microseconds per tick, usually 10000" | 21 | //usage: "\n -t TICK Microseconds per tick, usually 10000" |
22 | //usage: "\n (positive -t or -f values make clock run faster)" | ||
23 | //usage: "\n -p TCONST" | 23 | //usage: "\n -p TCONST" |
24 | 24 | ||
25 | #include "libbb.h" | 25 | #include "libbb.h" |
@@ -111,13 +111,13 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv) | |||
111 | } | 111 | } |
112 | 112 | ||
113 | if (!(opt & OPT_quiet)) { | 113 | if (!(opt & OPT_quiet)) { |
114 | int sep; | 114 | const char *sep; |
115 | const char *name; | 115 | const char *name; |
116 | 116 | ||
117 | printf( | 117 | printf( |
118 | " mode: %d\n" | 118 | " mode: %d\n" |
119 | "-o offset: %ld\n" | 119 | "-o offset: %ld us\n" |
120 | "-f frequency: %ld\n" | 120 | "-f freq.adjust: %ld (65536 = 1ppm)\n" |
121 | " maxerror: %ld\n" | 121 | " maxerror: %ld\n" |
122 | " esterror: %ld\n" | 122 | " esterror: %ld\n" |
123 | " status: %d (", | 123 | " status: %d (", |
@@ -125,15 +125,14 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv) | |||
125 | txc.esterror, txc.status); | 125 | txc.esterror, txc.status); |
126 | 126 | ||
127 | /* representative output of next code fragment: | 127 | /* representative output of next code fragment: |
128 | "PLL | PPSTIME" */ | 128 | * "PLL | PPSTIME" |
129 | */ | ||
129 | name = statlist_name; | 130 | name = statlist_name; |
130 | sep = 0; | 131 | sep = ""; |
131 | for (i = 0; statlist_bit[i]; i++) { | 132 | for (i = 0; statlist_bit[i]; i++) { |
132 | if (txc.status & statlist_bit[i]) { | 133 | if (txc.status & statlist_bit[i]) { |
133 | if (sep) | 134 | printf("%s%s", sep, name); |
134 | fputs(" | ", stdout); | 135 | sep = " | "; |
135 | fputs(name, stdout); | ||
136 | sep = 1; | ||
137 | } | 136 | } |
138 | name += strlen(name) + 1; | 137 | name += strlen(name) + 1; |
139 | } | 138 | } |
@@ -143,9 +142,9 @@ int adjtimex_main(int argc UNUSED_PARAM, char **argv) | |||
143 | descript = nth_string(ret_code_descript, ret); | 142 | descript = nth_string(ret_code_descript, ret); |
144 | printf(")\n" | 143 | printf(")\n" |
145 | "-p timeconstant: %ld\n" | 144 | "-p timeconstant: %ld\n" |
146 | " precision: %ld\n" | 145 | " precision: %ld us\n" |
147 | " tolerance: %ld\n" | 146 | " tolerance: %ld\n" |
148 | "-t tick: %ld\n" | 147 | "-t tick: %ld us\n" |
149 | " time.tv_sec: %ld\n" | 148 | " time.tv_sec: %ld\n" |
150 | " time.tv_usec: %ld\n" | 149 | " time.tv_usec: %ld\n" |
151 | " return value: %d (%s)\n", | 150 | " return value: %d (%s)\n", |
diff --git a/miscutils/crond.c b/miscutils/crond.c index 582dc991a..3659b9a6f 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c | |||
@@ -1,7 +1,5 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | 1 | /* vi: set sw=4 ts=4: */ |
2 | /* | 2 | /* |
3 | * crond -d[#] -c <crondir> -f -b | ||
4 | * | ||
5 | * run as root, but NOT setuid root | 3 | * run as root, but NOT setuid root |
6 | * | 4 | * |
7 | * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com) | 5 | * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com) |
@@ -10,6 +8,43 @@ | |||
10 | * | 8 | * |
11 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 9 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
12 | */ | 10 | */ |
11 | //config:config CROND | ||
12 | //config: bool "crond" | ||
13 | //config: default y | ||
14 | //config: select FEATURE_SYSLOG | ||
15 | //config: help | ||
16 | //config: Crond is a background daemon that parses individual crontab | ||
17 | //config: files and executes commands on behalf of the users in question. | ||
18 | //config: This is a port of dcron from slackware. It uses files of the | ||
19 | //config: format /var/spool/cron/crontabs/<username> files, for example: | ||
20 | //config: $ cat /var/spool/cron/crontabs/root | ||
21 | //config: # Run daily cron jobs at 4:40 every day: | ||
22 | //config: 40 4 * * * /etc/cron/daily > /dev/null 2>&1 | ||
23 | //config: | ||
24 | //config:config FEATURE_CROND_D | ||
25 | //config: bool "Support option -d to redirect output to stderr" | ||
26 | //config: depends on CROND | ||
27 | //config: default y | ||
28 | //config: help | ||
29 | //config: -d N sets loglevel (0:most verbose) and directs all output to stderr. | ||
30 | //config: | ||
31 | //config:config FEATURE_CROND_CALL_SENDMAIL | ||
32 | //config: bool "Report command output via email (using sendmail)" | ||
33 | //config: default y | ||
34 | //config: depends on CROND | ||
35 | //config: help | ||
36 | //config: Command output will be sent to corresponding user via email. | ||
37 | //config: | ||
38 | //config:config FEATURE_CROND_DIR | ||
39 | //config: string "crond spool directory" | ||
40 | //config: default "/var/spool/cron" | ||
41 | //config: depends on CROND || CRONTAB | ||
42 | //config: help | ||
43 | //config: Location of crond spool. | ||
44 | |||
45 | //applet:IF_CROND(APPLET(crond, BB_DIR_USR_SBIN, BB_SUID_DROP)) | ||
46 | |||
47 | //kbuild:lib-$(CONFIG_CROND) += crond.o | ||
13 | 48 | ||
14 | //usage:#define crond_trivial_usage | 49 | //usage:#define crond_trivial_usage |
15 | //usage: "-fbS -l N " IF_FEATURE_CROND_D("-d N ") "-L LOGFILE -c DIR" | 50 | //usage: "-fbS -l N " IF_FEATURE_CROND_D("-d N ") "-L LOGFILE -c DIR" |
@@ -17,12 +52,12 @@ | |||
17 | //usage: " -f Foreground" | 52 | //usage: " -f Foreground" |
18 | //usage: "\n -b Background (default)" | 53 | //usage: "\n -b Background (default)" |
19 | //usage: "\n -S Log to syslog (default)" | 54 | //usage: "\n -S Log to syslog (default)" |
20 | //usage: "\n -l Set log level. 0 is the most verbose, default 8" | 55 | //usage: "\n -l N Set log level. Most verbose:0, default:8" |
21 | //usage: IF_FEATURE_CROND_D( | 56 | //usage: IF_FEATURE_CROND_D( |
22 | //usage: "\n -d Set log level, log to stderr" | 57 | //usage: "\n -d N Set log level, log to stderr" |
23 | //usage: ) | 58 | //usage: ) |
24 | //usage: "\n -L Log to file" | 59 | //usage: "\n -L FILE Log to FILE" |
25 | //usage: "\n -c Working dir" | 60 | //usage: "\n -c DIR Cron dir. Default:"CONFIG_FEATURE_CROND_DIR"/crontabs" |
26 | 61 | ||
27 | #include "libbb.h" | 62 | #include "libbb.h" |
28 | #include <syslog.h> | 63 | #include <syslog.h> |
@@ -36,7 +71,7 @@ | |||
36 | #endif | 71 | #endif |
37 | 72 | ||
38 | 73 | ||
39 | #define TMPDIR CONFIG_FEATURE_CROND_DIR | 74 | #define CRON_DIR CONFIG_FEATURE_CROND_DIR |
40 | #define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs" | 75 | #define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs" |
41 | #ifndef SENDMAIL | 76 | #ifndef SENDMAIL |
42 | # define SENDMAIL "sendmail" | 77 | # define SENDMAIL "sendmail" |
@@ -69,6 +104,7 @@ typedef struct CronLine { | |||
69 | int cl_empty_mail_size; /* size of mail header only, 0 if no mailfile */ | 104 | int cl_empty_mail_size; /* size of mail header only, 0 if no mailfile */ |
70 | char *cl_mailto; /* whom to mail results, may be NULL */ | 105 | char *cl_mailto; /* whom to mail results, may be NULL */ |
71 | #endif | 106 | #endif |
107 | char *cl_shell; | ||
72 | /* ordered by size, not in natural order. makes code smaller: */ | 108 | /* ordered by size, not in natural order. makes code smaller: */ |
73 | char cl_Dow[7]; /* 0-6, beginning sunday */ | 109 | char cl_Dow[7]; /* 0-6, beginning sunday */ |
74 | char cl_Mons[12]; /* 0-11 */ | 110 | char cl_Mons[12]; /* 0-11 */ |
@@ -90,12 +126,6 @@ enum { | |||
90 | OPT_c = (1 << 5), | 126 | OPT_c = (1 << 5), |
91 | OPT_d = (1 << 6) * ENABLE_FEATURE_CROND_D, | 127 | OPT_d = (1 << 6) * ENABLE_FEATURE_CROND_D, |
92 | }; | 128 | }; |
93 | #if ENABLE_FEATURE_CROND_D | ||
94 | # define DebugOpt (option_mask32 & OPT_d) | ||
95 | #else | ||
96 | # define DebugOpt 0 | ||
97 | #endif | ||
98 | |||
99 | 129 | ||
100 | struct globals { | 130 | struct globals { |
101 | unsigned log_level; /* = 8; */ | 131 | unsigned log_level; /* = 8; */ |
@@ -106,6 +136,8 @@ struct globals { | |||
106 | #if SETENV_LEAKS | 136 | #if SETENV_LEAKS |
107 | char *env_var_user; | 137 | char *env_var_user; |
108 | char *env_var_home; | 138 | char *env_var_home; |
139 | char *env_var_shell; | ||
140 | char *env_var_logname; | ||
109 | #endif | 141 | #endif |
110 | } FIX_ALIASING; | 142 | } FIX_ALIASING; |
111 | #define G (*(struct globals*)&bb_common_bufsiz1) | 143 | #define G (*(struct globals*)&bb_common_bufsiz1) |
@@ -114,56 +146,56 @@ struct globals { | |||
114 | G.crontab_dir_name = CRONTABS; \ | 146 | G.crontab_dir_name = CRONTABS; \ |
115 | } while (0) | 147 | } while (0) |
116 | 148 | ||
149 | /* Log levels: | ||
150 | * 0 is the most verbose, default 8. | ||
151 | * For some reason, in fact only 5, 7 and 8 are used. | ||
152 | */ | ||
153 | static void crondlog(unsigned level, const char *msg, va_list va) | ||
154 | { | ||
155 | if (level >= G.log_level) { | ||
156 | /* | ||
157 | * We are called only for info meesages. | ||
158 | * Warnings/errors use plain bb_[p]error_msg's, which | ||
159 | * need not touch syslog_level | ||
160 | * (they are ok with LOG_ERR default). | ||
161 | */ | ||
162 | syslog_level = LOG_INFO; | ||
163 | bb_verror_msg(msg, va, /* strerr: */ NULL); | ||
164 | syslog_level = LOG_ERR; | ||
165 | } | ||
166 | } | ||
117 | 167 | ||
118 | /* 0 is the most verbose, default 8 */ | 168 | static void log5(const char *msg, ...) |
119 | #define LVL5 "\x05" | 169 | { |
120 | #define LVL7 "\x07" | 170 | va_list va; |
121 | #define LVL8 "\x08" | 171 | va_start(va, msg); |
122 | #define WARN9 "\x49" | 172 | crondlog(4, msg, va); |
123 | #define DIE9 "\xc9" | 173 | va_end(va); |
124 | /* level >= 20 is "error" */ | 174 | } |
125 | #define ERR20 "\x14" | ||
126 | 175 | ||
127 | static void crondlog(const char *ctl, ...) __attribute__ ((format (printf, 1, 2))); | 176 | static void log7(const char *msg, ...) |
128 | static void crondlog(const char *ctl, ...) | ||
129 | { | 177 | { |
130 | va_list va; | 178 | va_list va; |
131 | int level = (ctl[0] & 0x1f); | 179 | va_start(va, msg); |
132 | 180 | crondlog(7, msg, va); | |
133 | va_start(va, ctl); | 181 | va_end(va); |
134 | if (level >= (int)G.log_level) { | 182 | } |
135 | /* Debug mode: all to (non-redirected) stderr, */ | 183 | |
136 | /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */ | 184 | static void log8(const char *msg, ...) |
137 | if (!DebugOpt && G.log_filename) { | 185 | { |
138 | /* Otherwise (log to file): we reopen log file at every write: */ | 186 | va_list va; |
139 | int logfd = open_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND); | 187 | va_start(va, msg); |
140 | if (logfd >= 0) | 188 | crondlog(8, msg, va); |
141 | xmove_fd(logfd, STDERR_FILENO); | ||
142 | } | ||
143 | /* When we log to syslog, level > 8 is logged at LOG_ERR | ||
144 | * syslog level, level <= 8 is logged at LOG_INFO. */ | ||
145 | if (level > 8) { | ||
146 | bb_verror_msg(ctl + 1, va, /* strerr: */ NULL); | ||
147 | } else { | ||
148 | char *msg = NULL; | ||
149 | vasprintf(&msg, ctl + 1, va); | ||
150 | bb_info_msg("%s: %s", applet_name, msg); | ||
151 | free(msg); | ||
152 | } | ||
153 | } | ||
154 | va_end(va); | 189 | va_end(va); |
155 | if (ctl[0] & 0x80) | ||
156 | exit(20); | ||
157 | } | 190 | } |
158 | 191 | ||
192 | |||
159 | static const char DowAry[] ALIGN1 = | 193 | static const char DowAry[] ALIGN1 = |
160 | "sun""mon""tue""wed""thu""fri""sat" | 194 | "sun""mon""tue""wed""thu""fri""sat" |
161 | /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */ | ||
162 | ; | 195 | ; |
163 | 196 | ||
164 | static const char MonAry[] ALIGN1 = | 197 | static const char MonAry[] ALIGN1 = |
165 | "jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec" | 198 | "jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec" |
166 | /* "Jan""Feb""Mar""Apr""May""Jun""Jul""Aug""Sep""Oct""Nov""Dec" */ | ||
167 | ; | 199 | ; |
168 | 200 | ||
169 | static void ParseField(char *user, char *ary, int modvalue, int off, | 201 | static void ParseField(char *user, char *ary, int modvalue, int off, |
@@ -267,12 +299,12 @@ static void ParseField(char *user, char *ary, int modvalue, int off, | |||
267 | 299 | ||
268 | if (*ptr) { | 300 | if (*ptr) { |
269 | err: | 301 | err: |
270 | crondlog(WARN9 "user %s: parse error at %s", user, base); | 302 | bb_error_msg("user %s: parse error at %s", user, base); |
271 | return; | 303 | return; |
272 | } | 304 | } |
273 | 305 | ||
274 | if (DebugOpt && (G.log_level <= 5)) { /* like LVL5 */ | 306 | /* can't use log5 (it inserts newlines), open-coding it */ |
275 | /* can't use crondlog, it inserts '\n' */ | 307 | if (G.log_level <= 5 && logmode != LOGMODE_SYSLOG) { |
276 | int i; | 308 | int i; |
277 | for (i = 0; i < modvalue; ++i) | 309 | for (i = 0; i < modvalue; ++i) |
278 | fprintf(stderr, "%d", (unsigned char)ary[i]); | 310 | fprintf(stderr, "%d", (unsigned char)ary[i]); |
@@ -368,11 +400,12 @@ static void load_crontab(const char *fileName) | |||
368 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL | 400 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL |
369 | char *mailTo = NULL; | 401 | char *mailTo = NULL; |
370 | #endif | 402 | #endif |
403 | char *shell = NULL; | ||
371 | 404 | ||
372 | delete_cronfile(fileName); | 405 | delete_cronfile(fileName); |
373 | 406 | ||
374 | if (!getpwnam(fileName)) { | 407 | if (!getpwnam(fileName)) { |
375 | crondlog(LVL7 "ignoring file '%s' (no such user)", fileName); | 408 | log7("ignoring file '%s' (no such user)", fileName); |
376 | return; | 409 | return; |
377 | } | 410 | } |
378 | 411 | ||
@@ -393,14 +426,16 @@ static void load_crontab(const char *fileName) | |||
393 | while (1) { | 426 | while (1) { |
394 | CronLine *line; | 427 | CronLine *line; |
395 | 428 | ||
396 | if (!--maxLines) | 429 | if (!--maxLines) { |
430 | bb_error_msg("user %s: too many lines", fileName); | ||
397 | break; | 431 | break; |
432 | } | ||
433 | |||
398 | n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY); | 434 | n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY); |
399 | if (!n) | 435 | if (!n) |
400 | break; | 436 | break; |
401 | 437 | ||
402 | if (DebugOpt) | 438 | log5("user:%s entry:%s", fileName, parser->data); |
403 | crondlog(LVL5 "user:%s entry:%s", fileName, parser->data); | ||
404 | 439 | ||
405 | /* check if line is setting MAILTO= */ | 440 | /* check if line is setting MAILTO= */ |
406 | if (0 == strncmp(tokens[0], "MAILTO=", 7)) { | 441 | if (0 == strncmp(tokens[0], "MAILTO=", 7)) { |
@@ -410,6 +445,24 @@ static void load_crontab(const char *fileName) | |||
410 | #endif /* otherwise just ignore such lines */ | 445 | #endif /* otherwise just ignore such lines */ |
411 | continue; | 446 | continue; |
412 | } | 447 | } |
448 | if (0 == strncmp(tokens[0], "SHELL=", 6)) { | ||
449 | free(shell); | ||
450 | shell = xstrdup(&tokens[0][6]); | ||
451 | continue; | ||
452 | } | ||
453 | //TODO: handle HOME= too? "man crontab" says: | ||
454 | //name = value | ||
455 | // | ||
456 | //where the spaces around the equal-sign (=) are optional, and any subsequent | ||
457 | //non-leading spaces in value will be part of the value assigned to name. | ||
458 | //The value string may be placed in quotes (single or double, but matching) | ||
459 | //to preserve leading or trailing blanks. | ||
460 | // | ||
461 | //Several environment variables are set up automatically by the cron(8) daemon. | ||
462 | //SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd | ||
463 | //line of the crontab's owner. HOME and SHELL may be overridden by settings | ||
464 | //in the crontab; LOGNAME may not. | ||
465 | |||
413 | /* check if a minimum of tokens is specified */ | 466 | /* check if a minimum of tokens is specified */ |
414 | if (n < 6) | 467 | if (n < 6) |
415 | continue; | 468 | continue; |
@@ -429,11 +482,9 @@ static void load_crontab(const char *fileName) | |||
429 | /* copy mailto (can be NULL) */ | 482 | /* copy mailto (can be NULL) */ |
430 | line->cl_mailto = xstrdup(mailTo); | 483 | line->cl_mailto = xstrdup(mailTo); |
431 | #endif | 484 | #endif |
485 | line->cl_shell = xstrdup(shell); | ||
432 | /* copy command */ | 486 | /* copy command */ |
433 | line->cl_cmd = xstrdup(tokens[5]); | 487 | line->cl_cmd = xstrdup(tokens[5]); |
434 | if (DebugOpt) { | ||
435 | crondlog(LVL5 " command:%s", tokens[5]); | ||
436 | } | ||
437 | pline = &line->cl_next; | 488 | pline = &line->cl_next; |
438 | //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]); | 489 | //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]); |
439 | } | 490 | } |
@@ -441,12 +492,12 @@ static void load_crontab(const char *fileName) | |||
441 | 492 | ||
442 | file->cf_next = G.cron_files; | 493 | file->cf_next = G.cron_files; |
443 | G.cron_files = file; | 494 | G.cron_files = file; |
444 | |||
445 | if (maxLines == 0) { | ||
446 | crondlog(WARN9 "user %s: too many lines", fileName); | ||
447 | } | ||
448 | } | 495 | } |
449 | config_close(parser); | 496 | config_close(parser); |
497 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL | ||
498 | free(mailTo); | ||
499 | #endif | ||
500 | free(shell); | ||
450 | } | 501 | } |
451 | 502 | ||
452 | static void process_cron_update_file(void) | 503 | static void process_cron_update_file(void) |
@@ -482,17 +533,16 @@ static void rescan_crontab_dir(void) | |||
482 | /* Remove cron update file */ | 533 | /* Remove cron update file */ |
483 | unlink(CRONUPDATE); | 534 | unlink(CRONUPDATE); |
484 | /* Re-chdir, in case directory was renamed & deleted */ | 535 | /* Re-chdir, in case directory was renamed & deleted */ |
485 | if (chdir(G.crontab_dir_name) < 0) { | 536 | xchdir(G.crontab_dir_name); |
486 | crondlog(DIE9 "chdir(%s)", G.crontab_dir_name); | ||
487 | } | ||
488 | 537 | ||
489 | /* Scan directory and add associated users */ | 538 | /* Scan directory and add associated users */ |
490 | { | 539 | { |
491 | DIR *dir = opendir("."); | 540 | DIR *dir = opendir("."); |
492 | struct dirent *den; | 541 | struct dirent *den; |
493 | 542 | ||
543 | /* xopendir exists, but "can't open '.'" is not informative */ | ||
494 | if (!dir) | 544 | if (!dir) |
495 | crondlog(DIE9 "chdir(%s)", "."); /* exits */ | 545 | bb_error_msg_and_die("can't open '%s'", G.crontab_dir_name); |
496 | while ((den = readdir(dir)) != NULL) { | 546 | while ((den = readdir(dir)) != NULL) { |
497 | if (strchr(den->d_name, '.') != NULL) { | 547 | if (strchr(den->d_name, '.') != NULL) { |
498 | continue; | 548 | continue; |
@@ -519,19 +569,22 @@ static void safe_setenv(char **pvar_val, const char *var, const char *val) | |||
519 | } | 569 | } |
520 | #endif | 570 | #endif |
521 | 571 | ||
522 | static void set_env_vars(struct passwd *pas) | 572 | static void set_env_vars(struct passwd *pas, const char *shell) |
523 | { | 573 | { |
574 | /* POSIX requires crond to set up at least HOME, LOGNAME, PATH, SHELL. | ||
575 | * We assume crond inherited suitable PATH. | ||
576 | */ | ||
524 | #if SETENV_LEAKS | 577 | #if SETENV_LEAKS |
578 | safe_setenv(&G.env_var_logname, "LOGNAME", pas->pw_name); | ||
525 | safe_setenv(&G.env_var_user, "USER", pas->pw_name); | 579 | safe_setenv(&G.env_var_user, "USER", pas->pw_name); |
526 | safe_setenv(&G.env_var_home, "HOME", pas->pw_dir); | 580 | safe_setenv(&G.env_var_home, "HOME", pas->pw_dir); |
527 | /* if we want to set user's shell instead: */ | 581 | safe_setenv(&G.env_var_shell, "SHELL", shell); |
528 | /*safe_setenv(G.env_var_shell, "SHELL", pas->pw_shell);*/ | ||
529 | #else | 582 | #else |
583 | xsetenv("LOGNAME", pas->pw_name); | ||
530 | xsetenv("USER", pas->pw_name); | 584 | xsetenv("USER", pas->pw_name); |
531 | xsetenv("HOME", pas->pw_dir); | 585 | xsetenv("HOME", pas->pw_dir); |
586 | xsetenv("SHELL", shell); | ||
532 | #endif | 587 | #endif |
533 | /* currently, we use constant one: */ | ||
534 | /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */ | ||
535 | } | 588 | } |
536 | 589 | ||
537 | static void change_user(struct passwd *pas) | 590 | static void change_user(struct passwd *pas) |
@@ -539,10 +592,8 @@ static void change_user(struct passwd *pas) | |||
539 | /* careful: we're after vfork! */ | 592 | /* careful: we're after vfork! */ |
540 | change_identity(pas); /* - initgroups, setgid, setuid */ | 593 | change_identity(pas); /* - initgroups, setgid, setuid */ |
541 | if (chdir(pas->pw_dir) < 0) { | 594 | if (chdir(pas->pw_dir) < 0) { |
542 | crondlog(WARN9 "chdir(%s)", pas->pw_dir); | 595 | bb_error_msg("can't change directory to '%s'", pas->pw_dir); |
543 | if (chdir(TMPDIR) < 0) { | 596 | xchdir(CRON_DIR); |
544 | crondlog(DIE9 "chdir(%s)", TMPDIR); /* exits */ | ||
545 | } | ||
546 | } | 597 | } |
547 | } | 598 | } |
548 | 599 | ||
@@ -550,46 +601,53 @@ static void change_user(struct passwd *pas) | |||
550 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL | 601 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL |
551 | 602 | ||
552 | static pid_t | 603 | static pid_t |
553 | fork_job(const char *user, int mailFd, | 604 | fork_job(const char *user, int mailFd, CronLine *line, bool run_sendmail) |
554 | const char *prog, | 605 | { |
555 | const char *shell_cmd /* if NULL, we run sendmail */ | ||
556 | ) { | ||
557 | struct passwd *pas; | 606 | struct passwd *pas; |
607 | const char *shell, *prog; | ||
608 | smallint sv_logmode; | ||
558 | pid_t pid; | 609 | pid_t pid; |
559 | 610 | ||
560 | /* prepare things before vfork */ | 611 | /* prepare things before vfork */ |
561 | pas = getpwnam(user); | 612 | pas = getpwnam(user); |
562 | if (!pas) { | 613 | if (!pas) { |
563 | crondlog(WARN9 "can't get uid for %s", user); | 614 | bb_error_msg("can't get uid for %s", user); |
564 | goto err; | 615 | goto err; |
565 | } | 616 | } |
566 | set_env_vars(pas); | ||
567 | 617 | ||
618 | shell = line->cl_shell ? line->cl_shell : DEFAULT_SHELL; | ||
619 | prog = run_sendmail ? SENDMAIL : shell; | ||
620 | |||
621 | set_env_vars(pas, shell); | ||
622 | |||
623 | sv_logmode = logmode; | ||
568 | pid = vfork(); | 624 | pid = vfork(); |
569 | if (pid == 0) { | 625 | if (pid == 0) { |
570 | /* CHILD */ | 626 | /* CHILD */ |
571 | /* initgroups, setgid, setuid, and chdir to home or TMPDIR */ | 627 | /* initgroups, setgid, setuid, and chdir to home or CRON_DIR */ |
572 | change_user(pas); | 628 | change_user(pas); |
573 | if (DebugOpt) { | 629 | log5("child running %s", prog); |
574 | crondlog(LVL5 "child running %s", prog); | ||
575 | } | ||
576 | if (mailFd >= 0) { | 630 | if (mailFd >= 0) { |
577 | xmove_fd(mailFd, shell_cmd ? 1 : 0); | 631 | xmove_fd(mailFd, run_sendmail ? 0 : 1); |
578 | dup2(1, 2); | 632 | dup2(1, 2); |
579 | } | 633 | } |
580 | /* crond 3.0pl1-100 puts tasks in separate process groups */ | 634 | /* crond 3.0pl1-100 puts tasks in separate process groups */ |
581 | bb_setpgrp(); | 635 | bb_setpgrp(); |
582 | execlp(prog, prog, (shell_cmd ? "-c" : SENDMAIL_ARGS), shell_cmd, (char *) NULL); | 636 | if (!run_sendmail) |
583 | crondlog(ERR20 "can't execute '%s' for user %s", prog, user); | 637 | execlp(prog, prog, "-c", line->cl_cmd, (char *) NULL); |
584 | if (shell_cmd) { | 638 | else |
585 | fdprintf(1, "Exec failed: %s -c %s\n", prog, shell_cmd); | 639 | execlp(prog, prog, SENDMAIL_ARGS, (char *) NULL); |
586 | } | 640 | /* |
587 | _exit(EXIT_SUCCESS); | 641 | * I want this error message on stderr too, |
642 | * even if other messages go only to syslog: | ||
643 | */ | ||
644 | logmode |= LOGMODE_STDIO; | ||
645 | bb_error_msg_and_die("can't execute '%s' for user %s", prog, user); | ||
588 | } | 646 | } |
647 | logmode = sv_logmode; | ||
589 | 648 | ||
590 | if (pid < 0) { | 649 | if (pid < 0) { |
591 | /* FORK FAILED */ | 650 | bb_perror_msg("vfork"); |
592 | crondlog(ERR20 "can't vfork"); | ||
593 | err: | 651 | err: |
594 | pid = 0; | 652 | pid = 0; |
595 | } /* else: PARENT, FORK SUCCESS */ | 653 | } /* else: PARENT, FORK SUCCESS */ |
@@ -614,7 +672,7 @@ static void start_one_job(const char *user, CronLine *line) | |||
614 | 672 | ||
615 | if (line->cl_mailto) { | 673 | if (line->cl_mailto) { |
616 | /* Open mail file (owner is root so nobody can screw with it) */ | 674 | /* Open mail file (owner is root so nobody can screw with it) */ |
617 | snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid()); | 675 | snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", CRON_DIR, user, getpid()); |
618 | mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600); | 676 | mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600); |
619 | 677 | ||
620 | if (mailFd >= 0) { | 678 | if (mailFd >= 0) { |
@@ -622,18 +680,18 @@ static void start_one_job(const char *user, CronLine *line) | |||
622 | line->cl_cmd); | 680 | line->cl_cmd); |
623 | line->cl_empty_mail_size = lseek(mailFd, 0, SEEK_CUR); | 681 | line->cl_empty_mail_size = lseek(mailFd, 0, SEEK_CUR); |
624 | } else { | 682 | } else { |
625 | crondlog(ERR20 "can't create mail file %s for user %s, " | 683 | bb_error_msg("can't create mail file %s for user %s, " |
626 | "discarding output", mailFile, user); | 684 | "discarding output", mailFile, user); |
627 | } | 685 | } |
628 | } | 686 | } |
629 | 687 | ||
630 | line->cl_pid = fork_job(user, mailFd, DEFAULT_SHELL, line->cl_cmd); | 688 | line->cl_pid = fork_job(user, mailFd, line, /*sendmail?*/ 0); |
631 | if (mailFd >= 0) { | 689 | if (mailFd >= 0) { |
632 | if (line->cl_pid <= 0) { | 690 | if (line->cl_pid <= 0) { |
633 | unlink(mailFile); | 691 | unlink(mailFile); |
634 | } else { | 692 | } else { |
635 | /* rename mail-file based on pid of process */ | 693 | /* rename mail-file based on pid of process */ |
636 | char *mailFile2 = xasprintf("%s/cron.%s.%d", TMPDIR, user, (int)line->cl_pid); | 694 | char *mailFile2 = xasprintf("%s/cron.%s.%d", CRON_DIR, user, (int)line->cl_pid); |
637 | rename(mailFile, mailFile2); // TODO: xrename? | 695 | rename(mailFile, mailFile2); // TODO: xrename? |
638 | free(mailFile2); | 696 | free(mailFile2); |
639 | } | 697 | } |
@@ -665,7 +723,7 @@ static void process_finished_job(const char *user, CronLine *line) | |||
665 | * End of primary job - check for mail file. | 723 | * End of primary job - check for mail file. |
666 | * If size has changed and the file is still valid, we send it. | 724 | * If size has changed and the file is still valid, we send it. |
667 | */ | 725 | */ |
668 | snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, (int)pid); | 726 | snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", CRON_DIR, user, (int)pid); |
669 | mailFd = open(mailFile, O_RDONLY); | 727 | mailFd = open(mailFile, O_RDONLY); |
670 | unlink(mailFile); | 728 | unlink(mailFile); |
671 | if (mailFd < 0) { | 729 | if (mailFd < 0) { |
@@ -683,43 +741,41 @@ static void process_finished_job(const char *user, CronLine *line) | |||
683 | } | 741 | } |
684 | line->cl_empty_mail_size = 0; | 742 | line->cl_empty_mail_size = 0; |
685 | /* if (line->cl_mailto) - always true if cl_empty_mail_size was nonzero */ | 743 | /* if (line->cl_mailto) - always true if cl_empty_mail_size was nonzero */ |
686 | line->cl_pid = fork_job(user, mailFd, SENDMAIL, NULL); | 744 | line->cl_pid = fork_job(user, mailFd, line, /*sendmail?*/ 1); |
687 | } | 745 | } |
688 | 746 | ||
689 | #else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */ | 747 | #else /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */ |
690 | 748 | ||
691 | static void start_one_job(const char *user, CronLine *line) | 749 | static void start_one_job(const char *user, CronLine *line) |
692 | { | 750 | { |
751 | const char *shell; | ||
693 | struct passwd *pas; | 752 | struct passwd *pas; |
694 | pid_t pid; | 753 | pid_t pid; |
695 | 754 | ||
696 | pas = getpwnam(user); | 755 | pas = getpwnam(user); |
697 | if (!pas) { | 756 | if (!pas) { |
698 | crondlog(WARN9 "can't get uid for %s", user); | 757 | bb_error_msg("can't get uid for %s", user); |
699 | goto err; | 758 | goto err; |
700 | } | 759 | } |
701 | 760 | ||
702 | /* Prepare things before vfork */ | 761 | /* Prepare things before vfork */ |
703 | set_env_vars(pas); | 762 | shell = line->cl_shell ? line->cl_shell : DEFAULT_SHELL; |
763 | set_env_vars(pas, shell); | ||
704 | 764 | ||
705 | /* Fork as the user in question and run program */ | 765 | /* Fork as the user in question and run program */ |
706 | pid = vfork(); | 766 | pid = vfork(); |
707 | if (pid == 0) { | 767 | if (pid == 0) { |
708 | /* CHILD */ | 768 | /* CHILD */ |
709 | /* initgroups, setgid, setuid, and chdir to home or TMPDIR */ | 769 | /* initgroups, setgid, setuid, and chdir to home or CRON_DIR */ |
710 | change_user(pas); | 770 | change_user(pas); |
711 | if (DebugOpt) { | 771 | log5("child running %s", shell); |
712 | crondlog(LVL5 "child running %s", DEFAULT_SHELL); | ||
713 | } | ||
714 | /* crond 3.0pl1-100 puts tasks in separate process groups */ | 772 | /* crond 3.0pl1-100 puts tasks in separate process groups */ |
715 | bb_setpgrp(); | 773 | bb_setpgrp(); |
716 | execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_cmd, (char *) NULL); | 774 | execl(shell, shell, "-c", line->cl_cmd, (char *) NULL); |
717 | crondlog(ERR20 "can't execute '%s' for user %s", DEFAULT_SHELL, user); | 775 | bb_error_msg_and_die("can't execute '%s' for user %s", shell, user); |
718 | _exit(EXIT_SUCCESS); | ||
719 | } | 776 | } |
720 | if (pid < 0) { | 777 | if (pid < 0) { |
721 | /* FORK FAILED */ | 778 | bb_perror_msg("vfork"); |
722 | crondlog(ERR20 "can't vfork"); | ||
723 | err: | 779 | err: |
724 | pid = 0; | 780 | pid = 0; |
725 | } | 781 | } |
@@ -751,24 +807,20 @@ static void flag_starting_jobs(time_t t1, time_t t2) | |||
751 | 807 | ||
752 | ptm = localtime(&t); | 808 | ptm = localtime(&t); |
753 | for (file = G.cron_files; file; file = file->cf_next) { | 809 | for (file = G.cron_files; file; file = file->cf_next) { |
754 | if (DebugOpt) | 810 | log5("file %s:", file->cf_username); |
755 | crondlog(LVL5 "file %s:", file->cf_username); | ||
756 | if (file->cf_deleted) | 811 | if (file->cf_deleted) |
757 | continue; | 812 | continue; |
758 | for (line = file->cf_lines; line; line = line->cl_next) { | 813 | for (line = file->cf_lines; line; line = line->cl_next) { |
759 | if (DebugOpt) | 814 | log5(" line %s", line->cl_cmd); |
760 | crondlog(LVL5 " line %s", line->cl_cmd); | ||
761 | if (line->cl_Mins[ptm->tm_min] | 815 | if (line->cl_Mins[ptm->tm_min] |
762 | && line->cl_Hrs[ptm->tm_hour] | 816 | && line->cl_Hrs[ptm->tm_hour] |
763 | && (line->cl_Days[ptm->tm_mday] || line->cl_Dow[ptm->tm_wday]) | 817 | && (line->cl_Days[ptm->tm_mday] || line->cl_Dow[ptm->tm_wday]) |
764 | && line->cl_Mons[ptm->tm_mon] | 818 | && line->cl_Mons[ptm->tm_mon] |
765 | ) { | 819 | ) { |
766 | if (DebugOpt) { | 820 | log5(" job: %d %s", |
767 | crondlog(LVL5 " job: %d %s", | ||
768 | (int)line->cl_pid, line->cl_cmd); | 821 | (int)line->cl_pid, line->cl_cmd); |
769 | } | ||
770 | if (line->cl_pid > 0) { | 822 | if (line->cl_pid > 0) { |
771 | crondlog(LVL8 "user %s: process already running: %s", | 823 | log8("user %s: process already running: %s", |
772 | file->cf_username, line->cl_cmd); | 824 | file->cf_username, line->cl_cmd); |
773 | } else if (line->cl_pid == 0) { | 825 | } else if (line->cl_pid == 0) { |
774 | line->cl_pid = -1; | 826 | line->cl_pid = -1; |
@@ -797,7 +849,7 @@ static void start_jobs(void) | |||
797 | 849 | ||
798 | start_one_job(file->cf_username, line); | 850 | start_one_job(file->cf_username, line); |
799 | pid = line->cl_pid; | 851 | pid = line->cl_pid; |
800 | crondlog(LVL8 "USER %s pid %3d cmd %s", | 852 | log8("USER %s pid %3d cmd %s", |
801 | file->cf_username, (int)pid, line->cl_cmd); | 853 | file->cf_username, (int)pid, line->cl_cmd); |
802 | if (pid < 0) { | 854 | if (pid < 0) { |
803 | file->cf_wants_starting = 1; | 855 | file->cf_wants_starting = 1; |
@@ -849,12 +901,21 @@ static int check_completions(void) | |||
849 | return num_still_running; | 901 | return num_still_running; |
850 | } | 902 | } |
851 | 903 | ||
904 | static void reopen_logfile_to_stderr(void) | ||
905 | { | ||
906 | if (G.log_filename) { | ||
907 | int logfd = open_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND); | ||
908 | if (logfd >= 0) | ||
909 | xmove_fd(logfd, STDERR_FILENO); | ||
910 | } | ||
911 | } | ||
912 | |||
852 | int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 913 | int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
853 | int crond_main(int argc UNUSED_PARAM, char **argv) | 914 | int crond_main(int argc UNUSED_PARAM, char **argv) |
854 | { | 915 | { |
855 | time_t t2; | 916 | time_t t2; |
856 | int rescan; | 917 | unsigned rescan; |
857 | int sleep_time; | 918 | unsigned sleep_time; |
858 | unsigned opts; | 919 | unsigned opts; |
859 | 920 | ||
860 | INIT_G(); | 921 | INIT_G(); |
@@ -880,10 +941,11 @@ int crond_main(int argc UNUSED_PARAM, char **argv) | |||
880 | logmode = LOGMODE_SYSLOG; | 941 | logmode = LOGMODE_SYSLOG; |
881 | } | 942 | } |
882 | 943 | ||
883 | xchdir(G.crontab_dir_name); | ||
884 | //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ | 944 | //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ |
885 | xsetenv("SHELL", DEFAULT_SHELL); /* once, for all future children */ | 945 | |
886 | crondlog(LVL8 "crond (busybox "BB_VER") started, log level %d", G.log_level); | 946 | reopen_logfile_to_stderr(); |
947 | xchdir(G.crontab_dir_name); | ||
948 | log8("crond (busybox "BB_VER") started, log level %d", G.log_level); | ||
887 | rescan_crontab_dir(); | 949 | rescan_crontab_dir(); |
888 | write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid"); | 950 | write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid"); |
889 | 951 | ||
@@ -896,14 +958,14 @@ int crond_main(int argc UNUSED_PARAM, char **argv) | |||
896 | time_t t1; | 958 | time_t t1; |
897 | long dt; | 959 | long dt; |
898 | 960 | ||
899 | t1 = t2; | ||
900 | |||
901 | /* Synchronize to 1 minute, minimum 1 second */ | 961 | /* Synchronize to 1 minute, minimum 1 second */ |
902 | sleep(sleep_time - (time(NULL) % sleep_time) + 1); | 962 | t1 = t2; |
903 | 963 | sleep(sleep_time - (time(NULL) % sleep_time)); | |
904 | t2 = time(NULL); | 964 | t2 = time(NULL); |
905 | dt = (long)t2 - (long)t1; | 965 | dt = (long)t2 - (long)t1; |
906 | 966 | ||
967 | reopen_logfile_to_stderr(); | ||
968 | |||
907 | /* | 969 | /* |
908 | * The file 'cron.update' is checked to determine new cron | 970 | * The file 'cron.update' is checked to determine new cron |
909 | * jobs. The directory is rescanned once an hour to deal | 971 | * jobs. The directory is rescanned once an hour to deal |
@@ -931,20 +993,18 @@ int crond_main(int argc UNUSED_PARAM, char **argv) | |||
931 | rescan_crontab_dir(); | 993 | rescan_crontab_dir(); |
932 | } | 994 | } |
933 | process_cron_update_file(); | 995 | process_cron_update_file(); |
934 | if (DebugOpt) | 996 | log5("wakeup dt=%ld", dt); |
935 | crondlog(LVL5 "wakeup dt=%ld", dt); | ||
936 | if (dt < -60 * 60 || dt > 60 * 60) { | 997 | if (dt < -60 * 60 || dt > 60 * 60) { |
937 | crondlog(WARN9 "time disparity of %ld minutes detected", dt / 60); | 998 | bb_error_msg("time disparity of %ld minutes detected", dt / 60); |
938 | /* and we do not run any jobs in this case */ | 999 | /* and we do not run any jobs in this case */ |
939 | } else if (dt > 0) { | 1000 | } else if (dt > 0) { |
940 | /* Usual case: time advances forward, as expected */ | 1001 | /* Usual case: time advances forward, as expected */ |
941 | flag_starting_jobs(t1, t2); | 1002 | flag_starting_jobs(t1, t2); |
942 | start_jobs(); | 1003 | start_jobs(); |
1004 | sleep_time = 60; | ||
943 | if (check_completions() > 0) { | 1005 | if (check_completions() > 0) { |
944 | /* some jobs are still running */ | 1006 | /* some jobs are still running */ |
945 | sleep_time = 10; | 1007 | sleep_time = 10; |
946 | } else { | ||
947 | sleep_time = 60; | ||
948 | } | 1008 | } |
949 | } | 1009 | } |
950 | /* else: time jumped back, do not run any jobs */ | 1010 | /* else: time jumped back, do not run any jobs */ |
diff --git a/miscutils/less.c b/miscutils/less.c index 574f222e0..d84df469c 100644 --- a/miscutils/less.c +++ b/miscutils/less.c | |||
@@ -404,6 +404,9 @@ static void fill_match_lines(unsigned pos); | |||
404 | * last_line_pos - screen line position of next char to be read | 404 | * last_line_pos - screen line position of next char to be read |
405 | * (takes into account tabs and backspaces) | 405 | * (takes into account tabs and backspaces) |
406 | * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error | 406 | * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error |
407 | * | ||
408 | * "git log -p | less -m" on the kernel git tree is a good test for EAGAINs, | ||
409 | * "/search on very long input" and "reaching max line count" corner cases. | ||
407 | */ | 410 | */ |
408 | static void read_lines(void) | 411 | static void read_lines(void) |
409 | { | 412 | { |
@@ -414,9 +417,13 @@ static void read_lines(void) | |||
414 | #if ENABLE_FEATURE_LESS_REGEXP | 417 | #if ENABLE_FEATURE_LESS_REGEXP |
415 | unsigned old_max_fline = max_fline; | 418 | unsigned old_max_fline = max_fline; |
416 | time_t last_time = 0; | 419 | time_t last_time = 0; |
417 | unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */ | 420 | int had_progress = 2; |
418 | #endif | 421 | #endif |
419 | 422 | ||
423 | /* (careful: max_fline can be -1) */ | ||
424 | if (max_fline + 1 > MAXLINES) | ||
425 | return; | ||
426 | |||
420 | if (option_mask32 & FLAG_N) | 427 | if (option_mask32 & FLAG_N) |
421 | w -= 8; | 428 | w -= 8; |
422 | 429 | ||
@@ -441,6 +448,7 @@ static void read_lines(void) | |||
441 | char c; | 448 | char c; |
442 | /* if no unprocessed chars left, eat more */ | 449 | /* if no unprocessed chars left, eat more */ |
443 | if (readpos >= readeof) { | 450 | if (readpos >= readeof) { |
451 | errno = 0; | ||
444 | ndelay_on(0); | 452 | ndelay_on(0); |
445 | eof_error = safe_read(STDIN_FILENO, readbuf, sizeof(readbuf)); | 453 | eof_error = safe_read(STDIN_FILENO, readbuf, sizeof(readbuf)); |
446 | ndelay_off(0); | 454 | ndelay_off(0); |
@@ -448,6 +456,7 @@ static void read_lines(void) | |||
448 | readeof = eof_error; | 456 | readeof = eof_error; |
449 | if (eof_error <= 0) | 457 | if (eof_error <= 0) |
450 | goto reached_eof; | 458 | goto reached_eof; |
459 | IF_FEATURE_LESS_REGEXP(had_progress = 1;) | ||
451 | } | 460 | } |
452 | c = readbuf[readpos]; | 461 | c = readbuf[readpos]; |
453 | /* backspace? [needed for manpages] */ | 462 | /* backspace? [needed for manpages] */ |
@@ -519,31 +528,23 @@ static void read_lines(void) | |||
519 | #endif | 528 | #endif |
520 | } | 529 | } |
521 | if (eof_error <= 0) { | 530 | if (eof_error <= 0) { |
522 | if (eof_error < 0) { | ||
523 | if (errno == EAGAIN) { | ||
524 | /* not yet eof or error, reset flag (or else | ||
525 | * we will hog CPU - select() will return | ||
526 | * immediately */ | ||
527 | eof_error = 1; | ||
528 | } else { | ||
529 | print_statusline(bb_msg_read_error); | ||
530 | } | ||
531 | } | ||
532 | #if !ENABLE_FEATURE_LESS_REGEXP | 531 | #if !ENABLE_FEATURE_LESS_REGEXP |
533 | break; | 532 | break; |
534 | #else | 533 | #else |
535 | if (wanted_match < num_matches) { | 534 | if (wanted_match < num_matches) { |
536 | break; | 535 | break; |
537 | } else { /* goto_match called us */ | 536 | } /* else: goto_match() called us */ |
537 | if (errno == EAGAIN) { | ||
538 | time_t t = time(NULL); | 538 | time_t t = time(NULL); |
539 | if (t != last_time) { | 539 | if (t != last_time) { |
540 | last_time = t; | 540 | last_time = t; |
541 | if (--seconds_p1 == 0) | 541 | if (--had_progress < 0) |
542 | break; | 542 | break; |
543 | } | 543 | } |
544 | sched_yield(); | 544 | sched_yield(); |
545 | goto again0; /* go loop again (max 2 seconds) */ | 545 | goto again0; |
546 | } | 546 | } |
547 | break; | ||
547 | #endif | 548 | #endif |
548 | } | 549 | } |
549 | max_fline++; | 550 | max_fline++; |
@@ -551,6 +552,15 @@ static void read_lines(void) | |||
551 | p = current_line; | 552 | p = current_line; |
552 | last_line_pos = 0; | 553 | last_line_pos = 0; |
553 | } /* end of "read lines until we reach cur_fline" loop */ | 554 | } /* end of "read lines until we reach cur_fline" loop */ |
555 | |||
556 | if (eof_error < 0) { | ||
557 | if (errno == EAGAIN) { | ||
558 | eof_error = 1; | ||
559 | } else { | ||
560 | print_statusline(bb_msg_read_error); | ||
561 | } | ||
562 | } | ||
563 | |||
554 | fill_match_lines(old_max_fline); | 564 | fill_match_lines(old_max_fline); |
555 | #if ENABLE_FEATURE_LESS_REGEXP | 565 | #if ENABLE_FEATURE_LESS_REGEXP |
556 | /* prevent us from being stuck in search for a match */ | 566 | /* prevent us from being stuck in search for a match */ |