--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+ 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).
--- /dev/null
+# Anacron - run commands periodically
+# Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# The GNU General Public License can also be found in the file
+# `COPYING' that comes with the Anacron source distribution.
+
+
+PREFIX =
+BINDIR = $(PREFIX)/usr/sbin
+MANDIR = $(PREFIX)/usr/man
+CFLAGS = -Wall -pedantic -O2
+#CFLAGS = -Wall -O2 -g -DDEBUG
+
+# If you change these, please update the man-pages too
+# Only absolute paths here, please
+SPOOLDIR = /var/spool/anacron
+ANACRONTAB = /etc/anacrontab
+
+RELEASE = 2.3
+package_name = anacron-$(RELEASE)
+distfiles = ChangeLog COPYING README TODO anacron.8 anacrontab.5 Makefile *.h *.c
+
+SHELL = /bin/sh
+INSTALL = install
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_DATA = $(INSTALL)
+INSTALL_DIR = $(INSTALL) -d
+GZIP = gzip -9 -f
+ALL_CPPFLAGS = -DSPOOLDIR=\"$(SPOOLDIR)\" -DRELEASE=\"$(RELEASE)\" \
+ -DANACRONTAB=\"$(ANACRONTAB)\" $(CPPFLAGS)
+
+csources := $(wildcard *.c)
+objects = $(csources:.c=.o)
+
+.PHONY: all
+all: anacron
+
+# This makefile generates header file dependencies auto-magically
+%.d: %.c
+ $(SHELL) -ec "$(CC) -MM $(ALL_CPPFLAGS) $< \
+ | sed '1s/^\(.*\)\.o[ :]*/\1.d &/1' > $@"
+
+include $(csources:.c=.d)
+
+anacron: $(objects)
+ $(CC) $(LDFLAGS) $^ $(LOADLIBES) -o $@
+
+%.o : %.c
+ $(CC) -c $(ALL_CPPFLAGS) $(CFLAGS) $< -o $@
+
+.PHONY: installdirs
+installdirs:
+ $(INSTALL_DIR) $(BINDIR) $(PREFIX)$(SPOOLDIR) \
+ $(MANDIR)/man5 $(MANDIR)/man8
+
+.PHONY: install
+install: installdirs
+ $(INSTALL_PROGRAM) anacron $(BINDIR)/anacron
+ $(INSTALL_DATA) anacrontab.5 $(MANDIR)/man5/anacrontab.5
+ $(INSTALL_DATA) anacron.8 $(MANDIR)/man8/anacron.8
+
+.PHONY: clean
+clean:
+ rm -f *.o *.d anacron
+
+distclean: clean
+ rm -f *~
+
+.PHONY: dist
+dist: $(package_name).tar.gz
+
+$(package_name).tar.gz: $(distfiles)
+ mkdir $(package_name)
+ ln $(distfiles) $(package_name)
+ chmod 0644 $(package_name)/*
+ chmod 0755 $(package_name)
+ tar cf $(package_name).tar $(package_name)
+ $(GZIP) $(package_name).tar
+ rm -r $(package_name)
+
+release: distclean $(package_name).tar.gz
--- /dev/null
+
+ What is Anacron ?
+ -----------------
+
+ Anacron is a periodic command scheduler. It executes commands at
+intervals specified in days. Unlike cron, it does not assume that the
+system is running continuously. It can therefore be used to control
+the execution of daily, weekly and monthly jobs (or anything with a
+period of n days), on systems that don't run 24 hours a day. When
+installed and configured properly, Anacron will make sure that the
+commands are run at the specified intervals as closely as
+machine-uptime permits.
+
+ Every time Anacron is run, it reads a configuration file that
+specifies the jobs Anacron controls, and their periods in days. If a
+job wasn't executed in the last n days, where n is the period of that
+job, Anacron executes it. Anacron then records the date in a special
+timestamp file that it keeps for each job, so it can know when to run
+it again. When all the executed commands terminate, Anacron exits.
+
+ It is recommended to run Anacron from the system boot-scripts.
+This way the jobs "whose time has come" will be run shortly after the
+machine boots. A delay can be specified for each job so that the
+machine isn't overloaded at boot time.
+
+ In addition to running Anacron from the boot-scripts, it is also
+recommended to schedule it as a daily cron-job (usually at an early
+morning hour), so that if the machine is kept running for a night,
+jobs for the next day will still be executed.
+
+
+ Why this may be useful ?
+ ------------------------
+
+ Most Unix-like systems have daily, weekly and monthly scripts that
+take care of various "housekeeping chores" such as log-rotation,
+updating the "locate" and "man" databases, etc. Daily scripts are
+usually scheduled as cron-jobs to execute around 1-7 AM. Weekly
+scripts are scheduled to run on Sundays. On machines that are turned
+off for the night or for the weekend, these scripts rarely get run.
+
+ Anacron solves this problem. These jobs can simply be scheduled as
+Anacron-jobs with periods of 1, 7 and 30 days.
+
+
+ What Anacron is not ?
+ ---------------------
+
+ Anacron is not an attempt to make cron redundant. It cannot
+currently be used to schedule commands at intervals smaller than days.
+It also does not guarantee that the commands will be executed at any
+specific day or hour.
+
+ It isn't a full-time daemon. It has to be executed from boot
+scripts, from cron-jobs, or explicitly.
+
+
+ For more details, see the anacron(8) manpage.
+
+
+ Requirements
+ ------------
+
+ - A Linux system. (maybe other *NIX systems)
+ - A functioning syslog daemon.
+ - A functioning /usr/lib/sendmail command. (all MTAs should have
+ that).
+
+
+ Compilation and Installation
+ ----------------------------
+
+ - Untar the source package.
+
+ - Check the Makefile. Edit as required.
+
+ - Check the top of "global.h". You may want to change the syslog
+ facility and priorities, and the path to your MTA's sendmail
+ compatible command (/usr/lib/sendmail).
+
+ - cd to the directory.
+
+ - Type "make".
+ You can safely ignore warnings of the form: "*.d: No such file or
+ directory"
+
+ - Become root. Type "make install".
+
+
+ Setup
+ -----
+
+1. Locate your system's daily, weekly and monthly cron-jobs.
+ See your cron documentation for more details.
+
+2. Decide which of these jobs should be controlled by Anacron.
+ Remember that Anacron does not guarantee execution at any specific
+ day of the month, day of the week, or time of day. Jobs for which
+ the timing is critical should probably not be controlled by
+ Anacron.
+
+3. Comment these jobs out of their crontab files. (You may have to
+ use the "crontab" command for this. See the cron documentation.)
+
+4. Put them in /etc/anacrontab. Note that the format is not the same
+ as the crontab entries. See the anacrontab(5) manpage. Here's an
+ example from a typical Debian system:
+
+-----Cut
+# /etc/anacrontab example
+SHELL=/bin/sh
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+# format: period delay job-identifier command
+1 5 cron.daily run-parts /etc/cron.daily
+7 10 cron.weekly run-parts /etc/cron.weekly
+30 15 cron.monthly run-parts /etc/cron.monthly
+-----Cut
+
+5. Put the command "anacron -s" somewhere in your boot-scripts.
+ Make sure that syslogd is started before this command.
+
+6. Schedule the command "anacron -s" as a daily cron-job (preferably
+ at some early morning hour). This will make sure that jobs are run
+ when the systems is left running for a night.
+
+That's it.
+
+It is a good idea to check what your daily, weekly and monthly scripts
+actually do, and disable any parts that may be irrelevant for your
+system.
+
+
+ Credits
+ -------
+
+Anacron was originally conceived and implemented by Christian Schwarz
+<schwarz@monet.m.isar.de>.
+
+The current implementation is a complete rewrite by Itai Tzur
+<itzur@actcom.co.il>.
+
+Current code base maintained by Sean 'Shaleh' Perry <shaleh@(debian.org|valinux.com)>.
--- /dev/null
+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
--- /dev/null
+.TH ANACRON 8 2000-06-22 "Sean 'Shaleh' Perry" "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] ...
+.br
+.B anacron -u [\fB-t anacrontab\fR] \fR[\fIjob\fR] ...
+.br
+.B anacron \fR[\fB-V\fR|\fB-h\fR]
+.SH DESCRIPTION
+Anacron
+can be used to execute commands periodically, with a
+frequency specified in days. Unlike \fBcron(8)\fR,
+it does not assume that the machine is running continuously. Hence,
+it can be used on machines that aren't running 24 hours a day,
+to control daily, weekly, and monthly jobs that are
+usually controlled by \fBcron\fR.
+.PP
+When executed, Anacron reads a list of jobs from a configuration file, normally
+.I /etc/anacrontab
+(see \fBanacrontab(5)\fR). This file
+contains the list of jobs that Anacron controls. Each
+job entry specifies a period in days,
+a delay in minutes, a unique
+job identifier, and a shell command.
+.PP
+For each job, Anacron checks whether
+this job has been executed in the last n days, where n is the period specified
+for that job. If not, Anacron runs the job's shell command, after waiting
+for the number of minutes specified as the delay parameter.
+.PP
+After the command exits, Anacron records the date in a special
+timestamp file for that job, so it can know when to execute it again. Only
+the date is used for the time
+calculations. The hour is not used.
+.PP
+When there are no more jobs to be run, Anacron exits.
+.PP
+Anacron only considers jobs whose identifier, as
+specified in the \fIanacrontab\fR matches any of
+the
+.I job
+command-line arguments. The
+.I job
+arguments can be shell wildcard patterns (be sure to protect them from
+your shell with adequate quoting). Specifying no
+.I job
+arguments, is equivalent to specifying "*" (That is, all jobs will be
+considered).
+.PP
+Unless the \fB-d\fR option is given (see below), Anacron forks to the
+background when it starts, and the parent process exits
+immediately.
+.PP
+Unless the \fB-s\fR or \fB-n\fR options are given, Anacron starts jobs
+immediately when their delay is over. The execution of different jobs is
+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).
+.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
+priority \fBerror\fR.
+.PP
+"Active" jobs (i.e. jobs that Anacron already decided
+to run and now wait for their delay to pass, and jobs that are currently
+being executed by
+Anacron), are "locked", so that other copies of Anacron won't run them
+at the same time.
+.SH OPTIONS
+.TP
+.B -f
+Force execution of the jobs, ignoring the timestamps.
+.TP
+.B -u
+Only update the timestamps of the jobs, to the current date, but
+don't run anything.
+.TP
+.B -s
+Serialize execution of jobs. Anacron will not start a new job before the
+previous one finished.
+.TP
+.B -n
+Run jobs now. Ignore the delay specifications in the
+.I /etc/anacrontab
+file. This options implies \fB-s\fR.
+.TP
+.B -d
+Don't fork to the background. In this mode, Anacron will output informational
+messages to standard error, as well as to syslog. The output of jobs
+is mailed as usual.
+.TP
+.B -q
+Suppress messages to standard error. Only applicable with \fB-d\fR.
+.TP
+.B -t anacrontab
+Use specified anacrontab, rather than the default
+.TP
+.B -V
+Print version information, and exit.
+.TP
+.B -h
+Print short usage message, and exit.
+.SH SIGNALS
+After receiving a \fBSIGUSR1\fR signal, Anacron waits for running
+jobs, if any, to finish and then exits. This can be used to stop
+Anacron cleanly.
+.SH NOTES
+Make sure that the time-zone is set correctly before Anacron is
+started. (The time-zone affects the date). This is usually accomplished
+by setting the TZ environment variable, or by installing a
+.I /usr/lib/zoneinfo/localtime
+file. See
+.B tzset(3)
+for more information.
+.SH FILES
+.TP
+.I /etc/anacrontab
+Contains specifications of jobs. See \fBanacrontab(5)\fR for a complete
+description.
+.TP
+.I /var/spool/anacron
+This directory is used by Anacron for storing timestamp files.
+.SH "SEE ALSO"
+.B anacrontab(5), cron(8), tzset(3)
+.PP
+The Anacron
+.I README
+file.
+.SH BUGS
+Anacron never removes timestamp files. Remove unused files manually.
+.PP
+Anacron
+uses up to two file descriptors for each active job. It may run out of
+descriptors if there are more than about 125 active jobs (on normal kernels).
+.PP
+Mail comments, suggestions and bug reports to Sean 'Shaleh' Perry <shaleh@(debian.org|valinux.com)>.
+.SH AUTHOR
+Anacron was originally conceived and implemented by Christian Schwarz
+<schwarz@monet.m.isar.de>.
+.PP
+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)>.
--- /dev/null
+.TH ANACRONTAB 5 1998-02-02 "Itai Tzur" "Anacron Users' Manual"
+.SH NAME
+/etc/anacrontab \- configuration file for anacron
+.SH DESCRIPTION
+The file
+.I /etc/anacrontab
+describes the jobs controlled by \fBanacron(8)\fR. Its lines can be of
+three kinds: job-description lines, environment
+assignments, or empty lines.
+.PP
+Job-description lines are of the form:
+.PP
+ period delay job-identifier command
+.PP
+The
+.I period
+is specified in days, the
+.I delay
+in minutes. The
+.I job-identifier
+can contain any non-blank character, except slashes. It is used to identify
+the job in Anacron messages,
+and as the name for the job's timestamp file. The
+.I command
+can be any shell command.
+.PP
+Environment assignment lines are of the form:
+.PP
+ VAR = VALUE
+.PP
+Spaces around
+.I VAR
+are removed. No spaces around
+.I VALUE
+are allowed (unless you want them to be part of the value). The assignment
+takes effect from the next line to the end of the file, or to the next
+assignment of the same variable.
+.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.
+.SH "SEE ALSO"
+.B anacron(8)
+.PP
+The Anacron
+.I README
+file.
+.SH AUTHOR
+Itai Tzur <itzur@actcom.co.il>
--- /dev/null
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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>
+
+/* Some declarations */
+
+struct env_rec1 {
+ char *assign;
+
+ struct env_rec1 *next;
+};
+typedef struct env_rec1 env_rec;
+
+struct job_rec1 {
+ int period;
+ int delay;
+ char *ident;
+ char *command;
+
+ int tab_line;
+ int arg_num;
+ int timestamp_fd;
+ int output_fd;
+ int mail_header_size;
+ pid_t job_pid;
+ pid_t mailer_pid;
+
+ 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 int old_umask;
+extern sigset_t old_sigmask;
+extern int serialize,force,update_only,now,no_daemon,quiet;
+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 **args;
+extern int nargs;
+
+extern int njobs;
+extern job_rec **job_array;
+
+extern int running_jobs,running_mailers;
+
+
+/* Function prototypes */
+
+/* main.c */
+int xopen(int fd, const char *file_name, int flags);
+void xclose(int fd);
+pid_t xfork();
+
+/* log.c */
+void explain(const char *fmt, ...);
+void explain_e(const char *fmt, ...);
+void complain(const char *fmt, ...);
+void complain_e(const char *fmt, ...);
+void die(const char *fmt, ...);
+void die_e(const char *fmt, ...);
+void xdebug(const char *fmt, ...);
+void xdebug_e(const char *fmt, ...);
+void xcloselog();
+
+#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();
+void arrange_jobs();
+
+/* 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
--- /dev/null
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "gregor.h"
+
+const static 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;
+ const 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));
+}
--- /dev/null
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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);
--- /dev/null
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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;
+ 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;
+ }
+ }
+
+ /* 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);
+}
--- /dev/null
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "global.h"
+
+static char truncated[] = " (truncated)";
+static char msg[MAX_MSG + 1];
+static int log_open = 0;
+
+static void
+xopenlog()
+{
+ if (!log_open)
+ {
+ openlog(program_name, LOG_PID, SYSLOG_FACILITY);
+ log_open = 1;
+ }
+}
+
+void
+xcloselog()
+{
+ 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 >= sizeof(msg) - 1)
+ strcpy(msg + sizeof(msg) - sizeof(truncated), truncated);
+}
+
+static void
+log(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 log(), 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);
+ log(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);
+ log(COMPLAIN_LEVEL, fmt, args);
+ va_end(args);
+}
+
+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);
+}
+
+void
+die(const char *fmt, ...)
+/* Log a "complain" level message, and exit */
+{
+ va_list args;
+
+ va_start(args, fmt);
+ log(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);
+ log(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 */
--- /dev/null
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ The GNU General Public License can also be found in the file
+ `COPYING' that comes with the Anacron source distribution.
+*/
+
+
+#include <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 "global.h"
+#include "gregor.h"
+
+pid_t primary_pid;
+int day_now;
+int year, month, day_of_month; /* date anacron started */
+
+char *program_name;
+char *anacrontab;
+int serialize, force, update_only, now,
+ no_daemon, quiet; /* command-line options */
+char **args; /* vector of "job" command-line arguments */
+int nargs; /* number of these */
+char *defarg = "*";
+int in_background; /* are we in the background? */
+int old_umask; /* umask when started */
+sigset_t old_sigmask; /* signal mask when started */
+
+job_rec *first_job_rec;
+env_rec *first_env_rec;
+
+static time_t start_sec; /* time anacron started */
+static volatile int got_sigalrm, got_sigchld, got_sigusr1;
+int running_jobs, running_mailers; /* , number of */
+
+static void
+print_version()
+{
+ printf("Anacron " RELEASE "\n"
+ "Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>\n"
+ "Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>\n"
+ "\n"
+ "Mail comments, suggestions and bug reports to <shaleh@debian.org>."
+ "\n\n");
+}
+
+static void
+print_usage()
+{
+ printf("Usage: anacron [-s] [-f] [-n] [-d] [-q] [-t anacrontab] [job] ...\n"
+ " anacron -u [job] ...\n"
+ " anacron [-V|-h]\n"
+ "\n"
+ " -s Serialize execution of jobs\n"
+ " -f Force execution of jobs, even before their time\n"
+ " -n Run jobs with no delay, implies -s\n"
+ " -d Don't fork to the background\n"
+ " -q Suppress stderr messages, only applicable with -d\n"
+ " -u Update the timestamps without actually running anything\n"
+ " -t Use this anacrontab\n"
+ " -V Print version information\n"
+ " -h Print this message\n"
+ "\n"
+ "See the manpage for more details.\n"
+ "\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: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':
+ anacrontab = strdup(optarg);
+ break;
+ case 'V':
+ print_version();
+ exit(0);
+ case 'h':
+ print_usage();
+ exit(0);
+ 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: `*' */
+ nargs = 1;
+ args = &defarg;
+ }
+ else
+ {
+ nargs = argc - optind;
+ args = argv + optind;
+ }
+}
+
+pid_t
+xfork()
+/* 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()
+/* Become a daemon. The foreground process exits successfully. */
+{
+ pid_t pid;
+
+ /* stdin is already closed */
+
+ if (fclose(stdout)) die_e("Can't close stdout");
+ xopen(1, "/dev/null", O_WRONLY);
+
+ if (fclose(stderr)) die_e("Can't close stderr");
+ xopen(2, "/dev/null", O_WRONLY);
+
+ pid = xfork();
+ if (pid != 0)
+ {
+ /* parent */
+ exit(0);
+ }
+ else
+ {
+ /* child */
+ primary_pid = getpid();
+ if (setsid() == -1) die_e("setsid() error");
+ in_background = 1;
+ }
+}
+
+void
+handle_sigalrm()
+{
+ got_sigalrm = 1;
+}
+
+void
+handle_sigchld()
+{
+ got_sigchld = 1;
+}
+
+void
+handle_sigusr1()
+{
+ got_sigusr1 = 1;
+}
+
+static void
+set_signal_handling()
+/* 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()
+/* 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()
+/* 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()
+/* Execution is diverted here, when we get SIGUSR1 */
+{
+ explain("Received SIGUSR1");
+ got_sigusr1 = 0;
+ wait_children();
+ explain("Exited");
+ exit(0);
+}
+
+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()
+/* 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()
+{
+ 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)
+ explain("Anacron " RELEASE " started on %04d-%02d-%02d",
+ year, month, day_of_month);
+}
+
+static int
+time_till(job_rec *jr)
+/* Return the number of seconds that we have to wait until it's time
+ * to start job jr.
+ */
+{
+ unsigned int tj, tn;
+
+ if (now) return 0;
+ tn = time(NULL);
+ tj = start_sec + jr->delay * 60;
+ if (tj < tn) return 0;
+ return tj - tn;
+}
+
+static void
+fake_jobs()
+{
+ 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()
+{
+ 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;
+
+ anacrontab = NULL;
+
+ 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);
+
+ in_background = 0;
+
+ if (chdir(SPOOLDIR)) die_e("Can't chdir to " SPOOLDIR);
+
+ old_umask = umask(0);
+
+ if (sigprocmask(0, NULL, &old_sigmask)) die_e("sigset error");
+
+ if (fclose(stdin)) die_e("Can't close stdin");
+ xopen(0, "/dev/null", O_RDONLY);
+
+ if (!no_daemon)
+ go_background();
+ else
+ primary_pid = getpid();
+
+ record_start_time();
+ read_tab();
+ arrange_jobs();
+
+ if (update_only)
+ {
+ fake_jobs();
+ exit(0);
+ }
+
+ 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 jobs run)", njobs);
+ exit(0);
+}
--- /dev/null
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "matchrx.h"
+
+int
+match_rx(const char *rx, char *string, 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, n;
+ regex_t crx;
+ va_list va;
+ char **substring;
+ regmatch_t *sub_offsets;
+ sub_offsets = malloc(sizeof(regmatch_t) * (n_sub + 1));
+ memset(sub_offsets, 0, sizeof(regmatch_t) * (n_sub + 1));
+
+ if (regcomp(&crx, rx, REG_EXTENDED)) return - 1;
+ r = regexec(&crx, string, n_sub + 1, sub_offsets, 0);
+ if (r != 0 && r != REG_NOMATCH) return - 1;
+ regfree(&crx);
+ if (r == REG_NOMATCH) 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) 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;
+}
--- /dev/null
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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,
+ int n_sub, /* char **substrings */...);
--- /dev/null
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 */
+
+/* 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 l;
+}
+
+static char *
+read_tab_line ()
+/* Read one line and return a pointer to it.
+Return NULL if no more lines.
+ */
+{
+ int c;
+
+ if (feof(tab)) return NULL;
+ while ((c = getc(tab)) != EOF && c != '\n')
+ obstack_1grow(&input_o, 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 refering to this job-identifier.
+ * If it isn't specified, return -1.
+ */
+{
+ int i, r;
+
+ for (i = 0; i < nargs; i++)
+ {
+ r = fnmatch(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 = strlen(env_var);
+ val_len = strlen(value);
+ er = obstack_alloc(&tab_o, sizeof(env_rec));
+ er->assign = obstack_alloc(&tab_o, var_len + 1 + val_len + 1);
+ 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 = strlen(ident);
+ command_len = 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));
+ jr->period = period;
+ 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->period, jr->delay, jr->ident, jr->command));
+}
+
+static void
+parse_tab_line(char *line)
+{
+ int r;
+ char *env_var;
+ char *value;
+ char *periods;
+ char *delays;
+ char *ident;
+ char *command;
+
+ /* 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)
+ {
+ 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;
+ }
+ 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()
+/* 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 */
+ tab = fopen(anacrontab, "r");
+ 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()
+/* 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 || 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, njobs, sizeof(*job_array),
+ (int (*)(const void *, const void *))execution_order);
+}
--- /dev/null
+/*
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "global.h"
+
+static int
+temp_file()
+/* Open a temporary file and return its file descriptor */
+{
+ const int max_retries = 50;
+ char *name;
+ int fd, i;
+
+ i = 0;
+ name = NULL;
+ do
+ {
+ i++;
+ free(name);
+ name = tempnam(NULL, NULL);
+ if (name == NULL) die("Can't find a unique temporary filename");
+ fd = open(name, O_RDWR | O_CREAT | O_EXCL | O_APPEND,
+ S_IRUSR | S_IWUSR);
+ /* I'm not sure we actually need to be so persistent here */
+ } while (fd == -1 && errno == EEXIST && i < max_retries);
+
+ if (fd == -1) die_e("Can't open temporary file");
+ if (unlink(name)) die_e("Can't unlink temporary file");
+ free(name);
+ fcntl(fd, F_SETFD, 1); /* set close-on-exec flag */
+ return fd;
+}
+
+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()
+{
+ struct passwd *ps;
+
+ ps = getpwuid(geteuid());
+ if (ps == NULL) die_e("getpwuid() error");
+ return ps->pw_name;
+}
+
+static void
+xputenv(const char *s)
+{
+ if (putenv(s)) die_e("Can't set the environment");
+}
+
+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 */
+{
+ setup_env(jr);
+ /* 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 '/'");
+
+ umask(old_umask);
+ 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;
+
+ pid = xfork();
+ if (pid == 0)
+ {
+ /* child */
+ in_background = 1;
+ /* set stdin to the job's output */
+ xclose(0);
+ if (dup2(jr->output_fd, 0) != 0) die_e("Can't dup2()");
+ if (lseek(0, 0, SEEK_SET) != 0) die_e("Can't lseek()");
+ umask(old_umask);
+ if (sigprocmask(SIG_SETMASK, &old_sigmask, NULL))
+ die_e("sigprocmask error");
+ xcloselog();
+
+ /* 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",
+ username(), (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 ststus %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;
+
+ /* create temporary file for stdout and stderr of the job */
+ fd = jr->output_fd = temp_file();
+ /* write mail header */
+ xwrite(fd, "From: ");
+ xwrite(fd, username());
+ xwrite(fd, " (Anacron)\n");
+ xwrite(fd, "To: ");
+ xwrite(fd, username());
+ xwrite(fd, "\n");
+ xwrite(fd, "Subject: Anacron job '");
+ xwrite(fd, jr->ident);
+ xwrite(fd, "'\n\n");
+ 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;
+ char *m;
+
+ update_timestamp(jr);
+ unlock(jr);
+ if (file_size(jr->output_fd) > jr->mail_header_size) mail_output = 1;
+ else mail_output = 0;
+
+ m = mail_output ? " (mailing 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);
+ xclose(jr->output_fd);
+}
+
+void
+tend_children()
+/* 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++;
+ }
+}