I recently started using Alpine Linux, and ran into an issue where my user could no longer use the @reboot cron feature to define its own startup jobs. Attached is a patch to add support for the named times found here: https://linux.die.net/man/5/crontab.
size -A -d miscutils/crond.o change: section original patch .rodata.str1.1 693 775 .text.start_jobs 0 382 .text.load_crontab 804 1031 .text.crond_main 1535 1363 .rodata 0 50 .rodata.SpecAry 0 144 Total 4953 5666 Jon
diff --git a/miscutils/crond.c b/miscutils/crond.c index 88e7b47..c65ffd9 100644 --- a/miscutils/crond.c +++ b/miscutils/crond.c @@ -35,6 +35,22 @@ //config: help //config: Command output will be sent to corresponding user via email. //config: +//config:config FEATURE_CROND_SPECIAL_TIMES +//config: bool "Support special times (@reboot, @daily, etc) in crontabs" +//config: default y +//config: depends on CROND +//config: help +//config: string meaning +//config: ------ ------- +//config: @reboot Run once, at startup +//config: @yearly Run once a year, "0 0 1 1 *" +//config: @annually (same as @yearly) +//config: @monthly Run once a month, "0 0 1 * *" +//config: @weekly Run once a week, "0 0 * * 0" +//config: @daily Run once a day, "0 0 * * *" +//config: @midnight (same as @daily) +//config: @hourly Run once an hour, "0 * * * *" +//config: //config:config FEATURE_CROND_DIR //config: string "crond spool directory" //config: default "/var/spool/cron" @@ -74,6 +90,8 @@ #define CRON_DIR CONFIG_FEATURE_CROND_DIR #define CRONTABS CONFIG_FEATURE_CROND_DIR "/crontabs" +#define CRON_REBOOT CONFIG_PID_FILE_PATH "/crond.reboot" +#define DELIMS "# \t" #ifndef SENDMAIL # define SENDMAIL "sendmail" #endif @@ -107,6 +125,9 @@ typedef struct CronLine { #endif char *cl_shell; /* ordered by size, not in natural order. makes code smaller: */ +#if ENABLE_FEATURE_CROND_SPECIAL_TIMES + char cl_Reboot; /* is reboot command */ +#endif char cl_Dow[7]; /* 0-6, beginning sunday */ char cl_Mons[12]; /* 0-11 */ char cl_Hrs[24]; /* 0-23 */ @@ -114,6 +135,13 @@ typedef struct CronLine { char cl_Mins[60]; /* 0-59 */ } CronLine; +#if ENABLE_FEATURE_CROND_SPECIAL_TIMES +typedef struct SpecialEntry { + const char *name; + const char *tokens; +} SpecialEntry; +#endif + #define DAEMON_UID 0 @@ -200,6 +228,20 @@ static const char MonAry[] ALIGN1 = "jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec" ; +#if ENABLE_FEATURE_CROND_SPECIAL_TIMES +static const SpecialEntry SpecAry[] = { + { "reboot", NULL }, + { "yearly", "0\0" "0\0" "1\0" "1\0" "*" }, + { "annually", "0\0" "0\0" "1\0" "1\0" "*" }, + { "monthly", "0\0" "0\0" "1\0" "*\0" "*" }, + { "weekly", "0\0" "0\0" "*\0" "*\0" "0" }, + { "daily", "0\0" "0\0" "*\0" "*\0" "*" }, + { "midnight", "0\0" "0\0" "*\0" "*\0" "*" }, + { "hourly", "0\0" "*\0" "*\0" "*\0" "*" }, + { NULL, NULL } +}; +#endif + static void ParseField(char *user, char *ary, int modvalue, int off, const char *names, char *ptr) /* 'names' is a pointer to a set of 3-char abbreviations */ @@ -433,7 +475,7 @@ static void load_crontab(const char *fileName) break; } - n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY); + n = config_read(parser, tokens, 6, 1, DELIMS, PARSE_NORMAL | PARSE_KEEP_COPY); if (!n) break; @@ -452,6 +494,27 @@ static void load_crontab(const char *fileName) shell = xstrdup(&tokens[0][6]); continue; } +#if ENABLE_FEATURE_CROND_SPECIAL_TIMES + if (tokens[0][0] == '@') { + if (n < 2) + continue; + for (const SpecialEntry *e = SpecAry; e->name != NULL; ++e) { + if (strcmp(e->name, tokens[0] + 1) == 0) { + if (e->tokens != NULL) { + for (int i = 0; i < 5; ++i) + tokens[i] = (char *)e->tokens + 2 * i; + } else { + tokens[0] = NULL; + } + tokens[5] = parser->data + strspn(parser->data, DELIMS); + tokens[5] += strcspn(tokens[5], DELIMS); + tokens[5] += strspn(tokens[5], DELIMS); + n = 6; + break; + } + } + } +#endif //TODO: handle HOME= too? "man crontab" says: //name = value // @@ -469,6 +532,13 @@ static void load_crontab(const char *fileName) if (n < 6) continue; *pline = line = xzalloc(sizeof(*line)); +#if ENABLE_FEATURE_CROND_SPECIAL_TIMES + if (tokens[0] == NULL) { + line->cl_Reboot = 1; + } + else +#endif + { /* parse date ranges */ ParseField(file->cf_username, line->cl_Mins, 60, 0, NULL, tokens[0]); ParseField(file->cf_username, line->cl_Hrs, 24, 0, NULL, tokens[1]); @@ -480,6 +550,7 @@ static void load_crontab(const char *fileName) * is "*", the other is set to 0, and vise-versa */ FixDayDow(line); + } #if ENABLE_FEATURE_CROND_CALL_SENDMAIL /* copy mailto (can be NULL) */ line->cl_mailto = xstrdup(mailTo); @@ -834,6 +905,44 @@ static void flag_starting_jobs(time_t t1, time_t t2) } } +#if ENABLE_FEATURE_CROND_SPECIAL_TIMES +static int touch_reboot_file(void) +{ + if (access(CRON_REBOOT, R_OK | W_OK) == -1) { + close(open(CRON_REBOOT, O_WRONLY | O_CREAT, 0000)); + return TRUE; + } + + return FALSE; +} + +static void flag_reboot_jobs(void) +{ + CronFile *file; + CronLine *line; + + for (file = G.cron_files; file; file = file->cf_next) { + log5("file %s:", file->cf_username); + if (file->cf_deleted) + continue; + for (line = file->cf_lines; line; line = line->cl_next) { + log5(" line %s", line->cl_cmd); + if (line->cl_Reboot) { + log5(" job: %d %s", + (int)line->cl_pid, line->cl_cmd); + if (line->cl_pid > 0) { + log8("user %s: process already running: %s", + file->cf_username, line->cl_cmd); + } else if (line->cl_pid == 0) { + line->cl_pid = -1; + file->cf_wants_starting = 1; + } + } + } + } +} +#endif + static void start_jobs(void) { CronFile *file; @@ -950,6 +1059,12 @@ int crond_main(int argc UNUSED_PARAM, char **argv) log8("crond (busybox "BB_VER") started, log level %d", G.log_level); rescan_crontab_dir(); write_pidfile(CONFIG_PID_FILE_PATH "/crond.pid"); +#if ENABLE_FEATURE_CROND_SPECIAL_TIMES + if (touch_reboot_file()) { + flag_reboot_jobs(); + start_jobs(); + } +#endif /* Main loop */ t2 = time(NULL);
_______________________________________________ busybox mailing list busybox@busybox.net http://lists.busybox.net/mailman/listinfo/busybox