round all timestamps to start of minute
[yacron.git] / job.c
blobf30098e16b235c67e6e57737c9edae2f2274dba1
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
156 * we base off the time the job was scheduled/started waiting, not the time it finished
159 line->cl_NotUntil = time(NULL) + line->cl_Delay; // to base off time finished
160 line->cl_NotUntil += line->cl_Delay; // already applied
162 } else {
163 /* process finished without returning EAGAIN (it may have returned some other error)
164 * mark as having run and update timestamp
166 FILE *fi;
167 char buf[64];
168 int succeeded = 0;
170 * we base off the time the job was scheduled/started waiting, not the time it finished
173 line->cl_LastRan = time(NULL); // to base off time finished
175 line->cl_LastRan = line->cl_NotUntil - line->cl_Delay;
176 if ((fi = fopen(line->cl_Timestamp, "w")) != NULL) {
177 if (strftime(buf, sizeof(buf), TIMESTAMP_FMT, localtime(&line->cl_LastRan)))
178 if (fputs(buf, fi) >= 0)
179 succeeded = 1;
180 fclose(fi);
182 if (!succeeded)
183 logn(LOG_WARNING, "unable to write timestamp to %s (user %s %s)\n", line->cl_Timestamp, file->cf_UserName, line->cl_Description);
184 line->cl_NotUntil = line->cl_LastRan;
185 line->cl_NotUntil += (line->cl_Freq > 0) ? line->cl_Freq : line->cl_Delay;
189 if (exit_status != EAGAIN) {
191 * notify any waiters
193 notif = line->cl_Notifs;
194 while (notif) {
195 if (notif->cn_Waiter) {
196 notif->cn_Waiter->cw_Flag = exit_status;
198 notif = notif->cn_Next;
201 if (exit_status) {
203 * log non-zero exit_status
205 logn(LOG_INFO, "exit status %d from %s %s\n",
206 exit_status,
207 file->cf_UserName,
208 line->cl_Description
214 snprintf(mailFile, sizeof(mailFile), TempFileFmt,
215 file->cf_UserName, line->cl_Pid);
217 line->cl_Pid = 0;
219 if (line->cl_MailFlag != 1)
221 * End of job and no mail file
223 return;
225 line->cl_MailFlag = 0;
228 * Check mail file. If size has increased and
229 * the file is still valid, we sendmail it.
232 mailFd = open(mailFile, O_RDONLY);
233 remove(mailFile);
234 if (mailFd < 0) {
235 return;
238 if (fstat(mailFd, &sbuf) < 0 ||
239 sbuf.st_uid != DaemonUid ||
240 sbuf.st_nlink != 0 ||
241 sbuf.st_size == line->cl_MailPos ||
242 !S_ISREG(sbuf.st_mode)
244 close(mailFd);
245 return;
248 if ((line->cl_Pid = fork()) == 0) {
250 * CHILD, FORK OK
254 * change user id - no way in hell security can be compromised
255 * by the mailing and we already verified the mail file.
258 if (ChangeUser(file->cf_UserName, 1) < 0) {
259 logn(LOG_ERR, "ChangeUser %s failed; unable to send mail\n",
260 file->cf_UserName
262 exit(0);
265 /* create close-on-exec log descriptor in case exec fails */
266 dup2(2, 8);
267 fcntl(8, F_SETFD, 1);
268 fclose(stderr);
271 * run sendmail with mail file as standard input, only if
272 * mail file exists!
275 dup2(mailFd, 0);
276 dup2(1, 2);
277 close(mailFd);
279 if (!SendMail) {
280 logfd(LOG_INFO, 8, "mailing cron output for user %s %s\n",
281 file->cf_UserName,
282 line->cl_Description
284 execl(SENDMAIL, SENDMAIL, SENDMAIL_ARGS, NULL, NULL);
285 } else
286 execl(SendMail, SendMail, NULL, NULL);
288 logfd(LOG_WARNING, 8, "unable to exec %s %s: cron output for user %s %s to /dev/null\n",
289 SendMail,
290 SENDMAIL_ARGS,
291 file->cf_UserName,
292 line->cl_Description
294 exit(0);
295 } else if (line->cl_Pid < 0) {
297 * PARENT, FORK FAILED
299 logn(LOG_ERR, "unable to fork: cron output for user %s %s to /dev/null\n",
300 file->cf_UserName,
301 line->cl_Description
303 } else {
305 * PARENT, FORK OK
308 line->cl_Pid = 0;
309 close(mailFd);