Import Upstream version 1.7.2
This commit is contained in:
39
anacron/ChangeLog.anacron
Normal file
39
anacron/ChangeLog.anacron
Normal file
@@ -0,0 +1,39 @@
|
||||
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
|
||||
|
||||
|
||||
Changes in Anacron 2.1/2.2
|
||||
--------------------------
|
||||
* Sean 'Shaleh' Perry <shaleh@(debian.org|valinux.com)> is now maintainer
|
||||
* if timestamp is from the future, re-run job
|
||||
* ansi cleanup / code cleaning
|
||||
|
||||
|
||||
Changes in Anacron 2.0.1
|
||||
------------------------
|
||||
* Minor cosmetic changes to log messages.
|
||||
* Jobs are now started with "/" as their working directory. This is
|
||||
more compatible with older Anacron versions, avoids annoying errors on
|
||||
some systems, and generally seems to make more sense.
|
||||
|
||||
|
||||
Summary of major changes in Anacron 2.0
|
||||
---------------------------------------
|
||||
* Complete rewrite in C. Should be backwards compatible with existing
|
||||
Anacron installations.
|
||||
* First release as a "generic" Linux package (was a Debian package).
|
||||
* No longer needs special lock-files. Locking is done on the timestamp
|
||||
files.
|
||||
* Sends log messages to syslogd. There's no log file now.
|
||||
* Output of jobs, if any, is mailed to the user.
|
||||
* Added command line options: -s -f -n -d -q -u -V -h. See the manpage.
|
||||
* Specific jobs can now be selected on the command line.
|
||||
* Added SIGUSR1 handling, to cleanly stop execution.
|
||||
* Jobs will now be started with their current directory set to the home
|
||||
of the user running Anacron (usually root).
|
||||
42
anacron/Makemodule.am
Normal file
42
anacron/Makemodule.am
Normal file
@@ -0,0 +1,42 @@
|
||||
# Makefile.am - two binaries crond and crontab
|
||||
if ANACRON
|
||||
sbin_PROGRAMS += anacron/anacron
|
||||
anacron_anacron_SOURCES = \
|
||||
anacron-paths.h \
|
||||
anacron/global.h \
|
||||
anacron/gregor.c \
|
||||
anacron/gregor.h \
|
||||
anacron/lock.c \
|
||||
anacron/log.c \
|
||||
anacron/main.c \
|
||||
anacron/matchrx.c \
|
||||
anacron/matchrx.h \
|
||||
anacron/readtab.c \
|
||||
anacron/runjob.c \
|
||||
cronie_common.c
|
||||
common_nodist += anacron-paths.h
|
||||
nodist_anacron_anacron_SOURCES = $(common_nodist)
|
||||
BUILT_SOURCES += $(common_nodist)
|
||||
|
||||
if NEED_OBSTACK
|
||||
anacron_anacron_SOURCES += obstack/obstack.c
|
||||
endif
|
||||
|
||||
anacron_anacron_LDADD = $(LIBSELINUX) $(LIBPAM) $(LIBAUDIT)
|
||||
|
||||
# This header contains all the paths.
|
||||
# If they are configurable, they are declared in configure script.
|
||||
# Depends on this Makefile, because it uses make variables.
|
||||
CLEANFILES += anacron-paths.h
|
||||
anacron-paths.h: Makefile
|
||||
@echo 'creating $@'
|
||||
@sed >$@ 's/ *\\$$//' <<\END #\
|
||||
/* This file has been automatically generated. Do not edit. */ \
|
||||
\
|
||||
#ifndef _ANACRON_PATHS_H_ \
|
||||
#define _ANACRON_PATHS_H_ \
|
||||
#define ANACRON_SPOOL_DIR "$(ANACRON_SPOOL_DIR)" \
|
||||
#define ANACRONTAB "$(ANACRONTAB)" \
|
||||
#endif /* _ANACRON_PATHS_H_ */ \
|
||||
END
|
||||
endif
|
||||
164
anacron/global.h
Normal file
164
anacron/global.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
The GNU General Public License can also be found in the file
|
||||
`COPYING' that comes with the Anacron source distribution.
|
||||
*/
|
||||
|
||||
#ifndef _ANACRON_GLOBAL_H
|
||||
#define _ANACRON_GLOBAL_H
|
||||
|
||||
/* Syslog facility and priorities messages will be logged to (see syslog(3)).
|
||||
* If you change these, please update the man page. */
|
||||
#define SYSLOG_FACILITY LOG_CRON
|
||||
#define EXPLAIN_LEVEL LOG_NOTICE /* informational messages */
|
||||
#define COMPLAIN_LEVEL LOG_ERR /* error messages */
|
||||
#define DEBUG_LEVEL LOG_DEBUG /* only used when DEBUG is defined */
|
||||
|
||||
/* Mail interface. (All MTAs should supply this command) */
|
||||
#define SENDMAIL "/usr/sbin/sendmail"
|
||||
|
||||
/* End of user-configurable section */
|
||||
|
||||
|
||||
#define FAILURE_EXIT 1
|
||||
#define MAX_MSG 150
|
||||
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include "anacron-paths.h"
|
||||
#include "cronie_common.h"
|
||||
|
||||
/* Some declarations */
|
||||
|
||||
struct env_rec1 {
|
||||
char *assign;
|
||||
|
||||
struct env_rec1 *next;
|
||||
};
|
||||
typedef struct env_rec1 env_rec;
|
||||
|
||||
struct job_rec1 {
|
||||
int period;
|
||||
int named_period;
|
||||
int delay;
|
||||
char *ident;
|
||||
char *command;
|
||||
char *mailto;
|
||||
int no_mail_output;
|
||||
|
||||
int tab_line;
|
||||
int arg_num;
|
||||
int timestamp_fd;
|
||||
int input_fd;
|
||||
int output_fd;
|
||||
off_t mail_header_size;
|
||||
pid_t job_pid;
|
||||
pid_t mailer_pid;
|
||||
int drop_job;
|
||||
|
||||
struct job_rec1 *next;
|
||||
env_rec *prev_env_rec;
|
||||
};
|
||||
typedef struct job_rec1 job_rec;
|
||||
|
||||
/* Global variables */
|
||||
|
||||
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,testing_only;
|
||||
extern int day_now;
|
||||
extern int year,month,day_of_month;
|
||||
extern int in_background;
|
||||
|
||||
extern job_rec *first_job_rec;
|
||||
extern env_rec *first_env_rec;
|
||||
|
||||
extern char **job_args;
|
||||
extern int job_nargs;
|
||||
|
||||
extern int njobs;
|
||||
extern job_rec **job_array;
|
||||
|
||||
extern int running_jobs,running_mailers;
|
||||
|
||||
extern int complaints;
|
||||
|
||||
extern time_t start_sec;
|
||||
|
||||
/* time ranges for START_HOURS_RANGE */
|
||||
extern int range_start;
|
||||
extern int range_stop;
|
||||
|
||||
/* preferred hour for jobs */
|
||||
extern int preferred_hour;
|
||||
|
||||
/* Function prototypes */
|
||||
|
||||
/* main.c */
|
||||
int xopen(int fd, const char *file_name, int flags);
|
||||
void xclose(int fd);
|
||||
pid_t xfork(void);
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PRINTF_FORMAT(n, m) \
|
||||
__attribute__ ((format (printf, n, m)))
|
||||
#else
|
||||
#define PRINTF_FORMAT(n, m)
|
||||
#endif
|
||||
|
||||
/* log.c */
|
||||
void explain(const char *fmt, ...)PRINTF_FORMAT(1,2);
|
||||
void explain_e(const char *fmt, ...)PRINTF_FORMAT(1,2);
|
||||
void complain(const char *fmt, ...)PRINTF_FORMAT(1,2);
|
||||
void complain_e(const char *fmt, ...)PRINTF_FORMAT(1,2);
|
||||
void die(const char *fmt, ...)PRINTF_FORMAT(1,2) ATTRIBUTE_NORETURN;
|
||||
void die_e(const char *fmt, ...)PRINTF_FORMAT(1,2) ATTRIBUTE_NORETURN;
|
||||
void xdebug(const char *fmt, ...)PRINTF_FORMAT(1,2);
|
||||
void xdebug_e(const char *fmt, ...)PRINTF_FORMAT(1,2);
|
||||
void xcloselog(void);
|
||||
|
||||
#ifdef DEBUG
|
||||
#define Debug(args) xdebug args
|
||||
#define Debug_e(args) xdebug_e args
|
||||
#else /* not DEBUG */
|
||||
#define Debug(args) (void)(0)
|
||||
#define Debug_e(args) (void)(0)
|
||||
#endif /* not DEBUG */
|
||||
|
||||
/* readtab.c */
|
||||
void read_tab(int cwd);
|
||||
void arrange_jobs(void);
|
||||
|
||||
/* lock.c */
|
||||
int consider_job(job_rec *jr);
|
||||
void unlock(job_rec *jr);
|
||||
void update_timestamp(job_rec *jr);
|
||||
void fake_job(job_rec *jr);
|
||||
|
||||
/* runjob.c */
|
||||
void tend_children();
|
||||
void launch_job(job_rec *jr);
|
||||
|
||||
#endif
|
||||
181
anacron/gregor.c
Normal file
181
anacron/gregor.c
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
The GNU General Public License can also be found in the file
|
||||
`COPYING' that comes with the Anacron source distribution.
|
||||
*/
|
||||
|
||||
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
#include "gregor.h"
|
||||
|
||||
static const int
|
||||
days_in_month[] = {
|
||||
31, /* Jan */
|
||||
28, /* Feb (non-leap) */
|
||||
31, /* Mar */
|
||||
30, /* Apr */
|
||||
31, /* May */
|
||||
30, /* Jun */
|
||||
31, /* Jul */
|
||||
31, /* Aug */
|
||||
30, /* Sep */
|
||||
31, /* Oct */
|
||||
30, /* Nov */
|
||||
31 /* Dec */
|
||||
};
|
||||
|
||||
static int leap(int year);
|
||||
|
||||
int
|
||||
day_num(int year, int month, int day)
|
||||
/* Return the "day number" of the date year-month-day according to the
|
||||
* "proleptic Gregorian calendar".
|
||||
* If the given date is invalid, return -1.
|
||||
*
|
||||
* Here, "day number" is defined as the number of days since December 31,
|
||||
* 1 B.C. (Gregorian). (January 1, 1 A.D. is day number 1 etc...)
|
||||
*
|
||||
* The Gregorian calendar was instituted by Pope Gregory XIII in 1582,
|
||||
* and has gradually spread to become the international standard calendar.
|
||||
* The proleptic Gregorian calendar is formed by projecting the date system
|
||||
* of the Gregorian calendar to dates before its adoption.
|
||||
*
|
||||
* For more details, see:
|
||||
* http://astro.nmsu.edu/~lhuber/leaphist.html
|
||||
* http://www.magnet.ch/serendipity/hermetic/cal_stud/cal_art.htm
|
||||
* and your local library.
|
||||
*/
|
||||
{
|
||||
int dn;
|
||||
int i;
|
||||
int isleap; /* save three calls to leap() */
|
||||
|
||||
/* Some validity checks */
|
||||
|
||||
/* we don't deal with B.C. years here */
|
||||
if (year < 1) return - 1;
|
||||
/* conservative overflow estimate */
|
||||
if (year > (INT_MAX / 366)) return - 1;
|
||||
if (month > 12 || month < 1) return - 1;
|
||||
if (day < 1) return - 1;
|
||||
|
||||
isleap = leap(year);
|
||||
|
||||
if (month != 2) {
|
||||
if(day > days_in_month[month - 1]) return - 1;
|
||||
}
|
||||
else if ((isleap && day > 29) || (!isleap && day > 28))
|
||||
return - 1;
|
||||
|
||||
/* First calculate the day number of December 31 last year */
|
||||
|
||||
/* save us from doing (year - 1) over and over */
|
||||
i = year - 1;
|
||||
/* 365 days in a "regular" year + number of leap days */
|
||||
dn = (i * 365) + ((i / 4) - (i / 100) + (i / 400));
|
||||
|
||||
/* Now, day number of the last day of the previous month */
|
||||
|
||||
for (i = month - 1; i > 0; --i)
|
||||
dn += days_in_month[i - 1];
|
||||
/* Add 29 February ? */
|
||||
if (month > 2 && isleap) ++dn;
|
||||
|
||||
/* How many days into month are we */
|
||||
|
||||
dn += day;
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
static int
|
||||
leap(int year)
|
||||
/* Is this a leap year ? */
|
||||
{
|
||||
/* every year exactly divisible by 4 is "leap" */
|
||||
/* unless it is exactly divisible by 100 */
|
||||
/* 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;
|
||||
}
|
||||
30
anacron/gregor.h
Normal file
30
anacron/gregor.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
The GNU General Public License can also be found in the file
|
||||
`COPYING' that comes with the Anacron source distribution.
|
||||
*/
|
||||
|
||||
|
||||
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);
|
||||
217
anacron/lock.c
Normal file
217
anacron/lock.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
The GNU General Public License can also be found in the file
|
||||
`COPYING' that comes with the Anacron source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/* Lock and timestamp management
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include "global.h"
|
||||
#include "gregor.h"
|
||||
|
||||
static void
|
||||
open_tsfile(job_rec *jr)
|
||||
/* Open the timestamp file for job jr */
|
||||
{
|
||||
jr->timestamp_fd = open(jr->ident, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
|
||||
if (jr->timestamp_fd == -1)
|
||||
die_e("Can't open timestamp file for job %s", jr->ident);
|
||||
fcntl(jr->timestamp_fd, F_SETFD, 1); /* set close-on-exec flag */
|
||||
/* We want to own this file, and set its mode to 0600. This is necessary
|
||||
* in order to prevent other users from putting locks on it. */
|
||||
if (fchown(jr->timestamp_fd, getuid(), getgid()))
|
||||
die_e("Can't chown timestamp file %s", jr->ident);
|
||||
if (fchmod(jr->timestamp_fd, S_IRUSR | S_IWUSR))
|
||||
die_e("Can't chmod timestamp file %s", jr->ident);
|
||||
}
|
||||
|
||||
static int
|
||||
lock_file(int fd)
|
||||
/* Attempt to put an exclusive fcntl() lock on file "fd"
|
||||
* Return 1 on success, 0 on failure.
|
||||
*/
|
||||
{
|
||||
int r;
|
||||
struct flock sfl;
|
||||
|
||||
sfl.l_type = F_WRLCK;
|
||||
sfl.l_start = 0;
|
||||
sfl.l_whence = SEEK_SET;
|
||||
sfl.l_len = 0; /* we lock all the file */
|
||||
errno = 0;
|
||||
r = fcntl(fd, F_SETLK, &sfl);
|
||||
if (r != -1) return 1;
|
||||
if (errno != EACCES && errno != EAGAIN)
|
||||
die_e("fcntl() error");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
consider_job(job_rec *jr)
|
||||
/* Check the timestamp of the job. If "its time has come", lock the job
|
||||
* and return 1, if it's too early, or we can't get the lock, return 0.
|
||||
*/
|
||||
{
|
||||
char timestamp[9];
|
||||
int ts_year, ts_month, ts_day, dn;
|
||||
ssize_t b;
|
||||
|
||||
open_tsfile(jr);
|
||||
|
||||
/* read timestamp */
|
||||
b = read(jr->timestamp_fd, timestamp, 8);
|
||||
if (b == -1) die_e("Error reading timestamp file %s", jr->ident);
|
||||
timestamp[8] = 0;
|
||||
|
||||
/* is it too early? */
|
||||
if (!force && b == 8)
|
||||
{
|
||||
int day_delta;
|
||||
time_t jobtime;
|
||||
struct tm *t;
|
||||
|
||||
if (sscanf(timestamp, "%4d%2d%2d", &ts_year, &ts_month, &ts_day) == 3)
|
||||
dn = day_num(ts_year, ts_month, ts_day);
|
||||
else
|
||||
dn = 0;
|
||||
|
||||
day_delta = day_now - dn;
|
||||
|
||||
/*
|
||||
* if day_delta is negative, we assume there was a clock skew
|
||||
* and re-run any affected jobs
|
||||
* otherwise we check if the job's time has come
|
||||
*/
|
||||
if (day_delta >= 0 && day_delta < jr->period)
|
||||
{
|
||||
/* yes, skip job */
|
||||
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: /* monthly */
|
||||
period = days_last_month ();
|
||||
bypass = days_this_month ();
|
||||
break;
|
||||
case 2: /* yearly, annually */
|
||||
period = days_last_year ();
|
||||
bypass = days_this_year ();
|
||||
break;
|
||||
case 3: /* daily */
|
||||
period = 1;
|
||||
bypass = 1;
|
||||
break;
|
||||
case 4: /* weekly */
|
||||
period = 7;
|
||||
bypass = 7;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
jobtime = start_sec + jr->delay * 60;
|
||||
|
||||
t = localtime(&jobtime);
|
||||
if (!now && preferred_hour != -1 && t->tm_hour != preferred_hour) {
|
||||
Debug(("The job's %s preferred hour %d was missed, skipping the job.", jr->ident, preferred_hour));
|
||||
xclose (jr->timestamp_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!now && range_start != -1 && range_stop != -1 &&
|
||||
(t->tm_hour < range_start || t->tm_hour >= range_stop))
|
||||
{
|
||||
Debug(("The job `%s' falls out of the %02d:00-%02d:00 hours range, skipping.",
|
||||
jr->ident, range_start, range_stop));
|
||||
xclose (jr->timestamp_fd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* no! try to grab the lock */
|
||||
if (lock_file(jr->timestamp_fd)) return 1; /* success */
|
||||
|
||||
/* didn't get lock */
|
||||
xclose(jr->timestamp_fd);
|
||||
explain("Job `%s' locked by another anacron - skipping", jr->ident);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
unlock(job_rec *jr)
|
||||
{
|
||||
xclose(jr->timestamp_fd);
|
||||
}
|
||||
|
||||
void
|
||||
update_timestamp(job_rec *jr)
|
||||
/* We write the date "now". "Now" can be either the time when anacron
|
||||
* started, or the time when the job finished.
|
||||
* I'm not quite sure which is more "right", but I've decided on the first
|
||||
* option.
|
||||
* Note that this is not the way it was with anacron 1.0.3 to 1.0.7.
|
||||
*/
|
||||
{
|
||||
char stamp[10];
|
||||
|
||||
snprintf(stamp, 10, "%04d%02d%02d\n", year, month, day_of_month);
|
||||
if (lseek(jr->timestamp_fd, 0, SEEK_SET))
|
||||
die_e("Can't lseek timestamp file for job %s", jr->ident);
|
||||
if (write(jr->timestamp_fd, stamp, 9) != 9)
|
||||
die_e("Can't write timestamp file for job %s", jr->ident);
|
||||
if (ftruncate(jr->timestamp_fd, 9))
|
||||
die_e("ftruncate error");
|
||||
}
|
||||
|
||||
void
|
||||
fake_job(job_rec *jr)
|
||||
/* We don't bother with any locking here. There's no point. */
|
||||
{
|
||||
open_tsfile(jr);
|
||||
update_timestamp(jr);
|
||||
xclose(jr->timestamp_fd);
|
||||
}
|
||||
230
anacron/log.c
Normal file
230
anacron/log.c
Normal file
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
The GNU General Public License can also be found in the file
|
||||
`COPYING' that comes with the Anacron source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/* Error logging
|
||||
*
|
||||
* We have two levels of logging (plus debugging if DEBUG is defined):
|
||||
* "explain" level for informational messages, and "complain" level for errors.
|
||||
*
|
||||
* We log everything to syslog, see the top of global.h for relevant
|
||||
* definitions.
|
||||
*
|
||||
* Stderr gets "complain" messages when we're in the foreground,
|
||||
* and "explain" messages when we're in the foreground, and not "quiet".
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "global.h"
|
||||
|
||||
static char truncated[] = " (truncated)";
|
||||
static char msg[MAX_MSG + 1];
|
||||
static int log_open = 0;
|
||||
|
||||
/* Number of complaints that we've seen */
|
||||
int complaints = 0;
|
||||
|
||||
static void
|
||||
xopenlog(void)
|
||||
{
|
||||
if (!log_open)
|
||||
{
|
||||
openlog(program_name, LOG_PID, SYSLOG_FACILITY);
|
||||
log_open = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
xcloselog(void)
|
||||
{
|
||||
if (log_open) closelog();
|
||||
log_open = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
make_msg(const char *fmt, va_list args)
|
||||
/* Construct the message string from its parts */
|
||||
{
|
||||
int len;
|
||||
|
||||
/* There's some confusion in the documentation about what vsnprintf
|
||||
* returns when the buffer overflows. Hmmm... */
|
||||
len = vsnprintf(msg, sizeof(msg), fmt, args);
|
||||
if (len < 0) {
|
||||
strncpy(msg, "(vsnprintf failed)", sizeof(msg));
|
||||
msg[sizeof(msg) - 1] = '\0';
|
||||
return;
|
||||
}
|
||||
if ((size_t) len >= sizeof(msg) - 1)
|
||||
strcpy(msg + sizeof(msg) - sizeof(truncated), truncated);
|
||||
}
|
||||
|
||||
static void
|
||||
slog(int priority, const char *fmt, va_list args)
|
||||
/* Log a message, described by "fmt" and "args", with the specified
|
||||
* "priority". */
|
||||
{
|
||||
make_msg(fmt, args);
|
||||
xopenlog();
|
||||
syslog(priority, "%s", msg);
|
||||
if (!in_background)
|
||||
{
|
||||
if (priority == EXPLAIN_LEVEL && !quiet)
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
else if (priority == COMPLAIN_LEVEL)
|
||||
fprintf(stderr, "%s: %s\n", program_name, msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
log_e(int priority, const char *fmt, va_list args)
|
||||
/* Same as slog(), but also appends an error description corresponding
|
||||
* to "errno". */
|
||||
{
|
||||
int saved_errno;
|
||||
|
||||
saved_errno = errno;
|
||||
make_msg(fmt, args);
|
||||
xopenlog();
|
||||
syslog(priority, "%s: %s", msg, strerror(saved_errno));
|
||||
if (!in_background)
|
||||
{
|
||||
if (priority == EXPLAIN_LEVEL && !quiet)
|
||||
fprintf(stderr, "%s: %s\n", msg, strerror(saved_errno));
|
||||
else if (priority == COMPLAIN_LEVEL)
|
||||
fprintf(stderr, "%s: %s: %s\n",
|
||||
program_name, msg, strerror(saved_errno));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
explain(const char *fmt, ...)
|
||||
/* Log an "explain" level message */
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
slog(EXPLAIN_LEVEL, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
explain_e(const char *fmt, ...)
|
||||
/* Log an "explain" level message, with an error description */
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
log_e(EXPLAIN_LEVEL, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
complain(const char *fmt, ...)
|
||||
/* Log a "complain" level message */
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
slog(COMPLAIN_LEVEL, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
complaints += 1;
|
||||
}
|
||||
|
||||
void
|
||||
complain_e(const char *fmt, ...)
|
||||
/* Log a "complain" level message, with an error description */
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
log_e(COMPLAIN_LEVEL, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
complaints += 1;
|
||||
}
|
||||
|
||||
void
|
||||
die(const char *fmt, ...)
|
||||
/* Log a "complain" level message, and exit */
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
slog(COMPLAIN_LEVEL, fmt, args);
|
||||
va_end(args);
|
||||
if (getpid() == primary_pid) complain("Aborted");
|
||||
|
||||
exit(FAILURE_EXIT);
|
||||
}
|
||||
|
||||
void
|
||||
die_e(const char *fmt, ...)
|
||||
/* Log a "complain" level message, with an error description, and exit */
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
log_e(COMPLAIN_LEVEL, fmt, args);
|
||||
va_end(args);
|
||||
if (getpid() == primary_pid) complain("Aborted");
|
||||
|
||||
exit(FAILURE_EXIT);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
/* These are called through the Debug() and Debug_e() macros, defined
|
||||
* in global.h */
|
||||
|
||||
void
|
||||
xdebug(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
slog(DEBUG_LEVEL, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
xdebug_e(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
log_e(DEBUG_LEVEL, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#endif /* DEBUG */
|
||||
517
anacron/main.c
Normal file
517
anacron/main.c
Normal file
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
The GNU General Public License can also be found in the file
|
||||
`COPYING' that comes with the Anacron source distribution.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <locale.h>
|
||||
#include "global.h"
|
||||
#include "gregor.h"
|
||||
#include "cronie_common.h"
|
||||
|
||||
pid_t primary_pid;
|
||||
int day_now;
|
||||
int year, month, day_of_month; /* date anacron started */
|
||||
|
||||
char *program_name;
|
||||
char *anacrontab = NULL;
|
||||
char *spooldir = NULL;
|
||||
int serialize, force, update_only, now,
|
||||
no_daemon, quiet, testing_only; /* command-line options */
|
||||
char **job_args; /* vector of "job" command-line arguments */
|
||||
int job_nargs; /* number of these */
|
||||
char *defarg = "*";
|
||||
int in_background; /* are we in the background? */
|
||||
sigset_t old_sigmask; /* signal mask when started */
|
||||
|
||||
job_rec *first_job_rec;
|
||||
env_rec *first_env_rec;
|
||||
|
||||
time_t start_sec; /* time anacron started */
|
||||
static volatile int got_sigalrm, got_sigchld, got_sigusr1;
|
||||
int running_jobs, running_mailers; /* , number of */
|
||||
int range_start = -1;
|
||||
int range_stop = -1;
|
||||
int preferred_hour = -1;
|
||||
|
||||
static void
|
||||
print_version(void)
|
||||
{
|
||||
printf("Anacron from project %s\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 <pasc@redellipse.net>."
|
||||
"\n\n", PACKAGE_STRING);
|
||||
}
|
||||
|
||||
static void
|
||||
print_usage(void)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf(" %s [options] [job] ...\n", program_name);
|
||||
printf(" %s -T [-t anacrontab-file]\n", program_name);
|
||||
printf("\nOptions:\n");
|
||||
printf(" -s Serialize execution of jobs\n");
|
||||
printf(" -f Force execution of jobs, even before their time\n");
|
||||
printf(" -n Run jobs with no delay, implies -s\n");
|
||||
printf(" -d Don't fork to the background\n");
|
||||
printf(" -q Suppress stderr messages, only applicable with -d\n");
|
||||
printf(" -u Update the timestamps without actually running anything\n");
|
||||
printf(" -V Print version information\n");
|
||||
printf(" -h Print this message\n");
|
||||
printf(" -t <file> Use alternative anacrontab\n");
|
||||
printf(" -T Test an anacrontab\n");
|
||||
printf(" -S <dir> Select a different spool directory\n");
|
||||
printf("\nSee the anacron(8) manpage for more details.\n");
|
||||
}
|
||||
|
||||
static void
|
||||
parse_opts(int argc, char *argv[])
|
||||
/* Parse command-line options */
|
||||
{
|
||||
int opt;
|
||||
|
||||
quiet = no_daemon = serialize = force = update_only = now = 0;
|
||||
opterr = 0;
|
||||
while ((opt = getopt(argc, argv, "sfundqt:TS:Vh")) != EOF)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 's':
|
||||
serialize = 1;
|
||||
break;
|
||||
case 'f':
|
||||
force = 1;
|
||||
break;
|
||||
case 'u':
|
||||
update_only = 1;
|
||||
break;
|
||||
case 'n':
|
||||
now = serialize = 1;
|
||||
break;
|
||||
case 'd':
|
||||
no_daemon = 1;
|
||||
break;
|
||||
case 'q':
|
||||
quiet = 1;
|
||||
break;
|
||||
case 't':
|
||||
free(anacrontab);
|
||||
anacrontab = strdup(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
testing_only = 1;
|
||||
break;
|
||||
case 'S':
|
||||
free(spooldir);
|
||||
spooldir = strdup(optarg);
|
||||
break;
|
||||
case 'V':
|
||||
print_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'h':
|
||||
print_usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
case '?':
|
||||
fprintf(stderr, "%s: invalid option: %c\n",
|
||||
program_name, optopt);
|
||||
fprintf(stderr, "type: `%s -h' for more information\n",
|
||||
program_name);
|
||||
exit(FAILURE_EXIT);
|
||||
}
|
||||
}
|
||||
if (optind == argc)
|
||||
{
|
||||
/* no arguments. Equivalent to: `*' */
|
||||
job_nargs = 1;
|
||||
job_args = &defarg;
|
||||
}
|
||||
else
|
||||
{
|
||||
job_nargs = argc - optind;
|
||||
job_args = argv + optind;
|
||||
}
|
||||
}
|
||||
|
||||
pid_t
|
||||
xfork(void)
|
||||
/* Like fork(), only never returns on failure */
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) die_e("Can't fork");
|
||||
return pid;
|
||||
}
|
||||
|
||||
int
|
||||
xopen(int fd, const char *file_name, int flags)
|
||||
/* Like open, only it:
|
||||
* a) never returns on failure, and
|
||||
* b) if "fd" is non-negative, expect the file to open
|
||||
* on file-descriptor "fd".
|
||||
*/
|
||||
{
|
||||
int rfd;
|
||||
|
||||
rfd = open(file_name, flags);
|
||||
if (fd >= 0 && rfd != fd)
|
||||
die_e("Can't open %s on file-descriptor %d", file_name, fd);
|
||||
else if (rfd < 0)
|
||||
die_e("Can't open %s", file_name);
|
||||
return rfd;
|
||||
}
|
||||
|
||||
void
|
||||
xclose(int fd)
|
||||
/* Like close(), only doesn't return on failure */
|
||||
{
|
||||
if (close(fd)) die_e("Can't close file descriptor %d", fd);
|
||||
}
|
||||
|
||||
static void
|
||||
go_background(void)
|
||||
/* Become a daemon. The foreground process exits successfully. */
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
/* stdin is already closed */
|
||||
|
||||
xclose(STDOUT_FILENO);
|
||||
/* coverity[leaked_handle] – fd 1 closed automatically */
|
||||
xopen(STDOUT_FILENO, "/dev/null", O_WRONLY);
|
||||
|
||||
xclose(STDERR_FILENO);
|
||||
/* coverity[leaked_handle] – fd 2 closed automatically */
|
||||
xopen(STDERR_FILENO, "/dev/null", O_WRONLY);
|
||||
|
||||
pid = xfork();
|
||||
if (pid != 0)
|
||||
{
|
||||
/* parent */
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* child */
|
||||
primary_pid = getpid();
|
||||
if (setsid() == -1) die_e("setsid() error");
|
||||
in_background = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_sigalrm(int unused ATTRIBUTE_UNUSED)
|
||||
{
|
||||
got_sigalrm = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_sigchld(int unused ATTRIBUTE_UNUSED)
|
||||
{
|
||||
got_sigchld = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_sigusr1(int unused ATTRIBUTE_UNUSED)
|
||||
{
|
||||
got_sigusr1 = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
set_signal_handling(void)
|
||||
/* We only use SIGALRM, SIGCHLD and SIGUSR1, and we unblock them only
|
||||
* in wait_signal().
|
||||
*/
|
||||
{
|
||||
sigset_t ss;
|
||||
struct sigaction sa;
|
||||
|
||||
got_sigalrm = got_sigchld = got_sigusr1 = 0;
|
||||
|
||||
/* block SIGALRM, SIGCHLD and SIGUSR1 */
|
||||
if (sigemptyset(&ss) ||
|
||||
sigaddset(&ss, SIGALRM) ||
|
||||
sigaddset(&ss, SIGCHLD) ||
|
||||
sigaddset(&ss, SIGUSR1)) die_e("sigset error");
|
||||
if (sigprocmask(SIG_BLOCK, &ss, NULL)) die_e ("sigprocmask error");
|
||||
|
||||
/* setup SIGALRM handler */
|
||||
sa.sa_handler = handle_sigalrm;
|
||||
sa.sa_mask = ss;
|
||||
sa.sa_flags = 0;
|
||||
if (sigaction(SIGALRM, &sa, NULL)) die_e("sigaction error");
|
||||
|
||||
/* setup SIGCHLD handler */
|
||||
sa.sa_handler = handle_sigchld;
|
||||
sa.sa_mask = ss;
|
||||
sa.sa_flags = SA_NOCLDSTOP;
|
||||
if (sigaction(SIGCHLD, &sa, NULL)) die_e("sigaction error");
|
||||
|
||||
/* setup SIGUSR1 handler */
|
||||
sa.sa_handler = handle_sigusr1;
|
||||
sa.sa_mask = ss;
|
||||
sa.sa_flags = 0;
|
||||
if (sigaction(SIGUSR1, &sa, NULL)) die_e("sigaction error");
|
||||
}
|
||||
|
||||
static void
|
||||
wait_signal(void)
|
||||
/* Return after a signal is caught */
|
||||
{
|
||||
sigset_t ss;
|
||||
|
||||
if (sigprocmask(0, NULL, &ss)) die_e("sigprocmask error");
|
||||
if (sigdelset(&ss, SIGALRM) ||
|
||||
sigdelset(&ss, SIGCHLD) ||
|
||||
sigdelset(&ss, SIGUSR1)) die_e("sigset error");
|
||||
sigsuspend(&ss);
|
||||
}
|
||||
|
||||
static void
|
||||
wait_children(void)
|
||||
/* Wait until we have no more children (of any kind) */
|
||||
{
|
||||
while (running_jobs > 0 || running_mailers > 0)
|
||||
{
|
||||
wait_signal();
|
||||
if (got_sigchld) tend_children();
|
||||
got_sigchld = 0;
|
||||
if (got_sigusr1) explain("Received SIGUSR1");
|
||||
got_sigusr1 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
orderly_termination(void)
|
||||
/* Execution is diverted here, when we get SIGUSR1 */
|
||||
{
|
||||
explain("Received SIGUSR1");
|
||||
got_sigusr1 = 0;
|
||||
wait_children();
|
||||
explain("Exited");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void
|
||||
xsleep(unsigned int n)
|
||||
/* Sleep for n seconds, servicing SIGCHLDs and SIGUSR1s in the meantime.
|
||||
* If n=0, return immediately.
|
||||
*/
|
||||
{
|
||||
if (n == 0) return;
|
||||
alarm(n);
|
||||
do
|
||||
{
|
||||
wait_signal();
|
||||
if (got_sigchld) tend_children();
|
||||
got_sigchld = 0;
|
||||
if (got_sigusr1) orderly_termination();
|
||||
}
|
||||
while (!got_sigalrm);
|
||||
got_sigalrm = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
wait_jobs(void)
|
||||
/* Wait until there are no running jobs,
|
||||
* servicing SIGCHLDs and SIGUSR1s in the meantime.
|
||||
*/
|
||||
{
|
||||
while (running_jobs > 0)
|
||||
{
|
||||
wait_signal();
|
||||
if (got_sigchld) tend_children();
|
||||
got_sigchld = 0;
|
||||
if (got_sigusr1) orderly_termination();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
record_start_time(void)
|
||||
{
|
||||
struct tm *tm_now;
|
||||
|
||||
start_sec = time(NULL);
|
||||
tm_now = localtime(&start_sec);
|
||||
year = tm_now->tm_year + 1900;
|
||||
month = tm_now->tm_mon + 1;
|
||||
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 && !testing_only)
|
||||
explain("Anacron started on %04d-%02d-%02d",
|
||||
year, month, day_of_month);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
time_till(job_rec *jr)
|
||||
/* Return the number of seconds that we have to wait until it's time
|
||||
* to start job jr.
|
||||
*/
|
||||
{
|
||||
time_t tj, tn;
|
||||
|
||||
if (now) return 0;
|
||||
tn = time(NULL);
|
||||
tj = start_sec + (time_t)jr->delay * 60;
|
||||
if (tj < tn) return 0;
|
||||
if (tj - tn > 3600*24)
|
||||
{
|
||||
explain("System time manipulation detected, job `%s' will run immediately",
|
||||
jr->ident);
|
||||
return 0;
|
||||
}
|
||||
return (unsigned int)(tj - tn);
|
||||
}
|
||||
|
||||
static void
|
||||
fake_jobs(void)
|
||||
{
|
||||
int j;
|
||||
|
||||
j = 0;
|
||||
while (j < njobs)
|
||||
{
|
||||
fake_job(job_array[j]);
|
||||
explain("Updated timestamp for job `%s' to %04d-%02d-%02d",
|
||||
job_array[j]->ident, year, month, day_of_month);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
explain_intentions(void)
|
||||
{
|
||||
int j;
|
||||
|
||||
j = 0;
|
||||
while (j < njobs)
|
||||
{
|
||||
if (now)
|
||||
{
|
||||
explain("Will run job `%s'", job_array[j]->ident);
|
||||
}
|
||||
else
|
||||
{
|
||||
explain("Will run job `%s' in %d min.",
|
||||
job_array[j]->ident, job_array[j]->delay);
|
||||
}
|
||||
j++;
|
||||
}
|
||||
if (serialize && njobs > 0)
|
||||
explain("Jobs will be executed sequentially");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int j;
|
||||
int cwd;
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
|
||||
anacrontab = NULL;
|
||||
spooldir = NULL;
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
if (gettimeofday(&tv, &tz) != 0)
|
||||
explain("Can't get exact time, failure.");
|
||||
|
||||
srandom((unsigned int)(getpid() + tv.tv_usec));
|
||||
|
||||
if((program_name = strrchr(argv[0], '/')) == NULL)
|
||||
program_name = argv[0];
|
||||
else
|
||||
++program_name; /* move pointer to char after '/' */
|
||||
|
||||
parse_opts(argc, argv);
|
||||
|
||||
if (anacrontab == NULL)
|
||||
anacrontab = strdup(ANACRONTAB);
|
||||
|
||||
if (spooldir == NULL)
|
||||
spooldir = strdup(ANACRON_SPOOL_DIR);
|
||||
|
||||
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 %s", spooldir );
|
||||
|
||||
if (sigprocmask(0, NULL, &old_sigmask)) die_e("sigset error");
|
||||
|
||||
xclose(STDIN_FILENO);
|
||||
xopen(STDIN_FILENO, "/dev/null", O_RDONLY);
|
||||
|
||||
if (!no_daemon && !testing_only)
|
||||
go_background();
|
||||
else
|
||||
primary_pid = getpid();
|
||||
|
||||
record_start_time();
|
||||
read_tab(cwd);
|
||||
close(cwd);
|
||||
arrange_jobs();
|
||||
|
||||
if (testing_only)
|
||||
{
|
||||
if (complaints) exit (EXIT_FAILURE);
|
||||
|
||||
exit (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if (update_only)
|
||||
{
|
||||
fake_jobs();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
explain_intentions();
|
||||
set_signal_handling();
|
||||
running_jobs = running_mailers = 0;
|
||||
for(j = 0; j < njobs; ++j)
|
||||
{
|
||||
xsleep(time_till(job_array[j]));
|
||||
if (serialize) wait_jobs();
|
||||
launch_job(job_array[j]);
|
||||
}
|
||||
wait_children();
|
||||
explain("Normal exit (%d job%s run)", njobs, njobs == 1 ? "" : "s");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
92
anacron/matchrx.c
Normal file
92
anacron/matchrx.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Anacron - run commands periodically
|
||||
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
|
||||
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>
|
||||
|
||||
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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
The GNU General Public License can also be found in the file
|
||||
`COPYING' that comes with the Anacron source distribution.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <regex.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "matchrx.h"
|
||||
|
||||
int
|
||||
match_rx(const char *rx, char *string, unsigned int n_sub, /* char **substrings */...)
|
||||
/* Return 1 if the regular expression "*rx" matches the string "*string",
|
||||
* 0 if not, -1 on error.
|
||||
* "Extended" regular expressions are used.
|
||||
* Additionally, there should be "n_sub" "substrings" arguments. These,
|
||||
* if not NULL, and if the match succeeds are set to point to the
|
||||
* corresponding substrings of the regexp.
|
||||
* The original string is changed, and the substrings must not overlap,
|
||||
* or even be directly adjacent.
|
||||
* This is not the most efficient, or elegant way of doing this.
|
||||
*/
|
||||
{
|
||||
int r;
|
||||
unsigned int n;
|
||||
regex_t crx;
|
||||
va_list va;
|
||||
char **substring;
|
||||
regmatch_t *sub_offsets;
|
||||
|
||||
sub_offsets = malloc(sizeof(regmatch_t) * (n_sub + 1));
|
||||
if (sub_offsets == NULL)
|
||||
return -1;
|
||||
memset(sub_offsets, 0, sizeof(regmatch_t) * (n_sub + 1));
|
||||
|
||||
if (regcomp(&crx, rx, REG_EXTENDED)) {
|
||||
free(sub_offsets);
|
||||
return -1;
|
||||
}
|
||||
r = regexec(&crx, string, n_sub + 1, sub_offsets, 0);
|
||||
if (r != 0 && r != REG_NOMATCH) {
|
||||
free(sub_offsets);
|
||||
return -1;
|
||||
}
|
||||
regfree(&crx);
|
||||
if (r == REG_NOMATCH) {
|
||||
free(sub_offsets);
|
||||
return 0;
|
||||
}
|
||||
|
||||
va_start(va, n_sub);
|
||||
n = 1;
|
||||
while (n <= n_sub)
|
||||
{
|
||||
substring = va_arg(va, char**);
|
||||
if (substring != NULL)
|
||||
{
|
||||
if (sub_offsets[n].rm_so == -1) {
|
||||
va_end(va);
|
||||
free(sub_offsets);
|
||||
return - 1;
|
||||
}
|
||||
*substring = string + sub_offsets[n].rm_so;
|
||||
*(string + sub_offsets[n].rm_eo) = 0;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
va_end(va);
|
||||
free(sub_offsets);
|
||||
return 1;
|
||||
}
|
||||
26
anacron/matchrx.h
Normal file
26
anacron/matchrx.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Anacron - run commands periodically
|
||||
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
|
||||
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>
|
||||
|
||||
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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
The GNU General Public License can also be found in the file
|
||||
`COPYING' that comes with the Anacron source distribution.
|
||||
*/
|
||||
|
||||
|
||||
int match_rx(const char *rx, char *string,
|
||||
unsigned int n_sub, /* char **substrings */...);
|
||||
451
anacron/readtab.c
Normal file
451
anacron/readtab.c
Normal file
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
The GNU General Public License can also be found in the file
|
||||
`COPYING' that comes with the Anacron source distribution.
|
||||
*/
|
||||
|
||||
|
||||
/* /etc/anacrontab parsing, and job sorting
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <obstack.h>
|
||||
#include <limits.h>
|
||||
#include <fnmatch.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include "global.h"
|
||||
#include "matchrx.h"
|
||||
|
||||
static struct obstack input_o; /* holds input line */
|
||||
static struct obstack tab_o; /* holds processed data read from anacrontab */
|
||||
static FILE *tab;
|
||||
job_rec **job_array;
|
||||
int njobs; /* number of jobs to run */
|
||||
static int jobs_read; /* number of jobs read */
|
||||
static int line_num; /* current line in anacrontab */
|
||||
static job_rec *last_job_rec; /* last job stored in memory, at the moment */
|
||||
static env_rec *last_env_rec; /* last environment assignment stored */
|
||||
|
||||
static int random_number = 0;
|
||||
|
||||
/* some definitions for the obstack macros */
|
||||
#define obstack_chunk_alloc xmalloc
|
||||
#define obstack_chunk_free free
|
||||
|
||||
static void *
|
||||
xmalloc (size_t size)
|
||||
/* Just like standard malloc(), only never returns NULL. */
|
||||
{
|
||||
void * ptr;
|
||||
|
||||
ptr = malloc(size);
|
||||
if (ptr == NULL)
|
||||
die("Memory exhausted");
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static int
|
||||
conv2int(const char *s)
|
||||
/* Return the int or -1 on over/under-flow
|
||||
*/
|
||||
{
|
||||
long l;
|
||||
|
||||
errno = 0;
|
||||
l = strtol(s, NULL, 10);
|
||||
/* we use negative as error, so I am really returning unsigned int */
|
||||
if (errno == ERANGE || l < 0 || l > INT_MAX) return - 1;
|
||||
return (int)l;
|
||||
}
|
||||
|
||||
static char *
|
||||
read_tab_line (void)
|
||||
/* Read one line and return a pointer to it.
|
||||
Return NULL if no more lines.
|
||||
*/
|
||||
{
|
||||
int c, prev=0;
|
||||
|
||||
if (feof(tab)) return NULL;
|
||||
while (1)
|
||||
{
|
||||
c = getc(tab);
|
||||
if ((c == '\n' && prev != '\\') || c == EOF)
|
||||
{
|
||||
if (0 != prev) obstack_1grow(&input_o, (char)prev);
|
||||
break;
|
||||
}
|
||||
|
||||
if (('\\' != prev || c != '\n') && 0 != prev && '\n' != prev) obstack_1grow(&input_o, (char)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);
|
||||
}
|
||||
|
||||
static int
|
||||
job_arg_num(const char *ident)
|
||||
/* Return the command-line-argument number referring to this job-identifier.
|
||||
* If it isn't specified, return -1.
|
||||
*/
|
||||
{
|
||||
int i, r;
|
||||
|
||||
for (i = 0; i < job_nargs; i++)
|
||||
{
|
||||
r = fnmatch(job_args[i], ident, 0);
|
||||
if (r == 0) return i;
|
||||
if (r != FNM_NOMATCH) die("fnmatch() error");
|
||||
}
|
||||
return - 1;
|
||||
}
|
||||
|
||||
static void
|
||||
register_env(const char *env_var, const char *value)
|
||||
/* Store the environment assignment "env_var"="value" */
|
||||
{
|
||||
env_rec *er;
|
||||
int var_len, val_len;
|
||||
|
||||
var_len = (int)strlen(env_var);
|
||||
val_len = (int)strlen(value);
|
||||
if (!var_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
er = obstack_alloc(&tab_o, sizeof(env_rec));
|
||||
if (er == NULL) {
|
||||
die_e("Cannot allocate memory.");
|
||||
}
|
||||
|
||||
er->assign = obstack_alloc(&tab_o, var_len + 1 + val_len + 1);
|
||||
if (er->assign == NULL) {
|
||||
die_e("Cannot allocate memory.");
|
||||
}
|
||||
strcpy(er->assign, env_var);
|
||||
er->assign[var_len] = '=';
|
||||
strcpy(er->assign + var_len + 1, value);
|
||||
er->assign[var_len + 1 + val_len] = 0;
|
||||
if (last_env_rec != NULL) last_env_rec->next = er;
|
||||
else first_env_rec = er;
|
||||
last_env_rec = er;
|
||||
Debug(("on line %d: %s", line_num, er->assign));
|
||||
}
|
||||
|
||||
static void
|
||||
register_job(const char *periods, const char *delays,
|
||||
const char *ident, char *command)
|
||||
/* Store a job definition */
|
||||
{
|
||||
int period, delay;
|
||||
job_rec *jr;
|
||||
int ident_len, command_len;
|
||||
|
||||
ident_len = (int)strlen(ident);
|
||||
command_len = (int)strlen(command);
|
||||
jobs_read++;
|
||||
period = conv2int(periods);
|
||||
delay = conv2int(delays);
|
||||
if (period < 0 || 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 (jr == NULL) {
|
||||
die_e("Cannot allocate memory.");
|
||||
}
|
||||
jr->period = period;
|
||||
jr->named_period = 0;
|
||||
delay += random_number;
|
||||
jr->delay = delay;
|
||||
jr->tab_line = line_num;
|
||||
jr->ident = obstack_alloc(&tab_o, ident_len + 1);
|
||||
if (jr->ident == NULL) {
|
||||
die_e("Cannot allocate memory.");
|
||||
}
|
||||
strcpy(jr->ident, ident);
|
||||
jr->arg_num = job_arg_num(ident);
|
||||
jr->command = obstack_alloc(&tab_o, command_len + 1);
|
||||
if (jr->command == NULL) {
|
||||
die_e("Cannot allocate memory.");
|
||||
}
|
||||
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->period, jr->delay, jr->ident, jr->command));
|
||||
}
|
||||
|
||||
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 ident_len, command_len;
|
||||
|
||||
ident_len = (int)strlen(ident);
|
||||
command_len = (int)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 (jr == NULL) {
|
||||
die_e("Cannot allocate memory.");
|
||||
}
|
||||
if (!strncmp ("@monthly", periods, 8)) {
|
||||
jr->named_period = 1;
|
||||
} else if (!strncmp("@yearly", periods, 7) || !strncmp("@annually", periods, 9) || !strncmp(/* backwards compat misspelling */"@annualy", periods, 8)) {
|
||||
jr->named_period = 2;
|
||||
} else if (!strncmp ("@daily", periods, 6)) {
|
||||
jr->named_period = 3;
|
||||
} else if (!strncmp ("@weekly", periods, 7)) {
|
||||
jr->named_period = 4;
|
||||
} else {
|
||||
complain("%s: Unknown named period on line %d, skipping",
|
||||
anacrontab, line_num);
|
||||
}
|
||||
jr->period = 0;
|
||||
delay += random_number;
|
||||
jr->delay = delay;
|
||||
jr->tab_line = line_num;
|
||||
jr->ident = obstack_alloc(&tab_o, ident_len + 1);
|
||||
if (jr->ident == NULL) {
|
||||
die_e("Cannot allocate memory.");
|
||||
}
|
||||
strcpy(jr->ident, ident);
|
||||
jr->arg_num = job_arg_num(ident);
|
||||
jr->command = obstack_alloc(&tab_o, command_len + 1);
|
||||
if (jr->command == NULL) {
|
||||
die_e("Cannot allocate memory.");
|
||||
}
|
||||
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 long int
|
||||
unbiased_rand(long int max)
|
||||
{
|
||||
long int rn;
|
||||
long int divisor;
|
||||
|
||||
divisor = RAND_MAX / (max + 1);
|
||||
|
||||
do {
|
||||
rn = random() / divisor;
|
||||
} while (rn > max);
|
||||
|
||||
return rn;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_tab_line(char *line)
|
||||
{
|
||||
int r;
|
||||
char *env_var;
|
||||
char *value;
|
||||
char *periods;
|
||||
char *delays;
|
||||
char *ident;
|
||||
char *command;
|
||||
char *from;
|
||||
char *to;
|
||||
char *pref_hour;
|
||||
|
||||
/* an empty line? */
|
||||
r = match_rx("^[ \t]*($|#)", line, 0);
|
||||
if (r == -1) goto reg_err;
|
||||
if (r)
|
||||
{
|
||||
Debug(("line %d empty", line_num));
|
||||
return;
|
||||
}
|
||||
|
||||
/* an environment assignment? */
|
||||
r = match_rx("^[ \t]*([^ \t=]+)[ \t]*=(.*)$", line, 2,
|
||||
&env_var, &value);
|
||||
if (r == -1) goto reg_err;
|
||||
if (r)
|
||||
{
|
||||
if (strncmp(env_var, "START_HOURS_RANGE", 17) == 0)
|
||||
{
|
||||
r = match_rx("^([[:digit:]]+)-([[:digit:]]+)$", value, 2, &from, &to);
|
||||
if (r == -1) goto reg_err;
|
||||
if (r == 0) goto reg_invalid;
|
||||
range_start = atoi(from);
|
||||
range_stop = atoi(to);
|
||||
if (range_stop < range_start) {
|
||||
range_start = 0; range_stop = 0;
|
||||
goto reg_invalid;
|
||||
}
|
||||
Debug(("Jobs will start in the %02d:00-%02d:00 range.", range_start, range_stop));
|
||||
}
|
||||
else if (strncmp(env_var, "RANDOM_DELAY", 12) == 0) {
|
||||
r = match_rx("^([[:digit:]]+)$", value, 0);
|
||||
if (r == -1) goto reg_err;
|
||||
if (r == 0) goto reg_invalid;
|
||||
|
||||
random_number = (int)unbiased_rand(atoi(value));
|
||||
Debug(("Randomized delay set: %d", random_number));
|
||||
}
|
||||
else if (strncmp(env_var, "PREFERRED_HOUR", 14) == 0) {
|
||||
r = match_rx("^([[:digit:]]+)$", value, 1, &pref_hour);
|
||||
if (r == -1) goto reg_err;
|
||||
|
||||
if (r) {
|
||||
preferred_hour = atoi(pref_hour);
|
||||
if ((preferred_hour < 0) || (preferred_hour > 24)) {
|
||||
preferred_hour = -1;
|
||||
goto reg_invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
register_env(env_var, value);
|
||||
return;
|
||||
}
|
||||
|
||||
/* a job? */
|
||||
r = match_rx("^[ \t]*([[:digit:]]+)[ \t]+([[:digit:]]+)[ \t]+"
|
||||
"([^ \t/]+)[ \t]+([^ \t].*)$",
|
||||
line, 4, &periods, &delays, &ident, &command);
|
||||
if (r == -1) goto reg_err;
|
||||
if (r)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
reg_invalid:
|
||||
complain("Invalid syntax in %s on line %d - skipping this line",
|
||||
anacrontab, line_num);
|
||||
return;
|
||||
|
||||
reg_err:
|
||||
die("Regex error reading %s", anacrontab);
|
||||
}
|
||||
|
||||
void
|
||||
read_tab(int cwd)
|
||||
/* Read the anacrontab file into memory */
|
||||
{
|
||||
char *tab_line;
|
||||
|
||||
first_job_rec = last_job_rec = NULL;
|
||||
first_env_rec = last_env_rec = NULL;
|
||||
jobs_read = 0;
|
||||
line_num = 0;
|
||||
/* Open the anacrontab file */
|
||||
if (fchdir(cwd)) die_e("Can't chdir to original 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);
|
||||
obstack_init(&tab_o);
|
||||
while ((tab_line = read_tab_line()) != NULL)
|
||||
{
|
||||
line_num++;
|
||||
parse_tab_line(tab_line);
|
||||
obstack_free(&input_o, tab_line);
|
||||
}
|
||||
if (fclose(tab)) die_e("Error closing %s", anacrontab);
|
||||
}
|
||||
|
||||
static int
|
||||
execution_order(const job_rec **job1, const job_rec **job2)
|
||||
/* Comparison function for sorting the jobs.
|
||||
*/
|
||||
{
|
||||
int d;
|
||||
|
||||
d = (*job1)->arg_num - (*job2)->arg_num;
|
||||
if (d != 0 && now) return d;
|
||||
d = (*job1)->delay - (*job2)->delay;
|
||||
if (d != 0) return d;
|
||||
d = (*job1)->tab_line - (*job2)->tab_line;
|
||||
return d;
|
||||
}
|
||||
|
||||
void
|
||||
arrange_jobs(void)
|
||||
/* Make an array of pointers to jobs that are going to be executed,
|
||||
* and arrange them in the order of execution.
|
||||
* Also lock these jobs.
|
||||
*/
|
||||
{
|
||||
job_rec *j;
|
||||
|
||||
j = first_job_rec;
|
||||
njobs = 0;
|
||||
while (j != NULL)
|
||||
{
|
||||
if (j->arg_num != -1 && (update_only || testing_only || consider_job(j)))
|
||||
{
|
||||
njobs++;
|
||||
obstack_grow(&tab_o, &j, sizeof(j));
|
||||
}
|
||||
j = j->next;
|
||||
}
|
||||
job_array = obstack_finish(&tab_o);
|
||||
|
||||
/* sort the jobs */
|
||||
qsort(job_array, (size_t)njobs, sizeof(*job_array),
|
||||
(int (*)(const void *, const void *))execution_order);
|
||||
}
|
||||
438
anacron/runjob.c
Normal file
438
anacron/runjob.c
Normal file
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
Anacron - run commands periodically
|
||||
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
|
||||
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>
|
||||
|
||||
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
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
The GNU General Public License can also be found in the file
|
||||
`COPYING' that comes with the Anacron source distribution.
|
||||
*/
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include "global.h"
|
||||
#include "cronie_common.h"
|
||||
|
||||
#include <langinfo.h>
|
||||
|
||||
static int
|
||||
temp_file(job_rec *jr)
|
||||
/* Open a temporary file and return its file descriptor */
|
||||
{
|
||||
char *dir;
|
||||
char template[PATH_MAX+1];
|
||||
int fdin = -1;
|
||||
int fdout;
|
||||
int len;
|
||||
|
||||
dir = getenv("TMPDIR");
|
||||
if (dir == NULL || *dir == '\0')
|
||||
dir = P_tmpdir;
|
||||
|
||||
len = snprintf(template, sizeof(template), "%s/$anacronXXXXXX", dir);
|
||||
if (len < 0)
|
||||
die_e("snprintf failed");
|
||||
else if ((size_t) len >= sizeof(template))
|
||||
die_e("TMPDIR too long");
|
||||
|
||||
fdout = mkstemp(template);
|
||||
if (fdout == -1) die_e("Can't open temporary file for writing");
|
||||
|
||||
fdin = open(template, O_RDONLY, S_IRUSR | S_IWUSR);
|
||||
if (fdin == -1) die_e("Can't open temporary file for reading");
|
||||
|
||||
if (unlink(template)) die_e("Can't unlink temporary file");
|
||||
|
||||
fcntl(fdout, F_SETFD, FD_CLOEXEC); /* set close-on-exec flag */
|
||||
fcntl(fdin, F_SETFD, FD_CLOEXEC); /* set close-on-exec flag */
|
||||
|
||||
jr->input_fd = fdin;
|
||||
jr->output_fd = fdout;
|
||||
|
||||
return fdout;
|
||||
}
|
||||
|
||||
static off_t
|
||||
file_size(int fd)
|
||||
/* Return the size of temporary file fd */
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (fstat(fd, &st)) die_e("Can't fstat temporary file");
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
static char *
|
||||
username(void)
|
||||
{
|
||||
struct passwd *ps;
|
||||
static char *user;
|
||||
|
||||
if (user)
|
||||
return user;
|
||||
|
||||
ps = getpwuid(geteuid());
|
||||
if (ps == NULL || ps->pw_name == NULL) die_e("getpwuid() error");
|
||||
|
||||
user = strdup(ps->pw_name);
|
||||
if (user == NULL) die_e("memory allocation error");
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
static void
|
||||
xputenv(const char *s)
|
||||
{
|
||||
char *name = NULL, *val = NULL;
|
||||
char *eq_ptr;
|
||||
size_t eq_index;
|
||||
|
||||
if (s == NULL) {
|
||||
die_e("Invalid environment string");
|
||||
}
|
||||
|
||||
eq_ptr = strchr(s, '=');
|
||||
if (eq_ptr == NULL) {
|
||||
die_e("Invalid environment string");
|
||||
}
|
||||
|
||||
eq_index = (size_t) (eq_ptr - s);
|
||||
|
||||
name = malloc((eq_index + 1) * sizeof(char));
|
||||
if (name == NULL) {
|
||||
die_e("Not enough memory to set the environment");
|
||||
}
|
||||
|
||||
val = malloc((strlen(s) - eq_index) * sizeof(char));
|
||||
if (val == NULL) {
|
||||
die_e("Not enough memory to set the environment");
|
||||
}
|
||||
|
||||
strncpy(name, s, eq_index);
|
||||
name[eq_index] = '\0';
|
||||
strcpy(val, s + eq_index + 1);
|
||||
|
||||
if (setenv(name, val, 1)) {
|
||||
die_e("Can't set the environment");
|
||||
}
|
||||
|
||||
free(name);
|
||||
free(val);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
setup_env(const job_rec *jr)
|
||||
/* Setup the environment for the job according to /etc/anacrontab */
|
||||
{
|
||||
env_rec *er;
|
||||
|
||||
er = first_env_rec;
|
||||
if (er == NULL || jr->prev_env_rec == NULL) return;
|
||||
xputenv(er->assign);
|
||||
while (er != jr->prev_env_rec)
|
||||
{
|
||||
er = er->next;
|
||||
xputenv(er->assign);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
run_job(const job_rec *jr)
|
||||
/* This is called to start the job, after the fork */
|
||||
{
|
||||
/* If mail functionality is not disabled, pipe outputs to temp file */
|
||||
if (!jr->no_mail_output) {
|
||||
/* setup stdout and stderr */
|
||||
xclose(1);
|
||||
xclose(2);
|
||||
if (dup2(jr->output_fd, 1) != 1 || dup2(jr->output_fd, 2) != 2)
|
||||
die_e("dup2() error"); /* dup2 also clears close-on-exec flag */
|
||||
in_background = 0; /* now, errors will be mailed to the user */
|
||||
}
|
||||
|
||||
if (chdir("/")) die_e("Can't chdir to '/'");
|
||||
|
||||
if (sigprocmask(SIG_SETMASK, &old_sigmask, NULL))
|
||||
die_e("sigprocmask error");
|
||||
xcloselog();
|
||||
execl("/bin/sh", "/bin/sh", "-c", jr->command, (char *)NULL);
|
||||
die_e("execl() error");
|
||||
}
|
||||
|
||||
static void
|
||||
xwrite(int fd, const char *string)
|
||||
/* Write (using write()) the string "string" to temporary file "fd".
|
||||
* Don't return on failure */
|
||||
{
|
||||
if (write(fd, string, strlen(string)) == -1)
|
||||
die_e("Can't write to temporary file");
|
||||
}
|
||||
|
||||
static int
|
||||
xwait(pid_t pid , int *status)
|
||||
/* Check if child process "pid" has finished. If it has, return 1 and its
|
||||
* exit status in "*status". If not, return 0.
|
||||
*/
|
||||
{
|
||||
pid_t r;
|
||||
|
||||
r = waitpid(pid, status, WNOHANG);
|
||||
if (r == -1) die_e("waitpid() error");
|
||||
if (r == 0) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
launch_mailer(job_rec *jr)
|
||||
{
|
||||
pid_t pid;
|
||||
struct stat buf;
|
||||
|
||||
if (jr->mailto == NULL)
|
||||
{
|
||||
explain("Empty MAILTO set, not mailing output");
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
/* child */
|
||||
in_background = 1;
|
||||
/* set stdin to the job's output */
|
||||
xclose(STDIN_FILENO);
|
||||
if (dup2(jr->input_fd, STDIN_FILENO) != 0) die_e("Can't dup2()");
|
||||
if (lseek(STDIN_FILENO, 0, SEEK_SET) != 0) die_e("Can't lseek()");
|
||||
if (sigprocmask(SIG_SETMASK, &old_sigmask, NULL))
|
||||
die_e("sigprocmask error");
|
||||
xcloselog();
|
||||
|
||||
/* Ensure stdout/stderr are sane before exec-ing sendmail */
|
||||
/* coverity[leaked_handle] – STDOUT closed automatically */
|
||||
xclose(STDOUT_FILENO); xopen(STDOUT_FILENO, "/dev/null", O_WRONLY);
|
||||
/* coverity[leaked_handle] – STDERR closed automatically */
|
||||
xclose(STDERR_FILENO); xopen(STDERR_FILENO, "/dev/null", O_WRONLY);
|
||||
xclose(jr->output_fd);
|
||||
|
||||
/* Ensure stdin is not appendable ... ? */
|
||||
/* fdflags = fcntl(0, F_GETFL); fdflags &= ~O_APPEND; */
|
||||
/* fcntl(0, F_SETFL, fdflags ); */
|
||||
|
||||
/* Here, I basically mirrored the way /usr/sbin/sendmail is called
|
||||
* by cron on a Debian system, except for the "-oem" and "-or0s"
|
||||
* options, which don't seem to be appropriate here.
|
||||
* Hopefully, this will keep all the MTAs happy. */
|
||||
execl(SENDMAIL, SENDMAIL, "-FAnacron", "-odi",
|
||||
jr->mailto, (char *)NULL);
|
||||
die_e("Can't exec " SENDMAIL);
|
||||
}
|
||||
/* parent */
|
||||
/* record mailer pid */
|
||||
jr->mailer_pid = pid;
|
||||
running_mailers++;
|
||||
}
|
||||
|
||||
static void
|
||||
tend_mailer(job_rec *jr, int status)
|
||||
{
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
|
||||
complain("Tried to mail output of job `%s', "
|
||||
"but mailer process (" SENDMAIL ") exited with status %d",
|
||||
jr->ident, WEXITSTATUS(status));
|
||||
else if (!WIFEXITED(status) && WIFSIGNALED(status))
|
||||
complain("Tried to mail output of job `%s', "
|
||||
"but mailer process (" SENDMAIL ") got signal %d",
|
||||
jr->ident, WTERMSIG(status));
|
||||
else if (!WIFEXITED(status) && !WIFSIGNALED(status))
|
||||
complain("Tried to mail output of job `%s', "
|
||||
"but mailer process (" SENDMAIL ") terminated abnormally"
|
||||
, jr->ident);
|
||||
|
||||
jr->mailer_pid = 0;
|
||||
running_mailers--;
|
||||
}
|
||||
|
||||
void
|
||||
launch_job(job_rec *jr)
|
||||
{
|
||||
pid_t pid;
|
||||
int fd;
|
||||
char hostname[512];
|
||||
char *mailto;
|
||||
char *mailfrom;
|
||||
char mailto_expanded[MAX_EMAILSTR];
|
||||
char mailfrom_expanded[MAX_EMAILSTR];
|
||||
char *no_mail_output;
|
||||
|
||||
/* get hostname */
|
||||
if (gethostname(hostname, 512)) {
|
||||
strcpy (hostname,"unknown machine");
|
||||
}
|
||||
|
||||
setup_env(jr);
|
||||
|
||||
no_mail_output = getenv("NO_MAIL_OUTPUT");
|
||||
jr->no_mail_output = no_mail_output != NULL && *no_mail_output;
|
||||
|
||||
/* Set up email functionality if it isn't disabled */
|
||||
if (!jr->no_mail_output) {
|
||||
/* Get the destination email address if set, or current user otherwise */
|
||||
mailto = getenv("MAILTO");
|
||||
if (mailto == NULL) {
|
||||
mailto = username();
|
||||
}
|
||||
else {
|
||||
if (expand_envvar(mailto, mailto_expanded, sizeof(mailto_expanded))) {
|
||||
mailto = mailto_expanded;
|
||||
}
|
||||
else {
|
||||
complain("The environment variable 'MAILTO' could not be expanded. The non-expanded value will be used.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the source email address if set, or current user otherwise */
|
||||
mailfrom = getenv("MAILFROM");
|
||||
if (mailfrom == NULL) {
|
||||
mailfrom = username();
|
||||
}
|
||||
else {
|
||||
if (expand_envvar(mailfrom, mailfrom_expanded, sizeof(mailfrom_expanded))) {
|
||||
mailfrom = mailfrom_expanded;
|
||||
}
|
||||
else {
|
||||
complain("The environment variable 'MAILFROM' could not be expanded. The non-expanded value will be used.");
|
||||
}
|
||||
}
|
||||
|
||||
/* create temporary file for stdout and stderr of the job */
|
||||
temp_file(jr); fd = jr->output_fd;
|
||||
/* write mail header */
|
||||
xwrite(fd, "From: ");
|
||||
xwrite(fd, "Anacron <");
|
||||
xwrite(fd, mailfrom);
|
||||
xwrite(fd, ">\n");
|
||||
xwrite(fd, "To: ");
|
||||
xwrite(fd, mailto);
|
||||
xwrite(fd, "\n");
|
||||
xwrite(fd, "MIME-Version: 1.0\n");
|
||||
xwrite(fd, "Content-Type: text/plain; charset=\"");
|
||||
xwrite(fd, nl_langinfo(CODESET));
|
||||
xwrite(fd, "\"\n");
|
||||
xwrite(fd, "Content-Transfer-Encoding: 8bit\n");
|
||||
xwrite(fd, "Subject: Anacron job '");
|
||||
xwrite(fd, jr->ident);
|
||||
xwrite(fd, "' on ");
|
||||
xwrite(fd, hostname);
|
||||
xwrite(fd, "\n\n");
|
||||
|
||||
if (*mailto == '\0')
|
||||
jr->mailto = NULL;
|
||||
else
|
||||
/* ugly but works without strdup() */
|
||||
jr->mailto = mailto;
|
||||
|
||||
jr->mail_header_size = file_size(fd);
|
||||
}
|
||||
|
||||
pid = xfork();
|
||||
if (pid == 0)
|
||||
{
|
||||
/* child */
|
||||
in_background = 1;
|
||||
run_job(jr);
|
||||
/* execution never gets here */
|
||||
}
|
||||
/* parent */
|
||||
explain("Job `%s' started", jr->ident);
|
||||
jr->job_pid = pid;
|
||||
running_jobs++;
|
||||
}
|
||||
|
||||
static void
|
||||
tend_job(job_rec *jr, int status)
|
||||
/* Take care of a finished job */
|
||||
{
|
||||
int mail_output;
|
||||
const char *m;
|
||||
|
||||
update_timestamp(jr);
|
||||
unlock(jr);
|
||||
|
||||
mail_output = !jr->no_mail_output && file_size(jr->output_fd) > jr->mail_header_size;
|
||||
|
||||
m = mail_output ? " (produced output)" : "";
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
|
||||
explain("Job `%s' terminated%s", jr->ident, m);
|
||||
else if (WIFEXITED(status))
|
||||
explain("Job `%s' terminated (exit status: %d)%s",
|
||||
jr->ident, WEXITSTATUS(status), m);
|
||||
else if (WIFSIGNALED(status))
|
||||
complain("Job `%s' terminated due to signal %d%s",
|
||||
jr->ident, WTERMSIG(status), m);
|
||||
else /* is this possible? */
|
||||
complain("Job `%s' terminated abnormally%s", jr->ident, m);
|
||||
|
||||
jr->job_pid = 0;
|
||||
running_jobs--;
|
||||
if (mail_output) launch_mailer(jr);
|
||||
/* output_fd and input_fd are only set if mail functionality is enabled */
|
||||
if (!jr->no_mail_output) {
|
||||
xclose(jr->output_fd);
|
||||
xclose(jr->input_fd);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tend_children(void)
|
||||
/* This is called whenever we get a SIGCHLD.
|
||||
* Takes care of zombie children.
|
||||
*/
|
||||
{
|
||||
int j;
|
||||
int status;
|
||||
|
||||
j = 0;
|
||||
while (j < njobs)
|
||||
{
|
||||
if (job_array[j]->mailer_pid != 0 &&
|
||||
xwait(job_array[j]->mailer_pid, &status))
|
||||
tend_mailer(job_array[j], status);
|
||||
if (job_array[j]->job_pid != 0 &&
|
||||
xwait(job_array[j]->job_pid, &status))
|
||||
tend_job(job_array[j], status);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user