1 /*
2 Anacron - run commands periodically
3 Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
4 Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 The GNU General Public License can also be found in the file
21 `COPYING' that comes with the Anacron source distribution.
22 */
23
24
25 #include <time.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <signal.h>
30 #include <fcntl.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <string.h>
34 #include "global.h"
35 #include "gregor.h"
36
37 pid_t primary_pid;
38 int day_now;
39 int year, month, day_of_month; /* date anacron started */
40
41 char *program_name;
42 char *anacrontab;
43 int serialize, force, update_only, now,
44 no_daemon, quiet; /* command-line options */
45 char **args; /* vector of "job" command-line arguments */
46 int nargs; /* number of these */
47 char *defarg = "*";
48 int in_background; /* are we in the background? */
49 int old_umask; /* umask when started */
50 sigset_t old_sigmask; /* signal mask when started */
51
52 job_rec *first_job_rec;
53 env_rec *first_env_rec;
54
55 static time_t start_sec; /* time anacron started */
56 static volatile int got_sigalrm, got_sigchld, got_sigusr1;
57 int running_jobs, running_mailers; /* , number of */
58
59 static void
print_version()60 print_version()
61 {
62 printf("Anacron " RELEASE "\n"
63 "Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>\n"
64 "Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>\n"
65 "\n"
66 "Mail comments, suggestions and bug reports to <shaleh@debian.org>."
67 "\n\n");
68 }
69
70 static void
print_usage()71 print_usage()
72 {
73 printf("Usage: anacron [-s] [-f] [-n] [-d] [-q] [-t anacrontab] [job] ...\n"
74 " anacron -u [job] ...\n"
75 " anacron [-V|-h]\n"
76 "\n"
77 " -s Serialize execution of jobs\n"
78 " -f Force execution of jobs, even before their time\n"
79 " -n Run jobs with no delay, implies -s\n"
80 " -d Don't fork to the background\n"
81 " -q Suppress stderr messages, only applicable with -d\n"
82 " -u Update the timestamps without actually running anything\n"
83 " -t Use this anacrontab\n"
84 " -V Print version information\n"
85 " -h Print this message\n"
86 "\n"
87 "See the manpage for more details.\n"
88 "\n");
89 }
90
91 static void
parse_opts(int argc,char * argv[])92 parse_opts(int argc, char *argv[])
93 /* Parse command-line options */
94 {
95 int opt;
96
97 quiet = no_daemon = serialize = force = update_only = now = 0;
98 opterr = 0;
99 while ((opt = getopt(argc, argv, "sfundqt:Vh")) != EOF)
100 {
101 switch (opt)
102 {
103 case 's':
104 serialize = 1;
105 break;
106 case 'f':
107 force = 1;
108 break;
109 case 'u':
110 update_only = 1;
111 break;
112 case 'n':
113 now = serialize = 1;
114 break;
115 case 'd':
116 no_daemon = 1;
117 break;
118 case 'q':
119 quiet = 1;
120 break;
121 case 't':
122 anacrontab = strdup(optarg);
123 break;
124 case 'V':
125 print_version();
126 exit(0);
127 case 'h':
128 print_usage();
129 exit(0);
130 case '?':
131 fprintf(stderr, "%s: invalid option: %c\n",
132 program_name, optopt);
133 fprintf(stderr, "type: `%s -h' for more information\n",
134 program_name);
135 exit(FAILURE_EXIT);
136 }
137 }
138 if (optind == argc)
139 {
140 /* no arguments. Equivalent to: `*' */
141 nargs = 1;
142 args = &defarg;
143 }
144 else
145 {
146 nargs = argc - optind;
147 args = argv + optind;
148 }
149 }
150
151 pid_t
xfork()152 xfork()
153 /* Like fork(), only never returns on failure */
154 {
155 pid_t pid;
156
157 pid = fork();
158 if (pid == -1) die_e("Can't fork");
159 return pid;
160 }
161
162 int
xopen(int fd,const char * file_name,int flags)163 xopen(int fd, const char *file_name, int flags)
164 /* Like open, only it:
165 * a) never returns on failure, and
166 * b) if "fd" is non-negative, expect the file to open
167 * on file-descriptor "fd".
168 */
169 {
170 int rfd;
171
172 rfd = open(file_name, flags);
173 if (fd >= 0 && rfd != fd)
174 die_e("Can't open %s on file-descriptor %d", file_name, fd);
175 else if (rfd < 0)
176 die_e("Can't open %s", file_name);
177 return rfd;
178 }
179
180 void
xclose(int fd)181 xclose(int fd)
182 /* Like close(), only doesn't return on failure */
183 {
184 if (close(fd)) die_e("Can't close file descriptor %d", fd);
185 }
186
187 static void
go_background()188 go_background()
189 /* Become a daemon. The foreground process exits successfully. */
190 {
191 pid_t pid;
192
193 /* stdin is already closed */
194
195 if (fclose(stdout)) die_e("Can't close stdout");
196 xopen(1, "/dev/null", O_WRONLY);
197
198 if (fclose(stderr)) die_e("Can't close stderr");
199 xopen(2, "/dev/null", O_WRONLY);
200
201 pid = xfork();
202 if (pid != 0)
203 {
204 /* parent */
205 exit(0);
206 }
207 else
208 {
209 /* child */
210 primary_pid = getpid();
211 if (setsid() == -1) die_e("setsid() error");
212 in_background = 1;
213 }
214 }
215
216 void
handle_sigalrm()217 handle_sigalrm()
218 {
219 got_sigalrm = 1;
220 }
221
222 void
handle_sigchld()223 handle_sigchld()
224 {
225 got_sigchld = 1;
226 }
227
228 void
handle_sigusr1()229 handle_sigusr1()
230 {
231 got_sigusr1 = 1;
232 }
233
234 static void
set_signal_handling()235 set_signal_handling()
236 /* We only use SIGALRM, SIGCHLD and SIGUSR1, and we unblock them only
237 * in wait_signal().
238 */
239 {
240 sigset_t ss;
241 struct sigaction sa;
242
243 got_sigalrm = got_sigchld = got_sigusr1 = 0;
244
245 /* block SIGALRM, SIGCHLD and SIGUSR1 */
246 if (sigemptyset(&ss) ||
247 sigaddset(&ss, SIGALRM) ||
248 sigaddset(&ss, SIGCHLD) ||
249 sigaddset(&ss, SIGUSR1)) die_e("sigset error");
250 if (sigprocmask(SIG_BLOCK, &ss, NULL)) die_e ("sigprocmask error");
251
252 /* setup SIGALRM handler */
253 sa.sa_handler = handle_sigalrm;
254 sa.sa_mask = ss;
255 sa.sa_flags = 0;
256 if (sigaction(SIGALRM, &sa, NULL)) die_e("sigaction error");
257
258 /* setup SIGCHLD handler */
259 sa.sa_handler = handle_sigchld;
260 sa.sa_mask = ss;
261 sa.sa_flags = SA_NOCLDSTOP;
262 if (sigaction(SIGCHLD, &sa, NULL)) die_e("sigaction error");
263
264 /* setup SIGUSR1 handler */
265 sa.sa_handler = handle_sigusr1;
266 sa.sa_mask = ss;
267 sa.sa_flags = 0;
268 if (sigaction(SIGUSR1, &sa, NULL)) die_e("sigaction error");
269 }
270
271 static void
wait_signal()272 wait_signal()
273 /* Return after a signal is caught */
274 {
275 sigset_t ss;
276
277 if (sigprocmask(0, NULL, &ss)) die_e("sigprocmask error");
278 if (sigdelset(&ss, SIGALRM) ||
279 sigdelset(&ss, SIGCHLD) ||
280 sigdelset(&ss, SIGUSR1)) die_e("sigset error");
281 sigsuspend(&ss);
282 }
283
284 static void
wait_children()285 wait_children()
286 /* Wait until we have no more children (of any kind) */
287 {
288 while (running_jobs > 0 || running_mailers > 0)
289 {
290 wait_signal();
291 if (got_sigchld) tend_children();
292 got_sigchld = 0;
293 if (got_sigusr1) explain("Received SIGUSR1");
294 got_sigusr1 = 0;
295 }
296 }
297
298 static void
orderly_termination()299 orderly_termination()
300 /* Execution is diverted here, when we get SIGUSR1 */
301 {
302 explain("Received SIGUSR1");
303 got_sigusr1 = 0;
304 wait_children();
305 explain("Exited");
306 exit(0);
307 }
308
309 static void
xsleep(unsigned int n)310 xsleep(unsigned int n)
311 /* Sleep for n seconds, servicing SIGCHLDs and SIGUSR1s in the meantime.
312 * If n=0, return immediately.
313 */
314 {
315 if (n == 0) return;
316 alarm(n);
317 do
318 {
319 wait_signal();
320 if (got_sigchld) tend_children();
321 got_sigchld = 0;
322 if (got_sigusr1) orderly_termination();
323 }
324 while (!got_sigalrm);
325 got_sigalrm = 0;
326 }
327
328 static void
wait_jobs()329 wait_jobs()
330 /* Wait until there are no running jobs,
331 * servicing SIGCHLDs and SIGUSR1s in the meantime.
332 */
333 {
334 while (running_jobs > 0)
335 {
336 wait_signal();
337 if (got_sigchld) tend_children();
338 got_sigchld = 0;
339 if (got_sigusr1) orderly_termination();
340 }
341 }
342
343 static void
record_start_time()344 record_start_time()
345 {
346 struct tm *tm_now;
347
348 start_sec = time(NULL);
349 tm_now = localtime(&start_sec);
350 year = tm_now->tm_year + 1900;
351 month = tm_now->tm_mon + 1;
352 day_of_month = tm_now->tm_mday;
353 day_now = day_num(year, month, day_of_month);
354 if (day_now == -1) die("Invalid date (this is really embarrassing)");
355 if (!update_only)
356 explain("Anacron " RELEASE " started on %04d-%02d-%02d",
357 year, month, day_of_month);
358 }
359
360 static int
time_till(job_rec * jr)361 time_till(job_rec *jr)
362 /* Return the number of seconds that we have to wait until it's time
363 * to start job jr.
364 */
365 {
366 unsigned int tj, tn;
367
368 if (now) return 0;
369 tn = time(NULL);
370 tj = start_sec + jr->delay * 60;
371 if (tj < tn) return 0;
372 return tj - tn;
373 }
374
375 static void
fake_jobs()376 fake_jobs()
377 {
378 int j;
379
380 j = 0;
381 while (j < njobs)
382 {
383 fake_job(job_array[j]);
384 explain("Updated timestamp for job `%s' to %04d-%02d-%02d",
385 job_array[j]->ident, year, month, day_of_month);
386 j++;
387 }
388 }
389
390 static void
explain_intentions()391 explain_intentions()
392 {
393 int j;
394
395 j = 0;
396 while (j < njobs)
397 {
398 if (now)
399 {
400 explain("Will run job `%s'", job_array[j]->ident);
401 }
402 else
403 {
404 explain("Will run job `%s' in %d min.",
405 job_array[j]->ident, job_array[j]->delay);
406 }
407 j++;
408 }
409 if (serialize && njobs > 0)
410 explain("Jobs will be executed sequentially");
411 }
412
413 int
main(int argc,char * argv[])414 main(int argc, char *argv[])
415 {
416 int j;
417
418 anacrontab = NULL;
419
420 if((program_name = strrchr(argv[0], '/')) == NULL)
421 program_name = argv[0];
422 else
423 ++program_name; /* move pointer to char after '/' */
424
425 parse_opts(argc, argv);
426
427 if (anacrontab == NULL)
428 anacrontab = strdup(ANACRONTAB);
429
430 in_background = 0;
431
432 if (chdir(SPOOLDIR)) die_e("Can't chdir to " SPOOLDIR);
433
434 old_umask = umask(0);
435
436 if (sigprocmask(0, NULL, &old_sigmask)) die_e("sigset error");
437
438 if (fclose(stdin)) die_e("Can't close stdin");
439 xopen(0, "/dev/null", O_RDONLY);
440
441 if (!no_daemon)
442 go_background();
443 else
444 primary_pid = getpid();
445
446 record_start_time();
447 read_tab();
448 arrange_jobs();
449
450 if (update_only)
451 {
452 fake_jobs();
453 exit(0);
454 }
455
456 explain_intentions();
457 set_signal_handling();
458 running_jobs = running_mailers = 0;
459 for(j = 0; j < njobs; ++j)
460 {
461 xsleep(time_till(job_array[j]));
462 if (serialize) wait_jobs();
463 launch_job(job_array[j]);
464 }
465 wait_children();
466 explain("Normal exit (%d jobs run)", njobs);
467 exit(0);
468 }
469