diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1dc3934 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +# Build dcron +FROM alpine AS builder +COPY . /src +RUN apk add --no-cache gcc make musl-dev \ + && cd /src \ + && make \ + && make install + +# Build image +FROM alpine + +# Alpine includes a copy of dcron as part of busybox. +# Delete the /var/spool/cron dir and then recreate it. +RUN rm -rf /var/spool/cron \ +# These are copied from the Makefiles + && install -o root -d -m0755 -g root /var/spool/cron \ + && install -o root -d -m0755 -g root /etc/cron.d + +# Copy crond and crontab +COPY --from=builder /usr/local/sbin/crond /usr/local/sbin/crond +COPY --from=builder /usr/local/bin/crontab /usr/local/bin/crontab + +# Start dcron +ENTRYPOINT ["/usr/local/sbin/crond", "-C", "-f", "-L", "-"] +CMD [] + +# Mark /var/spool/cron as a volume so cron settings are persisted there +VOLUME /var/spool/cron + diff --git a/README b/README index 079a814..da129f2 100644 --- a/README +++ b/README @@ -71,7 +71,7 @@ wanting to edit defs.h directly, try editing the DEFS line in the Makefile inste make PREFIX=/usr CRONTAB_GROUP=users (3) If you're using the git version, you might also want to `make man`, -to be sure the manpages are updated. This requires +to be sure the manpages are updated. This requires [pandoc](http://johnmacfarlane.net/pandoc/). @@ -117,6 +117,30 @@ Here is the superuser's crontab, created using `sudo crontab -e`: -rw------- 0 root root 513 Jan 6 18:58 /var/spool/cron/crontabs/root +DOCKER +------ + +Using `dcron` with docker is rather straight forward. + +To build the base image use: + + docker build -t dcron dcron-source-dir + +To add on any additional scripts: + + cd your-project-dir + echo dcron.dockerfile <cf_UserName, line->cl_JobName); /* we continue checking other timestamps in this CronFile */ diff --git a/defs.h b/defs.h index 5903e6e..ba1c65c 100644 --- a/defs.h +++ b/defs.h @@ -117,7 +117,7 @@ #define LOCALE_LOGHEADER "%c %%s " LOG_IDENT ": " /* Limits */ -#define MAXOPEN 256 /* close fds < this limit */ +#define MAXOPEN 256 /* close fds < this limit */ #define MAXLINES 256 /* max lines in non-root crontabs */ #define SMALL_BUFFER 256 #define RW_BUFFER 1024 @@ -169,5 +169,7 @@ typedef struct CronNotifier { struct CronWaiter *cn_Waiter; } CronNotifier; +extern short Quit; + #include "protos.h" diff --git a/main.c b/main.c index 1fc5cd6..f4c34d0 100644 --- a/main.c +++ b/main.c @@ -28,12 +28,14 @@ Prototype const char *Mailto; Prototype char *TempDir; Prototype char *TempFileFmt; +short Quit = 0; short DebugOpt = 0; short LogLevel = LOG_LEVEL; short ForegroundOpt = 0; short SyslogOpt = 1; -const char *CDir = CRONTABS; -const char *SCDir = SCRONTABS; +short CreateCronDirsOpt = 0; +const char *CDir = CRONTABS; +const char *SCDir = SCRONTABS; const char *TSDir = CRONSTAMPS; const char *LogFile = NULL; /* opened with mode 0600 */ const char *LogHeader = LOGHEADER; @@ -72,7 +74,7 @@ main(int ac, char **av) opterr = 0; - while ((i = getopt(ac,av,"dl:L:fbSc:s:m:M:t:")) != -1) { + while ((i = getopt(ac,av,"dl:L:fbSc:s:m:M:t:C")) != -1) { switch (i) { case 'l': { @@ -159,25 +161,53 @@ main(int ac, char **av) if (*optarg != 0) SendMail = optarg; break; case 'm': - if (*optarg != 0) Mailto = optarg; + if (*optarg == 0) break; + + for (const char *c = optarg; *c != 0; ++c) { + if (*c == '@') { + Mailto = optarg; + break; + } + } + + if (Mailto == NULL && *optarg == '/') { + char *buffer = malloc(256); + FILE* file = fopen(optarg, "r"); + if (file) { + size_t s = fread(buffer, 1, 256, file); + if (s < 1) { + free(buffer); + } else { + buffer[s] = 0; + Mailto = buffer; + } + fclose(file); + } + } + break; + case 'C': + CreateCronDirsOpt = 1; break; + default: /* * check for parse error */ printf("dillon's cron daemon " VERSION "\n"); - printf("crond [-s dir] [-c dir] [-t dir] [-m user@host] [-M mailer] [-S|-L [file]] [-l level] [-b|-f|-d]\n"); - printf("-s directory of system crontabs (defaults to %s)\n", SCRONTABS); - printf("-c directory of per-user crontabs (defaults to %s)\n", CRONTABS); - printf("-t directory of timestamps (defaults to %s)\n", CRONSTAMPS); - printf("-m user@host where should cron output be directed? (defaults to local user)\n"); - printf("-M mailer (defaults to %s)\n", SENDMAIL); - printf("-S log to syslog using identity '%s' (default)\n", LOG_IDENT); - printf("-L file log to specified file instead of syslog\n"); - printf("-l loglevel log events <= this level (defaults to %s (level %d))\n", LevelAry[LOG_LEVEL], LOG_LEVEL); - printf("-b run in background (default)\n"); - printf("-f run in foreground\n"); - printf("-d run in debugging mode\n"); + printf("crond [-s dir] [-c dir] [-t dir] [-m user@host|file] [-M mailer] [-S|-L [file]] [-l level] [-b|-f|-d]\n"); + printf("-s directory of system crontabs (defaults to %s)\n", SCRONTABS); + printf("-c directory of per-user crontabs (defaults to %s)\n", CRONTABS); + printf("-t directory of timestamps (defaults to %s)\n", CRONSTAMPS); + printf("-C create per-user crontabs and timestamps directories, if they do not exists\n"); + printf("-m user@host|file where should cron output be directed? (defaults to local user)\n"); + printf(" if you specify a file, the email address will be read from the file.\n"); + printf("-M mailer (defaults to %s)\n", SENDMAIL); + printf("-S log to syslog using identity '%s' (default)\n", LOG_IDENT); + printf("-L file log to specified file instead of syslog\n"); + printf("-l loglevel log events <= this level (defaults to %s (level %d))\n", LevelAry[LOG_LEVEL], LOG_LEVEL); + printf("-b run in background (default)\n"); + printf("-f run in foreground\n"); + printf("-d run in debugging mode\n"); exit(2); } } @@ -215,6 +245,19 @@ main(int ac, char **av) exit(1); } + if (CreateCronDirsOpt) { + if (mkdir(CDir, 0755) == -1 && errno != EEXIST) { + fdprintf(2, "failed to create %s", CDir); + perror(": "); + exit(1); + } + if (mkdir(TSDir, 0755) == -1 && errno != EEXIST) { + fdprintf(2, "failed to create %s", TSDir); + perror(": "); + exit(1); + } + } + if (ForegroundOpt == 0) { int fd; @@ -269,11 +312,12 @@ main(int ac, char **av) /* daemon in foreground */ /* stay in existing session, but start a new process group */ - if (setpgid(0,0)) { - perror("setpgid"); - exit(1); + if (getsid(0) != getpid()) { + if (setpgid(0,0)) { + perror("setpgid"); + exit(1); + } } - /* stderr stays open, start SIGHUP ignoring, SIGCHLD handling */ initsignals(); } @@ -305,6 +349,11 @@ main(int ac, char **av) for (;;) { sleep((stime + 1) - (short)(time(NULL) % stime)); + if (Quit) { + fdprintf(2, "\n"); // print new line in case of Ctrl-C + break; + } + t2 = time(NULL); dt = t2 - t1; @@ -338,7 +387,7 @@ main(int ac, char **av) SynchronizeDir(SCDir, "root", 0); ReadTimestamps(NULL); } - } + } if (rescan < 60) { CheckUpdates(CDir, NULL, t1, t2); CheckUpdates(SCDir, "root", t1, t2); @@ -360,6 +409,5 @@ main(int ac, char **av) } } } - /* not reached */ } diff --git a/subs.c b/subs.c index 65cb564..4e09c2b 100644 --- a/subs.c +++ b/subs.c @@ -138,6 +138,10 @@ void waitmailjob(int sig) { /* if all children still running, child == 0 */ } +void quit(int sig) { + Quit = 1; +} + void initsignals (void) { struct sigaction sa; @@ -157,6 +161,7 @@ initsignals (void) { fdprintf(2, "failed to start SIGHUP handling, reason: %s", strerror(errno)); exit(n); } + sa.sa_flags = SA_RESTART; sa.sa_handler = waitmailjob; if (sigaction (SIGCHLD, &sa, NULL) != 0) { @@ -165,5 +170,27 @@ initsignals (void) { exit(n); } -} + sa.sa_flags = SA_RESTART; + sa.sa_handler = quit; + if (sigaction (SIGINT, &sa, NULL) != 0) { + n = errno; + fdprintf(2, "failed to start SIGINT handling, reason: %s", strerror(errno)); + exit(n); + } + + sa.sa_flags = SA_RESTART; + sa.sa_handler = quit; + if (sigaction (SIGTERM, &sa, NULL) != 0) { + n = errno; + fdprintf(2, "failed to start SIGTERM handling, reason: %s", strerror(errno)); + exit(n); + } + sa.sa_flags = SA_RESTART; + sa.sa_handler = quit; + if (sigaction (SIGQUIT, &sa, NULL) != 0) { + n = errno; + fdprintf(2, "failed to start SIGQUIT handling, reason: %s", strerror(errno)); + exit(n); + } +}