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
12 Prototype
void RunJob(CronFile
*file
, CronLine
*line
);
13 Prototype
void EndJob(CronFile
*file
, CronLine
*line
, int exit_status
);
14 Prototype
const char *SendMail
;
17 RunJob(CronFile
*file
, CronLine
*line
)
21 const char *value
= Mailto
;
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);
35 line
->cl_MailFlag
= 1;
37 value
= file
->cf_UserName
;
38 fdprintf(mailFd
, "To: %s\nSubject: cron for user %s %s\n\n",
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) {
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",
69 logn(LOG_DEBUG
, "child running: %s\n", line
->cl_Description
);
71 /* Setup close-on-exec descriptor in case exec fails */
76 /* stdin is already /dev/null, setup stdout and stderr */
83 logfd(LOG_WARNING
, 8, "unable to create mail file %s: cron output for user %s %s to /dev/null\n",
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",
94 /* we also write error to the mailed cron output */
95 fdprintf(1, "unable to exec: /bin/sh -c %s\n", line
->cl_Shell
);
97 } else if (line
->cl_Pid
< 0) {
101 logn(LOG_ERR
, "unable to fork (user %s %s)\n",
109 * PARENT, FORK SUCCESS
111 * rename mail-file based on pid of child process
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
130 * EndJob - called when job terminates and when mail terminates
134 EndJob(CronFile
*file
, CronLine
*line
, int exit_status
)
139 struct CronNotifier
*notif
;
141 if (line
->cl_Pid
<= 0) {
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
163 /* process finished without returning EAGAIN (it may have returned some other error)
164 * mark as having run and update timestamp
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)
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
) {
193 notif
= line
->cl_Notifs
;
195 if (notif
->cn_Waiter
) {
196 notif
->cn_Waiter
->cw_Flag
= exit_status
;
198 notif
= notif
->cn_Next
;
203 * log non-zero exit_status
205 logn(LOG_INFO
, "exit status %d from %s %s\n",
214 snprintf(mailFile
, sizeof(mailFile
), TempFileFmt
,
215 file
->cf_UserName
, line
->cl_Pid
);
219 if (line
->cl_MailFlag
!= 1)
221 * End of job and no mail file
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
);
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
)
248 if ((line
->cl_Pid
= fork()) == 0) {
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",
265 /* create close-on-exec log descriptor in case exec fails */
267 fcntl(8, F_SETFD
, 1);
271 * run sendmail with mail file as standard input, only if
280 logfd(LOG_INFO
, 8, "mailing cron output for user %s %s\n",
284 execl(SENDMAIL
, SENDMAIL
, SENDMAIL_ARGS
, NULL
, NULL
);
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",
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",