sbase

suckless unix tools
git clone git://git.2f30.org/sbase.git
Log | Files | Refs | README | LICENSE

commit e29632977393ef1f2d08fc1cabd1412f20c1d500
parent fea48eb229c4801d35ac6741bcaad35bfa59bf23
Author: sin <sin@2f30.org>
Date:   Tue, 16 Dec 2014 10:59:08 +0000

Rename crond to cron

Diffstat:
Makefile | 2+-
README | 2+-
cron.c | 494+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
crond.c | 494-------------------------------------------------------------------------------
4 files changed, 496 insertions(+), 496 deletions(-)

diff --git a/Makefile b/Makefile @@ -69,7 +69,7 @@ BIN =\ cols\ comm\ cp\ - crond\ + cron\ cut\ date\ dirname\ diff --git a/README b/README @@ -20,7 +20,7 @@ cmp cols comm cp -crond +cron cut date dirname diff --git a/cron.c b/cron.c @@ -0,0 +1,494 @@ +/* See LICENSE file for copyright and license details. */ +#include <sys/types.h> +#include <sys/wait.h> + +#include <errno.h> +#include <limits.h> +#include <signal.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include "queue.h" +#include "util.h" + +struct field { + /* [low, high] */ + long low; + long high; + /* for every `div' units */ + long div; +}; + +struct ctabentry { + struct field min; + struct field hour; + struct field mday; + struct field mon; + struct field wday; + char *cmd; + TAILQ_ENTRY(ctabentry) entry; +}; + +struct jobentry { + char *cmd; + pid_t pid; + TAILQ_ENTRY(jobentry) entry; +}; + +char *argv0; +static sig_atomic_t chldreap; +static sig_atomic_t reload; +static sig_atomic_t quit; +static TAILQ_HEAD(, ctabentry) ctabhead = TAILQ_HEAD_INITIALIZER(ctabhead); +static TAILQ_HEAD(, jobentry) jobhead = TAILQ_HEAD_INITIALIZER(jobhead); +static char *config = "/etc/crontab"; +static char *pidfile = "/var/run/crond.pid"; +static int nflag; + +static void +loginfo(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (nflag == 0) + vsyslog(LOG_INFO, fmt, ap); + else + vfprintf(stdout, fmt, ap); + fflush(stdout); + va_end(ap); +} + +static void +logwarn(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (nflag == 0) + vsyslog(LOG_WARNING, fmt, ap); + else + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void +logerr(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (nflag == 0) + vsyslog(LOG_ERR, fmt, ap); + else + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void +runjob(char *cmd) +{ + struct jobentry *je; + time_t t; + pid_t pid; + + t = time(NULL); + + /* If command is already running, skip it */ + TAILQ_FOREACH(je, &jobhead, entry) { + if (strcmp(je->cmd, cmd) == 0) { + loginfo("already running %s pid: %d at %s", + je->cmd, je->pid, ctime(&t)); + return; + } + } + + pid = fork(); + if (pid < 0) { + logerr("error: failed to fork job: %s time: %s", + cmd, ctime(&t)); + return; + } else if (pid == 0) { + setsid(); + loginfo("run: %s pid: %d at %s", + cmd, getpid(), ctime(&t)); + execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL); + logerr("error: failed to execute job: %s time: %s", + cmd, ctime(&t)); + _exit(1); + } else { + je = emalloc(sizeof(*je)); + je->cmd = estrdup(cmd); + je->pid = pid; + TAILQ_INSERT_TAIL(&jobhead, je, entry); + } +} + +static void +waitjob(void) +{ + struct jobentry *je, *tmp; + int status; + time_t t; + pid_t pid; + + t = time(NULL); + + while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { + je = NULL; + TAILQ_FOREACH(tmp, &jobhead, entry) { + if (tmp->pid == pid) { + je = tmp; + break; + } + } + if (je) { + TAILQ_REMOVE(&jobhead, je, entry); + free(je->cmd); + free(je); + } + if (WIFEXITED(status) == 1) + loginfo("complete: pid: %d returned: %d time: %s", + pid, WEXITSTATUS(status), ctime(&t)); + else if (WIFSIGNALED(status) == 1) + loginfo("complete: pid: %d terminated by signal: %s time: %s", + pid, strsignal(WTERMSIG(status)), ctime(&t)); + else if (WIFSTOPPED(status) == 1) + loginfo("complete: pid: %d stopped by signal: %s time: %s", + pid, strsignal(WSTOPSIG(status)), ctime(&t)); + } +} + +static int +isleap(int year) +{ + if (year % 400 == 0) + return 1; + if (year % 100 == 0) + return 0; + return (year % 4 == 0); +} + +static int +daysinmon(int mon, int year) +{ + int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + if (year < 1900) + year += 1900; + if (isleap(year)) + days[1] = 29; + return days[mon]; +} + +static int +matchentry(struct ctabentry *cte, struct tm *tm) +{ + struct { + struct field *f; + int tm; + int len; + } matchtbl[] = { + { .f = &cte->min, .tm = tm->tm_min, .len = 60 }, + { .f = &cte->hour, .tm = tm->tm_hour, .len = 24 }, + { .f = &cte->mday, .tm = tm->tm_mday, .len = daysinmon(tm->tm_mon, tm->tm_year) }, + { .f = &cte->mon, .tm = tm->tm_mon, .len = 12 }, + { .f = &cte->wday, .tm = tm->tm_wday, .len = 7 }, + }; + size_t i; + + for (i = 0; i < LEN(matchtbl); i++) { + if (matchtbl[i].f->high == -1) { + if (matchtbl[i].f->low == -1) { + continue; + } else if (matchtbl[i].f->div > 0) { + if (matchtbl[i].tm > 0) { + if (matchtbl[i].tm % matchtbl[i].f->div == 0) + continue; + } else { + if (matchtbl[i].len % matchtbl[i].f->div == 0) + continue; + } + } else if (matchtbl[i].f->low == matchtbl[i].tm) { + continue; + } + } else if (matchtbl[i].f->low <= matchtbl[i].tm && + matchtbl[i].f->high >= matchtbl[i].tm) { + continue; + } + break; + } + if (i != LEN(matchtbl)) + return 0; + return 1; +} + +static int +parsefield(const char *field, long low, long high, struct field *f) +{ + long min, max, div; + char *e1, *e2; + + if (strcmp(field, "*") == 0) { + f->low = -1; + f->high = -1; + return 0; + } + + div = -1; + max = -1; + min = strtol(field, &e1, 10); + + switch (e1[0]) { + case '-': + e1++; + errno = 0; + max = strtol(e1, &e2, 10); + if (e2[0] != '\0' || errno != 0) + return -1; + break; + case '*': + e1++; + if (e1[0] != '/') + return -1; + e1++; + errno = 0; + div = strtol(e1, &e2, 10); + if (e2[0] != '\0' || errno != 0) + return -1; + break; + case '\0': + break; + default: + return -1; + } + + if (min < low || min > high) + return -1; + if (max != -1) + if (max < low || max > high) + return -1; + if (div != -1) + if (div < low || div > high) + return -1; + + f->low = min; + f->high = max; + f->div = div; + return 0; +} + +static void +unloadentries(void) +{ + struct ctabentry *cte, *tmp; + + for (cte = TAILQ_FIRST(&ctabhead); cte; cte = tmp) { + tmp = TAILQ_NEXT(cte, entry); + TAILQ_REMOVE(&ctabhead, cte, entry); + free(cte->cmd); + free(cte); + } +} + +static int +loadentries(void) +{ + struct ctabentry *cte; + FILE *fp; + char *line = NULL, *p, *col; + int r = 0, y; + size_t size = 0; + ssize_t len; + + if ((fp = fopen(config, "r")) == NULL) { + logerr("error: can't open %s: %s\n", config, strerror(errno)); + return -1; + } + + for (y = 0; (len = getline(&line, &size, fp)) != -1; y++) { + p = line; + if (line[0] == '#' || line[0] == '\n' || line[0] == '\0') + continue; + + cte = emalloc(sizeof(*cte)); + + col = strsep(&p, "\t"); + if (!col || parsefield(col, 0, 59, &cte->min) < 0) { + logerr("error: failed to parse `min' field on line %d\n", + y + 1); + free(cte); + r = -1; + break; + } + + col = strsep(&p, "\t"); + if (!col || parsefield(col, 0, 23, &cte->hour) < 0) { + logerr("error: failed to parse `hour' field on line %d\n", + y + 1); + free(cte); + r = -1; + break; + } + + col = strsep(&p, "\t"); + if (!col || parsefield(col, 1, 31, &cte->mday) < 0) { + logerr("error: failed to parse `mday' field on line %d\n", + y + 1); + free(cte); + r = -1; + break; + } + + col = strsep(&p, "\t"); + if (!col || parsefield(col, 1, 12, &cte->mon) < 0) { + logerr("error: failed to parse `mon' field on line %d\n", + y + 1); + free(cte); + r = -1; + break; + } + + col = strsep(&p, "\t"); + if (!col || parsefield(col, 0, 6, &cte->wday) < 0) { + logerr("error: failed to parse `wday' field on line %d\n", + y + 1); + free(cte); + r = -1; + break; + } + + col = strsep(&p, "\n"); + if (!col) { + logerr("error: missing `cmd' field on line %d\n", + y + 1); + free(cte); + r = -1; + break; + } + cte->cmd = estrdup(col); + + TAILQ_INSERT_TAIL(&ctabhead, cte, entry); + } + + if (r < 0) + unloadentries(); + + free(line); + fclose(fp); + + return r; +} + +static void +reloadentries(void) +{ + unloadentries(); + if (loadentries() < 0) + logwarn("warning: discarding old crontab entries\n"); +} + +static void +sighandler(int sig) +{ + switch (sig) { + case SIGCHLD: + chldreap = 1; + break; + case SIGHUP: + reload = 1; + break; + case SIGTERM: + quit = 1; + break; + } +} + +static void +usage(void) +{ + eprintf("usage: %s [-f file] [-n]\n", argv0); +} + +int +main(int argc, char *argv[]) +{ + FILE *fp; + struct ctabentry *cte; + time_t t; + struct tm *tm; + struct sigaction sa; + + ARGBEGIN { + case 'n': + nflag = 1; + break; + case 'f': + config = EARGF(usage()); + break; + default: + usage(); + } ARGEND; + + if (argc > 0) + usage(); + + if (nflag == 0) { + openlog(argv[0], LOG_CONS | LOG_PID, LOG_CRON); + if (daemon(1, 0) < 0) { + logerr("error: failed to daemonize %s\n", strerror(errno)); + return 1; + } + if ((fp = fopen(pidfile, "w"))) { + fprintf(fp, "%d\n", getpid()); + fclose(fp); + } + } + + sa.sa_handler = sighandler; + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + loadentries(); + + while (1) { + t = time(NULL); + sleep(60 - t % 60); + + if (quit == 1) { + if (nflag == 0) + unlink(pidfile); + unloadentries(); + /* Don't wait or kill forked processes, just exit */ + break; + } + + if (reload == 1 || chldreap == 1) { + if (reload == 1) { + reloadentries(); + reload = 0; + } + if (chldreap == 1) { + waitjob(); + chldreap = 0; + } + continue; + } + + TAILQ_FOREACH(cte, &ctabhead, entry) { + t = time(NULL); + tm = localtime(&t); + if (matchentry(cte, tm) == 1) + runjob(cte->cmd); + } + } + + if (nflag == 0) + closelog(); + + return 0; +} diff --git a/crond.c b/crond.c @@ -1,494 +0,0 @@ -/* See LICENSE file for copyright and license details. */ -#include <sys/types.h> -#include <sys/wait.h> - -#include <errno.h> -#include <limits.h> -#include <signal.h> -#include <stdarg.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <syslog.h> -#include <time.h> -#include <unistd.h> - -#include "queue.h" -#include "util.h" - -struct field { - /* [low, high] */ - long low; - long high; - /* for every `div' units */ - long div; -}; - -struct ctabentry { - struct field min; - struct field hour; - struct field mday; - struct field mon; - struct field wday; - char *cmd; - TAILQ_ENTRY(ctabentry) entry; -}; - -struct jobentry { - char *cmd; - pid_t pid; - TAILQ_ENTRY(jobentry) entry; -}; - -char *argv0; -static sig_atomic_t chldreap; -static sig_atomic_t reload; -static sig_atomic_t quit; -static TAILQ_HEAD(, ctabentry) ctabhead = TAILQ_HEAD_INITIALIZER(ctabhead); -static TAILQ_HEAD(, jobentry) jobhead = TAILQ_HEAD_INITIALIZER(jobhead); -static char *config = "/etc/crontab"; -static char *pidfile = "/var/run/crond.pid"; -static int nflag; - -static void -loginfo(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - if (nflag == 0) - vsyslog(LOG_INFO, fmt, ap); - else - vfprintf(stdout, fmt, ap); - fflush(stdout); - va_end(ap); -} - -static void -logwarn(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - if (nflag == 0) - vsyslog(LOG_WARNING, fmt, ap); - else - vfprintf(stderr, fmt, ap); - va_end(ap); -} - -static void -logerr(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - if (nflag == 0) - vsyslog(LOG_ERR, fmt, ap); - else - vfprintf(stderr, fmt, ap); - va_end(ap); -} - -static void -runjob(char *cmd) -{ - struct jobentry *je; - time_t t; - pid_t pid; - - t = time(NULL); - - /* If command is already running, skip it */ - TAILQ_FOREACH(je, &jobhead, entry) { - if (strcmp(je->cmd, cmd) == 0) { - loginfo("already running %s pid: %d at %s", - je->cmd, je->pid, ctime(&t)); - return; - } - } - - pid = fork(); - if (pid < 0) { - logerr("error: failed to fork job: %s time: %s", - cmd, ctime(&t)); - return; - } else if (pid == 0) { - setsid(); - loginfo("run: %s pid: %d at %s", - cmd, getpid(), ctime(&t)); - execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL); - logerr("error: failed to execute job: %s time: %s", - cmd, ctime(&t)); - _exit(1); - } else { - je = emalloc(sizeof(*je)); - je->cmd = estrdup(cmd); - je->pid = pid; - TAILQ_INSERT_TAIL(&jobhead, je, entry); - } -} - -static void -waitjob(void) -{ - struct jobentry *je, *tmp; - int status; - time_t t; - pid_t pid; - - t = time(NULL); - - while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { - je = NULL; - TAILQ_FOREACH(tmp, &jobhead, entry) { - if (tmp->pid == pid) { - je = tmp; - break; - } - } - if (je) { - TAILQ_REMOVE(&jobhead, je, entry); - free(je->cmd); - free(je); - } - if (WIFEXITED(status) == 1) - loginfo("complete: pid: %d returned: %d time: %s", - pid, WEXITSTATUS(status), ctime(&t)); - else if (WIFSIGNALED(status) == 1) - loginfo("complete: pid: %d terminated by signal: %s time: %s", - pid, strsignal(WTERMSIG(status)), ctime(&t)); - else if (WIFSTOPPED(status) == 1) - loginfo("complete: pid: %d stopped by signal: %s time: %s", - pid, strsignal(WSTOPSIG(status)), ctime(&t)); - } -} - -static int -isleap(int year) -{ - if (year % 400 == 0) - return 1; - if (year % 100 == 0) - return 0; - return (year % 4 == 0); -} - -static int -daysinmon(int mon, int year) -{ - int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - if (year < 1900) - year += 1900; - if (isleap(year)) - days[1] = 29; - return days[mon]; -} - -static int -matchentry(struct ctabentry *cte, struct tm *tm) -{ - struct { - struct field *f; - int tm; - int len; - } matchtbl[] = { - { .f = &cte->min, .tm = tm->tm_min, .len = 60 }, - { .f = &cte->hour, .tm = tm->tm_hour, .len = 24 }, - { .f = &cte->mday, .tm = tm->tm_mday, .len = daysinmon(tm->tm_mon, tm->tm_year) }, - { .f = &cte->mon, .tm = tm->tm_mon, .len = 12 }, - { .f = &cte->wday, .tm = tm->tm_wday, .len = 7 }, - }; - size_t i; - - for (i = 0; i < LEN(matchtbl); i++) { - if (matchtbl[i].f->high == -1) { - if (matchtbl[i].f->low == -1) { - continue; - } else if (matchtbl[i].f->div > 0) { - if (matchtbl[i].tm > 0) { - if (matchtbl[i].tm % matchtbl[i].f->div == 0) - continue; - } else { - if (matchtbl[i].len % matchtbl[i].f->div == 0) - continue; - } - } else if (matchtbl[i].f->low == matchtbl[i].tm) { - continue; - } - } else if (matchtbl[i].f->low <= matchtbl[i].tm && - matchtbl[i].f->high >= matchtbl[i].tm) { - continue; - } - break; - } - if (i != LEN(matchtbl)) - return 0; - return 1; -} - -static int -parsefield(const char *field, long low, long high, struct field *f) -{ - long min, max, div; - char *e1, *e2; - - if (strcmp(field, "*") == 0) { - f->low = -1; - f->high = -1; - return 0; - } - - div = -1; - max = -1; - min = strtol(field, &e1, 10); - - switch (e1[0]) { - case '-': - e1++; - errno = 0; - max = strtol(e1, &e2, 10); - if (e2[0] != '\0' || errno != 0) - return -1; - break; - case '*': - e1++; - if (e1[0] != '/') - return -1; - e1++; - errno = 0; - div = strtol(e1, &e2, 10); - if (e2[0] != '\0' || errno != 0) - return -1; - break; - case '\0': - break; - default: - return -1; - } - - if (min < low || min > high) - return -1; - if (max != -1) - if (max < low || max > high) - return -1; - if (div != -1) - if (div < low || div > high) - return -1; - - f->low = min; - f->high = max; - f->div = div; - return 0; -} - -static void -unloadentries(void) -{ - struct ctabentry *cte, *tmp; - - for (cte = TAILQ_FIRST(&ctabhead); cte; cte = tmp) { - tmp = TAILQ_NEXT(cte, entry); - TAILQ_REMOVE(&ctabhead, cte, entry); - free(cte->cmd); - free(cte); - } -} - -static int -loadentries(void) -{ - struct ctabentry *cte; - FILE *fp; - char *line = NULL, *p, *col; - int r = 0, y; - size_t size = 0; - ssize_t len; - - if ((fp = fopen(config, "r")) == NULL) { - logerr("error: can't open %s: %s\n", config, strerror(errno)); - return -1; - } - - for (y = 0; (len = getline(&line, &size, fp)) != -1; y++) { - p = line; - if (line[0] == '#' || line[0] == '\n' || line[0] == '\0') - continue; - - cte = emalloc(sizeof(*cte)); - - col = strsep(&p, "\t"); - if (!col || parsefield(col, 0, 59, &cte->min) < 0) { - logerr("error: failed to parse `min' field on line %d\n", - y + 1); - free(cte); - r = -1; - break; - } - - col = strsep(&p, "\t"); - if (!col || parsefield(col, 0, 23, &cte->hour) < 0) { - logerr("error: failed to parse `hour' field on line %d\n", - y + 1); - free(cte); - r = -1; - break; - } - - col = strsep(&p, "\t"); - if (!col || parsefield(col, 1, 31, &cte->mday) < 0) { - logerr("error: failed to parse `mday' field on line %d\n", - y + 1); - free(cte); - r = -1; - break; - } - - col = strsep(&p, "\t"); - if (!col || parsefield(col, 1, 12, &cte->mon) < 0) { - logerr("error: failed to parse `mon' field on line %d\n", - y + 1); - free(cte); - r = -1; - break; - } - - col = strsep(&p, "\t"); - if (!col || parsefield(col, 0, 6, &cte->wday) < 0) { - logerr("error: failed to parse `wday' field on line %d\n", - y + 1); - free(cte); - r = -1; - break; - } - - col = strsep(&p, "\n"); - if (!col) { - logerr("error: missing `cmd' field on line %d\n", - y + 1); - free(cte); - r = -1; - break; - } - cte->cmd = estrdup(col); - - TAILQ_INSERT_TAIL(&ctabhead, cte, entry); - } - - if (r < 0) - unloadentries(); - - free(line); - fclose(fp); - - return r; -} - -static void -reloadentries(void) -{ - unloadentries(); - if (loadentries() < 0) - logwarn("warning: discarding old crontab entries\n"); -} - -static void -sighandler(int sig) -{ - switch (sig) { - case SIGCHLD: - chldreap = 1; - break; - case SIGHUP: - reload = 1; - break; - case SIGTERM: - quit = 1; - break; - } -} - -static void -usage(void) -{ - eprintf("usage: %s [-f file] [-n]\n", argv0); -} - -int -main(int argc, char *argv[]) -{ - FILE *fp; - struct ctabentry *cte; - time_t t; - struct tm *tm; - struct sigaction sa; - - ARGBEGIN { - case 'n': - nflag = 1; - break; - case 'f': - config = EARGF(usage()); - break; - default: - usage(); - } ARGEND; - - if (argc > 0) - usage(); - - if (nflag == 0) { - openlog(argv[0], LOG_CONS | LOG_PID, LOG_CRON); - if (daemon(1, 0) < 0) { - logerr("error: failed to daemonize %s\n", strerror(errno)); - return 1; - } - if ((fp = fopen(pidfile, "w"))) { - fprintf(fp, "%d\n", getpid()); - fclose(fp); - } - } - - sa.sa_handler = sighandler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - - loadentries(); - - while (1) { - t = time(NULL); - sleep(60 - t % 60); - - if (quit == 1) { - if (nflag == 0) - unlink(pidfile); - unloadentries(); - /* Don't wait or kill forked processes, just exit */ - break; - } - - if (reload == 1 || chldreap == 1) { - if (reload == 1) { - reloadentries(); - reload = 0; - } - if (chldreap == 1) { - waitjob(); - chldreap = 0; - } - continue; - } - - TAILQ_FOREACH(cte, &ctabhead, entry) { - t = time(NULL); - tm = localtime(&t); - if (matchentry(cte, tm) == 1) - runjob(cte->cmd); - } - } - - if (nflag == 0) - closelog(); - - return 0; -}