]> granicus.if.org Git - cronie/commitdiff
Initial upload of anacron-2.3 which should be optimized for better
authorMarcela Mašláňová <mmaslano@redhat.com>
Mon, 13 Jul 2009 13:36:53 +0000 (15:36 +0200)
committerMarcela Mašláňová <mmaslano@redhat.com>
Mon, 13 Jul 2009 13:36:53 +0000 (15:36 +0200)
cooperation with cronie. However, cronie should be working with or
without anacron, which should be configurable.

17 files changed:
anacron/COPYING [new file with mode: 0644]
anacron/ChangeLog [new file with mode: 0644]
anacron/Makefile [new file with mode: 0644]
anacron/README [new file with mode: 0644]
anacron/TODO [new file with mode: 0644]
anacron/anacron.8 [new file with mode: 0644]
anacron/anacrontab.5 [new file with mode: 0644]
anacron/global.h [new file with mode: 0644]
anacron/gregor.c [new file with mode: 0644]
anacron/gregor.h [new file with mode: 0644]
anacron/lock.c [new file with mode: 0644]
anacron/log.c [new file with mode: 0644]
anacron/main.c [new file with mode: 0644]
anacron/matchrx.c [new file with mode: 0644]
anacron/matchrx.h [new file with mode: 0644]
anacron/readtab.c [new file with mode: 0644]
anacron/runjob.c [new file with mode: 0644]

diff --git a/anacron/COPYING b/anacron/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
@@ -0,0 +1,340 @@
+                   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.
diff --git a/anacron/ChangeLog b/anacron/ChangeLog
new file mode 100644 (file)
index 0000000..39a18fb
--- /dev/null
@@ -0,0 +1,34 @@
+   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).
diff --git a/anacron/Makefile b/anacron/Makefile
new file mode 100644 (file)
index 0000000..555e524
--- /dev/null
@@ -0,0 +1,95 @@
+#   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
diff --git a/anacron/README b/anacron/README
new file mode 100644 (file)
index 0000000..eb53947
--- /dev/null
@@ -0,0 +1,142 @@
+
+     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)>.
diff --git a/anacron/TODO b/anacron/TODO
new file mode 100644 (file)
index 0000000..1354254
--- /dev/null
@@ -0,0 +1,6 @@
+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
diff --git a/anacron/anacron.8 b/anacron/anacron.8
new file mode 100644 (file)
index 0000000..a3561be
--- /dev/null
@@ -0,0 +1,147 @@
+.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)>.
diff --git a/anacron/anacrontab.5 b/anacron/anacrontab.5
new file mode 100644 (file)
index 0000000..93a67a0
--- /dev/null
@@ -0,0 +1,48 @@
+.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>
diff --git a/anacron/global.h b/anacron/global.h
new file mode 100644 (file)
index 0000000..4812c1c
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+    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
diff --git a/anacron/gregor.c b/anacron/gregor.c
new file mode 100644 (file)
index 0000000..1464398
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+    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));
+}
diff --git a/anacron/gregor.h b/anacron/gregor.h
new file mode 100644 (file)
index 0000000..3f2d436
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+    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);
diff --git a/anacron/lock.c b/anacron/lock.c
new file mode 100644 (file)
index 0000000..9284be1
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+    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);
+}
diff --git a/anacron/log.c b/anacron/log.c
new file mode 100644 (file)
index 0000000..452f819
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+    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 */
diff --git a/anacron/main.c b/anacron/main.c
new file mode 100644 (file)
index 0000000..949ecb2
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+    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);
+}
diff --git a/anacron/matchrx.c b/anacron/matchrx.c
new file mode 100644 (file)
index 0000000..a3e940c
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+    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;
+}
diff --git a/anacron/matchrx.h b/anacron/matchrx.h
new file mode 100644 (file)
index 0000000..3ce8350
--- /dev/null
@@ -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., 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 */...);
diff --git a/anacron/readtab.c b/anacron/readtab.c
new file mode 100644 (file)
index 0000000..4157ebc
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+    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);
+}
diff --git a/anacron/runjob.c b/anacron/runjob.c
new file mode 100644 (file)
index 0000000..4c8a2c9
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+    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++;
+    }
+}