--- cron-3.0pl1.orig/crontab.c +++ cron-3.0pl1/crontab.c @@ -31,6 +31,7 @@ #include "cron.h" #include #include +#include #include #include #ifdef USE_UTIMES @@ -46,7 +47,6 @@ #define NHEADER_LINES 3 - enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace }; #if DEBUGGING @@ -55,10 +55,12 @@ static PID_T Pid; -static char User[MAX_UNAME], RealUser[MAX_UNAME]; +static char *User, *RealUser; static char Filename[MAX_FNAME]; -static FILE *NewCrontab; +static char Directory[MAX_FNAME]; +static FILE *NewCrontab = NULL; static int CheckErrorCount; +static int PromptOnDelete; static enum opt_t Option; static struct passwd *pw; static void list_cmd __P((void)), @@ -69,6 +71,10 @@ parse_args __P((int c, char *v[])); static int replace_cmd __P((void)); +/* Support edit command */ +static int create_tmp_crontab __P((void)); +static int open_tmp_crontab __P((struct stat *fsbuf)); +static void cleanup_tmp_crontab __P((void)); static void usage(msg) @@ -76,11 +82,12 @@ { 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 ] [ -i ] { -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"); fprintf(stderr, "\t-r\t(delete user's crontab)\n"); + fprintf(stderr, "\t-i\t(prompt before deleting user's crontab)\n"); exit(ERROR_EXIT); } @@ -102,15 +109,25 @@ #if defined(BSD) setlinebuf(stderr); #endif + if (argv[1] == NULL) { + argv[1] = "-"; + } parse_args(argc, argv); /* sets many globals, opens a file */ - set_cron_uid(); set_cron_cwd(); if (!allowed(User)) { - fprintf(stderr, - "You (%s) are not allowed to use this program (%s)\n", - User, ProgramName); - fprintf(stderr, "See crontab(1) for more information\n"); - log_it(RealUser, Pid, "AUTH", "crontab command not allowed"); + if ( getuid() != 0 ) { + fprintf(stderr, + "You (%s) are not allowed to use this program (%s)\n", + User, ProgramName); + fprintf(stderr, "See crontab(1) for more information\n"); + log_it(RealUser, Pid, "AUTH", "crontab command not allowed"); + } else { + /* If the user is not allowed but root is running the + * program warn but do not log */ + fprintf(stderr, + "The user %s cannot use this program (%s)\n", + User, ProgramName); + } exit(ERROR_EXIT); } exitstatus = OK_EXIT; @@ -124,11 +141,23 @@ case opt_replace: if (replace_cmd() < 0) exitstatus = ERROR_EXIT; break; + /* The following was added to shut + -Wall up, but it will never be hit, + because the option parser will catch + it */ + case opt_unknown: usage("unknown option specified"); + break; } - exit(0); + exit(exitstatus); /*NOTREACHED*/ } +#if DEBUGGING +char *getoptarg = "u:lerix:"; +#else +char *getoptarg = "u:leri"; +#endif + static void parse_args(argc, argv) @@ -136,6 +165,7 @@ char *argv[]; { int argch; + struct stat statbuf; if (!(pw = getpwuid(getuid()))) { fprintf(stderr, "%s: your UID isn't in the passwd file.\n", @@ -143,30 +173,43 @@ fprintf(stderr, "bailing out.\n"); exit(ERROR_EXIT); } - strcpy(User, pw->pw_name); - strcpy(RealUser, User); + if (((User=strdup(pw->pw_name)) == NULL) || + ((RealUser=strdup(pw->pw_name)) == NULL)) { + fprintf(stderr, "Memory allocation error\n"); + exit(ERROR_EXIT); + } Filename[0] = '\0'; Option = opt_unknown; - while (EOF != (argch = getopt(argc, argv, "u:lerx:"))) { + PromptOnDelete = 0; + + while (EOF != (argch = getopt(argc, argv, getoptarg))) { switch (argch) { +#if DEBUGGING case 'x': if (!set_debug_flags(optarg)) usage("bad debug option"); + usage("unrecognized option"); break; +#endif case 'u': - if (getuid() != ROOT_UID) + if (!(pw = getpwnam(optarg))) + { + fprintf(stderr, "%s: user `%s' unknown\n", + ProgramName, optarg); + exit(ERROR_EXIT); + } + if ((getuid() != ROOT_UID) && + (getuid() != pw->pw_uid)) { fprintf(stderr, "must be privileged to use -u\n"); exit(ERROR_EXIT); } - if (!(pw = getpwnam(optarg))) - { - fprintf(stderr, "%s: user `%s' unknown\n", - ProgramName, optarg); + free(User); + if ((User=strdup(pw->pw_name)) == NULL) { + fprintf(stderr, "Memory allocation error\n"); exit(ERROR_EXIT); } - (void) strcpy(User, optarg); break; case 'l': if (Option != opt_unknown) @@ -183,6 +226,9 @@ usage("only one operation permitted"); Option = opt_edit; break; + case 'i': + PromptOnDelete = 1; + break; default: usage("unrecognized option"); } @@ -197,7 +243,9 @@ } else { if (argv[optind] != NULL) { Option = opt_replace; - (void) strcpy (Filename, argv[optind]); + (void) strncpy (Filename, argv[optind], (sizeof Filename)-1); + Filename[(sizeof Filename)-1] = '\0'; + } else { usage("file name must be specified for replace"); } @@ -227,7 +275,16 @@ perror(Filename); exit(ERROR_EXIT); } - if (swap_uids() < OK) { + /* Make sure we opened a normal file. */ + if (fstat(fileno(NewCrontab), &statbuf) < 0) { + perror("fstat"); + exit(ERROR_EXIT); + } + if (!S_ISREG(statbuf.st_mode)) { + fprintf(stderr, "%s: Not a regular file.\n", Filename); + exit(ERROR_EXIT); + } + if (swap_uids_back() < OK) { perror("swapping uids back"); exit(ERROR_EXIT); } @@ -244,20 +301,49 @@ char n[MAX_FNAME]; FILE *f; int ch; +#ifdef DEBIAN + int x; + char *ctnh; +#endif log_it(RealUser, Pid, "LIST", User); - (void) sprintf(n, CRON_TAB(User)); + (void) snprintf(n, MAX_FNAME, CRON_TAB(User)); if (!(f = fopen(n, "r"))) { - if (errno == ENOENT) + if (errno == ENOENT) fprintf(stderr, "no crontab for %s\n", User); - else - perror(n); + else { + fprintf(stderr, "%s/: fopen: %s\n", n, strerror(errno)); + } exit(ERROR_EXIT); } /* file is open. copy to stdout, close. */ Set_LineNum(1) +#ifdef DEBIAN + /* DEBIAN: Don't list header lines unless CRONTAB_NOHEADER is + 'N'. */ + /* ignore the top few comments since we probably put them there. + */ + if (!(ctnh = getenv("CRONTAB_NOHEADER")) || + toupper(*ctnh) != 'N') + { + for (x = 0; x < NHEADER_LINES; x++) { + ch = get_char(f); + if (EOF == ch) + break; + if ('#' != ch) { + putchar(ch); + break; + } + while (EOF != (ch = get_char(f))) + if (ch == '\n') + break; + if (EOF == ch) + break; + } + } +#endif while (EOF != (ch = get_char(f))) putchar(ch); fclose(f); @@ -267,14 +353,45 @@ static void delete_cmd() { char n[MAX_FNAME]; + char q[MAX_TEMPSTR]; + int ans; + struct stat fsbuf; + + /* Check if the user has a crontab file first */ + (void) snprintf(n, MAX_FNAME, CRON_TAB(User)); + if (stat(n, &fsbuf) < 0) { + fprintf(stderr, "no crontab for %s\n", User); + exit(ERROR_EXIT); + } + + if( PromptOnDelete == 1 ) + { + printf("crontab: really delete %s's crontab? (y/n) ", User); + fflush(stdout); + ans = 0; + q[0] = '\0'; + while ( ans == 0 ) { + (void) fgets(q, sizeof q, stdin); + switch (islower(q[0]) ? q[0] : tolower(q[0])) { + case 'y': + case 'n': + ans = 1; + break; + default: + fprintf(stderr, "Please enter Y or N: "); + } + } + if ( (q[0] == 'N') || (q[0] == 'n') ) + exit(OK_EXIT); + } log_it(RealUser, Pid, "DELETE", User); - (void) sprintf(n, CRON_TAB(User)); if (unlink(n)) { if (errno == ENOENT) fprintf(stderr, "no crontab for %s\n", User); - else - perror(n); + else { + fprintf(stderr, "%s/: unlink: %s\n", CRONDIR, strerror(errno)); + } exit(ERROR_EXIT); } poke_daemon(); @@ -290,21 +407,158 @@ } +/* The next several function implement 'crontab -e' */ + +/* Returns -1 on error, or fd to tempfile. */ +static int +create_tmp_crontab() +{ + const char *template = "/crontab.XXXXXX"; + int nfd; + char *tmp; + + /* Create the temp directory. Note that since crontab is + setuid(root), TMPDIR only work for root. */ + if ((tmp=getenv("TMPDIR")) && strlen(tmp) < MAX_FNAME) { + strcpy(Directory, tmp); + } else { + strcpy(Directory,"/tmp"); + } + + if (strlen(Directory) + strlen(template) < MAX_FNAME) { + strcat(Directory, template); + } else { + fprintf(stderr, "TMPDIR value is to long -- exiting\n"); + Directory[0] = '\0'; + return -1; + } + + if (!mkdtemp(Directory)) { + perror(Directory); + Directory[0] = '\0'; + return -1; + } + + /* Now create the actual temporary crontab file */ + if (snprintf(Filename, MAX_FNAME, "%s/crontab", Directory) + >= MAX_FNAME) { + fprintf(stderr, "Temporary filename too long - aborting\n"); + Filename[0] = '\0'; + return -1; + } + if ((nfd=open(Filename, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1) { + perror(Filename); + Filename[0] = '\0'; + return -1; + } + return nfd; +} + +/* Re-open the new (temporary) crontab, and check to make sure that + no-one is playing games. Return 0 on success, -1 on error. (Why not + just fopen() and stat()? Because there's no guarantee that you + fopen()ed the file you stat()ed.) */ +static int +open_tmp_crontab(fsbuf) + struct stat *fsbuf; +{ + int t; + struct stat statbuf; + + if ((t=open(Filename, O_RDONLY)) < 0) { + perror("Can't open tempfile after edit"); + return -1; + } + + if (fstat(t, &statbuf) < 0) { + perror("fstat"); + return -1; + } + if (statbuf.st_uid != getuid()) { + fprintf(stderr, "Temporary crontab no longer owned by you.\n"); + return -1;; + } + + if (!S_ISREG(statbuf.st_mode)) { + fprintf(stderr, "The temporary crontab must remain a regular file"); + return -1; + } + + if (statbuf.st_mtime == fsbuf->st_mtime) { + return 1; /* No change to file */ + } + + NewCrontab = fdopen(t, "r"); + if (!NewCrontab) { + perror("fdopen(): after edit"); + return -1; + } + return 0; +} + +/* We can't just delete Filename, because the editor might have + created other temporary files in there. If there's an error, we + just bail, and let the user/admin deal with it.*/ + +static void +cleanup_tmp_crontab(void) +{ + DIR *dp; + struct dirent *ep; + char fname[MAX_FNAME]; + + if (Directory[0] == '\0') { + return; + } + + /* Delete contents */ + dp = opendir (Directory); + if (dp == NULL) { + perror(Directory); + return; + } + + while ((ep = readdir (dp))) { + if (!strcmp(ep->d_name, ".") || + !strcmp(ep->d_name, "..")) { + continue; + } + if (snprintf(fname, MAX_FNAME, "%s/%s", + Directory, ep->d_name) >= MAX_FNAME) { + fprintf(stderr, "filename too long to delete: %s/%s", + Directory, ep->d_name); + return; + } + if (unlink(fname)) { + perror(ep->d_name); + return; + } + } + (void) closedir (dp); + + if (rmdir(Directory)) { + perror(Directory); + return; + } + return; +} + static void edit_cmd() { char n[MAX_FNAME], q[MAX_TEMPSTR], *editor; FILE *f; int ch, t, x; - struct stat statbuf; - time_t mtime; + struct stat fsbuf; WAIT_T waiter; PID_T pid, xpid; + mode_t um; + int add_help_text = 0; log_it(RealUser, Pid, "BEGIN EDIT", User); - (void) sprintf(n, CRON_TAB(User)); + (void) snprintf(n, MAX_FNAME, CRON_TAB(User)); if (!(f = fopen(n, "r"))) { if (errno != ENOENT) { - perror(n); + fprintf(stderr, "%s/: fdopen: %s", n, strerror(errno)); exit(ERROR_EXIT); } fprintf(stderr, "no crontab for %s - using an empty one\n", @@ -313,28 +567,52 @@ perror("/dev/null"); exit(ERROR_EXIT); } + add_help_text = 1; } - (void) sprintf(Filename, "/tmp/crontab.%d", Pid); - if (-1 == (t = open(Filename, O_CREAT|O_EXCL|O_RDWR, 0600))) { - perror(Filename); - goto fatal; - } -#ifdef HAS_FCHOWN - if (fchown(t, getuid(), getgid()) < 0) { -#else - if (chown(Filename, getuid(), getgid()) < 0) { -#endif - perror("fchown"); + um = umask(077); + + if ((t=create_tmp_crontab()) < 0) { + fprintf(stderr, "Creation of temporary crontab file failed - aborting\n"); + (void) umask(um); goto fatal; } - if (!(NewCrontab = fdopen(t, "r+"))) { + + (void) umask(um); + if (!(NewCrontab = fdopen(t, "w"))) { perror("fdopen"); goto fatal; } Set_LineNum(1) + if (add_help_text) { + fprintf(NewCrontab, +"# Edit this file to introduce tasks to be run by cron.\n" +"# \n" +"# Each task to run has to be defined through a single line\n" +"# indicating with different fields when the task will be run\n" +"# and what command to run for the task\n" +"# \n" +"# To define the time you can provide concrete values for\n" +"# minute (m), hour (h), day of month (dom), month (mon),\n" +"# and day of week (dow) or use '*' in these fields (for 'any')." +"# \n" +"# Notice that tasks will be started based on the cron's system\n" +"# daemon's notion of time and timezones.\n" +"# \n" +"# Output of the crontab jobs (including errors) is sent through\n" +"# email to the user the crontab file belongs to (unless redirected).\n" +"# \n" +"# For example, you can run a backup of all your user accounts\n" +"# at 5 a.m every week with:\n" +"# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/\n" +"# \n" +"# For more information see the manual pages of crontab(5) and cron(8)\n" +"# \n" +"# m h dom mon dow command\n" ); + } + /* ignore the top few comments since we probably put them there. */ for (x = 0; x < NHEADER_LINES; x++) { @@ -358,37 +636,44 @@ while (EOF != (ch = get_char(f))) putc(ch, NewCrontab); fclose(f); - if (fflush(NewCrontab) < OK) { - perror(Filename); - exit(ERROR_EXIT); - } - again: - rewind(NewCrontab); + if (ferror(NewCrontab)) { fprintf(stderr, "%s: error while writing new crontab to %s\n", ProgramName, Filename); - fatal: unlink(Filename); - exit(ERROR_EXIT); } - if (fstat(t, &statbuf) < 0) { - perror("fstat"); + + if (fstat(t, &fsbuf) < 0) { + perror("unable to stat temp file"); goto fatal; } - mtime = statbuf.st_mtime; - if ((!(editor = getenv("VISUAL"))) - && (!(editor = getenv("EDITOR"))) + + + /* Okay, edit the file */ + + if ((!((editor = getenv("VISUAL")) && strlen(editor))) + && (!((editor = getenv("EDITOR")) && strlen(editor))) ) { editor = EDITOR; } - /* we still have the file open. editors will generally rewrite the - * original file rather than renaming/unlinking it and starting a - * new one; even backup files are supposed to be made by copying - * rather than by renaming. if some editor does not support this, - * then don't use it. the security problems are more severe if we - * close and reopen the file around the edit. - */ + + /* Close before cleanup_tmp_crontab is called or otherwise + * (on NFS mounted /) will get renamed on unlink */ + if (fclose(NewCrontab) != 0) { + perror(Filename); + goto fatal; + } + +again: /* Loop point for retrying edit after error */ + + /* Turn off signals. */ + (void)signal(SIGHUP, SIG_IGN); + (void)signal(SIGINT, SIG_IGN); + (void)signal(SIGQUIT, SIG_IGN); + + /* Give up privileges while editing */ + swap_uids(); switch (pid = fork()) { case -1: @@ -396,10 +681,14 @@ goto fatal; case 0: /* child */ - if (setuid(getuid()) < 0) { - perror("setuid(getuid())"); - exit(ERROR_EXIT); - } + if (setgid(getgid()) < 0) { + perror("setgid(getgid())"); + exit(ERROR_EXIT); + } + if (setuid(getuid()) < 0) { + perror("setuid(getuid())"); + exit(ERROR_EXIT); + } if (chdir("/tmp") < 0) { perror("chdir(/tmp)"); exit(ERROR_EXIT); @@ -409,7 +698,7 @@ ProgramName); exit(ERROR_EXIT); } - sprintf(q, "%s %s", editor, Filename); + snprintf(q, MAX_TEMPSTR, "%s %s", editor, Filename); execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", q, NULL); perror(editor); exit(ERROR_EXIT); @@ -420,40 +709,60 @@ } /* parent */ - xpid = wait(&waiter); - if (xpid != pid) { - fprintf(stderr, "%s: wrong PID (%d != %d) from \"%s\"\n", - ProgramName, xpid, pid, editor); - goto fatal; - } - if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) { - fprintf(stderr, "%s: \"%s\" exited with status %d\n", - ProgramName, editor, WEXITSTATUS(waiter)); - goto fatal; - } - if (WIFSIGNALED(waiter)) { - fprintf(stderr, - "%s: \"%s\" killed; signal %d (%score dumped)\n", - ProgramName, editor, WTERMSIG(waiter), - WCOREDUMP(waiter) ?"" :"no "); - goto fatal; - } - if (fstat(t, &statbuf) < 0) { - perror("fstat"); - goto fatal; - } - if (mtime == statbuf.st_mtime) { - fprintf(stderr, "%s: no changes made to crontab\n", - ProgramName); - goto remove; - } + while (1) { + xpid = waitpid(pid, &waiter, WUNTRACED); + if (xpid == -1) { + fprintf(stderr, "%s: waitpid() failed waiting for PID %d from \"%s\": %s\n", + ProgramName, pid, editor, strerror(errno)); + } else if (xpid != pid) { + fprintf(stderr, "%s: wrong PID (%d != %d) from \"%s\"\n", + ProgramName, xpid, pid, editor); + goto fatal; + } else if (WIFSTOPPED(waiter)) { + /* raise(WSTOPSIG(waiter)); Not needed and breaks in job control shell*/ + } else if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) { + fprintf(stderr, "%s: \"%s\" exited with status %d\n", + ProgramName, editor, WEXITSTATUS(waiter)); + goto fatal; + } else if (WIFSIGNALED(waiter)) { + fprintf(stderr, + "%s: \"%s\" killed; signal %d (%score dumped)\n", + ProgramName, editor, WTERMSIG(waiter), + WCOREDUMP(waiter) ?"" :"no "); + goto fatal; + } else + break; + } + (void)signal(SIGHUP, SIG_DFL); + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + (void)signal(SIGTSTP, SIG_DFL); + + /* Need privs again */ + swap_uids_back(); + + switch (open_tmp_crontab(&fsbuf)) { + case -1: + fprintf(stderr, "Error while editing crontab\n"); + goto fatal; + case 1: + fprintf(stderr, "No modification made\n"); + goto remove; + case 0: + break; + default: + fprintf(stderr, + "cron@packages.debian.org fscked up. Send him a nasty note\n"); + break; + } + fprintf(stderr, "%s: installing new crontab\n", ProgramName); switch (replace_cmd()) { case 0: break; case -1: for (;;) { - printf("Do you want to retry the same edit? "); + printf("Do you want to retry the same edit? (y/n) "); fflush(stdout); q[0] = '\0'; (void) fgets(q, sizeof q, stdin); @@ -473,15 +782,33 @@ ProgramName, Filename); goto done; default: - fprintf(stderr, "%s: panic: bad switch() in replace_cmd()\n"); + fprintf(stderr, "%s: panic: bad switch() in replace_cmd()\n", + ProgramName); goto fatal; } + + if (fclose(NewCrontab) != 0) { + perror(Filename); + } + remove: - unlink(Filename); + cleanup_tmp_crontab(); done: log_it(RealUser, Pid, "END EDIT", User); + return; + fatal: + cleanup_tmp_crontab(); + unlink(Filename); + exit(ERROR_EXIT); } - + +static char tn[MAX_FNAME]; + +static void sig_handler(int x) +{ + unlink(tn); + exit(1); +} /* returns 0 on success * -1 on syntax error @@ -489,20 +816,44 @@ */ static int replace_cmd() { - char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME]; + char n[MAX_FNAME], envstr[MAX_ENVSTR]; FILE *tmp; - int ch, eof; + int ch, eof, fd; + int nl = FALSE; entry *e; time_t now = time(NULL); char **envp = env_init(); + mode_t um; - (void) sprintf(n, "tmp.%d", Pid); - (void) sprintf(tn, CRON_TAB(n)); - if (!(tmp = fopen(tn, "w+"))) { - perror(tn); + if (envp == NULL) { + fprintf(stderr, "%s: Cannot allocate memory.\n", ProgramName); return (-2); } + + /* Assumes Linux-style signal handlers (takes int, returns void) */ + /* Signal handlers, to ensure we do not leave temp files in the + spool dir. We don't remove these on exiting this function; + but that's OK, we exit immediately afterwards anyway. */ + signal(SIGHUP, sig_handler); + signal(SIGINT, sig_handler); + signal(SIGQUIT, sig_handler); + signal(SIGTSTP, SIG_IGN); + + (void) snprintf(tn, MAX_FNAME, CRON_TAB("tmp.XXXXXX")); + um = umask(077); + fd = mkstemp(tn); + if (fd < 0) { + fprintf(stderr, "%s/: mkstemp: %s\n", CRONDIR, strerror(errno)); + return(-2); + } + tmp = fdopen(fd, "w+"); + if (!tmp) { + fprintf(stderr, "%s/: fdopen: %s\n", CRONDIR, strerror(errno)); + return (-2); + } + (void) umask(um); + /* write a signature at the top of the file. * * VERY IMPORTANT: make sure NHEADER_LINES agrees with this code. @@ -517,19 +868,17 @@ Set_LineNum(1) while (EOF != (ch = get_char(NewCrontab))) putc(ch, tmp); - ftruncate(fileno(tmp), ftell(tmp)); - fflush(tmp); rewind(tmp); - if (ferror(tmp)) { - fprintf(stderr, "%s: error while writing new crontab to %s\n", - ProgramName, tn); + if (ferror(tmp) || fflush(tmp) || fsync(fd)) { + fprintf(stderr, "%s: %s: %s\n", + ProgramName, tn, strerror(errno)); fclose(tmp); unlink(tn); return (-2); } /* check the syntax of the file being installed. */ - + rewind(tmp); /* BUG: was reporting errors after the EOF if there were any errors * in the file proper -- kludged it by stopping after first error. * vix 31mar87 @@ -540,6 +889,8 @@ switch (load_env(envstr, tmp)) { case ERR: eof = TRUE; + if (envstr[0] == '\0') + nl = TRUE; break; case FALSE: e = load_entry(tmp, check_error, pw, envp); @@ -557,42 +908,50 @@ return (-1); } -#ifdef HAS_FCHOWN - if (fchown(fileno(tmp), ROOT_UID, -1) < OK) -#else - if (chown(tn, ROOT_UID, -1) < OK) -#endif - { - perror("chown"); + if (nl == FALSE) { + fprintf(stderr, "new crontab file is missing newline before " + "EOF, can't install.\n"); fclose(tmp); unlink(tn); - return (-2); + return (-1); } + #ifdef HAS_FCHMOD if (fchmod(fileno(tmp), 0600) < OK) #else if (chmod(tn, 0600) < OK) #endif { - perror("chown"); + perror("chmod"); fclose(tmp); unlink(tn); return (-2); } + if (fclose(tmp) == EOF) { perror("fclose"); unlink(tn); return (-2); } - (void) sprintf(n, CRON_TAB(User)); + /* Root on behalf of another user must set file owner to that user */ + if (getuid() == ROOT_UID && strcmp(User, RealUser) != 0) { + if (chown(tn, pw->pw_uid, -1) != 0) { + perror("chown"); + unlink(tn); + return -2; + } + } + + (void) snprintf(n, sizeof(n), CRON_TAB(User)); if (rename(tn, n)) { - fprintf(stderr, "%s: error renaming %s to %s\n", - ProgramName, tn, n); - perror("rename"); + fprintf(stderr, "%s: %s: rename: %s\n", + ProgramName, n, strerror(errno)); unlink(tn); return (-2); } + + log_it(RealUser, Pid, "REPLACE", User); poke_daemon(); @@ -610,14 +969,14 @@ (void) gettimeofday(&tvs[0], &tz); tvs[1] = tvs[0]; if (utimes(SPOOL_DIR, tvs) < OK) { - fprintf(stderr, "crontab: can't update mtime on spooldir\n"); - perror(SPOOL_DIR); + fprintf(stderr, "%s/: utimes: %s", CRONDIR, strerror(errno)); + fputs("crontab: can't update mtime on spooldir\n", stderr); return; } #else if (utime(SPOOL_DIR, NULL) < OK) { - fprintf(stderr, "crontab: can't update mtime on spooldir\n"); - perror(SPOOL_DIR); + fprintf(stderr, "%s: utime: %s\n", CRONDIR, strerror(errno)); + fputs("crontab: can't update mtime on spooldir\n", stderr); return; } #endif /*USE_UTIMES*/ --- cron-3.0pl1.orig/entry.c +++ cron-3.0pl1/entry.c @@ -91,6 +91,7 @@ int ch; char cmd[MAX_COMMAND]; char envstr[MAX_ENVSTR]; + char **tenvp; Debug(DPARS, ("load_entry()...about to eat comments\n")) @@ -129,18 +130,21 @@ bit_set(e->dom, 0); bit_set(e->month, 0); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); + e->flags |= DOW_STAR; } else if (!strcmp("monthly", cmd)) { bit_set(e->minute, 0); bit_set(e->hour, 0); bit_set(e->dom, 0); bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); + e->flags |= DOW_STAR; } else if (!strcmp("weekly", cmd)) { bit_set(e->minute, 0); bit_set(e->hour, 0); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); + e->flags |= DOM_STAR; bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); - bit_set(e->dow, 0); + bit_nset(e->dow, 0,0); } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) { bit_set(e->minute, 0); bit_set(e->hour, 0); @@ -149,10 +153,11 @@ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); } else if (!strcmp("hourly", cmd)) { bit_set(e->minute, 0); - bit_set(e->hour, (LAST_HOUR-FIRST_HOUR+1)); + bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); + e->flags |= HR_STAR; } else { ecode = e_timespec; goto eof; @@ -160,6 +165,8 @@ } else { Debug(DPARS, ("load_entry()...about to parse numerics\n")) + if (ch == '*') + e->flags |= MIN_STAR; ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, PPC_NULL, ch, file); if (ch == EOF) { @@ -170,6 +177,8 @@ /* hours */ + if (ch == '*') + e->flags |= HR_STAR; ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR, PPC_NULL, ch, file); if (ch == EOF) { @@ -218,6 +227,9 @@ bit_set(e->dow, 7); } + /* If we used one of the @commands, we may be pointing at + blanks, and if we don't skip over them, we'll miss the user/command */ + Skip_Blanks(ch, file); /* ch is the first character of a command, or a username */ unget_char(ch, file); @@ -239,6 +251,9 @@ goto eof; } Debug(DPARS, ("load_entry()...uid %d, gid %d\n",e->uid,e->gid)) + } else if (ch == '*') { + ecode = e_cmd; + goto eof; } e->uid = pw->pw_uid; @@ -247,24 +262,52 @@ /* copy and fix up environment. some variables are just defaults and * others are overrides. */ - e->envp = env_copy(envp); + if ((e->envp = env_copy(envp)) == NULL) { + ecode = e_none; + goto eof; + } if (!env_get("SHELL", e->envp)) { - sprintf(envstr, "SHELL=%s", _PATH_BSHELL); - e->envp = env_set(e->envp, envstr); + snprintf(envstr, MAX_ENVSTR, "SHELL=%s", _PATH_BSHELL); + if ((tenvp = env_set(e->envp, envstr))) { + e->envp = tenvp; + } else { + ecode = e_none; + goto eof; + } } if (!env_get("HOME", e->envp)) { - sprintf(envstr, "HOME=%s", pw->pw_dir); - e->envp = env_set(e->envp, envstr); + snprintf(envstr, MAX_ENVSTR, "HOME=%s", pw->pw_dir); + if ((tenvp = env_set(e->envp, envstr))) { + e->envp = tenvp; + } else { + ecode = e_none; + goto eof; + } } if (!env_get("PATH", e->envp)) { - sprintf(envstr, "PATH=%s", _PATH_DEFPATH); - e->envp = env_set(e->envp, envstr); + snprintf(envstr, MAX_ENVSTR, "PATH=%s", _PATH_DEFPATH); + if ((tenvp = env_set(e->envp, envstr))) { + e->envp = tenvp; + } else { + ecode = e_none; + goto eof; + } + } + snprintf(envstr, MAX_ENVSTR, "%s=%s", "LOGNAME", pw->pw_name); + if ((tenvp = env_set(e->envp, envstr))) { + e->envp = tenvp; + } else { + ecode = e_none; + goto eof; } - sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name); - e->envp = env_set(e->envp, envstr); #if defined(BSD) - sprintf(envstr, "%s=%s", "USER", pw->pw_name); - e->envp = env_set(e->envp, envstr); + snprintf(envstr, MAX_ENVSTR, "%s=%s", "USER", pw->pw_name); + if ((tenvp = env_set(e->envp, envstr))) { + e->envp = tenvp; + } else { + ecode = e_none; + goto eof; + } #endif Debug(DPARS, ("load_entry()...about to parse command\n")) @@ -277,6 +320,10 @@ ch = get_string(cmd, MAX_COMMAND, file, "\n"); /* a file without a \n before the EOF is rude, so we'll complain... + + CK 2010-04-14: this code will never be reached. All calls to + load_entry are proceeded by calls to load_env, which aborts on EOF, and + where load_env fails, the code bails out. */ if (ch == EOF) { ecode = e_cmd; @@ -285,7 +332,10 @@ /* got the command in the 'cmd' string; save it in *e. */ - e->cmd = strdup(cmd); + if ((e->cmd = strdup(cmd)) == NULL) { + ecode = e_none; + goto eof; + } Debug(DPARS, ("load_entry()...returning successfully\n")) @@ -294,6 +344,10 @@ return e; eof: + if (e->envp) + env_free(e->envp); + if (e->cmd) + free(e->cmd); free(e); if (ecode != e_none && error_func) (*error_func)(ecodes[(int)ecode]); @@ -414,7 +468,7 @@ * sent as a 0 since there is no offset either. */ ch = get_number(&num3, 0, PPC_NULL, ch, file); - if (ch == EOF) + if (ch == EOF || num3 <= 0) return EOF; } else { /* no step. default==1. @@ -422,6 +476,17 @@ num3 = 1; } + /* Explicitly check for sane values. Certain combinations of ranges and + * steps which should return EOF don't get picked up by the code below, + * eg: + * 5-64/30 * * * * touch /dev/null + * + * Code adapted from set_elements() where this error was probably intended + * to be catched. + */ + if (num1 < low || num1 > high || num2 < low || num2 > high) + return EOF; + /* range. set all elements from num1 to num2, stepping * by num3. (the step is a downward-compatible extension * proposed conceptually by bob@acornrc, syntactically @@ -464,6 +529,10 @@ } *pc = '\0'; + if (len == 0) { + return EOF; + } + /* try to find the name in the name list */ if (names) { --- cron-3.0pl1.orig/database.c +++ cron-3.0pl1/database.c @@ -24,29 +24,58 @@ #include "cron.h" +#define __USE_GNU /* For O_NOFOLLOW */ #include +#undef __USE_GNU #include #include - +#include #define TMAX(a,b) ((a)>(b)?(a):(b)) +/* Try to get maximum path name -- this isn't really correct, but we're +going to be lazy */ + +#ifndef PATH_MAX + +#ifdef MAXPATHLEN +#define PATH_MAX MAXPATHLEN +#else +#define PATH_MAX 2048 +#endif + +#endif /* ifndef PATH_MAX */ static void process_crontab __P((char *, char *, char *, struct stat *, cron_db *, cron_db *)); +#ifdef DEBIAN +static int valid_name (char *filename); +static user *get_next_system_crontab __P((user *)); +#endif +void force_rescan_user(cron_db *old_db, cron_db *new_db, const char *fname, time_t old_mtime); + +static void add_orphan(const char *uname, const char *fname, const char *tabname); +static void free_orphan(orphan *o); void load_database(old_db) cron_db *old_db; { - DIR *dir; + DIR *dir; struct stat statbuf; struct stat syscron_stat; DIR_T *dp; cron_db new_db; user *u, *nu; +#ifdef DEBIAN + struct stat syscrond_stat; + struct stat syscrond_file_stat; + + char syscrond_fname[PATH_MAX+1]; + int syscrond_change = 0; +#endif Debug(DLOAD, ("[%d] load_database()\n", getpid())) @@ -56,13 +85,63 @@ */ if (stat(SPOOL_DIR, &statbuf) < OK) { log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR); - (void) exit(ERROR_EXIT); + statbuf.st_mtime = 0; } /* track system crontab file */ - if (stat(SYSCRONTAB, &syscron_stat) < OK) + if (stat(SYSCRONTAB, &syscron_stat) < OK) { + log_it("CRON", getpid(), "STAT FAILED", SYSCRONTAB); syscron_stat.st_mtime = 0; + } + +#ifdef DEBIAN + /* 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); + syscrond_stat.st_mtime = 0; + } + + /* 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 + * 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. + * This is especially important on laptops with APM. + */ + if (old_db->sysd_mtime != syscrond_stat.st_mtime) { + syscrond_change = 1; + } else { + /* Look through the individual files */ + user *systab; + + Debug(DLOAD, ("[%d] system dir mtime unch, check files now.\n", + getpid())) + + for (systab = old_db->head; + (systab = get_next_system_crontab (systab)) != NULL; + systab = systab->next) { + + sprintf(syscrond_fname, "%s/%s", SYSCRONDIR, + systab->name + 8); + + Debug(DLOAD, ("\t%s:", syscrond_fname)) + + if (stat(syscrond_fname, &syscrond_file_stat) < OK) + syscrond_file_stat.st_mtime = 0; + + if (syscrond_file_stat.st_mtime != systab->mtime || + systab->mtime == 0) { + syscrond_change = 1; + } + + Debug(DLOAD, (" [checked]\n")) + } + } +#endif /* DEBIAN */ /* if spooldir's mtime has not changed, we don't need to fiddle with * the database. @@ -71,7 +150,14 @@ * so is guaranteed to be different than the stat() mtime the first * time this function is called. */ - if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime)) { +#ifdef DEBIAN + if ((old_db->user_mtime == statbuf.st_mtime) && + (old_db->sys_mtime == syscron_stat.st_mtime) && + (!syscrond_change)) { +#else + if ((old_db->user_mtime == statbuf.st_mtime) && + (old_db->sys_mtime == syscron_stat.st_mtime)) { +#endif Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n", getpid())) return; @@ -82,27 +168,70 @@ * actually changed. Whatever is left in the old database when * we're done is chaff -- crontabs that disappeared. */ - new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime); + new_db.user_mtime = statbuf.st_mtime; + new_db.sys_mtime = syscron_stat.st_mtime; +#ifdef DEBIAN + new_db.sysd_mtime = syscrond_stat.st_mtime; +#endif new_db.head = new_db.tail = NULL; if (syscron_stat.st_mtime) { - process_crontab("root", "*system*", + process_crontab(SYSUSERNAME, "*system*", SYSCRONTAB, &syscron_stat, &new_db, old_db); } +#ifdef DEBIAN + /* Read all the package crontabs. */ + if (!(dir = opendir(SYSCRONDIR))) { + log_it("CRON", getpid(), "OPENDIR FAILED", SYSCRONDIR); + } + + while (dir != NULL && NULL != (dp = readdir(dir))) { + char fname[MAXNAMLEN+1], + tabname[PATH_MAX+1]; + + + /* avoid file names beginning with ".". this is good + * because we would otherwise waste two guaranteed calls + * to stat() for . and .., and also because package names + * starting with a period are just too nasty to consider. + */ + if (dp->d_name[0] == '.') + continue; + + /* skipfile names with letters outside the set + * [A-Za-z0-9_-], like run-parts. + */ + if (!valid_name(dp->d_name)) + continue; + + /* Generate the "fname" */ + (void) strcpy(fname,"*system*"); + (void) strcat(fname, dp->d_name); + sprintf(tabname,"%s/%s", SYSCRONDIR, dp->d_name); + + /* statbuf is used as working storage by process_crontab() -- + current contents are irrelevant */ + process_crontab(SYSUSERNAME, fname, tabname, + &statbuf, &new_db, old_db); + + } + if (dir) + closedir(dir); +#endif + /* we used to keep this dir open all the time, for the sake of * efficiency. however, we need to close it in every fork, and * we fork a lot more often than the mtime of the dir changes. */ if (!(dir = opendir(SPOOL_DIR))) { log_it("CRON", getpid(), "OPENDIR FAILED", SPOOL_DIR); - (void) exit(ERROR_EXIT); } - while (NULL != (dp = readdir(dir))) { + while (dir != NULL && NULL != (dp = readdir(dir))) { char fname[MAXNAMLEN+1], - tabname[MAXNAMLEN+1]; + tabname[PATH_MAX+1]; /* avoid file names beginning with ".". this is good * because we would otherwise waste two guaranteed calls @@ -113,12 +242,13 @@ continue; (void) strcpy(fname, dp->d_name); - sprintf(tabname, CRON_TAB(fname)); + snprintf(tabname, PATH_MAX+1, CRON_TAB(fname)); process_crontab(fname, fname, tabname, &statbuf, &new_db, old_db); } - closedir(dir); + if (dir) + closedir(dir); /* if we don't do this, then when our children eventually call * getpwnam() in do_command.c's child_process to verify MAILTO=, @@ -201,29 +331,146 @@ { struct passwd *pw = NULL; int crontab_fd = OK - 1; - user *u; + user *u = NULL; +#ifdef DEBIAN + /* If the name begins with *system*, don't worry about password - + it's part of the system crontab */ + if (strncmp(fname, "*system*", 8) && !(pw = getpwnam(uname))) { +#else if (strcmp(fname, "*system*") && !(pw = getpwnam(uname))) { +#endif /* file doesn't have a user in passwd file. */ - log_it(fname, getpid(), "ORPHAN", "no passwd entry"); + if (strncmp(fname, "tmp.", 4)) { + /* don't log these temporary files */ + log_it(fname, getpid(), "ORPHAN", "no passwd entry"); + add_orphan(uname, fname, tabname); + } goto next_crontab; } - if ((crontab_fd = open(tabname, O_RDONLY, 0)) < OK) { + if (pw) { + /* Path for user crontabs (including root's!) */ + if ((crontab_fd = open(tabname, O_RDONLY|O_NOFOLLOW, 0)) < OK) { /* crontab not accessible? */ log_it(fname, getpid(), "CAN'T OPEN", tabname); goto next_crontab; - } + } - if (fstat(crontab_fd, statbuf) < OK) { + if (fstat(crontab_fd, statbuf) < OK) { log_it(fname, getpid(), "FSTAT FAILED", tabname); goto next_crontab; - } + } + /* Check to make sure that the crontab is owned by the correct user + (or root) */ + if (statbuf->st_uid != pw->pw_uid && statbuf->st_uid != ROOT_UID) { + log_it(fname, getpid(), "WRONG FILE OWNER", tabname); + force_rescan_user(old_db, new_db, fname, 0); + goto next_crontab; + } + + /* Check to make sure that the crontab is a regular file */ + if (!S_ISREG(statbuf->st_mode)) { + log_it(fname, getpid(), "NOT A REGULAR FILE", tabname); + goto next_crontab; + } + + /* Check to make sure that the crontab's permissions are secure */ + if ((statbuf->st_mode & 07777) != 0600) { + log_it(fname, getpid(), "INSECURE MODE (mode 0600 expected)", tabname); + force_rescan_user(old_db, new_db, fname, 0); + goto next_crontab; + } + + /* Check to make sure that there are no hardlinks to the crontab */ + if (statbuf->st_nlink != 1) { + log_it(fname, getpid(), "NUMBER OF HARD LINKS > 1", tabname); + force_rescan_user(old_db, new_db, fname, 0); + goto next_crontab; + } + } else { + /* System crontab path. These can be symlinks, but the + symlink and the target must be owned by root. */ + if (lstat(tabname, statbuf) < OK) { + log_it(fname, getpid(), "LSTAT FAILED", tabname); + goto next_crontab; + } + if (S_ISLNK(statbuf->st_mode) && statbuf->st_uid != ROOT_UID) { + log_it(fname, getpid(), "WRONG SYMLINK OWNER", tabname); + force_rescan_user(old_db, new_db, fname, 0); + goto next_crontab; + } + if ((crontab_fd = open(tabname, O_RDONLY, 0)) < OK) { + /* crontab not accessible? + + If tabname is a symlink, it's most probably just broken, so + we force a rescan. Once the link is fixed, it will get picked + up and processed again. If tabname is a regular file, this + error is bad so we skip it instead. + */ + if (S_ISLNK(statbuf->st_mode)) { + log_it(fname, getpid(), "CAN'T OPEN SYMLINK", tabname); + force_rescan_user(old_db, new_db, fname, 0); + goto next_crontab; + } else { + log_it(fname, getpid(), "CAN'T OPEN", tabname); + goto next_crontab; + } + } + + if (fstat(crontab_fd, statbuf) < OK) { + log_it(fname, getpid(), "FSTAT FAILED", tabname); + goto next_crontab; + } + + /* Check to make sure that the crontab is owned by root */ + if (statbuf->st_uid != ROOT_UID) { + log_it(fname, getpid(), "WRONG FILE OWNER", tabname); + force_rescan_user(old_db, new_db, fname, 0); + goto next_crontab; + } + + /* Check to make sure that the crontab is a regular file */ + if (!S_ISREG(statbuf->st_mode)) { + log_it(fname, getpid(), "NOT A REGULAR FILE", tabname); + goto next_crontab; + } + /* Check to make sure that the crontab is writable only by root + * This should really be in sync with the check for users above + * (mode 0600). An upgrade path could be implemented for 4.1 + */ + if ((statbuf->st_mode & S_IWGRP) || (statbuf->st_mode & S_IWOTH)) { + log_it(fname, getpid(), "INSECURE MODE (group/other writable)", tabname); + force_rescan_user(old_db, new_db, fname, 0); + goto next_crontab; + } + /* Technically, we should also check whether the parent dir is + * writable, and so on. This would only make proper sense for + * regular files; we can't realistically check all possible + * security issues resulting from symlinks. We'll just assume that + * root will handle responsible when creating them. + */ + + /* Check to make sure that there are no hardlinks to the crontab */ + if (statbuf->st_nlink != 1) { + log_it(fname, getpid(), "NUMBER OF HARD LINKS > 1", tabname); + force_rescan_user(old_db, new_db, fname, 0); + goto next_crontab; + } + } + /* + * The link count check is not sufficient (the owner may + * delete their original link, reducing the link count back to + * 1), but this is all we've got. + */ Debug(DLOAD, ("\t%s:", fname)) - u = find_user(old_db, fname); + + if (old_db != NULL) + u = find_user(old_db, fname); + if (u != NULL) { /* if crontab has not changed since we last read it * in, then we can just use our existing entry. @@ -247,11 +494,21 @@ free_user(u); log_it(fname, getpid(), "RELOAD", tabname); } - u = load_user(crontab_fd, pw, fname); + + u = load_user(crontab_fd, pw, uname, fname, tabname); if (u != NULL) { u->mtime = statbuf->st_mtime; link_user(new_db, u); - } + } else { + /* The crontab we attempted to load contains a syntax error. A + * fix won't get picked up by the regular change detection + * code, so we force a rescan. statbuf->st_mtime still contains + * the file's mtime, so we use it to rescan only when an update + * has actually taken place. + */ + force_rescan_user(old_db, new_db, fname, statbuf->st_mtime); + } + next_crontab: if (crontab_fd >= OK) { @@ -259,3 +516,166 @@ close(crontab_fd); } } + +#ifdef DEBIAN + +#include + +/* True or false? Is this a valid filename? */ + +/* Taken from Clint Adams 'run-parts' version to support lsb style + names, originally GPL, but relicensed to cron license per e-mail of + 27 September 2003. I've changed it to do regcomp() only once. */ + +static int +valid_name(char *filename) +{ + static regex_t hierre, tradre, excsre, classicalre; + static int donere = 0; + + if (!donere) { + donere = 1; + if (regcomp(&hierre, "^_?([a-z0-9_.]+-)+[a-z0-9]+$", + REG_EXTENDED | REG_NOSUB) + || regcomp(&excsre, "^[a-z0-9-].*dpkg-(old|dist)$", + REG_EXTENDED | REG_NOSUB) + || regcomp(&tradre, "^[a-z0-9][a-z0-9-]*$", REG_NOSUB) + || regcomp(&classicalre, "^[a-zA-Z0-9_-]+$", + REG_EXTENDED | REG_NOSUB)) { + log_it("CRON", getpid(), "REGEX FAILED", "valid_name"); + (void) exit(ERROR_EXIT); + } + } + if (lsbsysinit_mode) { + if (!regexec(&hierre, filename, 0, NULL, 0)) { + return regexec(&excsre, filename, 0, NULL, 0); + } else { + return !regexec(&tradre, filename, 0, NULL, 0); + } + } + /* Old standard style */ + return !regexec(&classicalre, filename, 0, NULL, 0); +} + + +static user * +get_next_system_crontab (curtab) + user *curtab; +{ + for ( ; curtab != NULL; curtab = curtab->next) + if (!strncmp(curtab->name, "*system*", 8) && curtab->name [8]) + break; + return curtab; +} + +#endif + +/* Force rescan of a crontab the next time cron wakes up + * + * cron currently only detects changes caused by an mtime update; it does not + * detect other attribute changes such as UID or mode. To allow cron to recover + * from errors of that nature as well, this function removes the crontab from + * the old DB (if present there) and adds an empty crontab to the new DB with + * a given mtime. Specifying mtime as 0 will force a rescan the next time the + * daemon wakes up. + */ +void +force_rescan_user(cron_db *old_db, cron_db *new_db, const char *fname, time_t old_mtime) +{ + user *u; + + /* Remove from old DB and free resources */ + u = find_user(old_db, fname); + if (u != NULL) { + Debug(DLOAD, (" [delete old data]")) + unlink_user(old_db, u); + free_user(u); + } + + /* Allocate an empty crontab with the specified mtime, add it to new DB */ + if ((u = (user *) malloc(sizeof(user))) == NULL) { + errno = ENOMEM; + } + if ((u->name = strdup(fname)) == NULL) { + free(u); + errno = ENOMEM; + } + u->mtime = old_mtime; + u->crontab = NULL; +#ifdef WITH_SELINUX + u->scontext = NULL; +#endif + Debug(DLOAD, ("\t%s: [added empty placeholder to force rescan]\n", fname)) + link_user(new_db, u); +} + +/* This fix was taken from Fedora cronie */ +static orphan *orphans; + +static void +free_orphan(orphan *o) { + free(o->tabname); + free(o->fname); + free(o->uname); + free(o); +} + +void +check_orphans(cron_db *db) { + orphan *prev_orphan = NULL; + orphan *o = orphans; + struct stat statbuf; + + while (o != NULL) { + if (getpwnam(o->uname) != NULL) { + orphan *next = o->next; + + if (prev_orphan == NULL) { + orphans = next; + } else { + prev_orphan->next = next; + } + + process_crontab(o->uname, o->fname, o->tabname, + &statbuf, db, NULL); + + /* process_crontab could have added a new orphan */ + if (prev_orphan == NULL && orphans != next) { + prev_orphan = orphans; + } + free_orphan(o); + o = next; + } else { + prev_orphan = o; + o = o->next; + } + } +} + +static void +add_orphan(const char *uname, const char *fname, const char *tabname) { + orphan *o; + + o = calloc(1, sizeof(*o)); + if (o == NULL) + return; + + if (uname) + if ((o->uname=strdup(uname)) == NULL) + goto cleanup; + + if (fname) + if ((o->fname=strdup(fname)) == NULL) + goto cleanup; + + if (tabname) + if ((o->tabname=strdup(tabname)) == NULL) + goto cleanup; + + o->next = orphans; + orphans = o; + return; + +cleanup: + free_orphan(o); +} --- cron-3.0pl1.orig/popen.c +++ cron-3.0pl1/popen.c @@ -29,9 +29,14 @@ #endif /* not lint */ #include "cron.h" -#include +#include + +#if defined(BSD) || defined(POSIX) +# include +#endif +#define MAX_ARGS 100 #define WANT_GLOBBING 0 /* @@ -43,14 +48,15 @@ static int fds; FILE * -cron_popen(program, type) +cron_popen(program, type, e) char *program, *type; + entry *e; { register char *cp; FILE *iop; int argc, pdes[2]; PID_T pid; - char *argv[100]; + char *argv[MAX_ARGS + 1]; #if WANT_GLOBBING char **pop, *vv[2]; int gargc; @@ -58,7 +64,7 @@ extern char **glob(), **copyblk(); #endif - if (*type != 'r' && *type != 'w' || type[1]) + if ((*type != 'r' && *type != 'w') || type[1]) return(NULL); if (!pids) { @@ -72,9 +78,10 @@ return(NULL); /* break up string into pieces */ - for (argc = 0, cp = program;; cp = NULL) + for (argc = 0, cp = program; argc < MAX_ARGS; cp = NULL) if (!(argv[argc++] = strtok(cp, " \t\n"))) break; + argv[MAX_ARGS] = NULL; #if WANT_GLOBBING /* glob each piece */ @@ -93,7 +100,7 @@ #endif iop = NULL; - switch(pid = vfork()) { + switch(pid = fork()) { case -1: /* error */ (void)close(pdes[0]); (void)close(pdes[1]); @@ -114,6 +121,34 @@ } (void)close(pdes[1]); } + /* set our directory, uid and gid. Set gid first, since once + * we set uid, we've lost root privleges. + */ + if (setgid(e->gid) !=0) { + char msg[256]; + snprintf(msg, 256, "popen:setgid(%lu) failed: %s", + (unsigned long) e->gid, strerror(errno)); + log_it("CRON",getpid(),"error",msg); + exit(ERROR_EXIT); + } +# if defined(BSD) || defined(POSIX) + if (initgroups(env_get("LOGNAME", e->envp), e->gid) !=0) { + char msg[256]; + snprintf(msg, 256, "popen:initgroups(%lu) failed: %s", + (unsigned long) e->gid, strerror(errno)); + log_it("CRON",getpid(),"error",msg); + exit(ERROR_EXIT); + } +# endif + if (setuid(e->uid) !=0) { + char msg[256]; + snprintf(msg, 256, "popen: setuid(%lu) failed: %s", + (unsigned long) e->uid, strerror(errno)); + log_it("CRON",getpid(),"error",msg); + exit(ERROR_EXIT); + } + chdir(env_get("HOME", e->envp)); + #if WANT_GLOBBING execvp(gargv[0], gargv); #else @@ -146,7 +181,7 @@ FILE *iop; { register int fdes; - int omask; + sigset_t omask, mask; WAIT_T stat_loc; PID_T pid; @@ -157,10 +192,15 @@ if (pids == 0 || pids[fdes = fileno(iop)] == 0) return(-1); (void)fclose(iop); - omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); - while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1) - ; - (void)sigsetmask(omask); + sigemptyset(&mask); + sigaddset(&mask, SIGQUIT); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGHUP); + sigprocmask(SIG_BLOCK, &mask, &omask); + pid = waitpid(pids[fdes], &stat_loc, 0); + sigprocmask(SIG_SETMASK, &omask, NULL); pids[fdes] = 0; - return (pid == -1 ? -1 : WEXITSTATUS(stat_loc)); + if (pid == -1 || !WIFEXITED(stat_loc)) + return -1; + return WEXITSTATUS(stat_loc); } --- cron-3.0pl1.orig/cron.h +++ cron-3.0pl1/cron.h @@ -40,6 +40,20 @@ #include "config.h" #include "externs.h" +#if SYS_TIME_H +# include +#else +# include +#endif + + +#ifdef WITH_SELINUX +#include +#endif + +#define SYSUSERNAME "root" + + /* these are really immutable, and are * defined for symbolic convenience only * TRUE, FALSE, and ERR must be distinct @@ -66,8 +80,8 @@ #define OK_EXIT 0 /* exit() with this is considered 'normal' */ #define MAX_FNAME 100 /* max length of internally generated fn */ #define MAX_COMMAND 1000 /* max length of internally generated cmd */ -#define MAX_ENVSTR 1000 /* max length of envvar=value\0 strings */ -#define MAX_TEMPSTR 100 /* obvious */ +#define MAX_TEMPSTR 1000 /* max length of envvar=value\0 strings */ +#define MAX_ENVSTR MAX_TEMPSTR /* DO NOT change - buffer overruns otherwise */ #define MAX_UNAME 20 /* max length of username, should be overkill */ #define ROOT_UID 0 /* don't change this, it really must be root */ #define ROOT_USER "root" /* ditto */ @@ -105,7 +119,7 @@ #if DEBUGGING # define Debug(mask, message) \ - if ( (DebugFlags & (mask) ) == (mask) ) \ + if ( (DebugFlags & (mask) ) ) \ printf message; #else /* !DEBUGGING */ # define Debug(mask, message) \ @@ -118,6 +132,16 @@ LineNumber = ln; \ } +typedef int time_min; + +/* Log levels */ +#define CRON_LOG_JOBSTART 0x01 +#define CRON_LOG_JOBEND 0x02 +#define CRON_LOG_JOBFAILED 0x04 +#define CRON_LOG_JOBPID 0x08 + +#define SECONDS_PER_MINUTE 60 + #define FIRST_MINUTE 0 #define LAST_MINUTE 59 #define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1) @@ -160,6 +184,8 @@ #define DOM_STAR 0x01 #define DOW_STAR 0x02 #define WHEN_REBOOT 0x04 +#define MIN_STAR 0x08 +#define HR_STAR 0x10 } entry; /* the crontab database will be a list of the @@ -174,13 +200,26 @@ char *name; time_t mtime; /* last modtime of crontab */ entry *crontab; /* this person's crontab */ +#ifdef WITH_SELINUX + security_context_t scontext; /* SELinux security context */ +#endif } user; typedef struct _cron_db { user *head, *tail; /* links */ - time_t mtime; /* last modtime on spooldir */ + time_t user_mtime; /* last modtime on spooldir */ + time_t sys_mtime; /* last modtime on system crontab */ +#ifdef DEBIAN + time_t sysd_mtime; /* last modtime on system crondir */ +#endif } cron_db; +typedef struct _orphan { + struct _orphan *next; /* link */ + char *uname; + char *fname; + char *tabname; +} orphan; void set_cron_uid __P((void)), set_cron_cwd __P((void)), @@ -198,19 +237,23 @@ acquire_daemonlock __P((int)), skip_comments __P((FILE *)), log_it __P((char *, int, char *, char *)), - log_close __P((void)); + log_close __P((void)), + check_orphans __P((cron_db *)); int job_runqueue __P((void)), set_debug_flags __P((char *)), get_char __P((FILE *)), get_string __P((char *, int, FILE *, char *)), swap_uids __P((void)), + swap_uids_back __P((void)), load_env __P((char *, FILE *)), cron_pclose __P((FILE *)), strcmp_until __P((char *, char *, int)), allowed __P((char *)), strdtb __P((char *)); +long get_gmtoff(time_t *, struct tm *); + char *env_get __P((char *, char **)), *arpadate __P((time_t *)), *mkprints __P((unsigned char *, unsigned int)), @@ -219,13 +262,13 @@ **env_copy __P((char **)), **env_set __P((char **, char *)); -user *load_user __P((int, struct passwd *, char *)), +user *load_user __P((int, struct passwd *, char *, char *, char *)), *find_user __P((cron_db *, char *)); entry *load_entry __P((FILE *, void (*)(), struct passwd *, char **)); -FILE *cron_popen __P((char *, char *)); +FILE *cron_popen __P((char *, char *, entry *)); /* in the C tradition, we only create @@ -254,7 +297,16 @@ char *ProgramName; int LineNumber; -time_t TargetTime; +time_t StartTime; +time_min timeRunning; +time_min virtualTime; +time_min clockTime; +static long GMToff; + +int stay_foreground; +int lsbsysinit_mode; +int log_level = 1; +char cron_default_mail_charset[MAX_ENVSTR] = ""; # if DEBUGGING int DebugFlags; @@ -268,8 +320,14 @@ *MonthNames[], *DowNames[], *ProgramName; +extern int lsbsysinit_mode; +extern int log_level; extern int LineNumber; -extern time_t TargetTime; +extern time_t StartTime; +extern time_min timeRunning; +extern time_min virtualTime; +extern time_min clockTime; +extern char cron_default_mail_charset[MAX_ENVSTR]; # if DEBUGGING extern int DebugFlags; extern char *DebugFlagNames[]; --- cron-3.0pl1.orig/pathnames.h +++ cron-3.0pl1/pathnames.h @@ -19,7 +19,7 @@ * $Id: pathnames.h,v 1.3 1994/01/15 20:43:43 vixie Exp $ */ -#if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX) +#if (defined(BSD)) && (BSD >= 199103) || defined(__linux__) || defined(AIX) || defined(__GNU__) || defined(__GLIBC__) # include #endif /*BSD*/ @@ -28,7 +28,7 @@ * to; SPOOL_DIR, ALLOW_FILE, DENY_FILE, and LOG_FILE * are all relative to this directory. */ -#define CRONDIR "/var/cron" +#define CRONDIR "/var/spool/cron" #endif /* SPOOLDIR is where the crontabs live. @@ -39,7 +39,7 @@ * newer than they were last time around (or which * didn't exist last time around...) */ -#define SPOOL_DIR "tabs" +#define SPOOL_DIR "crontabs" /* undefining these turns off their features. note * that ALLOW_FILE and DENY_FILE must both be defined @@ -47,9 +47,14 @@ * LOG_FILE or SYSLOG is defined, we don't log. If * both are defined, we log both ways. */ +#ifdef DEBIAN +#define ALLOW_FILE "/etc/cron.allow" /*-*/ +#define DENY_FILE "/etc/cron.deny" /*-*/ +#else #define ALLOW_FILE "allow" /*-*/ #define DENY_FILE "deny" /*-*/ -#define LOG_FILE "log" /*-*/ +#endif +/* #define LOG_FILE "log" -*/ /* where should the daemon stick its PID? */ @@ -58,15 +63,20 @@ #else # define PIDDIR "/etc/" #endif -#define PIDFILE "%scron.pid" +#define PIDFILE "%scrond.pid" /* 4.3BSD-style crontab */ #define SYSCRONTAB "/etc/crontab" - +#ifdef DEBIAN + /* where package specific crontabs live */ +#define SYSCRONDIR "/etc/cron.d" +#endif /* what editor to use if no EDITOR or VISUAL * environment variable specified. */ -#if defined(_PATH_VI) +#if defined(DEBIAN) +# define EDITOR "/usr/bin/sensible-editor" +#elif defined(_PATH_VI) # define EDITOR _PATH_VI #else # define EDITOR "/usr/ucb/vi" @@ -79,3 +89,29 @@ #ifndef _PATH_DEFPATH # define _PATH_DEFPATH "/usr/bin:/bin" #endif + +#ifndef _PATH_DEFPATH_ROOT +# define _PATH_DEFPATH_ROOT "/usr/sbin:/usr/bin:/sbin:/bin" +#endif + + +#ifdef DEBIAN +#ifndef CRONDIR_MODE + /* Create mode for CRONDIR; must be in sync with + * packaging + */ +#define CRONDIR_MODE 0755 +#endif +#ifndef SPOOL_DIR_MODE + /* Create mode for SPOOL_DIR; must be in sync with + * packaging + */ +#define SPOOL_DIR_MODE 01730 +#endif +#ifndef SPOOL_DIR_GROUP + /* Chown SPOOL_DIR to this group (needed by Debian's + * SGID crontab feature) + */ +#define SPOOL_DIR_GROUP "crontab" +#endif +#endif --- cron-3.0pl1.orig/Makefile +++ cron-3.0pl1/Makefile @@ -55,34 +55,39 @@ INCLUDE = -I. #INCLUDE = #<> -LIBS = +LIBS = $(PAM_LIBS) $(SELINUX_LIBS) $(AUDIT_LIBS) #<> -#OPTIM = -O -OPTIM = -g +OPTIM = -O2 +#OPTIM = -g #<> # (ATT untested) #COMPAT = -DATT #(BSD is only needed if does not define it, as on ULTRIX) #COMPAT = -DBSD # (POSIX) -#COMPAT = -DPOSIX +COMPAT = -DPOSIX #<> LINTFLAGS = -hbxa $(INCLUDE) $(COMPAT) $(DEBUGGING) #<> #CC = vcc #<> -DEFS = +# Allow override from command line +DEBUG_DEFS ?= -DDEBUGGING=0 +# The -DUSE_SIGCHLD is needed for the Alpha port +DEFS = -DDEBIAN -DUSE_SIGCHLD $(DEBUG_DEFS) $(PAM_DEFS) $(SELINUX_DEFS) $(AUDIT_DEFS) #(SGI IRIX systems need this) #DEFS = -D_BSD_SIGNALS -Dconst= #<> #INSTALL = installbsd -INSTALL = install +INSTALL = install -s #<> -LDFLAGS = +# LDFLAGS = -s +# Let install do the strip + #################################### end configurable stuff SHELL = /bin/sh -CFLAGS = $(OPTIM) $(INCLUDE) $(COMPAT) $(DEFS) +CFLAGS += $(INCLUDE) $(COMPAT) $(DEFS) INFOS = README CHANGES FEATURES INSTALL CONVERSION THANKS MAIL MANPAGES = bitstring.3 crontab.5 crontab.1 cron.8 putman.sh @@ -113,13 +118,14 @@ $(CC) $(LDFLAGS) -o crontab $(CRONTAB_OBJ) $(LIBS) install : all - $(INSTALL) -c -m 111 -o root -s cron $(DESTSBIN)/ - $(INSTALL) -c -m 4111 -o root -s crontab $(DESTBIN)/ + $(INSTALL) -c -m 755 -o root cron $(DESTSBIN)/ + $(INSTALL) -c -m 4755 -o root crontab $(DESTBIN)/ sh putman.sh crontab.1 $(DESTMAN) sh putman.sh cron.8 $(DESTMAN) sh putman.sh crontab.5 $(DESTMAN) -clean :; rm -f *.o cron crontab a.out core tags *~ #* +clean : + rm -f *.o cron crontab a.out core tags *~ #* kit : $(SHAR_SOURCE) makekit -m -s99k $(SHAR_SOURCE) --- cron-3.0pl1.orig/env.c +++ cron-3.0pl1/env.c @@ -28,7 +28,8 @@ { register char **p = (char **) malloc(sizeof(char **)); - p[0] = NULL; + if (p) + p[0] = NULL; return (p); } @@ -39,6 +40,9 @@ { char **p; + if(!envp) + return; + for (p = envp; *p; p++) free(*p); free(envp); @@ -55,8 +59,18 @@ for (count = 0; envp[count] != NULL; count++) ; p = (char **) malloc((count+1) * sizeof(char *)); /* 1 for the NULL */ + if (p == NULL) { + errno = ENOMEM; + return NULL; + } for (i = 0; i < count; i++) - p[i] = strdup(envp[i]); + if ((p[i] = strdup(envp[i])) == NULL) { + while (--i >= 0) + (void) free(p[i]); + free(p); + errno = ENOMEM; + return NULL; + } p[count] = NULL; return (p); } @@ -87,7 +101,11 @@ * save our new one there, and return the existing array. */ free(envp[found]); - envp[found] = strdup(envstr); + if ((envp[found] = strdup(envstr)) == NULL) { + envp[found] = ""; + errno = ENOMEM; + return NULL; + } return (envp); } @@ -98,11 +116,29 @@ */ p = (char **) realloc((void *) envp, (unsigned) ((count+1) * sizeof(char **))); + if (p == NULL) { + errno = ENOMEM; + return NULL; + } p[count] = p[count-1]; - p[count-1] = strdup(envstr); + if ((p[count-1] = strdup(envstr)) == NULL) { + errno = ENOMEM; + return NULL; + } return (p); } +/* The following states are used by load_env(), traversed in order: */ +enum env_state { + NAMEI, /* First char of NAME, may be quote */ + NAME, /* Subsequent chars of NAME */ + EQ1, /* After end of name, looking for '=' sign */ + EQ2, /* After '=', skipping whitespace */ + VALUEI, /* First char of VALUE, may be quote */ + VALUE, /* Subsequent chars of VALUE */ + FINI, /* All done, skipping trailing whitespace */ + ERROR, /* Error */ +}; /* return ERR = end of file * FALSE = not an env setting (file was repositioned) @@ -115,31 +151,104 @@ { long filepos; int fileline; - char name[MAX_TEMPSTR], val[MAX_ENVSTR]; - int fields; + enum env_state state; + char name[MAX_ENVSTR], val[MAX_ENVSTR]; + char quotechar, *c, *str; filepos = ftell(f); fileline = LineNumber; skip_comments(f); - if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n")) + if (EOF == get_string(envstr, MAX_ENVSTR - 1, f, "\n")) return (ERR); + envstr[MAX_ENVSTR - 1] = '\0'; + Debug(DPARS, ("load_env, read <%s>\n", envstr)) - name[0] = val[0] = '\0'; - fields = sscanf(envstr, "%[^ =] = %[^\n#]", name, val); - if (fields != 2) { - Debug(DPARS, ("load_env, not 2 fields (%d)\n", fields)) + bzero(name, sizeof name); + bzero(val, sizeof val); + str = name; + state = NAMEI; + quotechar = '\0'; + c = envstr; + while (state != ERROR && *c) { + switch (state) { + case NAMEI: + case VALUEI: + if (*c == '\'' || *c == '"') + quotechar = *c++; + state++; + /* FALLTHROUGH */ + case NAME: + case VALUE: + if (quotechar) { + if (*c == quotechar) { + state++; + c++; + break; + } + if (state == NAME && *c == '=') { + state = ERROR; + break; + } + } else { + if (state == NAME) { + if (isspace((unsigned char)*c)) { + c++; + state++; + break; + } + if (*c == '=') { + state++; + break; + } + } + } + *str++ = *c++; + break; + + case EQ1: + if (*c == '=') { + state++; + str = val; + quotechar = '\0'; + } else { + if (!isspace((unsigned char)*c)) + state = ERROR; + } + c++; + break; + + case EQ2: + case FINI: + if (isspace((unsigned char)*c)) + c++; + else + state++; + break; + + default: + abort(); + } + } + if (state != FINI && !(state == VALUE && !quotechar)) { + Debug(DPARS, ("load_env, not an env var, state = %d\n", state)) fseek(f, filepos, 0); Set_LineNum(fileline); return (FALSE); } + if (state == VALUE) { + /* End of unquoted value: trim trailing whitespace */ + c = val + strlen(val); + while (c > val && isspace((unsigned char)c[-1])) + *(--c) = '\0'; + } - /* 2 fields from scanf; looks like an env setting - */ + /* 2 fields from parser; looks like an env setting */ /* - * process value string + * This can't overflow because get_string() limited the size of the + * name and val fields. Still, it doesn't hurt to be careful... */ /*local*/{ int len = strdtb(val); @@ -154,12 +263,13 @@ } } + if (strlen(name) + 1 + strlen(val) >= MAX_ENVSTR-1) + return (FALSE); (void) sprintf(envstr, "%s=%s", name, val); Debug(DPARS, ("load_env, <%s> <%s> -> <%s>\n", name, val, envstr)) return (TRUE); } - char * env_get(name, envp) register char *name; @@ -168,7 +278,7 @@ register int len = strlen(name); register char *p, *q; - while (p = *envp++) { + while ((p = *envp++)) { if (!(q = strchr(p, '='))) continue; if ((q - p) == len && !strncmp(p, name, len)) --- cron-3.0pl1.orig/compat.c +++ cron-3.0pl1/compat.c @@ -53,7 +53,10 @@ { char *temp; - temp = malloc(strlen(str) + 1); + if ((temp = malloc(strlen(str) + 1)) == NULL) { + errno = ENOMEM; + return NULL; + } (void) strcpy(temp, str); return temp; } @@ -73,7 +76,7 @@ return sys_errlist[error]; } - sprintf(buf, "Unknown error: %d", error); + snprintf(buf, 32, "Unknown error: %d", error); return buf; } #endif @@ -218,16 +221,18 @@ int overwrite; { char *tmp; + int tmp_size; if (overwrite && getenv(name)) return -1; - if (!(tmp = malloc(strlen(name) + strlen(value) + 2))) { + tmp_size = strlen(name) + strlen(value) + 2; + if (!(tmp = malloc(tmp_size))) { errno = ENOMEM; return -1; } - sprintf("%s=%s", name, value); + snprintf(tmp, tmp_size, "%s=%s", name, value); return putenv(tmp); /* intentionally orphan 'tmp' storage */ } #endif --- cron-3.0pl1.orig/cron.c +++ cron-3.0pl1/cron.c @@ -24,19 +24,16 @@ #include "cron.h" -#include -#if SYS_TIME_H -# include -#else -# include -#endif +#include +#include +#include static void usage __P((void)), run_reboot_jobs __P((cron_db *)), - cron_tick __P((cron_db *)), - cron_sync __P((void)), - cron_sleep __P((void)), + find_jobs __P((time_min, cron_db *, int, int)), + set_time __P((int)), + cron_sleep __P((time_min)), #ifdef USE_SIGCHLD sigchld_handler __P((int)), #endif @@ -46,7 +43,16 @@ static void usage() { - fprintf(stderr, "usage: %s [-x debugflag[,...]]\n", ProgramName); +#if DEBUGGING + char **dflags; + + fprintf(stderr, "usage: %s [-x [", ProgramName); + for(dflags = DebugFlagNames; *dflags; dflags++) + fprintf(stderr, "%s%s", *dflags, dflags[1] ? "," : "]"); + fprintf(stderr, "]\n"); +#else + fprintf(stderr, "usage: %s\n", ProgramName); +#endif exit(ERROR_EXIT); } @@ -57,6 +63,7 @@ char *argv[]; { cron_db database; + char *cs; ProgramName = argv[0]; @@ -74,6 +81,13 @@ #endif (void) signal(SIGHUP, sighup_handler); + /* Reopen stdin in case some idiot closed it before starting + us - it will only be closed, but not having it open here + screws up other things that will be opened */ + if (fdopen(0,"r") == NULL) { + (void) open("dev/null", 0); + } + acquire_daemonlock(0); set_cron_uid(); set_cron_cwd(); @@ -82,6 +96,20 @@ setenv("PATH", _PATH_DEFPATH, 1); #endif + /* Get the default locale character set for the mail + * "Content-Type: ...; charset=" header + */ + setlocale(LC_ALL,""); /* set locale to system defaults or to + that specified by any LC_* env vars */ + setlocale(LC_COLLATE, "C"); /* Except for collation, since load_database() uses a-z */ + /* Except that "US-ASCII" is preferred to "ANSI_x3.4-1968" in MIME, + * even though "ANSI_x3.4-1968" is the official charset name. */ + if ( ( cs = nl_langinfo( CODESET ) ) != 0L && + strcmp(cs, "ANSI_x3.4-1968") != 0 ) + strncpy( cron_default_mail_charset, cs, MAX_ENVSTR ); + else + strcpy( cron_default_mail_charset, "US-ASCII" ); + /* if there are no debug flags turned on, fork as a daemon should. */ # if DEBUGGING @@ -90,7 +118,7 @@ if (0) { # endif (void) fprintf(stderr, "[%d] cron started\n", getpid()); - } else { + } else if (!stay_foreground) { switch (fork()) { case -1: log_it("CRON",getpid(),"DEATH","can't fork"); @@ -100,6 +128,9 @@ /* child process */ log_it("CRON",getpid(),"STARTUP","fork ok"); (void) setsid(); + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); break; default: /* parent process should just die */ @@ -110,28 +141,140 @@ acquire_daemonlock(0); database.head = NULL; database.tail = NULL; - database.mtime = (time_t) 0; + database.sys_mtime = (time_t) 0; + database.user_mtime = (time_t) 0; +#ifdef DEBIAN + database.sysd_mtime = (time_t) 0; +#endif load_database(&database); + + set_time(TRUE); run_reboot_jobs(&database); - cron_sync(); + timeRunning = virtualTime = clockTime; + + /* + * too many clocks, not enough time (Al. Einstein) + * These clocks are in minutes since the epoch (time()/60). + * virtualTime: is the time it *would* be if we woke up + * promptly and nobody ever changed the clock. It is + * monotonically increasing... unless a timejump happens. + * At the top of the loop, all jobs for 'virtualTime' have run. + * timeRunning: is the time we last awakened. + * clockTime: is the time when set_time was last called. + */ while (TRUE) { -# if DEBUGGING - if (!(DebugFlags & DTEST)) -# endif /*DEBUGGING*/ - cron_sleep(); + time_min timeDiff; + int wakeupKind; + + /* ... wait for the time (in minutes) to change ... */ + do { + cron_sleep(timeRunning + 1); + set_time(FALSE); + } while (clockTime == timeRunning); + timeRunning = clockTime; + check_orphans(&database); load_database(&database); - /* do this iteration + /* + * ... calculate how the current time differs from + * our virtual clock. Classify the change into one + * of 4 cases */ - cron_tick(&database); + timeDiff = timeRunning - virtualTime; - /* sleep 1 minute - */ - TargetTime += 60; + Debug(DSCH, ("[%d] pulse: %d = %d - %d\n", + getpid(), timeDiff, timeRunning, virtualTime)); + + /* shortcut for the most common case */ + if (timeDiff == 1) { + virtualTime = timeRunning; + find_jobs(virtualTime, &database, TRUE, TRUE); + } else { + wakeupKind = -1; + if (timeDiff > -(3*MINUTE_COUNT)) + wakeupKind = 0; + if (timeDiff > 0) + wakeupKind = 1; + if (timeDiff > 5) + wakeupKind = 2; + if (timeDiff > (3*MINUTE_COUNT)) + wakeupKind = 3; + + switch (wakeupKind) { + case 1: + /* + * case 1: timeDiff is a small positive number + * (wokeup late) run jobs for each virtual minute + * until caught up. + */ + Debug(DSCH, ("[%d], normal case %d minutes to go\n", + getpid(), timeRunning - virtualTime)) + do { + if (job_runqueue()) + sleep(10); + virtualTime++; + find_jobs(virtualTime, &database, TRUE, TRUE); + } while (virtualTime< timeRunning); + break; + + case 2: + /* + * case 2: timeDiff is a medium-sized positive number, + * for example because we went to DST run wildcard + * jobs once, then run any fixed-time jobs that would + * otherwise be skipped if we use up our minute + * (possible, if there are a lot of jobs to run) go + * around the loop again so that wildcard jobs have + * a chance to run, and we do our housekeeping + */ + Debug(DSCH, ("[%d], DST begins %d minutes to go\n", + getpid(), timeRunning - virtualTime)) + /* run wildcard jobs for current minute */ + find_jobs(timeRunning, &database, TRUE, FALSE); + + /* run fixed-time jobs for each minute missed */ + do { + if (job_runqueue()) + sleep(10); + virtualTime++; + find_jobs(virtualTime, &database, FALSE, TRUE); + set_time(FALSE); + } while (virtualTime< timeRunning && + clockTime == timeRunning); + break; + + case 0: + /* + * case 3: timeDiff is a small or medium-sized + * negative num, eg. because of DST ending just run + * the wildcard jobs. The fixed-time jobs probably + * have already run, and should not be repeated + * virtual time does not change until we are caught up + */ + Debug(DSCH, ("[%d], DST ends %d minutes to go\n", + getpid(), virtualTime - timeRunning)) + find_jobs(timeRunning, &database, TRUE, FALSE); + break; + default: + /* + * other: time has changed a *lot*, + * jump virtual time, and run everything + */ + Debug(DSCH, ("[%d], clock jumped\n", getpid())) + virtualTime = timeRunning; + find_jobs(timeRunning, &database, TRUE, TRUE); + } + } + /* jobs to be run (if any) are loaded. clear the queue */ + job_runqueue(); } } +#ifdef DEBIAN +#include +#include +#endif static void run_reboot_jobs(db) @@ -139,7 +282,31 @@ { register user *u; register entry *e; + int rbfd; +#ifdef DEBIAN +#define REBOOT_FILE "/var/run/crond.reboot" + /* Run on actual reboot, rather than cron restart */ + if (access(REBOOT_FILE, F_OK) == 0) { + /* File exists, return */ + log_it("CRON", getpid(),"INFO", + "Skipping @reboot jobs -- not system startup"); + return; + } + /* Create the file */ + if ((rbfd = creat(REBOOT_FILE, S_IRUSR&S_IWUSR)) < 0) { + /* Bad news, bail out */ + log_it("CRON",getpid(),"DEATH","Can't create reboot check file"); + exit(0); + } else { + close(rbfd); + log_it("CRON", getpid(),"INFO", "Running @reboot jobs"); + } + + Debug(DMISC, ("[%d], Debian running reboot jobs\n",getpid())); + +#endif + Debug(DMISC, ("[%d], vixie running reboot jobs\n", getpid())); for (u = db->head; u != NULL; u = u->next) { for (e = u->crontab; e != NULL; e = e->next) { if (e->flags & WHEN_REBOOT) { @@ -152,10 +319,14 @@ static void -cron_tick(db) +find_jobs(vtime, db, doWild, doNonWild) + time_min vtime; cron_db *db; + int doWild; + int doNonWild; { - register struct tm *tm = localtime(&TargetTime); + time_t virtualSecond = vtime * SECONDS_PER_MINUTE; + register struct tm *tm = gmtime(&virtualSecond); register int minute, hour, dom, month, dow; register user *u; register entry *e; @@ -168,8 +339,9 @@ month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; dow = tm->tm_wday -FIRST_DOW; - Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n", - getpid(), minute, hour, dom, month, dow)) + Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d) %s %s\n", + getpid(), minute, hour, dom, month, dow, + doWild?" ":"No wildcard",doNonWild?" ":"Wildcard only")) /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the * first and fifteenth AND every Sunday; '* * * * Sun' will run *only* @@ -180,73 +352,72 @@ for (u = db->head; u != NULL; u = u->next) { for (e = u->crontab; e != NULL; e = e->next) { Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n", - env_get("LOGNAME", e->envp), - e->uid, e->gid, e->cmd)) - if (bit_test(e->minute, minute) - && bit_test(e->hour, hour) - && bit_test(e->month, month) - && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) + env_get("LOGNAME", e->envp), + e->uid, e->gid, e->cmd)) + if (bit_test(e->minute, minute) && + bit_test(e->hour, hour) && + bit_test(e->month, month) && + ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR)) ? (bit_test(e->dow,dow) && bit_test(e->dom,dom)) - : (bit_test(e->dow,dow) || bit_test(e->dom,dom)) - ) - ) { - job_add(e, u); + : (bit_test(e->dow,dow) || bit_test(e->dom,dom)))) { + if ((doNonWild && !(e->flags & (MIN_STAR|HR_STAR))) + || (doWild && (e->flags & (MIN_STAR|HR_STAR)))) + job_add(e, u); } } } } -/* the task here is to figure out how long it's going to be until :00 of the - * following minute and initialize TargetTime to this value. TargetTime - * will subsequently slide 60 seconds at a time, with correction applied - * implicitly in cron_sleep(). it would be nice to let cron execute in - * the "current minute" before going to sleep, but by restarting cron you - * could then get it to execute a given minute's jobs more than once. - * instead we have the chance of missing a minute's jobs completely, but - * that's something sysadmin's know to expect what with crashing computers.. +/* + * Set StartTime and clockTime to the current time. + * These are used for computing what time it really is right now. + * Note that clockTime is a unix wallclock time converted to minutes. */ static void -cron_sync() { - register struct tm *tm; +set_time(int initialize) +{ + struct tm tm; + static int isdst; - TargetTime = time((time_t*)0); - tm = localtime(&TargetTime); - TargetTime += (60 - tm->tm_sec); -} + StartTime = time(NULL); + /* We adjust the time to GMT so we can catch DST changes. */ + tm = *localtime(&StartTime); + if (initialize || tm.tm_isdst != isdst) { + isdst = tm.tm_isdst; + GMToff = get_gmtoff(&StartTime, &tm); + Debug(DSCH, ("[%d] GMToff=%ld\n", + getpid(), (long)GMToff)) + } + clockTime = (StartTime + GMToff) / (time_t)SECONDS_PER_MINUTE; +} +/* + * try to just hit the next minute + */ static void -cron_sleep() { - register int seconds_to_wait; +cron_sleep(target) + time_min target; +{ + time_t t; + int seconds_to_wait; - do { - seconds_to_wait = (int) (TargetTime - time((time_t*)0)); - Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n", - getpid(), TargetTime, seconds_to_wait)) - - /* if we intend to sleep, this means that it's finally - * time to empty the job queue (execute it). - * - * if we run any jobs, we'll probably screw up our timing, - * so go recompute. - * - * note that we depend here on the left-to-right nature - * of &&, and the short-circuiting. - */ - } while (seconds_to_wait > 0 && job_runqueue()); + t = time(NULL) + GMToff; - while (seconds_to_wait > 0) { - Debug(DSCH, ("[%d] sleeping for %d seconds\n", - getpid(), seconds_to_wait)) - seconds_to_wait = (int) sleep((unsigned int) seconds_to_wait); - } + seconds_to_wait = (int)(target * SECONDS_PER_MINUTE - t) + 1; + Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n", + getpid(), (long)target*SECONDS_PER_MINUTE, seconds_to_wait)) + + if (seconds_to_wait > 0 && seconds_to_wait < 65) + sleep((unsigned int) seconds_to_wait); } #ifdef USE_SIGCHLD static void sigchld_handler(x) { + int save_errno = errno; WAIT_T waiter; PID_T pid; @@ -260,10 +431,12 @@ case -1: Debug(DPROC, ("[%d] sigchld...no children\n", getpid())) + errno = save_errno; return; case 0: Debug(DPROC, ("[%d] sigchld...no dead kids\n", getpid())) + errno = save_errno; return; default: Debug(DPROC, @@ -271,6 +444,7 @@ getpid(), pid, WEXITSTATUS(waiter))) } } + errno = save_errno; } #endif /*USE_SIGCHLD*/ @@ -278,6 +452,10 @@ static void sighup_handler(x) { log_close(); + + /* we should use sigaction for proper signal blocking as this + has a race, but... */ + signal(SIGHUP, sighup_handler); } @@ -288,14 +466,27 @@ { int argch; - while (EOF != (argch = getopt(argc, argv, "x:"))) { + log_level = 1; + stay_foreground = 0; + lsbsysinit_mode = 0; + + while (EOF != (argch = getopt(argc, argv, "lfx:L:"))) { switch (argch) { default: usage(); + case 'f': + stay_foreground = 1; + break; case 'x': if (!set_debug_flags(optarg)) usage(); break; + case 'l': + lsbsysinit_mode = 1; + break; + case 'L': + log_level = atoi(optarg); + break; } } } --- cron-3.0pl1.orig/job.c +++ cron-3.0pl1/job.c @@ -45,7 +45,8 @@ if (j->e == e && j->u == u) { return; } /* build a job queue element */ - j = (job*)malloc(sizeof(job)); + if ((j = (job*)malloc(sizeof(job))) == NULL) + return; j->next = (job*) NULL; j->e = e; j->u = u; --- cron-3.0pl1.orig/externs.h +++ cron-3.0pl1/externs.h @@ -20,11 +20,17 @@ # include # include # include +# include # define DIR_T struct dirent # define WAIT_T int # define WAIT_IS_INT 1 extern char *tzname[2]; # define TZONE(tm) tzname[(tm).tm_isdst] +/* include locale stuff for mailer "Content-Type": + */ +#include +#include +#include #endif #if defined(UNIXPC) @@ -55,6 +61,7 @@ extern void perror(), exit(), free(); extern char *getenv(), *strcpy(), *strchr(), *strtok(); extern void *malloc(), *realloc(); + # define SIG_T void # define TIME_T long # define PID_T int --- cron-3.0pl1.orig/README +++ cron-3.0pl1/README @@ -24,6 +24,12 @@ [V1.0 was May 6, 1987] Paul Vixie +[Note from Debian cron maintainer: This is the original README from +the the vixie-cron package. The location of many cron files has been +changed in order to comply with Debian policy and common sense -- look +in the cron(8), crontab(1) and crontab(5) man pages for more info, as +well as the README.Debian file in this directory.] + This is a version of 'cron' that is known to run on BSD 4.[23] systems. It is functionally based on the SysV cron, which means that each user can have their own crontab file (all crontab files are stored in a read-protected --- cron-3.0pl1.orig/user.c +++ cron-3.0pl1/user.c @@ -23,9 +23,139 @@ */ +#include +#include #include "cron.h" +#ifdef WITH_SELINUX +#include +#include +#include +#include +#include + +static int get_security_context(char *name, int crontab_fd, security_context_t + *rcontext, char *tabname) { + security_context_t *context_list = NULL; + security_context_t current_con; + int list_count = 0; + security_context_t file_context=NULL; + struct av_decision avd; + int retval=0; + char *seuser = NULL; + char *level = NULL; + int i; + + if (name != NULL) { + if (getseuserbyname(name, &seuser, &level)) { + log_it(name, getpid(), "getseuserbyname FAILED", tabname); + return (security_getenforce() > 0); + } + } + else + { + seuser = strdup("system_u"); + } + + *rcontext = NULL; + if(getcon(¤t_con)) { + log_it(name, getpid(), "Can't get current context", tabname); + return -1; + } + list_count = get_ordered_context_list_with_level(seuser, level, current_con, &context_list); + freecon(current_con); + free(seuser); + free(level); + if (list_count == -1) { + if (security_getenforce() > 0) { + log_it(name, getpid(), "No SELinux security context", tabname); + return -1; + } else { + log_it(name, getpid(), + "No security context but SELinux in permissive mode," + " continuing", tabname); + return 0; + } + } + + if (fgetfilecon(crontab_fd, &file_context) < OK) { + if (security_getenforce() > 0) { + log_it(name, getpid(), "getfilecon FAILED", tabname); + freeconary(context_list); + return -1; + } else { + log_it(name, getpid(), "getfilecon FAILED but SELinux in " + "permissive mode, continuing", tabname); + *rcontext = strdup(context_list[0]); + freeconary(context_list); + return 0; + } + } + + /* + * Since crontab files are not directly executed, + * crond must ensure that the crontab file has + * a context that is appropriate for the context of + * the user cron job. It performs an entrypoint + * permission check for this purpose. + */ + + for(i = 0; i < list_count; i++) + { + retval = security_compute_av(context_list[i], + file_context, + SECCLASS_FILE, + FILE__ENTRYPOINT, + &avd); + if(!retval && ((FILE__ENTRYPOINT & avd.allowed) == FILE__ENTRYPOINT)) { + *rcontext = strdup(context_list[i]); + freecon(file_context); + freeconary(context_list); + return 0; + } + } + freecon(file_context); + if (security_getenforce() > 0) { + log_it(name, getpid(), "ENTRYPOINT FAILED", tabname); + freeconary(context_list); + return -1; + } else { + log_it(name, getpid(), "ENTRYPOINT FAILED but SELinux in permissive mode, continuing", tabname); + *rcontext = strdup(context_list[0]); + freeconary(context_list); + } + return 0; +} +#endif + + +#ifdef DEBIAN +/* Function used to log errors in crontabs from cron daemon. (User + crontabs are checked before they're accepted, but system crontabs + are not. */ +static char *err_user=NULL; + +void +crontab_error(msg) + char *msg; +{ + const char *fn; + /* Figure out the file name from the username */ + if (0 == strcmp(err_user,"*system*")) { + syslog(LOG_ERR|LOG_CRON,"Error: %s; while reading %s", msg, SYSCRONTAB); + } else if (0 == strncmp(err_user,"*system*",8)) { + fn = err_user+8; + syslog(LOG_ERR|LOG_CRON,"Error: %s; while reading %s/%s", msg, + SYSCRONDIR,fn); + } else { + syslog(LOG_ERR|LOG_CRON, "Error: %s; while reading crontab for user %s", + msg, err_user); + } +} + +#endif + void free_user(u) user *u; @@ -37,22 +167,28 @@ ne = e->next; free_entry(e); } +#ifdef WITH_SELINUX + if (u->scontext) + freecon(u->scontext); +#endif free(u); } user * -load_user(crontab_fd, pw, name) +load_user(crontab_fd, pw, uname, fname, tabname) int crontab_fd; struct passwd *pw; /* NULL implies syscrontab */ - char *name; + char *uname; + char *fname; + char *tabname; { char envstr[MAX_ENVSTR]; FILE *file; user *u; entry *e; int status; - char **envp; + char **envp = NULL, **tenvp; if (!(file = fdopen(crontab_fd, "r"))) { perror("fdopen on crontab_fd in load_user"); @@ -63,36 +199,97 @@ /* file is open. build user entry, then read the crontab file. */ - u = (user *) malloc(sizeof(user)); - u->name = strdup(name); + if ((u = (user *) malloc(sizeof(user))) == NULL) { + errno = ENOMEM; + return NULL; + } + if ((u->name = strdup(fname)) == NULL) { + free(u); + errno = ENOMEM; + return NULL; + } u->crontab = NULL; +#ifdef WITH_SELINUX + u->scontext = NULL; + if (is_selinux_enabled() > 0) { + char *sname=uname; + if (pw==NULL) { + sname="system_u"; + } + if (get_security_context(sname, crontab_fd, + &u->scontext, tabname) != 0 ) { + u->scontext = NULL; + free_user(u); + u = NULL; + goto done; + } + } +#endif + + /* * init environment. this will be copied/augmented for each entry. */ - envp = env_init(); + if ((envp = env_init()) == NULL) { + free(u->name); + free(u); + return NULL; + } /* * load the crontab */ - while ((status = load_env(envstr, file)) >= OK) { + do { + status = load_env(envstr, file); switch (status) { case ERR: - free_user(u); - u = NULL; + /* If envstr has no content, we reached a proper EOF + * and we can return to continue regular processing. + * + * If it does have content, we reached EOF without a + * newline, so we bail out + */ + if (envstr[0] != '\0') { + log_it(u->name, getpid(), "ERROR", "Missing " + "newline before EOF, this crontab file will be " + "ignored"); + free_user(u); + u = NULL; + } goto done; case FALSE: +#ifdef DEBIAN + err_user = fname; + e = load_entry(file, crontab_error, pw, envp); + err_user = NULL; +#else e = load_entry(file, NULL, pw, envp); +#endif if (e) { e->next = u->crontab; u->crontab = e; + } else { + /* stop processing on syntax error */ + log_it(u->name, getpid(), "ERROR", "Syntax " + "error, this crontab file will be " + "ignored"); + free_user(u); + u = NULL; + goto done; } break; case TRUE: - envp = env_set(envp, envstr); + if ((tenvp = env_set(envp, envstr))) { + envp = tenvp; + } else { + free_user(u); + u = NULL; + goto done; + } break; } - } + } while (status >= OK); done: env_free(envp); --- cron-3.0pl1.orig/do_command.c +++ cron-3.0pl1/do_command.c @@ -21,18 +21,63 @@ #include "cron.h" -#include +#include +#include +#include +#include #if defined(sequent) # include #endif #if defined(SYSLOG) # include #endif +#if defined(USE_PAM) +#include +static pam_handle_t *pamh = NULL; +static const struct pam_conv conv = { + NULL +}; +#define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \ + fprintf(stderr,"\n%s\n",pam_strerror(pamh, retcode)); \ + syslog(LOG_ERR,"%s",pam_strerror(pamh, retcode)); \ + pam_end(pamh, retcode); exit(1); \ + } +#endif + +#ifdef WITH_SELINUX +#include +/* #include */ +#endif static void child_process __P((entry *, user *)), do_univ __P((user *)); +/* Build up the job environment from the PAM environment plus the + crontab environment */ +static char ** build_env(char **cronenv) +{ + char **jobenv = cronenv; +#if defined(USE_PAM) + char **pamenv = pam_getenvlist(pamh); + char *cronvar; + int count = 0; + + jobenv = env_copy(pamenv); + + /* Now add the cron environment variables. Since env_set() + overwrites existing variables, this will let cron's + environment settings override pam's */ + + while ((cronvar = cronenv[count++])) { + if (!(jobenv = env_set(jobenv, cronvar))) { + syslog(LOG_ERR, "Setting Cron environment variable %s failed", cronvar); + return NULL; + } + } +#endif + return jobenv; +} void do_command(e, u) @@ -68,15 +113,29 @@ } +/* + * CROND + * - cron (runs child_process); + * - cron (runs exec sh -c 'tab entry'); + * - cron (writes any %-style stdin to the command); + * - mail (popen writes any stdout to mailcmd); + */ + static void child_process(e, u) entry *e; user *u; { - int stdin_pipe[2], stdout_pipe[2]; + int stdin_pipe[2]; + FILE *tmpout; register char *input_data; char *usernm, *mailto; int children = 0; + pid_t job_pid; + +#if defined(USE_PAM) + int retcode = 0; +#endif Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd)) @@ -95,13 +154,32 @@ usernm = env_get("LOGNAME", e->envp); mailto = env_get("MAILTO", e->envp); + /* Check for arguments */ + if (mailto) { + const char *end; + + /* These chars have to match those cron_popen() + * uses to split the command string */ + mailto += strspn(mailto, " \t\n"); + end = mailto + strcspn(mailto, " \t\n"); + if (*mailto == '-' || *end != '\0') { + printf("Bad Mailto karma.\n"); + log_it("CRON",getpid(),"error","bad mailto"); + mailto = NULL; + } + } + #ifdef USE_SIGCHLD /* our parent is watching for our death by catching SIGCHLD. we * do not care to watch for our children's deaths this way -- we * use wait() explictly. so we have to disable the signal (which * was inherited from the parent). */ +#ifdef DEBIAN + (void) signal(SIGCHLD, SIG_DFL); +#else (void) signal(SIGCHLD, SIG_IGN); +#endif #else /* on system-V systems, we are ignoring SIGCLD. we have to stop * ignoring it now or the wait() in cron_pclose() won't work. @@ -110,10 +188,14 @@ (void) signal(SIGCLD, SIG_DFL); #endif /*BSD*/ - /* create some pipes to talk to our future child + /* create a pipe to talk to our future child */ pipe(stdin_pipe); /* child's stdin */ - pipe(stdout_pipe); /* child's stdout */ + /* child's stdout */ + if ((tmpout = tmpfile()) == NULL) { + log_it("CRON", getpid(), "error", "create tmpfile"); + exit(ERROR_EXIT); + } /* since we are a forked process, we can diddle the command string * we were passed -- nobody else is going to use it again, right? @@ -122,13 +204,21 @@ * command, and subsequent characters are the additional input to * the command. Subsequent %'s will be transformed into newlines, * but that happens later. + * + * If there are escaped %'s, remove the escape character. */ /*local*/{ register int escaped = FALSE; register int ch; + register char *p; - for (input_data = e->cmd; ch = *input_data; input_data++) { + for (input_data = p = e->cmd; (ch = *input_data); + input_data++, p++) { + if (p != input_data) + *p = ch; if (escaped) { + if (ch == '%' || ch == '\\') + *--p = ch; escaped = FALSE; continue; } @@ -141,36 +231,47 @@ break; } } + *p = '\0'; } +#if defined(USE_PAM) + retcode = pam_start("cron", usernm, &conv, &pamh); + PAM_FAIL_CHECK; + retcode = pam_set_item(pamh, PAM_TTY, "cron"); + PAM_FAIL_CHECK; + retcode = pam_acct_mgmt(pamh, PAM_SILENT); + PAM_FAIL_CHECK; + retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT); + PAM_FAIL_CHECK; + retcode = pam_open_session(pamh, PAM_SILENT); + PAM_FAIL_CHECK; + +#endif + /* fork again, this time so we can exec the user's command. */ - switch (vfork()) { + switch (job_pid = fork()) { case -1: - log_it("CRON",getpid(),"error","can't vfork"); + log_it("CRON",getpid(),"error","can't fork"); exit(ERROR_EXIT); /*NOTREACHED*/ case 0: - Debug(DPROC, ("[%d] grandchild process Vfork()'ed\n", + Debug(DPROC, ("[%d] grandchild process fork()'ed\n", getpid())) - /* write a log message. we've waited this long to do it + /* write a log message . we've waited this long to do it * because it was not until now that we knew the PID that * the actual user command shell was going to get and the * PID is part of the log message. */ - /*local*/{ + if ( (log_level & CRON_LOG_JOBSTART) && ! (log_level & CRON_LOG_JOBPID)) { char *x = mkprints((u_char *)e->cmd, strlen(e->cmd)); - log_it(usernm, getpid(), "CMD", x); free(x); } - - /* that's the last thing we'll log. close the log files. + /* nothing to log from now on. close the log files. */ -#ifdef SYSLOG - closelog(); -#endif + log_close(); /* get new pgrp, void tty, etc. */ @@ -183,19 +284,21 @@ * appropriate circumstances. */ close(stdin_pipe[WRITE_PIPE]); - close(stdout_pipe[READ_PIPE]); /* grandchild process. make std{in,out} be the ends of * pipes opened by our daddy; make stderr go to stdout. */ - close(STDIN); dup2(stdin_pipe[READ_PIPE], STDIN); - close(STDOUT); dup2(stdout_pipe[WRITE_PIPE], STDOUT); - close(STDERR); dup2(STDOUT, STDERR); + /* Closes are unnecessary -- let dup2() do it */ + + /* close(STDIN) */; dup2(stdin_pipe[READ_PIPE], STDIN); + dup2(fileno(tmpout), STDOUT); + /* close(STDERR)*/; dup2(STDOUT, STDERR); + - /* close the pipes we just dup'ed. The resources will remain. + /* close the pipe we just dup'ed. The resources will remain. */ close(stdin_pipe[READ_PIPE]); - close(stdout_pipe[WRITE_PIPE]); + fclose(tmpout); /* set our login universe. Do this in the grandchild * so that the child can invoke /usr/lib/sendmail @@ -206,18 +309,36 @@ /* set our directory, uid and gid. Set gid first, since once * we set uid, we've lost root privledges. */ - setgid(e->gid); -# if defined(BSD) - initgroups(env_get("LOGNAME", e->envp), e->gid); + if (setgid(e->gid) !=0) { + char msg[256]; + snprintf(msg, 256, "do_command:setgid(%lu) failed: %s", + (unsigned long) e->gid, strerror(errno)); + log_it("CRON",getpid(),"error",msg); + exit(ERROR_EXIT); + } +# if defined(BSD) || defined(POSIX) + if (initgroups(env_get("LOGNAME", e->envp), e->gid) !=0) { + char msg[256]; + snprintf(msg, 256, "do_command:initgroups(%lu) failed: %s", + (unsigned long) e->gid, strerror(errno)); + log_it("CRON",getpid(),"error",msg); + exit(ERROR_EXIT); + } # endif - setuid(e->uid); /* we aren't root after this... */ + if (setuid(e->uid) !=0) { /* we aren't root after this... */ + char msg[256]; + snprintf(msg, 256, "do_command:setuid(%lu) failed: %s", + (unsigned long) e->uid, strerror(errno)); + log_it("CRON",getpid(),"error",msg); + exit(ERROR_EXIT); + } chdir(env_get("HOME", e->envp)); /* exec the command. */ { - char *shell = env_get("SHELL", e->envp); - + char **jobenv = build_env(e->envp); + char *shell = env_get("SHELL", jobenv); # if DEBUGGING if (DebugFlags & DTEST) { fprintf(stderr, @@ -227,14 +348,47 @@ _exit(OK_EXIT); } # endif /*DEBUGGING*/ - execle(shell, shell, "-c", e->cmd, (char *)0, e->envp); - fprintf(stderr, "execl: couldn't exec `%s'\n", shell); - perror("execl"); +#if 0 + { + struct sigaction oact; + sigaction(SIGCHLD, NULL, &oact); + } + fprintf(stdout,"error"); +#endif +#ifdef WITH_SELINUX + if (is_selinux_enabled() > 0) { + if (u->scontext != 0L) { + if (setexeccon(u->scontext) < 0) { + if (security_getenforce() > 0) { + fprintf(stderr, "Could not set exec context to %s for user %s\n", u->scontext,u->name); + _exit(ERROR_EXIT); + } + } + } + else if(security_getenforce() > 0) + { + fprintf(stderr, "Error, must have a security context for the cron job when in enforcing mode.\nUser %s.\n", u->name); + _exit(ERROR_EXIT); + } + } +#endif + execle(shell, shell, "-c", e->cmd, (char *)0, jobenv); + fprintf(stderr, "%s: execle: %s\n", shell, strerror(errno)); _exit(ERROR_EXIT); } break; default: /* parent process */ + /* write a log message if we want the parent and child + * PID values + */ + if ( (log_level & CRON_LOG_JOBSTART) && (log_level & CRON_LOG_JOBPID)) { + char logcmd[MAX_COMMAND + 8]; + snprintf(logcmd, sizeof(logcmd), "[%d] %s", (int) job_pid, e->cmd); + char *x = mkprints((u_char *)logcmd, strlen(logcmd)); + log_it(usernm, getpid(), "CMD", x); + free(x); + } break; } @@ -246,11 +400,10 @@ Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid())) - /* close the ends of the pipe that will only be referenced in the + /* close the end of the pipe that will only be referenced in the * grandchild process... */ close(stdin_pipe[READ_PIPE]); - close(stdout_pipe[WRITE_PIPE]); /* * write, to the pipe connected to child's stdin, any input specified @@ -271,17 +424,12 @@ Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid())) - /* close the pipe we don't use, since we inherited it and - * are part of its reference count now. - */ - close(stdout_pipe[READ_PIPE]); - /* translation: * \% -> % * % -> \n * \x -> \x for all x != % */ - while (ch = *input_data++) { + while ((ch = *input_data++) != '\0') { if (escaped) { if (ch != '%') putc('\\', out); @@ -323,143 +471,197 @@ * when the grandchild exits, we'll get EOF. */ - Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid())) + /* wait for children to die. + */ + int status = 0; + for (; children > 0; children--) + { + char msg[256]; + WAIT_T waiter; + PID_T pid; - /*local*/{ - register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); - register int ch = getc(in); + Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n", + getpid(), children)) + pid = wait(&waiter); + if (pid < OK) { + Debug(DPROC, ("[%d] no more grandchildren\n", getpid())) + break; + } + Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x\n", + getpid(), pid, WEXITSTATUS(waiter))) - if (ch != EOF) { - register FILE *mail; - register int bytes = 1; - int status = 0; - - Debug(DPROC|DEXT, - ("[%d] got data (%x:%c) from grandchild\n", - getpid(), ch, ch)) - - /* get name of recipient. this is MAILTO if set to a - * valid local username; USER otherwise. - */ - if (mailto) { - /* MAILTO was present in the environment - */ - if (!*mailto) { - /* ... but it's empty. set to NULL - */ - mailto = NULL; - } - } else { - /* MAILTO not present, set to USER. - */ - mailto = usernm; - } - - /* if we are supposed to be mailing, MAILTO will - * be non-NULL. only in this case should we set - * up the mail command and subjects and stuff... - */ - - if (mailto) { - register char **env; - auto char mailcmd[MAX_COMMAND]; - auto char hostname[MAXHOSTNAMELEN]; - - (void) gethostname(hostname, MAXHOSTNAMELEN); - (void) sprintf(mailcmd, MAILARGS, - MAILCMD, mailto); - if (!(mail = cron_popen(mailcmd, "w"))) { - perror(MAILCMD); - (void) _exit(ERROR_EXIT); - } - fprintf(mail, "From: root (Cron Daemon)\n"); - fprintf(mail, "To: %s\n", mailto); - fprintf(mail, "Subject: Cron <%s@%s> %s\n", - usernm, first_word(hostname, "."), - e->cmd); + if (log_level & CRON_LOG_JOBFAILED) { + if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) { + status = waiter; + snprintf(msg, 256, "grandchild #%d failed with exit " + "status %d", pid, WEXITSTATUS(waiter)); + log_it("CRON", getpid(), "error", msg); + } else if (WIFSIGNALED(waiter)) { + status = waiter; + snprintf(msg, 256, "grandchild #%d terminated by signal" + " %d%s", pid, WTERMSIG(waiter), + WCOREDUMP(waiter) ? ", dumped core" : ""); + log_it("CRON", getpid(), "error", msg); + } + } + } + +// Finally, send any output of the command to the mailer; also, alert +// the user if their job failed. Avoid popening the mailcmd until now +// since sendmail may time out, and to write info about the exit +// status. + + long pos; + struct stat mcsb; + int statret; + + fseek(tmpout, 0, SEEK_END); + pos = ftell(tmpout); + fseek(tmpout, 0, SEEK_SET); + + Debug(DPROC|DEXT, ("[%d] got %ld bytes data from grandchild tmpfile\n", + getpid(), (long) pos)) + if (pos == 0) + goto mail_finished; + + // get name of recipient. + if (mailto == NULL) + mailto = usernm; + else if (!*mailto) + goto mail_finished; + + /* Don't send mail if MAILCMD is not available */ + if ((statret = stat(MAILCMD, &mcsb)) != 0) { + Debug(DPROC|DEXT, ("%s not found, not sending mail\n", MAILCMD)) + if (pos > 0) { + log_it("CRON", getpid(), "info", "No MTA installed, discarding output"); + } + goto mail_finished; + } else { + Debug(DPROC|DEXT, ("%s found, will send mail\n", MAILCMD)) + } + + register FILE *mail = NULL; + register int bytes = 1; + + register char **env; + char **jobenv = build_env(e->envp); + auto char mailcmd[MAX_COMMAND]; + auto char hostname[MAXHOSTNAMELEN]; + char *content_type = env_get("CONTENT_TYPE",jobenv), + *content_transfer_encoding = env_get("CONTENT_TRANSFER_ENCODING",jobenv); + + (void) gethostname(hostname, MAXHOSTNAMELEN); + (void) snprintf(mailcmd, sizeof(mailcmd), + MAILARGS, MAILCMD, mailto); + if (!(mail = cron_popen(mailcmd, "w", e))) { + perror(MAILCMD); + (void) _exit(ERROR_EXIT); + } + fprintf(mail, "From: root (Cron Daemon)\n"); + fprintf(mail, "To: %s\n", mailto); + fprintf(mail, "Subject: Cron <%s@%s> %s%s\n", + usernm, first_word(hostname, "."), + e->cmd, status?" (failed)":""); # if defined(MAIL_DATE) - fprintf(mail, "Date: %s\n", - arpadate(&TargetTime)); + fprintf(mail, "Date: %s\n", + arpadate(&StartTime)); # endif /* MAIL_DATE */ - for (env = e->envp; *env; env++) - fprintf(mail, "X-Cron-Env: <%s>\n", - *env); - fprintf(mail, "\n"); - - /* this was the first char from the pipe - */ - putc(ch, mail); - } + if ( content_type == 0L ) { + fprintf(mail, "Content-Type: text/plain; charset=%s\n", + cron_default_mail_charset + ); + } else { + /* user specified Content-Type header. + * disallow new-lines for security reasons + * (else users could specify arbitrary mail headers!) + */ + char *nl=content_type; + size_t ctlen = strlen(content_type); + + while( (*nl != '\0') + && ((nl=strchr(nl,'\n')) != 0L) + && (nl < (content_type+ctlen)) + ) *nl = ' '; + fprintf(mail,"Content-Type: %s\n", content_type); + } + if ( content_transfer_encoding != 0L ) { + char *nl=content_transfer_encoding; + size_t ctlen = strlen(content_transfer_encoding); + while( (*nl != '\0') + && ((nl=strchr(nl,'\n')) != 0L) + && (nl < (content_transfer_encoding+ctlen)) + ) *nl = ' '; - /* we have to read the input pipe no matter whether - * we mail or not, but obviously we only write to - * mail pipe if we ARE mailing. - */ - - while (EOF != (ch = getc(in))) { - bytes++; - if (mailto) - putc(ch, mail); - } + fprintf(mail,"Content-Transfer-Encoding: %s\n", content_transfer_encoding); + } - /* only close pipe if we opened it -- i.e., we're - * mailing... - */ - - if (mailto) { - Debug(DPROC, ("[%d] closing pipe to mail\n", - getpid())) - /* Note: the pclose will probably see - * the termination of the grandchild - * in addition to the mail process, since - * it (the grandchild) is likely to exit - * after closing its stdout. - */ - status = cron_pclose(mail); - } + for (env = e->envp; *env; env++) + fprintf(mail, "X-Cron-Env: <%s>\n", + *env); + fputc('\n', mail); - /* if there was output and we could not mail it, - * log the facts so the poor user can figure out - * what's going on. - */ - if (mailto && status) { - char buf[MAX_TEMPSTR]; - - sprintf(buf, - "mailed %d byte%s of output but got status 0x%04x\n", - bytes, (bytes==1)?"":"s", - status); - log_it(usernm, getpid(), "MAIL", buf); +// Append the actual output of the child to the mail + + char buf[4096]; + int ret, remain; + + while(1) { + if ((ret = fread(buf, 1, sizeof(buf), tmpout)) == 0) + break; + for (remain = ret; remain != 0; ) { + ret = fwrite(buf, 1, remain, mail); + if (ret > 0) { + remain -= ret; + continue; } + // XXX error + break; + } + } - } /*if data from grandchild*/ + Debug(DPROC, ("[%d] closing pipe to mail\n", getpid())) + status = cron_pclose(mail); - Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid())) + /* if there was output and we could not mail it, + * log the facts so the poor user can figure out + * what's going on. + */ + if (status) { + char buf[MAX_TEMPSTR]; + snprintf(buf, MAX_TEMPSTR, + "mailed %d byte%s of output; " + "but got status 0x%04x, " + "\n", + bytes, (bytes==1)?"":"s", status); + log_it(usernm, getpid(), "MAIL", buf); + } - fclose(in); /* also closes stdout_pipe[READ_PIPE] */ + if (ferror(tmpout)) { + log_it(usernm, getpid(), "MAIL", "stream error reading output"); } - /* wait for children to die. - */ - for (; children > 0; children--) - { - WAIT_T waiter; - PID_T pid; +mail_finished: + fclose(tmpout); - Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n", - getpid(), children)) - pid = wait(&waiter); - if (pid < OK) { - Debug(DPROC, ("[%d] no more grandchildren--mail written?\n", - getpid())) - break; + if (log_level & CRON_LOG_JOBEND) { + char *x; + if (log_level & CRON_LOG_JOBPID) { + char logcmd[MAX_COMMAND + 8]; + snprintf(logcmd, sizeof(logcmd), "[%d] %s", (int) job_pid, e->cmd); + x = mkprints((u_char *)logcmd, strlen(logcmd)); + } else { + x = mkprints((u_char *)e->cmd, strlen(e->cmd)); } - Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x", - getpid(), pid, WEXITSTATUS(waiter))) - if (WIFSIGNALED(waiter) && WCOREDUMP(waiter)) - Debug(DPROC, (", dumped core")) - Debug(DPROC, ("\n")) + log_it(usernm, job_pid, "END", x); + free(x); } + +#if defined(USE_PAM) + pam_setcred(pamh, PAM_DELETE_CRED | PAM_SILENT); + retcode = pam_close_session(pamh, PAM_SILENT); + pam_end(pamh, retcode); +#endif } --- cron-3.0pl1.orig/misc.c +++ cron-3.0pl1/misc.c @@ -35,6 +35,10 @@ #include #include #include +#include +#ifdef WITH_AUDIT +#include +#endif #if defined(SYSLOG) # include #endif @@ -191,19 +195,29 @@ set_cron_cwd() { struct stat sb; - + mode_t um; + struct group *gr; + /* first check for CRONDIR ("/var/cron" or some such) */ if (stat(CRONDIR, &sb) < OK && errno == ENOENT) { perror(CRONDIR); - if (OK == mkdir(CRONDIR, 0700)) { + + /* crontab(1) running SGID crontab shouldn't attempt to create + * directories */ + if (getuid() != 0 ) + exit(ERROR_EXIT); + + um = umask(000); + if (OK == mkdir(CRONDIR, CRONDIR_MODE)) { fprintf(stderr, "%s: created\n", CRONDIR); stat(CRONDIR, &sb); } else { - fprintf(stderr, "%s: ", CRONDIR); - perror("mkdir"); + fprintf(stderr, "%s: mkdir: %s\n", CRONDIR, + strerror(errno)); exit(ERROR_EXIT); } + (void) umask(um); } if (!(sb.st_mode & S_IFDIR)) { fprintf(stderr, "'%s' is not a directory, bailing out.\n", @@ -211,8 +225,7 @@ exit(ERROR_EXIT); } if (chdir(CRONDIR) < OK) { - fprintf(stderr, "cannot chdir(%s), bailing out.\n", CRONDIR); - perror(CRONDIR); + fprintf(stderr, "%s: chdir: %s\n", CRONDIR, strerror(errno)); exit(ERROR_EXIT); } @@ -220,12 +233,33 @@ */ if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) { perror(SPOOL_DIR); - if (OK == mkdir(SPOOL_DIR, 0700)) { + + /* crontab(1) running SGID crontab shouldn't attempt to create + * directories */ + if (getuid() != 0 ) + exit(ERROR_EXIT); + + um = umask(000); + if (OK == mkdir(SPOOL_DIR, SPOOL_DIR_MODE)) { fprintf(stderr, "%s: created\n", SPOOL_DIR); - stat(SPOOL_DIR, &sb); } else { - fprintf(stderr, "%s: ", SPOOL_DIR); - perror("mkdir"); + fprintf(stderr, "%s: mkdir: %s\n", SPOOL_DIR, + strerror(errno)); + exit(ERROR_EXIT); + } + (void) umask(um); + + if (!(gr = getgrnam(SPOOL_DIR_GROUP))) { + fprintf(stderr, "%s: getgrnam: %s\n", SPOOL_DIR, + strerror(errno)); + exit(ERROR_EXIT); + } + if (OK == chown(SPOOL_DIR, -1, gr->gr_gid)) { + fprintf(stderr, "%s: chowned\n", SPOOL_DIR); + stat(SPOOL_DIR, &sb); + } else { + fprintf(stderr, "%s: chown: %s\n", SPOOL_DIR, + strerror(errno)); exit(ERROR_EXIT); } } @@ -237,7 +271,7 @@ } -/* acquire_daemonlock() - write our PID into /etc/cron.pid, unless +/* acquire_daemonlock() - write our PID into /etc/crond.pid, unless * another daemon is already running, which we detect here. * * note: main() calls us twice; once before forking, once after. @@ -263,11 +297,11 @@ char buf[MAX_TEMPSTR]; int fd, otherpid; - (void) sprintf(pidfile, PIDFILE, PIDDIR); + (void) snprintf(pidfile, MAX_FNAME, PIDFILE, PIDDIR); if ((-1 == (fd = open(pidfile, O_RDWR|O_CREAT, 0644))) || (NULL == (fp = fdopen(fd, "r+"))) ) { - sprintf(buf, "can't open or create %s: %s", + snprintf(buf, MAX_TEMPSTR, "can't open or create %s: %s", pidfile, strerror(errno)); fprintf(stderr, "%s: %s\n", ProgramName, buf); log_it("CRON", getpid(), "DEATH", buf); @@ -278,13 +312,14 @@ int save_errno = errno; fscanf(fp, "%d", &otherpid); - sprintf(buf, "can't lock %s, otherpid may be %d: %s", + snprintf(buf, MAX_TEMPSTR, "can't lock %s, otherpid may be %d: %s", pidfile, otherpid, strerror(save_errno)); fprintf(stderr, "%s: %s\n", ProgramName, buf); log_it("CRON", getpid(), "DEATH", buf); exit(ERROR_EXIT); } - + snprintf(buf, MAX_TEMPSTR, "pidfile fd = %d", fd); + log_it("CRON", getpid(), "INFO", buf); (void) fcntl(fd, F_SETFD, 1); } @@ -296,6 +331,7 @@ /* abandon fd and fp even though the file is open. we need to * keep it open and locked, but we don't need the handles elsewhere. */ + } /* get_char(file) : like getc() but increment LineNumber on newlines @@ -308,7 +344,7 @@ ch = getc(file); if (ch == '\n') - Set_LineNum(LineNumber + 1) + Set_LineNum(LineNumber + 1); return ch; } @@ -322,7 +358,7 @@ { ungetc(ch, file); if (ch == '\n') - Set_LineNum(LineNumber - 1) + Set_LineNum(LineNumber - 1); } @@ -428,7 +464,17 @@ { static int init = FALSE; static FILE *allow, *deny; + int isallowed; + /* Root cannot be denied execution of cron jobs even if in the + * 'DENY_FILE' so we return inmediately */ + if (strcmp(username, ROOT_USER) == 0) + return (TRUE); + + isallowed = FALSE; +#if defined(ALLOW_ONLY_ROOT) + Debug(DMISC, "only root access is allowed") +#else if (!init) { init = TRUE; #if defined(ALLOW_FILE) && defined(DENY_FILE) @@ -441,16 +487,24 @@ #endif } - if (allow) - return (in_file(username, allow)); - if (deny) - return (!in_file(username, deny)); + if (allow) + isallowed = in_file(username, allow); + else + isallowed = TRUE; /* Allow access if ALLOW_FILE does not exist */ + if (deny && !allow) + isallowed = !in_file(username, deny); +#endif -#if defined(ALLOW_ONLY_ROOT) - return (strcmp(username, ROOT_USER) == 0); -#else - return TRUE; +#ifdef WITH_AUDIT + /* Log an audit message if the user is rejected */ + if (isallowed == FALSE) { + int audit_fd = audit_open(); + audit_log_user_message(audit_fd, AUDIT_USER_START, "cron deny", + NULL, NULL, NULL, 0); + close(audit_fd); + } #endif + return isallowed; } @@ -461,46 +515,46 @@ char *event; char *detail; { - PID_T pid = xpid; #if defined(LOG_FILE) + PID_T pid = xpid; char *msg; TIME_T now = time((TIME_T) 0); register struct tm *t = localtime(&now); + int msg_size; #endif /*LOG_FILE*/ -#if defined(SYSLOG) - static int syslog_open = 0; -#endif #if defined(LOG_FILE) /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation. */ - msg = malloc(strlen(username) - + strlen(event) - + strlen(detail) - + MAX_TEMPSTR); - + msg_size = strlen(username) + strlen(event) + strlen(detail) + MAX_TEMPSTR; + msg = malloc(msg_size); + if (msg == NULL) { + /* damn, out of mem and we did not test that before... */ + fprintf(stderr, "%s: Run OUT OF MEMORY while %s\n", + ProgramName, __FUNCTION__); + return; + } if (LogFD < OK) { LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600); if (LogFD < OK) { - fprintf(stderr, "%s: can't open log file\n", - ProgramName); - perror(LOG_FILE); + fprintf(stderr, "%s: %s: open: %s\n", + ProgramName, LOG_FILE, strerror(errno)); } else { (void) fcntl(LogFD, F_SETFD, 1); } } - /* we have to sprintf() it because fprintf() doesn't always write + /* we have to snprintf() it because fprintf() doesn't always write * everything out in one chunk and this has to be atomically appended * to the log file. */ - sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n", + snprintf(msg, msg_size, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n", username, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, pid, event, detail); - /* we have to run strlen() because sprintf() returns (char*) on old BSD + /* we have to run strlen() because snprintf() returns (char*) on old BSD */ if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) { if (LogFD >= OK) @@ -513,27 +567,30 @@ #endif /*LOG_FILE*/ #if defined(SYSLOG) - if (!syslog_open) { - /* we don't use LOG_PID since the pid passed to us by - * our client may not be our own. therefore we want to - * print the pid ourselves. - */ -# ifdef LOG_DAEMON - openlog(ProgramName, LOG_PID, LOG_CRON); + + + /* we don't use LOG_PID since the pid passed to us by + * our client may not be our own. therefore we want to + * print the pid ourselves. + */ + /* SteveG says: That comment is not consistent with the + code, and makes no sense -- I suspect it's a remnant + of a cut-n-paster... */ +# ifdef LOG_CRON + openlog(ProgramName, LOG_PID, LOG_CRON); # else - openlog(ProgramName, LOG_PID); + openlog(ProgramName, LOG_PID); # endif - syslog_open = TRUE; /* assume openlog success */ - } - - syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail); + + syslog(LOG_INFO, "(%s) %s (%s)", username, event, detail); + closelog(); #endif /*SYSLOG*/ #if DEBUGGING if (DebugFlags) { fprintf(stderr, "log_it: (%s %d) %s (%s)\n", - username, pid, event, detail); + username, xpid, event, detail); } #endif } @@ -541,10 +598,15 @@ void log_close() { +#if defined(LOG_FILE) if (LogFD != ERR) { close(LogFD); LogFD = ERR; } +#endif +#if defined(SYSLOG) + closelog(); +#endif } @@ -604,8 +666,10 @@ *dst++ = '^'; *dst++ = '?'; } else { /* parity character */ - sprintf(dst, "\\%03o", ch); - dst += 4; + /* well, the following snprintf is paranoid, but that will + * keep grep happy */ + snprintf(dst, 5, "\\%03o", ch); + dst += 4; } } *dst = '\0'; @@ -622,43 +686,101 @@ { register char *dst = malloc(len*4 + 1); - mkprint(dst, src, len); + if (dst) + mkprint(dst, src, len); return dst; } #ifdef MAIL_DATE -/* Sat, 27 Feb 93 11:44:51 CST - * 123456789012345678901234567 +/* Sat, 27 Feb 1993 11:44:51 -0800 (CST) + * 1234567890123456789012345678901234567 */ char * arpadate(clock) time_t *clock; { - time_t t = clock ?*clock :time(0L); + static char ret[64]; /* zone name might be >3 chars */ + time_t t = clock ? *clock : time(NULL); struct tm *tm = localtime(&t); - static char ret[30]; /* zone name might be >3 chars */ - - (void) sprintf(ret, "%s, %2d %s %2d %02d:%02d:%02d %s", - DowNames[tm->tm_wday], - tm->tm_mday, - MonthNames[tm->tm_mon], - tm->tm_year, - tm->tm_hour, - tm->tm_min, - tm->tm_sec, - TZONE(*tm)); + char *qmark; + size_t len; + long gmtoff = get_gmtoff(&t, tm); + int hours = gmtoff / 3600; + int minutes = (gmtoff - (hours * 3600)) / 60; + + if (minutes < 0) + minutes = -minutes; + + /* Defensive coding (almost) never hurts... */ + len = strftime(ret, sizeof(ret), "%a, %e %b %Y %T ????? (%Z)", tm); + if (len == 0) { + ret[0] = '?'; + ret[1] = '\0'; + return ret; + } + qmark = strchr(ret, '?'); + if (qmark && len - (qmark - ret) >= 6) { + snprintf(qmark, 6, "% .2d%.2d", hours, minutes); + qmark[5] = ' '; + } return ret; } #endif /*MAIL_DATE*/ -#ifdef HAVE_SAVED_SUIDS -static int save_euid; -int swap_uids() { save_euid = geteuid(); return seteuid(getuid()); } -int swap_uids_back() { return seteuid(save_euid); } +#ifdef HAVE_SAVED_UIDS +static uid_t save_euid, save_egid; +int swap_uids() +{ + save_euid = geteuid(); save_egid = getegid(); + return (setegid(getgid()) || seteuid(getuid())) ? -1 : 0; +} +int swap_uids_back() +{ + return (setegid(save_egid) || seteuid(save_euid)) ? -1 : 0; +} #else /*HAVE_SAVED_UIDS*/ -int swap_uids() { return setreuid(geteuid(), getuid()); } +int swap_uids() +{ + return (setregid(getegid(), getgid()) || setreuid(geteuid(), getuid())) + ? -1 : 0; +} int swap_uids_back() { return swap_uids(); } #endif /*HAVE_SAVED_UIDS*/ + + +/* Return the offset from GMT in seconds (algorithm taken from sendmail). + * + * warning: + * clobbers the static storage space used by localtime() and gmtime(). + * If the local pointer is non-NULL it *must* point to a local copy. + */ +#ifndef HAVE_TM_GMTOFF +long get_gmtoff(time_t *clock, struct tm *local) +{ + struct tm gmt; + long offset; + + gmt = *gmtime(clock); + if (local == NULL) + local = localtime(clock); + + offset = (local->tm_sec - gmt.tm_sec) + + ((local->tm_min - gmt.tm_min) * 60) + + ((local->tm_hour - gmt.tm_hour) * 3600); + + /* Timezone may cause year rollover to happen on a different day. */ + if (local->tm_year < gmt.tm_year) + offset -= 24 * 3600; + else if (local->tm_year > gmt.tm_year) + offset += 24 * 3600; + else if (local->tm_yday < gmt.tm_yday) + offset -= 24 * 3600; + else if (local->tm_yday > gmt.tm_yday) + offset += 24 * 3600; + + return (offset); +} +#endif /* HAVE_TM_GMTOFF */ --- cron-3.0pl1.orig/compat.h +++ cron-3.0pl1/compat.h @@ -62,8 +62,8 @@ #endif #ifndef POSIX -# if (BSD >= 199103) || defined(__linux) || defined(ultrix) || defined(AIX) ||\ - defined(HPUX) || defined(CONVEX) || defined(IRIX) +# if (BSD >= 199103) || defined(__linux__) || defined(__GNU__) || defined(ultrix) ||\ + defined(AIX) ||\ defined(HPUX) || defined(CONVEX) || defined(IRIX) || defined(__GLIBC__) # define POSIX # endif #endif @@ -76,17 +76,17 @@ /*****************************************************************/ -#if !defined(BSD) && !defined(HPUX) && !defined(CONVEX) && !defined(__linux) +#if !defined(BSD) && !defined(HPUX) && !defined(CONVEX) && !defined(__linux__) && !defined(__GNU__) # define NEED_VFORK #endif -#if (!defined(BSD) || (BSD < 198902)) && !defined(__linux) && \ - !defined(IRIX) && !defined(NeXT) && !defined(HPUX) +#if (!defined(BSD) || (BSD < 198902)) && !defined(__linux__) && \ + !defined(IRIX) && !defined(NeXT) && !defined(HPUX) && !defined(__GNU__) && !defined(__GLIBC__) # define NEED_STRCASECMP #endif -#if (!defined(BSD) || (BSD < 198911)) && !defined(__linux) &&\ - !defined(IRIX) && !defined(UNICOS) && !defined(HPUX) +#if (!defined(BSD) || (BSD < 198911)) && !defined(__linux__) &&\ + !defined(IRIX) && !defined(UNICOS) && !defined(HPUX) && !defined(__GNU__) && !defined(__GLIBC__) # define NEED_STRDUP #endif @@ -102,19 +102,19 @@ # define NEED_SETSID #endif -#if (defined(POSIX) && !defined(BSD)) && !defined(__linux) +#if (defined(POSIX) && !defined(BSD)) && !defined(__linux__) && !defined(__GNU__) && !defined(__GLIBC__) # define NEED_GETDTABLESIZE #endif -#if (BSD >= 199103) +#if (BSD >= 199103) || defined(__linux) # define HAVE_SAVED_UIDS #endif -#if !defined(ATT) && !defined(__linux) && !defined(IRIX) && !defined(UNICOS) +#if !defined(ATT) && !defined(__linux__) && !defined(__GNU__) && !defined(IRIX) && !defined(UNICOS) && !defined(__GLIBC__) # define USE_SIGCHLD #endif -#if !defined(AIX) && !defined(UNICOS) +#if !defined(AIX) && !defined(UNICOS) && !defined(DEBIAN) # define SYS_TIME_H 1 #else # define SYS_TIME_H 0 --- cron-3.0pl1.orig/crontab.5 +++ cron-3.0pl1/crontab.5 @@ -17,7 +17,7 @@ .\" .\" $Id: crontab.5,v 2.4 1994/01/15 20:43:43 vixie Exp $ .\" -.TH CRONTAB 5 "24 January 1994" +.TH CRONTAB 5 "19 April 2010" .UC 4 .SH NAME crontab \- tables for driving cron @@ -34,13 +34,15 @@ as part of a cron command. .PP Blank lines and leading spaces and tabs are ignored. Lines whose first -non-space character is a pound-sign (#) are comments, and are ignored. +non-space character is a hash-sign (#) are comments, and are ignored. Note that comments are not allowed on the same line as cron commands, since they will be taken to be part of the command. Similarly, comments are not allowed on the same line as environment variable settings. .PP An active line in a crontab will be either an environment setting or a cron -command. An environment setting is of the form, +command. The crontab file is parsed from top to bottom, so any environment +settings will affect only the cron commands below them in the file. +An environment setting is of the form, .PP name = value .PP @@ -52,15 +54,41 @@ The .I value string may be placed in quotes (single or double, but matching) to preserve -leading or trailing blanks. +leading or trailing blanks. To define an empty variable, quotes +.B must +be used. The +.I value +string is +.B not +parsed for environmental substitutions or replacement of variables, thus lines +like +.PP + PATH = $HOME/bin:$PATH +.PP +will not work as you might expect. And neither will this work +.PP + A=1 + B=2 + C=$A $B +.PP +There will not be any subsitution for the defined variables in the +last value. +.PP +An alternative for setting up the commands path is using the fact that +many shells will treat the tilde(~) as substitution of $HOME, so if you use +.I bash +for your tasks you can use this: +.PP + SHELL=/bin/bash + PATH=~/bin:/usr/bin/:/bin .PP -Several environment variables are set up -automatically by the +Several environment variables are set up automatically by the .IR cron (8) daemon. SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd -line of the crontab's owner. -HOME and SHELL may be overridden by settings in the crontab; LOGNAME may not. +line of the crontab's owner. PATH is set to "/usr/bin:/bin". +HOME, SHELL, and PATH may be overridden by settings in the crontab; +LOGNAME is the user that the job is running from, and may not be changed. .PP (Another note: the LOGNAME variable is sometimes called USER on BSD systems... on these systems, USER will be set also.) @@ -69,16 +97,47 @@ .IR cron (8) will look at MAILTO if it has any reason to send mail as a result of running commands in ``this'' crontab. If MAILTO is defined (and non-empty), mail is -sent to the user so named. If MAILTO is defined but empty (MAILTO=""), no -mail will be sent. Otherwise mail is sent to the owner of the crontab. This -option is useful if you decide on /bin/mail instead of /usr/lib/sendmail as -your mailer when you install cron -- /bin/mail doesn't do aliasing, and UUCP -usually doesn't read its mail. +sent to the user so named. MAILTO may also be used to direct mail to multiple +recipients by separating recipient users with a comma. If MAILTO is defined +but empty (MAILTO=""), no mail will be sent. Otherwise mail is sent to the +owner of the crontab. +.PP +On the Debian GNU/Linux system, cron supports the +.B pam_env +module, and loads the environment specified by +.IR /etc/environment +and +.IR /etc/security/pam_env.conf . +It also reads locale information from +.IR /etc/default/locale . +However, the PAM settings do +.B NOT +override the settings described above nor any settings in the +.I crontab +file itself. Note in particular that if you want a PATH other than +"/usr/bin:/bin", you will need to set it in the crontab file. +.PP +By default, cron will send mail using the mail "Content-Type:" header of +"text/plain" with the "charset=" parameter set to the charmap / codeset of the +locale in which +.IR crond (8) +is started up - ie. either the default system locale, if no LC_* environment +variables are set, or the locale specified by the LC_* environment variables +( see +.IR locale (7) ). +You can use different character encodings for mailed cron job output by +setting the CONTENT_TYPE and CONTENT_TRANSFER_ENCODING variables in crontabs, +to the correct values of the mail headers of those names. .PP The format of a cron command is very much the V7 standard, with a number of upward-compatible extensions. Each line has five time and date fields, -followed by a user name if this is the system crontab file, -followed by a command. Commands are executed by +followed by a command, followed by a newline character ('\\n'). +The system crontab (/etc/crontab) uses the same format, except that +the username for the command is specified after the time and +date fields and before the command. The fields may be separated +by spaces or tabs. +.PP +Commands are executed by .IR cron (8) when the minute, hour, and month of year fields match the current time, .I and @@ -97,9 +156,9 @@ .br hour 0-23 .br -day of month 0-31 +day of month 1-31 .br -month 0-12 (or names, see below) +month 1-12 (or names, see below) .br day of week 0-7 (0 or 7 is Sun, or use names) .br @@ -131,26 +190,64 @@ run. The entire command portion of the line, up to a newline or % character, will be executed by /bin/sh or by the shell -specified in the SHELL variable of the cronfile. +specified in the SHELL variable of the crontab file. Percent-signs (%) in the command, unless escaped with backslash (\\), will be changed into newline characters, and all data after the first % will be sent to the command as standard -input. +input. There is no way to split a single command line onto multiple +lines, like the shell's trailing "\\". .PP Note: The day of a command's execution can be specified by two fields \(em day of month, and day of week. If both fields are -restricted (ie, aren't *), the command will be run when +restricted (i.e., aren't *), the command will be run when .I either field matches the current time. For example, .br ``30 4 1,15 * 5'' would cause a command to be run at 4:30 am on the 1st and 15th of each -month, plus every Friday. +month, plus every Friday. One can, however, achieve the desired result +by adding a test to the command (see the last example in EXAMPLE CRON FILE +below). +.PP +Instead of the first five fields, one of eight special strings may appear: +.IP +.ta 1.5i +string meaning +.br +------ ------- +.br +@reboot Run once, at startup. +.br +@yearly Run once a year, "0 0 1 1 *". +.br +@annually (same as @yearly) +.br +@monthly Run once a month, "0 0 1 * *". +.br +@weekly Run once a week, "0 0 * * 0". +.br +@daily Run once a day, "0 0 * * *". +.br +@midnight (same as @daily) +.br +@hourly Run once an hour, "0 * * * *". +.br +.PP +Please note that startup, as far as @reboot is concerned, is the time when +the +.IR cron (8) +daemon startup. In particular, it may be before some system daemons, +or other facilities, were startup. This is due to the boot order +sequence of the machine. + .SH EXAMPLE CRON FILE + +The following lists an example of a user crontab file. + .nf -# use /bin/sh to run commands, no matter what /etc/passwd says -SHELL=/bin/sh +# use /bin/bash to run commands, instead of the default /bin/sh +SHELL=/bin/bash # mail any output to `paul', no matter whose crontab this is MAILTO=paul # @@ -159,30 +256,101 @@ # run at 2:15pm on the first of every month -- output mailed to paul 15 14 1 * * $HOME/bin/monthly # run at 10 pm on weekdays, annoy Joe -0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?% +0 22 * * 1-5 mail \-s "It's 10pm" joe%Joe,%%Where are your kids?% 23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday" 5 4 * * sun echo "run at 5 after 4 every sunday" +# Run on every second Saturday of the month +0 4 8-14 * * test $(date +\\%u) \-eq 6 && echo "2nd Saturday" +.fi +.SH EXAMPLE SYSTEM CRON FILE + +The following lists the content of a regular system-wide crontab file. Unlinke a +user's crontab, this file has the username field, as used by /etc/crontab. + +.nf +# /etc/crontab: system-wide crontab +# Unlike any other crontab you don't have to run the `crontab' +# command to install the new version when you edit this file +# and files in /etc/cron.d. These files also have username fields, +# that none of the other crontabs do. + +SHELL=/bin/sh +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +# m h dom mon dow user command +17 * * * * root cd / && run-parts --report /etc/cron.hourly +25 6 * * * root test \-x /usr/sbin/anacron || ( cd / && run-parts \-\-report /etc/cron.daily ) +47 6 * * 7 root test \-x /usr/sbin/anacron || ( cd / && run-parts \-\-report /etc/cron.weekly ) +52 6 1 * * root test \-x /usr/sbin/anacron || ( cd / && run-parts \-\-report /etc/cron.monthly ) +# .fi .SH SEE ALSO cron(8), crontab(1) .SH EXTENSIONS When specifying day of week, both day 0 and day 7 will be considered Sunday. -BSD and ATT seem to disagree about this. +BSD and AT&T seem to disagree about this. .PP Lists and ranges are allowed to co-exist in the same field. "1-3,7-9" would -be rejected by ATT or BSD cron -- they want to see "1-3" or "7,8,9" ONLY. +be rejected by AT&T or BSD cron -- they want to see "1-3" or "7,8,9" ONLY. .PP Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9". .PP -Names of months or days of the week can be specified by name. +Months or days of the week can be specified by name. .PP -Environment variables can be set in the crontab. In BSD or ATT, the +Environment variables can be set in the crontab. In BSD or AT&T, the environment handed to child processes is basically the one from /etc/rc. .PP Command output is mailed to the crontab owner (BSD can't do this), can be mailed to a person other than the crontab owner (SysV can't do this), or the feature can be turned off and no mail will be sent at all (SysV can't do this either). -.SH AUTHOR +.PP +All of the `@' commands that can appear in place of the first five fields +are extensions. +.SH LIMITATIONS +The +.I cron +daemon runs with a defined timezone. It currently does not support +per-user timezones. All the tasks: system's and user's will be run based on the +configured timezone. Even if a user specifies the +.I TZ +environment variable in his +.I crontab +this will affect only the commands executed in the crontab, not the execution +of the crontab tasks themselves. + +The +.I crontab +syntax does not make it possible to define all possible periods one could +image off. For example, it is not straightforward to define the last +weekday of a month. If a task needs to be run in a specific period of time +that cannot be defined in the +.I crontab +syntaxs the best approach would be to have the program itself check the +date and time information and continue execution only if the period +matches the desired one. + +If the program itself cannot do the checks then a wrapper script would be +required. Useful tools that could be used for date analysis are +.I ncal +or +.I calendar +For example, to run a program the last Saturday of every month you could use +the following wrapper code: + .nf -Paul Vixie +0 4 * * Sat [ "$(date +\\%e)" = "`ncal | grep $(date +\\%a | sed -e 's/.$//') | sed -e 's/^.*\\s\\([0-9]\\+\\)\\s*$/\\1/'`" ] && echo "Last Saturday" && program_to_run +.fi + + +.SH DIAGNOSTICS +cron requires that each entry in a crontab end in a newline character. If the +last entry in a crontab is missing a newline (ie, terminated by EOF), cron will +consider the crontab (at least partially) broken. A warning will be written to +syslog. + +.SH AUTHOR +Paul Vixie is the author of +.I cron +and original creator of this manual page. This page has also been modified for +Debian by Steve Greenland, Javier Fernandez-Sanguino and Christian Kastner. --- cron-3.0pl1.orig/crontab.1 +++ cron-3.0pl1/crontab.1 @@ -17,40 +17,54 @@ .\" .\" $Id: crontab.1,v 2.4 1993/12/31 10:47:33 vixie Exp $ .\" -.TH CRONTAB 1 "29 December 1993" +.TH CRONTAB 1 "19 April 2010" .UC 4 .SH NAME -crontab \- maintain crontab files for individual users (V3) +crontab \- maintain crontab files for individual users (Vixie Cron) .SH SYNOPSIS -crontab [ -u user ] file +crontab [ \-u user ] file .br -crontab [ -u user ] { -l | -r | -e } +crontab [ \-u user ] [ \-i ] { \-e | \-l | \-r } .SH DESCRIPTION -.I Crontab +.I crontab is the program used to install, deinstall or list the tables used to drive the .IR cron (8) daemon in Vixie Cron. Each user can have their own crontab, and though -these are files in /var, they are not intended to be edited directly. +these are files in /var/spool/cron/crontabs, +they are not intended to be edited directly. .PP If the -.I allow -file exists, then you must be listed therein in order to be allowed to use -this command. If the -.I allow +.I /etc/cron.allow +file exists, then you must be listed (one user per line) therein in order to be +allowed to use this command. If the +.I /etc/cron.allow file does not exist but the -.I deny +.I /etc/cron.deny file does exist, then you must \fBnot\fR be listed in the -.I deny -file in order to use this command. If neither of these files exists, then -depending on site-dependent configuration parameters, only the super user -will be allowed to use this command, or all users will be able to use this -command. +.I /etc/cron.deny +file in order to use this command. +.PP +If neither of these files exists, then depending on site-dependent +configuration parameters, only the super user will be allowed to use this +command, or all users will be able to use this command. +.PP +If both files exist then +.I /etc/cron.allow +takes precedence. Which means that +.I /etc/cron.deny +is not considered and your user must be listed in +.I /etc/cron.allow +in order to be able to use the crontab. +.PP +Regardless of the existance of any of these files, the root administrative +user is always allowed to setup a crontab. For standard Debian systems, all +users may use this command. .PP If the .I -u option is given, it specifies the name of the user whose crontab is to be -tweaked. If this option is not given, +used (when listing) or modified (when editing). If this option is not given, .I crontab examines "your" crontab, i.e., the crontab of the person executing the command. Note that @@ -58,7 +72,7 @@ can confuse .I crontab and that if you are running inside of -.IR su (8) +.IR su (8) you should always use the .I -u option for safety's sake. @@ -68,7 +82,10 @@ .PP The .I -l -option causes the current crontab to be displayed on standard output. +option causes the current crontab to be displayed on standard output. See +the note under +.B DEBIAN SPECIFIC +below. .PP The .I -r @@ -77,24 +94,71 @@ The .I -e 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. +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. If +neither of the environment variables is defined, then the +default editor /usr/bin/editor is used. +.PP +The +.I -i +option modifies the \-r option to prompt the user for a 'y/Y' response +before actually removing the crontab. +.SH DEBIAN SPECIFIC +The "out-of-the-box" behaviour for +.I crontab -l +is to display the three line "DO NOT EDIT THIS FILE" header +that is placed at the +beginning of the crontab when it is installed. The problem is that +it makes the sequence +.PP +crontab \-l | crontab \- +.PP +non-idempotent -- you keep adding copies of the header. This causes +pain to scripts that use sed to edit a crontab. Therefore, the default +behaviour of the +.B -l +option has been changed to not output such header. You may obtain the +original behaviour by setting the environment variable +.B CRONTAB_NOHEADER +to 'N', which will cause the +.I crontab -l +command to emit the extraneous header. .SH "SEE ALSO" crontab(5), cron(8) .SH FILES .nf -/var/cron/allow -/var/cron/deny +/etc/cron.allow +/etc/cron.deny +/var/spool/cron/crontabs .fi +.PP +There is one file for each user's crontab under the /var/spool/cron/crontabs +directory. Users are not allowed to edit the files under that directory +directly to ensure that only users allowed by the system to run periodic tasks +can add them, and only syntactically correct crontabs will be written there. +This is enforced by having the directory writable only by the +.I crontab +group and configuring +.I crontab +command with the setgid bid set for that specific group. .SH STANDARDS The .I crontab command conforms to IEEE Std1003.2-1992 (``POSIX''). This new command syntax differs from previous versions of Vixie Cron, as well as from the classic SVR3 syntax. + .SH DIAGNOSTICS A fairly informative usage message appears if you run it with a bad command line. + +cron requires that each entry in a crontab end in a newline character. If the +last entry in a crontab is missing the newline, cron will consider the crontab +(at least partially) broken and refuse to install it. + .SH AUTHOR -.nf -Paul Vixie +Paul Vixie is the author of +.I cron +and original creator of this manual page. This page has also been modified for +Debian by Steve Greenland, Javier Fernandez-Sanguino and Christian Kastner. --- cron-3.0pl1.orig/config.h +++ cron-3.0pl1/config.h @@ -29,6 +29,7 @@ */ #ifndef DEBUGGING +#error DEBUGGING not defined #define DEBUGGING 1 /* 1 or 0 -- do you want debugging code built in? */ #endif @@ -42,22 +43,27 @@ */ #define MAILCMD _PATH_SENDMAIL /*-*/ -#define MAILARGS "%s -FCronDaemon -odi -oem -or0s %s" /*-*/ - /* -Fx = set full-name of sender +/* #define MAILARGS "%s -i -FCronDaemon -odi -oem %s" /*-*/ +#define MAILARGS "%s -i -FCronDaemon -oem %s" /*-*/ + /* -i = don't terminate on "." by itself + * -Fx = set full-name of sender * -odi = Option Deliverymode Interactive * -oem = Option Errors Mailedtosender + * -t = read recipient from header of message * -or0s = Option Readtimeout -- don't time out + * XXX: sendmail doesn't allow -or0s when invoked + * by joe user. --okir */ -/* #define MAILCMD "/bin/mail" /*-*/ -/* #define MAILARGS "%s -d %s" /*-*/ +/* #define MAILCMD "/bin/mail" -*/ +/* #define MAILARGS "%s -d %s" -*/ /* -d = undocumented but common flag: deliver locally? */ -/* #define MAILCMD "/usr/mmdf/bin/submit" /*-*/ -/* #define MAILARGS "%s -mlrxto %s" /*-*/ +/* #define MAILCMD "/usr/mmdf/bin/submit" -*/ +/* #define MAILARGS "%s -mlrxto %s" -*/ -/* #define MAIL_DATE /*-*/ +/* #define MAIL_DATE -*/ /* should we include an ersatz Date: header in * generated mail? if you are using sendmail * for MAILCMD, it is better to let sendmail @@ -68,7 +74,7 @@ * defined but neither exists, should crontab(1) be * usable only by root? */ -/*#define ALLOW_ONLY_ROOT /*-*/ +/*#define ALLOW_ONLY_ROOT -*/ /* if you want to use syslog(3) instead of appending * to CRONDIR/LOG_FILE (/var/cron/log, e.g.), define --- cron-3.0pl1.orig/cron.8 +++ cron-3.0pl1/cron.8 @@ -17,45 +17,288 @@ .\" .\" $Id: cron.8,v 2.2 1993/12/28 08:34:43 vixie Exp $ .\" -.TH CRON 8 "20 December 1993" +.TH CRON 8 "19 April 2010" .UC 4 .SH NAME cron \- daemon to execute scheduled commands (Vixie Cron) .SH SYNOPSIS cron +.RB [ -f ] +.RB [ -l ] +.RB [ -L +.IR loglevel ] .SH DESCRIPTION -.I Cron -should be started from /etc/rc or /etc/rc.local. It will return immediately, -so you don't need to start it with '&'. -.PP -.I Cron -searches /var/cron/tabs for crontab files which are named after accounts in -/etc/passwd; crontabs found are loaded into memory. -.I Cron -also searches for /etc/crontab which is in a different format (see -.IR crontab(5)). -.I Cron -then wakes up every minute, examining all stored crontabs, checking each -command to see if it should be run in the current minute. When executing -commands, any output is mailed to the owner of the crontab (or to the user -named in the MAILTO environment variable in the crontab, if such exists). +.I cron +is started automatically from /etc/init.d on entering multi-user +runlevels. +.SH OPTIONS +.TP 8 +.B -f +Stay in foreground mode, don't daemonize. +.TP +.B -l +Enable LSB compliant names for /etc/cron.d files. This setting, however, does +not affect the parsing of files under /etc/cron.hourly, /etc/cron.daily, +/etc/cron.weekly or /etc/cron.monthly. +.TP +.B -L loglevel +Tell cron what to log about \fBjobs\fR (errors are logged regardless of this +value) as the sum of the following values: +.br +.RS 12 +.IP \fB1\fR +will log the start of all cron jobs +.IP \fB2\fR +will log the end of all cron jobs +.IP \fB4\fR +will log all failed jobs (exit status != 0) +.IP \fB8\fR +will log the process number of all cron jobs +.RE +.IP +The default is to log the start of all jobs (1). Logging will be disabled +if \fIlevels\fR is set to zero (0). A value of fifteen (15) will select all +options. +.SH NOTES +.PP +.I cron +searches its spool area (/var/spool/cron/crontabs) for crontab +files (which are named after accounts in +/etc/passwd); crontabs found are loaded into memory. Note that +crontabs in this directory should not be accessed directly - +the +.I crontab +command should be used to access and update them. + +.I cron +also reads /etc/crontab, which is in a slightly different format (see +.IR crontab(5) ). +In Debian, the content of /etc/crontab is predefined +to run programs under /etc/cron.hourly, /etc/cron.daily, +/etc/cron.weekly and /etc/cron.monthly. This configuration is specific to +Debian, see the note under +.B DEBIAN SPECIFIC +below. +in order + +Additionally, in Debian, +.I cron +reads the files in the /etc/cron.d directory. +.I cron +treats the files in /etc/cron.d as in the same way as the /etc/crontab file (they +follow the special format of that file, i.e. they include the +.I user +field). However, they are independent of /etc/crontab: they do not, for +example, inherit environment variable settings from it. This change is +specific to Debian see the note under +.B DEBIAN SPECIFIC +below. + +Like /etc/crontab, the files in the /etc/cron.d directory are +monitored for changes. In general, the system administrator should not use /etc/cron.d/, +but use the standard system crontab /etc/crontab. + +/etc/crontab and the files in /etc/cron.d must be owned by root, and must not +be group- or other-writable. In contrast to the spool area, the files +under /etc/cron.d or the files under /etc/cron.hourly, /etc/cron.daily, +/etc/cron.weekly and /etc/cron.monthly may also be symlinks, +provided that both the symlink and the file it points to are owned by root. +The files under /etc/cron.d do not need to be executable, while the files +under /etc/cron.hourly, /etc/cron.daily, +/etc/cron.weekly and /etc/cron.monthly do, as they are run by +.I run-parts +(see +.IR run-parts(8) +for more information). + +.I cron +then wakes up every minute, examining all stored crontabs, checking +each command to see if it should be run in the current minute. When +executing commands, any output is mailed to the owner of the crontab +(or to the user named in the MAILTO environment variable in the +crontab, if such exists). The children copies of cron running these +processes have their name coerced to uppercase, as will be seen in the +syslog and ps output. .PP Additionally, .I cron checks each minute to see if its spool directory's modtime (or the modtime -on -.IR /etc/crontab) +on the +.IR /etc/crontab +file) has changed, and if it has, .I cron -will then examine the modtime on all crontabs and reload those which have +will then examine the modtime on all crontabs files and reload those which have changed. Thus .I cron need not be restarted whenever a crontab file is modified. Note that the -.IR Crontab (1) +.IR crontab (1) command updates the modtime of the spool directory whenever it changes a crontab. +.PP +Special considerations exist when the clock is changed by less than 3 +hours, for example at the beginning and end of daylight savings +time. If the time has moved forwards, those jobs which would have +run in the time that was skipped will be run soon after the change. +Conversely, if the time has moved backwards by less than 3 hours, +those jobs that fall into the repeated time will not be re-run. +.PP +Only jobs that run at a particular time (not specified as +@hourly, nor with '*' in the hour or minute specifier) are +affected. Jobs which are specified with wildcards are run based on the +new time immediately. +.PP +Clock changes of more than 3 hours are considered to be corrections to +the clock, and the new time is used immediately. +.PP +.I cron +logs its action to the syslog facility 'cron', and logging may be +controlled using the standard +.IR syslogd(8) +facility. +.SH ENVIRONMENT +If configured in +.I /etc/default/cron +in Debian systems, the +.I cron +daemon localisation settings environment can be managed through the use of +.I /etc/environment +or through the use of +.I /etc/default/locale +with values from the latter overriding values from the former. These +files are read and they will be used to setup the LANG, LC_ALL, and +LC_CTYPE environment variables. These variables are then used to set the +charset of mails, which defaults to 'C'. +.PP +This does +.B NOT +affect the environment of tasks running under cron. For more information +on how to modify the environment of tasks, consult +.IR crontab(5) +\. +.PP +The daemon will use, if present, the definition from +.I /etc/timezone +for the timezone. +.PP +The environment can be redefined in user's crontab definitions but +.I cron +will only handle tasks in a single timezone. + +.SH DEBIAN SPECIFIC +.PP +Debian introduces some changes to +.I cron +that were not originally available upstream. The most significant +changes introduced are: + +.IP \(em +Support for /etc/cron.{hourly,daily,weekly,monthly} via /etc/crontab, +.IP \(em +Support for /etc/cron.d (drop-in dir for package crontabs), +.IP \(em +PAM support, +.IP \(em +SELinux support, +.IP \(em +auditlog support, +.IP \(em +DST and other time-related changes/fixes, +.IP \(em +SGID crontab(1) instead of SUID root, +.IP \(em +Debian-specific file locations and commands, +.IP \(em +Debian-specific configuration (/etc/default/cron), +.IP \(em +numerous other smaller features and fixes. + +.PP +Support for /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly and +/etc/cron.monthly is provided in Debian through the default setting +of the /etc/crontab file (see the system-wide example in +.IR crontab(5) ). +The default sytem-wide crontab contains four tasks: run every hour, every +day, every week and every month. Each of these tasks will execute +.B run-parts +providing each one of the directories as an argument. These tasks are disabled if +.B anacron +is installed (except for the hourly task) to prevent conflicts between +both daemons. + +As described above, the files under these directories have to be pass +some sanity checks including the following: be executable, be owned by root, +not be writable by group or other and, if symlinks, point to files owned by +root. Additionally, the file names must conform to the filename requirements +of +.BR run-parts : +they must be entirely made up of letters, digits and can only contain the +special signs underscores ('_') and hyphens ('-'). Any file that does +not conform to these requirements will not be executed by +.BR run-parts . +For example, any file containing dots will be ignored. +This is done to prevent cron from running any of the files +that are left by the Debian package management system when handling files in +/etc/cron.d/ as configuration files (i.e. files ending in .dpkg-dist, .dpkg-orig, +and .dpkg-new). + +This feature can be used by system administrators and packages to include +tasks that will be run at defined intervals. Files created by packages in these +directories should be named after the package that supplies them. + +.PP +Support for /etc/cron.d is included in the +.I cron +daemon itself, which handles this location as the system-wide crontab spool. +This directory can contain any file defining tasks following the format +used in /etc/crontab, i.e. unlike the user cron spool, these files must +provide the username to run the task as in the task definition. + +Files in this directory have to be owned by root, do not need to be executable +(they are configuration files, just like /etc/crontab) and +must conform to the same naming convention as used by +.IR run-parts(8) : +they +must consist solely of upper- and lower-case letters, digits, underscores, +and hyphens. This means that they +.B cannot +contain any dots. +If the +.B -l +option is specified to +.I cron +(this option can be setup through /etc/default/cron, see below), then they must +conform to the LSB namespace specification, exactly as in the +.B --lsbsysinit +option in +.IR run-parts . + +The intended purpose +of this feature is to allow packages that require +finer control of their scheduling than the /etc/cron.{hourly,daily,weekly,monthly} +directories to add a crontab file to /etc/cron.d. Such files +should be named after the package that supplies them. + + +Also, the default configuration of +.I cron +is controlled by +.I /etc/default/cron +which is read by the init.d script that launches the +.I cron +daemon. This file determines whether +.I cron +will read the system's environment variables and makes it possible to add +additional options to the +.I cron +program before it is executed, either to configure its logging or to define how +it will treat the files under /etc/cron.d. + .SH "SEE ALSO" -crontab(1), crontab(5) +crontab(1), crontab(5), run-parts(8) .SH AUTHOR -.nf -Paul Vixie +Paul Vixie is the author of +.I cron +and original creator of this manual page. This page has also been modified for +Debian by Steve Greenland, Javier Fernandez-Sanguino and Christian Kastner. + --- cron-3.0pl1.orig/debian/crontab.main +++ cron-3.0pl1/debian/crontab.main @@ -0,0 +1,15 @@ +# /etc/crontab: system-wide crontab +# Unlike any other crontab you don't have to run the `crontab' +# command to install the new version when you edit this file +# and files in /etc/cron.d. These files also have username fields, +# that none of the other crontabs do. + +SHELL=/bin/sh +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +# m h dom mon dow user command +17 * * * * root cd / && run-parts --report /etc/cron.hourly +25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily ) +47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly ) +52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly ) +# --- cron-3.0pl1.orig/debian/REFS +++ cron-3.0pl1/debian/REFS @@ -0,0 +1,74 @@ + +References of previous discussions (not bug reports) + +PROPOSED +-------- +cron daemon virtual package + http://lists.debian.org/debian-devel/2004/06/msg00024.html + http://lists.debian.org/debian-devel/2006/10/msg00281.html + + +REVIEW +------ + +cronjobs more often than daily + http://lists.debian.org/debian-devel/1998/01/msg00155.html + +cron shouts + http://lists.debian.org/debian-devel/1997/12/msg00407.html + http://lists.debian.org/debian-devel/1998/10/msg02425.html + +cron fork + http://lists.debian.org/debian-devel/2004/07/msg01342.html + +crontab and editors + http://lists.debian.org/debian-devel/2002/05/msg00488.html + +common basic security checks (cron-driven) + http://lists.debian.org/debian-devel/2002/12/msg01517.html + +proposal to serialize cron + http://lists.debian.org/debian-devel/2004/09/msg00320.html + +should packages depend on cron + http://lists.debian.org/debian-devel/1997/07/msg01041.html + +crontabs + m4 + http://lists.debian.org/debian-devel/1998/01/msg00763.html + +what is /var/backups for + http://lists.debian.org/debian-devel/2002/08/msg00150.html + +opinions about potential new cron/at features + http://lists.debian.org/debian-devel/1999/02/msg01366.html + +display popup messages (in X) with cron + http://lists.debian.org/debian-user/2006/01/msg01089.html + http://lists.debian.org/debian-user/2006/06/msg02314.html + +ISSUES (REVIEW) +--------------- + +cron and GMT time + http://lists.debian.org/debian-user/2006/08/msg00186.html + +user variable in cron + http://lists.debian.org/debian-user/2006/01/msg02995.html + +Attack of the undead /usr/sbin/cron +(issue with pam-ldap) + http://lists.debian.org/debian-user/2006/05/msg02523.html + http://serendipity.ruwenzori.net/index.php/2006/05/23/attack-of-the-undead-usrsbincron + +blank line in crontabs + http://lists.debian.org/debian-user/2005/08/msg01630.html + +IMPLEMENTED +----------- +setgid crontab + http://lists.debian.org/debian-devel/2003/08/msg00160.html + http://lists.debian.org/debian-news/debian-news-2003/msg00042.html + +niced cron jobs + http://lists.debian.org/debian-devel/1999/06/msg01697.html + --- cron-3.0pl1.orig/debian/cron.upstart +++ cron-3.0pl1/debian/cron.upstart @@ -0,0 +1,14 @@ +# cron - regular background program processing daemon +# +# cron is a standard UNIX program that runs user-specified programs at +# periodic scheduled times + +description "regular background program processing daemon" + +start on runlevel [2345] +stop on runlevel [!2345] + +expect fork +respawn + +exec cron --- cron-3.0pl1.orig/debian/cron.bug-control +++ cron-3.0pl1/debian/cron.bug-control @@ -0,0 +1 @@ +package-status: nscd nis libpam-mount libpam-ldap libnss-ldap libnss-ldapd --- cron-3.0pl1.orig/debian/cron.manpages +++ cron-3.0pl1/debian/cron.manpages @@ -0,0 +1,3 @@ +cron.8 +crontab.1 +crontab.5 --- cron-3.0pl1.orig/debian/install +++ cron-3.0pl1/debian/install @@ -0,0 +1,2 @@ +cron usr/sbin +crontab usr/bin --- cron-3.0pl1.orig/debian/cron.pam +++ cron-3.0pl1/debian/cron.pam @@ -0,0 +1,18 @@ +# The PAM configuration file for the cron daemon + +@include common-auth + +# Read environment variables from pam_env's default files, /etc/environment +# and /etc/security/pam_env.conf. +session required pam_env.so + +# In addition, read system locale information +session required pam_env.so envfile=/etc/default/locale + +@include common-account +@include common-session-noninteractive + +# Sets up user limits, please define limits for cron tasks +# through /etc/security/limits.conf +session required pam_limits.so + --- cron-3.0pl1.orig/debian/copyright +++ cron-3.0pl1/debian/copyright @@ -0,0 +1,87 @@ +Format: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=173 +Upstream-Name: vixie-cron +Ustream-Contact: Paul Vixie +Source: + The original source for this package could be obtained from + ftp://ftp.vix.com/pub/vixie/cron-3.0, although that link appears to have gone + down. + +Files: * +Copyright: 1988, 1990, 1993, 1994, Paul Vixie + 1994, Ian Jackson + 1996-2005, Steve Greenland + 2005-2006, 2008-2010, Javier Fernández-Sanguino Peña + 2010-2011, Christian Kastner + Numerous contributions via the Debian BTS copyright their respective authors +License: Paul Vixie's license + +Files: database.c +Copyright: 1988, 1990, 1993, 1994 Paul Vixie + 1996-2005, Steve Greenland + 2003, Clint Adams + 2010-2011, Christian Kastner + 2011, Red Hat, Inc. +License: Paul Vixie's license and GPL-2+ and ISC + +Files: debian/examples/cron-stats.pl +Copyright: 2006, Javier Fernández-Sanguino Peña +License: GPL-2+ + +Files: debian/examples/cron-tasks-review.sh +Copyright: 2011, Javier Fernández-Sanguino Peña +License: GPL-2+ + +Files: debian/examples/crontab2english.pl +Copyright: 2001, Sean M. Burke +License: Artistic + +License: Paul Vixie's license + Distribute freely, except: don't remove my name from the source or + documentation (don't take credit for my work), mark your changes (don't + get me blamed for your possible bugs), don't alter or remove this + notice. May be sold if buildable source is provided to buyer. No + warranty of any kind, express or implied, is included with this + software; use at your own risk, responsibility for damages (if any) to + anyone resulting from the use of this software rests entirely with the + user. + +License: GPL-2+ + This package 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 package 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, see + . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". + +License: Artistic + This program is free software; you can redistribute it and/or modify it + under the terms of the "Artistic License" which comes with Debian. + . + THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES + OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + . + On Debian systems, the complete text of the Artistic License + can be found in "/usr/share/common-licenses/Artistic". + +License: ISC + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + . + THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. --- cron-3.0pl1.orig/debian/cron.bug-script +++ cron-3.0pl1/debian/cron.bug-script @@ -0,0 +1,28 @@ +#!/bin/sh +# reportbug output generator script for cron package + +# reportbug expects output on fd 3 +exec 1>&3 2>&3 + +echo "--- EDITOR:" +if [ -n "$EDITOR" ] ; then + echo "not set" +else + echo $EDITOR +fi +echo + +echo "--- /usr/bin/editor:" +readlink -f /usr/bin/editor +echo + +echo "--- /usr/bin/crontab:" +ls -l /usr/bin/crontab +echo + +for dir in /var/spool/cron /var/spool/cron/crontabs /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.monthly /etc/cron.weekly ; do + echo "--- $dir:" + ls -ld $dir + echo +done + --- cron-3.0pl1.orig/debian/placeholder +++ cron-3.0pl1/debian/placeholder @@ -0,0 +1,2 @@ +# DO NOT EDIT OR REMOVE +# This file is a simple placeholder to keep dpkg from removing this directory --- cron-3.0pl1.orig/debian/TODO +++ cron-3.0pl1/debian/TODO @@ -0,0 +1,46 @@ + +TODO stuff +---------- + +- Split of standard tasks to cron-standard, preliminary packages + currently available at + http://people.debian.org/~jfs/cron-standard + but we need to handle the conffiles so that they don't get messed up + See bug #257393 and + Message-ID: <20050309142043.GB16617@dat.etsit.upm.es> + +- Better behave with cron replacements, investigate how cron packages + (like fcron or bcron) can be installed/deinstalled without breaking + the system. + This might imply fixing #304036 (which might help fix #312614 too BTW) + +- Debate changing the policy of /etc/cron.d being a drop-in for packages + only vs. a general drop-in for crontabs + + +Milestones +---------- + +- Convert source package to source format 3.0 (quilt) + The current cron source package is the result of a 1993 upstream and + 17 years of commits. This organic growth must be normalized before an + upgrade to upstream cron-4.1 can be considered. + +- Compare our resulting cron-3.0 patches to FreeBSD's where possible + This is intended as a safety measure; the goal is to identify any grave + errors or other larger issues in our patches so we don't carry them on + into cron-4.1. + +- Review and update the ancient Debian packaging + Certain elements are heavily outdated and must be either brought + up-to-date, or dropped. + +- Update to upstream cron-4.1 + +- Compare our resulting cron-4.1 to RHEL/SLES, with a focus on possible + security issues (SUID/SGID, SELinux, ...) + +- Compare our resulting cron-4.1 to Fedora's cronie (a fork of ISC cron) + +- Consider switching upstream to cronie, or at least grabbing some + of it's features such as INOTIFY support. --- cron-3.0pl1.orig/debian/cron.default +++ cron-3.0pl1/debian/cron.default @@ -0,0 +1,3 @@ +# This file has been deprecated. Please add custom options for cron to +# /etc/init/cron.conf and/or /etc/init/cron.override directly. See +# the init(5) man page for more information. --- cron-3.0pl1.orig/debian/svn-git-migration.txt +++ cron-3.0pl1/debian/svn-git-migration.txt @@ -0,0 +1,121 @@ +Subversion-to-Git Migration Notes +================================= + +The following is a documentation of the exact steps that were taken to convert +Debian cron's subversion repository to a git repository. This was a one-shot +conversion, after which the git repository became authoritative. git-svn's +bridging features were therefore not needed. + +The following packages were used in this conversion: + * git-svn + * pristine-tar + +Additionally, a collection of scripts known as git-svn-abandon were used. These +scripts post-process a git repository cloned from SVN by converting subversion +tag branches into proper git tags, rewriting commits and so on. They were +retrieved from: + + https://github.com/nothingmuch/git-svn-abandon + +The grafting step was skipped, as there were no branch merges within SVN. + + +STEPS +===== + +1) The git-svn-abandon scripts were cloned, and the destination directory was + added to $PATH: + + $ git clone git://github.com/nothingmuch/git-svn-abandon.git + $ PATH=$PATH:$PWD/git-svn-abandon/ + + A tiny patch was applied to the scripts to prettify the tags during the + process (upstream/*, debian/*, and so on). The patch can be found at the + bottom of this document. + + +2) Before importing the svn history, an authors file /path/to/authors-file + was created for use by git-svn. See git-svn(1) for more info. + + +3) The svn repository was cloned with + + $ git svn clone --stdlayout --authors-file=/path/to/authors-file \ + --prefix=svn/ svn://anonscm.debian.org/pkg-cron + + +4) The resulting git repository was then post-processed with git-svn-abandon: + + $ git svn-abandon-fix-refs + $ git svn-abandon-cleanup + + At this point, all SVN branches had been converted to git branches, and all + SVN tags to git tags (annotated). + + +5) The resulting repository was slightly restructured. All update branches + were renamed to update/, + + $ for brname in woody slink potato lenny; do + git branch -m ${brname}_updates updates/$brname; + done + + and all other branches -- except sf3! -- were removed: + + $ git branch -D rDoS-patch sid-squeeze source-dist + + +6) The new upstream branch was created from commit e5a00cee3 ("Initial + revision"), which corresponds to SVN revision #2 -- that is, the commit + with which the original upstream source was initially added to the repository: + + $ git branch upstream e5a00cee31b43146f8e661fef0623a4203d00f9e + + Comparing the contents of the tree of that commit to the contents of + original source tarball currently in the archive (as there has only been one + upstream version, ever), one can easily verify that their contents are + identical. (This process is not shown here). + + +7) The pristine-tar delta was committed so that the original tarball could be + recreated from the upstream branch: + + $ pristine-tar commit /path/to/cron_3.0pl1.orig.tar.gz + + At this point, the pristine-tar/upstream/master branches commonly used by + git-buildpackage are properly set up. + + +==> At this point, the git repository became authoritative, and all further + commits to the subversion repository were ignored. + + + -- Christian Kastner Wed, 15 Jun 2011 15:39:26 +0200 + + + +diff --git a/git-svn-abandon-fix-refs b/git-svn-abandon-fix-refs +index c47405d..960b9a1 100755 +--- a/git-svn-abandon-fix-refs ++++ b/git-svn-abandon-fix-refs +@@ -27,13 +27,19 @@ git for-each-ref --format='%(refname)' refs/remotes/svn/tags/* | while read tag_ + target_ref="$tag_ref" + fi + ++ # Added for Debian cron: prettify tags ++ pretty_tag=`echo $tag | sed -re \ ++ 's,debian_version_3(_|\.)0pl1-([0-9]+),debian/3.0pl1-\2,; ++ s,upstream.*,upstream/3.0pl1,; ++ s,debian-sf3-3\.0pl1-([0-9]+),sf3/\1,'` ++ + # create an annotated tag based on the last commit in the tag, and delete the "branchy" ref for the tag + git show -s --pretty='format:%s%n%n%b' "$tag_ref" | \ + perl -ne 'next if /^git-svn-id:/; $s++, next if /^\s*r\d+\@.*:.*\|/; s/^ // if $s; print' | \ + env GIT_COMMITTER_NAME="$( git show -s --pretty='format:%an' "$tag_ref" )" \ + GIT_COMMITTER_EMAIL="$( git show -s --pretty='format:%ae' "$tag_ref" )" \ + GIT_COMMITTER_DATE="$( git show -s --pretty='format:%ad' "$tag_ref" )" \ +- git tag -a -F - "$tag" "$target_ref" ++ git tag -a -F - "$pretty_tag" "$target_ref" + + git update-ref -d "$tag_ref" + done --- cron-3.0pl1.orig/debian/changelog +++ cron-3.0pl1/debian/changelog @@ -0,0 +1,1693 @@ +cron (3.0pl1-124ubuntu2) raring; urgency=low + + * Restore libaudit-dev build-dep now that it has been promoted to main. + + -- Adam Conrad Fri, 08 Feb 2013 23:54:51 -0700 + +cron (3.0pl1-124ubuntu1) raring; urgency=low + + * Merge from Debian unstable. Remaining Changes: + - Drop build-dependency on libaudit, not in main and the security + team doesn't want it there. + - debian/control: + + Move MTA to Suggests field. + - debian/cron.upstart: Add Upstart script. + - debian/rules: Call dh_installinit to install Upstart job properly. + - d/cron.default: change to a deprecated message to make it clear + that the file is no longer in use. + * Dropped changes: + - debian/rules: add support for cross-compiling. (Applied in Debian) + - Requires debhelper >= 7.3.15ubuntu2 (for Upstart). (Debian req's >= 9) + + -- Clint Byrum Wed, 07 Nov 2012 00:03:51 -0800 + +cron (3.0pl1-124) unstable; urgency=medium + + [ Release targeted to Wheezy including fixes for critical/important bugs ] + * debian/{preinst,postinst,postrm}: remove obsolete /etc/cron.daily/standard + on upgrade. (Closes: #679062) + * debian/cron.init: Have the init.d file remove cron's pidfile if it + exists after killing the process. The pidfile should be removed + by cron itself (but it isn't) through its SIGhandler as done in later + versions in cron (4.1 and cronie). This fix is, however, simpler and less + intrusive. (Closes: 679106, 615855) + * debian/control: + - Reword last paragraph since cron does not provide any standard tasks + anymore. (Closes: 674192) + - Update to Standards-Version: 3.9.3 - which BTW includes an indication + on which are the valid cron filenames. + - Depend on debhelper >= 9 (lintian warning) + + -- Javier Fernández-Sanguino Peña Thu, 28 Jun 2012 12:08:09 +0200 + +cron (3.0pl1-123) unstable; urgency=low + + * debian/standard.daily: Apply patch from Steve Allison to prevent checking + two times the same filesystem. This prevents bogus warnings about missing + lost+found in schroot bind mounts (Closes: #660879) + * debian/rules: Apply patch from Steve Langasek to add cross compiler + support to cron (Closes: #666892) + * debian/cron.default, debian/standard.daily, debian/rules: Remove + the lost+found check as this is no longer required and causes + issues with some filesystems (e.g. bind mounts, locally mounted USB + drives). Additionally, in some filesystems the directory + will be generated when required. (Closes: #620164, #658743, #662605, + #660879, #579640, #473711) + + -- Javier Fernández-Sanguino Peña Sat, 23 Jun 2012 08:28:27 +0200 + +cron (3.0pl1-121ubuntu1) quantal; urgency=low + + * Merge from Debian unstable. Remaining changes: + - debian/rules: add support for cross-compiling. + - Drop build-dependency on libaudit, not in main and the security + team doesn't want it there. + - debian/control: + + Requires debhelper >= 7.3.15ubuntu2 (for Upstart). + + Move MTA to Suggests field. + - debian/cron.upstart: Add Upstart script. + - debian/rules: Call dh_installinit to install Upstart job properly. + * d/cron.default: change to a deprecated message to make it clear + that the file is no longer in use. (LP: #794082) + + -- Clint Byrum Thu, 14 Jun 2012 11:05:24 -0700 + +cron (3.0pl1-121) unstable; urgency=low + + * debian/control: + - Put the maintainer address in UTF-8 encoding + * debian/postinst: + - Only go ahead when configuring the package (Closes: #653304, #657073) + * debian/source/lintian-overrides: + - Removed, no longer required + * crontab.5: + - Escape the '%' in the examples so that they can work when copied over to + crontab (Closes: #642309) + - Remove tabs from manpage since they are not formatted properly in the + nonfill area + * cron.8, crontab.1, crontab.5: + - Fix formatting errors in manual pages with patch provided by David + Prévot (Closes: #643603) + * debian/cron.default: + - Fix typo (Closes: #643897) + * debian/compat: + - Set to compatibility version 9 to enable hardened build flags + (Closes: #653720) + + -- Javier Fernández-Sanguino Peña Mon, 19 Sep 2011 00:20:17 +0200 + +cron (3.0pl1-120ubuntu3) precise; urgency=low + + * debian/rules: add support for cross-compiling. + + -- Steve Langasek Mon, 02 Apr 2012 07:50:11 +0000 + +cron (3.0pl1-120ubuntu2) precise; urgency=low + + * Drop build-dependency on libaudit, not in main and the security team + doesn't want it there. LP: #878155. + + -- Steve Langasek Thu, 20 Oct 2011 07:57:06 -0700 + +cron (3.0pl1-120ubuntu1) precise; urgency=low + + [ Ubuntu Merge-o-Matic ] + * Merge from Debian unstable. Remaining changes: + - debian/control: + + Requires debhelper >= 7.3.15ubuntu2 (for Upstart). + + Move MTA to Suggests field. + - debian/cron.upstart: Add Upstart script. + - debian/rules: Call dh_installinit to install Upstart job properly. + * Dropped changes, superseded upstream: + - debian/control: lockfile-progs no longer recommended. + - debian/{prerm,postinst,postrm}: scripts now generated with debhelper. + + -- Steve Langasek Mon, 17 Oct 2011 15:33:56 -0700 + +cron (3.0pl1-120) unstable; urgency=low + + * do_command.c, cron.h, cron.8: + - Change the behaviour when logging the information of the child processes. + A new loglevel (8) is introduced and documented in cron.8. The previous + log format is kept unless the sysadmin choses to select this new option. + (Closes: #637295) + * debian/examples/cron-stats.pl: + - Adjust the script with the changes introduced in the logging behaviour. + * cron.8: Fix minor formatting errors + * debian/cron.bug-script: + - Report properly on the EDITOR variable. Thanks to Jan Braun for the + report. + - Also report the contents of /etc/cron.* dirs. This is helpful when + debugging syntax/mode/naming errors. Users that do not want to share this + information should edit/delete before reporting bugs. + * debian/changelog: Typo fixes and missing information + * debian/control: + - Update package description, removing the "backup of key system files" as + a feature. The last of these responsibilities were transffered in -117. + * crontab.5: + - Document limitations of the periods that can be defined in crontab syntax + and provide workarounds (Closes: #640086) + - Document limitation of the use of @reboot due to the definition of the + boot sequence as suggested by Regid Ichira. + * debian/source/lintian-overrides: + - Add overrides file for lintian required because the maintainer name in + debian/changelog and in debian/control do not match due to encoding. + [ Christian Kastner ] + * debian/control: + - Fix broken Vcs-* URLs + * debian/NEWS: + - Remove spurious NEWS entry (the entry for -110 was duplicated at the + bottom) + + -- Javier Fernández-Sanguino Peña Fri, 09 Sep 2011 23:25:35 +0200 + +cron (3.0pl1-119) unstable; urgency=low + + * The development team has migrated from Subversion to git, with the latter + becoming authoritative immediately. A log of the migration process can be + found in debian/svn-git-migration.txt. + + [ Javier Fernandez-Sanguino ] + * debian/NEWS: + - Remove the entry introduced in the previous version until an agreement is + reached on the desired behaviour (Reopens: #608774) + * debian/standard.daily: + - Work properly if mount points have a space (Closes: #633972) + - Sanity check mount point value before generating an error + * Add one new example script: + - cron-tasks-review.sh: Reviews the files that exist in /etc/cron.d + and in /etc/cron.{hourly,daily,weekly,monthly} to find those that + will not be run by cron due to some error. + * debian/copyright: + - Adjust debian/copyright to acknowledge the (c) and license of the new + example script. + * debian/control: + - Remove Steve from the Uploaders field, no longer needed there + * cron.8: + - Document the Debian specific changes in a separate section, describing + how /etc/cron.{hourly,daily,weekly,monthly} and /etc/cron.d behave + and should be used. + + [ Christian Kastner ] + * debian/control: + - Updated Vcs-* URLs to point to the new git repository + * debian/cron.c, database.c: + - Interpret the "-L" option of the daemon as a bitmask, and add initial + support for three selectors: start, stop, end + * database.c, cron.c, cron.h, debian/copyright: + - Check orphaned crontabs for adoption. Fix taken from Fedora cronie. + Closes: #634926 + * do_command.c: + - When logging a job run, prefix the command text with the PID of the + process. Closes: #609780 + * debian/NEWS: + - Added entry documenting the change of the "-L" option as well as the + dropping of LSBNAMES in -117. + + -- Javier Fernandez-Sanguino Pen~a Wed, 08 Jun 2011 20:44:49 +0200 + +cron (3.0pl1-118) unstable; urgency=low + + * debian/control + - Limited Build-Depends for libaudit-dev to linux-any. I erroneously + assumed it applied to other OSes as well. + * debian/rules + - DEB_BUILD_OPTIONS for audit support is limited to Linux platforms + + [ Javier Fernandez-Sanguino] + * debian/NEWS + - Document the new behaviour of cron regarding logging of non-zero + exit status cronjobs (Closes: #608874) + + -- Christian Kastner Tue, 31 May 2011 14:50:34 +0200 + +cron (3.0pl1-117) unstable; urgency=low + + * Makefile: + - Fixed integration of cron-internal debug code into the package building + process. Instead of having to modify debian/rules, this can now driven by + DEB_BUILD_OPTIONS=debug + - Removed hard-coded compiler flags, honoring those provided by + dpkg-buildflags instead + * do_command.c: + - When logging the end of cron jobs, log the PID of the actually exec'ed + user command, not the PID of the parent. Closes: #609780 + * database.c: + - Split crontab security checks (type, owner, mode, links) into separate + tests with individual error messages instead of the cryptic message + "WRONG INODE INFO". Closes: #625493 + - Extended the ability to recover from broken symlinks (added in -110) to + also recover from failed security checks above. Fixes to these were not + being detected as cron only looks at mtime. Closes: #625495 + - Also recover from syntax errors. Fixes to these did change mtime, but + were ignored as cron permanently removes broken crontabs from its + database of files to check. Closes: #627859 + * cron.8: + - Documented the fact that /etc/crontab and files in /etc/cron.d must not + be group- or other-writable. Closes: #625491, LP: #741979 + * crontab.5: + - Specify parse order of crontabs (thanks, Jonathan Amery). Closes: #622645 + * debian/control: + - Bumped Standards-Version to 3.9.2 (no changes needed) + - Added missing Build-Depends on libaudit-dev + - Removed Conflicts for nonexistent package suidmanager + - Removed Conflicts for lockfile-progs (which should have been a Breaks + anyway) as we no longer rely on it, we use flock from util-linux instead + - Changed architecture-specific Build-Depends on libselinux1-dev to + linux-any instead of negating all other OSes + * debian/cron.default: + - Merged the LSBNAMES option with EXTRA_OPTS. Both where used to pass + options to the daemon, making one of them redundant + - Added the ability to disable the daily lost+found check by setting the + new variable CHECK_LOSTFOUND to "no". Closes: #579640 + * debian/cron.init: + - Don't call start-stop-daemon directly, use LSB init functions instead. + Among other things, this works around the cron daemon not deleting its + pidfile from /var/run/. Closes: #615855, 622348 + - Added $network to Should-Start + * debian/rules: + - Converted to dh syntax. This included replacing some manually executed + steps with debhelper file-automated ones (eg: cron.man, cron.examples) + to lessen clutter + - Completely rewrote the PAM/SELinux/audit integration parts. PAM support + was being skipped on kfreebsd and hurd, and audit support was completely + broken. From now on: + + PAM is enabled by default on all platforms + + SELinux is enabled by default on linux-any + + audit is disabled by default on all platforms + All of these can be driven by DEB_BUILD_OPTIONS (see debian/rules) + - Do not compress example perl scripts + * debian/{prerm,postrm,postinst}: + - Let dh_installinit manage stopping/(re)starting of jobs instead of + calling invoke-rc.d ourselves + * debian/standard.daily: + - Backup of /etc/{passd,group} is no longer performed by cron; the task + was handed over to src:shadow (see #554170). In Squeeze, this task will + be performed redundantly by both packages (as discussed on + debian-release) (Closes: 333837) + - Rewrite locking and lost+found-checking code, based on a submission to + the BTS by Justin B. Rye. Closes: #478967, #537073, LP: #257367 + - Parse /etc/mtab instead of /proc/mounts; the former makes handling bind + mounts easier. Closes: #578856 + - Pull in /etc/cron/default for CHECK_LOSTFOUND (see above) + * debian/copyright: + - Convert to DEP5 format (r173) + * debian/watch: + - Added watch file to silence a lintian warning, even though the last + upstream release was in 2004 + * Packaging (general): removed a lot of cruft which seems to have accumulated + over time. This included all the cron-se stuff, which appears to have been + a remnant from the SELinux integration process, and was either not being + used anymore or completely broken. Also, the patches/ directory was + removed, as that location has a specific meaning and the patches in there + were no longer relevant. + + -- Christian Kastner Sun, 08 May 2011 01:21:15 +0200 + +cron (3.0pl1-116ubuntu3) oneiric; urgency=low + + * Cherry-pick fix from Debian: database.c, cron.c, cron.h, + debian/copyright: + - Check orphaned crontabs for adoption. Fix taken from Fedora cronie. + Closes: #634926, LP: #27520. + + -- Steve Langasek Mon, 19 Sep 2011 10:21:01 -0700 + +cron (3.0pl1-116ubuntu2) oneiric; urgency=low + + * No-change rebuild with current debhelper, to fix maintainer script + which keeps trying to start cron in my chroots due to bypassing + invoke-rc.d + + -- Steve Langasek Mon, 15 Aug 2011 16:30:24 -0700 + +cron (3.0pl1-116ubuntu1) natty; urgency=low + + * Merge from debian unstable (LP: #696953), remaining changes: + - debian/control: + + Requires debhelper >= 7.3.15ubuntu2 (for Upstart). + + Move MTA,lockfile-progs to Suggests field. + - debian/cron.upstart: Add Upstart script. + - debian/{prerm,postinst,postrm}: + + Don't call update-rc.d,invoke-rc.d and + /etc/init.d/cron. + - debian/rules: Call dh_installinit to install Upstart job properly. + + -- Lorenzo De Liso Mon, 03 Jan 2011 20:32:01 +0100 + +cron (3.0pl1-116) unstable; urgency=high + + * Upload with approval from Release Team to get RC bug fixes in Squeeze + (see http://lists.debian.org/debian-release/2010/12/msg00719.html) + * do_command.c, popen.c: + - Use fork() instead of vfork(). + * do_command.c: + - Close an unused stream in the fork()ed child prior to exec'ing the + user's command, thereby avoiding an fd leak. Closes: #604181, LP: #665912 + Previous to this, and in conjunction with LVM, the fd leak may have the + effect of the user being spammed by warnings every time a cron job was + executed. + * crontab.5: + - Fixed the example demonstrating how to run a job on a certain weekday of + the month (date range was off-by-one). Also, the same example contained + a superfluous escape, resulting in wrong output. Closes: #606325 + * cron.init: + - Added $named to Should-Start, in case @reboot jobs need DNS resolution. + Closes: #602903 + - Added nslcd to Should-Start. LP: #27520 + + -- Christian Kastner Wed, 10 Nov 2010 21:13:19 +0100 + +cron (3.0pl1-115ubuntu1) natty; urgency=low + + * Merge from Debian testing, Ubuntu remaining changes: + - debian/control: + + Requires debhelper >= 7.3.15ubuntu2 (for Upstart). + + Move MTA,lockfile-progs to Suggests field. + - debian/cron.upstart: Add Upstart script. + - debian/{prerm,postinst,postrm}: + + Don't call update-rc.d,invoke-rc.d and + /etc/init.d/cron. + - debian/rules: + + Call dh_installinit to install Upstart job properly. + + -- Alessio Treglia Sat, 27 Nov 2010 10:46:19 +0100 + +cron (3.0pl1-115) unstable; urgency=high + + * cron.c: set LC_COLLATE to 'C' in order to properly work when locales + are set that change the collation order expected by the regular + expressions used in load_database(). By setting locale we were + skipping some crontabs unexpectedly due to [a-z] not matching all the ascii + characters in some languages (such as Estonian). (Closes: #600310) + Thanks to Michael Tokarev for spotting this issue and for + Bastian Blank for pointing for the fix. + * debian/cron.init: + - Fix typo that prevented it from properly loading LC_CTYPE (Closes: + #600082) + - Force LC_COLLATE to 'C' regardless of user locale settings. + + -- Javier Fernandez-Sanguino Pen~a Sat, 16 Oct 2010 13:05:02 +0200 + +cron (3.0pl1-114ubuntu1) maverick; urgency=low + + * Merge from debian unstable for documentation updates and reading of + /etc/default/locale. Remaining changes: + - debian/control: + + Build-Depends on debhelper >= 7.3.15ubuntu2, for Upstart + + Drop MTA and lockfile-args to Suggests + - add debian/cron.upstart + - debian/postinst: remove calls to update-rc.d, invoke-rc.d and + /etc/init.d/cron + - debian/postrm: remove call to update-rc.d + - debian/prerm: remove calls to invoke-rc.d and /etc/init.d/cron + - debian/rules: install Upstart job + + -- Jamie Strandboge Tue, 24 Aug 2010 14:56:42 -0500 + +cron (3.0pl1-114) unstable; urgency=low + + * Cleanup of the package in preparation of the next Debian release. + - Notice that only one change introduces new behaviour (debian/cron.pam), + the other changes are just documentation fixes or cleanups to the + packaging. + * debian/cron.pam: + - In addition to the reading /etc/security/pam_env.conf and + /etc/environment (obsolete config file) previous default, + cron will now also read /etc/default/locale in order to setup + the proper localisation environment for tasks. + [ Documentation fixes ] + * debian/cron.{default,init}, cron.8, crontab.5: + - Updated documentation (comments in scripts and manpages) regarding the + reading and setting of locale information for the cron daemon and for + cron tasks to document the new behaviour due to the above change. + * debian/{preinst,postinst,postrm}: + - Since /etc/cron.monthly/standard does not provide any worthwhile task + anymore, remove obsolete /etc/cron.monthly/standard after upgrading + (local changes are preserved, however). Closes: #585680 + * crontab.1 + - Minor typo fixes to the manpage. + * crontab.5: + - Added an example submitted by jidanni (thanks!), showing how to run a + command on the second Saturday of the month. Closes: #584514 + - Minor fixes + [ Fixes to the packaging ] + * debian/control: + - Bumped Standards-Version to 3.9.1 (no changes needed) + - Added Pre-Depends for dpkg (>= 1.15.7.2) for a dpkg-maintscript-helper + with support for safely removing conffiles + - Added Homepage: field + - Added Vcs-* fields + - Upgraded debhelper dependency version for dh_bugscripts + * debian/rules: + - Replace deprecated dh_clean -k with dh_prep + * debian/NEWS: + - Apply Steve Langasek's patch to remove non-newsworthy content, avoiding + unnecessary output from apt-listchanges during upgrade. Closes: #591005 + * debian/source/format: + - Make source format explicit (1.0) + + -- Christian Kastner Sun, 18 Jul 2010 22:51:21 +0200 + +cron (3.0pl1-113ubuntu1) maverick; urgency=low + + * Merge from debian unstable. Fixes: + - LP: #46493 (this should have been fixed way back in 3.0pl1-87, and I + confirmed it is no longer a problem) + - LP: #118168 (Debian #79037) + - LP: #151231 (Debian #155109, #443615) + - LP: #308341 (Debian #437180) + * Remaining changes: + - debian/control: + + Build-Depends on debhelper >= 7.3.15ubuntu2, for Upstart + + Drop MTA and lockfile-args to Suggests + - add debian/cron.upstart + - debian/postinst: remove calls to update-rc.d, invoke-rc.d and + /etc/init.d/cron + - debian/postrm: remove call to update-rc.d + - debian/prerm: remove calls to invoke-rc.d and /etc/init.d/cron + - debian/rules: install Upstart job + * Drop the following changes, now in debian: + - popen.c: check return code of initgroups() in cron_popen() + - debian/control: add missing ${misc:Depends} + - debian/control: Depends bump on lsb to >= 3.2.12ubuntu2. No longer + required now that we use Upstart + - debian/cron.pam: switch from including common-session to including + the new common-session-noninteractive + - pathnames.h: use sensible-editor + + -- Jamie Strandboge Fri, 25 Jun 2010 16:08:12 -0500 + +cron (3.0pl1-113) unstable; urgency=medium + + [ Christian Kastner / Javier Fernandez-Sanguino ] + * debian/postinst: + - Now that permissions and ownership of crontabs are changed unconditionally, + do not attempt to chown user crontabs if none are present. Closes: #585636 + - Only change permissions if the crontabs directory exist + + -- Christian Kastner Sat, 12 Jun 2010 18:25:59 +0200 + +cron (3.0pl1-112) unstable; urgency=low + + [ Christian Kastner ] + * do_command.c: + - Don't send mail when a job exits non-zero, only send mail if the job sent + output to stderr. This behaviour was introduced erroneously; while it + does have merit, it is completely against standard cron behaviour. + Closes: #581612 + * debian/compat: + - Bumped debhelper compatibility to 7 + * debian/control: + - Bumped Standards-Version to 3.8.4 (no change needed) + - Build-Depend on debhelper (>= 7.0.50~) + - Added dependency on ${misc:Depends} to package cron + * debian/cron.init: + - Changed Default-Stop from (1) to (empty). rc0 and rc6 were removed in + 3.0pl1-101 because the stop action -- sending SIGTERM/SIGKILL to cron + on shutdown/reboot -- was redundant. This, however, also applies to + rc1, because killprocs will do that for us. + * debian/postinst: + - Removed obsolete dpkg file backup code, this has been handed over to dpkg + in 3.0pl1-109 + - Removed last remaining stop action (for rc1) from upate-rc.d (see above) + - Add dpkg-statoverride for /usr/bin/crontab, and unconditionally change + permissions of /var/spool/cron/crontabs. Closes: #304036, #460095 + * debian/standard.monthly: + - Removed because it had been empty for years and therefore served no + purpose + * debian/cron.bug-{control,script} + - Added to extend information submitted by reportbug + * debian/rules: + - Applied changes for standard.monthly and cron.bug-{control,script} above + * debian/copyright: + - Updated to reflect recent contributions + * debian/README: + - Updated ancient information with the current status (PAM support, SELinux + support, etc.) + * debian/TODO: + - Added new entry regarding clarification of the purpose behind /etc/cron.d + * debian/REFS: + - Summary of links to relevant development threads; re-added for + informational purposes. + [ Javier Fernandez-Sanguino ] + * debian/cron.init: + - Fix typo (Closes: #585399) + + -- Christian Kastner Thu, 10 Jun 2010 15:21:39 +0200 + +cron (3.0pl1-111) unstable; urgency=low + + [ Christian Kastner ] + * debian/cron.init: + - When both /etc/environment and /etc/default/locale contain locale + information, warn that variables from /etc/environment will be ignored. + Closes: #543895, #580942 + - Add autofs and NIS+ to Should-Starts, because especially NIS+ must be run + before cron, if present. Closes: #512757 + - init script should be stopped in runlevel 0 and 6, too + [ Javier Fernandez-Sanguino ] + * do_command.c: Revert change so that mails are not sent if MAILTO + is null. Closes: #580938 + + -- Javier Fernandez-Sanguino Pen~a Sun, 16 May 2010 15:16:24 +0200 + +cron (3.0pl1-110) unstable; urgency=medium + [ Christian Kastner ] + * user.c + - Fix broken parsing and handling of crontabs with no-newline-before-EOF + - Refuse to install such crontabs via crontab(1). Closes: #79037 + - Log crontabs missing newline before EOF to syslog. This is only relevant + for crontabs not installed via crontab(1), ie. /etc/cron.d/* and + /etc/crontab. Closes: #76625 + - Add a log message explicitly stating that all jobs of crontabs with + syntax errors will not be run (ie, all-or-nothing). See #555954 + * crontab.c + - Tell the user which chars are expected on retry, too + * cron.c + - Don't let the daemon die when directories disappear, eg. when moving + directories around. Patch provided by Justin Pryzby (thanks!). + Closes: #470564 + * database.c + - Recover from broken symlinks in /etc/cron.d/ (Closes: #433609) + - Don't report wrong file owner in /etc/cron.d/ as wrong symlink owner + - Check the permissions of symlink targets (writeable only by root) + * do_command.c + - Don't let long-running commands time out certain MTAs. Patch provided by + Justin Pryzby, based on an analysis by Alexis Huxley (thanks!). Debian's + default MTA, exim, does not time out, so it shouldn't have been + affected. Closes: #155109, #443615 + - Bypass mail generation/sending code when no MTA is installed. + Previously, jobs with a lot of output would die once the pipe's buffer + was filled. Closes: #577133 + * popen.c + - Check return value of setgid() call, also associated with the + CVE-2006-2607 fix + - Move signal handling away from ancient API. Patch provided by Justin + Pryzby (thanks!). + * misc.c + - On startup, when recreating missing directories, do so with permissions + reflecting Debian's SGID crontab vs upstream's SUID + * entry.c + - Explicitly check for valid ranges in range values instead of upstream's + broken approach which misses certain combinations of ranges and steps. + Closes: #533726 + * env.c + - Backport environment variable parser from 4.1. The 3.0 parser has a + number of issues, especially with otherwise trivial matters such as + whitespace. Closes: #437180 + * cron.8 + - Document symlink behaviour. + * crontab.1 + - Document the new EOF handling described above. + * crontab.5 + - Clarify parsing of empty variables (Closes: #497742) + * debian/TODO + - Remove obsolete entries; add a table of planned milestones + * debian/NEWS + - Briefly describe the most important changes since lenny's release + * debian/control + - Add Christian Kastner to the Maintainers list (Closes: #565143) + + [ Javier Fernandez-Sanguino Pen~a ] + * debian/rules: Install the cron.default file properly + * debian/standard.daily: + - Do not indicate that lost+found does not exist if the filesystem is XFS + (Closes: #577508 577536) + + -- Christian Kastner Thu, 29 Apr 2010 16:23:38 +0200 + +cron (3.0pl1-109) unstable; urgency=medium + + * cron.c: + - Apply patch derived from OpenBSD's cron by Petya Kohts to + handle properly DST and avoid running cron jobs twice, or + skip cron jobs when there is a savings time change. + Closes: #217836, #458123, #474157, LP: #36690 + * crontab.c: + - Revert the behaviour that prevented 'alias crontab="crontab -i"' from + working. Now the -i flag is allowed regardless of other command line + switches. (Closes: #513379) + - Be more verbose in the example provided for first-time users than run + 'crontab -e' + - Check if the crontab exists before attempting its removal and before + prompting the user (whe using -i) + - When prompting, tell the user which chars are expected + * crontab.1: + - Change usage to match crontab's output + - Clarify use of the cron.allow and cron.deny files (Closes: #511782) + * crontab.5: + - Describe a known limitation related to the lack of the cron daemon's + per-user timezone support (Closes: #497741, 353246, 166533) + - Describe the use of tilde (~) as a substitute for $HOME (Closes: 477198) + - Describe the lack of replacements of variables (Closes: #493636) + * popen.c: Check return call of initgroups(), associated with + CVE-2006-2607 fix. Thanks to Christian Kastner for the heads up. + * cron.8: + - Describe how the environment is managed in Debian + * debian/cron.init: Also use /etc/timezone to setup the timezone environment + (TZ) if not set. + * debian/standard.daily: + - Do not make a backup copy of dpkg files anymore since dpkg (1.15.4) does + this already (when #541412 was fixed) (Closes: #541415) + - Fix a bug in lost+found reporting. Files in lost+found were never + + actually reported. [ Patch from Teemu Kiviniemi ] + - Added ext4 for lost+found searches. [ Patch from Teemu Kiviniemi ] + Closes LP: #367383 + * debian/control: + - Add a Breaks dpkg (<< 1.15.4) to ensure that one always has the backup + functionality either via cron or via dpkg. + - Provides: cron-daemon, which was reinstated in the virtual package + list in November 2009 (Closes: #349170) + + -- Javier Fernandez-Sanguino Pen~a Sat, 10 Apr 2010 12:48:42 +0200 + +cron (3.0pl1-108) unstable; urgency=high + + * Add a Depends: on libpam-runtime (>= 1.0.1-11) since this + is required for the use of common-session-noninteractive + (Closes: 575342) + + -- Javier Fernandez-Sanguino Pen~a Fri, 26 Mar 2010 00:17:02 +0100 + +cron (3.0pl1-107) unstable; urgency=low + + * debian/cron.pam: Use common-session-noninteractive in pam.d configuration + instead of common-session so that libpam-ssh is not run. If libpam-ssh was + installed the previous configuration resulted in cron spawning + many ssh-agent processes (Closes: #572292) + * debian/cron.init: Add Should-Start/Stop dependency to slapd so that cron + * can start up tasks for users define in an LDAP (Closes: #557659, #546367) + * Fix the use of perror so that the error message is correct when + printing the reason why an operation failed. Thanks to + Justin Pryzby for the patch. (Closes: 470587) + * pathnames.h: Use /usr/bin/sensible-editor instead of /usr/bin/editor when + calling crontab -e (Closes: #482284) + * Fix debian/control description, thanks to Justin B Rye for the patch + (Closes: #535227) + * crontab.c: Produce a different warning message when root tries to setup a + crontab for a user that is not allowed to do so, and do not log the + action (Closes: #505288) + * Only allow -i if -r has been specified previously (Closes: #513379) + * Update debian/README.Debian to indicate that the package is + being maintained at Alioth, thanks Tomas Pospisek for the patch + (Closes: #511740) + * crontab.c: Update the usage() call so that it refers to the use + of -i when removing (-r) + * crontab.1: Update the manpage: + - Use the same option listing as in the crontab usage() call + - Remove 'tweaked', explain what -u does in each context. + - Remove V3, use 'Vixie Cron' instead + (Closes: #572249) + - Update the manpage years (Closes: #563296) + - Describe the usage of the crontab group wrt /var/spool/cron/crontabs + (Closes: #539182) + * Use debhelper compatibility version 5 + * Include cross build support with patch to debian/rules provided by Neil + Williams (Closes: #465077) + * Change maintainer's email address + + -- Javier Fernandez-Sanguino Pen~a Tue, 16 Mar 2010 22:55:26 +0100 + +cron (3.0pl1-106ubuntu5) lucid; urgency=low + + * SECURITY UPDATE: check return code of initgroups() in cron_popen(). + Related to fix for CVE-2006-2607. Thanks to Javier Fernandez-Sanguino + Pen~a from Debian for pointing this out. This can be dropped in + 3.0pl1-109. + - http://svn.debian.org/wsvn/pkg-cron/trunk/popen.c?op=diff&rev=0&sc=0 + + -- Jamie Strandboge Wed, 14 Apr 2010 09:10:42 -0500 + +cron (3.0pl1-106ubuntu4) lucid; urgency=low + + * rebuild rest of main for armel armv7/thumb2 optimization; + UbuntuSpec:mobile-lucid-arm-gcc-v7-thumb2 + + -- Alexander Sack Fri, 05 Mar 2010 04:06:36 +0100 + +cron (3.0pl1-106ubuntu3) karmic; urgency=low + + FFE LP: #427356. + + * Replace init script with Upstart job. + * debian/control: + - Add missing ${misc:Depends} + - Bump build-dependency on debhelper for Upstart-aware dh_installinit + + -- Scott James Remnant Tue, 15 Sep 2009 03:32:59 +0100 + +cron (3.0pl1-106ubuntu2) karmic; urgency=low + + * debian/cron.pam: switch from including common-session to including + the new common-session-noninteractive. + * debian/control: depend on libpam-runtime (>= 1.0.1-11) for the above. + + -- Steve Langasek Mon, 24 Aug 2009 03:19:49 +0000 + +cron (3.0pl1-106ubuntu1) karmic; urgency=low + + * Merge from debian unstable, remaining changes: + - debian/control: Depend on lsb-base >= 3.2-12ubuntu4 + - debian/control: Drop MTA and lockfile-args to Suggests + - pathnames.h: use sensible-editor + * New Debian release fixes LP: #46649 + + -- Jamie Strandboge Thu, 14 May 2009 09:53:08 -0500 + +cron (3.0pl1-106) unstable; urgency=high + + * SECURITY UPDATE: cron does not check the return code of setgid() and + initgroups(), which under certain circumstances could cause + applications to run with elevated group privileges. Note that the more + serious issue of not checking the return code of setuid() was fixed already + in 3.0pl1-64. (Closes: #528434) + - do_command.c: check return code of setgid() and initgroups() + - This fixes (hopefully completely) CVE-2006-2607 + * crontab.c: + - close the temporary file after it is edited and + before calling cleanup_tmp_crontab() to behave properly on NFS + mounted / (Closes: #413962) + - if crontab is run without argument then it will read stdin to replace + the users crontab. This way it is POSIXLY_CORRECT. More information at + http://www.opengroup.org/onlinepubs/9699919799/utilities/crontab.html + (Closes: #514062) + * crontab.5 : + - Add details about multiple recipients in MAILTO (LP: #235464) + (Closes: #502650) + - Indicate that it also reads environment from /etc/environment + - Substitute ATT for AT&T (Closes: #405474) + * Proper fix for PAM configuration to make cron read the system + environment (Closes: #511684) + * debian/cron.init: + - Add support for 'status' in the init.d (Closes: #514721) + - Use 'cron' instead of 'crond' (Closes: #497699) + * Change lockfile-progs from Suggests: to Recommends: and remove wording + related to dselect, which is no longer relevant (Closes: #452460, #468262) + * Add justification of checksecurity being in the Suggests: line + * Change the (outdated) wording of the description based on an example + provided by Justin B Rye (Closes: 485452) + * Change the postinst so that update-rc.d is only run if /etc/init.d/cron is + executable (Closes: #500610) + + -- Javier Fernandez-Sanguino Pen~a Wed, 13 May 2009 01:05:41 +0200 + +cron (3.0pl1-105ubuntu1) jaunty; urgency=low + + * Merge from debian unstable, remaining changes: + - crontab.5 : Add details about multiple recipients in MAILTO + - pathnames.h: use sensible-editor + - debian/control: Depend on lsb-base >= 3.2-12ubuntu3. + - Drop MTA to Suggests + - debian/cron.init: Add the 'status' action + + -- Michael Vogt Wed, 12 Nov 2008 16:41:26 +0100 + +cron (3.0pl1-105) unstable; urgency=low + + * Non-maintainer upload with permission. + * Fixes an uninitialised data problem in the SE Linux code, also fixes a + similar compile warning in code which is not related to SE Linux which + isn't a bug (to avoid confusion). + Closes: #472938 + * Updates the SE Linux patch to work with the new code base, the previous + version was almost totally unusable with Lenny SE Linux. + Closes: #499518 + * Fixes inadequate security checks in the SE Linux code path, the previous + code might be vulnerable to exploit at some future time. + Closes: #499584 + + -- Russell Coker Sun, 28 Sep 2008 19:23:50 +1000 + +cron (3.0pl1-104+ubuntu5) intrepid; urgency=low + + * crontab.5 : Add details about multiple recipients in MAILTO (LP: #235464) + + -- Jean-Baptiste Lallement Wed, 30 Jul 2008 23:41:42 +0200 + +cron (3.0pl1-104+ubuntu4) intrepid; urgency=low + + * debian/control: Depend on lsb-base >= 3.2-12ubuntu3. + + -- Chuck Short Thu, 10 Jul 2008 14:57:12 +0000 + +cron (3.0pl1-104+ubuntu3) intrepid; urgency=low + + * debian/control: Depend on lsb >= 3.2.12ubuntu2, which has + the status_of_proc() function. + * debian/cron.init: Add the 'status' action (LP: #201504), + thanks to Dustin Kirkland. + + -- Chuck Short Thu, 10 Jul 2008 14:47:09 +0000 + +cron (3.0pl1-104+ubuntu2) intrepid; urgency=low + + * Drop MTA to Suggests for Ubuntu for the moment, now that apt installs + Recommends by default. + + -- Colin Watson Tue, 08 Jul 2008 10:37:59 +0100 + +cron (3.0pl1-104+ubuntu1) intrepid; urgency=low + + * pathnames.h: Use the Debian sensible-editor (LP: #222830). + * Modify Maintainer value to match the DebianMaintainerField + specification. + + -- Dustin Kirkland Mon, 09 Jun 2008 11:42:25 -0500 + +cron (3.0pl1-104+build1) intrepid; urgency=low + + * Fake sync with Debian. The previous merge wasn't necessary, Debian adopted + our remaining change (do not call init script stop for runlevels 0 and 6). + + -- Martin Pitt Thu, 05 Jun 2008 09:05:55 +0200 + +cron (3.0pl1-104) unstable; urgency=low + + * Discard errors from df in the standard daily cron task to prevent errors + in user's systems that do not have ext2, ext3 or xfs filesystems + (Closes: #462472, #394646, #463656, #463713, #464749, #504146) + + -- Javier Fernandez-Sanguino Pen~a Fri, 14 Mar 2008 01:03:03 +0100 + +cron (3.0pl1-103) unstable; urgency=low + + * Fix bug introduced in the init.d file with the changes in the previous + version that makes the init.d not work properly when stopping cron due + to the improper definition of PIDFILE (Closes: #460869, #460886) + + -- Javier Fernandez-Sanguino Pen~a Tue, 15 Jan 2008 19:50:37 +0100 + +cron (3.0pl1-102) unstable; urgency=low + + * Modify pathnames.h so that GNU/KFreeBSD is detected and /var/run is + used instead of /etc/ to store the PID file (Closes: #460514) + * Modify the init.d script so that it uses /etc/cron.pid as its pidfile + if it exists, this will prevent the init.d script from failing if + cron is compiled in a system which does not get to define _PATH_RUN + as /var/run + * Fix typo in changelog (Closes: 459952) + + -- Javier Fernandez-Sanguino Pen~a Sun, 13 Jan 2008 23:08:11 +0100 + +cron (3.0pl1-101) unstable; urgency=low + + * Init.d changes: + - Add LSB dependencies on mounted /usr (Closes: #459489) + - Remove 0 and 6 from Default-Stop LSB definition in the init script + as sendsigs take care of stopping cron" debian/cron.init. Also + change the postinst call to update-rc.d so no stop symlinks + are added in those runlevels (Closes: #422785) + [ Notice that the existing symlinks are not removed when upgrading + it is up to the administrator to remove this himself so as to not + break local changes ] + - Parse system's locale to set the charset used to email cron tasks. This + will affect tasks that expect a 'C' locale (for example to parse + messages) and are running in systems with a non-C locale. + (Closes: #410057, #405335, #309150) + - Add support for /etc/default/cron and make it possible to add additional + options and control if environment is parsed. (Closes: #396928, #471506) + * cron.c: + - Do not use ANSI_x3.4-1968 but US-ASCII instead since it is the preferred + MIME name as per http://www.iana.org/assignments/character-sets" + (Closes: #415302) + * crontab.c: + - Add an option to ask for confirmation when a crontab is to be removed, + based on Fedora's patch for this same request. Users wanting this + behaviour have to alias cron (just as rm is aliased to 'rm -i') + (Closes: #117758, #289636, #324103, #414048) + - Print the location of CRONDIR when printing files on the crontab dir so + that the error messages have the absolute location of the files they + refer to. (Closes: #391350) + * Lintian fixes: + - Modify debian/copyright to point to the location of the Artistic + License. + + -- Javier Fernandez-Sanguino Pen~a Tue, 08 Jan 2008 22:13:35 +0100 + +cron (3.0pl1-100) unstable; urgency=low + + [ Last upload for etch fixing documentation in this package ] + * Describe the -L option in cron's manpage. + * Fix minor typos, and change Debian-specific content of manpages and + default crontab that could lead to user's being confounded. Explicitly say + that both /etc/crontab and files under /etc/cron.d/ can have users in the crontab + lines. (Closes: #402934) + + -- Javier Fernandez-Sanguino Pen~a Wed, 20 Dec 2006 00:53:06 +0100 + +cron (3.0pl1-99) unstable; urgency=low + + * Fix the test for anacron in /etc/crontab that would make cron run the + run-parts regardless of whether anacron was installed or not, thanks to + Paul Kimoto for spotting the bug introduced in the fix to #383857 + (Closes: #392600) + * Fix lintian error by adding a comment line in the init.d script + + -- Javier Fernandez-Sanguino Pen~a Thu, 12 Oct 2006 20:49:50 +0200 + +cron (3.0pl1-98) unstable; urgency=low + + * Fix LSB-formatted init.d meta info, thanks to Petter Reinholdtsen + (Closes: #386709) + * Make crontab tasks run with CWD=/, to make SELinux happy, thanks to + Erich Shubert (Closes: #383857) + * Use latest lsb logging functions, thanks to David Hardeman + (Closes: #384858, #389893) + + -- Javier Fernandez-Sanguino Pen~a Thu, 5 Oct 2006 21:03:19 +0200 + +cron (3.0pl1-97) unstable; urgency=high + + * Fix the behaviour of crontab so that in systems that do *not* have + a cron.allow file users will be allowed to use crontab, change + the behaviour of the previous version because it was contrary + to the manpage (now a user that is in *both* cron.deny and + cron.allow will be allowed access) (Closes: #383741) + + -- Javier Fernandez-Sanguino Pen~a Sat, 19 Aug 2006 10:36:48 +0200 + +cron (3.0pl1-96) unstable; urgency=low + + * Fix crontab.5 typo (changed quotes ' to ") that prevent a line from being + presented (Closes: #379230) + * Small typo fix in cron.8 + * Introduce Steve Grubb's patch to emit audit log message on crontab denial + based on the patch from Fedora, and adapted to Debian's cron version. The + main difference is that audit logs are generated if cron is compiled to + 'ALLOW_ONLY_ROOT'. This patch is currently disabled + since LSPP's audit library is not yet available. + * Change misc.c so that a user that is *both* in the cron.deny and + cron.allow files will be denied access (previously he would be permitted + access) + * Add two example scripts: + - stats-cron.pl: A script written by myself that can be used to audit + the behaviour of cron and benefits from the new cron logging (-L 2) + option to log the *end* of a cronjob. + - crontab2english.pl: A script written by Sean M. Burke that translates + the crontab notation to natural language. + * Adjust debian/copyright to acknowledge the (c) and license of the above + scripts. + + -- Javier Fernandez-Sanguino Pen~a Wed, 9 Aug 2006 01:07:40 +0200 + +cron (3.0pl1-95) unstable; urgency=low + + * Handle errors when reading crontabs so that it stops reading entries in + a crontab file after a syntax error. This prevents a rogue user from + dumping binary files in /etc/cron.d/ or /var/spool/cron/crontabs/ + and have them executed by cron (but binary files under + /etc/cron.{daily,weekly,monthly} will still be processed as they are + handled by run-parts). Thanks to Faidon Liambotis for the patch. + (Closes: #378153) + * Reuse vixie-cron-4.1-_51-bz180145-mail_i18n.patch from Fedora Core in + order to setup a proper Content-Type when sending e-mails. The + Content-Type will be set to the default system locale or cron's + environment (if it modifies it). If the locale is undefined it will + default to US ASCII (Closes: #338051) + - There is no need to define a CHARSET variable now with the above + patch (Closes: #309150) + * Support kfreebsd-amd64 by modifying the Build-Depends for SElinux + (Closes: #361458) + * Fix typos in the crontab.1 manpage spotted by Jonas Koelker + (Closes: #369153) + * Add option to control logging (-L) with patch provided by Steve Fosdick. + This makes it possible to log cron job finishing if you use -L 2 + (Closes: #271747, #318247) + * Change Steve's email in crontab.c error message to + cron@packages.debian.org + + -- Javier Fernandez-Sanguino Pen~a Wed, 19 Jul 2006 21:31:13 +0200 + +cron (3.0pl1-94) unstable; urgency=low + + * Lsb-base is now priority 'required' (3.0-16) instead of 'extra' + (3.0-15) so I'm moving the Suggests: up to a Depends: (Closes #360236) + + -- Javier Fernandez-Sanguino Pen~a Sat, 1 Apr 2006 03:41:55 +0200 + +cron (3.0pl1-93) unstable; urgency=low + + * Include patch from Ubuntu that incorporates LSB functions to the init. + This should help have Ubuntu synchronise their changes easier. + * Fix FTBFS on GNU/kFreeBSD with patch from Aurelien Jarno (Closes: #338246) + * Add brackets to the initscript for bash completion (Closes: #350605) + + + -- Javier Fernandez-Sanguino Pen~a Mon, 27 Feb 2006 22:39:24 +0100 + +cron (3.0pl1-92) unstable; urgency=low + + * Don't use SElinux on GNU/kFreeBSD, as it is Linux-specific, patch + from Aurelien Jarno (Closes: #325404) + * Added LSB formatted dependency info in init.d script (Closes: #335305) + + -- Javier Fernandez-Sanguino Pen~a Fri, 14 Oct 2005 14:29:40 +0200 + +cron (3.0pl1-91) unstable; urgency=low + + * Proper fix for #324017, the previous fix was dumb. This one + includes fixes from Fedora which should prevent segfaults under + other circumstances. + + -- Javier Fernandez-Sanguino Pen~a Sun, 21 Aug 2005 03:14:01 +0200 + +cron (3.0pl1-90) unstable; urgency=low + + * Fix SIGSEV due to a NULL free in the libselinux code, as a consequence + cron dies before cronjobs are started (Closes: #324017) + + -- Javier Fernandez-Sanguino Pen~a Sun, 21 Aug 2005 02:25:10 +0200 + +cron (3.0pl1-89) unstable; urgency=low + + * Fix bashishm in cron.daily script (Closes: #323983) + + -- Javier Fernandez-Sanguino Pen~a Sun, 21 Aug 2005 00:12:26 +0200 + +cron (3.0pl1-88) unstable; urgency=low + + * Taken over maintainership of this package and made the + "Let's clean up the bug list" release + * Added a debian/TODO file with some tasks not done in this upload. + * Use patch provided by Marc Dequenes to fix FTBFS on Hurd + (due to the use of selinux) (Closes: #279429) + * Enable pam_limits.so in the default configuration (Closes: #300725) + * Fix standard.daily so it can cope with mount points with spaces + (Closes: #198211) + * Standard.daily now warns if no lost+found directory was found in a local + ext2/3 filesystem (Closes: #46779) [as the other wishlists are + checksecurity related] + * Standard.daily now will also check xfs filesystems (Closes: #279613) + * Fix crontab.5's example to prevent confusion to users, default shell + is always /bin/sh (Closes: #231172) + * SELinux related fixes: + - Applied Russell Coker patches to fix three bugs that appear when + using SELinux (Closes: #264320) + - Applied Manoj Srivastava's patches to turn on SELinux support + (Closes: #315509) + * Add help text when creating new crontabs as suggested by Anthony Towns + (Closes: #18733) + * Use debhelper version 4 instead of the deprecated version 3, removed + debian/conffiles as it is no longer needed. + + -- Javier Fernandez-Sanguino Pen~a Tue, 5 Jul 2005 16:39:29 +0200 + +cron (3.0pl1-87) unstable; urgency=low + + * Clarify wording about /etc/cron.d files in cron.8. + * Make sure file descriptor 0==stdin at startup, so we don't + accidentally use it for something else. (closes: #295589) + + -- Steve Greenland Wed, 16 Feb 2005 18:37:27 -0600 + +cron (3.0pl1-86) unstable; urgency=high + + * And yes, still needs urgency=high. + + -- Steve Greenland Wed, 28 Jul 2004 15:43:18 -0500 + +cron (3.0pl1-85) unstable; urgency=low + + * Umm, yeah, need the 'install' file in CVS. + + -- Steve Greenland Wed, 28 Jul 2004 15:31:28 -0500 + +cron (3.0pl1-84) unstable; urgency=high + + * Urgency "high" to make the sudden freeze. + * Exit with failure status from crontab when action fails (thanks to + Christoph Berg for noticing and the patch) (closes: #229604) + * Require anacron to be executable (closes: #240822) + * Set ttyname to prevent pam_access confusion. Problem and solution from + Daniel Kobras (it's nice to get both!) (closes: #254845) + * Fix reference to syslogd(8) in cron.8 (closes: #246580) + * Fix awkward construction in crontab.5 (closes: #252420) + * Cleanup explanation of LOGNAME in crontab.5 (closes: #257672) + * Don't use --exec in start-stop-daemon calls. + + -- Steve Greenland Mon, 26 Jul 2004 10:40:22 -0500 + +cron (3.0pl1-83) unstable; urgency=low + + * Update default MTA from exim to exim4 (closes:#228561) + + -- Steve Greenland Mon, 19 Jan 2004 16:39:52 -0600 + +cron (3.0pl1-82) unstable; urgency=low + + * Added /etc/cron.d/.placeholder to prevent dpkg from removing the + directory when all packages using /etc/cron.d are purged. Likewise to + cron.weekly (which needs it), and cron.daily and cron.monthly (which + don't, but I like consistency.) (closes: #217760) + * Add '--retry 5' to the stop part of /etc/init.d/cron restart, so that + cron is completely stopped before attempting restart. (closes: #223945) + * Add cron.hourly directory (closes:#200848, #219338, #226710) + * Support the LSB naming convention for /etc/cron.d (closes: #171587) + * Move cron.d manual section out of DEBIAN SPECIFIC, since many + distributions now support this. (closes:#2277770) + * Clarify that environment setting are not re-expanded (closes: #223570) + * Some crontab(5) man page fixups (closes: #200100) + + -- Steve Greenland Sun, 18 Jan 2004 16:20:59 -0600 + +cron (3.0pl1-81) unstable; urgency=low + + * Only protect owner/group setting on individual user crontabs by + version, the setgid setting of /usr/bin/crontab needs to happen every + time (unless the admin has set something with + dpkg-statoverride. (closes: #210838) + * Change cron init script so that '/etc/init.d/cron {stop,restart}' only + kill off the parent daemon, not currently running cron jobs. Much + thanks to Dean Gaudet for the patch. (closes: #211849) + + -- Steve Greenland Sun, 21 Sep 2003 16:37:16 -0500 + +cron (3.0pl1-80) unstable; urgency=low + + * Kill one last spurious syslog() (closes: #211117, #211245) + * Allow system crontabs to be symlinks (closes: #210467) + + -- Steve Greenland Wed, 17 Sep 2003 19:15:55 -0500 + +cron (3.0pl1-79) unstable; urgency=low + + * Don't exit postinst when user is unkown (closes: #208432) + * Remove spurious debug messages (closes: #211117) + + -- Steve Greenland Tue, 16 Sep 2003 08:26:17 -0500 + +cron (3.0pl1-78) unstable; urgency=low + + * Okay, I think the owner/group fixup in for setgid crontabs is finally + as right as it's going to be. It will work for usernames with spaces, + and if the user doesn't exist (e.g. old crontab for a deleted user), + it will print an error message but not exit, which I think is okay. If + you have a username with a newline in it, then you deserve to lose, + and you will (closes: #210838). + * Allow cron to run in foreground. Possibly useful for running it under + monitoring tools like runit, monit, daemontools, etc. Thanks to Erich + Schubert for the patch (closes: #108492) + * Use the PAM environment settings, if so configured. (closes: #203737) + + -- Steve Greenland Sun, 14 Sep 2003 16:53:36 -0500 + +cron (3.0pl1-77) unstable; urgency=low + + * Smarter fix-up of crontabs groups (closes: #208555) + * Add dependency for adduser (closes: #208848) + * Support new PAM @includes for standard PAM configuration. + * Move note about loss of checksecurity from debconf to NEWS.Debian.gz, + the more appropriate place. Since that was the only use of debconf, + this also (closes: #195887) + * Add commented-out pam_limits support. (closes: 190556) + + -- Steve Greenland Sat, 6 Sep 2003 18:47:02 -0500 + +cron (3.0pl1-76) unstable; urgency=low + + * Fix postinst bug for no user crontabs (closes: #208433, #208435, #208448) + + -- Steve Greenland Tue, 2 Sep 2003 18:05:31 -0500 + +cron (3.0pl1-75) unstable; urgency=low + + * Make crontab setgid 'crontab' rather than setuid 'root' (closes: #18333) + + -- Steve Greenland Mon, 1 Sep 2003 18:57:14 -0500 + +cron (3.0pl1-74) unstable; urgency=low + + * Split out the checksecurity script et. al. to seperate package + 'checksecurity' + * Detect missing or stupid stepsize (e.g. */ or 7-9/0) (closes: #183650) + * Allow for crontab editors that don't write to the same file. Finally. + (closes: #149908) + * Don't weird out if VISUAL or EDITOR is set to "" (closes: #148809) + * Fixed several documentation/script typos and clarifications + (closes: #149448) + * Standards-Version 3.5.9 + + -- Steve Greenland Sun, 1 Jun 2003 15:02:23 -0500 + +cron (3.0pl1-73) unstable; urgency=low + + * Fixed spelling error in control file (Hi, Matt!) (closes: #124523) + * Check for existence of /etc/init.d/cron in prerm (closes:#151006) + * Added conflict for ancient version of lockfile-progs (closes: #123269) + * Added Mosix FS to list of excluded FS types in checksecurity.conf + (closes: #129177) + * chmod group.bak instead of passwd.bak twice (closes: #130079) + * Added ext3 for lost+found searches (closes:#135013) + * Finally fixed longstanding bug where cron doesn't recognize crontab + changes until 1 minute later. I don't know what I was looking at before, + as it was trivial. I apologize to all those bothered by this problem. + (closes: #74524, #13952) + * Remove debian/preinst, used only for a fix for a pre-slink development + release. + * Added support for invoke-rc.d, patch from Andreas Metzler (closes:#162676) + * Finally figured out why some error messages weren't getting printed + for the system crontabs (/etc/crontab, /etc/cron.d/*). Added new error + printing function and use it when user.c calls load_entry(). + (closes:#79037, #122358) + * Remove -odi option from invocation of /usr/sbin/sendmail. (closes:#146224) + * Don't run @monthly jobs every day. (closes: #150591). Ditto + @yearly. (Isn't it funny that despite the big whinefest about how + critically important the @whatever timespecs are, nobody previously + noticed this serious bug in the entire 7+ years I've been maintaining + cron?) + * Fix grammatical here in the cron(8) manpage. (closes: 147277) + * Fix spelling error in checksecurity.conf. (closes: #151601) + * Fix check for nfs/afs mounts in checksecurity. (closes: #157822) + * Replaced some tabs with spaces in crontab.5. (closes: #159912) + * Fix cron Makefile to not hardcode '-s' in $(INSTALL) commands. + (closes: #164966) + + -- Steve Greenland Mon, 4 Nov 2002 18:14:45 -0600 + +cron (3.0pl1-72) unstable; urgency=low + + * Add type usbdevfs to skipped "file systems" (this is /proc/bus/usb) + (closes: #113186,#113187) + * Removed 'xfs' from list of skipped file system types. Unfortunately, + it appears to be used by both the arla network file system and the SGI + XFS filesystem, and I must make the conservative choice. (closes: #113234) + * Remove extra diff in checksecurity. (closes: 113950) + * Add type none to skipped "file systems" (--bind mounts) (closes: #111395) + + -- Steve Greenland Mon, 1 Oct 2001 07:46:34 -0500 + +cron (3.0pl1-71) unstable; urgency=medium + + * Fixed nasty typo in checksecurity. (closes: #112156) + * Note to release manager: either -71 should go into woody, + or only -69. + + -- Steve Greenland Thu, 13 Sep 2001 16:39:25 -0500 + +cron (3.0pl1-70) unstable; urgency=medium + + * Catch full disk when writing new crontab. (closes: #110612) + * Don't modify backup file modes if no change (to prevent misleading + time mods). (closes: #104093) + * Remove obsolete reference to /bin/mail in crontab.5. (closes: #110962) + * Add Marc Merlin's checksecurity patches with more configuration + options. (closes: #89547, #63445) + * Make all setuid.{today,yesterday,etc.} files group adm. (closes: #109520) + + -- Steve Greenland Fri, 7 Sep 2001 18:27:37 -0500 + +cron (3.0pl1-69) unstable; urgency=low + + * Don't run checksecurity on any partition mount nosuid or noexec. + (closes:#98160) + * Don't run on devpts, either. + + -- Steve Greenland Sun, 20 May 2001 13:48:15 -0500 + +cron (3.0pl1-68) unstable; urgency=low + + * Finally tracked down the problem with @{reboot,hourly,etc.} commands + in the root crontabs (/etc/crontab, /etc/cron.d/*) (closes:#62141, #84727) + * Minor cosmetic cleanup in lost+found output, from Matthijs Melchior + (closes:#88861) + + -- Steve Greenland Sat, 19 May 2001 17:37:52 -0500 + +cron (3.0pl1-67) unstable; urgency=high + + * With crontab -e, don't give uid root on re-edit. + + -- Steve Greenland Sun, 6 May 2001 11:41:50 -0500 + +cron (3.0pl1-66) unstable; urgency=low + + * The "Julian Gilbey has been spending too much time proofreading" + release. Remove hyphens in cron.init (closes:#91323) + * change log_it() to open and close syslog every time, remove SIGPIPE + catcher, etc., all in attempt to keep PAM happy. + * Added original location to copyright file (closes:#88507) + * Allocate username dynamically in crontab.c (closes:#89040,#62268) + * Remove pam_limits.so from cron.pam file. + + -- Steve Greenland Tue, 27 Mar 2001 18:04:16 -0600 + +cron (3.0pl1-65) unstable; urgency=high + + * Close reboot file /var/run/crond.reboot after creating it, duh. Leaving + it open allowed possible writes from cron jobs. + + -- Steve Greenland Mon, 26 Feb 2001 09:47:34 -0600 + +cron (3.0pl1-64) unstable; urgency=high + * The "President's Birthday" release. + * Add checks for setuid() failures, to avoid running user jobs as root. + (closes:#85609, #86775) + * Add portability flag -P to df (in /etc/cron.daily/standard) to + avoid problem with long device names. (closes:#86851) + * Convert from suidmanager to dpkg --statoverride. + * Remove unnecessary dependency on bsdutils (it's Essential) + * Change compat.h to not cause inclusion of sys/time.h (instead of + time.h), due to recent change in glibc. Why it was doing this, I have + no idea. + + -- Steve Greenland Wed, 21 Feb 2001 16:26:57 -0600 + +cron (3.0pl1-63) unstable; urgency=medium + + * Fix mistaken diagnostic in crontab ("Reached end of file + while reading environment"). + * Fix "Too many arguments to [" error in postinst. (closes:#79725) + + -- Steve Greenland Sun, 28 Jan 2001 12:00:55 -0600 + +cron (3.0pl1-62) unstable; urgency=medium + + * Change tracking of crontab directory mod times to deal with directories + with mtimes in the future. Thanks to Florian Lohoff for tracking down + the cause of this rare yet long-standing bug. + (closes:#51202, #57356, #64198, #77353) + * Run @reboot jobs only on reboot instead of cron restart. + (closes:#74762, #77563) + * Clarify which directory the lost+found files are in. + * Protect against reading other people's crontabs via temp file symlink + in crontab -e. + + -- Steve Greenland Sat, 27 Jan 2001 17:01:43 -0600 + +cron (3.0pl1-61) unstable; urgency=high + + * Fix usage message in cron.init. + * Use pam only on non-Hurd. (closes:#75408) + * Move dpkg status backups to /var/backups. + * Apply security fix for temp file attack, thanks to Daniel Jacobowitz. + * Made /etc/pam.d/cron a conffile. + + -- Steve Greenland Fri, 17 Nov 2000 16:06:03 -0600 + +cron (3.0pl1-60) unstable; urgency=low + + * Re-set log to LOG_CRON after PAM changes it to LOG_AUTH.(closes:#70028) + + -- Steve Greenland Thu, 31 Aug 2000 14:37:44 -0500 + +cron (3.0pl1-59) unstable; urgency=low + + * Fixed typo of @cmp in standard.daily (closes:#69921,#69937,#69956) + * Add /etc/pam.d/cron, using standard Unix authorizations (closes:#69939) + + -- Steve Greenland Fri, 25 Aug 2000 14:02:47 -0500 + +cron (3.0pl1-58) unstable; urgency=low + + * Fix use of PATH_MAX/MAXPATHLEN in database.c and other HURD issues + (closes:#64382) + * Look in all (ext2) lost+found directories, not just + /lost+found (closes:#66629) + * Cosmetic changes to daily/standard (closes:#65078) + * Use diff -u0 in checksecurity to avoid useless context (closes:#60919) + * Note uppercasing of child processes in cron.8 (closes:#62103) + * Added Topi Mitterands PAM support patch - Thanks! (closes:#68366,#67586) + * Fix segfault in crontab -l (closes:#53735) + * Added Build-Depends. + * Added support for DEB_BUILD_OPTIONS, changed default compile to '-02 -Wall' + * Fixed various complaints generated by -Wall :-), except 'rcsid unused' + + -- Steve Greenland Wed, 23 Aug 2000 16:20:39 -0500 + +cron (3.0pl1-57) frozen unstable; urgency=low + + * Release Manager: 61296 is RC bug; + * Change lockfile-progs "Depends" to "Suggests" (policy violation), + only try to use lockfile-create in /etc/cron.daily/standard if it is + installed (closes:#61296) + + -- Steve Greenland Wed, 29 Mar 2000 20:12:06 -0600 + +cron (3.0pl1-56) frozen unstable; urgency=medium + + * Release Manager: 59115 is RC bug. + * set backup password files to root-only rw (closes:#59115) + * Fixed missing ntfs in checksecurity.conf (closes:#56939) + + -- Steve Greenland Mon, 28 Feb 2000 19:16:20 -0600 + +cron (3.0pl1-55) unstable; urgency=low + + * Changed __linux feature checks to __linux__ (closes:#50240) + * Added '-u' to diff in checksecurity (closes:#51679) + * Moved checksecurity LOGDIR (formerly LOG) setting to config + file (closes:#49683) + * Fixed removal of lockfile in cron.daily/standard. + + -- Steve Greenland Sat, 18 Dec 1999 18:53:29 -0600 + +cron (3.0pl1-54) unstable; urgency=low + + * Fixed spelling in checksecurity.8 (closes:#45281) + * Only look in /lost+found if it exists (closes:#46389) + * Only run @weekly jobs once a week (was looking at dom with OR + rather than AND.) (closes:#49437) + * Don't run more than one instance of /etc/cron.daily/standard (closes:#45487) + * Removed extra generation of TSTP in crontab -e. (closes:#48542) + + -- Steve Greenland Sun, 7 Nov 1999 15:09:48 -0600 + +cron (3.0pl1-53) unstable; urgency=medium + + * Applied OpenBSD patches supplied Topi Miettinen. Big change is + better handling of timekeeping (in particular, changes to/from daylight + savings time) (closes:#8499). + * Redirect stdin, stdout, and stderr to /dev/null. + (closes:#37189, #23231, #30653). + * Fixed bit_set()/bit_nset() confusion that caused @hourly to not work, so + that functionality is now documented (closes:#43282). + * Changed warrantee to warranty in debian/copyright (but it's still + misspelled in the source files) (closes:#40312) + * Adjust checksecurity and cron_daily/standard to get consistent owner/perms + on /var/log/setuid* files. (closes:#15295) + * Add 'coda' to the list of nfs/afs like file systems in checksecurity.conf + (closes:#29909) + * Fix version comparison to use dpkg --compare-versions, which is what I + should have done in the first place (closes:#42454) + * Add 'xfs' to the list of nfs/afs like file systems in checksecurity.conf, + as it appears that xfs==arla and arla==xfs. If there are other file systems + that show up as 'xfs', please let me know (closes:#37523) + * Don't rotate wtmp and btmp (closes:#42301). + + -- Steve Greenland Sat, 11 Sep 1999 18:42:37 -0500 + +cron (3.0pl1-52) unstable; urgency=high + + * Patch to fix "Reverse DoS": user could send arbitrary options to + sendmail via MAILTO variable. Fix by not allowing spaces or leading + dashes in MAILTO, and run sendmail as user, not root. + + -- Steve Greenland Sun, 29 Aug 1999 21:50:28 -0500 + +cron (3.0pl1-51) unstable; urgency=low + + * Changed default of "crontab -l" to NOT list the extra header lines. May + obtain original behaviour by setting "CRONTAB_NOHEADER=N". + * Fixed comment in misc.c that accidentally cause the SIGPIPE handler to + be reset more often than necessary. + * Don't checksecurity on ntfs file systems (closes:#33079) + * Added '-i' flag to sendmail command (closes:#36338) + * Added check for files in /lost+found (closes:#29791) + * Added preferential invocation of anacron for cron.{daily,weekly,monthly} + + -- Steve Greenland Thu, 27 May 1999 17:37:54 -0500 + +cron (3.0pl1-50) frozen unstable; urgency=low + + * Fixed rules file *again* so that crontab suidness doesn't get + stepped on by debhelper under certain kernel versions. + + -- Steve Greenland Sat, 9 Jan 1999 14:17:09 -0600 + +cron (3.0pl1-49) frozen unstable; urgency=low + + * Check for /etc/shadow and /etc/gshadow separately (closes:Bug#30232) + + -- Steve Greenland Wed, 2 Dec 1998 21:30:00 -0600 + +cron (3.0pl1-48) frozen unstable; urgency=low + + * Fixed rules file so that crontab doesn't get suid bit removed by latest + version of debhelper. (closes:Bug#29683) + + -- Steve Greenland Mon, 30 Nov 1998 21:10:00 -0600 + +cron (3.0pl1-47) frozen unstable; urgency=high + + * Removed close of {stdin,stdout,stderr}, as it appears that + some cron users expect it. + + -- Steve Greenland Mon, 2 Nov 1998 09:25:44 -0600 + +Cron (3.0pl1-46) frozen unstable; urgency=medium + + * changed mention of /var/spool/crontabs to /var/spool/cron/crontabs, and + added example /etc/crontab. (closes:Bug#28458,Bug#23101) + * Incorporated buffer overflow patches. (closes:Bug#26705,Bug#26749) + * Added backup of /etc/shadow and /etc/gshadow in cron.daily/standard. + (closes:Bug#25853) + * Better daemonization (close stdin,stdout,stderr). (closes:Bug#23231) + * Do cron.daily a little earlier, try to avoid overlap with cron.weekly. + (closes:Bug#23023) + * Added note in crontab.1 and crontab.5 about trailing newline requirement. + (closes:Bug#16132) + + -- Steve Greenland Sun, 1 Nov 1998 19:10:45 -0600 + +cron (3.0pl1-45) frozen unstable; urgency=high + + * Updated README and README.Debian w.r.t. file locations. + * Updated crontab.1 w.r.t. location of allow and deny files. + * Fixed problem in postinst when new install (instead of upgrade) + (closes:Bug#21426) + * Smarter algorithm for checking /etc/cron.d for changes, no longer + spins up laptop disks every minute. Thanks to Alexander "Shurik" + Shumakovitch for the fix. + * Re-did checksecurity to only report NFS/AFS insecure mounts + instead of running find on them (suggestion from Gregory Stark). + + -- Steve Greenland Sun, 26 Apr 1998 13:41:51 -0500 + +cron (3.0pl1-44) unstable; urgency=low + + * Fixed standards version reference. + * Fixed init script "comment", sigh. Let this be a warning to you: NEVER EVER + change a file with doing a complete test, even if it is "just a comment". + (closes:Bug#19466,Bug#19492,Bug#19479,Bug#19595,Bug#19725,Bug#19840, + Bug#19845,Bug19852). Sheesh. + + -- Steve Greenland Tue, 17 Mar 1998 21:55:03 -0600 + +cron (3.0pl1-43) unstable; urgency=medium + + * Removed support for TMPDIR. This is due to the problem it causes + with emacs -- emacs does different things with it's backup files + depending on whether or not the file is in /tmp. Emacs probably ought + to use TMPDIR instead of /tmp in its check. I've opened a "wishlist" + bug for crontab to support TMPDIR. + (closes: Bug#15985) + * When USE_SIGCHLD is defined, before running user process, set handling + for SIGCHLD to SIG_DFL, not SIG_IGN. Despite documentation that the + default for SIGCHLD is ignore, the behaviour is different. + (closes:Bug#17564,Bug#9075,Bug#16436,Bug#17814) + * Moved allow and deny files from /var/spool/cron to /etc/cron.{allow,deny} + (closes:Bug#14970) + * filenames in /etc/cron.d must comply with run-parts naming convention - + alpha-numeric, underscore, or hyphen. This ensures that conffile + remains (e.g. package.dpkg-dist) files don't get run. + * removed cronanacron command from /etc/crontab and distribution -- + replaced by /etc/cron.d/anacron file (from anacron package, not cron). + * Documented /etc/cron.d dir in manpage and readme.debian + * modified checksecurity.conf to not check auto file systems. + (closes:Bug#8702) + * crontab -u `whoami` now works. (closes:Bug#8702) + * documented CRONTAB_NOHEADER in crontab.1 + * /etc/crontab now uses run-parts(8) --report switch. (closes:Bug#18273) + * fixed initialization of oact in misc.c. (closes:Bug#17134) + * converted to use debhelper rather than debmake. + * changed date format in checksecurity so that we don't get false positives + after six months. (closes:Bug#19025) + * remove /etc/cron.{allow,deny} on purge + + -- Steve Greenland Mon, 9 Mar 1998 01:31:13 -0600 + +cron (3.0pl1-42) unstable; urgency=low + + * Don't output header on 'crontab -l' if CRONTAB_NOHEADER is 'Y' (Bug#15642) + * Read files in /etc/cron.d in addition to /etc/crontab (Bug#16628, + Bug#16617) + + -- Steve Greenland Sun, 11 Jan 1998 18:32:26 -0600 + +cron (3.0pl1-41) unstable; urgency=low + + * Updated crontab to use /usr/bin/editor if EDITOR and VISUAL undefined. + * Added support for TMPDIR from crontab (Bug#9309) + * Compress /usr/doc/cron/changelog.Debian -- this time for sure! (Bug#15296) + + -- Steve Greenland Fri, 28 Nov 1997 13:47:03 -0600 + +cron (3.0pl1-40) unstable; urgency=low + + * Correct directory reference in standard.monthly to /var/log + (Bug#12315, Bug#13466) + * Added -DUSE_SIGCHLD to defs, needed for alpha (Bug#12855,Bug#8611). + May also fix Bug#9075. + * Changed check for suidmanager (Bug#9326) + * Checksecurity no longer checks fat or vfat filesystems (Bug#8845, + Bug#13574) + * Changed spaces in crontab to , appearance only (Bug#13369) + * Removed bashism from standard.daily and checksecurity (Bug#9908, + Bug#9909) + * Added CHECKSECURITY_DISABLE flag to checksecurity.conf. + * Get the changelog names correct (Bug#14618) + * Correct day-of-month and month ranges in crontab.5 (Bug#15073) + + -- Steve Greenland Sun, 23 Nov 1997 22:58:05 -0600 + +cron (3.0pl1-39) unstable; urgency=low + + * Added configuration variable to eliminate error messages from + checksecurity's find command + * Better integration with anacron: suggest anacron, call anacron + on a regular basis so that it can keeps it's status up-to-date. New + file /usr/sbin/cronanacron. + * Call savelog for /var/adm/btmp from /etc/cron.monthly/standard. + + -- Steve Greenland Sun, 24 Aug 1997 18:52:55 -0500 + +cron (3.0pl1-38) stable unstable; urgency=low + + * When upgrading from -36 or -36.1, stop old cron daemon + in preinst, because the prerm's in those versions doesn't. + * Follow console message standard. + + -- Steve Greenland Sat, 18 Jan 1997 15:34:42 -0600 + +cron (3.0pl1-37) stable unstable; urgency=medium + + * Corrected postinst,prerm, and postrm scripts -- Installs and + removes rc.d links, and re-starts cron after install. + * More fixes from the BSD crew, sent to me by Marek Michalkiewicz: mostly + checks for null pointers, but also a few Makefile fixes, and at least + one potential buffer overrun (but I know of no exploits). + * Left in suidmanager stuff, but corrected default permission to 4755, + per Debian standards. + * Added CHANGES file (as /usr/doc/cron/changelog.upstream.gz) to + distribution. Added upstream README to distribution. + * Moved files out of ./debian/extra into ./debian because dpkg-source + cannot deal with createing directories. Hmmph. + * Removed filereaper reference from standard.daily + + -- Steve Greenland Sun, 5 Jan 1997 18:23:14 -0600 + +cron (3.0pl1-36.1) stable unstable; urgency=HIGH + + * Mended `find' invocation in debian/rules. + * Serious security hole (buffer overrun) fixed. + * Silly suidmanager stuff added by debmake. Pah. + + -- Jon Rabone Wed, 18 Dec 1996 21:38:33 +0000 + +cron (3.0pl1-36) frozen unstable; urgency=low + + * Removed DEBUG logging about SIGPIPE -- it's generated by every + child once syslogd has been restarted. + + -- Steve Greenland Mon, 2 Dec 1996 01:41:16 -0600 + +cron (3.0pl1-35) frozen unstable; urgency=low + + * Converted to new source format + * Added code in misc.c to catch SIGPIPE and re-open syslog. This + is all in #if defined(DEBIAN), and should be removed when syslog(3) + is fixed. Also added -DDEBIAN to build commands in Makefile. + + -- Steve Greenland Sun, 1 Dec 1996 16:21:52 -0600 + --- cron-3.0pl1.orig/debian/compat +++ cron-3.0pl1/debian/compat @@ -0,0 +1 @@ +9 --- cron-3.0pl1.orig/debian/rules +++ cron-3.0pl1/debian/rules @@ -0,0 +1,75 @@ +#!/usr/bin/make -f +# -*- makefile -*- + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + + +# Set system type (Linux, HURD, etc.) +DEB_HOST_ARCH_OS = $(shell dpkg-architecture -qDEB_HOST_ARCH_OS) + +DEB_HOST_GNU_TYPE = $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE = $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) +ifneq ($(DEB_HOST_GNU_TYPE),$(DEB_BUILD_GNU_TYPE)) +export CC=$(DEB_HOST_GNU_TYPE)-gcc +endif + +# Standard compiler flags +CFLAGS += -Wall -Wno-unused -Wno-comment + +# debug is disabled by default +ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) + DEBUG_DEFS = -DDEBUGGING=1 +else + DEBUG_DEFS = -DDEBUGGING=0 +endif +export DEBUG_DEFS + +# PAM is enabled by default +ifeq (,$(findstring nopam,$(DEB_BUILD_OPTIONS))) + PAM_DEFS = -DUSE_PAM + PAM_LIBS = -lpam + export PAM_DEFS PAM_LIBS +endif + +# SELinux and audit are only available on Linux +ifeq ($(DEB_HOST_ARCH_OS), linux) +# SELinux is enabled by default +ifeq (,$(findstring noselinux,$(DEB_BUILD_OPTIONS))) + SELINUX_DEFS = -DWITH_SELINUX + SELINUX_LIBS = -lselinux + export SELINUX_DEFS SELINUX_LIBS +endif +# audit is disabled by default +ifneq (,$(findstring withaudit,$(DEB_BUILD_OPTIONS))) + AUDIT_DEFS = -DWITH_AUDIT + AUDIT_LIBS = -laudit + export AUDIT_DEFS AUDIT_LIBS +endif +endif + + +%: + dh $@ + + +override_dh_auto_install: + # Empty target to bypass the auto-detected install target in Makefile + + +override_dh_install: + dh_install + install -m 644 debian/crontab.main debian/cron/etc/crontab + install -m 644 debian/placeholder debian/cron/etc/cron.d/.placeholder + install -m 644 debian/placeholder debian/cron/etc/cron.hourly/.placeholder + install -m 644 debian/placeholder debian/cron/etc/cron.daily/.placeholder + install -m 644 debian/placeholder debian/cron/etc/cron.weekly/.placeholder + install -m 644 debian/placeholder debian/cron/etc/cron.monthly/.placeholder + + +override_dh_installinit: + dh_installinit -- start 89 2 3 4 5 . stop 11 1 . + + +override_dh_compress: + dh_compress -Xexamples --- cron-3.0pl1.orig/debian/dirs +++ cron-3.0pl1/debian/dirs @@ -0,0 +1,8 @@ +usr/bin +usr/sbin +etc/cron.hourly +etc/cron.daily +etc/cron.weekly +etc/cron.monthly +etc/cron.d +var/spool/cron/crontabs --- cron-3.0pl1.orig/debian/control +++ cron-3.0pl1/debian/control @@ -0,0 +1,48 @@ +Source: cron +Section: admin +Priority: important +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Maintainer: Javier Fernández-Sanguino Peña +Uploaders: Christian Kastner +Standards-Version: 3.9.3 +Build-Depends: + debhelper (>= 9), + libpam0g-dev, + libselinux1-dev [linux-any], + libaudit-dev [linux-any] +Homepage: http://ftp.isc.org/isc/cron/ +Vcs-Git: git://git.debian.org/git/pkg-cron/pkg-cron.git +Vcs-Browser: http://anonscm.debian.org/gitweb/?p=pkg-cron/pkg-cron.git + +Package: cron +Architecture: any +Pre-Depends: + dpkg (>= 1.15.7.2) +Depends: + ${shlibs:Depends}, + ${misc:Depends}, + debianutils (>= 1.7), + adduser, + lsb-base (>= 3.0-10), + libpam-runtime (>= 1.0.1-11) +Suggests: + anacron (>=2.0-1), + logrotate, + checksecurity, + exim4 | postfix | mail-transport-agent +Breaks: + dpkg (<< 1.15.4) +Provides: cron-daemon +Description: process scheduling daemon + The cron daemon is a background process that runs particular programs at + particular times (for example, every minute, day, week, or month), as + specified in a crontab. By default, users may also create crontabs of + their own so that processes are run on their behalf. + . + Output from the commands is usually mailed to the system administrator + (or to the user in question); you should probably install a mail system + as well so that you can receive these messages. + . + This cron package does not provide any system maintenance tasks. Basic + periodic maintenance tasks are provided by other packages, such + as checksecurity. --- cron-3.0pl1.orig/debian/postinst +++ cron-3.0pl1/debian/postinst @@ -0,0 +1,69 @@ +#!/bin/sh +set -e + +crondir="/var/spool/cron" +action="$1" + +if [ "$action" != configure ]; then + exit 0 +fi + + +# LEGACY: This hasn't been relevant to Debian cron since the last Millenium, +# but we keep it here to preserve the upgrade path... +# Copy existing allow/deny files +pausemessage="F" +for fname in allow deny ; do + if [ -f $crondir/$fname ] ; then + if [ ! -f $/etc/cron.$fname ] ; then + mv $crondir/$fname /etc/cron.$fname + echo " " + echo "Moving $crondir/$fname to /etc/cron.$fname to comply with Debian policy" + pausemessage="T" + else + echo " " + echo "Warning:" + echo "Both $crondir/$fname and /etc/cron.$fname exist -- cron will" + echo "use /etc/cron.$fname" + pausemessage="T" + fi + fi +done + +# Conffiles that are now obsolete +dpkg-maintscript-helper rm_conffile /etc/cron.monthly/standard "3.0pl1-113" -- "$@" +dpkg-maintscript-helper rm_conffile /etc/cron.daily/standard "3.0pl1-124" -- "$@" +# If dpkg-maintscript-helper renames it instead of removing it because it has +# local modifications, we must rename it back to a valid name, or crond +# will not accept it. Thanks to Raphaël Hertzog for the tip. +if [ -e /etc/cron.monthly/standard.dpkg-bak ]; then + mv /etc/cron.monthly/standard.dpkg-bak \ + /etc/cron.monthly/standard-obsolete-please-verify-and-remove +fi + + +# Add group for crontabs +getent group crontab > /dev/null 2>&1 || addgroup --system crontab + +# Fixup crontab binary for new group 'crontab'. +if ! dpkg-statoverride --list /usr/bin/crontab > /dev/null ; then + dpkg-statoverride --update --add root crontab 2755 /usr/bin/crontab +fi + +# Fixup crontab , directory and files for new group 'crontab'. +# Can't use dpkg-statoverride for this because it doesn't cooperate nicely +# with cron alternatives such as bcron +if [ -d $crondir/crontabs ] ; then + chown root:crontab $crondir/crontabs + chmod 1730 $crondir/crontabs + # This used to be done conditionally. For versions prior to "3.0pl1-81" + # It has been disabled to suit cron alternative such as bcron. + cd $crondir/crontabs + set +e + ls -1 | xargs -r -n 1 --replace=xxx chown 'xxx:crontab' 'xxx' + ls -1 | xargs -r -n 1 chmod 600 + set -e +fi + + +#DEBHELPER# --- cron-3.0pl1.orig/debian/postrm +++ cron-3.0pl1/debian/postrm @@ -0,0 +1,12 @@ +#!/bin/sh +set -e + +# Conffiles that are now obsolete +dpkg-maintscript-helper rm_conffile /etc/cron.monthly/standard "3.0pl1-113" -- "$@" +dpkg-maintscript-helper rm_conffile /etc/cron.daily/standard "3.0pl1-124" -- "$@" + +if [ "$1" = "purge" ]; then + rm -f /etc/cron.allow /etc/cron.deny +fi + +#DEBHELPER# --- cron-3.0pl1.orig/debian/NEWS +++ cron-3.0pl1/debian/NEWS @@ -0,0 +1,33 @@ +cron (3.0pl1-119) unstable; urgency=low + + The semantics of the -L option of the cron daemon have changed: from + now on, the value will be interpreted as a bitmask of various log + selectors, with "1" (log only the start of jobs) being the new default. + + Additionally, since -117 (NEWS entry was overlooked), the LSBNAMES + variable in /etc/default/cron was merged with the EXTRA_OPTS variable + as it was redundant. + + -- Christian Kastner Sun, 07 Aug 2011 21:13:19 +0200 + +cron (3.0pl1-110) unstable; urgency=low + + In the past, long-running jobs caused certain MTAs to time out, + resulting in no output being sent. Justin Pryzby's patch to fix this + has been included. + + crontab(1) now refuses any crontabs missing a newline before EOF. + + cron(8) now recovers from broken symlinks in /etc/cron.d. This fix + may cause power management issues (disk spin-ups), feedback welcome. + + -- Christian Kastner Thu, 29 Apr 2010 13:25:09 +0200 + +cron (3.0pl1-74) unstable; urgency=low + + The checksecurity script is no longer included with the cron package: + it now has its own package, "checksecurity". If you liked the + functionality provided with that script, please install the new + package. + + -- Steve Greenland Sat, 6 Sep 2003 17:15:03 -0500 --- cron-3.0pl1.orig/debian/README.anacron +++ cron-3.0pl1/debian/README.anacron @@ -0,0 +1,14 @@ +To ease coordination with anacron, the invocation of the run-parts for +the /etc/cron.daily, /etc/cron.weekly, and /etc/cron.monthly directories +was changed to the form + + test -e /usr/sbin/anacron || run-parts --report /etc/cron.daily + +What this means is that if anacron has been installed, it will +be responsible for running those scripts. This is the standard +configuration of anacron: if you simply install both cron and anacron, +things will work as expected. + +However, if you have modified your anacron configuration +(/etc/anacrontab), you may need to re-adjust it in order to accommodate +this change. --- cron-3.0pl1.orig/debian/watch +++ cron-3.0pl1/debian/watch @@ -0,0 +1,3 @@ +version=3 + +http://ftp.isc.org/isc/cron/cron_(.*)\.shar --- cron-3.0pl1.orig/debian/README.Debian +++ cron-3.0pl1/debian/README.Debian @@ -0,0 +1,41 @@ +cron for DEBIAN +---------------------- + +This is the Debian GNU/Linux prepackaged and heavily modified version +of Paul Vixie's cron subsystem. + +Debian's cron development is being done on Alioth: +http://anonscm.debian.org/gitweb/?p=pkg-cron/cron.git + +This package was put together by Ian Jackson , +from the standard sources to 3.0pl1, as posted to comp.sources.unix. +Ian obtained them from +src.doc.ic.ac.uk:/usenet/comp.sources.unix/volume27/vixie-cron. + +Debian's cron version introduces a significant number of changes: + * Support for /etc/cron.d (drop-in dir for package crontabs) + * Debian-specific file locations and commands + * PAM support + * SELinux support + * auditlog support + * DST and other time-related changes/fixes + * SGID crontab(1) instead of SUID root + * numerous other smaller features and fixes. + +Users are STRONGLY advised to carefully check the man pages crontab(1) +and crontab(5). System administrators should also read cron(8). + +File locations that are different than that indicated in +the cron distributions README: + +user crontabs: /var/spool/cron/crontabs/* +log file: Check your syslog settings, facility "cron" +allow file: /etc/cron.allow +deny file: /etc/cron.deny + +This package is now maintained by Javier Fernández-Sanguino Peña + and Christian Kastner . + +Historical note: The old subversion repository from before the +migration to git can be found here: + http://svn.debian.org/wsvn/pkg-cron --- cron-3.0pl1.orig/debian/docs +++ cron-3.0pl1/debian/docs @@ -0,0 +1,4 @@ +FEATURES +THANKS +README +debian/README.anacron --- cron-3.0pl1.orig/debian/preinst +++ cron-3.0pl1/debian/preinst @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +# Conffiles that are now obsolete +dpkg-maintscript-helper rm_conffile /etc/cron.monthly/standard "3.0pl1-113" -- "$@" +dpkg-maintscript-helper rm_conffile /etc/cron.daily/standard "3.0pl1-124" -- "$@" + +#DEBHELPER# --- cron-3.0pl1.orig/debian/prerm +++ cron-3.0pl1/debian/prerm @@ -0,0 +1,5 @@ +#!/bin/sh +set -e + +#DEBHELPER# + --- cron-3.0pl1.orig/debian/cron.examples +++ cron-3.0pl1/debian/cron.examples @@ -0,0 +1 @@ +debian/examples/* --- cron-3.0pl1.orig/debian/cron.init +++ cron-3.0pl1/debian/cron.init @@ -0,0 +1,92 @@ +#!/bin/sh +# Start/stop the cron daemon. +# +### BEGIN INIT INFO +# Provides: cron +# Required-Start: $remote_fs $syslog $time +# Required-Stop: $remote_fs $syslog $time +# Should-Start: $network $named slapd autofs ypbind nscd nslcd +# Should-Stop: $network $named slapd autofs ypbind nscd nslcd +# Default-Start: 2 3 4 5 +# Default-Stop: +# Short-Description: Regular background program processing daemon +# Description: cron is a standard UNIX program that runs user-specified +# programs at periodic scheduled times. vixie cron adds a +# number of features to the basic UNIX cron, including better +# security and more powerful configuration options. +### END INIT INFO + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +DESC="cron daemon" +NAME=cron +DAEMON=/usr/sbin/cron +PIDFILE=/var/run/crond.pid +SCRIPTNAME=/etc/init.d/"$NAME" + +test -f $DAEMON || exit 0 + +. /lib/lsb/init-functions + +[ -r /etc/default/cron ] && . /etc/default/cron + +# Read the system's locale and set cron's locale. This is only used for +# setting the charset of mails generated by cron. To provide locale +# information to tasks running under cron, see /etc/pam.d/cron. +# +# We read /etc/environment, but warn about locale information in +# there because it should be in /etc/default/locale. +parse_environment () +{ + for ENV_FILE in /etc/environment /etc/default/locale; do + [ -r "$ENV_FILE" ] || continue + [ -s "$ENV_FILE" ] || continue + + for var in LANG LANGUAGE LC_ALL LC_CTYPE; do + value=`egrep "^${var}=" "$ENV_FILE" | tail -n1 | cut -d= -f2` + [ -n "$value" ] && eval export $var=$value + + if [ -n "$value" ] && [ "$ENV_FILE" = /etc/environment ]; then + log_warning_msg "/etc/environment has been deprecated for locale information; use /etc/default/locale for $var=$value instead" + fi + done + done + +# Get the timezone set. + if [ -z "$TZ" -a -e /etc/timezone ]; then + TZ=`cat /etc/timezone` + fi +} + +# Parse the system's environment +if [ "$READ_ENV" = "yes" ] ; then + parse_environment +fi + + +case "$1" in +start) log_daemon_msg "Starting periodic command scheduler" "cron" + start_daemon -p $PIDFILE $DAEMON $EXTRA_OPTS + log_end_msg $? + ;; +stop) log_daemon_msg "Stopping periodic command scheduler" "cron" + killproc -p $PIDFILE $DAEMON + RETVAL=$? + [ $RETVAL -eq 0 ] && [ -e "$PIDFILE" ] && rm -f $PIDFILE + log_end_msg $RETVAL + ;; +restart) log_daemon_msg "Restarting periodic command scheduler" "cron" + $0 stop + $0 start + ;; +reload|force-reload) log_daemon_msg "Reloading configuration files for periodic command scheduler" "cron" + # cron reloads automatically + log_end_msg 0 + ;; +status) + status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $? + ;; +*) log_action_msg "Usage: /etc/init.d/cron {start|stop|status|restart|reload|force-reload}" + exit 2 + ;; +esac +exit 0 --- cron-3.0pl1.orig/debian/examples/cron-tasks-review.sh +++ cron-3.0pl1/debian/examples/cron-tasks-review.sh @@ -0,0 +1,186 @@ +#!/bin/bash +# +# Review the cron tasks defined in the system and warn the admin +# if some of the files will not be run +# +# This program is copyright 2011 by Javier Fernandez-Sanguino +# +# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# For more information please see +# http://www.gnu.org/licenses/licenses.html#GPL + + +set -e +# reset locale, just in case +LC_ALL=C +export LC_ALL + +PROGNAME=${0##*/} +PROGVERSION=1.0 +# Command line options +SHORTOPTS=hvsi +LONGOPTS=help,version,syslog,info +set -- $(getopt -s bash -o $SHORTOPTS -l $LONGOPTS --n $PROGNAME -- "$@") + +version () { + echo "$PROGNAME $PROGVERSION" + echo "$PROGNAME is copyright © Javier Fernandez-Sanguino " + echo "Released under the terms of the GPL version 2 or later" + echo "This program is part of the cron package" +} + +usage () { + cat <&2 + ;; + "info") + echo "INFO: $msg" + ;; + esac + fi +} + +warn () { +# Send a warning to the user + file=$1 + reason=$2 + + name=`basename $file` + # Skip hidden files + echo $name | grep -q -E '^\.' && return + # Skip disabled files + echo $name | grep -q -E '\.disabled' && return + + # TODO: Should we send warnings for '.old' or '.orig'? + + # Do not send a warning if the file is '.dpkg-old' or '.dpkg-dist' + if ! echo $file | grep -q -E '\.dpkg-(old|dist)$' ; then + send_message "warn" "The file $file will not be executed by cron: $reason" + else + send_message "info" "The file $file is a leftover from the Debian package manager" + fi +} + +check_results () { + + dir=$1 + run_file=$2 + exec_test=$3 + + # Now check the files we found and the ones that exist in the directory + find $dir \( -type f -o -type l \) -printf '%p %l\n' | + while read file pointer; do + if ! grep -q "^$file$" $run_file; then + [ -L "$file" ] && [ ! -e "$pointer" ] && \ + warn $file "Points to an nonexistent location ($pointer)" && continue + [ "$exec_test" = "yes" ] && [ ! -x "$file" ] && \ + warn $file "Is not executable" && continue + [ ! -r "$file" ] && [ "`id -u`" != "0" ] && \ + warn $file "Cannot read the file to determine if it will be run ($PROGNAME is not running as root)" && continue + [ ! -r "$file" ] && \ + warn $file "File is unreadable" && continue + warn $file "Does not conform to the run-parts convention" + else + +# do additional checks for symlinks for files that *are* listed by run-parts + if [ -L "$file" ] ; then +# for symlinks: does the file exist? + if [ ! -e "$pointer" ] ; then + warn $file "Points to an nonexistent location ($pointer)" + fi +# for symlinks: is it owned by root? + owner=`ls -l $pointer | awk '{print $3}'` + if [ "$owner" != "root" ]; then + warn $file "Is not owned by root" + fi + fi + + fi + done + +} + +# Setup for the tests + +# First: check if we are using -l +[ -r /etc/default/cron ] && . /etc/default/cron +use_lsb="no" +[ "$LSBNAMES" = "-l" ] && use_lsb="yes" +echo $EXTRA_OPTS | grep -q -- '-l' && use_lsb="yes" +# Set the options for run parts +run_opts="" +[ "$use_lsb" = "yes" ] && run_opts="--lsbsysinit" + +temp=`tempfile` || { echo "ERROR: Cannot create temporary file" >&2 ; exit 1; } +trap "rm -f $temp" 0 1 2 3 13 15 + +# Now review the scripts, note that cron does not use run-parts to run these +# so they are *not* required to be executables, just to conform with the + +# Step 1: Review /etc/cron.d +run-parts $run_opts --list /etc/cron.d >$temp +check_results /etc/cron.d $temp "no" + + +# Step 2: Review /etc/cron.{hourly,daily,weekly,monthly} + +for interval in hourly daily weekly monthly; do + testdir=/etc/cron.$interval + run-parts $run_opts --test $testdir >$temp + check_results $testdir $temp "yes" +done + + +exit 0 + --- cron-3.0pl1.orig/debian/examples/crontab2english.pl +++ cron-3.0pl1/debian/examples/crontab2english.pl @@ -0,0 +1,958 @@ +#!/usr/bin/perl +#Time-stamp: "2001-07-29 16:07:28 MDT" +my $VERSION = '0.71'; +require 5.00404; # I don't think there's (?: ... ) before that. + +=head1 NAME + +crontab2english -- explain crontab commands in English + +=head1 SYNOPSIS + + Usage: + % crontab2english [-f] files... + Or: + % cat files... | crontab2english + + If you do just this: + % crontab2english + then it's the same as crontab -l | crontab2english + + Example output: + % crontab2english | less + Setting env var MAILTO to hulahoops@polygon.int + + Command: (line 2) + Run: /bin/csh -c 'perl ~/thang.pl | mail -s hujambo root' + At: 8:10am on the 15th of every month + + Command: (line 5) + Run: df -k + At: 5:40am every day + + Command: (line 7) + Run: ls -l /tmp + At: 6:50am every Monday + + Or with the -f ("f" for filter) switch, it just adds comments + to the input file: + + % crontab2english -f | less + # My happy crontab file + MAILTO=hulahoops@polygon.int + 10 8 15 * * /bin/csh -c 'perl ~/thang.pl | mail -s hujambo root' + #> At: 8:10am on the 15th of every month + + + 40 5 * * * df -k + #> At: 5:40am every day + + 50 6 * * 1 ls -l /tmp + #> At: 6:50am every Monday + +=head1 DESCRIPTION + +It's easy to make mistakes in crontab files. Running +C on your crontab files and reading the +resulting English explanations will help you catch errors. + +=head1 SWITCHES + +C<-f> puts this in "filter mode" -- the output is just +the input plus commentary. + +C<-v> describes the current C +version to STDOUT and exits. + +C<-p> forces POSIX-only mode: anything not allowed in +the POSIX crontab spec won't be understood. + +C<-e> (usually default) turns off POSIX-only mode: +i.e., it doesn't feign ignorance of things not in +the POSIX spec. + +C<--> signals end of switches. + +=head1 ENVIRONMENT + +If the evironment variables C and/or +C are true, then this turns on C<-p> (POSIX-only) +mode by default. That's overrideable with the C<-e> switch. + +=head1 CAVEATS + +I've tried to make this program understand all the kinds of crontab +lines that are out there. That probably includes a few kinds of lines +that your particular cron daemon doesn't understand, so just because +crontab2english understands something doesn't mean your cron daemon +will. + +Pragmatically, however, there seem to be three kinds of cron daemons +around these days: + +=over + +=item * + +Ones based on old (1993ish) Vixie crontab. These understand all of +POSIX, and more. This is what almost almost everyone runs these days. + +=item * + +Ones that understand I what the POSIX crontab spec allows -- +which excludes all sorts of amenities including: stepped ranges +("1-9/2"), "VAR=NAME" lines, English month or day abbreviations ("mon" +or "jan"), day 7 meaning "Sunday", and "*/3" meaning "every third...". + +=item * + +Even more recent (post-1993ish) Vixies. These seem relatively rare. +They seem to be just like old Vixies, plus they understand at-words +like "@annually", "@reboot", etc. (altho in some cases, those aren't +mentioned in the docs!). + +=back + +There I be some ancient or demented pre-Vixie non-POSIX crons +running somewhere. One hopes that these would all basically +understand anything that POSIX does (and possibly nothing more?), but +you just might find peculiarities including: + +=over + +=item * + +Rejecting 0 for Sunday, and accepting only 7. + +=item * + +Rejecting 7 for Sunday, and accepting only 0. + +=item * + +Accepting lists I ranges, but no lists that include ranges. I.e., +allowing "7,8,9", allowing "1-3", but I allowing "1-3,7-9". + +=item * + +Not accepting ranges at all? + +=back + +Consult your man pages carefully. + +Good general advice: keep your crontab lines simple, and that'll +minimize the chances of disagreement between what you indend, what +crontab2english understands (with or without C<-p>), and what your +cron daemon understands. + +=head1 SEE ALSO + +C + +C + +C + +=head1 BUG REPORTS + +If this program explains a crontab line wrong, or can't parse +it, then email me the line, and an explanation of how it you +think it should parse. + +=head1 DISCLAIMER + +C is distributed in the hope that it will be useful, +but B; without even the implied warranty of +B or B. + +=head1 COPYRIGHT + +Copyright 2001 Sean M. Burke. + +This library is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +=head1 AUTHOR + +Sean M. Burke, Esburke@cpan.orgE + +=head1 README + +Translates crontab notation into English, for sanity checking: For +example, "10 8 15 * * foo bar" into: Run: foo bar with input +"baz\x0a" At: 8:10am on the 15th of every month + +=head1 SCRIPT CATEGORIES + +UNIX/System_administration + +=head1 CHANGE LOG + +=over + +=item v0.71: 2001-07-29 + +Now supports the weird new Vixie-isms like "C<@annually>". + +Features for feigning ignorance of non-POSIX features: C<-p>, C<-e>, +and the evironment variables C and/or +C. + +=item v0.63: 2001-07-17 + +Fixed a bug spotted by Greg Wimpey, where leading whitespace wasn't +getting duly ignored. + +=item v0.62: 2001-07-14 + +Added special cases for when minutes field is 0. + +Added explicit "require" statement to ensure acceptable Perl version. + +Changed "Every Tuesday of May" to "Every Tuesday in May" + +Changed qr//'s to just plain strings, for all the 500404 dinosaurs. + +=item v0.61 2001-01-23 + +First public release. + +=back + +=cut + +use strict; +use integer; +use constant 'DEBUG' => 0; + +my $filter = ''; # ...which is false. + +# Lame switch processing: + +my $posix; # whether to be just POSIX +while(@ARGV and $ARGV[0] =~ m<^->s) { + if($ARGV[0] eq '--') { # end of switches. + shift @ARGV; + last; + } elsif($ARGV[0] eq '-f') { # filter mode + shift @ARGV; + $filter = '#> '; # ...which is true! + } elsif($ARGV[0] eq '-p') { # disable extensions + shift @ARGV; + $posix = 1; + } elsif($ARGV[0] eq '-e') { # enable extensions + shift @ARGV; + $posix = 0; + } elsif($ARGV[0] eq '-v') { + print "crontab2english v$VERSION sburke\@cpan.org\n"; + exit; + } else { + die "Usage: crontab2english [-f] [files]\n" + . "See 'perldoc crontab2english' for more info.\n"; + } +} + +$posix = $ENV{'POSIXLY_CORRECT'} || $ENV{'POSIX_ME_HARDER'} + unless defined $posix; + +print $filter, " POSIX-only mode\n" if $posix; + +my @lines; +if(@ARGV) { + @lines = <>; +} elsif(-t *STDIN) { + @lines = `crontab -l`; +} else { + @lines = ; +} + +#-------------------------------------------------------------------------- +# Build tables. +my @dows = qw(Sun Mon Tue Wed Thu Fri Sat); +my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); +my($dow, $month, %dow2num, %month2num, %num2dow, %num2month); + +my %mil2ampm; +@mil2ampm{0 .. 23} + = ('midnight', map($_ . 'am', 1 .. 11), 'noon', map($_ . 'pm', 1 .. 11)); + +@dow2num{map lc($_), @dows} = (0 .. 6); +push @dows, 'Sun' unless $posix; + # POSIX doesn't know about day 7 +@num2dow{0 .. $#dows} = @dows; +DEBUG and print "DOWS: @dows\n"; + + +@month2num{map lc($_), @months} = (1 .. 12); +@num2month{1 .. 12} = @months; +unshift @months, ''; + +{ + my $x = join '|', map quotemeta($_), @dows; + $dow = "^($x)\$"; # regexp + $x = join '|', map quotemeta($_), @months; + $month = "^($x)\$"; # regexp +} + +my(%num2month_long, %num2dow_long); +@num2month_long{1 .. 12} = qw( + January February March April May June July August September October + November December +); +@num2dow_long{0 .. 6} = qw( + Sunday Monday Tuesday Wednesday Thursday Friday Saturday +); +$num2dow_long{7} = 'Sunday' unless $posix; + +my $atom; +if($posix) { + $atom = '\d+|(?:\d+-\d+)'; # will be a RE + # Yes, POSIX allows no stepped ranges. +} else { + $atom = '\d+|(?:\d+-\d+(?:/\d+)?)'; # will be a RE +} +my $atoms = "^(?:$atom)(?:,$atom)*\$"; # well be a RE + +print "Atoms RE: $atoms\n" if DEBUG; + +my %atword = ( # for latter-day Vixie-isms + 'reboot' => 'At reboot', + 'yearly' => 'Yearly (midnight on January 1st)', + 'annually' => 'Yearly (midnight on January 1st)', + 'monthly' => 'Monthly (midnight on the first of every month)', + 'weekly' => 'Weekly (midnight every Sunday)', + 'daily' => 'Daily, at midnight', + 'midnight' => 'Daily, at midnight', + 'hourly' => 'At the top of every hour', + # These are no longer documented in Vixie cron 3.0. Why not? +); + +#-------------------------------------------------------------------------- + +my $line_number = 0; + +{ + my(@bits,$k,$v); + foreach (@lines) { + print $_ if $filter; + chomp; + DEBUG > 1 and print "Line: <$_>\n"; + ++$line_number; + next if m/^[ \t]*#/s or m/^[ \t]*$/s; + s/^[ \t]+//s; # "leading spaces and tabs are ignored" + + if(DEBUG > 1) { + @bits = split m/[ \t]+/,$_,6; + print "Bit count: ", scalar(@bits), ".\n"; + } + + # The POSIX cron spec doesn't seem to mention + # environment-setting lines at all! + + if(!$posix and m/^([^= \t]+)[ \t]*=[ \t]*\"(.*)\"[ \t]*$/s ) { + # NAME = "VALUE" + $k =~ s/[ \t]+$//; + $filter or print "Setting env var $k to \"$v\"\n"; + } elsif(!$posix and m/^([^= \t]+)[ \t]*=[ \t]*\'(.*)\'[ \t]*$/s ) { + # NAME = 'VALUE' + ($k,$v) = ($1,$2); + #$k =~ s/[ \t]+$//; + $filter or print "Setting env var $k to \'$v\'\n"; + } elsif(!$posix and m/^([^= \t]+)[ \t]*=(.*)/s ) { + # NAME = VALUE + ($k,$v) = ($1,$2); + #$k =~ s/[ \t]+$//; + $v =~ s/^[ \t]+//; + $filter or print "Setting env var $k to $v\n\n"; + } elsif(!$posix and m/^\@(\w+)[ \t]+(.*)/s and exists $atword{lc $1}) { + process_command($_, $atword{lc $1}, $2); + } elsif( (@bits = split m/[ \t]+/, $_, 6) and @bits == 6 ) { + DEBUG and print "Bits: ", map("<$_> ", @bits), "\n"; + process_command($_, @bits); + } else { + if($filter) { + print $filter, "UNPARSEABLE LINE?!\n"; + } else { + print "Unparseable line (#$line_number): \"", esc($_), "\"\n"; + } + } + } + exit; +} + +#-------------------------------------------------------------------------- +sub process_command { + # 0 m, 1 h, 2 day-of-month, 3 month, 4 dow + my $line = shift; + + my(@time_lines, $command_string); + if(@_ == 2) { # hack for funky vixieism + $command_string = $_[1]; + @time_lines = ($_[0]); + } else { + # a normal line -- expand and Englishify it + my(@bits) = expand_time_bits(@_); + + if(@bits == 1) { # signals error condition + my $x = $bits[0]; + if($filter) { + print $filter, + "Unparseable ", @$x == 1 ? 'bit' : 'bits', + ": ", join(' ', map "\"$_\"", @$x), "\n" + ; + } else { + print + "Unparseable ", @$x == 1 ? 'bit' : 'bits', + " in parsing of command ${$x}[-1] at line number $line_number:\n", + map(" $_\n", @$x), "\n" + ; + } + return; + } + + @time_lines = bits_to_english(@bits); + $time_lines[0] = ucfirst($time_lines[0]); + if(length(join ' ', @time_lines) <= 75) { + @time_lines = (join ' ', @time_lines); + } + for(@time_lines) { $_ = ' ' . $_ }; # indent over + $time_lines[0] = "At:" . $time_lines[0]; + $command_string = pop @bits; + } + + my @command = split( "\n", percent_proc($command_string), -1 ); + + if(@command) { + pop @command if @command == 2 and $command[1] eq ''; + # Eliminate mention of basically null input + } else { + push @command, ''; + } + + if(@command > 1) { + my $x = join "\n", splice @command, 1; + push @command, " with input \"" . esc($x) . "\""; + } + if($command[0] =~ m<^\*>s) { + push @command, " (Do you really mean the command to start with \"*\"?)"; + } elsif($command[0] eq '') { + push @command, " (Do you really mean to run a null command?)"; + } + $command[0] = "Run: $command[0]"; + + if($filter) { + print + map("$filter $_\n", + (@command == 1) ? () : (@command), # be concise for simple cases + @time_lines + ), + ; + } else { + print + #was: "Command: (line $line_number) $line\n", + # but that's awful verbose + "Command: (line $line_number)\n", + map(" $_\n", @command, @time_lines ), "\n"; + } + + return; +} + +#-------------------------------------------------------------------------- +sub expand_time_bits { + my @bits = @_; + my @unparseable; + + # 0 m, 1 h, 2 day-of-month, 3 month, 4 dow + + unless($posix) { + if($bits[3] =~ m/($month)/oi) { $bits[3] = $month2num{lc $1} } + if($bits[4] =~ m/($dow)/oi ) { $bits[4] = $dow2num{lc $1} } + } + + for(my $i = 0; $i < 5 ; ++$i) { + my @segments; + if($bits[$i] eq '*') { + push @segments, ['*']; + } elsif(!$posix and $bits[$i] =~ m<^\*/(\d+)$>s) { + # a hack for "*/3" etc + push @segments, ['*', 0 + $1]; + } elsif($bits[$i] =~ m/$atoms/ois) { + foreach my $thang (split ',', $bits[$i]) { + if($thang =~ m<^(?:(\d+)|(?:(\d+)-(\d+)(?:/(\d+))?))$>s) { + if(defined $1) { + push @segments, [0 + $1]; # "7" + } elsif(defined $4) { + push @segments, [0 + $2, 0 + $3, 0 + $4]; # "3-20/4" + } else { + push @segments, [0 + $2, 0 + $3]; # "3-20" + } + } else { + warn "GWAH? thang \"$thang\""; + } + } + } else { + push @unparseable, sprintf "field %s: \"%s\"", $i + 1, esc($bits[$i]); + next; + } + + $bits[$i] = \@segments; + } + return \@unparseable if @unparseable; + return @bits; +} + +#-------------------------------------------------------------------------- + +sub bits_to_english { + # This is the deep ugly scary guts of this program. + # The older and eldritch among you might recognize this as sort of a + # parody of bad old Lisp style of data-structure handling. + + my @bits = @_; + my @time_lines; + + { # gratuitous block. + + # Render the minutes and hours ######################################## + if(@{$bits[0]} == 1 and @{$bits[1]} == 1 and + @{$bits[0][0]} == 1 and @{$bits[1][0]} == 1 and + $bits[0][0][0] ne '*' and $bits[1][0][0] ne '*' + # It's a highly simplifiable time expression! + # This is a very common case. Like "46 13" -> 1:46pm + # Formally: when minute and hour are each a single number. + ) { + my $h = $bits[1][0][0]; + if($bits[0][0][0] == 0) { + # Simply at the top of the hour, so just call it by the hour name. + push @time_lines, $mil2ampm{$h}; + } else { + # Can't say "noon:02", so use an always-numeric time format: + push @time_lines, sprintf '%s:%02d%s', + ($h > 12) ? ($h - 12) : $h, + $bits[0][0][0], + ($h >= 12) ? 'pm' : 'am'; + } + $time_lines[-1] .= ' on'; + + } else { # It's not a highly simplifiable time expression + + # First, minutes: + if($bits[0][0][0] eq '*') { + if(1 == @{$bits[0][0]} or $bits[0][0][1] == 1) { + push @time_lines, 'every minute of'; + } else { + push @time_lines, 'every ' . freq($bits[0][0][1]) . ' minute of'; + } + + } elsif( @{$bits[0]} == 1 and $bits[0][0][0] == 0 ) { + # It's just a '0'. Ignore it -- instead of bothering + # to add a "0 minutes past" + } elsif( !grep @$_ > 1, @{$bits[0]} ) { + # it's all like 7,10,15. conjoinable + push @time_lines, conj_and(map $_->[0], @{$bits[0]}) . ( + $bits[0][-1][0] == 1 ? ' minute past' : ' minutes past' ); + } else { # it's just gonna be long. + my @hunks; + foreach my $bit (@{$bits[0]}) { + if(@$bit == 1) { #"7" + push @hunks, $bit->[0] == 1 ? '1 minute' : "$bit->[0] minutes"; + } elsif(@$bit == 2) { #"7-9" + push @hunks, sprintf "from %d to %d %s", @$bit, + $bit->[1] == 1 ? 'minute' : 'minutes'; + } elsif(@$bit == 3) { # "7-20/2" + push @hunks, sprintf "every %d %s from %d to %d", + $bit->[2], + $bit->[2] == 1 ? 'minute' : 'minutes', + $bit->[0], $bit->[1], + ; + } + } + push @time_lines, conj_and(@hunks) . ' past'; + } + + # Now hours + if($bits[1][0][0] eq '*') { + if(1 == @{$bits[1][0]} or $bits[1][0][1] == 1) { + push @time_lines, 'every hour of'; + } else { + push @time_lines, 'every ' . freq($bits[1][0][1]) . ' hour of'; + } + } else { + my @hunks; + foreach my $bit (@{$bits[1]}) { + if(@$bit == 1) { # "7" + push @hunks, $mil2ampm{$bit->[0]} || "HOUR_$bit->[0]??"; + } elsif(@$bit == 2) { # "7-9" + push @hunks, sprintf "from %s to %s", + $mil2ampm{$bit->[0]} || "HOUR_$bit->[0]??", + $mil2ampm{$bit->[1]} || "HOUR_$bit->[1]??", + } elsif(@$bit == 3) { # "7-20/2" + push @hunks, sprintf "every %d %s from %s to %s", + $bit->[2], + $bit->[2] == 1 ? 'hour' : 'hours', + $mil2ampm{$bit->[0]} || "HOUR_$bit->[0]??", + $mil2ampm{$bit->[1]} || "HOUR_$bit->[1]??", + } + } + push @time_lines, conj_and(@hunks) . ' of'; + } + # End of hours and minutes + } + + # Day-of-month ######################################################## + if($bits[2][0][0] eq '*') { + $time_lines[-1] =~ s/ on$//s; + if(1 == @{$bits[2][0]} or $bits[2][0][1] == 1) { + push @time_lines, 'every day of'; + } else { + push @time_lines, 'every ' . freq($bits[2][0][1]) . ' day of'; + } + } else { + my @hunks; + foreach my $bit (@{$bits[2]}) { + if(@$bit == 1) { # "7" + push @hunks, 'the ' . ordinate($bit->[0]); + } elsif(@$bit == 2) { # "7-9" + push @hunks, sprintf "from the %s to the %s", + ordinate($bit->[0]), ordinate($bit->[1]), + } elsif(@$bit == 3) { # "7-20/2" + push @hunks, sprintf "every %d %s from the %s to the %s", + $bit->[2], + $bit->[2] == 1 ? 'day' : 'days', + ordinate($bit->[0]), ordinate($bit->[1]), + } + } + + # collapse the "the"s, if all the elements have one + if(@hunks > 1 and !grep !m/^the /s, @hunks) { + for (@hunks) { s/^the //s; } + $hunks[0] = 'the '. $hunks[0]; + } + + push @time_lines, conj_and(@hunks) . ' of'; + } + + # Month ############################################################### + if($bits[3][0][0] eq '*') { + if(1 == @{$bits[3][0]} or $bits[3][0][1] == 1) { + push @time_lines, 'every month'; + } else { + push @time_lines, 'every ' . freq($bits[3][0][1]) . ' month'; + } + } else { + my @hunks; + foreach my $bit (@{$bits[3]}) { + if(@$bit == 1) { # "7" + push @hunks, $num2month_long{$bit->[0]} || "MONTH_$bit->[0]??" + } elsif(@$bit == 2) { # "7-9" + push @hunks, sprintf "from %s to %s", + $num2month_long{$bit->[0]} || "MONTH_$bit->[0]??", + $num2month_long{$bit->[1]} || "MONTH_$bit->[1]??", + } elsif(@$bit == 3) { # "7-20/2" + push @hunks, sprintf "every %d %s from %s to %s", + $bit->[2], + $bit->[2] == 1 ? 'month' : 'months', + $num2month_long{$bit->[0]} || "MONTH_$bit->[0]??", + $num2month_long{$bit->[1]} || "MONTH_$bit->[1]??", + } + } + push @time_lines, conj_and(@hunks); + + # put in semicolons in the case of complex constituency + #if($time_lines[-1] =~ m/every|from/) { + # $time_lines[-1] =~ tr/,/;/; + # s/ (and|or)\b/\; $1/g; + #} + } + + + # Weekday ############################################################# + # + # + # +# +# From man 5 crontab: +# Note: The day of a command's execution can be specified by two fields +# -- day of month, and day of week. If both fields are restricted +# (ie, aren't *), the command will be run when either field matches the +# current time. For example, "30 4 1,15 * 5" would cause a command to +# be run at 4:30 am on the 1st and 15th of each month, plus every Friday. +# +# [But if both fields ARE *, then it just means "every day". +# and if one but not both are *, then ignore the *'d one -- +# so "1 2 3 4 *" means just 2:01, April 3rd +# and "1 2 * 4 5" means just 2:01, on every Friday in April +# But "1 2 3 4 5" means 2:01 of every 3rd or Friday in April. ] +# + # + # + # + # And that's a bit tricky. + + if($bits[4][0][0] eq '*' and ( + @{$bits[4][0]} == 1 or $bits[4][0][1] == 1 + ) + ) { + # Most common case -- any weekday. Do nothing really. + # + # Hm, does "*/1" really mean "*" here, given the above note? + # + + # Tidy things up while we're here: + if($time_lines[-2] eq "every day of" and + $time_lines[-1] eq 'every month' + ) { + $time_lines[-2] = "every day"; + pop @time_lines; + } + + } else { + # Ugh, there's some restriction on weekdays. + + # Translate the DOW-expression + my $expression; + my @hunks; + foreach my $bit (@{$bits[4]}) { + if(@$bit == 1) { + push @hunks, $num2dow_long{$bit->[0]} || "DOW_$bit->[0]??"; + } elsif(@$bit == 2) { + if($bit->[0] eq '*') { # it's like */3 + #push @hunks, sprintf "every %s day of the week", freq($bit->[1]); + # the above was ambiguous -- "every third day of the week" + # sounds synonymous with just "3" + if($bit->[1] eq 2) { + # common and unambiguous case. + push @hunks, "every other day of the week"; + } else { + # rare cases: N > 2 + push @hunks, "every $bit->[1] days of the week"; + # sounds clunky, but it's a clunky concept + } + } else { + # it's like "7-9" + push @hunks, sprintf "%s through %s", + $num2dow_long{$bit->[0]} || "DOW_$bit->[0]??", + $num2dow_long{$bit->[1]} || "DOW_$bit->[1]??", + } + } elsif(@$bit == 3) { # "7-20/2" + push @hunks, sprintf "every %s %s from %s through %s", + ordinate_soft($bit->[2]), #$bit->[2], + 'day', #$bit->[2] == 1 ? 'days' : 'days', + $num2dow_long{$bit->[0]} || "DOW_$bit->[0]??", + $num2dow_long{$bit->[1]} || "DOW_$bit->[1]??", + } + } + $expression = conj_or(@hunks); + + # Now figure where to put it... + # + if($time_lines[-2] eq "every day of") { + # Unrestricted day-of-month, hooray. + # + if($time_lines[-1] eq 'every month') { + # change it to "every Tuesday", killing the "of every month". + $time_lines[-2] = "every $expression"; + $time_lines[-2] =~ s/every every /every /g; + pop @time_lines; + } else { + # change it to "every Tuesday in" + $time_lines[-2] = "every $expression in"; + $time_lines[-2] =~ s/every every /every /g; + } + } else { + # This is the messy case where there's a DOM and DOW + # restriction + + # Was, wrongly: + # $time_lines[-1] .= ','; + # push @time_lines, "if it's also " . $expression; + + $time_lines[-2] .= " -- or every $expression in --"; + # Yes, dashes look very strange, but then this is a very + # rare case. + $time_lines[-2] =~ s/every every /every /g; + } + } + ####################################################################### + } + # TODO: change "3pm" -> "the 3pm hour" or something? + $time_lines[-1] =~ s/ of$//s; + + return @time_lines; +} + +########################################################################### +# Just utility routines below here. + +my %pretty_form; +BEGIN { + %pretty_form = ( '"' => '\"', '\\' => '\\\\', ); +} + +sub esc { + my $x = $_[0]; + $x =~ + #s<([^\x20\x21\x23\x27-\x3F\x41-\x5B\x5D-\x7E])> + s<([\x00-\x1F"\\])> + <$pretty_form{$1} || '\\x'.(unpack("H2",$1))>eg; + return $x; +} + +#-------------------------------------------------------------------------- + +# if($time_lines[-1] =~ m/every|from/) { +# $time_lines[-1] =~ tr/,/;/; +# s/ (and|or)\b/\; $1/g; +# } + +sub conj_and { + if(grep m/every|from/, @_) { + # put in semicolons in the case of complex constituency + return join('; and ', @_) if @_ < 2; + my $last = pop @_; + return join('; ', @_) . '; and ' . $last; + } + + return join(' and ', @_) if @_ < 3; + my $last = pop @_; + return join(', ', @_) . ', and ' . $last; +} + +sub conj_or { + if(grep m/every|from/, @_) { + # put in semicolons in the case of complex constituency + return join('; or ', @_) if @_ < 2; + my $last = pop @_; + return join('; ', @_) . '; or ' . $last; + } + + return join(' or ', @_) if @_ < 3; + my $last = pop @_; + return join(', ', @_) . ', or ' . $last; +} + +#-------------------------------------------------------------------------- + +my %ordinations; +BEGIN { + # English-language overrides for common ordinals + %ordinations = qw( + 1 first 2 second 3 third 4 fourth 5 fifth 6 sixth + 7 seventh 8 eighth 9 ninth 10 tenth + ); + # 11 eleventh 12 twelfth + # 13 thirteenth 14 fourteen 15 fifteenth 16 sixteenth + # 17 seventeenth 18 eighteenth 19 nineteenth 20 twentieth +} + +sub ordsuf { + return 'th' if not(defined($_[0])) or not( 0 + $_[0] ); + # 'th' for undef, 0, or anything non-number. + my $n = abs($_[0]); # Throw away the sign. + return 'th' unless $n == int($n); # Best possible, I guess. + $n %= 100; + return 'th' if $n == 11 or $n == 12 or $n == 13; + $n %= 10; + return 'st' if $n == 1; + return 'nd' if $n == 2; + return 'rd' if $n == 3; + return 'th'; +} + +sub ordinate { + my $i = $_[0] || 0; + $ordinations{$i} || ($i . ordsuf($i)); +} + +sub freq { + # frequentive form. Like ordinal, except that 2 -> 'other' + # (as in every other) + my $i = $_[0] || 0; + return 'other' if $i == 2; # special case + $ordinations{$i} || ($i . ordsuf($i)); +} + +sub ordinate_soft { + my $i = $_[0] || 0; + $i . ordsuf($i); +} + +#-------------------------------------------------------------------------- + +sub percent_proc { + # Translated literally from the C, cron/do_command.c. + my($esc,$need_newline); + my $out = ''; + my $c; + for(my $i = 0; $i < length($_[0]); $i++) { + $c = substr($_[0],$i,1); + if($esc) { + $out .= "\\" unless $c eq '%'; + } else { + $c = "\n" if $c eq '%'; + } + unless($esc = ($c eq "\\")) { + # For unescaped characters, + $out .= $c; + $need_newline = ($c ne "\n"); + } + } + $out .= "\\" if $esc; + $out .= "\n" if $need_newline; + return $out; + + # I think this would do the same thing: + # $x =~ s/((?:\\\\)+) | (\\\%) | (\%) /$3 ? "\n" : $2 ? '%' : $1/xeg; + # But I don't want to think about it, and I need it to work just + # as the original does. +} + +#-------------------------------------------------------------------------- + +__END__ + +# Test data for crontab parsing: + + MAILTO=hulahoops@polygon.int +10 8 15 * * /bin/csh -c 'perl ~/thang.pl | mail -s hujambo root' + +40 5 * * * df -k + + 50 6 * * 1 ls -l /tmp + +1 2 * apr mOn foo + + +foo=bar + foo=bar + foo = "bar" + foo="bar" + foo = 'bar' + foo='bar' + + 1 2 3 4 7 Foo +1-20/3 * * * * Foo + 1,2,3 * * * * Foo +1-9,15-30 * * * * Foo + 1-9/3,15-30/4 * * * * Foo + + 1 2 3 jan mon stuff + 1 2 3 4 mON stuff + 1 2 3 jan 5 stuff + + @reboot xxstartuply foo + @yearly xxx yearlijk%thig%hooboy + @annually xxannuallijk heehoo + @monthly xxx monthlijk + @weekly xXxX weeklijk + @daily xxxdaylijk + @midnight xxxmidnightlijk + @hourly xXxXhourlijk + + +*/3 * * * * stuff + + +#End, really. --- cron-3.0pl1.orig/debian/examples/cron-stats.pl +++ cron-3.0pl1/debian/examples/cron-stats.pl @@ -0,0 +1,390 @@ +#!/usr/bin/perl -w +# +# Generate stats of Cron messages sent to syslog +# +# +# This script can be used to generate per-user and per-task +# statistics in order to determine how much time do cron tasks take +# and audit cron use. It might be useful to debug issues related +# to cron tasks that take too much time to run, or overlap, or get executed +# too fast.. +# +# Hint: In some systems, cron tasks might not properly use lockfiles to +# prevent themselves from being run twice in a row and some "strange" +# things might happen if the system is overloaded and a cron task takes +# more than expected. And, no, this is not something that Cron should +# prevent (for more information see Debian bug #194805). +# +# In order for this script to work +# +# together with the logging enhancements included +# in Debian's vixie cron (3.0pl1-95) that make it possible to log +# when do cron tasks end. +# +# +# How to use: +# - Modify /etc/init.d/cron so that the calls to cron pass the '-L 2' +# argument +# (Hint: change lines 27 and 47 so that they end with '-- -L 2 $LSBNAMES' +# instead of '-- $LSBNAMES') +# In Debian you need cron-3.0pl1-95 or later. +# Note: future versions of cron might use an /etc/default/cron file +# to provide arguments to cron. +# +# - Review /etc/syslog.conf to determine where does the output of the cron +# facility goes to (might be /var/log/syslog together with other messages +# but you can also configure syslog to store them at /var/log/cron.log) +# +# - Run the script (by default it will read input /var/log/syslog but you +# can point to other file using the '-f' switch) and review the output +# +# - (Optionally) If you want to analyse log files for a long period +# concatenate different log files, extract all CRON related entries +# to a file and process that. +# +# You could do, for example, this: +# +# zcat -f /var/log/syslog* | grep CRON >cron-log ; \ +# perl stats-cron.pl -f cron-log +# +# +# This program is copyright 2006 by Javier Fernandez-Sanguino +# +# 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 +# +# For more information please see +# http://www.gnu.org/licenses/licenses.html#GPL + +# TODO: +# - Print time internal of file analysis (from 'XXX' to 'XXX') based on the +# first and the last log entry read. +# +# - Detect overlaped entries for the same task (start of an entry before the +# previous one finished) +# +# - Make it possible to filter by users +# +# - Consider adapting to other log formats (other cron programs? Solaris?) by +# separating analysis and extraction in the code and making it possible +# to switch to different analysis methods. + + +# Required modules +eval 'use Parse::Syslog'; +if ($@) { + print STDERR "ERROR: Parse::Syslog not installed, please install this Perl library\n"; + print STDERR "ERROR: (in Debian it is provided by the 'libparse-syslog-perl' package)\n"; + exit 1; +} +use Parse::Syslog; +use Getopt::Std; +# Parse command line +getopts('dhvf:'); +$opt_d = 0 if ! defined ($opt_d) ; + +if ( $opt_h ) { + print "Usage: $ARGV[0] [-dhv] [-f syslog file]"; + exit 1; +} + +# Note: in Debian's default syslog configuration log messages are +# sent to /var/log/syslog but you might have edited it to use +# /var/log/cron.log instead. Which, BTW, is more efficient because +# we do not need to parse the syslog to extract only cron messages. +my $LOGFILE = $opt_f || "/var/log/syslog"; + +# Sanity checks +if ( ! -r $LOGFILE ) { + print STDERR "ERROR: Cannot read logfile $LOGFILE"; +} + +my $parser = Parse::Syslog->new( $LOGFILE); +print STDERR "Starting parse of file $LOGFILE\n" if $opt_v; +while(my $log = $parser->next) { + if ( $log->{program} =~ /CRON/ ) { + print STDERR "DEBUG: Found syslog entry: $log->{pid}\n" if $opt_d; +# We extract the entries with the same text but with 'CMD' (start) +# and 'END' (end) to check if it's the same we use the PID reported +# by cron. + if ($log->{text} =~ /CMD \(/) { + my $pid; + if ($log->{text} =~ /\(\[(\d+)\]/ ) { + # Debian - with loglevel 8, the child process id is + # reported + $pid = $1; + } else { + # All the other cases, the process ID of the + # child process is the same one as the log entry + $pid = $log->{pid}; + } + add_entry($log->{timestamp},$log->{text},$pid, $log->{pid}) + } + if ($log->{text} =~ /END \(/) { + my $pid; + if ($log->{text} =~ /\(\[(\d+)\]/ ) { + # Debian - with loglevel 8 + $pid = $1; + } else { + # Other cases + $pid = "UNKNOWN"; + } + clear_entry($log->{timestamp},$log->{text},$pid,$log->{pid}); + } + } +} +print STDERR "Finished parse of file $LOGFILE\n" if $opt_v; + + +# Analysis, we find all entries with a start and stop timestamp +# and print them +generate_analysis(); + +# Stats: +# - average time of execution of cron jobs +# - cronjob which executed slowest +# - cronjob which executed fastes +# - cronjobs which start before they finish +print_stats(); + +exit 0; + +sub find_user { +# Extract a user from a cron entry + my ($text) = @_; + my $user = ""; + if ( $text =~ /^\((.*?)\)/ ) { + $user = $1; + } + return $user; +} + +sub find_prg { +# Extract a program from a cron entry + my ($text) = @_; + my $prg = ""; + if ( $text =~ /(CM|EN)D\s*\((.*)\s*\)\s*$/ ) { + $prg = $2; + $prg =~ s/^\[\d+\]\s+//; # Strip off child process numbers + # (Debian loglevel 8) + } + return $prg; +} + +sub add_entry { +# Get a syslog entry and add it to the table of entries for analysis + my ($time, $text, $pid, $ppid) = @_; + my ($user, $prg); + print STDERR "DEBUG: Entering add_entry with @_\n" if $opt_d; + $user = find_user($text); + if ( ! defined ($user) ) { + print STDERR "WARN: Cannot find user for cron job (pid $pid)\n"; + return; + } + $prg = find_prg($text); + if ( ! defined ($prg) ) { + print STDERR "WARN: Cannot find program for cron job (pid $pid)\n"; + return; + } + my $value = $pid."-".$time; + print STDERR "DEBUG: Adding cronjob of user $user to list: $prg\n" if $opt_d; + add_cron_entry($user, $prg, $value, $ppid); + $cron_entries{$user}{$prg}{'count'}++; + return; +} + +sub clear_entry { +# Get a syslog entry, find the start entry and add the timestamp difference +# to the list of timestamps for that user +# +# There's two ways to match entries: +# - (in Debian, using loglevel 8): By matching the child process ID +# between the entries +# - in all the other cases: By matching the process ID of the +# END message (which belongs to the parent) with the process IF +# of the CMD message (which belongs to the child) + my ($timestamp, $text, $pid, $ppid) = @_; + my ($user, $prg, $entry, $count); + my @array; my @stack; + print STDERR "DEBUG: Entering clear_entry with @_\n" if $opt_d; + $user = find_user($text); + if ( $user eq "" ) { + print STDERR "WARN: Cannot find user for cron job (pid $pid)\n"; + return; + } + $prg = find_prg($text); + if ( $prg eq "" ) { + print STDERR "WARN: Cannot find program for cron job (pid $pid)\n"; + return; + } + if ( ! defined ( $cron_entries{$user}{$prg}{'item'} ) ) { + print STDERR "WARN: End entry without start entry (pid $pid)\n"; + return; + } + @array = split(':', $cron_entries{$user}{$prg}{'item'}); + $count = $#array + 1 ; + print STDERR "DEBUG: $count cron entries for $user, task '$prg'\n" if $opt_d; + my $finish = 0; + while ( $finish == 0 ) { + $entry = pop @array; + last if ! defined ($entry) ; + # Pop all entries and look for one whose pid matches + my ($spid, $stimestamp) = split ("-", $entry); + print STDERR "DEBUG: Comparing entry $spid to $pid\n" if $opt_d; + if ( ( $pid ne "UNKNOWN" && $pid == $spid ) || $ppid-$spid == 1 ) { + my $timediff = $timestamp-$stimestamp; + $timediff = 0 if $timediff < 0; + print STDERR "DEBUG: Entries match, time difference of $timediff\n" if $opt_d ; + $cron_entries{$user}{$prg}{'finished'}++; + if (defined ( $cron_times{$user}{$prg} ) ) { + $cron_times{$user}{$prg} = join(":", $cron_times{$user}{$prg}, $timediff); + } else { + $cron_times{$user}{$prg} = $timediff; + } + $finish = 1; + } else { + print STDERR "DEBUG: Pushing entry $spid into stack\n" if $opt_d; + push @stack, $entry; + } + } + # Push all remaining entries to the stack + $cron_entries{$user}{$prg}{'item'}=""; + $count = $#array + 1 ; + if ($opt_d) { + print STDERR "DEBUG: Restoring all entries (size: array $count"; + $count = $#stack + 1 ; + print STDERR ", stack $count)\n"; + } + print STDERR "DEBUG: Total finished tasks: $cron_entries{$user}{$prg}{'finished'} out of $cron_entries{$user}{$prg}{'count'}\n" if defined $cron_entries{$user}{$prg}{'finished'} && $opt_d; + print STDERR "DEBUG: Unmatched now: $cron_entries{$user}{$prg}{'item'}\n" if $opt_d; + while ( $entry = pop @array ) { + add_cron_entry($user, $prg, $entry); + } + while ( $entry = pop @stack ) { + add_cron_entry($user, $prg, $entry); + } + print STDERR "DEBUG: Finish execution of clear_entry\n" if $opt_d; + return; +} + +sub add_cron_entry { + my ($user, $prg, $entry) = @_; + if ( defined ($cron_entries{$user}{$prg}) && $cron_entries{$user}{$prg} ne "" ) { + $cron_entries{$user}{$prg}{'item'} = join(":", $cron_entries{$user}{$prg}{'item'}, $entry); + } else { + $cron_entries{$user}{$prg}{'item'} = $entry; + $cron_entries{$user}{$prg}{'count'} = 0; + $cron_entries{$user}{$prg}{'finished'} = 0; + $cron_entries{$user}{$prg}{'unmatched'} = 0 ; + $cron_entries{$user}{$prg}{'average'} = 0 ; + $cron_entries{$user}{$prg}{'minimum'} = 0 ; + $cron_entries{$user}{$prg}{'maximum'} = 0; + } +} + +sub count_unmatched { + my ($user, $prg) = @_; + my ($count, @array); + return 0 if ! defined ( $cron_entries{$user}{$prg}{'item'} ); + @array = split(':', $cron_entries{$user}{$prg}{'item'}); + $count = $#array + 1 ; + return $count; +} + +sub find_average { + my ($user, $prg) = @_; + my ($average, $count, $total, @array, $entry); + $total = 0 ; + return -1 if ! defined ( $cron_times{$user}{$prg} ); + @array = split(':', $cron_times{$user}{$prg}); + $count = $#array + 1 ; + while ( defined ( $entry = pop @array ) ) { + $total += $entry; + } + $average = $total / $count; + return $average; +} + +sub find_minimum { + my ($user, $prg) = @_; + my ($minimum, @array, $entry); + $minimum = -1; + return -1 if ! defined ( $cron_times{$user}{$prg} ); + @array = split(':', $cron_times{$user}{$prg}); + while ( defined ( $entry = pop @array ) ) { + if ( $minimum == -1 ) { + $minimum = $entry; + } else { + $minimum = $entry if $entry < $minimum; + } + } + return $minimum; +} + +sub find_maximum { + my ($user, $prg) = @_; + my ($maximum, @array); + $maximum = -1; + return -1 if ! defined ( $cron_times{$user}{$prg} ); + @array = split(':', $cron_times{$user}{$prg}); + while ( defined ( $entry = pop @array ) ) { + if ( $maximum == -1 ) { + $maximum = $entry ; + } else { + $maximum = $entry if $entry > $maximum; + } + } + return $maximum; +} + + + +sub generate_analysis { +# For each user and program calculate the average time for the task + my ($user, $prg); + foreach $user (keys %cron_entries) { + print STDERR "DEBUG: Calculating data for user '$user'\n" if $opt_d; + foreach my $prg ( keys %{$cron_entries{$user}} ) { + print STDERR "DEBUG: Calculating data for task '$prg'\n" if $opt_d; + my $unmatched = count_unmatched($user, $prg); + my $average = find_average($user, $prg); + my $minimum = find_minimum($user, $prg); + my $maximum = find_maximum($user, $prg); + $cron_entries{$user}{$prg}{'unmatched'} = $unmatched; + $cron_entries{$user}{$prg}{'average'} = $average; + $cron_entries{$user}{$prg}{'minimum'} = $minimum; + $cron_entries{$user}{$prg}{'maximum'} = $maximum; + } + } +} + +sub print_stats { +# Print information of cron statistics + my ($user, $prg); + foreach $user (keys %cron_entries) { + print "Cron statistics for user '$user'\n"; + foreach $prg ( keys %{$cron_entries{$user}} ) { + print "\tTask: '$prg'\n"; + print "\t\tDEBUG: $cron_times{$user}{$prg}\n" if $opt_d; + print <