tweak log levels & messages
[yacron.git] / job.c
blob15d390c586f7f6c3655a0fb3161bc93907d9b886
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, "unable to ChangeUser (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_NOTICE, "exit status %d from user %s %s\n",
206 exit_status,
207 file->cf_UserName,
208 line->cl_Description
213 if (!exit_status || exit_status == EAGAIN)
214 if (DebugOpt)
215 logn(LOG_DEBUG, "exit status %d from user %s %s\n",
216 exit_status,
217 file->cf_UserName,
218 line->cl_Description
222 snprintf(mailFile, sizeof(mailFile), TempFileFmt,
223 file->cf_UserName, line->cl_Pid);
225 line->cl_Pid = 0;
227 if (line->cl_MailFlag != 1)
229 * End of job and no mail file
231 return;
233 line->cl_MailFlag = 0;
236 * Check mail file. If size has increased and
237 * the file is still valid, we sendmail it.
240 mailFd = open(mailFile, O_RDONLY);
241 remove(mailFile);
242 if (mailFd < 0) {
243 return;
246 if (fstat(mailFd, &sbuf) < 0 ||
247 sbuf.st_uid != DaemonUid ||
248 sbuf.st_nlink != 0 ||
249 sbuf.st_size == line->cl_MailPos ||
250 !S_ISREG(sbuf.st_mode)
252 close(mailFd);
253 return;
256 if ((line->cl_Pid = fork()) == 0) {
258 * CHILD, FORK OK
262 * change user id - no way in hell security can be compromised
263 * by the mailing and we already verified the mail file.
266 if (ChangeUser(file->cf_UserName, 1) < 0) {
267 logn(LOG_ERR, "unable to ChangeUser to send mail (user %s %s)\n",
268 file->cf_UserName,
269 line->cl_Description
271 exit(0);
274 /* create close-on-exec log descriptor in case exec fails */
275 dup2(2, 8);
276 fcntl(8, F_SETFD, 1);
277 fclose(stderr);
280 * run sendmail with mail file as standard input, only if
281 * mail file exists!
284 dup2(mailFd, 0);
285 dup2(1, 2);
286 close(mailFd);
288 if (!SendMail) {
289 logfd(LOG_INFO, 8, "mailing cron output for user %s %s\n",
290 file->cf_UserName,
291 line->cl_Description
293 execl(SENDMAIL, SENDMAIL, SENDMAIL_ARGS, NULL, NULL);
294 /* exec failed, log error */
295 SendMail = SENDMAIL;
296 } else
297 execl(SendMail, SendMail, NULL, NULL);
299 logfd(LOG_WARNING, 8, "unable to exec %s: cron output for user %s %s to /dev/null\n",
300 SendMail,
301 file->cf_UserName,
302 line->cl_Description
304 exit(0);
305 } else if (line->cl_Pid < 0) {
307 * PARENT, FORK FAILED
309 logn(LOG_WARNING, "unable to fork: cron output for user %s %s to /dev/null\n",
310 file->cf_UserName,
311 line->cl_Description
313 } else {
315 * PARENT, FORK OK
318 line->cl_Pid = 0;
319 close(mailFd);