ArmJob: fixed comparison btw MaxWait and NotUntil
[yacron.git] / job.c
blob8dc14028a18dc5d16d1f67e410659c52325b0b6d
2 /*
3 * JOB.C
5 * Copyright 1994 Matthew Dillon (dillon@apollo.backplane.com)
6 * Copyright 2009 James Pryor <profjim@jimpryor.net>
7 * May be distributed under the GNU General Public License
8 */
10 #include "defs.h"
12 Prototype void RunJob(CronFile *file, CronLine *line);
13 Prototype void EndJob(CronFile *file, CronLine *line, int exit_status);
14 Prototype const char *SendMail;
16 void
17 RunJob(CronFile *file, CronLine *line)
19 char mailFile[128];
20 int mailFd;
21 const char *value = Mailto;
23 line->cl_Pid = 0;
24 line->cl_MailFlag = 0;
27 * open mail file - owner root so nobody can screw with it.
30 snprintf(mailFile, sizeof(mailFile), TempFileFmt,
31 file->cf_UserName, (int)getpid());
32 mailFd = open(mailFile, O_CREAT|O_TRUNC|O_WRONLY|O_EXCL|O_APPEND, 0600);
34 if (mailFd >= 0) {
35 line->cl_MailFlag = 1;
36 if (!value)
37 value = file->cf_UserName;
38 fdprintf(mailFd, "To: %s\nSubject: cron for user %s %s\n\n",
39 value,
40 file->cf_UserName,
41 line->cl_Description
43 line->cl_MailPos = lseek(mailFd, 0, 1);
45 /* else we issue warning later */
48 * Fork as the user in question and run program
51 if ((line->cl_Pid = fork()) == 0) {
53 * CHILD, FORK OK
57 * Change running state to the user in question
60 if (ChangeUser(file->cf_UserName, 1) < 0) {
61 logn(LOG_ERR, "ChangeUser failed (user %s %s)\n",
62 file->cf_UserName,
63 line->cl_Description
65 exit(0);
68 if (DebugOpt)
69 logn(LOG_DEBUG, "child running: %s\n", line->cl_Description);
71 /* Setup close-on-exec descriptor in case exec fails */
72 dup2(2, 8);
73 fcntl(8, F_SETFD, 1);
74 fclose(stderr);
76 /* stdin is already /dev/null, setup stdout and stderr */
78 if (mailFd >= 0) {
79 dup2(mailFd, 1);
80 dup2(mailFd, 2);
81 close(mailFd);
82 } else {
83 logfd(LOG_WARNING, 8, "unable to create mail file %s: cron output for user %s %s to /dev/null\n",
84 mailFile,
85 file->cf_UserName,
86 line->cl_Description
89 execl("/bin/sh", "/bin/sh", "-c", line->cl_Shell, NULL, NULL);
90 logfd(LOG_ERR, 8, "unable to exec (user %s cmd /bin/sh -c %s)\n",
91 file->cf_UserName,
92 line->cl_Shell
94 /* we also write error to the mailed cron output */
95 fdprintf(1, "unable to exec: /bin/sh -c %s\n", line->cl_Shell);
96 exit(0);
97 } else if (line->cl_Pid < 0) {
99 * PARENT, FORK FAILED
101 logn(LOG_ERR, "unable to fork (user %s %s)\n",
102 file->cf_UserName,
103 line->cl_Description
105 line->cl_Pid = 0;
106 remove(mailFile);
107 } else {
109 * PARENT, FORK SUCCESS
111 * rename mail-file based on pid of child process
113 char mailFile2[128];
115 snprintf(mailFile2, sizeof(mailFile2), TempFileFmt,
116 file->cf_UserName, line->cl_Pid);
117 rename(mailFile, mailFile2);
121 * Close the mail file descriptor.. we can't just leave it open in
122 * a structure, closing it later, because we might run out of descriptors
125 if (mailFd >= 0)
126 close(mailFd);
130 * EndJob - called when job terminates and when mail terminates
133 void
134 EndJob(CronFile *file, CronLine *line, int exit_status)
136 int mailFd;
137 char mailFile[128];
138 struct stat sbuf;
139 struct CronNotifier *notif;
141 if (line->cl_Pid <= 0) {
143 * No job
145 line->cl_Pid = 0;
146 return;
151 * check return status?
153 if (line->cl_Delay > 0) {
154 if (exit_status == EAGAIN) {
155 /* returned EAGAIN, wait cl_Delay then retry */
157 line->cl_NotUntil = time(NULL) + line->cl_Delay;
159 line->cl_NotUntil += line->cl_Delay; // we base off the time the job was scheduled/started waiting, not the time it finished
160 } else {
161 /* process finished without returning EAGAIN (it may have returned some other error)
162 * mark as having run and update timestamp
164 FILE *fi;
165 char buf[64];
167 line->cl_LastRan = time(NULL);
169 line->cl_LastRan = line->cl_NotUntil; // we base off the time the job was scheduled/started waiting, not the time it finished
170 if ((fi = fopen(line->cl_Timestamp, "w")) != NULL) {
171 if (strftime(buf, sizeof(buf), TIMESTAMP_FMT, localtime(&line->cl_LastRan))) {
172 fputs(buf, fi);
173 } else
174 logn(LOG_NOTICE, "unable to format timestamp (user %s %s)\n", file->cf_UserName, line->cl_Description);
175 fclose(fi);
176 } else {
177 logn(LOG_NOTICE, "unable to write timestamp to %s (user %s %s)\n", line->cl_Timestamp, file->cf_UserName, line->cl_Description);
179 line->cl_NotUntil = line->cl_LastRan;
180 line->cl_NotUntil += (line->cl_Freq > 0) ? line->cl_Freq : line->cl_Delay;
184 if (exit_status != EAGAIN) {
186 * notify any waiters
188 notif = line->cl_Notifs;
189 while (notif) {
190 if (notif->cn_Waiter) {
191 notif->cn_Waiter->cw_Flag = exit_status;
193 notif = notif->cn_Next;
196 if (exit_status) {
198 * log non-zero exit_status
200 logn(LOG_INFO, "exit status %d from %s %s\n",
201 exit_status,
202 file->cf_UserName,
203 line->cl_Description
209 snprintf(mailFile, sizeof(mailFile), TempFileFmt,
210 file->cf_UserName, line->cl_Pid);
212 line->cl_Pid = 0;
214 if (line->cl_MailFlag != 1)
216 * End of job and no mail file
218 return;
220 line->cl_MailFlag = 0;
223 * Check mail file. If size has increased and
224 * the file is still valid, we sendmail it.
227 mailFd = open(mailFile, O_RDONLY);
228 remove(mailFile);
229 if (mailFd < 0) {
230 return;
233 if (fstat(mailFd, &sbuf) < 0 ||
234 sbuf.st_uid != DaemonUid ||
235 sbuf.st_nlink != 0 ||
236 sbuf.st_size == line->cl_MailPos ||
237 !S_ISREG(sbuf.st_mode)
239 close(mailFd);
240 return;
243 if ((line->cl_Pid = fork()) == 0) {
245 * CHILD, FORK OK
249 * change user id - no way in hell security can be compromised
250 * by the mailing and we already verified the mail file.
253 if (ChangeUser(file->cf_UserName, 1) < 0) {
254 logn(LOG_ERR, "ChangeUser %s failed; unable to send mail\n",
255 file->cf_UserName
257 exit(0);
260 /* create close-on-exec log descriptor in case exec fails */
261 dup2(2, 8);
262 fcntl(8, F_SETFD, 1);
263 fclose(stderr);
266 * run sendmail with mail file as standard input, only if
267 * mail file exists!
270 dup2(mailFd, 0);
271 dup2(1, 2);
272 close(mailFd);
274 if (!SendMail) {
275 logfd(LOG_INFO, 8, "mailing cron output for user %s %s\n",
276 file->cf_UserName,
277 line->cl_Description
279 execl(SENDMAIL, SENDMAIL, SENDMAIL_ARGS, NULL, NULL);
280 } else
281 execl(SendMail, SendMail, NULL, NULL);
283 logfd(LOG_WARNING, 8, "unable to exec %s %s: cron output for user %s %s to /dev/null\n",
284 SendMail,
285 SENDMAIL_ARGS,
286 file->cf_UserName,
287 line->cl_Description
289 exit(0);
290 } else if (line->cl_Pid < 0) {
292 * PARENT, FORK FAILED
294 logn(LOG_ERR, "unable to fork: cron output for user %s %s to /dev/null\n",
295 file->cf_UserName,
296 line->cl_Description
298 } else {
300 * PARENT, FORK OK
303 line->cl_Pid = 0;
304 close(mailFd);