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