Index: Makefile
===================================================================
--- Makefile (nonexistent)
+++ Makefile (revision 5)
@@ -0,0 +1,56 @@
+
+COMPONENT_TARGETS = $(HARDWARE_NOARCH)
+
+
+include ../../../../build-system/constants.mk
+
+
+url = $(DOWNLOAD_SERVER)/sources/packages/a/dcron
+
+versions = 4.5
+pkgname = dcron
+suffix = tar.gz
+
+tarballs = $(addsuffix .$(suffix), $(addprefix $(pkgname)-, $(versions)))
+sha1s = $(addsuffix .sha1sum, $(tarballs))
+
+patches = $(CURDIR)/patches/dcron-4.5.patch
+
+.NOTPARALLEL: $(patches)
+
+
+BUILD_TARGETS = $(tarballs) $(sha1s) $(patches)
+
+
+include ../../../../build-system/core.mk
+
+
+.PHONY: download_clean
+
+
+$(tarballs):
+ @echo -e "\n======= Downloading source tarballs =======" ; \
+ for tarball in $(tarballs) ; do \
+ echo "$(url)/$$tarball" | xargs -n 1 -P 100 wget $(WGET_OPTIONS) - & \
+ done ; wait
+
+$(sha1s): $(tarballs)
+ @for sha in $@ ; do \
+ echo -e "\n======= Downloading '$$sha' signature =======\n" ; \
+ echo "$(url)/$$sha" | xargs -n 1 -P 100 wget $(WGET_OPTIONS) - & wait %1 ; \
+ touch $$sha ; \
+ echo -e "\n======= Check the '$$sha' sha1sum =======\n" ; \
+ sha1sum --check $$sha ; ret="$$?" ; \
+ if [ "$$ret" == "1" ]; then \
+ echo -e "\n======= ERROR: Bad '$$sha' sha1sum =======\n" ; \
+ exit 1 ; \
+ fi ; \
+ done
+
+$(patches): $(sha1s)
+ @echo -e "\n======= Create Patches =======\n" ; \
+ ( cd create-4.5-patch ; ./create.patch.sh ) ; \
+ echo -e "\n"
+
+download_clean:
+ @rm -f $(tarballs) $(sha1s) $(patches)
Index: create-4.5-patch/create.patch.sh
===================================================================
--- create-4.5-patch/create.patch.sh (nonexistent)
+++ create-4.5-patch/create.patch.sh (revision 5)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+VERSION=4.5
+
+tar --files-from=file.list -xzvf ../dcron-$VERSION.tar.gz
+mv dcron-$VERSION dcron-$VERSION-orig
+
+cp -rf ./dcron-$VERSION-new ./dcron-$VERSION
+
+diff --unified -Nr dcron-$VERSION-orig dcron-$VERSION > dcron-$VERSION.patch
+
+mv dcron-$VERSION.patch ../patches
+
+rm -rf ./dcron-$VERSION
+rm -rf ./dcron-$VERSION-orig
Property changes on: create-4.5-patch/create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: create-4.5-patch/dcron-4.5-new/CHANGELOG
===================================================================
--- create-4.5-patch/dcron-4.5-new/CHANGELOG (nonexistent)
+++ create-4.5-patch/dcron-4.5-new/CHANGELOG (revision 5)
@@ -0,0 +1,168 @@
+todo
+ * add timestamps to -d output
+
+ * manual cron.update prodding doesn't affect cl_NotUntil
+
+ * Use hash when ID=... is supplied. FS#18292.
+
+ * FS#18352: Another thing: when moving the original file to the backup name, and the edited version is written in it's place, the file is written without preserving the same permissions as the original, so if you have a umask that prevents others from reading your stuff, crontab won't be able to load the new file.
+
+git
+ * Numeric loglevels specified by 'crond -l <level>' weren't being validated.
+ Now we no longer accept numeric loglevels; they must be specified
+ symbolically. Thanks to Rogutės Sparnuotos.
+
+ * Continued portability improvements. Makefile now uses -lbsd-compat.
+ Factored allocation and string calls to utils.c.
+
+ * Many internal changes and annotations to pass splint review.
+
+ * Documentation and error message updates.
+
+v4.5 1-May-2011
+ * Some cron jobs were running multiple times. Now we make sure not to
+ ArmJobs that are already running; and not to resynchronize while jobs are
+ running; and to poll the DST setting. (Fixes Arch FS#18681; thanks to Vincent
+ Cappe and Paul Gideon Dann for identifying the second issue; and Tilman
+ Sauerbeck for identifying the third.)
+
+ * @monthly was wrongly being parsed the same as @yearly (fixes Arch
+ FS#19123). Thanks to Peter Johnson, Paul Gideon Dann, and Tilman Sauerbeck.
+
+ * extra/crond.rc: now uses $CROND_ARGS from /etc/conf.d/crond; sample included
+ as extra/crond.conf. Suggested by Eric Bélanger.
+
+ * Running `/etc/rc.d/crond start` after startup could leak unwanted
+ environment into cronjobs; now we force crond to start in empty env
+ (fixes Arch FS#22085). Thanks to Mantas.
+
+ * Also set LOGNAME environment variable in cronjobs. Requested by Michael
+ Trunner; fixes Arch FS#18338.
+
+ * extra/crond.logrotate now correctly gets pid from /var/run/crond.pid
+ (fixes Arch FS#18039). Thanks to Kay Abendroth, revel, and Chlump Chatkupt.
+
+ * extra/prune-cronstamps now only deletes files, and is formatted as a
+ @weekly crontab. Thanks to Alec Moskvin <alecm@gmx.com>.
+
+ * extra/crontab.vim works around an issue where vim's writebackup would
+ interfere with crontab's security model (addresses Arch FS#18352).
+ Thanks to Armadillo and Simon Williams.
+
+ * Makefile uses $LDFLAGS (fixes Arch FS#23784). Thanks to Kristoffer Tidemann
+ and Mike Frysinger.
+
+ * defs.h sets default locations for CRONTABS and CRONSTAMPS beneath /var/spool/cron/,
+ as in earlier versions of dcron.
+
+ * Documentation updates.
+
+ * Thanks for testing and feedback: Feifei Jia, Spider.007, Ray Kohler,
+ Igor Zakharoff, Edward Hades, and Joe Lightning.
+
+v4.4 17-Jan-2010
+ * Finished mailjobs were being left as zombie processes. Fixed.
+
+ * When using crond with logging-to-file, user jobs could only log some
+ events if they had write access to the log. Fixed this by having crond
+ keep a file descriptor open to the log; also added a SIGHUP handler
+ to make crond re-open the logfile. The sample logrotate script now
+ sends that signal.
+
+ * More sensible command-line parsing by crontab.
+
+ * Add prune-cronstamps to extra; document extra/*; general improvement
+ of README and manpages.
+
+ * Portability improvements, and defs.h now has fuller comments about
+ requirements.
+
+ * Makefile improvements: `make` now caches variables for `make install`;
+ don't stomp CFLAGS environment variable, and added BINDIR,SBINDIR,MANDIR.
+
+ * Thanks to Juergen Daubert for more testing and suggestions.
+
+v4.3 11-Jan-2010
+ * Internal refactoring to make buffer overflow checks
+ clearer and portability issues more explicit.
+
+ * Made file argument to -L mandatory; optional args to
+ getopt needs GNU extensions.
+
+ * Makefile tweaks. Added CRONTAB_GROUP for `make install`.
+ Renamed TIMESTAMPS -> CRONSTAMPS.
+
+ * Thanks to Juergen Daubert for testing and suggestions.
+
+v4.2 11-Jan-2010
+ * Makefile tweaks; moved more constants to #defines.
+
+v4.1 10-Jan-2010
+ * Fixed bug in parsing some numeric fields in crontabs. (Terminus of range
+ wasn't being modded.)
+
+ * Updated Makefile to make it easier to customize timestamps at configure
+ time. Also, if LC_TIME is defined when crond runs, we use that instead of
+ compiled-in default (for logging to files, to customize syslog output use
+ syslog-ng's 'template' command).
+
+ * Fixed Makefile permissions on crond and crontab binaries.
+
+v4.0 6-Jan-2010
+ * Jim Pryor took over development; folded in changes from his fork "yacron"
+
+ * Applied "Daniel's patch" from dcron 3.x tarballs to enable logging to syslog or
+ files. Added further logging improvements.
+
+ * Added -m user@host and -M mailer options
+
+ * Various crontab syntax extensions, including "2nd Monday of every month",
+ @reboot, @daily, and finer-grained frequency specifiers.
+
+ * Jobs can wait until AFTER other jobs have finished.
+
+ * Enhanced parsing of cron.update file, to make it possible for scripts to
+ interact with a running crond in limited ways.
+
+ * Various internal changes
+
+ * Updated Makefile, manpage buildchain, and docs
+
+v3.2
+ Fixed a minor bug, remove the newline terminating a line only if there
+ is a newline to remove.
+
+v3.1
+ Add support for root-run crontab files in /etc/cron.d and rewrite a
+ good chunk of the crontab file management code. By VMiklos and Matt
+ Dillon.
+
+v3.0
+ Fix /tmp race and misc cleanups from Emiel Kollof <emiel@gamepoint.net>
+
+v2.9
+ Modernize the code, remove strcpy() and sprintf() in favor of snprintf().
+ (Supplied by Christine Jamison <technobabe@mail.nwmagic.net>)
+
+v2.8
+ Fixed bug found by Christian HOFFMANN. newline removal was broken
+ for lines that began with whitespace, causing crontab lines to be
+ chopped off.
+
+v2.7
+ Committed changes suggested by
+ Ragnar Hojland Espinosa <ragnar@redestb.es>
+
+ Fixed a few printfs, removed strdup() function ( strdup() is now standard
+ in all major clib's )
+
+v2.4-2.6
+ ( changes lost )
+
+v2.3
+ dillon: Fixed bug in job.c -- if ChangeUser() fails, would return from child fork rather
+ then exit! Oops.
+
+v2.2
+ dillon: Initial release
+
Index: create-4.5-patch/dcron-4.5-new/Makefile
===================================================================
--- create-4.5-patch/dcron-4.5-new/Makefile (nonexistent)
+++ create-4.5-patch/dcron-4.5-new/Makefile (revision 5)
@@ -0,0 +1,86 @@
+# Makefile for Dillon's crond and crontab
+VERSION = 4.5
+
+# these variables can be configured by e.g. `make SCRONTABS=/different/path`
+PREFIX = /usr
+CRONTAB_GROUP = root
+SCRONTABS = /etc/cron.d
+CRONTABS = /var/spool/cron/crontabs
+CRONSTAMPS = /var/spool/cron/cronstamps
+# used for syslog
+LOG_IDENT = crond
+# used for logging to file (syslog manages its own timestamps)
+# if LC_TIME is set, it will override any compiled-in timestamp format
+TIMESTAMP_FMT = %b %e %H:%M:%S
+SBINDIR = $(PREFIX)/sbin
+BINDIR = $(PREFIX)/bin
+MANDIR = $(PREFIX)/share/man
+
+-include config
+
+
+SHELL = /bin/sh
+INSTALL = install -o root
+INSTALL_PROGRAM = $(INSTALL) -D
+INSTALL_DATA = $(INSTALL) -D -m0644 -g root
+INSTALL_DIR = $(INSTALL) -d -m0755 -g root
+CFLAGS ?= -O2
+CFLAGS += -Wall -Wstrict-prototypes -Wno-missing-field-initializers
+SRCS = main.c subs.c database.c job.c concat.c chuser.c
+OBJS = main.o subs.o database.o job.o concat.o chuser.o
+TABSRCS = crontab.c chuser.c
+TABOBJS = crontab.o chuser.o
+PROTOS = protos.h
+DEFS = -DVERSION='"$(VERSION)"' \
+ -DSCRONTABS='"$(SCRONTABS)"' -DCRONTABS='"$(CRONTABS)"' \
+ -DCRONSTAMPS='"$(CRONSTAMPS)"' -DLOG_IDENT='"$(LOG_IDENT)"' \
+ -DTIMESTAMP_FMT='"$(TIMESTAMP_FMT)"'
+
+# save variables needed for `make install` in config
+all: $(PROTOS) crond crontab ;
+ rm -f config
+ echo "PREFIX = $(PREFIX)" >> config
+ echo "SBINDIR = $(SBINDIR)" >> config
+ echo "BINDIR = $(BINDIR)" >> config
+ echo "MANDIR = $(MANDIR)" >> config
+ echo "CRONTAB_GROUP = $(CRONTAB_GROUP)" >> config
+ echo "SCRONTABS = $(SCRONTABS)" >> config
+ echo "CRONTABS = $(CRONTABS)" >> config
+ echo "CRONSTAMPS = $(CRONSTAMPS)" >> config
+
+protos.h: $(SRCS) $(TABSRCS)
+ fgrep -h Prototype $(SRCS) $(TABSRCS) > protos.h
+
+crond: $(OBJS)
+ $(CC) $(LDFLAGS) $^ $(LIBS) -o crond
+
+crontab: $(TABOBJS)
+ $(CC) $(LDFLAGS) $^ -o crontab
+
+%.o: %.c defs.h $(PROTOS)
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $(DEFS) $< -o $@
+
+install:
+ $(INSTALL_PROGRAM) -m0700 -g root crond $(DESTDIR)$(SBINDIR)/crond
+ $(INSTALL_PROGRAM) -m4750 -g $(CRONTAB_GROUP) crontab $(DESTDIR)$(BINDIR)/crontab
+ $(INSTALL_DATA) crontab.1 $(DESTDIR)$(MANDIR)/man1/crontab.1
+ $(INSTALL_DATA) crond.8 $(DESTDIR)$(MANDIR)/man8/crond.8
+ $(INSTALL_DIR) $(DESTDIR)$(SCRONTABS)
+ $(INSTALL_DIR) $(DESTDIR)$(CRONTABS)
+ $(INSTALL_DIR) $(DESTDIR)$(CRONSTAMPS)
+
+clean: force
+ rm -f *.o $(PROTOS)
+ rm -f crond crontab config
+
+force: ;
+
+man: force
+ -pandoc -t man -f markdown -s crontab.markdown -o crontab.1
+ -pandoc -t man -f markdown -s crond.markdown -o crond.8
+
+# for maintainer's use only
+TARNAME = /home/abs/_dcron/dcron-$(VERSION).tar.gz
+dist: clean man
+ bsdtar -cz --exclude repo/.git -f $(TARNAME).new -s'=^repo=dcron-$(VERSION)=' -C .. repo
+ mv -f $(TARNAME).new $(TARNAME)
Index: create-4.5-patch/dcron-4.5-new/README
===================================================================
--- create-4.5-patch/dcron-4.5-new/README (nonexistent)
+++ create-4.5-patch/dcron-4.5-new/README (revision 5)
@@ -0,0 +1,214 @@
+DCRON - DILLON'S LIGHTWEIGHT CRON DAEMON
+========================================
+
+This lightweight cron daemon aims to be simple and secure, with just enough
+features to stay useful. It was written from scratch by Matt Dillon in 1994.
+It's now developed and maintained by Jim Pryor.
+
+In the author's opinion, having to combine a cron daemon with another daemon
+like anacron makes for too much complexity. So the goal is a simple cron daemon
+that can also take over the central functions of anacron.
+
+Unlike other fatter cron daemons, though, this cron doesn't even try to manage
+environment variables or act as a shell. All jobs are run with `/bin/sh` for
+conformity and portability. We don't try to use the user's preferred shell:
+that breaks down for special users and even makes some of us normal users
+unhappy (for example, /bin/csh does not use a true O_APPEND mode and has
+difficulty redirecting stdout and stderr both to different places!). You can,
+of course, run shell scripts in whatever language you like by making them
+executable with #!/bin/csh or whatever as the first line. If you don't like
+the extra processes, just `exec` them.
+
+If you need to set special environment variables, pass them as arguments to a
+script.
+
+The programs were written with an eye towards security, hopefully we haven't
+forgotton anything. The programs were also written with an eye towards nice,
+clean, algorithmically sound code. It's small, and the only fancy code is that
+which deals with child processes. We do not try to optimize with vfork() since
+it causes headaches and is rather pointless considering we're execing a shell
+most of the time, and we pay close attention to leaving descriptors open in the
+crond and close attention to preventing crond from running away.
+
+
+DOWNLOADING
+-----------
+
+The project is hosted at: <http://www.jimpryor.net/linux/dcron.html>.
+
+The latest version is 4.5, which can be downloaded here:
+<http://www.jimpryor.net/linux/releases/dcron-4.5.tar.gz>.
+
+A public git repo is available at: <http://repo.or.cz/w/dcron.git>.
+
+
+COMPILING
+---------
+
+You must use a compiler that understands prototypes, such as GCC.
+
+(1) The following compile-time defaults are configurable via
+command-line assignments on the `make` line (they're shown here with
+their default values):
+
+ PREFIX=/usr/local # where files will ultimately be installed
+ SBINDIR = $(PREFIX)/sbin # where crond will be installed
+ BINDIR = $(PREFIX)/bin # where crontab will be installed
+ MANDIR = $(PREFIX)/share/man # where manpages will be installed
+ CRONTABS = /var/spool/cron/crontabs # default dir for per-user crontabs
+ CRONSTAMPS = /var/spool/cron/cronstamps # default dir
+ SCRONTABS = /etc/cron.d # default dir for system crontabs
+
+ CRONTAB_GROUP = wheel # who's allowed to edit their own crontabs?
+ LOG_IDENT = crond # syslog uses facility LOG_CRON and this identity
+ TIMESTAMP_FMT = %b %e %H:%M:%S # used if LC_TIME unset and logging to file
+
+A few additional compile-time settings are defined in defs.h. If you find yourself
+wanting to edit defs.h directly, try editing the DEFS line in the Makefile instead.
+
+(2) Run make with your desired settings. For example:
+
+ make PREFIX=/usr CRONTAB_GROUP=users
+
+(3) If you're using the git version, you might also want to `make man`,
+to be sure the manpages are updated. This requires
+[pandoc](http://johnmacfarlane.net/pandoc/).
+
+
+INSTALLING
+----------
+
+(4) `make install` installs the files underneath PREFIX (by default, /usr/local).
+If you're packaging, you can supply a DESTDIR argument here:
+
+ make DESTDIR=/path/to/your/package/root install
+
+Permissions will be as follows:
+
+ -rwx------ 0 root root 32232 Jan 6 18:58 /usr/local/sbin/crond
+ -rwsr-x--- 0 root wheel 15288 Jan 6 18:58 /usr/local/bin/crontab
+
+Only users belonging to crontab's group (here "wheel") will be able to use it.
+You may want to create a special "cron" group and assign crontab to it:
+
+ groupadd cron
+ chgrp cron /usr/local/bin/crontab
+ chmod 4750 /usr/local/bin/crontab
+
+(If the group already exists, you can specify it by supplying CRONTAB_GROUP
+to the `make` or `make install` commands.)
+
+Then add users to group "cron" when you want them to be able to install
+or edit their own crontabs. The superuser is able to install crontabs for users
+who don't have the privileges to edit their own.
+
+You should schedule crond to run automatically from system startup, using
+/etc/rc.local or a similar mechanism. crond automatically detaches. By default
+it logs all events <= loglevel NOTICE to syslog.
+
+The crontab files are normally located in /var/spool/cron/crontabs, and timestamps
+are normally in /var/spool/cron/cronstamps. These directories normally have permissions:
+
+ drwxr-xr-x 2 root root 4096 Jan 6 18:50 /var/spool/cron
+ drwxr-xr-x 1 root root 0 Jan 6 18:58 /var/spool/cron/crontabs
+ drwxr-xr-x 1 root root 0 Jan 6 18:58 /var/spool/cron/cronstamps/
+
+Here is the superuser's crontab, created using `sudo crontab -e`:
+
+ -rw------- 0 root root 513 Jan 6 18:58 /var/spool/cron/crontabs/root
+
+TESTING
+-------
+
+Use the crontab program to create a personal crontab with the following
+two lines:
+
+ * * * * * date >> /tmp/test
+ * * * * * date
+
+Check the log output of crond to ensure the cron entries are being
+run once a minute, check /tmp/test to ensure the date is being
+appended to it once a minute, and check your mail to ensure that crond
+is mailing you the date from the other entry once a minute.
+
+After you are through testing cron, delete the entries with `crontab -e`
+or `crontab -d`.
+
+EXTRAS
+------
+
+The following are included in the "extra" folder. None of them are installed
+by `make install`:
+
+crond.rc
+: This is an example rc script to start and stop crond. It could be placed in
+/etc/rc.d or /etc/init.d in suitable systems.
+
+crond.conf
+: This contains user-modifiable settings for crond.rc. The sample crond.rc
+expects to source this file from /etc/conf.d/crond.
+
+run-cron
+: This simple shell script is a bare-bones alternative to Debian's run-parts.
+
+root.crontab
+: This is an example crontab to install for the root user, or to install
+in /etc/cron.d. It runs any executable scripts located in the directories /etc/cron.hourly,
+/etc/cron.daily, /etc/cron.weekly, and /etc/cron.monthly at the appropriate times.
+This example uses the run-cron script mentioned above, and relies on you to
+create the /etc/cron.* directories.
+
+prune-cronstamps
+: crond never removes any files from your cronstamps directory. If usernames
+are abandoned, or cron job names are abandoned, unused files will accumulate
+there. This simple cronjob will prune any cronstamp files older than three months.
+It will run weekly if placed in /etc/cron.d.
+
+crond.logrotate
+: This is an example to place in /etc/logrotate.d. This config file assumes you
+run crond using -L /var/log/crond.log. If you run crond using syslog instead (the default),
+you may prefer to configure the rotation of all your syslog-generated logs in a
+single config file.
+
+crontab.vim
+: This makes vim handle backup files in way that doesn't interfere with crontab's security
+model.
+
+
+BUG REPORTS, SUBMISSIONS
+------------------------
+
+Send any bug reports and source code changes to Jim Pryor:
+<profjim@jimpryor.net>.
+
+We aim to keep this program simple, secure, and bug-free, in preference to
+adding features. Those advanced features we have added recently (such as
+@noauto, FREQ= and AFTER= tags, advanced cron.update parsing) fit naturally
+into the existing codebase.
+
+Our goal is also to make this program compilable in as near to a C89-strict a
+manner as possible. Less-portable features we're aware of are described in the
+comments to defs.h. We'll reduce these dependencies as feasible. Do let us know
+if any of them are an obstacle to using crond on your platform.
+
+Changes to defs.h, whether to override defaults or to accommodate your platform,
+should be made by a combination of a -D option in the Makefile
+and an #ifdef for that option in defs.h. Don't rely on pre-definitions made
+by the C compiler.
+
+Prototypes for system functions should come from external include
+files and NOT from defs.h or any source file. If no prototype exists for a
+particular function, contact your vendor to get an update for your includes.
+
+Note that the source code, especially in regard to changing the
+effective user, is Linux specific (SysVish). We welcome any changes
+in regard to making the mechanism work with other platforms.
+
+
+CREDITS
+-------
+
+We use `concat`, a lightweight replacement for `asprintf`, in order to be more
+portable. This was written by Solar Designer and is in the public domain. See
+<http://www.openwall.com/popa3d/>.
+
Index: create-4.5-patch/dcron-4.5-new/crontab.c
===================================================================
--- create-4.5-patch/dcron-4.5-new/crontab.c (nonexistent)
+++ create-4.5-patch/dcron-4.5-new/crontab.c (revision 5)
@@ -0,0 +1,369 @@
+
+/*
+ * CRONTAB.C
+ *
+ * crontab [-u user] [-c dir] [-l|-e|-d|file|-]
+ * usually run as setuid root
+ * -u and -c options only work if getuid() == geteuid()
+ *
+ * Copyright 1994 Matthew Dillon (dillon@apollo.backplane.com)
+ * Copyright 2009-2011 James Pryor <profjim@jimpryor.net>
+ * May be distributed under the GNU General Public License
+ */
+
+#include "defs.h"
+
+Prototype void printlogf(int level, const char *ctl, ...);
+
+void Usage(void);
+int GetReplaceStream(const char *user, const char *file);
+void EditFile(const char *user, const char *file);
+
+const char *CDir = CRONTABS;
+int UserId;
+
+
+int
+main(int ac, char **av)
+{
+ enum { NONE, EDIT, LIST, REPLACE, DELETE } option = NONE;
+ struct passwd *pas;
+ char *repFile = NULL;
+ int repFd = 0;
+ int i;
+ char caller[SMALL_BUFFER]; /* user that ran program */
+
+ UserId = getuid();
+ if ((pas = getpwuid(UserId)) == NULL) {
+ perror("getpwuid");
+ exit(1);
+ }
+ /* [v]snprintf write at most size including \0; they'll null-terminate, even when they truncate */
+ /* return value >= size means result was truncated */
+ if (snprintf(caller, sizeof(caller), "%s", pas->pw_name) >= sizeof(caller)) {
+ printlogf(0, "username '%s' too long", caller);
+ exit(1);
+ }
+
+ opterr = 0;
+ while ((i=getopt(ac,av,"ledu:c:")) != -1) {
+ switch(i) {
+ case 'l':
+ if (option != NONE)
+ Usage();
+ else
+ option = LIST;
+ break;
+ case 'e':
+ if (option != NONE)
+ Usage();
+ else
+ option = EDIT;
+ break;
+ case 'd':
+ if (option != NONE)
+ Usage();
+ else
+ option = DELETE;
+ break;
+ case 'u':
+ /* getopt guarantees optarg != 0 here */
+ if (*optarg != 0 && getuid() == geteuid()) {
+ pas = getpwnam(optarg);
+ if (pas) {
+ UserId = pas->pw_uid;
+ /* paranoia */
+ if ((pas = getpwuid(UserId)) == NULL) {
+ perror("getpwuid");
+ exit(1);
+ }
+ } else {
+ printlogf(0, "user '%s' unknown", optarg);
+ exit(1);
+ }
+ } else {
+ printlogf(0, "-u option: superuser only");
+ exit(1);
+ }
+ break;
+ case 'c':
+ /* getopt guarantees optarg != 0 here */
+ if (*optarg != 0 && getuid() == geteuid()) {
+ CDir = optarg;
+ } else {
+ printlogf(0, "-c option: superuser only");
+ exit(1);
+ }
+ break;
+ default:
+ /* unrecognized -X */
+ option = NONE;
+ }
+ }
+
+ if (option == NONE && optind == ac - 1) {
+ if (av[optind][0] != '-') {
+ option = REPLACE;
+ repFile = av[optind];
+ optind++;
+ } else if (av[optind][1] == 0) {
+ option = REPLACE;
+ optind++;
+ }
+ }
+ if (option == NONE || optind != ac) {
+ Usage();
+ }
+
+ /*
+ * If there is a replacement file, obtain a secure descriptor to it.
+ */
+
+ if (repFile) {
+ repFd = GetReplaceStream(caller, repFile);
+ if (repFd < 0) {
+ printlogf(0, "unable to read replacement file %s", repFile);
+ exit(1);
+ }
+ }
+
+ /*
+ * Change directory to our crontab directory
+ */
+
+ if (chdir(CDir) < 0) {
+ printlogf(0, "cannot change dir to %s: %s", CDir, strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * Handle options as appropriate
+ */
+
+ switch(option) {
+ case LIST:
+ {
+ FILE *fi;
+ char buf[RW_BUFFER];
+
+ if ((fi = fopen(pas->pw_name, "r"))) {
+ while (fgets(buf, sizeof(buf), fi) != NULL)
+ fputs(buf, stdout);
+ fclose(fi);
+ } else {
+ fprintf(stderr, "no crontab for %s\n", pas->pw_name);
+ /* no error code */
+ }
+ }
+ break;
+ case EDIT:
+ {
+ FILE *fi;
+ int fd;
+ int n;
+ char tmp[] = TMPDIR "/crontab.XXXXXX";
+ char buf[RW_BUFFER];
+
+ /*
+ * Create temp file with perm 0600 and O_EXCL flag, ensuring that this call creates the file
+ * Read from fi for "$CDir/$USER", write to fd for temp file
+ * EditFile changes user if necessary, and runs editor on temp file
+ * Then we delete the temp file, keeping its fd as repFd
+ */
+ if ((fd = mkstemp(tmp)) >= 0) {
+ chown(tmp, getuid(), getgid());
+ if ((fi = fopen(pas->pw_name, "r"))) {
+ while ((n = fread(buf, 1, sizeof(buf), fi)) > 0)
+ write(fd, buf, n);
+ }
+ EditFile(caller, tmp);
+ remove(tmp);
+ lseek(fd, 0L, 0);
+ repFd = fd;
+ } else {
+ printlogf(0, "unable to create %s: %s", tmp, strerror(errno));
+ exit(1);
+ }
+
+ }
+ option = REPLACE;
+ /* fall through */
+ case REPLACE:
+ {
+ char buf[RW_BUFFER];
+ char path[SMALL_BUFFER];
+ int fd;
+ int n;
+
+ /*
+ * Read from repFd, write to fd for "$CDir/$USER.new"
+ */
+ snprintf(path, sizeof(path), "%s.new", pas->pw_name);
+ if ((fd = open(path, O_CREAT|O_TRUNC|O_APPEND|O_WRONLY, 0600)) >= 0) {
+ while ((n = read(repFd, buf, sizeof(buf))) > 0) {
+ write(fd, buf, n);
+ }
+ close(fd);
+ rename(path, pas->pw_name);
+ } else {
+ fprintf(stderr, "unable to create %s/%s: %s\n",
+ CDir,
+ path,
+ strerror(errno)
+ );
+ }
+ close(repFd);
+ }
+ break;
+ case DELETE:
+ remove(pas->pw_name);
+ break;
+ case NONE:
+ default:
+ break;
+ }
+
+ /*
+ * Bump notification file. Handle window where crond picks file up
+ * before we can write our entry out.
+ */
+
+ if (option == REPLACE || option == DELETE) {
+ FILE *fo;
+ struct stat st;
+
+ while ((fo = fopen(CRONUPDATE, "a"))) {
+ fprintf(fo, "%s\n", pas->pw_name);
+ fflush(fo);
+ if (fstat(fileno(fo), &st) != 0 || st.st_nlink != 0) {
+ fclose(fo);
+ break;
+ }
+ fclose(fo);
+ /* loop */
+ }
+ if (fo == NULL) {
+ fprintf(stderr, "unable to append to %s/%s\n", CDir, CRONUPDATE);
+ }
+ }
+ exit(0);
+ /* not reached */
+}
+
+void
+printlogf(int level, const char *ctl, ...)
+{
+ va_list va;
+ char buf[LOG_BUFFER];
+
+ va_start(va, ctl);
+ vsnprintf(buf, sizeof(buf), ctl, va);
+ write(2, buf, strlen(buf));
+ va_end(va);
+}
+
+void
+Usage(void)
+{
+ /*
+ * parse error
+ */
+ printf("crontab " VERSION "\n");
+ printf("crontab file [-u user] replace crontab from file\n");
+ printf("crontab - [-u user] replace crontab from stdin\n");
+ printf("crontab -l [-u user] list crontab\n");
+ printf("crontab -e [-u user] edit crontab\n");
+ printf("crontab -d [-u user] delete crontab\n");
+ printf("crontab -c dir <opts> specify crontab directory\n");
+ exit(2);
+}
+
+int
+GetReplaceStream(const char *user, const char *file)
+{
+ int filedes[2];
+ int pid;
+ int fd;
+ int n;
+ char buf[RW_BUFFER];
+
+ if (pipe(filedes) < 0) {
+ perror("pipe");
+ return(-1);
+ }
+ if ((pid = fork()) < 0) {
+ perror("fork");
+ return(-1);
+ }
+ if (pid > 0) {
+ /*
+ * PARENT
+ * Read from pipe[0], return it (or -1 if it's empty)
+ */
+
+ close(filedes[1]);
+ if (read(filedes[0], buf, 1) != 1) {
+ close(filedes[0]);
+ filedes[0] = -1;
+ }
+ return(filedes[0]);
+ }
+
+ /*
+ * CHILD
+ * Read from fd for "$file", write to pipe[1]
+ */
+
+ close(filedes[0]);
+
+ if (ChangeUser(user, NULL) < 0)
+ exit(0);
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ printlogf(0, "unable to open %s: %s", file, strerror(errno));
+ exit(1);
+ }
+ buf[0] = 0;
+ write(filedes[1], buf, 1);
+ while ((n = read(fd, buf, sizeof(buf))) > 0) {
+ write(filedes[1], buf, n);
+ }
+ exit(0);
+}
+
+void
+EditFile(const char *user, const char *file)
+{
+ int pid;
+
+ if ((pid = fork()) == 0) {
+ /*
+ * CHILD - change user and run editor on "$file"
+ */
+ const char *ptr;
+ char visual[SMALL_BUFFER];
+
+ if (ChangeUser(user, TMPDIR) < 0)
+ exit(0);
+ if ((ptr = getenv("EDITOR")) == NULL || strlen(ptr) >= sizeof(visual))
+ if ((ptr = getenv("VISUAL")) == NULL || strlen(ptr) >= sizeof(visual))
+ ptr = PATH_VI;
+
+ /* [v]snprintf write at most size including \0; they'll null-terminate, even when they truncate */
+ /* return value >= size means result was truncated */
+ if (snprintf(visual, sizeof(visual), "%s %s", ptr, file) < sizeof(visual))
+ execl("/bin/sh", "/bin/sh", "-c", visual, NULL);
+ printlogf(0, "couldn't exec %s", visual);
+ exit(1);
+ }
+ if (pid < 0) {
+ /*
+ * PARENT - failure
+ */
+ perror("fork");
+ exit(1);
+ }
+ waitpid(pid, NULL, 0);
+}
+
Index: create-4.5-patch/dcron-4.5-new/database.c
===================================================================
--- create-4.5-patch/dcron-4.5-new/database.c (nonexistent)
+++ create-4.5-patch/dcron-4.5-new/database.c (revision 5)
@@ -0,0 +1,1250 @@
+
+/*
+ * DATABASE.C
+ *
+ * Copyright 1994 Matthew Dillon (dillon@apollo.backplane.com)
+ * Copyright 2009-2011 James Pryor <profjim@jimpryor.net>
+ * May be distributed under the GNU General Public License
+ */
+
+#include "defs.h"
+
+Prototype void CheckUpdates(const char *dpath, const char *user_override, time_t t1, time_t t2);
+Prototype void SynchronizeDir(const char *dpath, const char *user_override, int initial_scan);
+Prototype void ReadTimestamps(const char *user);
+Prototype int TestJobs(time_t t1, time_t t2);
+Prototype int TestStartupJobs(void);
+Prototype int ArmJob(CronFile *file, CronLine *line, time_t t1, time_t t2);
+Prototype void RunJobs(void);
+Prototype int CheckJobs(void);
+
+void SynchronizeFile(const char *dpath, const char *fname, const char *uname);
+void DeleteFile(CronFile **pfile);
+char *ParseInterval(int *interval, char *ptr);
+char *ParseField(char *userName, char *ary, int modvalue, int off, int onvalue, const char **names, char *ptr);
+void FixDayDow(CronLine *line);
+
+CronFile *FileBase = NULL;
+
+const char *DowAry[] = {
+ "sun",
+ "mon",
+ "tue",
+ "wed",
+ "thu",
+ "fri",
+ "sat",
+
+ "Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+ NULL
+};
+
+const char *MonAry[] = {
+ "jan",
+ "feb",
+ "mar",
+ "apr",
+ "may",
+ "jun",
+ "jul",
+ "aug",
+ "sep",
+ "oct",
+ "nov",
+ "dec",
+
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+ NULL
+};
+
+const char *FreqAry[] = {
+ "noauto",
+ "reboot",
+ "hourly",
+ "daily",
+ "weekly",
+ "monthly",
+ "yearly",
+ NULL
+};
+
+/*
+ * Check the cron.update file in the specified directory. If user_override
+ * is NULL then the files in the directory belong to the user whose name is
+ * the file, otherwise they belong to the user_override user.
+ */
+void
+CheckUpdates(const char *dpath, const char *user_override, time_t t1, time_t t2)
+{
+ FILE *fi;
+ char buf[SMALL_BUFFER];
+ char *fname, *ptok, *job;
+ char *path;
+
+ if (!(path = concat(dpath, "/", CRONUPDATE, NULL))) {
+ errno = ENOMEM;
+ perror("CheckUpdates");
+ exit(1);
+ }
+ if ((fi = fopen(path, "r")) != NULL) {
+ remove(path);
+ printlogf(LOG_INFO, "reading %s/%s\n", dpath, CRONUPDATE);
+ while (fgets(buf, sizeof(buf), fi) != NULL) {
+ /*
+ * if buf has only sep chars, return NULL and point ptok at buf's terminating 0
+ * else return pointer to first non-sep of buf and
+ * if there's a following sep, overwrite it to 0 and point ptok to next char
+ * else point ptok at buf's terminating 0
+ */
+ fname = strtok_r(buf, " \t\n", &ptok);
+
+ if (user_override)
+ SynchronizeFile(dpath, fname, user_override);
+ else if (!getpwnam(fname))
+ printlogf(LOG_WARNING, "ignoring %s/%s (non-existent user)\n", dpath, fname);
+ else if (*ptok == 0 || *ptok == '\n') {
+ SynchronizeFile(dpath, fname, fname);
+ ReadTimestamps(fname);
+ } else {
+ /* if fname is followed by whitespace, we prod any following jobs */
+ CronFile *file = FileBase;
+ while (file) {
+ if (strcmp(file->cf_UserName, fname) == 0)
+ break;
+ file = file->cf_Next;
+ }
+ if (!file)
+ printlogf(LOG_WARNING, "unable to prod for user %s: no crontab\n", fname);
+ else {
+ CronLine *line;
+ /* calling strtok(ptok...) then strtok(NULL) is equiv to calling strtok_r(NULL,..&ptok) */
+ while ((job = strtok(ptok, " \t\n")) != NULL) {
+ time_t force = t2;
+ ptok = NULL;
+ if (*job == '!') {
+ force = (time_t)-1;
+ ++job;
+ }
+ line = file->cf_LineBase;
+ while (line) {
+ if (line->cl_JobName && strcmp(line->cl_JobName, job) == 0)
+ break;
+ line = line->cl_Next;
+ }
+ if (line)
+ ArmJob(file, line, t1, force);
+ else {
+ printlogf(LOG_WARNING, "unable to prod for user %s: unknown job %s\n", fname, job);
+ /* we can continue parsing this line, we just don't install any CronWaiter for the requested job */
+ }
+ }
+ }
+ }
+ }
+ fclose(fi);
+ }
+ free(path);
+}
+
+void
+SynchronizeDir(const char *dpath, const char *user_override, int initial_scan)
+{
+ CronFile **pfile;
+ CronFile *file;
+ struct dirent *den;
+ DIR *dir;
+ char *path;
+
+ if (DebugOpt)
+ printlogf(LOG_DEBUG, "Synchronizing %s\n", dpath);
+
+ /*
+ * Delete all database CronFiles for this directory. DeleteFile() will
+ * free *pfile and relink the *pfile pointer, or in the alternative will
+ * mark it as deleted.
+ */
+ pfile = &FileBase;
+ while ((file = *pfile) != NULL) {
+ if (file->cf_Deleted == 0 && strcmp(file->cf_DPath, dpath) == 0) {
+ DeleteFile(pfile);
+ } else {
+ pfile = &file->cf_Next;
+ }
+ }
+
+ /*
+ * Since we are resynchronizing the entire directory, remove the
+ * the CRONUPDATE file.
+ */
+ if (!(path = concat(dpath, "/", CRONUPDATE, NULL))) {
+ errno = ENOMEM;
+ perror("SynchronizeDir");
+ exit(1);
+ }
+ remove(path);
+ free(path);
+
+ /*
+ * Scan the specified directory
+ */
+ if ((dir = opendir(dpath)) != NULL) {
+ while ((den = readdir(dir)) != NULL) {
+ if (strchr(den->d_name, '.') != NULL)
+ continue;
+ if (strcmp(den->d_name, CRONUPDATE) == 0)
+ continue;
+ if (user_override) {
+ SynchronizeFile(dpath, den->d_name, user_override);
+ } else if (getpwnam(den->d_name)) {
+ SynchronizeFile(dpath, den->d_name, den->d_name);
+ } else {
+ printlogf(LOG_WARNING, "ignoring %s/%s (non-existent user)\n",
+ dpath, den->d_name);
+ }
+ }
+ closedir(dir);
+ } else {
+ if (initial_scan)
+ printlogf(LOG_ERR, "unable to scan directory %s\n", dpath);
+ /* softerror, do not exit the program */
+ }
+}
+
+
+void
+ReadTimestamps(const char *user)
+{
+ CronFile *file;
+ CronLine *line;
+ FILE *fi;
+ char buf[SMALL_BUFFER];
+ char *ptr;
+ struct tm tm = {0};
+ time_t sec, freq;
+
+ file = FileBase;
+ while (file != NULL) {
+ if (file->cf_Deleted == 0 && (!user || strcmp(user, file->cf_UserName) == 0)) {
+ line = file->cf_LineBase;
+ while (line != NULL) {
+ if (line->cl_Timestamp) {
+ if ((fi = fopen(line->cl_Timestamp, "r")) != NULL) {
+ if (fgets(buf, sizeof(buf), fi) != NULL) {
+ int fake = 0;
+ ptr = buf;
+ if (strncmp(buf, "after ", 6) == 0) {
+ fake = 1;
+ ptr += 6;
+ }
+ sec = (time_t)-1;
+ ptr = strptime(ptr, CRONSTAMP_FMT, &tm);
+ if (ptr && (*ptr == 0 || *ptr == '\n')) {
+ /* strptime uses current seconds when seconds not specified? anyway, we don't get round minutes */
+ tm.tm_sec = 0;
+ tm.tm_isdst = -1;
+ sec = mktime(&tm);
+ }
+ if (sec == (time_t)-1) {
+ printlogf(LOG_ERR, "unable to parse timestamp (user %s job %s)\n", file->cf_UserName, line->cl_JobName);
+ /* we continue checking other timestamps in this CronFile */
+ } else {
+ /* sec -= sec % 60; */
+ if (fake) {
+ line->cl_NotUntil = sec;
+ } else {
+ line->cl_LastRan = sec;
+ freq = (line->cl_Freq > 0) ? line->cl_Freq : line->cl_Delay;
+ /* if (line->cl_NotUntil < line->cl_LastRan + freq) */
+ line->cl_NotUntil = line->cl_LastRan + freq;
+ }
+ }
+ }
+ fclose(fi);
+ } else {
+ int succeeded = 0;
+ printlogf(LOG_NOTICE, "no timestamp found (user %s job %s)\n", file->cf_UserName, line->cl_JobName);
+ /* write a fake timestamp file so our initial NotUntil doesn't keep being reset every hour when crond does a SynchronizeDir */
+ if ((fi = fopen(line->cl_Timestamp, "w")) != NULL) {
+ if (strftime(buf, sizeof(buf), CRONSTAMP_FMT, localtime(&line->cl_NotUntil)))
+ if (fputs("after ", fi) >= 0)
+ if (fputs(buf,fi) >= 0)
+ succeeded = 1;
+ fclose(fi);
+ }
+ if (!succeeded)
+ printlogf(LOG_WARNING, "unable to write timestamp to %s (user %s %s)\n", line->cl_Timestamp, file->cf_UserName, line->cl_Description);
+ }
+ }
+ line = line->cl_Next;
+ }
+ }
+ file = file->cf_Next;
+ }
+}
+
+void
+SynchronizeFile(const char *dpath, const char *fileName, const char *userName)
+{
+ CronFile **pfile;
+ CronFile *file;
+ int maxEntries;
+ int maxLines;
+ char buf[RW_BUFFER]; /* max length for crontab lines */
+ char *path;
+ FILE *fi;
+
+ /*
+ * Limit entries
+ */
+ if (strcmp(userName, "root") == 0)
+ maxEntries = 65535;
+ else
+ maxEntries = MAXLINES;
+ maxLines = maxEntries * 10;
+
+ /*
+ * Delete any existing copy of this CronFile
+ */
+ pfile = &FileBase;
+ while ((file = *pfile) != NULL) {
+ if (file->cf_Deleted == 0 && strcmp(file->cf_DPath, dpath) == 0 &&
+ strcmp(file->cf_FileName, fileName) == 0
+ ) {
+ DeleteFile(pfile);
+ } else {
+ pfile = &file->cf_Next;
+ }
+ }
+
+ if (!(path = concat(dpath, "/", fileName, NULL))) {
+ errno = ENOMEM;
+ perror("SynchronizeFile");
+ exit(1);
+ }
+ if ((fi = fopen(path, "r")) != NULL) {
+ struct stat sbuf;
+
+ if (fstat(fileno(fi), &sbuf) == 0 && sbuf.st_uid == DaemonUid) {
+ CronFile *file = calloc(1, sizeof(CronFile));
+ CronLine **pline;
+ time_t tnow = time(NULL);
+ tnow -= tnow % 60;
+
+ file->cf_UserName = strdup(userName);
+ file->cf_FileName = strdup(fileName);
+ file->cf_DPath = strdup(dpath);
+ pline = &file->cf_LineBase;
+
+ /* fgets reads at most size-1 chars until \n or EOF, then adds a\0; \n if present is stored in buf */
+ while (fgets(buf, sizeof(buf), fi) != NULL && --maxLines) {
+ CronLine line;
+ char *ptr = buf;
+ int len;
+
+ while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
+ ++ptr;
+
+ len = strlen(ptr);
+ if (len && ptr[len-1] == '\n')
+ ptr[--len] = 0;
+
+ if (*ptr == 0 || *ptr == '#')
+ continue;
+
+ if (--maxEntries == 0)
+ break;
+
+ memset(&line, 0, sizeof(line));
+
+ if (DebugOpt)
+ printlogf(LOG_DEBUG, "User %s Entry %s\n", userName, buf);
+
+ if (*ptr == '@') {
+ /*
+ * parse @hourly, etc
+ */
+ int j;
+ line.cl_Delay = -1;
+ ptr += 1;
+ for (j = 0; FreqAry[j]; ++j) {
+ if (strncmp(ptr, FreqAry[j], strlen(FreqAry[j])) == 0) {
+ break;
+ }
+ }
+ if (FreqAry[j]) {
+ ptr += strlen(FreqAry[j]);
+ switch(j) {
+ case 0:
+ /* noauto */
+ line.cl_Freq = -2;
+ line.cl_Delay = 0;
+ break;
+ case 1:
+ /* reboot */
+ line.cl_Freq = -1;
+ line.cl_Delay = 0;
+ break;
+ case 2:
+ line.cl_Freq = HOURLY_FREQ;
+ break;
+ case 3:
+ line.cl_Freq = DAILY_FREQ;
+ break;
+ case 4:
+ line.cl_Freq = WEEKLY_FREQ;
+ break;
+ case 5:
+ line.cl_Freq = MONTHLY_FREQ;
+ break;
+ case 6:
+ line.cl_Freq = YEARLY_FREQ;
+ break;
+ /* else line.cl_Freq will remain 0 */
+ }
+ }
+
+ if (!line.cl_Freq || (*ptr != ' ' && *ptr != '\t')) {
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: %s\n", userName, buf);
+ continue;
+ }
+
+ if (line.cl_Delay < 0) {
+ /*
+ * delays on @daily, @hourly, etc are 1/20 of the frequency
+ * so they don't all start at once
+ * this also affects how they behave when the job returns EAGAIN
+ */
+ line.cl_Delay = line.cl_Freq / 20;
+ line.cl_Delay -= line.cl_Delay % 60;
+ if (line.cl_Delay == 0)
+ line.cl_Delay = 60;
+ /* all minutes are permitted */
+ for (j=0; j<60; ++j)
+ line.cl_Mins[j] = 1;
+ for (j=0; j<24; ++j)
+ line.cl_Hrs[j] = 1;
+ for (j=1; j<32; ++j)
+ /* days are numbered 1..31 */
+ line.cl_Days[j] = 1;
+ for (j=0; j<12; ++j)
+ line.cl_Mons[j] = 1;
+ }
+
+ while (*ptr == ' ' || *ptr == '\t')
+ ++ptr;
+
+ } else {
+ /*
+ * parse date ranges
+ */
+
+ ptr = ParseField(file->cf_UserName, line.cl_Mins, 60, 0, 1,
+ NULL, ptr);
+ ptr = ParseField(file->cf_UserName, line.cl_Hrs, 24, 0, 1,
+ NULL, ptr);
+ ptr = ParseField(file->cf_UserName, line.cl_Days, 32, 0, 1,
+ NULL, ptr);
+ ptr = ParseField(file->cf_UserName, line.cl_Mons, 12, -1, 1,
+ MonAry, ptr);
+ ptr = ParseField(file->cf_UserName, line.cl_Dow, 7, 0, 31,
+ DowAry, ptr);
+ /*
+ * check failure
+ */
+
+ if (ptr == NULL)
+ continue;
+
+ /*
+ * fix days and dow - if one is not * and the other
+ * is *, the other is set to 0, and vise-versa
+ */
+
+ FixDayDow(&line);
+ }
+
+ /* check for ID=... and AFTER=... and FREQ=... */
+ do {
+ if (strncmp(ptr, ID_TAG, strlen(ID_TAG)) == 0) {
+ if (line.cl_JobName) {
+ /* only assign ID_TAG once */
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: repeated %s\n", userName, ptr);
+ ptr = NULL;
+ } else {
+ ptr += strlen(ID_TAG);
+ /*
+ * name = strsep(&ptr, seps):
+ * return name = ptr, and if ptr contains sep chars, overwrite first with 0 and point ptr to next char
+ * else set ptr=NULL
+ */
+ if (!(line.cl_Description = concat("job ", strsep(&ptr, " \t"), NULL))) {
+ errno = ENOMEM;
+ perror("SynchronizeFile");
+ exit(1);
+ }
+ line.cl_JobName = line.cl_Description + 4;
+ if (!ptr)
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: no command after %s%s\n", userName, ID_TAG, line.cl_JobName);
+ }
+ } else if (strncmp(ptr, FREQ_TAG, strlen(FREQ_TAG)) == 0) {
+ if (line.cl_Freq) {
+ /* only assign FREQ_TAG once */
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: repeated %s\n", userName, ptr);
+ ptr = NULL;
+ } else {
+ char *base = ptr;
+ ptr += strlen(FREQ_TAG);
+ ptr = ParseInterval(&line.cl_Freq, ptr);
+ if (ptr && *ptr == '/')
+ ptr = ParseInterval(&line.cl_Delay, ++ptr);
+ else
+ line.cl_Delay = line.cl_Freq;
+ if (!ptr) {
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: %s\n", userName, base);
+ } else if (*ptr != ' ' && *ptr != '\t') {
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: no command after %s\n", userName, base);
+ ptr = NULL;
+ }
+ }
+ } else if (strncmp(ptr, WAIT_TAG, strlen(WAIT_TAG)) == 0) {
+ if (line.cl_Waiters) {
+ /* only assign WAIT_TAG once */
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: repeated %s\n", userName, ptr);
+ ptr = NULL;
+ } else {
+ short more = 1;
+ char *name;
+ ptr += strlen(WAIT_TAG);
+ do {
+ CronLine *job, **pjob;
+ if (strcspn(ptr,",") < strcspn(ptr," \t"))
+ name = strsep(&ptr, ",");
+ else {
+ more = 0;
+ name = strsep(&ptr, " \t");
+ }
+ if (!ptr || *ptr == 0) {
+ /* unexpectedly this was the last token in buf; so abort */
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: no command after %s%s\n", userName, WAIT_TAG, name);
+ ptr = NULL;
+ } else {
+ int waitfor = 0;
+ char *w, *wsave;
+ if ((w = strchr(name, '/')) != NULL) {
+ wsave = w++;
+ w = ParseInterval(&waitfor, w);
+ if (!w || *w != 0) {
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: %s%s\n", userName, WAIT_TAG, name);
+ ptr = NULL;
+ } else
+ /* truncate name */
+ *wsave = 0;
+ }
+ if (ptr) {
+ /* look for a matching CronLine */
+ pjob = &file->cf_LineBase;
+ while ((job = *pjob) != NULL) {
+ if (job->cl_JobName && strcmp(job->cl_JobName, name) == 0) {
+ CronWaiter *waiter = malloc(sizeof(CronWaiter));
+ CronNotifier *notif = malloc(sizeof(CronNotifier));
+ waiter->cw_Flag = -1;
+ waiter->cw_MaxWait = waitfor;
+ waiter->cw_NotifLine = job;
+ waiter->cw_Notifier = notif;
+ waiter->cw_Next = line.cl_Waiters; /* add to head of line.cl_Waiters */
+ line.cl_Waiters = waiter;
+ notif->cn_Waiter = waiter;
+ notif->cn_Next = job->cl_Notifs; /* add to head of job->cl_Notifs */
+ job->cl_Notifs = notif;
+ break;
+ } else
+ pjob = &job->cl_Next;
+ }
+ if (!job) {
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: unknown job %s\n", userName, name);
+ /* we can continue parsing this line, we just don't install any CronWaiter for the requested job */
+ }
+ }
+ }
+ } while (ptr && more);
+ }
+ } else
+ break;
+ if (!ptr)
+ break;
+ while (*ptr == ' ' || *ptr == '\t')
+ ++ptr;
+ } while (!line.cl_JobName || !line.cl_Waiters || !line.cl_Freq);
+
+ if (line.cl_JobName && (!ptr || *line.cl_JobName == 0)) {
+ /* we're aborting, or ID= was empty */
+ free(line.cl_Description);
+ line.cl_Description = NULL;
+ line.cl_JobName = NULL;
+ }
+ if (ptr && line.cl_Delay > 0 && !line.cl_JobName) {
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: writing timestamp requires job %s to be named\n", userName, ptr);
+ ptr = NULL;
+ }
+ if (!ptr) {
+ /* couldn't parse so we abort; free any cl_Waiters */
+ if (line.cl_Waiters) {
+ CronWaiter **pwaiters, *waiters;
+ pwaiters = &line.cl_Waiters;
+ while ((waiters = *pwaiters) != NULL) {
+ *pwaiters = waiters->cw_Next;
+ /* leave the Notifier allocated but disabled */
+ waiters->cw_Notifier->cn_Waiter = NULL;
+ free(waiters);
+ }
+ }
+ continue;
+ }
+ /* now we've added any ID=... or AFTER=... */
+
+ /*
+ * copy command string
+ */
+ line.cl_Shell = strdup(ptr);
+
+ if (line.cl_Delay > 0) {
+ if (!(line.cl_Timestamp = concat(TSDir, "/", userName, ".", line.cl_JobName, NULL))) {
+ errno = ENOMEM;
+ perror("SynchronizeFile");
+ exit(1);
+ }
+ line.cl_NotUntil = tnow + line.cl_Delay;
+ }
+
+ if (line.cl_JobName) {
+ if (DebugOpt)
+ printlogf(LOG_DEBUG, " Command %s Job %s\n", line.cl_Shell, line.cl_JobName);
+ } else {
+ /* when cl_JobName is NULL, we point cl_Description to cl_Shell */
+ line.cl_Description = line.cl_Shell;
+ if (DebugOpt)
+ printlogf(LOG_DEBUG, " Command %s\n", line.cl_Shell);
+ }
+
+ *pline = calloc(1, sizeof(CronLine));
+ /* copy working CronLine to newly allocated one */
+ **pline = line;
+
+ pline = &((*pline)->cl_Next);
+ }
+
+ *pline = NULL;
+
+ file->cf_Next = FileBase;
+ FileBase = file;
+
+ if (maxLines == 0 || maxEntries == 0)
+ printlogf(LOG_WARNING, "maximum number of lines reached for user %s\n", userName);
+ }
+ fclose(fi);
+ }
+ free(path);
+}
+
+char *
+ParseInterval(int *interval, char *ptr)
+{
+ int n = 0;
+ if (ptr && *ptr >= '0' && *ptr <= '9' && (n = strtol(ptr, &ptr, 10)) > 0)
+ switch (*ptr) {
+ case 'm':
+ n *= 60;
+ break;
+ case 'h':
+ n *= HOURLY_FREQ;
+ break;
+ case 'd':
+ n *= DAILY_FREQ;
+ break;
+ case 'w':
+ n *= WEEKLY_FREQ;
+ break;
+ default:
+ n = 0;
+ }
+ if (n > 0) {
+ *interval = n;
+ return (ptr+1);
+ } else
+ return (NULL);
+}
+
+char *
+ParseField(char *user, char *ary, int modvalue, int off, int onvalue, const char **names, char *ptr)
+{
+ char *base = ptr;
+ int n1 = -1;
+ int n2 = -1;
+
+ if (base == NULL)
+ return (NULL);
+
+ while (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
+ int skip = 0;
+
+ /*
+ * Handle numeric digit or symbol or '*'
+ */
+
+ if (*ptr == '*') {
+ n1 = 0; /* everything will be filled */
+ n2 = modvalue - 1;
+ skip = 1;
+ ++ptr;
+ } else if (*ptr >= '0' && *ptr <= '9') {
+ if (n1 < 0)
+ n1 = strtol(ptr, &ptr, 10) + off;
+ else
+ n2 = strtol(ptr, &ptr, 10) + off;
+ skip = 1;
+ } else if (names) {
+ int i;
+
+ for (i = 0; names[i]; ++i) {
+ if (strncmp(ptr, names[i], strlen(names[i])) == 0) {
+ break;
+ }
+ }
+ if (names[i]) {
+ ptr += strlen(names[i]);
+ if (n1 < 0)
+ n1 = i;
+ else
+ n2 = i;
+ skip = 1;
+ }
+ }
+
+ /*
+ * handle optional range '-'
+ */
+
+ if (skip == 0) {
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: %s\n", user, base);
+ return(NULL);
+ }
+ if (*ptr == '-' && n2 < 0) {
+ ++ptr;
+ continue;
+ }
+
+ /*
+ * collapse single-value ranges, handle skipmark, and fill
+ * in the character array appropriately.
+ */
+
+ if (n2 < 0)
+ n2 = n1;
+
+ n2 = n2 % modvalue;
+
+ if (*ptr == '/')
+ skip = strtol(ptr + 1, &ptr, 10);
+
+ /*
+ * fill array, using a failsafe is the easiest way to prevent
+ * an endless loop
+ */
+
+ {
+ int s0 = 1;
+ int failsafe = 1024;
+
+ --n1;
+ do {
+ n1 = (n1 + 1) % modvalue;
+
+ if (--s0 == 0) {
+ ary[n1] = onvalue;
+ s0 = skip;
+ }
+ } while (n1 != n2 && --failsafe);
+
+ if (failsafe == 0) {
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: %s\n", user, base);
+ return(NULL);
+ }
+ }
+ if (*ptr != ',')
+ break;
+ ++ptr;
+ n1 = -1;
+ n2 = -1;
+ }
+
+ if (*ptr != ' ' && *ptr != '\t' && *ptr != '\n') {
+ printlogf(LOG_WARNING, "failed parsing crontab for user %s: %s\n", user, base);
+ return(NULL);
+ }
+
+ while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
+ ++ptr;
+
+ if (DebugOpt) {
+ int i;
+
+ for (i = 0; i < modvalue; ++i)
+ if (modvalue == 7)
+ printlogf(LOG_DEBUG, "%2x ", ary[i]);
+ else
+ printlogf(LOG_DEBUG, "%d", ary[i]);
+ printlogf(LOG_DEBUG, "\n");
+ }
+
+ return(ptr);
+}
+
+void
+FixDayDow(CronLine *line)
+{
+ unsigned short i,j;
+ short weekUsed = 0;
+ short daysUsed = 0;
+
+ for (i = 0; i < arysize(line->cl_Dow); ++i) {
+ if (line->cl_Dow[i] == 0) {
+ weekUsed = 1;
+ break;
+ }
+ }
+ for (i = 0; i < arysize(line->cl_Days); ++i) {
+ if (line->cl_Days[i] == 0) {
+ if (weekUsed) {
+ if (!daysUsed) {
+ daysUsed = 1;
+ /* change from "every Mon" to "ith Mon"
+ * 6th,7th... Dow are treated as 1st,2nd... */
+ for (j = 0; j < arysize(line->cl_Dow); ++j) {
+ line->cl_Dow[j] &= 1 << (i-1)%5;
+ }
+ } else {
+ /* change from "nth Mon" to "nth or ith Mon" */
+ for (j = 0; j < arysize(line->cl_Dow); ++j) {
+ if (line->cl_Dow[j])
+ line->cl_Dow[j] |= 1 << (i-1)%5;
+ }
+ }
+ /* continue cycling through cl_Days */
+ }
+ else {
+ daysUsed = 1;
+ break;
+ }
+ }
+ }
+ if (weekUsed) {
+ memset(line->cl_Days, 0, sizeof(line->cl_Days));
+ }
+ if (daysUsed && !weekUsed) {
+ memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
+ }
+}
+
+/*
+ * DeleteFile() - destroy a CronFile.
+ *
+ * The CronFile (*pfile) is destroyed if possible, and marked cf_Deleted
+ * if there are still active processes running on it. *pfile is relinked
+ * on success.
+ */
+void
+DeleteFile(CronFile **pfile)
+{
+ CronFile *file = *pfile;
+ CronLine **pline = &file->cf_LineBase;
+ CronLine *line;
+ CronWaiter **pwaiters, *waiters;
+ CronNotifier **pnotifs, *notifs;
+
+ file->cf_Running = 0;
+ file->cf_Deleted = 1;
+
+ while ((line = *pline) != NULL) {
+ if (line->cl_Pid > 0) {
+ file->cf_Running = 1;
+ pline = &line->cl_Next;
+ } else {
+ *pline = line->cl_Next;
+ free(line->cl_Shell);
+
+ if (line->cl_JobName)
+ /* this frees both cl_Description and cl_JobName
+ * if cl_JobName is NULL, Description pointed to ch_Shell, which was already freed
+ */
+ free(line->cl_Description);
+ if (line->cl_Timestamp)
+ free(line->cl_Timestamp);
+
+ pnotifs = &line->cl_Notifs;
+ while ((notifs = *pnotifs) != NULL) {
+ *pnotifs = notifs->cn_Next;
+ if (notifs->cn_Waiter) {
+ notifs->cn_Waiter->cw_NotifLine = NULL;
+ notifs->cn_Waiter->cw_Notifier = NULL;
+ }
+ free(notifs);
+ }
+ pwaiters = &line->cl_Waiters;
+ while ((waiters = *pwaiters) != NULL) {
+ *pwaiters = waiters->cw_Next;
+ if (waiters->cw_Notifier)
+ waiters->cw_Notifier->cn_Waiter = NULL;
+ free(waiters);
+ }
+
+ free(line);
+ }
+ }
+ if (file->cf_Running == 0) {
+ *pfile = file->cf_Next;
+ free(file->cf_DPath);
+ free(file->cf_FileName);
+ free(file->cf_UserName);
+ free(file);
+ }
+}
+
+
+/*
+ * TestJobs()
+ *
+ * determine which jobs need to be run. Under normal conditions, the
+ * period is about a minute (one scan). Worst case it will be one
+ * hour (60 scans).
+ */
+
+int
+TestJobs(time_t t1, time_t t2)
+{
+ short nJobs = 0;
+ time_t t;
+ CronFile *file;
+ CronLine *line;
+
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (file->cf_Deleted)
+ continue;
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ struct CronWaiter *waiter;
+
+ if (line->cl_Pid == -2) {
+ /* can job stop waiting? */
+ int ready = 1;
+ waiter = line->cl_Waiters;
+ while (waiter != NULL) {
+ if (waiter->cw_Flag > 0) {
+ /* notifier exited unsuccessfully */
+ ready = 2;
+ break;
+ } else if (waiter->cw_Flag < 0)
+ /* still waiting, notifier hasn't run to completion */
+ ready = 0;
+ waiter = waiter->cw_Next;
+ }
+ if (ready == 2) {
+ if (DebugOpt)
+ printlogf(LOG_DEBUG, "cancelled waiting: user %s %s\n", file->cf_UserName, line->cl_Description);
+ line->cl_Pid = 0;
+ } else if (ready) {
+ if (DebugOpt)
+ printlogf(LOG_DEBUG, "finished waiting: user %s %s\n", file->cf_UserName, line->cl_Description);
+ nJobs += ArmJob(file, line, 0, -1);
+ /*
+ if (line->cl_NotUntil)
+ line->cl_NotUntil = t2;
+ */
+ }
+ }
+ }
+ }
+
+ /*
+ * Find jobs > t1 and <= t2
+ */
+
+ for (t = t1 - t1 % 60; t <= t2; t += 60) {
+ if (t > t1) {
+ struct tm *tp = localtime(&t);
+
+ unsigned short n_wday = (tp->tm_mday - 1)%7 + 1;
+ if (n_wday >= 4) {
+ struct tm tnext = *tp;
+ tnext.tm_mday += 7;
+ if (mktime(&tnext) != (time_t)-1 && tnext.tm_mon != tp->tm_mon)
+ n_wday |= 16; /* last dow in month is always recognized as 5th */
+ }
+
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (file->cf_Deleted)
+ continue;
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ if ((line->cl_Pid == -2 || line->cl_Pid == 0) && (line->cl_Freq == 0 || (line->cl_Freq > 0 && t2 >= line->cl_NotUntil))) {
+ /* (re)schedule job? */
+ if (line->cl_Mins[tp->tm_min] &&
+ line->cl_Hrs[tp->tm_hour] &&
+ (line->cl_Days[tp->tm_mday] || (n_wday && line->cl_Dow[tp->tm_wday]) ) &&
+ line->cl_Mons[tp->tm_mon]
+ ) {
+ if (line->cl_NotUntil)
+ line->cl_NotUntil = t2 - t2 % 60 + line->cl_Delay; /* save what minute this job was scheduled/started waiting, plus cl_Delay */
+ nJobs += ArmJob(file, line, t1, t2);
+ }
+ }
+ }
+ }
+ }
+ }
+ return(nJobs);
+}
+
+/*
+ * ArmJob: if t2 is (time_t)-1, we force-schedule the job without any waiting
+ * else it will wait on any of its declared notifiers who will run <= t2 + cw_MaxWait
+ */
+
+int
+ArmJob(CronFile *file, CronLine *line, time_t t1, time_t t2)
+{
+ struct CronWaiter *waiter;
+ if (line->cl_Pid > 0) {
+ printlogf(LOG_NOTICE, "process already running (%d): user %s %s\n",
+ line->cl_Pid,
+ file->cf_UserName,
+ line->cl_Description
+ );
+ } else if (t2 == -1 && line->cl_Pid != -1) {
+ line->cl_Pid = -1;
+ file->cf_Ready = 1;
+ return 1;
+ } else if (line->cl_Pid == 0) {
+ /* arming a waiting job (cl_Pid == -2) without forcing has no effect */
+ line->cl_Pid = -1;
+ /* if we have any waiters, zero them and arm cl_Pid=-2 */
+ waiter = line->cl_Waiters;
+ while (waiter != NULL) {
+ /* check if notifier will run <= t2 + cw_Max_Wait? */
+ if (!waiter->cw_NotifLine)
+ /* notifier deleted */
+ waiter->cw_Flag = 0;
+ else if (waiter->cw_NotifLine->cl_Pid != 0) {
+ /* if notifier is armed, or waiting, or running, we wait for it */
+ waiter->cw_Flag = -1;
+ line->cl_Pid = -2;
+ } else if (waiter->cw_NotifLine->cl_Freq < 0) {
+ /* arm any @noauto or @reboot jobs we're waiting on */
+ ArmJob(file, waiter->cw_NotifLine, t1, t2);
+ waiter->cw_Flag = -1;
+ line->cl_Pid = -2;
+ } else {
+ time_t t;
+ if (waiter->cw_MaxWait == 0)
+ /* when no MaxWait interval specified, we always wait */
+ waiter->cw_Flag = -1;
+ else if (waiter->cw_NotifLine->cl_Freq == 0 || (waiter->cw_NotifLine->cl_Freq > 0 && t2 + waiter->cw_MaxWait >= waiter->cw_NotifLine->cl_NotUntil)) {
+ /* default is don't wait */
+ waiter->cw_Flag = 0;
+ for (t = t1 - t1 % 60; t <= t2; t += 60) {
+ if (t > t1) {
+ struct tm *tp = localtime(&t);
+
+ unsigned short n_wday = (tp->tm_mday - 1)%7 + 1;
+ if (n_wday >= 4) {
+ struct tm tnext = *tp;
+ tnext.tm_mday += 7;
+ if (mktime(&tnext) != (time_t)-1 && tnext.tm_mon != tp->tm_mon)
+ n_wday |= 16; /* last dow in month is always recognized as 5th */
+ }
+ if (line->cl_Mins[tp->tm_min] &&
+ line->cl_Hrs[tp->tm_hour] &&
+ (line->cl_Days[tp->tm_mday] || (n_wday && line->cl_Dow[tp->tm_wday]) ) &&
+ line->cl_Mons[tp->tm_mon]
+ ) {
+ /* notifier will run soon enough, we wait for it */
+ waiter->cw_Flag = -1;
+ line->cl_Pid = -2;
+ break;
+ }
+ }
+ }
+ }
+ }
+ waiter = waiter->cw_Next;
+ }
+ if (line->cl_Pid == -1) {
+ /* job is ready to run */
+ file->cf_Ready = 1;
+ if (DebugOpt)
+ printlogf(LOG_DEBUG, "scheduled: user %s %s\n",
+ file->cf_UserName,
+ line->cl_Description
+ );
+ return 1;
+ } else if (DebugOpt)
+ printlogf(LOG_DEBUG, "waiting: user %s %s\n",
+ file->cf_UserName,
+ line->cl_Description
+ );
+ }
+ return 0;
+}
+
+int
+TestStartupJobs(void)
+{
+ short nJobs = 0;
+ time_t t1 = time(NULL);
+ CronFile *file;
+ CronLine *line;
+
+ t1 = t1 - t1 % 60 + 60;
+
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (DebugOpt)
+ printlogf(LOG_DEBUG, "TestStartup for FILE %s/%s USER %s:\n",
+ file->cf_DPath, file->cf_FileName, file->cf_UserName);
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ struct CronWaiter *waiter;
+ if (DebugOpt) {
+ if (line->cl_JobName)
+ printlogf(LOG_DEBUG, " LINE %s JOB %s\n", line->cl_Shell, line->cl_JobName);
+ else
+ printlogf(LOG_DEBUG, " LINE %s\n", line->cl_Shell);
+ }
+
+ if (line->cl_Freq == -1) {
+ /* freq is @reboot */
+
+ line->cl_Pid = -1;
+ /* if we have any waiters, reset them and arm Pid = -2 */
+ waiter = line->cl_Waiters;
+ while (waiter != NULL) {
+ waiter->cw_Flag = -1;
+ line->cl_Pid = -2;
+ /* we only arm @noauto jobs we're waiting on, not other @reboot jobs */
+ if (waiter->cw_NotifLine && waiter->cw_NotifLine->cl_Freq == -2)
+ ArmJob(file, waiter->cw_NotifLine, t1, t1+60);
+ waiter = waiter->cw_Next;
+ }
+ if (line->cl_Pid == -1) {
+ /* job is ready to run */
+ file->cf_Ready = 1;
+ ++nJobs;
+ if (DebugOpt)
+ printlogf(LOG_DEBUG, " scheduled: %s\n", line->cl_Description);
+ } else if (DebugOpt)
+ printlogf(LOG_DEBUG, " waiting: %s\n", line->cl_Description);
+
+ }
+
+ } /* for line */
+ }
+ return(nJobs);
+}
+
+void
+RunJobs(void)
+{
+ CronFile *file;
+ CronLine *line;
+
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (file->cf_Ready) {
+ file->cf_Ready = 0;
+
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ if (line->cl_Pid == -1) {
+
+ RunJob(file, line);
+
+ printlogf(LOG_INFO, "FILE %s/%s USER %s PID %3d %s\n",
+ file->cf_DPath,
+ file->cf_FileName,
+ file->cf_UserName,
+ line->cl_Pid,
+ line->cl_Description
+ );
+ if (line->cl_Pid < 0)
+ /* QUESTION how could this happen? RunJob will leave cl_Pid set to 0 or the actual pid */
+ file->cf_Ready = 1;
+ else if (line->cl_Pid > 0)
+ file->cf_Running = 1;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * CheckJobs() - check for job completion
+ *
+ * Check for job completion, return number of CronFiles still running after
+ * all done.
+ */
+
+int
+CheckJobs(void)
+{
+ CronFile *file;
+ CronLine *line;
+ int nStillRunning = 0;
+
+ for (file = FileBase; file; file = file->cf_Next) {
+ if (file->cf_Running) {
+ file->cf_Running = 0;
+
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ if (line->cl_Pid > 0) {
+ int status;
+ int r = waitpid(line->cl_Pid, &status, WNOHANG);
+
+ /* waitpid returns -1 for error, 0 if cl_Pid still running, cl_Pid if it's dead */
+
+ if (r < 0 || r == line->cl_Pid) {
+ if (r > 0 && WIFEXITED(status))
+ status = WEXITSTATUS(status);
+ else
+ status = 1;
+ EndJob(file, line, status);
+
+ } else if (r == 0) {
+ file->cf_Running = 1;
+ }
+ }
+ }
+ nStillRunning += file->cf_Running;
+ }
+ /* For the purposes of this check, increase the "still running" counter if a file has lines that are waiting */
+ if (file->cf_Running == 0) {
+ for (line = file->cf_LineBase; line; line = line->cl_Next) {
+ if (line->cl_Pid == -2) {
+ nStillRunning += 1;
+ break;
+ }
+ }
+ }
+ }
+ return(nStillRunning);
+}
+
Index: create-4.5-patch/dcron-4.5-new/defs.h
===================================================================
--- create-4.5-patch/dcron-4.5-new/defs.h (nonexistent)
+++ create-4.5-patch/dcron-4.5-new/defs.h (revision 5)
@@ -0,0 +1,165 @@
+
+/*
+ * DEFS.H
+ *
+ * Copyright 1994-1998 Matthew Dillon (dillon@backplane.com)
+ * Copyright 2009-2011 James Pryor <profjim@jimpryor.net>
+ * May be distributed under the GNU General Public License
+ */
+
+/*
+ * portability issues
+ * 0. gcc defaults to _BSD_SOURCE and _POSIX_SOURCE
+ * 1. need _POSIX_SOURCE or _XOPEN_SOURCE for getopt, fileno, sigaction
+ * 2. need _XOPEN_SOURCE for strptime
+ * 3. need _BSD_SOURCE for setenv, mk{d,s}temp, [v]snprintf, initgroups, strsep, strdup, setre{u,g}id, gethostname, perror
+ * 4. use concat.c instead of requiring asprintf / _GNU_SOURCE
+ */
+
+#define _XOPEN_SOURCE 1
+#define _DEFAULT_SOURCE 1
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <pwd.h>
+#ifndef S_SPLINT_S
+#include <unistd.h>
+#endif
+#include <grp.h>
+#include <syslog.h>
+#include <signal.h>
+#include <getopt.h>
+#include <err.h>
+#include <limits.h>
+
+#include <time.h>
+#include <string.h>
+#include <stdio.h>
+
+#define Prototype extern
+#define arysize(ary) (sizeof(ary)/sizeof((ary)[0]))
+
+#ifndef SCRONTABS
+#define SCRONTABS "/etc/cron.d"
+#endif
+#ifndef CRONTABS
+#define CRONTABS "/var/spool/cron/crontabs"
+#endif
+#ifndef CRONSTAMPS
+#define CRONSTAMPS "/var/spool/cron/cronstamps"
+#endif
+#ifndef LOG_IDENT
+#define LOG_IDENT "crond"
+#endif
+#ifndef TIMESTAMP_FMT
+#define TIMESTAMP_FMT "%b %e %H:%M:%S"
+#endif
+
+#ifndef LOG_LEVEL
+#define LOG_LEVEL LOG_NOTICE
+#endif
+#ifndef CRONSTAMP_FMT
+#define CRONSTAMP_FMT "%Y-%m-%d %H:%M"
+#endif
+#ifndef CRONUPDATE
+#define CRONUPDATE "cron.update"
+#endif
+#ifndef TMPDIR
+#define TMPDIR "/var/spool/cron"
+#endif
+
+#ifndef SENDMAIL
+#define SENDMAIL "/usr/sbin/sendmail"
+#endif
+#ifndef SENDMAIL_ARGS
+#define SENDMAIL_ARGS "-t", "-oem", "-i"
+#endif
+#ifndef PATH_VI
+#define PATH_VI "/usr/bin/vi" /* location of vi */
+#endif
+
+#ifndef ID_TAG
+#define ID_TAG "ID="
+#endif
+#ifndef WAIT_TAG
+#define WAIT_TAG "AFTER="
+#endif
+#ifndef FREQ_TAG
+#define FREQ_TAG "FREQ="
+#endif
+
+#define HOURLY_FREQ 60 * 60
+#define DAILY_FREQ 24 * HOURLY_FREQ
+#define WEEKLY_FREQ 7 * DAILY_FREQ
+#define MONTHLY_FREQ 30 * DAILY_FREQ
+#define YEARLY_FREQ 365 * DAILY_FREQ
+
+#define LOGHEADER TIMESTAMP_FMT " %%s " LOG_IDENT ": "
+#define LOCALE_LOGHEADER "%c %%s " LOG_IDENT ": "
+
+/* Limits */
+#define MAXOPEN 256 /* close fds < this limit */
+#define MAXLINES 256 /* max lines in non-root crontabs */
+#define SMALL_BUFFER 256
+#define RW_BUFFER 1024
+#define LOG_BUFFER 2048 /* max size of log line */
+
+
+
+
+typedef struct CronFile {
+ struct CronFile *cf_Next;
+ struct CronLine *cf_LineBase;
+ char *cf_DPath; /* Directory path to cronfile */
+ char *cf_FileName; /* Name of cronfile */
+ char *cf_UserName; /* username to execute jobs as */
+ int cf_Ready; /* bool: one or more jobs ready */
+ int cf_Running; /* bool: one or more jobs running */
+ int cf_Deleted; /* marked for deletion, ignore */
+} CronFile;
+
+typedef struct CronLine {
+ struct CronLine *cl_Next;
+ char *cl_Shell; /* shell command */
+ char *cl_Description; /* either "<cl_Shell>" or "job <cl_JobName>" */
+ char *cl_JobName; /* job name, if any */
+ char *cl_Timestamp; /* path to timestamp file, if cl_Freq defined */
+ struct CronWaiter *cl_Waiters;
+ struct CronNotifier *cl_Notifs;
+ int cl_Freq; /* 0 (use arrays), minutes, -1 (noauto), -2 (startup) */
+ int cl_Delay; /* defaults to cl_Freq or hourly */
+ time_t cl_LastRan;
+ time_t cl_NotUntil;
+ int cl_Pid; /* running pid, 0, or armed (-1), or waiting (-2) */
+ int cl_MailFlag; /* running pid is for mail */
+ int cl_MailPos; /* 'empty file' size */
+ char cl_Mins[60]; /* 0-59 */
+ char cl_Hrs[24]; /* 0-23 */
+ char cl_Days[32]; /* 1-31 */
+ char cl_Mons[12]; /* 0-11 */
+ char cl_Dow[7]; /* 0-6, beginning sunday */
+} CronLine;
+
+typedef struct CronWaiter {
+ struct CronWaiter *cw_Next;
+ struct CronNotifier *cw_Notifier;
+ struct CronLine *cw_NotifLine;
+ short cw_Flag;
+ int cw_MaxWait;
+} CronWaiter;
+
+typedef struct CronNotifier {
+ struct CronNotifier *cn_Next;
+ struct CronWaiter *cn_Waiter;
+} CronNotifier;
+
+#include "protos.h"
+
Index: create-4.5-patch/dcron-4.5-new/main.c
===================================================================
--- create-4.5-patch/dcron-4.5-new/main.c (nonexistent)
+++ create-4.5-patch/dcron-4.5-new/main.c (revision 5)
@@ -0,0 +1,365 @@
+
+/*
+ * MAIN.C
+ *
+ * crond [-s dir] [-c dir] [-t dir] [-m user@host] [-M mailer] [-S|-L [file]] [-l level] [-b|-f|-d]
+ * run as root, but NOT setuid root
+ *
+ * Copyright 1994 Matthew Dillon (dillon@apollo.backplane.com)
+ * Copyright 2009-2011 James Pryor <profjim@jimpryor.net>
+ * May be distributed under the GNU General Public License
+ */
+
+#include "defs.h"
+
+Prototype short DebugOpt;
+Prototype short LogLevel;
+Prototype short ForegroundOpt;
+Prototype short SyslogOpt;
+Prototype const char *CDir;
+Prototype const char *SCDir;
+Prototype const char *TSDir;
+Prototype const char *LogFile;
+Prototype const char *LogHeader;
+Prototype uid_t DaemonUid;
+Prototype pid_t DaemonPid;
+Prototype const char *SendMail;
+Prototype const char *Mailto;
+Prototype char *TempDir;
+Prototype char *TempFileFmt;
+
+short DebugOpt = 0;
+short LogLevel = LOG_LEVEL;
+short ForegroundOpt = 0;
+short SyslogOpt = 1;
+const char *CDir = CRONTABS;
+const char *SCDir = SCRONTABS;
+const char *TSDir = CRONSTAMPS;
+const char *LogFile = NULL; /* opened with mode 0600 */
+const char *LogHeader = LOGHEADER;
+const char *SendMail = NULL;
+const char *Mailto = NULL;
+char *TempDir;
+char *TempFileFmt;
+
+uid_t DaemonUid;
+pid_t DaemonPid;
+
+int
+main(int ac, char **av)
+{
+ const char *LevelAry[] = {
+ "emerg",
+ "alert",
+ "crit",
+ "err",
+ "warning",
+ "notice",
+ "info",
+ "debug",
+ "panic",
+ "error",
+ "warn",
+ NULL
+ };
+ int i;
+
+ /*
+ * parse options
+ */
+
+ DaemonUid = getuid();
+
+ opterr = 0;
+
+ while ((i = getopt(ac,av,"dl:L:fbSc:s:m:M:t:")) != -1) {
+ switch (i) {
+ case 'l':
+ {
+ char *ptr;
+ int j;
+ ptr = optarg;
+ for (j = 0; LevelAry[j]; ++j) {
+ if (strncmp(ptr, LevelAry[j], strlen(LevelAry[j])) == 0) {
+ break;
+ }
+ }
+ switch(j) {
+ case 0:
+ case 8:
+ /* #define LOG_EMERG 0 [* system is unusable *] */
+ LogLevel = LOG_EMERG;
+ break;
+ case 1:
+ /* #define LOG_ALERT 1 [* action must be taken immediately *] */
+ LogLevel = LOG_ALERT;
+ break;
+ case 2:
+ /* #define LOG_CRIT 2 [* critical conditions *] */
+ LogLevel = LOG_CRIT;
+ break;
+ case 3:
+ case 9:
+ /* #define LOG_ERR 3 [* error conditions *] */
+ LogLevel = LOG_ERR;
+ break;
+ case 4:
+ case 10:
+ /* #define LOG_WARNING 4 [* warning conditions *] */
+ LogLevel = LOG_WARNING;
+ break;
+ case 5:
+ /* #define LOG_NOTICE 5 [* normal but significant condition *] */
+ LogLevel = LOG_NOTICE;
+ break;
+ case 6:
+ /* #define LOG_INFO 6 [* informational *] */
+ LogLevel = LOG_INFO;
+ break;
+ case 7:
+ /* #define LOG_DEBUG 7 [* debug-level messages *] */
+ LogLevel = LOG_DEBUG;
+ break;
+ default:
+ LogLevel = atoi(optarg);
+ }
+ }
+ break;
+ case 'd':
+ DebugOpt = 1;
+ LogLevel = LOG_DEBUG;
+ /* fall through to include f too */
+ case 'f':
+ ForegroundOpt = 1;
+ break;
+ case 'b':
+ ForegroundOpt = 0;
+ break;
+ case 'S': /* log through syslog */
+ SyslogOpt = 1;
+ break;
+ case 'L': /* use internal log formatter */
+ SyslogOpt = 0;
+ LogFile = optarg;
+ /* if LC_TIME is defined, we use it for logging to file instead of compiled-in TIMESTAMP_FMT */
+ if (getenv("LC_TIME") != NULL) {
+ LogHeader = LOCALE_LOGHEADER;
+ }
+ break;
+ case 'c':
+ if (*optarg != 0) CDir = optarg;
+ break;
+ case 's':
+ if (*optarg != 0) SCDir = optarg;
+ break;
+ case 't':
+ if (*optarg != 0) TSDir = optarg;
+ break;
+ case 'M':
+ if (*optarg != 0) SendMail = optarg;
+ break;
+ case 'm':
+ if (*optarg != 0) Mailto = optarg;
+ break;
+ default:
+ /*
+ * check for parse error
+ */
+ printf("dillon's cron daemon " VERSION "\n");
+ printf("crond [-s dir] [-c dir] [-t dir] [-m user@host] [-M mailer] [-S|-L [file]] [-l level] [-b|-f|-d]\n");
+ printf("-s directory of system crontabs (defaults to %s)\n", SCRONTABS);
+ printf("-c directory of per-user crontabs (defaults to %s)\n", CRONTABS);
+ printf("-t directory of timestamps (defaults to %s)\n", CRONSTAMPS);
+ printf("-m user@host where should cron output be directed? (defaults to local user)\n");
+ printf("-M mailer (defaults to %s)\n", SENDMAIL);
+ printf("-S log to syslog using identity '%s' (default)\n", LOG_IDENT);
+ printf("-L file log to specified file instead of syslog\n");
+ printf("-l loglevel log events <= this level (defaults to %s (level %d))\n", LevelAry[LOG_LEVEL], LOG_LEVEL);
+ printf("-b run in background (default)\n");
+ printf("-f run in foreground\n");
+ printf("-d run in debugging mode\n");
+ exit(2);
+ }
+ }
+
+ /*
+ * close stdin and stdout.
+ * close unused descriptors - don't need.
+ * optional detach from controlling terminal
+ */
+
+ fclose(stdin);
+ fclose(stdout);
+
+ i = open("/dev/null", O_RDWR);
+ if (i < 0) {
+ perror("open: /dev/null");
+ exit(1);
+ }
+ dup2(i, 0);
+ dup2(i, 1);
+
+ /* create tempdir with permissions 0755 for cron output */
+ TempDir = strdup(TMPDIR "/cron.XXXXXX");
+ if (mkdtemp(TempDir) == NULL) {
+ perror("mkdtemp");
+ exit(1);
+ }
+ if (chmod(TempDir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) {
+ perror("chmod");
+ exit(1);
+ }
+ if (!(TempFileFmt = concat(TempDir, "/cron.%s.%d", NULL))) {
+ errno = ENOMEM;
+ perror("main");
+ exit(1);
+ }
+
+ if (ForegroundOpt == 0) {
+
+ int fd;
+ int pid;
+
+ if ((pid = fork()) < 0) {
+ /* fork failed */
+ perror("fork");
+ exit(1);
+ } else if (pid > 0) {
+ /* parent */
+ exit(0);
+ }
+ /* child continues */
+
+ /* become session leader, detach from terminal */
+
+ if (setsid() < 0)
+ perror("setsid");
+ if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
+ ioctl(fd, TIOCNOTTY, 0);
+ close(fd);
+ }
+
+ /* setup logging for backgrounded daemons */
+
+ if (SyslogOpt) {
+ /* start SIGHUP and SIGCHLD handling while stderr still open */
+ initsignals();
+ /* 2> /dev/null */
+ fclose(stderr);
+ dup2(1, 2);
+
+ /* open syslog */
+ openlog(LOG_IDENT, LOG_CONS|LOG_PID, LOG_CRON);
+
+ } else {
+ /* open logfile */
+ if ((fd = open(LogFile, O_WRONLY|O_CREAT|O_APPEND, 0600)) >= 0) {
+ /* start SIGHUP ignoring, SIGCHLD handling while stderr still open */
+ initsignals();
+ /* 2> LogFile */
+ fclose(stderr);
+ dup2(fd, 2);
+ } else {
+ int n = errno;
+ fdprintf(2, "failed to open logfile '%s', reason: %s", LogFile, strerror(n));
+ exit(n);
+ }
+ }
+ } else {
+ /* daemon in foreground */
+
+ /* stay in existing session, but start a new process group */
+ if (setpgid(0,0)) {
+ perror("setpgid");
+ exit(1);
+ }
+
+ /* stderr stays open, start SIGHUP ignoring, SIGCHLD handling */
+ initsignals();
+ }
+
+ /* close all other fds, including the ones we opened as /dev/null and LogFile */
+ for (i = 3; i < MAXOPEN; ++i) {
+ close(i);
+ }
+
+
+ /*
+ * main loop - synchronize to 1 second after the minute, minimum sleep
+ * of 1 second.
+ */
+
+ printlogf(LOG_NOTICE,"%s " VERSION " dillon's cron daemon, started with loglevel %s\n", av[0], LevelAry[LogLevel]);
+ SynchronizeDir(CDir, NULL, 1);
+ SynchronizeDir(SCDir, "root", 1);
+ ReadTimestamps(NULL);
+ TestStartupJobs(); /* @startup jobs only run when crond is started, not when their crontab is loaded */
+
+ {
+ time_t t1 = time(NULL);
+ time_t t2;
+ long dt;
+ short rescan = 60;
+ short stime = 60;
+
+ for (;;) {
+ sleep((stime + 1) - (short)(time(NULL) % stime));
+
+ t2 = time(NULL);
+ dt = t2 - t1;
+
+ /*
+ * The file 'cron.update' is checked to determine new cron
+ * jobs. The directory is rescanned once an hour to deal
+ * with any screwups.
+ *
+ * check for disparity. Disparities over an hour either way
+ * result in resynchronization. A reverse-indexed disparity
+ * less then an hour causes us to effectively sleep until we
+ * match the original time (i.e. no re-execution of jobs that
+ * have just been run). A forward-indexed disparity less then
+ * an hour causes intermediate jobs to be run, but only once
+ * in the worst case.
+ *
+ * when running jobs, the inequality used is greater but not
+ * equal to t1, and less then or equal to t2.
+ */
+
+ if (--rescan == 0) {
+ /*
+ * If we resynchronize while jobs are running, we'll clobber
+ * the job pids, so we won't know what's already running.
+ */
+ if (CheckJobs() > 0) {
+ rescan = 1;
+ } else {
+ rescan = 60;
+ SynchronizeDir(CDir, NULL, 0);
+ SynchronizeDir(SCDir, "root", 0);
+ ReadTimestamps(NULL);
+ }
+ }
+ if (rescan < 60) {
+ CheckUpdates(CDir, NULL, t1, t2);
+ CheckUpdates(SCDir, "root", t1, t2);
+ }
+ if (DebugOpt)
+ printlogf(LOG_DEBUG, "Wakeup dt=%d\n", dt);
+ if (dt < -60*60 || dt > 60*60) {
+ t1 = t2;
+ printlogf(LOG_NOTICE,"time disparity of %d minutes detected\n", dt / 60);
+ } else if (dt > 0) {
+ TestJobs(t1, t2);
+ RunJobs();
+ sleep(5);
+ if (CheckJobs() > 0)
+ stime = 10;
+ else
+ stime = 60;
+ t1 = t2;
+ }
+ }
+ }
+ /* not reached */
+}
+
Index: create-4.5-patch/dcron-4.5-new
===================================================================
--- create-4.5-patch/dcron-4.5-new (nonexistent)
+++ create-4.5-patch/dcron-4.5-new (revision 5)
Property changes on: create-4.5-patch/dcron-4.5-new
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: create-4.5-patch/file.list
===================================================================
--- create-4.5-patch/file.list (nonexistent)
+++ create-4.5-patch/file.list (revision 5)
@@ -0,0 +1,7 @@
+dcron-4.5/CHANGELOG
+dcron-4.5/Makefile
+dcron-4.5/README
+dcron-4.5/crontab.c
+dcron-4.5/database.c
+dcron-4.5/defs.h
+dcron-4.5/main.c
Index: create-4.5-patch
===================================================================
--- create-4.5-patch (nonexistent)
+++ create-4.5-patch (revision 5)
Property changes on: create-4.5-patch
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: patches/README
===================================================================
--- patches/README (nonexistent)
+++ patches/README (revision 5)
@@ -0,0 +1,6 @@
+
+/* begin *
+
+ TODO: Leave some comment here.
+
+ * end */
Index: patches
===================================================================
--- patches (nonexistent)
+++ patches (revision 5)
Property changes on: patches
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~
Index: .
===================================================================
--- . (nonexistent)
+++ . (revision 5)
Property changes on: .
___________________________________________________________________
Added: svn:ignore
## -0,0 +1,73 ##
+
+# install dir
+dist
+
+# Target build dirs
+.a1x-newlib
+.a2x-newlib
+.at91sam7s-newlib
+
+.build-machine
+
+.a1x-glibc
+.a2x-glibc
+.h3-glibc
+.h5-glibc
+.i586-glibc
+.i686-glibc
+.imx6-glibc
+.jz47xx-glibc
+.makefile
+.am335x-glibc
+.omap543x-glibc
+.p5600-glibc
+.power8-glibc
+.power8le-glibc
+.power9-glibc
+.power9le-glibc
+.m1000-glibc
+.riscv64-glibc
+.rk328x-glibc
+.rk33xx-glibc
+.rk339x-glibc
+.s8xx-glibc
+.s9xx-glibc
+.x86_64-glibc
+
+# Hidden files (each file)
+.makefile
+.dist
+.rootfs
+
+# src & hw requires
+.src_requires
+.src_requires_depend
+.requires
+.requires_depend
+
+# Tarballs
+*.gz
+*.bz2
+*.lz
+*.xz
+*.tgz
+*.txz
+
+# Signatures
+*.asc
+*.sig
+*.sign
+*.sha1sum
+
+# Patches
+*.patch
+
+# Descriptions
+*.dsc
+*.txt
+
+# Default linux config files
+*.defconfig
+
+# backup copies
+*~