diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-12 22:10:25 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-12 22:10:25 +0000 |
commit | 4e6c8120a53709c4a352db42e997303844c74584 (patch) | |
tree | 67494852dbb86ff0c25f161181bca156cd9a612e /miscutils | |
parent | 35e9c5d83ada1fea40798365a1b4c728f64ded98 (diff) | |
download | busybox-w32-4e6c8120a53709c4a352db42e997303844c74584.tar.gz busybox-w32-4e6c8120a53709c4a352db42e997303844c74584.tar.bz2 busybox-w32-4e6c8120a53709c4a352db42e997303844c74584.zip |
crond: make it NOMMU-capable
function old new delta
safe_setenv4 - 62 +62
ForkJob 472 511 +39
change_identity 21 57 +36
ParseField 564 571 +7
SynchronizeDir 178 176 -2
LogLevel 4 - -4
LogFile 4 - -4
FileBase 4 - -4
DebugOpt 4 - -4
CDir 4 - -4
DeleteFile 141 132 -9
packed_usage 24248 24228 -20
crondlog 157 113 -44
change_identity_e2str 54 - -54
SynchronizeFile 729 671 -58
crond_main 1555 1404 -151
------------------------------------------------------------------------------
(add/remove: 1/6 grow/shrink: 3/6 up/down: 144/-358) Total: -214 bytes
Diffstat (limited to 'miscutils')
-rw-r--r-- | miscutils/Config.in | 8 | ||||
-rw-r--r-- | miscutils/crond.c | 745 |
2 files changed, 364 insertions, 389 deletions
diff --git a/miscutils/Config.in b/miscutils/Config.in index ac1e2b57c..2cc6d9e80 100644 --- a/miscutils/Config.in +++ b/miscutils/Config.in | |||
@@ -106,15 +106,13 @@ config CROND | |||
106 | $ cat /var/spool/cron/crontabs/root | 106 | $ cat /var/spool/cron/crontabs/root |
107 | # Run daily cron jobs at 4:40 every day: | 107 | # Run daily cron jobs at 4:40 every day: |
108 | 40 4 * * * /etc/cron/daily > /dev/null 2>&1 | 108 | 40 4 * * * /etc/cron/daily > /dev/null 2>&1 |
109 | Note that Busybox binary must be setuid root for this applet to | ||
110 | work properly. | ||
111 | 109 | ||
112 | config DEBUG_CROND_OPTION | 110 | config DEBUG_CROND_OPTION |
113 | bool "Support debug option -d" | 111 | bool "Support option -d to redirect output to stderr" |
114 | depends on CROND | 112 | depends on CROND |
115 | default n | 113 | default n |
116 | help | 114 | help |
117 | Support option -d to enter debug mode. | 115 | -d sets loglevel to 0 (most verbose) and directs all output to stderr. |
118 | 116 | ||
119 | config FEATURE_CROND_CALL_SENDMAIL | 117 | config FEATURE_CROND_CALL_SENDMAIL |
120 | bool "Using /usr/sbin/sendmail?" | 118 | bool "Using /usr/sbin/sendmail?" |
@@ -130,6 +128,8 @@ config CRONTAB | |||
130 | help | 128 | help |
131 | Crontab manipulates the crontab for a particular user. Only | 129 | Crontab manipulates the crontab for a particular user. Only |
132 | the superuser may specify a different user and/or crontab directory. | 130 | the superuser may specify a different user and/or crontab directory. |
131 | Note that Busybox binary must be setuid root for this applet to | ||
132 | work properly. | ||
133 | 133 | ||
134 | config DC | 134 | config DC |
135 | bool "dc" | 135 | bool "dc" |
diff --git a/miscutils/crond.c b/miscutils/crond.c index 9721a8a9a..6d4825e85 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c | |||
@@ -14,6 +14,15 @@ | |||
14 | #include "libbb.h" | 14 | #include "libbb.h" |
15 | #include <syslog.h> | 15 | #include <syslog.h> |
16 | 16 | ||
17 | /* glibc frees previous setenv'ed value when we do next setenv() | ||
18 | * of the same variable. uclibc does not do this! */ | ||
19 | #if (defined(__GLIBC__) && !defined(__UCLIBC__)) /* || OTHER_SAFE_LIBC... */ | ||
20 | #define SETENV_LEAKS 0 | ||
21 | #else | ||
22 | #define SETENV_LEAKS 1 | ||
23 | #endif | ||
24 | |||
25 | |||
17 | #ifndef CRONTABS | 26 | #ifndef CRONTABS |
18 | #define CRONTABS "/var/spool/cron/crontabs" | 27 | #define CRONTABS "/var/spool/cron/crontabs" |
19 | #endif | 28 | #endif |
@@ -33,160 +42,169 @@ | |||
33 | #define MAXLINES 256 /* max lines in non-root crontabs */ | 42 | #define MAXLINES 256 /* max lines in non-root crontabs */ |
34 | #endif | 43 | #endif |
35 | 44 | ||
45 | |||
36 | typedef struct CronFile { | 46 | typedef struct CronFile { |
37 | struct CronFile *cf_Next; | 47 | struct CronFile *cf_Next; |
38 | struct CronLine *cf_LineBase; | 48 | struct CronLine *cf_LineBase; |
39 | char *cf_User; /* username */ | 49 | char *cf_User; /* username */ |
40 | int cf_Ready; /* bool: one or more jobs ready */ | 50 | smallint cf_Ready; /* bool: one or more jobs ready */ |
41 | int cf_Running; /* bool: one or more jobs running */ | 51 | smallint cf_Running; /* bool: one or more jobs running */ |
42 | int cf_Deleted; /* marked for deletion, ignore */ | 52 | smallint cf_Deleted; /* marked for deletion, ignore */ |
43 | } CronFile; | 53 | } CronFile; |
44 | 54 | ||
45 | typedef struct CronLine { | 55 | typedef struct CronLine { |
46 | struct CronLine *cl_Next; | 56 | struct CronLine *cl_Next; |
47 | char *cl_Shell; /* shell command */ | 57 | char *cl_Shell; /* shell command */ |
48 | pid_t cl_Pid; /* running pid, 0, or armed (-1) */ | 58 | pid_t cl_Pid; /* running pid, 0, or armed (-1) */ |
49 | int cl_MailFlag; /* running pid is for mail */ | 59 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL |
50 | int cl_MailPos; /* 'empty file' size */ | 60 | int cl_MailPos; /* 'empty file' size */ |
51 | char cl_Mins[60]; /* 0-59 */ | 61 | smallint cl_MailFlag; /* running pid is for mail */ |
52 | char cl_Hrs[24]; /* 0-23 */ | 62 | #endif |
53 | char cl_Days[32]; /* 1-31 */ | 63 | /* ordered by size, not in natural order. makes code smaller: */ |
54 | char cl_Mons[12]; /* 0-11 */ | 64 | char cl_Dow[7]; /* 0-6, beginning sunday */ |
55 | char cl_Dow[7]; /* 0-6, beginning sunday */ | 65 | char cl_Mons[12]; /* 0-11 */ |
66 | char cl_Hrs[24]; /* 0-23 */ | ||
67 | char cl_Days[32]; /* 1-31 */ | ||
68 | char cl_Mins[60]; /* 0-59 */ | ||
56 | } CronLine; | 69 | } CronLine; |
57 | 70 | ||
58 | #define RUN_RANOUT 1 | ||
59 | #define RUN_RUNNING 2 | ||
60 | #define RUN_FAILED 3 | ||
61 | 71 | ||
62 | #define DaemonUid 0 | 72 | #define DaemonUid 0 |
63 | 73 | ||
74 | |||
75 | enum { | ||
76 | OPT_l = (1 << 0), | ||
77 | OPT_L = (1 << 1), | ||
78 | OPT_f = (1 << 2), | ||
79 | OPT_b = (1 << 3), | ||
80 | OPT_S = (1 << 4), | ||
81 | OPT_c = (1 << 5), | ||
82 | OPT_d = (1 << 6) * ENABLE_DEBUG_CROND_OPTION, | ||
83 | }; | ||
64 | #if ENABLE_DEBUG_CROND_OPTION | 84 | #if ENABLE_DEBUG_CROND_OPTION |
65 | static unsigned DebugOpt; | 85 | #define DebugOpt (option_mask32 & OPT_d) |
86 | #else | ||
87 | #define DebugOpt 0 | ||
66 | #endif | 88 | #endif |
67 | 89 | ||
68 | static unsigned LogLevel = 8; | ||
69 | static const char *LogFile; | ||
70 | static const char *CDir = CRONTABS; | ||
71 | 90 | ||
72 | static void startlogger(void); | 91 | struct globals { |
92 | unsigned LogLevel; /* = 8; */ | ||
93 | const char *LogFile; | ||
94 | const char *CDir; /* = CRONTABS; */ | ||
95 | CronFile *FileBase; | ||
96 | #if SETENV_LEAKS | ||
97 | char *env_var_user; | ||
98 | char *env_var_home; | ||
99 | #endif | ||
100 | }; | ||
101 | #define G (*(struct globals*)&bb_common_bufsiz1) | ||
102 | #define LogLevel (G.LogLevel ) | ||
103 | #define LogFile (G.LogFile ) | ||
104 | #define CDir (G.CDir ) | ||
105 | #define FileBase (G.FileBase ) | ||
106 | #define env_var_user (G.env_var_user ) | ||
107 | #define env_var_home (G.env_var_home ) | ||
108 | #define INIT_G() do { \ | ||
109 | LogLevel = 8; \ | ||
110 | CDir = CRONTABS; \ | ||
111 | } while (0) | ||
112 | |||
73 | 113 | ||
74 | static void CheckUpdates(void); | 114 | static void CheckUpdates(void); |
75 | static void SynchronizeDir(void); | 115 | static void SynchronizeDir(void); |
76 | static int TestJobs(time_t t1, time_t t2); | 116 | static int TestJobs(time_t t1, time_t t2); |
77 | static void RunJobs(void); | 117 | static void RunJobs(void); |
78 | static int CheckJobs(void); | 118 | static int CheckJobs(void); |
79 | 119 | static void RunJob(const char *user, CronLine *line); | |
80 | static void RunJob(const char *user, CronLine * line); | ||
81 | |||
82 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL | 120 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL |
83 | static void EndJob(const char *user, CronLine * line); | 121 | static void EndJob(const char *user, CronLine *line); |
84 | #else | 122 | #else |
85 | #define EndJob(user, line) line->cl_Pid = 0 | 123 | #define EndJob(user, line) ((line)->cl_Pid = 0) |
86 | #endif | 124 | #endif |
87 | |||
88 | static void DeleteFile(const char *userName); | 125 | static void DeleteFile(const char *userName); |
89 | 126 | ||
90 | static CronFile *FileBase; | ||
91 | 127 | ||
128 | #define LVL5 "\x05" | ||
129 | #define LVL7 "\x07" | ||
130 | #define LVL8 "\x08" | ||
131 | #define LVL9 "\x09" | ||
132 | #define WARN9 "\x49" | ||
133 | #define DIE9 "\xc9" | ||
134 | /* level >= 20 is "error" */ | ||
135 | #define ERR20 "\x14" | ||
92 | 136 | ||
93 | static void crondlog(const char *ctl, ...) | 137 | static void crondlog(const char *ctl, ...) |
94 | { | 138 | { |
95 | va_list va; | 139 | va_list va; |
96 | const char *fmt; | 140 | int level = (ctl[0] & 0x1f); |
97 | int level = (int) (ctl[0] & 0xf); | ||
98 | int type = level == 20 ? | ||
99 | LOG_ERR : ((ctl[0] & 0100) ? LOG_WARNING : LOG_NOTICE); | ||
100 | 141 | ||
101 | va_start(va, ctl); | 142 | va_start(va, ctl); |
102 | fmt = ctl + 1; | ||
103 | if (level >= LogLevel) { | 143 | if (level >= LogLevel) { |
104 | 144 | /* Debug mode: all to (non-redirected) stderr, */ | |
105 | #if ENABLE_DEBUG_CROND_OPTION | 145 | /* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */ |
106 | if (DebugOpt) { | 146 | if (!DebugOpt && LogFile) { |
107 | vfprintf(stderr, fmt, va); | 147 | /* Otherwise (log to file): we reopen log file at every write: */ |
108 | } else | ||
109 | #endif | ||
110 | if (LogFile == NULL) { | ||
111 | vsyslog(type, fmt, va); | ||
112 | } else { | ||
113 | #if !ENABLE_DEBUG_CROND_OPTION | ||
114 | int logfd = open(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600); | ||
115 | #else | ||
116 | int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600); | 148 | int logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600); |
117 | #endif | 149 | if (logfd >= 0) |
118 | if (logfd >= 0) { | 150 | xmove_fd(logfd, STDERR_FILENO); |
119 | vdprintf(logfd, fmt, va); | ||
120 | close(logfd); | ||
121 | } | ||
122 | } | 151 | } |
152 | // TODO: ERR -> error, WARN -> warning, LVL -> info | ||
153 | bb_verror_msg(ctl + 1, va, /* strerr: */ NULL); | ||
123 | } | 154 | } |
124 | va_end(va); | 155 | va_end(va); |
125 | if (ctl[0] & 0200) { | 156 | if (ctl[0] & 0x80) |
126 | exit(20); | 157 | exit(20); |
127 | } | ||
128 | } | 158 | } |
129 | 159 | ||
130 | int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 160 | int crond_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
131 | int crond_main(int ac, char **av) | 161 | int crond_main(int ac, char **av) |
132 | { | 162 | { |
133 | unsigned opt; | 163 | unsigned opt; |
134 | char *lopt, *Lopt, *copt; | ||
135 | USE_DEBUG_CROND_OPTION(char *dopt;) | ||
136 | 164 | ||
137 | opt_complementary = "f-b:b-f:S-L:L-S" USE_DEBUG_CROND_OPTION(":d-l"); | 165 | INIT_G(); |
138 | opterr = 0; /* disable getopt 'errors' message. */ | 166 | |
167 | /* "-b after -f is ignored", and so on for every pair a-b */ | ||
168 | opt_complementary = "f-b:b-f:S-L:L-S" USE_DEBUG_CROND_OPTION(":d-l") | ||
169 | ":l+:d+"; /* -l and -d have numeric param */ | ||
139 | opt = getopt32(av, "l:L:fbSc:" USE_DEBUG_CROND_OPTION("d:"), | 170 | opt = getopt32(av, "l:L:fbSc:" USE_DEBUG_CROND_OPTION("d:"), |
140 | &lopt, &Lopt, &copt USE_DEBUG_CROND_OPTION(, &dopt)); | 171 | &LogLevel, &LogFile, &CDir |
141 | if (opt & 1) /* -l */ | 172 | USE_DEBUG_CROND_OPTION(,&LogLevel)); |
142 | LogLevel = xatou(lopt); | 173 | /* both -d N and -l N set the same variable: LogLevel */ |
143 | if (opt & 2) /* -L */ | ||
144 | if (*Lopt) | ||
145 | LogFile = Lopt; | ||
146 | if (opt & 32) /* -c */ | ||
147 | if (*copt) | ||
148 | CDir = copt; | ||
149 | #if ENABLE_DEBUG_CROND_OPTION | ||
150 | if (opt & 64) { /* -d */ | ||
151 | DebugOpt = xatou(dopt); | ||
152 | LogLevel = 0; | ||
153 | } | ||
154 | #endif | ||
155 | 174 | ||
156 | /* close stdin and stdout, stderr. | 175 | if (!(opt & OPT_f)) { |
157 | * close unused descriptors - don't need. | 176 | /* close stdin, stdout, stderr. |
158 | * optional detach from controlling terminal | 177 | * close unused descriptors - don't need them. */ |
159 | */ | ||
160 | if (!(opt & 4)) | ||
161 | bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, av); | 178 | bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, av); |
179 | } | ||
162 | 180 | ||
163 | xchdir(CDir); | 181 | if (!DebugOpt && LogFile == NULL) { |
164 | signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ | 182 | /* logging to syslog */ |
165 | 183 | openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); | |
166 | startlogger(); /* need if syslog mode selected */ | 184 | logmode = LOGMODE_SYSLOG; |
167 | 185 | } | |
168 | /* | ||
169 | * main loop - synchronize to 1 second after the minute, minimum sleep | ||
170 | * of 1 second. | ||
171 | */ | ||
172 | crondlog("\011%s " BB_VER " started, log level %d\n", | ||
173 | applet_name, LogLevel); | ||
174 | 186 | ||
187 | xchdir(CDir); | ||
188 | //signal(SIGHUP, SIG_IGN); /* ? original crond dies on HUP... */ | ||
189 | setenv("SHELL", DEFAULT_SHELL, 1); /* once, for all future children */ | ||
190 | crondlog(LVL9 "crond (busybox "BB_VER") started, log level %d", LogLevel); | ||
175 | SynchronizeDir(); | 191 | SynchronizeDir(); |
176 | 192 | ||
193 | /* main loop - synchronize to 1 second after the minute, minimum sleep | ||
194 | * of 1 second. */ | ||
177 | { | 195 | { |
178 | time_t t1 = time(NULL); | 196 | time_t t1 = time(NULL); |
179 | time_t t2; | 197 | time_t t2; |
180 | long dt; | 198 | long dt; |
181 | int rescan = 60; | 199 | int rescan = 60; |
182 | short sleep_time = 60; | 200 | int sleep_time = 60; |
183 | 201 | ||
184 | write_pidfile("/var/run/crond.pid"); | 202 | write_pidfile("/var/run/crond.pid"); |
185 | for (;;) { | 203 | for (;;) { |
186 | sleep((sleep_time + 1) - (short) (time(NULL) % sleep_time)); | 204 | sleep((sleep_time + 1) - (time(NULL) % sleep_time)); |
187 | 205 | ||
188 | t2 = time(NULL); | 206 | t2 = time(NULL); |
189 | dt = t2 - t1; | 207 | dt = (long)t2 - (long)t1; |
190 | 208 | ||
191 | /* | 209 | /* |
192 | * The file 'cron.update' is checked to determine new cron | 210 | * The file 'cron.update' is checked to determine new cron |
@@ -204,19 +222,15 @@ int crond_main(int ac, char **av) | |||
204 | * when running jobs, the inequality used is greater but not | 222 | * when running jobs, the inequality used is greater but not |
205 | * equal to t1, and less then or equal to t2. | 223 | * equal to t1, and less then or equal to t2. |
206 | */ | 224 | */ |
207 | |||
208 | if (--rescan == 0) { | 225 | if (--rescan == 0) { |
209 | rescan = 60; | 226 | rescan = 60; |
210 | SynchronizeDir(); | 227 | SynchronizeDir(); |
211 | } | 228 | } |
212 | CheckUpdates(); | 229 | CheckUpdates(); |
213 | #if ENABLE_DEBUG_CROND_OPTION | ||
214 | if (DebugOpt) | 230 | if (DebugOpt) |
215 | crondlog("\005Wakeup dt=%d\n", dt); | 231 | crondlog(LVL5 "wakeup dt=%ld", dt); |
216 | #endif | ||
217 | if (dt < -60 * 60 || dt > 60 * 60) { | 232 | if (dt < -60 * 60 || dt > 60 * 60) { |
218 | t1 = t2; | 233 | crondlog(WARN9 "time disparity of %d minutes detected", dt / 60); |
219 | crondlog("\111time disparity of %d minutes detected\n", dt / 60); | ||
220 | } else if (dt > 0) { | 234 | } else if (dt > 0) { |
221 | TestJobs(t1, t2); | 235 | TestJobs(t1, t2); |
222 | RunJobs(); | 236 | RunJobs(); |
@@ -226,66 +240,59 @@ int crond_main(int ac, char **av) | |||
226 | } else { | 240 | } else { |
227 | sleep_time = 60; | 241 | sleep_time = 60; |
228 | } | 242 | } |
229 | t1 = t2; | ||
230 | } | 243 | } |
244 | t1 = t2; | ||
231 | } | 245 | } |
232 | } | 246 | } |
233 | return 0; /* not reached */ | 247 | return 0; /* not reached */ |
234 | } | 248 | } |
235 | 249 | ||
236 | static int ChangeUser(const char *user) | 250 | #if SETENV_LEAKS |
251 | /* We set environment *before* vfork (because we want to use vfork), | ||
252 | * so we cannot use setenv() - repeated calls to setenv() may leak memory! | ||
253 | * Using putenv(), and freeing memory after unsetenv() won't leak */ | ||
254 | static void safe_setenv4(char **pvar_val, const char *var, const char *val /*, int len*/) | ||
237 | { | 255 | { |
238 | struct passwd *pas; | 256 | const int len = 4; /* both var names are 4 char long */ |
239 | const char *err_msg; | 257 | char *var_val = *pvar_val; |
240 | 258 | ||
241 | /* | 259 | if (var_val) { |
242 | * Obtain password entry and change privileges | 260 | var_val[len] = '\0'; /* nuke '=' */ |
243 | */ | 261 | unsetenv(var_val); |
244 | pas = getpwnam(user); | 262 | free(var_val); |
245 | if (pas == 0) { | ||
246 | crondlog("\011failed to get uid for %s", user); | ||
247 | return -1; | ||
248 | } | 263 | } |
264 | *pvar_val = xasprintf("%s=%s", var, val); | ||
265 | putenv(*pvar_val); | ||
266 | } | ||
267 | #endif | ||
268 | |||
269 | static void SetEnv(struct passwd *pas) | ||
270 | { | ||
271 | #if SETENV_LEAKS | ||
272 | safe_setenv4(&env_var_user, "USER", pas->pw_name); | ||
273 | safe_setenv4(&env_var_home, "HOME", pas->pw_dir); | ||
274 | /* if we want to set user's shell instead: */ | ||
275 | /*safe_setenv(env_var_user, "SHELL", pas->pw_shell, 5);*/ | ||
276 | #else | ||
249 | setenv("USER", pas->pw_name, 1); | 277 | setenv("USER", pas->pw_name, 1); |
250 | setenv("HOME", pas->pw_dir, 1); | 278 | setenv("HOME", pas->pw_dir, 1); |
251 | setenv("SHELL", DEFAULT_SHELL, 1); | 279 | #endif |
252 | 280 | /* currently, we use constant one: */ | |
253 | /* | 281 | /*setenv("SHELL", DEFAULT_SHELL, 1); - done earlier */ |
254 | * Change running state to the user in question | ||
255 | */ | ||
256 | err_msg = change_identity_e2str(pas); | ||
257 | if (err_msg) { | ||
258 | crondlog("\011%s for user %s", err_msg, user); | ||
259 | return -1; | ||
260 | } | ||
261 | if (chdir(pas->pw_dir) < 0) { | ||
262 | crondlog("\011chdir failed: %s: %m", pas->pw_dir); | ||
263 | if (chdir(TMPDIR) < 0) { | ||
264 | crondlog("\011chdir failed: %s: %m", TMPDIR); | ||
265 | return -1; | ||
266 | } | ||
267 | } | ||
268 | return pas->pw_uid; | ||
269 | } | 282 | } |
270 | 283 | ||
271 | static void startlogger(void) | 284 | static void ChangeUser(struct passwd *pas) |
272 | { | 285 | { |
273 | if (LogFile == NULL) { | 286 | /* careful: we're after vfork! */ |
274 | openlog(applet_name, LOG_CONS | LOG_PID, LOG_CRON); | 287 | change_identity(pas); /* - initgroups, setgid, setuid */ |
275 | } | 288 | if (chdir(pas->pw_dir) < 0) { |
276 | #if ENABLE_DEBUG_CROND_OPTION | 289 | crondlog(LVL9 "can't chdir(%s)", pas->pw_dir); |
277 | else { /* test logfile */ | 290 | if (chdir(TMPDIR) < 0) { |
278 | int logfd; | 291 | crondlog(DIE9 "can't chdir(%s)", TMPDIR); /* exits */ |
279 | |||
280 | logfd = open3_or_warn(LogFile, O_WRONLY | O_CREAT | O_APPEND, 0600); | ||
281 | if (logfd >= 0) { | ||
282 | close(logfd); | ||
283 | } | 292 | } |
284 | } | 293 | } |
285 | #endif | ||
286 | } | 294 | } |
287 | 295 | ||
288 | |||
289 | static const char DowAry[] ALIGN1 = | 296 | static const char DowAry[] ALIGN1 = |
290 | "sun""mon""tue""wed""thu""fri""sat" | 297 | "sun""mon""tue""wed""thu""fri""sat" |
291 | /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */ | 298 | /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */ |
@@ -308,17 +315,16 @@ static char *ParseField(char *user, char *ary, int modvalue, int off, | |||
308 | return NULL; | 315 | return NULL; |
309 | } | 316 | } |
310 | 317 | ||
311 | while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') { | 318 | while (!isspace(*ptr)) { |
312 | int skip = 0; | 319 | int skip = 0; |
313 | 320 | ||
314 | /* Handle numeric digit or symbol or '*' */ | 321 | /* Handle numeric digit or symbol or '*' */ |
315 | |||
316 | if (*ptr == '*') { | 322 | if (*ptr == '*') { |
317 | n1 = 0; /* everything will be filled */ | 323 | n1 = 0; /* everything will be filled */ |
318 | n2 = modvalue - 1; | 324 | n2 = modvalue - 1; |
319 | skip = 1; | 325 | skip = 1; |
320 | ++ptr; | 326 | ++ptr; |
321 | } else if (*ptr >= '0' && *ptr <= '9') { | 327 | } else if (isdigit(*ptr)) { |
322 | if (n1 < 0) { | 328 | if (n1 < 0) { |
323 | n1 = strtol(ptr, &ptr, 10) + off; | 329 | n1 = strtol(ptr, &ptr, 10) + off; |
324 | } else { | 330 | } else { |
@@ -344,9 +350,8 @@ static char *ParseField(char *user, char *ary, int modvalue, int off, | |||
344 | } | 350 | } |
345 | 351 | ||
346 | /* handle optional range '-' */ | 352 | /* handle optional range '-' */ |
347 | |||
348 | if (skip == 0) { | 353 | if (skip == 0) { |
349 | crondlog("\111failed user %s parsing %s\n", user, base); | 354 | crondlog(WARN9 "user %s: parse error at %s", user, base); |
350 | return NULL; | 355 | return NULL; |
351 | } | 356 | } |
352 | if (*ptr == '-' && n2 < 0) { | 357 | if (*ptr == '-' && n2 < 0) { |
@@ -358,18 +363,17 @@ static char *ParseField(char *user, char *ary, int modvalue, int off, | |||
358 | * collapse single-value ranges, handle skipmark, and fill | 363 | * collapse single-value ranges, handle skipmark, and fill |
359 | * in the character array appropriately. | 364 | * in the character array appropriately. |
360 | */ | 365 | */ |
361 | |||
362 | if (n2 < 0) { | 366 | if (n2 < 0) { |
363 | n2 = n1; | 367 | n2 = n1; |
364 | } | 368 | } |
365 | if (*ptr == '/') { | 369 | if (*ptr == '/') { |
366 | skip = strtol(ptr + 1, &ptr, 10); | 370 | skip = strtol(ptr + 1, &ptr, 10); |
367 | } | 371 | } |
372 | |||
368 | /* | 373 | /* |
369 | * fill array, using a failsafe is the easiest way to prevent | 374 | * fill array, using a failsafe is the easiest way to prevent |
370 | * an endless loop | 375 | * an endless loop |
371 | */ | 376 | */ |
372 | |||
373 | { | 377 | { |
374 | int s0 = 1; | 378 | int s0 = 1; |
375 | int failsafe = 1024; | 379 | int failsafe = 1024; |
@@ -382,13 +386,12 @@ static char *ParseField(char *user, char *ary, int modvalue, int off, | |||
382 | ary[n1 % modvalue] = 1; | 386 | ary[n1 % modvalue] = 1; |
383 | s0 = skip; | 387 | s0 = skip; |
384 | } | 388 | } |
385 | } | 389 | if (--failsafe == 0) { |
386 | while (n1 != n2 && --failsafe); | 390 | crondlog(WARN9 "user %s: parse error at %s", user, base); |
391 | return NULL; | ||
392 | } | ||
393 | } while (n1 != n2); | ||
387 | 394 | ||
388 | if (failsafe == 0) { | ||
389 | crondlog("\111failed user %s parsing %s\n", user, base); | ||
390 | return NULL; | ||
391 | } | ||
392 | } | 395 | } |
393 | if (*ptr != ',') { | 396 | if (*ptr != ',') { |
394 | break; | 397 | break; |
@@ -398,147 +401,124 @@ static char *ParseField(char *user, char *ary, int modvalue, int off, | |||
398 | n2 = -1; | 401 | n2 = -1; |
399 | } | 402 | } |
400 | 403 | ||
401 | if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') { | 404 | if (!isspace(*ptr)) { |
402 | crondlog("\111failed user %s parsing %s\n", user, base); | 405 | crondlog(WARN9 "user %s: parse error at %s", user, base); |
403 | return NULL; | 406 | return NULL; |
404 | } | 407 | } |
405 | 408 | ||
406 | while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') { | 409 | if (DebugOpt && (LogLevel <= 5)) { /* like LVL5 */ |
407 | ++ptr; | 410 | /* can't use crondlog, it inserts '\n' */ |
408 | } | ||
409 | #if ENABLE_DEBUG_CROND_OPTION | ||
410 | if (DebugOpt) { | ||
411 | int i; | 411 | int i; |
412 | 412 | for (i = 0; i < modvalue; ++i) | |
413 | for (i = 0; i < modvalue; ++i) { | 413 | fprintf(stderr, "%d", (unsigned char)ary[i]); |
414 | crondlog("\005%d", ary[i]); | 414 | fputc('\n', stderr); |
415 | } | ||
416 | crondlog("\005\n"); | ||
417 | } | 415 | } |
418 | #endif | 416 | return skip_whitespace(ptr); |
419 | |||
420 | return ptr; | ||
421 | } | 417 | } |
422 | 418 | ||
423 | static void FixDayDow(CronLine * line) | 419 | static void FixDayDow(CronLine *line) |
424 | { | 420 | { |
425 | int i; | 421 | int i; |
426 | int weekUsed = 0; | 422 | int weekUsed = 0; |
427 | int daysUsed = 0; | 423 | int daysUsed = 0; |
428 | 424 | ||
429 | for (i = 0; i < (int)(ARRAY_SIZE(line->cl_Dow)); ++i) { | 425 | for (i = 0; i < ARRAY_SIZE(line->cl_Dow); ++i) { |
430 | if (line->cl_Dow[i] == 0) { | 426 | if (line->cl_Dow[i] == 0) { |
431 | weekUsed = 1; | 427 | weekUsed = 1; |
432 | break; | 428 | break; |
433 | } | 429 | } |
434 | } | 430 | } |
435 | for (i = 0; i < (int)(ARRAY_SIZE(line->cl_Days)); ++i) { | 431 | for (i = 0; i < ARRAY_SIZE(line->cl_Days); ++i) { |
436 | if (line->cl_Days[i] == 0) { | 432 | if (line->cl_Days[i] == 0) { |
437 | daysUsed = 1; | 433 | daysUsed = 1; |
438 | break; | 434 | break; |
439 | } | 435 | } |
440 | } | 436 | } |
441 | if (weekUsed && !daysUsed) { | 437 | if (weekUsed != daysUsed) { |
442 | memset(line->cl_Days, 0, sizeof(line->cl_Days)); | 438 | if (weekUsed) |
443 | } | 439 | memset(line->cl_Days, 0, sizeof(line->cl_Days)); |
444 | if (daysUsed && !weekUsed) { | 440 | else /* daysUsed */ |
445 | memset(line->cl_Dow, 0, sizeof(line->cl_Dow)); | 441 | memset(line->cl_Dow, 0, sizeof(line->cl_Dow)); |
446 | } | 442 | } |
447 | } | 443 | } |
448 | 444 | ||
449 | |||
450 | |||
451 | static void SynchronizeFile(const char *fileName) | 445 | static void SynchronizeFile(const char *fileName) |
452 | { | 446 | { |
453 | int maxEntries = MAXLINES; | 447 | FILE *fi; |
448 | struct stat sbuf; | ||
449 | int maxEntries; | ||
454 | int maxLines; | 450 | int maxLines; |
455 | char buf[1024]; | 451 | char buf[1024]; |
456 | 452 | ||
453 | if (!fileName) | ||
454 | return; | ||
455 | |||
456 | DeleteFile(fileName); | ||
457 | fi = fopen(fileName, "r"); | ||
458 | if (!fi) | ||
459 | return; | ||
460 | |||
461 | maxEntries = MAXLINES; | ||
457 | if (strcmp(fileName, "root") == 0) { | 462 | if (strcmp(fileName, "root") == 0) { |
458 | maxEntries = 65535; | 463 | maxEntries = 65535; |
459 | } | 464 | } |
460 | maxLines = maxEntries * 10; | 465 | maxLines = maxEntries * 10; |
461 | 466 | ||
462 | if (fileName) { | 467 | if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) { |
463 | FILE *fi; | 468 | CronFile *file = xzalloc(sizeof(CronFile)); |
464 | 469 | CronLine **pline; | |
465 | DeleteFile(fileName); | ||
466 | |||
467 | fi = fopen(fileName, "r"); | ||
468 | if (fi != NULL) { | ||
469 | struct stat sbuf; | ||
470 | |||
471 | if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) { | ||
472 | CronFile *file = xzalloc(sizeof(CronFile)); | ||
473 | CronLine **pline; | ||
474 | |||
475 | file->cf_User = strdup(fileName); | ||
476 | pline = &file->cf_LineBase; | ||
477 | |||
478 | while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) { | ||
479 | CronLine line; | ||
480 | char *ptr; | ||
481 | |||
482 | trim(buf); | ||
483 | if (buf[0] == 0 || buf[0] == '#') { | ||
484 | continue; | ||
485 | } | ||
486 | if (--maxEntries == 0) { | ||
487 | break; | ||
488 | } | ||
489 | memset(&line, 0, sizeof(line)); | ||
490 | |||
491 | #if ENABLE_DEBUG_CROND_OPTION | ||
492 | if (DebugOpt) { | ||
493 | crondlog("\111User %s Entry %s\n", fileName, buf); | ||
494 | } | ||
495 | #endif | ||
496 | |||
497 | /* parse date ranges */ | ||
498 | ptr = ParseField(file->cf_User, line.cl_Mins, 60, 0, NULL, buf); | ||
499 | ptr = ParseField(file->cf_User, line.cl_Hrs, 24, 0, NULL, ptr); | ||
500 | ptr = ParseField(file->cf_User, line.cl_Days, 32, 0, NULL, ptr); | ||
501 | ptr = ParseField(file->cf_User, line.cl_Mons, 12, -1, MonAry, ptr); | ||
502 | ptr = ParseField(file->cf_User, line.cl_Dow, 7, 0, DowAry, ptr); | ||
503 | 470 | ||
504 | /* check failure */ | 471 | file->cf_User = xstrdup(fileName); |
505 | if (ptr == NULL) { | 472 | pline = &file->cf_LineBase; |
506 | continue; | ||
507 | } | ||
508 | |||
509 | /* | ||
510 | * fix days and dow - if one is not * and the other | ||
511 | * is *, the other is set to 0, and vise-versa | ||
512 | */ | ||
513 | |||
514 | FixDayDow(&line); | ||
515 | |||
516 | *pline = xzalloc(sizeof(CronLine)); | ||
517 | **pline = line; | ||
518 | 473 | ||
519 | /* copy command */ | 474 | while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) { |
520 | (*pline)->cl_Shell = strdup(ptr); | 475 | CronLine *line; |
521 | 476 | char *ptr; | |
522 | #if ENABLE_DEBUG_CROND_OPTION | ||
523 | if (DebugOpt) { | ||
524 | crondlog("\111 Command %s\n", ptr); | ||
525 | } | ||
526 | #endif | ||
527 | 477 | ||
528 | pline = &((*pline)->cl_Next); | 478 | trim(buf); |
529 | } | 479 | if (buf[0] == '\0' || buf[0] == '#') { |
530 | *pline = NULL; | 480 | continue; |
481 | } | ||
482 | if (--maxEntries == 0) { | ||
483 | break; | ||
484 | } | ||
485 | if (DebugOpt) { | ||
486 | crondlog(LVL5 "user:%s entry:%s", fileName, buf); | ||
487 | } | ||
488 | *pline = line = xzalloc(sizeof(CronLine)); | ||
489 | /* parse date ranges */ | ||
490 | ptr = ParseField(file->cf_User, line->cl_Mins, 60, 0, NULL, buf); | ||
491 | ptr = ParseField(file->cf_User, line->cl_Hrs, 24, 0, NULL, ptr); | ||
492 | ptr = ParseField(file->cf_User, line->cl_Days, 32, 0, NULL, ptr); | ||
493 | ptr = ParseField(file->cf_User, line->cl_Mons, 12, -1, MonAry, ptr); | ||
494 | ptr = ParseField(file->cf_User, line->cl_Dow, 7, 0, DowAry, ptr); | ||
495 | /* check failure */ | ||
496 | if (ptr == NULL) { | ||
497 | free(line); | ||
498 | continue; | ||
499 | } | ||
500 | /* | ||
501 | * fix days and dow - if one is not * and the other | ||
502 | * is *, the other is set to 0, and vise-versa | ||
503 | */ | ||
504 | FixDayDow(line); | ||
505 | /* copy command */ | ||
506 | line->cl_Shell = xstrdup(ptr); | ||
507 | if (DebugOpt) { | ||
508 | crondlog(LVL5 " command:%s", ptr); | ||
509 | } | ||
510 | pline = &line->cl_Next; | ||
511 | } | ||
512 | *pline = NULL; | ||
531 | 513 | ||
532 | file->cf_Next = FileBase; | 514 | file->cf_Next = FileBase; |
533 | FileBase = file; | 515 | FileBase = file; |
534 | 516 | ||
535 | if (maxLines == 0 || maxEntries == 0) { | 517 | if (maxLines == 0 || maxEntries == 0) { |
536 | crondlog("\111Maximum number of lines reached for user %s\n", fileName); | 518 | crondlog(WARN9 "maximum number of lines reached for user %s", fileName); |
537 | } | ||
538 | } | ||
539 | fclose(fi); | ||
540 | } | 519 | } |
541 | } | 520 | } |
521 | fclose(fi); | ||
542 | } | 522 | } |
543 | 523 | ||
544 | static void CheckUpdates(void) | 524 | static void CheckUpdates(void) |
@@ -550,6 +530,7 @@ static void CheckUpdates(void) | |||
550 | if (fi != NULL) { | 530 | if (fi != NULL) { |
551 | unlink(CRONUPDATE); | 531 | unlink(CRONUPDATE); |
552 | while (fgets(buf, sizeof(buf), fi) != NULL) { | 532 | while (fgets(buf, sizeof(buf), fi) != NULL) { |
533 | /* use first word only */ | ||
553 | SynchronizeFile(strtok(buf, " \t\r\n")); | 534 | SynchronizeFile(strtok(buf, " \t\r\n")); |
554 | } | 535 | } |
555 | fclose(fi); | 536 | fclose(fi); |
@@ -558,16 +539,14 @@ static void CheckUpdates(void) | |||
558 | 539 | ||
559 | static void SynchronizeDir(void) | 540 | static void SynchronizeDir(void) |
560 | { | 541 | { |
542 | CronFile *file; | ||
561 | /* Attempt to delete the database. */ | 543 | /* Attempt to delete the database. */ |
562 | 544 | again: | |
563 | for (;;) { | 545 | for (file = FileBase; file; file = file->cf_Next) { |
564 | CronFile *file; | 546 | if (!file->cf_Deleted) { |
565 | 547 | DeleteFile(file->cf_User); | |
566 | for (file = FileBase; file && file->cf_Deleted; file = file->cf_Next); | 548 | goto again; |
567 | if (file == NULL) { | ||
568 | break; | ||
569 | } | 549 | } |
570 | DeleteFile(file->cf_User); | ||
571 | } | 550 | } |
572 | 551 | ||
573 | /* | 552 | /* |
@@ -578,41 +557,36 @@ static void SynchronizeDir(void) | |||
578 | * | 557 | * |
579 | * scan directory and add associated users | 558 | * scan directory and add associated users |
580 | */ | 559 | */ |
581 | |||
582 | unlink(CRONUPDATE); | 560 | unlink(CRONUPDATE); |
583 | if (chdir(CDir) < 0) { | 561 | if (chdir(CDir) < 0) { |
584 | crondlog("\311cannot find %s\n", CDir); | 562 | crondlog(DIE9 "can't chdir(%s)", CDir); |
585 | } | 563 | } |
586 | { | 564 | { |
587 | DIR *dir = opendir("."); | 565 | DIR *dir = opendir("."); |
588 | struct dirent *den; | 566 | struct dirent *den; |
589 | 567 | ||
590 | if (dir) { | 568 | if (!dir) |
591 | while ((den = readdir(dir))) { | 569 | crondlog(DIE9 "can't chdir(%s)", "."); /* exits */ |
592 | if (strchr(den->d_name, '.') != NULL) { | 570 | while ((den = readdir(dir))) { |
593 | continue; | 571 | if (strchr(den->d_name, '.') != NULL) { |
594 | } | 572 | continue; |
595 | if (getpwnam(den->d_name)) { | 573 | } |
596 | SynchronizeFile(den->d_name); | 574 | if (getpwnam(den->d_name)) { |
597 | } else { | 575 | SynchronizeFile(den->d_name); |
598 | crondlog("\007ignoring %s\n", den->d_name); | 576 | } else { |
599 | } | 577 | crondlog(LVL7 "ignoring %s", den->d_name); |
600 | } | 578 | } |
601 | closedir(dir); | ||
602 | } else { | ||
603 | crondlog("\311cannot open current dir!\n"); | ||
604 | } | 579 | } |
580 | closedir(dir); | ||
605 | } | 581 | } |
606 | } | 582 | } |
607 | 583 | ||
608 | |||
609 | /* | 584 | /* |
610 | * DeleteFile() - delete user database | 585 | * DeleteFile() - delete user database |
611 | * | 586 | * |
612 | * Note: multiple entries for same user may exist if we were unable to | 587 | * Note: multiple entries for same user may exist if we were unable to |
613 | * completely delete a database due to running processes. | 588 | * completely delete a database due to running processes. |
614 | */ | 589 | */ |
615 | |||
616 | static void DeleteFile(const char *userName) | 590 | static void DeleteFile(const char *userName) |
617 | { | 591 | { |
618 | CronFile **pfile = &FileBase; | 592 | CronFile **pfile = &FileBase; |
@@ -656,7 +630,6 @@ static void DeleteFile(const char *userName) | |||
656 | * period is about a minute (one scan). Worst case it will be one | 630 | * period is about a minute (one scan). Worst case it will be one |
657 | * hour (60 scans). | 631 | * hour (60 scans). |
658 | */ | 632 | */ |
659 | |||
660 | static int TestJobs(time_t t1, time_t t2) | 633 | static int TestJobs(time_t t1, time_t t2) |
661 | { | 634 | { |
662 | int nJobs = 0; | 635 | int nJobs = 0; |
@@ -665,40 +638,37 @@ static int TestJobs(time_t t1, time_t t2) | |||
665 | /* Find jobs > t1 and <= t2 */ | 638 | /* Find jobs > t1 and <= t2 */ |
666 | 639 | ||
667 | for (t = t1 - t1 % 60; t <= t2; t += 60) { | 640 | for (t = t1 - t1 % 60; t <= t2; t += 60) { |
668 | if (t > t1) { | 641 | struct tm *tp; |
669 | struct tm *tp = localtime(&t); | 642 | CronFile *file; |
670 | CronFile *file; | 643 | CronLine *line; |
671 | CronLine *line; | ||
672 | 644 | ||
673 | for (file = FileBase; file; file = file->cf_Next) { | 645 | if (t <= t1) |
674 | #if ENABLE_DEBUG_CROND_OPTION | 646 | continue; |
647 | |||
648 | tp = localtime(&t); | ||
649 | for (file = FileBase; file; file = file->cf_Next) { | ||
650 | if (DebugOpt) | ||
651 | crondlog(LVL5 "file %s:", file->cf_User); | ||
652 | if (file->cf_Deleted) | ||
653 | continue; | ||
654 | for (line = file->cf_LineBase; line; line = line->cl_Next) { | ||
675 | if (DebugOpt) | 655 | if (DebugOpt) |
676 | crondlog("\005FILE %s:\n", file->cf_User); | 656 | crondlog(LVL5 " line %s", line->cl_Shell); |
677 | #endif | 657 | if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour] |
678 | if (file->cf_Deleted) | 658 | && (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday]) |
679 | continue; | 659 | && line->cl_Mons[tp->tm_mon] |
680 | for (line = file->cf_LineBase; line; line = line->cl_Next) { | 660 | ) { |
681 | #if ENABLE_DEBUG_CROND_OPTION | 661 | if (DebugOpt) { |
682 | if (DebugOpt) | 662 | crondlog(LVL5 " job: %d %s", |
683 | crondlog("\005 LINE %s\n", line->cl_Shell); | 663 | (int)line->cl_Pid, line->cl_Shell); |
684 | #endif | 664 | } |
685 | if (line->cl_Mins[tp->tm_min] && line->cl_Hrs[tp->tm_hour] && | 665 | if (line->cl_Pid > 0) { |
686 | (line->cl_Days[tp->tm_mday] || line->cl_Dow[tp->tm_wday]) | 666 | crondlog(LVL8 "user %s: process already running: %s", |
687 | && line->cl_Mons[tp->tm_mon]) { | 667 | file->cf_User, line->cl_Shell); |
688 | #if ENABLE_DEBUG_CROND_OPTION | 668 | } else if (line->cl_Pid == 0) { |
689 | if (DebugOpt) { | 669 | line->cl_Pid = -1; |
690 | crondlog("\005 JobToDo: %d %s\n", | 670 | file->cf_Ready = 1; |
691 | line->cl_Pid, line->cl_Shell); | 671 | ++nJobs; |
692 | } | ||
693 | #endif | ||
694 | if (line->cl_Pid > 0) { | ||
695 | crondlog("\010 process already running: %s %s\n", | ||
696 | file->cf_User, line->cl_Shell); | ||
697 | } else if (line->cl_Pid == 0) { | ||
698 | line->cl_Pid = -1; | ||
699 | file->cf_Ready = 1; | ||
700 | ++nJobs; | ||
701 | } | ||
702 | } | 672 | } |
703 | } | 673 | } |
704 | } | 674 | } |
@@ -713,23 +683,21 @@ static void RunJobs(void) | |||
713 | CronLine *line; | 683 | CronLine *line; |
714 | 684 | ||
715 | for (file = FileBase; file; file = file->cf_Next) { | 685 | for (file = FileBase; file; file = file->cf_Next) { |
716 | if (file->cf_Ready) { | 686 | if (!file->cf_Ready) |
717 | file->cf_Ready = 0; | 687 | continue; |
718 | |||
719 | for (line = file->cf_LineBase; line; line = line->cl_Next) { | ||
720 | if (line->cl_Pid < 0) { | ||
721 | |||
722 | RunJob(file->cf_User, line); | ||
723 | 688 | ||
724 | crondlog("\010USER %s pid %3d cmd %s\n", | 689 | file->cf_Ready = 0; |
725 | file->cf_User, line->cl_Pid, line->cl_Shell); | 690 | for (line = file->cf_LineBase; line; line = line->cl_Next) { |
726 | if (line->cl_Pid < 0) { | 691 | if (line->cl_Pid >= 0) |
727 | file->cf_Ready = 1; | 692 | continue; |
728 | } | 693 | |
729 | else if (line->cl_Pid > 0) { | 694 | RunJob(file->cf_User, line); |
730 | file->cf_Running = 1; | 695 | crondlog(LVL8 "USER %s pid %3d cmd %s", |
731 | } | 696 | file->cf_User, (int)line->cl_Pid, line->cl_Shell); |
732 | } | 697 | if (line->cl_Pid < 0) { |
698 | file->cf_Ready = 1; | ||
699 | } else if (line->cl_Pid > 0) { | ||
700 | file->cf_Running = 1; | ||
733 | } | 701 | } |
734 | } | 702 | } |
735 | } | 703 | } |
@@ -741,7 +709,6 @@ static void RunJobs(void) | |||
741 | * Check for job completion, return number of jobs still running after | 709 | * Check for job completion, return number of jobs still running after |
742 | * all done. | 710 | * all done. |
743 | */ | 711 | */ |
744 | |||
745 | static int CheckJobs(void) | 712 | static int CheckJobs(void) |
746 | { | 713 | { |
747 | CronFile *file; | 714 | CronFile *file; |
@@ -753,18 +720,18 @@ static int CheckJobs(void) | |||
753 | file->cf_Running = 0; | 720 | file->cf_Running = 0; |
754 | 721 | ||
755 | for (line = file->cf_LineBase; line; line = line->cl_Next) { | 722 | for (line = file->cf_LineBase; line; line = line->cl_Next) { |
756 | if (line->cl_Pid > 0) { | 723 | int status, r; |
757 | int status; | 724 | if (line->cl_Pid <= 0) |
758 | int r = waitpid(line->cl_Pid, &status, WNOHANG); | 725 | continue; |
759 | 726 | ||
760 | if (r < 0 || r == line->cl_Pid) { | 727 | r = waitpid(line->cl_Pid, &status, WNOHANG); |
761 | EndJob(file->cf_User, line); | 728 | if (r < 0 || r == line->cl_Pid) { |
762 | if (line->cl_Pid) { | 729 | EndJob(file->cf_User, line); |
763 | file->cf_Running = 1; | 730 | if (line->cl_Pid) { |
764 | } | ||
765 | } else if (r == 0) { | ||
766 | file->cf_Running = 1; | 731 | file->cf_Running = 1; |
767 | } | 732 | } |
733 | } else if (r == 0) { | ||
734 | file->cf_Running = 1; | ||
768 | } | 735 | } |
769 | } | 736 | } |
770 | } | 737 | } |
@@ -773,45 +740,51 @@ static int CheckJobs(void) | |||
773 | return nStillRunning; | 740 | return nStillRunning; |
774 | } | 741 | } |
775 | 742 | ||
776 | |||
777 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL | 743 | #if ENABLE_FEATURE_CROND_CALL_SENDMAIL |
744 | |||
745 | // TODO: sendmail should be _run-time_ option, not compile-time! | ||
746 | |||
778 | static void | 747 | static void |
779 | ForkJob(const char *user, CronLine * line, int mailFd, | 748 | ForkJob(const char *user, CronLine *line, int mailFd, |
780 | const char *prog, const char *cmd, const char *arg, | 749 | const char *prog, const char *cmd, const char *arg, |
781 | const char *mail_filename) | 750 | const char *mail_filename) |
782 | { | 751 | { |
783 | /* Fork as the user in question and run program */ | 752 | struct passwd *pas; |
784 | pid_t pid = fork(); | 753 | pid_t pid; |
785 | 754 | ||
786 | line->cl_Pid = pid; | 755 | /* prepare things before vfork */ |
756 | pas = getpwnam(user); | ||
757 | if (!pas) { | ||
758 | crondlog(LVL9 "can't get uid for %s", user); | ||
759 | goto err; | ||
760 | } | ||
761 | SetEnv(pas); | ||
762 | |||
763 | pid = vfork(); | ||
787 | if (pid == 0) { | 764 | if (pid == 0) { |
788 | /* CHILD */ | 765 | /* CHILD */ |
789 | /* Change running state to the user in question */ | 766 | /* change running state to the user in question */ |
790 | 767 | ChangeUser(pas); | |
791 | if (ChangeUser(user) < 0) { | ||
792 | exit(0); | ||
793 | } | ||
794 | #if ENABLE_DEBUG_CROND_OPTION | ||
795 | if (DebugOpt) { | 768 | if (DebugOpt) { |
796 | crondlog("\005Child Running %s\n", prog); | 769 | crondlog(LVL5 "child running %s", prog); |
797 | } | 770 | } |
798 | #endif | ||
799 | |||
800 | if (mailFd >= 0) { | 771 | if (mailFd >= 0) { |
801 | xmove_fd(mailFd, mail_filename ? 1 : 0); | 772 | xmove_fd(mailFd, mail_filename ? 1 : 0); |
802 | dup2(1, 2); | 773 | dup2(1, 2); |
803 | } | 774 | } |
804 | execl(prog, prog, cmd, arg, NULL); | 775 | execl(prog, prog, cmd, arg, NULL); |
805 | crondlog("\024cannot exec, user %s cmd %s %s %s\n", user, prog, cmd, arg); | 776 | crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, prog, cmd, arg); |
806 | if (mail_filename) { | 777 | if (mail_filename) { |
807 | fdprintf(1, "Exec failed: %s -c %s\n", prog, arg); | 778 | fdprintf(1, "Exec failed: %s -c %s\n", prog, arg); |
808 | } | 779 | } |
809 | exit(0); | 780 | _exit(0); |
810 | } | 781 | } |
811 | 782 | ||
783 | line->cl_Pid = pid; | ||
812 | if (pid < 0) { | 784 | if (pid < 0) { |
813 | /* FORK FAILED */ | 785 | /* FORK FAILED */ |
814 | crondlog("\024cannot fork\n"); | 786 | crondlog(ERR20 "can't vfork"); |
787 | err: | ||
815 | line->cl_Pid = 0; | 788 | line->cl_Pid = 0; |
816 | if (mail_filename) { | 789 | if (mail_filename) { |
817 | unlink(mail_filename); | 790 | unlink(mail_filename); |
@@ -822,20 +795,20 @@ ForkJob(const char *user, CronLine * line, int mailFd, | |||
822 | */ | 795 | */ |
823 | char mailFile2[128]; | 796 | char mailFile2[128]; |
824 | 797 | ||
825 | snprintf(mailFile2, sizeof(mailFile2), TMPDIR "/cron.%s.%d", user, pid); | 798 | snprintf(mailFile2, sizeof(mailFile2), "%s/cron.%s.%d", TMPDIR, user, pid); |
826 | rename(mail_filename, mailFile2); // TODO: xrename? | 799 | rename(mail_filename, mailFile2); // TODO: xrename? |
827 | } | 800 | } |
801 | |||
828 | /* | 802 | /* |
829 | * Close the mail file descriptor.. we can't just leave it open in | 803 | * Close the mail file descriptor.. we can't just leave it open in |
830 | * a structure, closing it later, because we might run out of descriptors | 804 | * a structure, closing it later, because we might run out of descriptors |
831 | */ | 805 | */ |
832 | |||
833 | if (mailFd >= 0) { | 806 | if (mailFd >= 0) { |
834 | close(mailFd); | 807 | close(mailFd); |
835 | } | 808 | } |
836 | } | 809 | } |
837 | 810 | ||
838 | static void RunJob(const char *user, CronLine * line) | 811 | static void RunJob(const char *user, CronLine *line) |
839 | { | 812 | { |
840 | char mailFile[128]; | 813 | char mailFile[128]; |
841 | int mailFd; | 814 | int mailFd; |
@@ -844,8 +817,7 @@ static void RunJob(const char *user, CronLine * line) | |||
844 | line->cl_MailFlag = 0; | 817 | line->cl_MailFlag = 0; |
845 | 818 | ||
846 | /* open mail file - owner root so nobody can screw with it. */ | 819 | /* open mail file - owner root so nobody can screw with it. */ |
847 | 820 | snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, getpid()); | |
848 | snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, getpid()); | ||
849 | mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600); | 821 | mailFd = open(mailFile, O_CREAT | O_TRUNC | O_WRONLY | O_EXCL | O_APPEND, 0600); |
850 | 822 | ||
851 | if (mailFd >= 0) { | 823 | if (mailFd >= 0) { |
@@ -854,7 +826,8 @@ static void RunJob(const char *user, CronLine * line) | |||
854 | line->cl_Shell); | 826 | line->cl_Shell); |
855 | line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR); | 827 | line->cl_MailPos = lseek(mailFd, 0, SEEK_CUR); |
856 | } else { | 828 | } else { |
857 | crondlog("\024cannot create mail file user %s file %s, output to /dev/null\n", user, mailFile); | 829 | crondlog(ERR20 "cannot create mail file %s for user %s, " |
830 | "discarding output", mailFile, user); | ||
858 | } | 831 | } |
859 | 832 | ||
860 | ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile); | 833 | ForkJob(user, line, mailFd, DEFAULT_SHELL, "-c", line->cl_Shell, mailFile); |
@@ -863,15 +836,13 @@ static void RunJob(const char *user, CronLine * line) | |||
863 | /* | 836 | /* |
864 | * EndJob - called when job terminates and when mail terminates | 837 | * EndJob - called when job terminates and when mail terminates |
865 | */ | 838 | */ |
866 | 839 | static void EndJob(const char *user, CronLine *line) | |
867 | static void EndJob(const char *user, CronLine * line) | ||
868 | { | 840 | { |
869 | int mailFd; | 841 | int mailFd; |
870 | char mailFile[128]; | 842 | char mailFile[128]; |
871 | struct stat sbuf; | 843 | struct stat sbuf; |
872 | 844 | ||
873 | /* No job */ | 845 | /* No job */ |
874 | |||
875 | if (line->cl_Pid <= 0) { | 846 | if (line->cl_Pid <= 0) { |
876 | line->cl_Pid = 0; | 847 | line->cl_Pid = 0; |
877 | return; | 848 | return; |
@@ -881,11 +852,10 @@ static void EndJob(const char *user, CronLine * line) | |||
881 | * End of job and no mail file | 852 | * End of job and no mail file |
882 | * End of sendmail job | 853 | * End of sendmail job |
883 | */ | 854 | */ |
884 | 855 | snprintf(mailFile, sizeof(mailFile), "%s/cron.%s.%d", TMPDIR, user, line->cl_Pid); | |
885 | snprintf(mailFile, sizeof(mailFile), TMPDIR "/cron.%s.%d", user, line->cl_Pid); | ||
886 | line->cl_Pid = 0; | 856 | line->cl_Pid = 0; |
887 | 857 | ||
888 | if (line->cl_MailFlag != 1) { | 858 | if (line->cl_MailFlag == 0) { |
889 | return; | 859 | return; |
890 | } | 860 | } |
891 | line->cl_MailFlag = 0; | 861 | line->cl_MailFlag = 0; |
@@ -894,7 +864,6 @@ static void EndJob(const char *user, CronLine * line) | |||
894 | * End of primary job - check for mail file. If size has increased and | 864 | * End of primary job - check for mail file. If size has increased and |
895 | * the file is still valid, we sendmail it. | 865 | * the file is still valid, we sendmail it. |
896 | */ | 866 | */ |
897 | |||
898 | mailFd = open(mailFile, O_RDONLY); | 867 | mailFd = open(mailFile, O_RDONLY); |
899 | unlink(mailFile); | 868 | unlink(mailFile); |
900 | if (mailFd < 0) { | 869 | if (mailFd < 0) { |
@@ -910,37 +879,43 @@ static void EndJob(const char *user, CronLine * line) | |||
910 | } | 879 | } |
911 | ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL); | 880 | ForkJob(user, line, mailFd, SENDMAIL, SENDMAIL_ARGS, NULL); |
912 | } | 881 | } |
913 | #else | ||
914 | /* crond without sendmail */ | ||
915 | 882 | ||
916 | static void RunJob(const char *user, CronLine * line) | 883 | #else /* crond without sendmail */ |
884 | |||
885 | static void RunJob(const char *user, CronLine *line) | ||
917 | { | 886 | { |
918 | /* Fork as the user in question and run program */ | 887 | struct passwd *pas; |
919 | pid_t pid = fork(); | 888 | pid_t pid; |
889 | |||
890 | /* prepare things before vfork */ | ||
891 | pas = getpwnam(user); | ||
892 | if (!pas) { | ||
893 | crondlog(LVL9 "can't get uid for %s", user); | ||
894 | goto err; | ||
895 | } | ||
896 | SetEnv(pas); | ||
920 | 897 | ||
898 | /* fork as the user in question and run program */ | ||
899 | pid = vfork(); | ||
921 | if (pid == 0) { | 900 | if (pid == 0) { |
922 | /* CHILD */ | 901 | /* CHILD */ |
923 | 902 | /* change running state to the user in question */ | |
924 | /* Change running state to the user in question */ | 903 | ChangeUser(pas); |
925 | |||
926 | if (ChangeUser(user) < 0) { | ||
927 | exit(0); | ||
928 | } | ||
929 | #if ENABLE_DEBUG_CROND_OPTION | ||
930 | if (DebugOpt) { | 904 | if (DebugOpt) { |
931 | crondlog("\005Child Running %s\n", DEFAULT_SHELL); | 905 | crondlog(LVL5 "child running %s", DEFAULT_SHELL); |
932 | } | 906 | } |
933 | #endif | ||
934 | |||
935 | execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL); | 907 | execl(DEFAULT_SHELL, DEFAULT_SHELL, "-c", line->cl_Shell, NULL); |
936 | crondlog("\024cannot exec, user %s cmd %s -c %s\n", user, | 908 | crondlog(ERR20 "can't exec, user %s cmd %s %s %s", user, |
937 | DEFAULT_SHELL, line->cl_Shell); | 909 | DEFAULT_SHELL, "-c", line->cl_Shell); |
938 | exit(0); | 910 | _exit(0); |
939 | } else if (pid < 0) { | 911 | } |
912 | if (pid < 0) { | ||
940 | /* FORK FAILED */ | 913 | /* FORK FAILED */ |
941 | crondlog("\024cannot, user %s\n", user); | 914 | crondlog(ERR20 "can't vfork"); |
915 | err: | ||
942 | pid = 0; | 916 | pid = 0; |
943 | } | 917 | } |
944 | line->cl_Pid = pid; | 918 | line->cl_Pid = pid; |
945 | } | 919 | } |
920 | |||
946 | #endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */ | 921 | #endif /* ENABLE_FEATURE_CROND_CALL_SENDMAIL */ |