diff -u -rN cron-3.0.1/README.timezone cron-3.0.1-at/README.timezone --- cron-3.0.1/README.timezone 1969-12-31 19:00:00.000000000 -0500 +++ cron-3.0.1-at/README.timezone 2005-08-21 03:44:28.000000000 -0400 @@ -0,0 +1,68 @@ +Cron with multiple timezones +---- ---- -------- --------- +August 20, 2005 + +This is a modified version of Paul Vixie's cron which allows one or more +instances of cron to be started for non-local timezones. + +I made this modification primarily because I listen to the BBC via Real +Player and use cron to start realplay for programs I want to listen to +or record. But the switch between GMT and British Summer Time (BST) +is different from the transistion between the US Eastern Standard and +Eastern Daylight Time. This difference will become greater now with +Congress's passage of the 2005 Energy Bill. It seemed the easiest way +to ensure realplay gets started at the right time would be to have the +crontab entries in London time, instead of Eastern. + +Launching cron with the TZ variable set is easy enough, but cron had +no way of overriding the files used, so I could only have one instance +running. I needed a way to launch multiple instances for the different +timezones I wanted. + +To accomplish this, the path specifications (/etc/crontab, /etc/cron.d, +/var/spool/cron, and /cron.pid) have been changed to variables. +If the new -t option is supplied to the cron or crontab command, its +operation is modified as follows: + +1. Obviously, cron will keep time using the timezone specified in the + TZ environment variable. +2. The pathnames are modified as follows: + /etc/crontab -> /etc/crontab- + /etc/cron.d -> /etc/cron-.d + /var/spool/cron -> /var/spool/cron- + /cron.pid -> /cron-.pid + When making these conversions, if there are slashes ('/') in the TZ + variable, they are converted to periods ('.'). + +One bug/shortcoming is that cron does not pass the TZ variable to +spawned processes automatically. Thus, if you need it passed, it +must be specified in the crontab file. + +To start multiple cron daemons: + +Add a line for each cron to the appropriate startup script. For example, +to start a local cron and a cron running with London time: + + #! /bin/sh + /usr/sbin/cron + TZ='Europe/London' /usr/sbin/cron -t + +Editing user crontabs is accomplished similarly: + + TZ='Europe/London' crontab -t -e + + +This code hasn't been thoroughly tested yet, but the changes are simple +enough. This version is based on the SuSE 9.1 distribution, without +the LAUS patch applied, since I didn't see a need for it, and didn't +have the development libraries on my system. + +Feel free to contact me with any corrections, patches, bug reports, etc. +I'm not a professional programmer, nor do I know much about cron's deeper +inner workings, but I'll try my best to help with problems or questions +about this patch. I'd also be happy to hear from anyone who finds this +patch useful. + +Andrew Turnquist +andrew@turnquist.name + diff -u -rN cron-3.0.1/cron.8 cron-3.0.1-at/cron.8 --- cron-3.0.1/cron.8 2005-08-21 03:03:27.000000000 -0400 +++ cron-3.0.1-at/cron.8 2005-08-20 19:02:48.000000000 -0400 @@ -17,12 +17,12 @@ .\" .\" $Id: cron.8,v 2.2 1993/12/28 08:34:43 vixie Exp $ .\" -.TH CRON 8 "20 December 1993" +.TH CRON 8 "20 August 2005" .UC 4 .SH NAME cron \- daemon to execute scheduled commands (Vixie Cron) .SH SYNOPSIS -cron +cron [ -t ] .SH DESCRIPTION .I Cron should be started from /sbin/init.d/cron, /etc/rc or /etc/rc.local. @@ -66,8 +66,22 @@ .IR crontab (1) command updates the modtime of the spool directory whenever it changes a crontab. +.PP +If the +.I -t +option is given, +.I cron +starts in a timezone specific manner based on the value of the \s-1TZ\s+1 +environment variable. Specifically, it will utilize the configuration +files /etc/crontab-, /etc/cron-.d, and +/var/spool/cron-. Any slashes in \s-1TZ\s+1 are converted +to periods when creating these pathnames. +.SH "BUGS" +.PP +The TZ environment variable is not passed to the spawned processes. .SH "SEE ALSO" crontab(1), crontab(5) .SH AUTHOR .nf Paul Vixie +Timezone code by Andrew Turnquist diff -u -rN cron-3.0.1/cron.c cron-3.0.1-at/cron.c --- cron-3.0.1/cron.c 2005-08-21 03:03:27.000000000 -0400 +++ cron-3.0.1-at/cron.c 2005-08-20 14:54:24.000000000 -0400 @@ -66,6 +66,10 @@ setlinebuf(stderr); #endif + CronDir = CRONDIR; + PidFile = PIDFILE; + SysCronTab = SYSCRONTAB; + SysCronDir = SYSCRONDIR; parse_args(argc, argv); #ifdef USE_SIGCHLD @@ -298,6 +302,48 @@ } +/* + * Set paths to timezone specific values. + * Added by andrew@turnquist.name. + */ + +static void +set_TZ_paths(void) +{ + char *tz; + char buf[MAXPATHLEN]; + int i; + + tz = strdup(getenv("TZ")); + + /* Leave everything as is if TZ isn't set or is empty. */ + if ((tz == NULL) || (tz[0] == '\0')) { + free(tz); /* Needed if TZ was set, but empty */ + fprintf(stderr, "%s: Timezone specific cron requested, but TZ not set!\n", ProgramName); + return; + } + i=0; + while (tz[i] != '\0') { + if (tz[i] == '/') tz[i] = '.'; + i++; + } + + snprintf(buf, sizeof(buf), "%s-%s", CRONDIR, tz); + CronDir = strdup(buf); + + snprintf(buf, sizeof(buf), "%%scron-%s.pid", tz); + PidFile = strdup(buf); + + snprintf(buf, sizeof(buf), "/etc/crontab-%s", tz); + SysCronTab = strdup(buf); + + snprintf(buf, sizeof(buf), "/etc/cron-%s.d", tz); + SysCronDir = strdup(buf); + + free(tz); +} + + static void parse_args(argc, argv) int argc; @@ -305,10 +351,13 @@ { int argch; - while (EOF != (argch = getopt(argc, argv, "x:"))) { + while (EOF != (argch = getopt(argc, argv, "tx:"))) { switch (argch) { default: usage(); + case 't': /* Use TZ as a prefix */ + set_TZ_paths(); + break; case 'x': if (!set_debug_flags(optarg)) usage(); diff -u -rN cron-3.0.1/cron.h cron-3.0.1-at/cron.h --- cron-3.0.1/cron.h 2005-08-21 03:03:27.000000000 -0400 +++ cron-3.0.1-at/cron.h 2005-08-20 14:43:52.000000000 -0400 @@ -254,6 +254,10 @@ }; char *ProgramName; +char *CronDir; +char *PidFile; +char *SysCronTab; +char *SysCronDir; int LineNumber; time_t TargetTime; @@ -268,7 +272,11 @@ extern char *copyright[], *MonthNames[], *DowNames[], - *ProgramName; + *ProgramName, + *CronDir, + *PidFile, + *SysCronTab, + *SysCronDir; extern int LineNumber; extern time_t TargetTime; # if DEBUGGING diff -u -rN cron-3.0.1/crontab.1 cron-3.0.1-at/crontab.1 --- cron-3.0.1/crontab.1 2005-08-21 03:03:27.000000000 -0400 +++ cron-3.0.1-at/crontab.1 2005-08-20 19:14:31.000000000 -0400 @@ -17,14 +17,14 @@ .\" .\" $Id: crontab.1,v 2.4 1993/12/31 10:47:33 vixie Exp $ .\" -.TH CRONTAB 1 "29 December 1993" +.TH CRONTAB 1 "20 August 2005" .UC 4 .SH NAME crontab \- maintain crontab files for individual users (V3) .SH SYNOPSIS -crontab [ -u user ] file +crontab [ -u user ] [ -t ] file .br -crontab [ -u user ] { -l | -r | -e } +crontab [ -u user ] [ -t ] { -l | -r | -e } .SH DESCRIPTION .I crontab is the program used to install, deinstall or list the tables @@ -79,6 +79,19 @@ option is used to edit the current crontab using the editor specified by the \s-1VISUAL\s+1 or \s-1EDITOR\s+1 environment variables. After you exit from the editor, the modified crontab will be installed automatically. +.PP +If the +.I -t +option is given, +.I crontab +will work with a timezone specific crontab based on the value of the \s-1TZ\s+1 +environment variable. The system administrator must start an instance of +.I cron +for the same timezone for this to work. +.SH "BUGS" +The TZ environment variable is not passed to executed processes by +.IR cron . +If it is needed, it must be set in the individual crontab. .SH "SEE ALSO" crontab(5), cron(8) .SH FILES @@ -98,3 +111,4 @@ .SH AUTHOR .nf Paul Vixie +Timezone code by Andrew Turnquist diff -u -rN cron-3.0.1/crontab.c cron-3.0.1-at/crontab.c --- cron-3.0.1/crontab.c 2005-08-21 03:04:32.000000000 -0400 +++ cron-3.0.1-at/crontab.c 2005-08-20 15:18:17.000000000 -0400 @@ -78,7 +78,7 @@ { fprintf(stderr, "%s: usage error: %s\n", ProgramName, msg); fprintf(stderr, "usage:\t%s [-u user] file\n", ProgramName); - fprintf(stderr, "\t%s [-u user] { -e | -l | -r }\n", ProgramName); + fprintf(stderr, "\t%s [-u user] [-t] { -e | -l | -r }\n", ProgramName); fprintf(stderr, "\t\t(default operation is replace, per 1003.2)\n"); fprintf(stderr, "\t-e\t(edit user's crontab)\n"); fprintf(stderr, "\t-l\t(list user's crontab)\n"); @@ -104,6 +104,10 @@ #if defined(BSD) setlinebuf(stderr); #endif + CronDir = CRONDIR; + PidFile = PIDFILE; + SysCronTab = SYSCRONTAB; + SysCronDir = SYSCRONDIR; parse_args(argc, argv); /* sets many globals, opens a file */ set_cron_uid(); set_cron_cwd(); @@ -134,7 +138,44 @@ exit(exitstatus); /*NOTREACHED*/ } - + + +static void +set_TZ_paths(void) +{ + char *tz; + char buf[MAXPATHLEN]; + int i; + + tz = strdup(getenv("TZ")); + + /* Leave everything as is if TZ isn't set or is empty. */ + if ((tz == NULL) || (tz[0] == '\0')) { + free(tz); /* Needed if TZ was set, but empty */ + fprintf(stderr, "%s: Timezone specific cron requested, but TZ not set!\n", ProgramName); + return; + } + i=0; + while (tz[i] != '\0') { + if (tz[i] == '/') tz[i] = '.'; + i++; + } + + snprintf(buf, sizeof(buf), "%s-%s", CRONDIR, tz); + CronDir = strdup(buf); + + snprintf(buf, sizeof(buf), "%%scron-%s.pid", tz); + PidFile = strdup(buf); + + snprintf(buf, sizeof(buf), "/etc/crontab-%s", tz); + SysCronTab = strdup(buf); + + snprintf(buf, sizeof(buf), "/etc/cron-%s.d", tz); + SysCronDir = strdup(buf); + + free(tz); +} + static void parse_args(argc, argv) @@ -154,8 +195,11 @@ strncpy(RealUser, User, MAX_UNAME); Filename[0] = '\0'; Option = opt_unknown; - while (EOF != (argch = getopt(argc, argv, "u:lerx:"))) { + while (EOF != (argch = getopt(argc, argv, "u:lertx:"))) { switch (argch) { + case 't': /* Use TZ as a prefix */ + set_TZ_paths(); + break; case 'x': if (!set_debug_flags(optarg)) usage("bad debug option"); diff -u -rN cron-3.0.1/database.c cron-3.0.1-at/database.c --- cron-3.0.1/database.c 2005-08-21 03:03:27.000000000 -0400 +++ cron-3.0.1-at/database.c 2005-08-20 14:50:48.000000000 -0400 @@ -67,24 +67,24 @@ /* track system crontab file */ - if (stat(SYSCRONTAB, &syscron_stat) < OK) + if (stat(SysCronTab, &syscron_stat) < OK) syscron_stat.st_mtime = 0; - /* Check mod time of SYSCRONDIR. This won't tell us if a file + /* Check mod time of SysCronDir. This won't tell us if a file * in it changed, but will capture deletions, which the individual * file check won't */ - if (stat(SYSCRONDIR, &syscrond_stat) < OK) { - log_it("CRON", getpid(), "STAT FAILED", SYSCRONDIR); + if (stat(SysCronDir, &syscrond_stat) < OK) { + log_it("CRON", getpid(), "STAT FAILED", SysCronDir); (void) exit(ERROR_EXIT); } syscrond_files_mtime = syscrond_stat.st_mtime; - /* If SYSCRONDIR was modified, we know that something is changed and + /* If SysCronDir was modified, we know that something is changed and * there is no need for any further checks. If it wasn't, we should - * pass through the old list of files in SYSCRONDIR and check their + * pass through the old list of files in SysCronDir and check their * mod time. Therefore a stopped hard drive won't be spun up, since - * we avoid reading of SYSCRONDIR and don't change its access time. + * we avoid reading of SysCronDir and don't change its access time. * This is especially important on laptops with APM. */ if (old_db->mtime >= syscrond_files_mtime) { @@ -97,7 +97,7 @@ (systab = get_next_system_crontab (systab)) != NULL; systab = systab->next) { - snprintf(syscrond_fname, MAXPATHLEN, "%s/%s", SYSCRONDIR, + snprintf(syscrond_fname, MAXPATHLEN, "%s/%s", SysCronDir, systab->name + 8); Debug(DLOAD, ("\t%s:", syscrond_fname)) @@ -137,13 +137,13 @@ if (syscron_stat.st_mtime) { process_crontab("root", "*system*", - SYSCRONTAB, &syscron_stat, + SysCronTab, &syscron_stat, &new_db, old_db); } /* Read all the package crontabs. */ - if (!(dir = opendir(SYSCRONDIR))) { - log_it("CRON", getpid(), "OPENDIR FAILED", SYSCRONDIR); + if (!(dir = opendir(SysCronDir))) { + log_it("CRON", getpid(), "OPENDIR FAILED", SysCronDir); (void) exit(ERROR_EXIT); } @@ -169,7 +169,7 @@ /* Generate the "fname" */ (void) strcpy(fname,"*system*"); (void) strcat(fname, dp->d_name); - snprintf(tabname,MAXPATHLEN,"%s/%s", SYSCRONDIR, dp->d_name); + snprintf(tabname,MAXPATHLEN,"%s/%s", SysCronDir, dp->d_name); /* statbuf is used as working storage by process_crontab() -- current contents are irrelevant */ diff -u -rN cron-3.0.1/misc.c cron-3.0.1-at/misc.c --- cron-3.0.1/misc.c 2005-08-21 03:04:16.000000000 -0400 +++ cron-3.0.1-at/misc.c 2005-08-20 14:41:20.000000000 -0400 @@ -192,31 +192,31 @@ { struct stat sb; - /* first check for CRONDIR ("/var/cron" or some such) + /* first check for CronDir ("/var/cron" or some such) */ - if (stat(CRONDIR, &sb) < OK && errno == ENOENT) { - perror(CRONDIR); - if (OK == mkdir(CRONDIR, 0700)) { - fprintf(stderr, "%s: created\n", CRONDIR); - stat(CRONDIR, &sb); + if (stat(CronDir, &sb) < OK && errno == ENOENT) { + perror(CronDir); + if (OK == mkdir(CronDir, 0700)) { + fprintf(stderr, "%s: created\n", CronDir); + stat(CronDir, &sb); } else { - fprintf(stderr, "%s: ", CRONDIR); + fprintf(stderr, "%s: ", CronDir); perror("mkdir"); exit(ERROR_EXIT); } } if (!(sb.st_mode & S_IFDIR)) { fprintf(stderr, "'%s' is not a directory, bailing out.\n", - CRONDIR); + CronDir); exit(ERROR_EXIT); } - if (chdir(CRONDIR) < OK) { - fprintf(stderr, "cannot chdir(%s), bailing out.\n", CRONDIR); - perror(CRONDIR); + if (chdir(CronDir) < OK) { + fprintf(stderr, "cannot chdir(%s), bailing out.\n", CronDir); + perror(CronDir); exit(ERROR_EXIT); } - /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such) + /* CronDir okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such) */ if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) { perror(SPOOL_DIR); @@ -242,7 +242,7 @@ * * note: main() calls us twice; once before forking, once after. * we maintain static storage of the file pointer so that we - * can rewrite our PID into the PIDFILE after the fork. + * can rewrite our PID into the PidFile after the fork. * * it would be great if fflush() disassociated the file buffer. */ @@ -263,7 +263,7 @@ char buf[MAX_TEMPSTR]; int fd, otherpid; - (void) snprintf(pidfile, MAX_FNAME, PIDFILE, PIDDIR); + (void) snprintf(pidfile, MAX_FNAME, PidFile, PIDDIR); if ((-1 == (fd = open(pidfile, O_RDWR|O_CREAT, 0644))) || (NULL == (fp = fdopen(fd, "r+"))) ) {