static int
openlock(char *file)
{
return create(file, ORDWR, 0600);
}
static int
mklock(char *file)
{
int fd, try;
Dir *dir;
fd = openlock(file);
if (fd >= 0) {
/* make it a lock file if it wasn't */
dir = dirfstat(fd);
if (dir == nil)
error("%s vanished: %r", file);
dir->mode |= DMEXCL;
dir->qid.type |= QTEXCL;
dirfwstat(fd, dir);
free(dir);
/* reopen in case it wasn't a lock file at last open */
close(fd);
}
for (try = 0; try < 65 && (fd = openlock(file)) < 0; try++)
sleep(10*1000);
return fd;
}
void
main(int argc, char *argv[])
{
Job *j;
Tm tm;
Time t;
ulong now, last; /* in seconds */
int i, lock;
debug = 0;
ARGBEGIN{
case 'c':
createuser();
exits(0);
case 'd':
debug = 1;
break;
default:
usage();
}ARGEND
initcap(); /* do this early, before cpurc removes it */
switch(fork()){
case -1:
fatal("can't fork: %r");
case 0:
break;
default:
exits(0);
}
/*
* it can take a few minutes before the file server notices that
* we've rebooted and gives up the lock.
*/
lock = mklock("/cron/lock");
if (lock < 0)
fatal("cron already running: %r");
argv0 = "cron";
last = time(0);
for(;;){
readalljobs();
/*
* the system's notion of time may have jumped forward or
* backward an arbitrary amount since the last call to time().
*/
now = time(0);
/*
* if time has jumped backward, just note it and adapt.
* if time has jumped forward more than a day,
* just execute one day's jobs.
*/
if (now < last) {
clog("time went backward");
last = now;
} else if (now - last > Day) {
clog("time advanced more than a day");
last = now - Day;
}
now = minute(now);
for(last = minute(last); last <= now; last += Minute){
tm = *localtime(last);
t.min = 1ULL << tm.min;
t.hour = 1 << tm.hour;
t.wday = 1 << tm.wday;
t.mday = 1 << tm.mday;
t.mon = 1 << (tm.mon + 1);
for(i = 0; i < nuser; i++)
for(j = users[i].jobs; j; j = j->next)
if(j->time.min & t.min
&& j->time.hour & t.hour
&& j->time.wday & t.wday
&& j->time.mday & t.mday
&& j->time.mon & t.mon)
rexec(&users[i], j);
}
seek(lock, 0, 0);
write(lock, "x", 1); /* keep the lock alive */
/*
* if we're not at next minute yet, sleep until a second past
* (to allow for sleep intervals being approximate),
* which synchronises with minute roll-over as a side-effect.
*/
sleepuntil(now + Minute + 1);
}
/* not reached */
}
void
createuser(void)
{
Dir d;
char file[128], *user;
int fd;
/*
* return the next time range (as a bit vector) in the file:
* times: '*'
* | range
* range: number
* | number '-' number
* | range ',' range
* a return of zero means a syntax error was discovered
*/
uvlong
gettime(int min, int max)
{
uvlong n, m, e;
if(gettok(min, max) == '*')
return ~0ULL;
n = 0;
while(tok == '1'){
m = 1ULL << lexval;
n |= m;
if(gettok(0, 0) == '-'){
if(gettok(lexval, max) != '1')
return 0;
e = 1ULL << lexval;
for( ; m <= e; m <<= 1)
n |= m;
gettok(min, max);
}
if(tok != ',')
break;
if(gettok(min, max) != '1')
return 0;
}
pushtok();
return n;
}
void
pushtok(void)
{
savec = savetok;
}
int
gettok(int min, int max)
{
char c;
savetok = savec;
if(!savec)
return tok = 0;
while((c = *savec) == ' ' || c == '\t')
savec++;
switch(c){
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
lexval = strtoul(savec, &savec, 10);
if(lexval < min || lexval > max)
return tok = 0;
return tok = '1';
case '*': case '-': case ',':
savec++;
return tok = c;
default:
return tok = 0;
}
}
/*
* convert command to run properly on the remote machine
* need to escape the quotes so they don't get stripped
*/
int
mkcmd(char *cmd, char *buf, int len)
{
char *p;
int n, m;
n = sizeof "exec rc -c '" -1;
if(n >= len)
return 0;
strcpy(buf, "exec rc -c '");
while(p = utfrune(cmd, L'\'')){
p++;
m = p - cmd;
if(n + m + 1 >= len)
return 0;
strncpy(&buf[n], cmd, m);
n += m;
buf[n++] = '\'';
cmd = p;
}
m = strlen(cmd);
if(n + m + sizeof "'</dev/null>/dev/null>[2=1]" >= len)
return 0;
strcpy(&buf[n], cmd);
strcpy(&buf[n+m], "'</dev/null>/dev/null>[2=1]");
return 1;
}