From 042e5b9437dcc3842fe35ace7a8e7e63e6c34b9f Mon Sep 17 00:00:00 2001 From: Dave Kok <59438632+DaveBRH@users.noreply.github.com> Date: Thu, 23 Jan 2020 16:33:27 +0100 Subject: [PATCH 1/9] Fixes perm issue with -f --- main.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/main.c b/main.c index 1fc5cd6..bd2c36e 100644 --- a/main.c +++ b/main.c @@ -269,11 +269,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(); } From 3b5d0685e5b71e805269a4a4a079a17adfbdc56f Mon Sep 17 00:00:00 2001 From: Dave Kok <59438632+DaveBRH@users.noreply.github.com> Date: Thu, 23 Jan 2020 16:37:01 +0100 Subject: [PATCH 2/9] readability issue --- database.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/database.c b/database.c index 270ab9b..99e59c3 100644 --- a/database.c +++ b/database.c @@ -265,11 +265,12 @@ ReadTimestamps(const char *user) } sec = (time_t)-1; ptr = strptime(ptr, CRONSTAMP_FMT, &tm); - if (ptr && (*ptr == 0 || *ptr == '\n')) + if (ptr && (*ptr == 0 || *ptr == '\n')) { /* strptime uses current seconds when seconds not specified? anyway, we don't get round minutes */ tm.tm_sec = 0; - tm.tm_isdst = -1; - sec = mktime(&tm); + } + tm.tm_isdst = -1; + sec = mktime(&tm); if (sec == (time_t)-1) { printlogf(LOG_ERR, "unable to parse timestamp (user %s job %s)\n", file->cf_UserName, line->cl_JobName); /* we continue checking other timestamps in this CronFile */ From 4ff41f0a193156b8fcc71275103e891c672c6788 Mon Sep 17 00:00:00 2001 From: Dave Kok <59438632+DaveBRH@users.noreply.github.com> Date: Thu, 6 Feb 2020 15:55:25 +0100 Subject: [PATCH 3/9] m option now also support a config file --- main.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/main.c b/main.c index bd2c36e..f4283df 100644 --- a/main.c +++ b/main.c @@ -159,25 +159,44 @@ 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) { + Mailto = malloc(256); + FILE* file = fopen(optarg, "r"); + if (file) { + if (fread(Mailto, 1, 256, file) < 1) { + free(Mailto); + Mailto = NULL; + } + fclose(file); + } + } 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("-m user@host|file where should cron output be directed?\n"); + printf(" if you specify a file it contents is grepped.\n"); + printf(" (defaults to local user)\n"); + printf("-M mailer (defaults to %s)\n", SENDMAIL); + printf("-S log to syslog using identity '%s' (default)\n", LOG_IDENT); + printf("-L file log to specified file instead of syslog\n"); + printf("-l loglevel log events <= this level (defaults to %s (level %d))\n", LevelAry[LOG_LEVEL], LOG_LEVEL); + printf("-b run in background (default)\n"); + printf("-f run in foreground\n"); + printf("-d run in debugging mode\n"); exit(2); } } From 01f84681bb32ce23a8f9b1621f8ed6c45b0cea91 Mon Sep 17 00:00:00 2001 From: Dave Kok <59438632+DaveBRH@users.noreply.github.com> Date: Fri, 7 Feb 2020 07:42:58 +0100 Subject: [PATCH 4/9] added null byte --- main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index f4283df..1b27abd 100644 --- a/main.c +++ b/main.c @@ -170,9 +170,12 @@ main(int ac, char **av) Mailto = malloc(256); FILE* file = fopen(optarg, "r"); if (file) { - if (fread(Mailto, 1, 256, file) < 1) { + size_t s = fread(Mailto, 1, 256, file); + if (s < 1) { free(Mailto); Mailto = NULL; + } else { + Mailto[s] = 0; } fclose(file); } From 6f1569dfb277b63d81c6263716615384915ca680 Mon Sep 17 00:00:00 2001 From: Dave Kok Date: Fri, 7 Feb 2020 07:36:03 +0000 Subject: [PATCH 5/9] Docker support/extended -m option to read email address from file --- Dockerfile | 30 ++++++++++++++++++++++++++++++ docker-entrypoint.sh | 13 +++++++++++++ main.c | 16 ++++++++-------- 3 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 Dockerfile create mode 100755 docker-entrypoint.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b7fafce --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +# 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 + +RUN apk add --no-cache tini \ + && rm -rf /var/spool/cron \ + && install -o root -d -m0755 -g root /var/spool/cron \ + && install -o root -d -m0755 -g root /etc/cron.d + +# Copy dcron +COPY --from=builder /usr/local/sbin/crond /usr/local/sbin/crond +COPY --from=builder /usr/local/bin/crontab /usr/local/bin/crontab +COPY /docker-entrypoint.sh /docker-entrypoint.sh + +# The entrypoint makes sure the cron directories exists. +ENTRYPOINT ["/docker-entrypoint.sh"] + +# Start dcron +CMD ["/usr/local/sbin/crond", "-f", "-L", "-", "-l", "info"] + +# Mark /etc/crontab as a volume so cron settings are saved +VOLUME /var/spool/cron + diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..1edd930 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +if [[ ! -d "/var/spool/cron/crontabs" ]] +then + install -o root -d -m0755 -g root /var/spool/cron/crontabs +fi + +if [[ ! -d "/var/spool/cron/cronstamps" ]] +then + install -o root -d -m0755 -g root /var/spool/cron/cronstamps +fi + +exec tini $* diff --git a/main.c b/main.c index 1b27abd..f9a1fde 100644 --- a/main.c +++ b/main.c @@ -166,16 +166,16 @@ main(int ac, char **av) break; } } - if (Mailto != NULL) { - Mailto = malloc(256); + if (Mailto == NULL) { + char *buffer = malloc(256); FILE* file = fopen(optarg, "r"); if (file) { - size_t s = fread(Mailto, 1, 256, file); + size_t s = fread(buffer, 1, 256, file); if (s < 1) { - free(Mailto); - Mailto = NULL; + free(buffer); } else { - Mailto[s] = 0; + buffer[s] = 0; + Mailto = buffer; } fclose(file); } @@ -296,7 +296,7 @@ main(int ac, char **av) perror("setpgid"); exit(1); } - } + } /* stderr stays open, start SIGHUP ignoring, SIGCHLD handling */ initsignals(); } @@ -361,7 +361,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); From 3f91923ccf27e77d11480c4e092ddcc464486968 Mon Sep 17 00:00:00 2001 From: Dave Kok Date: Sun, 9 Feb 2020 11:15:35 +0000 Subject: [PATCH 6/9] modifications to make dcron more Docker friendly --- Dockerfile | 17 +++++++++-------- defs.h | 4 +++- docker-entrypoint.sh | 13 ------------- main.c | 41 +++++++++++++++++++++++++++++++++-------- subs.c | 29 ++++++++++++++++++++++++++++- 5 files changed, 73 insertions(+), 31 deletions(-) delete mode 100755 docker-entrypoint.sh diff --git a/Dockerfile b/Dockerfile index b7fafce..ea433cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,22 +9,23 @@ RUN apk add --no-cache gcc make musl-dev \ # Build image FROM alpine -RUN apk add --no-cache tini \ - && rm -rf /var/spool/cron \ +# 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 dcron +# Copy dcron, crontab and the docker-entrypoint.sh COPY --from=builder /usr/local/sbin/crond /usr/local/sbin/crond COPY --from=builder /usr/local/bin/crontab /usr/local/bin/crontab -COPY /docker-entrypoint.sh /docker-entrypoint.sh # The entrypoint makes sure the cron directories exists. -ENTRYPOINT ["/docker-entrypoint.sh"] +ENTRYPOINT ["/usr/local/sbin/crond"] -# Start dcron -CMD ["/usr/local/sbin/crond", "-f", "-L", "-", "-l", "info"] +# Default options +CMD ["-C", "-f", "-L", "-"] -# Mark /etc/crontab as a volume so cron settings are saved +# Mark /var/spool/cron as a volume so cron settings are persisted there VOLUME /var/spool/cron 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/docker-entrypoint.sh b/docker-entrypoint.sh deleted file mode 100755 index 1edd930..0000000 --- a/docker-entrypoint.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -if [[ ! -d "/var/spool/cron/crontabs" ]] -then - install -o root -d -m0755 -g root /var/spool/cron/crontabs -fi - -if [[ ! -d "/var/spool/cron/cronstamps" ]] -then - install -o root -d -m0755 -g root /var/spool/cron/cronstamps -fi - -exec tini $* diff --git a/main.c b/main.c index f9a1fde..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': { @@ -160,13 +162,15 @@ main(int ac, char **av) break; case 'm': if (*optarg == 0) break; + for (const char *c = optarg; *c != 0; ++c) { if (*c == '@') { Mailto = optarg; break; } } - if (Mailto == NULL) { + + if (Mailto == NULL && *optarg == '/') { char *buffer = malloc(256); FILE* file = fopen(optarg, "r"); if (file) { @@ -181,6 +185,10 @@ main(int ac, char **av) } } break; + case 'C': + CreateCronDirsOpt = 1; + break; + default: /* * check for parse error @@ -190,9 +198,9 @@ main(int ac, char **av) 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|file where should cron output be directed?\n"); - printf(" if you specify a file it contents is grepped.\n"); - printf(" (defaults to local user)\n"); + 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"); @@ -237,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; @@ -328,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; @@ -383,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); + } +} From 8f91c71f9eeb231374b5d0e44a668b299a65903e Mon Sep 17 00:00:00 2001 From: Dave Kok Date: Sun, 9 Feb 2020 15:57:27 +0000 Subject: [PATCH 7/9] updated Dockerfile and README --- Dockerfile | 8 +++----- README | 26 +++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index ea433cf..6178ca6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,11 +20,9 @@ RUN rm -rf /var/spool/cron \ COPY --from=builder /usr/local/sbin/crond /usr/local/sbin/crond COPY --from=builder /usr/local/bin/crontab /usr/local/bin/crontab -# The entrypoint makes sure the cron directories exists. -ENTRYPOINT ["/usr/local/sbin/crond"] - -# Default options -CMD ["-C", "-f", "-L", "-"] +# 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 < Date: Sun, 9 Feb 2020 16:05:31 +0000 Subject: [PATCH 8/9] updated outdated comment --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6178ca6..1dc3934 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ RUN rm -rf /var/spool/cron \ && install -o root -d -m0755 -g root /var/spool/cron \ && install -o root -d -m0755 -g root /etc/cron.d -# Copy dcron, crontab and the docker-entrypoint.sh +# 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 From acfa853171a9aad52e5cda854ec987287775581e Mon Sep 17 00:00:00 2001 From: Dave Kok Date: Sun, 9 Feb 2020 16:22:07 +0000 Subject: [PATCH 9/9] database.c readability issue now in sync with previous approved pull request --- database.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database.c b/database.c index 99e59c3..37cf17a 100644 --- a/database.c +++ b/database.c @@ -268,9 +268,9 @@ ReadTimestamps(const char *user) if (ptr && (*ptr == 0 || *ptr == '\n')) { /* strptime uses current seconds when seconds not specified? anyway, we don't get round minutes */ tm.tm_sec = 0; + tm.tm_isdst = -1; + sec = mktime(&tm); } - tm.tm_isdst = -1; - sec = mktime(&tm); if (sec == (time_t)-1) { printlogf(LOG_ERR, "unable to parse timestamp (user %s job %s)\n", file->cf_UserName, line->cl_JobName); /* we continue checking other timestamps in this CronFile */