--- anacron-2.3.orig/global.h +++ anacron-2.3/global.h @@ -2,6 +2,7 @@ Anacron - run commands periodically Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il> Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org> + Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -53,9 +54,11 @@ struct job_rec1 { int period; + int named_period; int delay; char *ident; char *command; + char *mailto; int tab_line; int arg_num; @@ -75,9 +78,10 @@ extern pid_t primary_pid; extern char *program_name; extern char *anacrontab; +extern char *spooldir; extern int old_umask; extern sigset_t old_sigmask; -extern int serialize,force,update_only,now,no_daemon,quiet; +extern int serialize,force,update_only,now,no_daemon,quiet,testing_only; extern int day_now; extern int year,month,day_of_month; extern int in_background; @@ -93,6 +97,7 @@ extern int running_jobs,running_mailers; +extern int complaints; /* Function prototypes */ @@ -121,7 +126,7 @@ #endif /* not DEBUG */ /* readtab.c */ -void read_tab(); +void read_tab(int cwd); void arrange_jobs(); /* lock.c */ --- anacron-2.3.orig/lock.c +++ anacron-2.3/lock.c @@ -2,6 +2,7 @@ Anacron - run commands periodically Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il> Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org> + Copyirght (C) 2004 Pascal Hakim <pasc@redellipse.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -111,6 +112,35 @@ xclose(jr->timestamp_fd); return 0; } + + /* + * Check to see if it's a named period, in which case we need + * to figure it out. + */ + if (jr->named_period) + { + int period = 0, bypass = 0; + switch (jr->named_period) + { + case 1: + period = days_last_month (); + bypass = days_this_month (); + break; + case 2: + period = days_last_year (); + bypass = days_this_year (); + break; + default: + die ("Unknown named period for %s (%d)", jr->ident, jr->named_period); + } + printf ("Checking against %d with %d\n", day_delta, period); + if (day_delta < period && day_delta != bypass) + { + /* Job is still too young */ + xclose (jr->timestamp_fd); + return 0; + } + } } /* no! try to grab the lock */ --- anacron-2.3.orig/anacrontab.5 +++ anacron-2.3/anacrontab.5 @@ -1,4 +1,4 @@ -.TH ANACRONTAB 5 1998-02-02 "Itai Tzur" "Anacron Users' Manual" +.TH ANACRONTAB 5 2004-07-11 "Pascal Hakim" "Anacron Users' Manual" .SH NAME /etc/anacrontab \- configuration file for anacron .SH DESCRIPTION @@ -8,10 +8,13 @@ three kinds: job-description lines, environment assignments, or empty lines. .PP -Job-description lines are of the form: +Job-description lines are of one of these two forms: .PP period delay job-identifier command .PP +.PP + @period_name delay job-identify command +.PP The .I period is specified in days, the @@ -22,7 +25,12 @@ the job in Anacron messages, and as the name for the job's timestamp file. The .I command -can be any shell command. +can be any shell command. The fields can be seperated by blank spaces or tabs. +The +.I period_name +can only be set to monthly at the present time. This will ensure jobs +are only run once a month, no matter the number of days in this month, +or the previous month. .PP Environment assignment lines are of the form: .PP @@ -38,6 +46,8 @@ .PP Empty lines are either blank lines, line containing white-space only, or lines with white-space followed by a '#' followed by an arbitrary comment. +.PP +You can continue a line onto the next line by ending it with a '\'. .SH "SEE ALSO" .B anacron(8) .PP @@ -46,3 +56,5 @@ file. .SH AUTHOR Itai Tzur <itzur@actcom.co.il> +.PP +Currently maintained by Pascal Hakim <pasc@(debian.org|redellipse.net)>. --- anacron-2.3.orig/log.c +++ anacron-2.3/log.c @@ -2,6 +2,7 @@ Anacron - run commands periodically Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il> Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org> + Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -48,6 +49,9 @@ static char msg[MAX_MSG + 1]; static int log_open = 0; +/* Number of complaints that we've seen */ +int complaints = 0; + static void xopenlog() { @@ -147,6 +151,8 @@ va_start(args, fmt); log(COMPLAIN_LEVEL, fmt, args); va_end(args); + + complaints += 1; } void @@ -158,6 +164,8 @@ va_start(args, fmt); log_e(COMPLAIN_LEVEL, fmt, args); va_end(args); + + complaints += 1; } void --- anacron-2.3.orig/anacron.apm +++ anacron-2.3/anacron.apm @@ -0,0 +1,19 @@ +#! /bin/sh + +# This script makes anacron jobs start to run when the machine is +# plugged into AC power, or woken up. For a laptop, these are the +# closest parallels to turning on a desktop. + +# The /etc/init.d/anacron script now normally tries to avoid running +# anacron unless on AC power, so as to avoid running down the battery. +# (Things like the slocate updatedb cause a lot of IO.) Rather than +# trying to second-guess which events reflect having or not having +# power, we just try to run anacron every time and let it abort if +# there's no AC. You'll see a message on the cron syslog facility +# (typically /var/log/cron) if it does run. + +case "$1,$2" in +change,power|resume,*) + /etc/init.d/anacron start >/dev/null + ;; +esac --- anacron-2.3.orig/anacron.8 +++ anacron-2.3/anacron.8 @@ -1,13 +1,15 @@ -.TH ANACRON 8 2000-06-22 "Sean 'Shaleh' Perry" "Anacron Users' Manual" +.TH ANACRON 8 2004-07-11 "Pascal Hakim" "Anacron Users' Manual" .SH NAME anacron \- runs commands periodically .SH SYNOPSIS .B anacron \fR[\fB-s\fR] [\fB-f\fR] [\fB-n\fR] [\fB-d\fR] [\fB-q\fR] -[\fB-t anacrontab\fR] [\fIjob\fR] ... +[\fB-t anacrontab\fR] [\fB-S spooldir\fR] [\fIjob\fR] ... .br -.B anacron -u [\fB-t anacrontab\fR] \fR[\fIjob\fR] ... +.B anacron [\fB-S spooldir\fR] -u [\fB-t anacrontab\fR] \fR[\fIjob\fR] ... .br .B anacron \fR[\fB-V\fR|\fB-h\fR] +.br +.B anacron -T [\fB-t anacrontab\fR] .SH DESCRIPTION Anacron can be used to execute commands periodically, with a @@ -58,7 +60,10 @@ completely independent. .PP If a job generates any output on its standard output or standard error, -the output is mailed to the user running Anacron (usually root). +the output is mailed to the user running Anacron (usually root), or to +the address contained by the MAILTO environment variable in the crontab, if such +exists. If the LOGNAME environment variable is set, it will be used as From: +field. .PP Informative messages about what Anacron is doing are sent to \fBsyslogd(8)\fR under facility \fBcron\fR, priority \fBnotice\fR. Error messages are sent at @@ -98,6 +103,15 @@ .B -t anacrontab Use specified anacrontab, rather than the default .TP +.B -T +Anacrontab testing. The configuration file will be tested for validity. If +there is an error in the file, an error will be shown and anacron will +return 1. Valid anacrontabs will return 0. +.TP +.B -S spooldir +Use the specified spooldir to store timestamps in. This option is required for +users who wish to run anacron themselves. +.TP .B -V Print version information, and exit. .TP @@ -115,6 +129,8 @@ file. See .B tzset(3) for more information. + +Timestamp files are created in the spool directory for each job in anacrontab. These are never removed automatically by anacron, and should be removed by hand if a job is no longer being scheduled. .SH FILES .TP .I /etc/anacrontab @@ -144,4 +160,7 @@ The current implementation is a complete rewrite by Itai Tzur <itzur@actcom.co.il>. .PP -The code base is currently maintained by Sean 'Shaleh' Perry <shaleh@(debian.org|valinux.com)>. +The code base was maintained by Sean 'Shaleh' Perry <shaleh@(debian.org|valinux.com)>. +.PP +Since 2004, it is maintained by Pascal Hakim <pasc@(debian.org|redellipse.net)>. + --- anacron-2.3.orig/TODO +++ anacron-2.3/TODO @@ -1,6 +1,3 @@ -anacron runs jobs twice in a 31 day month -add hostname to emails sent to admin -allow user anacrontabs make manpages match #defines automagically --> sed fu full ANSI / POSIX compliance code cleaning --- anacron-2.3.orig/gregor.h +++ anacron-2.3/gregor.h @@ -2,6 +2,7 @@ Anacron - run commands periodically Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il> Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org> + Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,3 +24,7 @@ int day_num(int year, int month, int day); +int days_last_month (void); +int days_this_month (void); +int days_last_year (void); +int days_this_year (void); --- anacron-2.3.orig/gregor.c +++ anacron-2.3/gregor.c @@ -2,6 +2,7 @@ Anacron - run commands periodically Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il> Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org> + Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,6 +24,7 @@ #include <limits.h> +#include <time.h> #include "gregor.h" const static int @@ -65,7 +67,7 @@ { int dn; int i; - const int isleap; /* save three calls to leap() */ + int isleap; /* save three calls to leap() */ /* Some validity checks */ @@ -114,3 +116,66 @@ /* but not by 400 */ return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); } + +int +days_last_month (void) +/* How many days did last month have? */ +{ + struct tm time_record; + time_t current_time; + time (¤t_time); + localtime_r (¤t_time, &time_record); + + switch (time_record.tm_mon) { + case 0: return days_in_month[11]; + case 2: return days_in_month[1] + (leap (time_record.tm_year + 1900) ? 1 : 0); + default: return days_in_month[time_record.tm_mon - 1]; + } +} + +int +days_this_month (void) +/* How many days does this month have? */ +{ + struct tm time_record; + time_t current_time; + time (¤t_time); + localtime_r (¤t_time, &time_record); + + switch (time_record.tm_mon) { + case 1: return days_in_month[1] + (leap (time_record.tm_year + 1900) ? 1 : 0); + default: return days_in_month[time_record.tm_mon]; + } +} + +int +days_last_year (void) +/* How many days this last year have? */ +{ + struct tm time_record; + time_t current_time; + time (¤t_time); + localtime_r (¤t_time, &time_record); + + if (leap(time_record.tm_year - 1 + 1900)) { + return 366; + } + + return 365; +} + +int +days_this_year (void) +/* How many days does this year have */ +{ + struct tm time_record; + time_t current_time; + time (¤t_time); + localtime_r (¤t_time, &time_record); + + if (leap(time_record.tm_year + 1900)) { + return 366; + } + + return 365; +} --- anacron-2.3.orig/main.c +++ anacron-2.3/main.c @@ -2,6 +2,7 @@ Anacron - run commands periodically Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il> Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org> + Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -39,8 +40,9 @@ char *program_name; char *anacrontab; +char *spooldir; int serialize, force, update_only, now, - no_daemon, quiet; /* command-line options */ + no_daemon, quiet, testing_only; /* command-line options */ char **args; /* vector of "job" command-line arguments */ int nargs; /* number of these */ char *defarg = "*"; @@ -61,17 +63,19 @@ printf("Anacron " RELEASE "\n" "Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>\n" "Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>\n" + "Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net>\n" "\n" - "Mail comments, suggestions and bug reports to <shaleh@debian.org>." + "Mail comments, suggestions and bug reports to <pasc@redellipse.net>." "\n\n"); } static void print_usage() { - printf("Usage: anacron [-s] [-f] [-n] [-d] [-q] [-t anacrontab] [job] ...\n" - " anacron -u [job] ...\n" + printf("Usage: anacron [-s] [-f] [-n] [-d] [-q] [-t anacrontab] [-S spooldir] [job] ...\n" + " anacron [-S spooldir] -u [job] ...\n" " anacron [-V|-h]\n" + " anacron -T [-t anacrontab]\n" "\n" " -s Serialize execution of jobs\n" " -f Force execution of jobs, even before their time\n" @@ -82,6 +86,8 @@ " -t Use this anacrontab\n" " -V Print version information\n" " -h Print this message\n" + " -T Test an anacrontab\n" + " -S Select a different spool directory\n" "\n" "See the manpage for more details.\n" "\n"); @@ -95,7 +101,7 @@ quiet = no_daemon = serialize = force = update_only = now = 0; opterr = 0; - while ((opt = getopt(argc, argv, "sfundqt:Vh")) != EOF) + while ((opt = getopt(argc, argv, "sfundqt:TS:Vh")) != EOF) { switch (opt) { @@ -120,6 +126,12 @@ case 't': anacrontab = strdup(optarg); break; + case 'T': + testing_only = 1; + break; + case 'S': + spooldir = strdup(optarg); + break; case 'V': print_version(); exit(0); @@ -351,7 +363,7 @@ day_of_month = tm_now->tm_mday; day_now = day_num(year, month, day_of_month); if (day_now == -1) die("Invalid date (this is really embarrassing)"); - if (!update_only) + if (!update_only && !testing_only) explain("Anacron " RELEASE " started on %04d-%02d-%02d", year, month, day_of_month); } @@ -414,7 +426,10 @@ { int j; + int cwd; + anacrontab = NULL; + spooldir = NULL; if((program_name = strrchr(argv[0], '/')) == NULL) program_name = argv[0]; @@ -426,9 +441,16 @@ if (anacrontab == NULL) anacrontab = strdup(ANACRONTAB); + if (spooldir == NULL) + spooldir = strdup(SPOOLDIR); + + if ((cwd = open ("./", O_RDONLY)) == -1) { + die_e ("Can't save current directory"); + } + in_background = 0; - if (chdir(SPOOLDIR)) die_e("Can't chdir to " SPOOLDIR); + if (chdir(spooldir)) die_e("Can't chdir to %s", spooldir ); old_umask = umask(0); @@ -437,15 +459,22 @@ if (fclose(stdin)) die_e("Can't close stdin"); xopen(0, "/dev/null", O_RDONLY); - if (!no_daemon) + if (!no_daemon && !testing_only) go_background(); else primary_pid = getpid(); record_start_time(); - read_tab(); + read_tab(cwd); arrange_jobs(); + if (testing_only) + { + if (complaints) exit (1); + + exit (0); + } + if (update_only) { fake_jobs(); @@ -462,6 +491,6 @@ launch_job(job_array[j]); } wait_children(); - explain("Normal exit (%d jobs run)", njobs); + explain("Normal exit (%d job%s run)", njobs, (njobs == 1 ? "" : "s")); exit(0); } --- anacron-2.3.orig/ChangeLog +++ anacron-2.3/ChangeLog @@ -1,3 +1,8 @@ + Changes in Anacron 2.3.1 + ------------------------ +* documentation no longer suggests adding local directories to the PATH + + Changes in Anacron 2.3 ---------------------- * anacron can now read an arbitrary anacrontab file, use the -t option --- anacron-2.3.orig/readtab.c +++ anacron-2.3/readtab.c @@ -2,6 +2,7 @@ Anacron - run commands periodically Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il> Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org> + Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -83,11 +84,23 @@ Return NULL if no more lines. */ { - int c; + int c, prev=0; if (feof(tab)) return NULL; - while ((c = getc(tab)) != EOF && c != '\n') - obstack_1grow(&input_o, c); + while (1) + { + c = getc(tab); + if ((c == '\n' && prev != '\\') || c == EOF) + { + if (0 != prev) obstack_1grow(&input_o, prev); + break; + } + + if ('\\' != prev && 0 != prev && '\n' != prev) obstack_1grow(&input_o, prev); + else if ('\n' == prev) obstack_1grow(&input_o, ' '); + + prev = c; + } if (ferror(tab)) die_e("Error reading %s", anacrontab); obstack_1grow(&input_o, '\0'); return obstack_finish(&input_o); @@ -153,6 +166,7 @@ } jr = obstack_alloc(&tab_o, sizeof(job_rec)); jr->period = period; + jr->named_period = 0; jr->delay = delay; jr->tab_line = line_num; jr->ident = obstack_alloc(&tab_o, ident_len + 1); @@ -171,6 +185,54 @@ } static void +register_period_job(const char *periods, const char *delays, + const char *ident, char *command) +/* Store a job definition with a named period */ +{ + int delay; + job_rec *jr; + int period_len, ident_len, command_len; + + period_len = strlen(periods); + ident_len = strlen(ident); + command_len = strlen(command); + jobs_read++; + delay = conv2int(delays); + if (delay < 0) + { + complain("%s: number out of range on line %d, skipping", + anacrontab, line_num); + return; + } + + jr = obstack_alloc(&tab_o, sizeof(job_rec)); + if (!strncmp ("@monthly", periods, 7)) { + jr->named_period = 1; + } else if (!strncmp("@yearly", periods, 7)) { + jr->named_period = 2; + } else { + complain("%s: Unknown named period on line %d, skipping", + anacrontab, line_num); + } + jr->period = 0; + jr->delay = delay; + jr->tab_line = line_num; + jr->ident = obstack_alloc(&tab_o, ident_len + 1); + strcpy(jr->ident, ident); + jr->arg_num = job_arg_num(ident); + jr->command = obstack_alloc(&tab_o, command_len + 1); + strcpy(jr->command, command); + jr->job_pid = jr->mailer_pid = 0; + if (last_job_rec != NULL) last_job_rec->next = jr; + else first_job_rec = jr; + last_job_rec = jr; + jr->prev_env_rec = last_env_rec; + jr->next = NULL; + Debug(("Read job - period %d, delay=%d, ident%s, command=%s", + jr->named_period, jr->delay, jr->ident, jr->command)); +} + +static void parse_tab_line(char *line) { int r; @@ -210,6 +272,18 @@ register_job(periods, delays, ident, command); return; } + + /* A period job? */ + r = match_rx("^[ \t]*(@[^ \t]+)[ \t]+([[:digit:]]+)[ \t]+" + "([^ \t/]+)[ \t]+([^ \t].*)$", + line, 4, &periods, &delays, &ident, &command); + if (r == -1) goto reg_err; + if (r) + { + register_period_job(periods, delays, ident, command); + return; + } + complain("Invalid syntax in %s on line %d - skipping this line", anacrontab, line_num); return; @@ -219,7 +293,7 @@ } void -read_tab() +read_tab(int cwd) /* Read the anacrontab file into memory */ { char *tab_line; @@ -229,7 +303,10 @@ jobs_read = 0; line_num = 0; /* Open the anacrontab file */ + fchdir (cwd); tab = fopen(anacrontab, "r"); + if (chdir(spooldir)) die_e("Can't chdir to %s", SPOOLDIR); + if (tab == NULL) die_e("Error opening %s", anacrontab); /* Initialize the obstacks */ obstack_init(&input_o); @@ -271,7 +348,7 @@ njobs = 0; while (j != NULL) { - if (j->arg_num != -1 && (update_only || consider_job(j))) + if (j->arg_num != -1 && (update_only || testing_only || consider_job(j))) { njobs++; obstack_grow(&tab_o, &j, sizeof(j)); --- anacron-2.3.orig/runjob.c +++ anacron-2.3/runjob.c @@ -109,7 +109,6 @@ run_job(const job_rec *jr) /* This is called to start the job, after the fork */ { - setup_env(jr); /* setup stdout and stderr */ xclose(1); xclose(2); @@ -153,6 +152,15 @@ launch_mailer(job_rec *jr) { pid_t pid; + struct stat buf; + int r; + + /* Check that we have a way of sending mail. */ + if(stat(SENDMAIL, &buf)) + { + complain("Can't find sendmail at %s, not mailing output", SENDMAIL); + return; + } pid = xfork(); if (pid == 0) @@ -173,7 +181,7 @@ * options, which don't seem to be appropriate here. * Hopefully, this will keep all the MTAs happy. */ execl(SENDMAIL, SENDMAIL, "-FAnacron", "-odi", - username(), (char *)NULL); + jr->mailto, (char *)NULL); die_e("Can't exec " SENDMAIL); } /* parent */ @@ -207,19 +215,44 @@ { pid_t pid; int fd; + char hostname[512]; + char *mailto; + + /* get hostname */ + if (gethostname(hostname, 512)) { + strcpy (hostname,"unknown machine"); + } + + setup_env(jr); + + /* Get the destination email address if set, or current user otherwise */ + mailto = getenv("MAILTO"); + + if (mailto) + jr->mailto = mailto; + else + jr->mailto = username (); /* create temporary file for stdout and stderr of the job */ fd = jr->output_fd = temp_file(); /* write mail header */ xwrite(fd, "From: "); + xwrite(fd, "Anacron <"); xwrite(fd, username()); - xwrite(fd, " (Anacron)\n"); + xwrite(fd, ">\n"); xwrite(fd, "To: "); - xwrite(fd, username()); + if (mailto) { + xwrite(fd, mailto); + } else { + xwrite(fd, username()); + } xwrite(fd, "\n"); xwrite(fd, "Subject: Anacron job '"); xwrite(fd, jr->ident); - xwrite(fd, "'\n\n"); + xwrite(fd, "' on "); + xwrite(fd, hostname); + xwrite(fd, "\n\n"); + jr->mail_header_size = file_size(fd); pid = xfork();