====== busybox crond / crontab fields ====== I could not find easily what expressions are valid in crontab files for the [[http://busybox.net/|busybox]] crond daemon. The [[http://busybox.net/downloads/BusyBox.html#crond|man pages]] I found are minimalists -- as busybox is itself -- and only list the options that the ''crond'' commands accepts. No doc lists what you may write in the crontab file itself. I had to "//use the source, Luke !//". ++++Here is the relevant part of ''crond.c'' file for busybox 1.13.2| static const char DowAry[] ALIGN1 = "sun""mon""tue""wed""thu""fri""sat" /* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */ ; static const char MonAry[] ALIGN1 = "jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec" /* "Jan""Feb""Mar""Apr""May""Jun""Jul""Aug""Sep""Oct""Nov""Dec" */ ; 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 */ { char *base = ptr; int n1 = -1; int n2 = -1; // this can't happen due to config_read() /*if (base == NULL) return;*/ while (1) { int skip = 0; /* Handle numeric digit or symbol or '*' */ if (*ptr == '*') { n1 = 0; /* everything will be filled */ n2 = modvalue - 1; skip = 1; ++ptr; } else if (isdigit(*ptr)) { if (n1 < 0) { n1 = strtol(ptr, &ptr, 10) + off; } else { n2 = strtol(ptr, &ptr, 10) + off; } skip = 1; } else if (names) { int i; for (i = 0; names[i]; i += 3) { /* was using strncmp before... */ if (strncasecmp(ptr, &names[i], 3) == 0) { ptr += 3; if (n1 < 0) { n1 = i / 3; } else { n2 = i / 3; } skip = 1; break; } } } /* handle optional range '-' */ if (skip == 0) { goto err; } if (*ptr == '-' && n2 < 0) { ++ptr; continue; } /* * collapse single-value ranges, handle skipmark, and fill * in the character array appropriately. */ if (n2 < 0) { n2 = n1; } if (*ptr == '/') { skip = strtol(ptr + 1, &ptr, 10); } /* * fill array, using a failsafe is the easiest way to prevent * an endless loop */ { int s0 = 1; int failsafe = 1024; --n1; do { n1 = (n1 + 1) % modvalue; if (--s0 == 0) { ary[n1 % modvalue] = 1; s0 = skip; } if (--failsafe == 0) { goto err; } } while (n1 != n2); } if (*ptr != ',') { break; } ++ptr; n1 = -1; n2 = -1; } if (*ptr) { err: crondlog(WARN9 "user %s: parse error at %s", user, base); return; } if (DebugOpt && (LogLevel <= 5)) { /* like LVL5 */ /* can't use crondlog, it inserts '\n' */ int i; for (i = 0; i < modvalue; ++i) fprintf(stderr, "%d", (unsigned char)ary[i]); fputc('\n', stderr); } } static void SynchronizeFile(const char *fileName) { struct parser_t *parser; struct stat sbuf; int maxLines; char *tokens[6]; #if ENABLE_FEATURE_CROND_CALL_SENDMAIL char *mailTo = NULL; #endif if (!fileName) return; DeleteFile(fileName); parser = config_open(fileName); if (!parser) return; maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES; if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DaemonUid) { CronFile *file = xzalloc(sizeof(CronFile)); CronLine **pline; int n; file->cf_User = xstrdup(fileName); pline = &file->cf_LineBase; while (1) { CronLine *line; if (!--maxLines) break; n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY); if (!n) break; if (DebugOpt) crondlog(LVL5 "user:%s entry:%s", fileName, parser->data); /* check if line is setting MAILTO= */ if (0 == strncmp(tokens[0], "MAILTO=", 7)) { #if ENABLE_FEATURE_CROND_CALL_SENDMAIL free(mailTo); mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL; #endif /* otherwise just ignore such lines */ continue; } /* check if a minimum of tokens is specified */ if (n < 6) continue; *pline = line = xzalloc(sizeof(*line)); /* parse date ranges */ ParseField(file->cf_User, line->cl_Mins, 60, 0, NULL, tokens[0]); ParseField(file->cf_User, line->cl_Hrs, 24, 0, NULL, tokens[1]); ParseField(file->cf_User, line->cl_Days, 32, 0, NULL, tokens[2]); ParseField(file->cf_User, line->cl_Mons, 12, -1, MonAry, tokens[3]); ParseField(file->cf_User, line->cl_Dow, 7, 0, DowAry, tokens[4]); /* * fix days and dow - if one is not "*" and the other * 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); #endif /* copy command */ line->cl_Shell = xstrdup(tokens[5]); if (DebugOpt) { crondlog(LVL5 " command:%s", tokens[5]); } pline = &line->cl_Next; //bb_error_msg("M[%s]F[%s][%s][%s][%s][%s][%s]", mailTo, tokens[0], tokens[1], tokens[2], tokens[3], tokens[4], tokens[5]); } *pline = NULL; file->cf_Next = FileBase; FileBase = file; if (maxLines == 0) { crondlog(WARN9 "user %s: too many lines", fileName); } } config_close(parser); } ++++ What does it tells us ? well, you can use things like this : MAILTO=me@somewhere.com * * * * * touch /tmp/everyminute.$$ 0-55/5 * * * * touch /tmp/every-5-minutes.$$ MAILTO= 5,11,19,37,59 1,3,5,7,11,13,17,19,23 * * * touch /tmp/prime.$$ 11,17,30-50/3 5,7,11-22/5,23 * jan-oct/3,12 sun-3/2 touch /tmp/guess-when.$$ ''MAILTO='' may be obeyed or not, depending on the fact that **your** busybox crond was compiled with option for sending mail or not. How you can check that ? Don't know. ===== Errors ===== What if you enter something invalid ? In my case, there wass just a warning in the log. If you use this 11,17,30-50/3 5,7,11-22/5,23 * jan-oct-nob/3,12 sun-3/2 touch /tmp/guess-when.$$ here is what you get in the logs Jan 3 06:51:24 (none) cron.err crond[14635]: crond (busybox 1.13.2) started, log level 8 Jan 3 06:51:24 (none) cron.err crond[14635]: user root: parse error at jan-oct-nob/3,12