anacron-2.3/0040755000112500011250000000000007124524231012265 5ustar shalehshalehanacron-2.3/ChangeLog0100644000112500011250000000254107124524216014041 0ustar shalehshaleh 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 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). anacron-2.3/COPYING0100644000112500011250000004312707123775122013332 0ustar shalehshaleh 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. 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.) 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. 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. 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 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. Copyright (C) 19yy 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. , 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. anacron-2.3/README0100644000112500011250000001161307124524216013147 0ustar shalehshaleh 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 . The current implementation is a complete rewrite by Itai Tzur . Current code base maintained by Sean 'Shaleh' Perry . anacron-2.3/TODO0100644000112500011250000000030707124524216012755 0ustar shalehshalehanacron runs jobs twice in a 31 day month add hostname to emails sent to admin allow user anacrontabs make manpages match #defines automagically --> sed fu full ANSI / POSIX compliance code cleaning anacron-2.3/anacron.80100644000112500011250000001151707124524216014004 0ustar shalehshaleh.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 . .SH AUTHOR Anacron was originally conceived and implemented by Christian Schwarz . .PP The current implementation is a complete rewrite by Itai Tzur . .PP The code base is currently maintained by Sean 'Shaleh' Perry . anacron-2.3/anacrontab.50100644000112500011250000000235107123775122014467 0ustar shalehshaleh.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 anacron-2.3/Makefile0100644000112500011250000000523207124524216013727 0ustar shalehshaleh# Anacron - run commands periodically # Copyright (C) 1998 Itai Tzur # # 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 anacron-2.3/global.h0100644000112500011250000000657707124524216013715 0ustar shalehshaleh/* Anacron - run commands periodically Copyright (C) 1998 Itai Tzur Copyright (C) 1999 Sean 'Shaleh' Perry 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 /* 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 anacron-2.3/gregor.h0100644000112500011250000000204407124524216013723 0ustar shalehshaleh/* Anacron - run commands periodically Copyright (C) 1998 Itai Tzur Copyright (C) 1999 Sean 'Shaleh' Perry 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); anacron-2.3/matchrx.h0100644000112500011250000000212207124524216014101 0ustar shalehshaleh/* Anacron - run commands periodically Copyright (C) 1998 Itai Tzur Copyright (C) 1999 Sean 'Shaleh' Perry 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 */...); anacron-2.3/gregor.c0100644000112500011250000000661707124524216013730 0ustar shalehshaleh/* Anacron - run commands periodically Copyright (C) 1998 Itai Tzur Copyright (C) 1999 Sean 'Shaleh' Perry 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 #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)); } anacron-2.3/lock.c0100644000112500011250000001064607124500574013371 0ustar shalehshaleh/* Anacron - run commands periodically Copyright (C) 1998 Itai Tzur Copyright (C) 1999 Sean 'Shaleh' Perry 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 #include #include #include #include #include #include #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); } anacron-2.3/log.c0100644000112500011250000001160207124524216013212 0ustar shalehshaleh/* Anacron - run commands periodically Copyright (C) 1998 Itai Tzur Copyright (C) 1999 Sean 'Shaleh' Perry 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 #include #include #include #include #include #include #include #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 */ anacron-2.3/main.c0100644000112500011250000002422607124524216013363 0ustar shalehshaleh/* Anacron - run commands periodically Copyright (C) 1998 Itai Tzur Copyright (C) 1999 Sean 'Shaleh' Perry 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 #include #include #include #include #include #include #include #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 \n" "Copyright (C) 1999 Sean 'Shaleh' Perry \n" "\n" "Mail comments, suggestions and bug reports to ." "\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); } anacron-2.3/matchrx.c0100644000112500011250000000456207123775122014111 0ustar shalehshaleh/* Anacron - run commands periodically Copyright (C) 1998 Itai Tzur Copyright (C) 1999 Sean 'Shaleh' Perry 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 #include #include #include #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; } anacron-2.3/readtab.c0100644000112500011250000001645307124524216014044 0ustar shalehshaleh/* Anacron - run commands periodically Copyright (C) 1998 Itai Tzur Copyright (C) 1999 Sean 'Shaleh' Perry 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 #include #include #include #include #include #include #include #include #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); } anacron-2.3/runjob.c0100644000112500011250000001651607123775122013744 0ustar shalehshaleh/* Anacron - run commands periodically Copyright (C) 1998 Itai Tzur Copyright (C) 1999 Sean 'Shaleh' Perry 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 #include #include #include #include #include #include #include #include #include #include #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++; } }