1219b2ee8SDavid du Colombier #include <u.h>
2219b2ee8SDavid du Colombier #include <libc.h>
3219b2ee8SDavid du Colombier #include <bio.h>
43ff48bf5SDavid du Colombier #include <libsec.h>
53ff48bf5SDavid du Colombier #include <auth.h>
69a747e4fSDavid du Colombier #include "authcmdlib.h"
7219b2ee8SDavid du Colombier
8219b2ee8SDavid du Colombier char CRONLOG[] = "cron";
9219b2ee8SDavid du Colombier
1053b9a848SDavid du Colombier enum {
1153b9a848SDavid du Colombier Minute = 60,
1253b9a848SDavid du Colombier Hour = 60 * Minute,
1353b9a848SDavid du Colombier Day = 24 * Hour,
1453b9a848SDavid du Colombier };
1553b9a848SDavid du Colombier
16219b2ee8SDavid du Colombier typedef struct Job Job;
17219b2ee8SDavid du Colombier typedef struct Time Time;
18219b2ee8SDavid du Colombier typedef struct User User;
19219b2ee8SDavid du Colombier
20219b2ee8SDavid du Colombier struct Time{ /* bit masks for each valid time */
2153b9a848SDavid du Colombier uvlong min;
22219b2ee8SDavid du Colombier ulong hour;
23219b2ee8SDavid du Colombier ulong mday;
24219b2ee8SDavid du Colombier ulong wday;
25219b2ee8SDavid du Colombier ulong mon;
26219b2ee8SDavid du Colombier };
27219b2ee8SDavid du Colombier
28219b2ee8SDavid du Colombier struct Job{
299a747e4fSDavid du Colombier char *host; /* where ... */
30219b2ee8SDavid du Colombier Time time; /* when ... */
31219b2ee8SDavid du Colombier char *cmd; /* and what to execute */
32219b2ee8SDavid du Colombier Job *next;
33219b2ee8SDavid du Colombier };
34219b2ee8SDavid du Colombier
35219b2ee8SDavid du Colombier struct User{
367dd7cddfSDavid du Colombier Qid lastqid; /* of last read /cron/user/cron */
379a747e4fSDavid du Colombier char *name; /* who ... */
38219b2ee8SDavid du Colombier Job *jobs; /* wants to execute these jobs */
39219b2ee8SDavid du Colombier };
40219b2ee8SDavid du Colombier
41219b2ee8SDavid du Colombier User *users;
42219b2ee8SDavid du Colombier int nuser;
43219b2ee8SDavid du Colombier int maxuser;
44219b2ee8SDavid du Colombier char *savec;
45219b2ee8SDavid du Colombier char *savetok;
46219b2ee8SDavid du Colombier int tok;
47219b2ee8SDavid du Colombier int debug;
48219b2ee8SDavid du Colombier ulong lexval;
49219b2ee8SDavid du Colombier
50219b2ee8SDavid du Colombier void rexec(User*, Job*);
51219b2ee8SDavid du Colombier void readalljobs(void);
52219b2ee8SDavid du Colombier Job *readjobs(char*, User*);
539a747e4fSDavid du Colombier int getname(char**);
5453b9a848SDavid du Colombier uvlong gettime(int, int);
55219b2ee8SDavid du Colombier int gettok(int, int);
563ff48bf5SDavid du Colombier void initcap(void);
57219b2ee8SDavid du Colombier void pushtok(void);
58219b2ee8SDavid du Colombier void usage(void);
59219b2ee8SDavid du Colombier void freejobs(Job*);
60219b2ee8SDavid du Colombier User *newuser(char*);
61219b2ee8SDavid du Colombier void *emalloc(ulong);
62219b2ee8SDavid du Colombier void *erealloc(void*, ulong);
63219b2ee8SDavid du Colombier int myauth(int, char*);
64219b2ee8SDavid du Colombier void createuser(void);
65219b2ee8SDavid du Colombier int mkcmd(char*, char*, int);
66219b2ee8SDavid du Colombier void printjobs(void);
677dd7cddfSDavid du Colombier int qidcmp(Qid, Qid);
683ff48bf5SDavid du Colombier int becomeuser(char*);
69219b2ee8SDavid du Colombier
705344664fSDavid du Colombier ulong
minute(ulong tm)715344664fSDavid du Colombier minute(ulong tm)
725344664fSDavid du Colombier {
735344664fSDavid du Colombier return tm - tm%Minute; /* round down to the minute */
745344664fSDavid du Colombier }
755344664fSDavid du Colombier
765344664fSDavid du Colombier int
sleepuntil(ulong tm)775344664fSDavid du Colombier sleepuntil(ulong tm)
785344664fSDavid du Colombier {
795344664fSDavid du Colombier ulong now = time(0);
805344664fSDavid du Colombier
815344664fSDavid du Colombier if (now < tm)
825344664fSDavid du Colombier return sleep((tm - now)*1000);
835344664fSDavid du Colombier else
845344664fSDavid du Colombier return 0;
855344664fSDavid du Colombier }
865344664fSDavid du Colombier
873179bee6SDavid du Colombier #pragma varargck argpos clog 1
88e329647eSDavid du Colombier #pragma varargck argpos fatal 1
893179bee6SDavid du Colombier
903179bee6SDavid du Colombier static void
clog(char * fmt,...)913179bee6SDavid du Colombier clog(char *fmt, ...)
923179bee6SDavid du Colombier {
933179bee6SDavid du Colombier char msg[256];
943179bee6SDavid du Colombier va_list arg;
953179bee6SDavid du Colombier
963179bee6SDavid du Colombier va_start(arg, fmt);
973179bee6SDavid du Colombier vseprint(msg, msg + sizeof msg, fmt, arg);
983179bee6SDavid du Colombier va_end(arg);
993179bee6SDavid du Colombier syslog(0, CRONLOG, msg);
1003179bee6SDavid du Colombier }
1013179bee6SDavid du Colombier
102e329647eSDavid du Colombier static void
fatal(char * fmt,...)103e329647eSDavid du Colombier fatal(char *fmt, ...)
104e329647eSDavid du Colombier {
105e329647eSDavid du Colombier char msg[256];
106e329647eSDavid du Colombier va_list arg;
107e329647eSDavid du Colombier
108e329647eSDavid du Colombier va_start(arg, fmt);
109e329647eSDavid du Colombier vseprint(msg, msg + sizeof msg, fmt, arg);
110e329647eSDavid du Colombier va_end(arg);
111e329647eSDavid du Colombier clog("%s", msg);
112e329647eSDavid du Colombier error("%s", msg);
113e329647eSDavid du Colombier }
114e329647eSDavid du Colombier
115e329647eSDavid du Colombier static int
openlock(char * file)116e329647eSDavid du Colombier openlock(char *file)
117e329647eSDavid du Colombier {
118e329647eSDavid du Colombier return create(file, ORDWR, 0600);
119e329647eSDavid du Colombier }
120e329647eSDavid du Colombier
121e329647eSDavid du Colombier static int
mklock(char * file)122e329647eSDavid du Colombier mklock(char *file)
123e329647eSDavid du Colombier {
12414cc0f53SDavid du Colombier int fd, try;
125e329647eSDavid du Colombier Dir *dir;
126e329647eSDavid du Colombier
127e329647eSDavid du Colombier fd = openlock(file);
12814cc0f53SDavid du Colombier if (fd >= 0) {
129e329647eSDavid du Colombier /* make it a lock file if it wasn't */
130e329647eSDavid du Colombier dir = dirfstat(fd);
131e329647eSDavid du Colombier if (dir == nil)
132e329647eSDavid du Colombier error("%s vanished: %r", file);
133e329647eSDavid du Colombier dir->mode |= DMEXCL;
134e329647eSDavid du Colombier dir->qid.type |= QTEXCL;
135e329647eSDavid du Colombier dirfwstat(fd, dir);
136e329647eSDavid du Colombier free(dir);
137e329647eSDavid du Colombier
138e329647eSDavid du Colombier /* reopen in case it wasn't a lock file at last open */
139e329647eSDavid du Colombier close(fd);
14014cc0f53SDavid du Colombier }
14114cc0f53SDavid du Colombier for (try = 0; try < 65 && (fd = openlock(file)) < 0; try++)
14214cc0f53SDavid du Colombier sleep(10*1000);
14314cc0f53SDavid du Colombier return fd;
144e329647eSDavid du Colombier }
145e329647eSDavid du Colombier
146219b2ee8SDavid du Colombier void
main(int argc,char * argv[])147219b2ee8SDavid du Colombier main(int argc, char *argv[])
148219b2ee8SDavid du Colombier {
149219b2ee8SDavid du Colombier Job *j;
150219b2ee8SDavid du Colombier Tm tm;
151219b2ee8SDavid du Colombier Time t;
1525344664fSDavid du Colombier ulong now, last; /* in seconds */
153e329647eSDavid du Colombier int i, lock;
154219b2ee8SDavid du Colombier
155219b2ee8SDavid du Colombier debug = 0;
156219b2ee8SDavid du Colombier ARGBEGIN{
157219b2ee8SDavid du Colombier case 'c':
158219b2ee8SDavid du Colombier createuser();
159219b2ee8SDavid du Colombier exits(0);
160219b2ee8SDavid du Colombier case 'd':
161219b2ee8SDavid du Colombier debug = 1;
162219b2ee8SDavid du Colombier break;
163219b2ee8SDavid du Colombier default:
164219b2ee8SDavid du Colombier usage();
165219b2ee8SDavid du Colombier }ARGEND
166219b2ee8SDavid du Colombier
167219b2ee8SDavid du Colombier if(debug){
168219b2ee8SDavid du Colombier readalljobs();
169219b2ee8SDavid du Colombier printjobs();
170219b2ee8SDavid du Colombier exits(0);
171219b2ee8SDavid du Colombier }
172219b2ee8SDavid du Colombier
173684b447eSDavid du Colombier initcap(); /* do this early, before cpurc removes it */
1743ff48bf5SDavid du Colombier
175219b2ee8SDavid du Colombier switch(fork()){
176219b2ee8SDavid du Colombier case -1:
177*4377bcc7SDavid du Colombier fatal("can't fork: %r");
178219b2ee8SDavid du Colombier case 0:
179219b2ee8SDavid du Colombier break;
180219b2ee8SDavid du Colombier default:
181219b2ee8SDavid du Colombier exits(0);
182219b2ee8SDavid du Colombier }
183219b2ee8SDavid du Colombier
18414cc0f53SDavid du Colombier /*
18514cc0f53SDavid du Colombier * it can take a few minutes before the file server notices that
18614cc0f53SDavid du Colombier * we've rebooted and gives up the lock.
18714cc0f53SDavid du Colombier */
18814cc0f53SDavid du Colombier lock = mklock("/cron/lock");
18914cc0f53SDavid du Colombier if (lock < 0)
19014cc0f53SDavid du Colombier fatal("cron already running: %r");
19114cc0f53SDavid du Colombier
192219b2ee8SDavid du Colombier argv0 = "cron";
193219b2ee8SDavid du Colombier srand(getpid()*time(0));
1945344664fSDavid du Colombier last = time(0);
195219b2ee8SDavid du Colombier for(;;){
196219b2ee8SDavid du Colombier readalljobs();
1975344664fSDavid du Colombier /*
1985344664fSDavid du Colombier * the system's notion of time may have jumped forward or
1995344664fSDavid du Colombier * backward an arbitrary amount since the last call to time().
2005344664fSDavid du Colombier */
2015344664fSDavid du Colombier now = time(0);
2025344664fSDavid du Colombier /*
2035344664fSDavid du Colombier * if time has jumped backward, just note it and adapt.
2045344664fSDavid du Colombier * if time has jumped forward more than a day,
2055344664fSDavid du Colombier * just execute one day's jobs.
2065344664fSDavid du Colombier */
2075344664fSDavid du Colombier if (now < last) {
2083179bee6SDavid du Colombier clog("time went backward");
2095344664fSDavid du Colombier last = now;
2105344664fSDavid du Colombier } else if (now - last > Day) {
2113179bee6SDavid du Colombier clog("time advanced more than a day");
2125344664fSDavid du Colombier last = now - Day;
2135344664fSDavid du Colombier }
2145344664fSDavid du Colombier now = minute(now);
2155344664fSDavid du Colombier for(last = minute(last); last <= now; last += Minute){
2165344664fSDavid du Colombier tm = *localtime(last);
21753b9a848SDavid du Colombier t.min = 1ULL << tm.min;
218219b2ee8SDavid du Colombier t.hour = 1 << tm.hour;
219219b2ee8SDavid du Colombier t.wday = 1 << tm.wday;
220219b2ee8SDavid du Colombier t.mday = 1 << tm.mday;
221219b2ee8SDavid du Colombier t.mon = 1 << (tm.mon + 1);
222219b2ee8SDavid du Colombier for(i = 0; i < nuser; i++)
223219b2ee8SDavid du Colombier for(j = users[i].jobs; j; j = j->next)
22453b9a848SDavid du Colombier if(j->time.min & t.min
22553b9a848SDavid du Colombier && j->time.hour & t.hour
226219b2ee8SDavid du Colombier && j->time.wday & t.wday
227219b2ee8SDavid du Colombier && j->time.mday & t.mday
228219b2ee8SDavid du Colombier && j->time.mon & t.mon)
229219b2ee8SDavid du Colombier rexec(&users[i], j);
230219b2ee8SDavid du Colombier }
231e329647eSDavid du Colombier seek(lock, 0, 0);
232e329647eSDavid du Colombier write(lock, "x", 1); /* keep the lock alive */
23353b9a848SDavid du Colombier /*
2345344664fSDavid du Colombier * if we're not at next minute yet, sleep until a second past
2355344664fSDavid du Colombier * (to allow for sleep intervals being approximate),
23653b9a848SDavid du Colombier * which synchronises with minute roll-over as a side-effect.
23753b9a848SDavid du Colombier */
2385344664fSDavid du Colombier sleepuntil(now + Minute + 1);
239219b2ee8SDavid du Colombier }
240b85a8364SDavid du Colombier /* not reached */
241219b2ee8SDavid du Colombier }
242219b2ee8SDavid du Colombier
243219b2ee8SDavid du Colombier void
createuser(void)244219b2ee8SDavid du Colombier createuser(void)
245219b2ee8SDavid du Colombier {
246219b2ee8SDavid du Colombier Dir d;
2479a747e4fSDavid du Colombier char file[128], *user;
248219b2ee8SDavid du Colombier int fd;
249219b2ee8SDavid du Colombier
250219b2ee8SDavid du Colombier user = getuser();
251f54edc78SDavid du Colombier snprint(file, sizeof file, "/cron/%s", user);
2529a747e4fSDavid du Colombier fd = create(file, OREAD, 0755|DMDIR);
253d854de59SDavid du Colombier if(fd < 0)
254*4377bcc7SDavid du Colombier fatal("couldn't create %s: %r", file);
2559a747e4fSDavid du Colombier nulldir(&d);
2569a747e4fSDavid du Colombier d.gid = user;
257219b2ee8SDavid du Colombier dirfwstat(fd, &d);
258219b2ee8SDavid du Colombier close(fd);
259f54edc78SDavid du Colombier snprint(file, sizeof file, "/cron/%s/cron", user);
260219b2ee8SDavid du Colombier fd = create(file, OREAD, 0644);
261d854de59SDavid du Colombier if(fd < 0)
262*4377bcc7SDavid du Colombier fatal("couldn't create %s: %r", file);
2639a747e4fSDavid du Colombier nulldir(&d);
2649a747e4fSDavid du Colombier d.gid = user;
265219b2ee8SDavid du Colombier dirfwstat(fd, &d);
266219b2ee8SDavid du Colombier close(fd);
267219b2ee8SDavid du Colombier }
268219b2ee8SDavid du Colombier
269219b2ee8SDavid du Colombier void
readalljobs(void)270219b2ee8SDavid du Colombier readalljobs(void)
271219b2ee8SDavid du Colombier {
272219b2ee8SDavid du Colombier User *u;
2739a747e4fSDavid du Colombier Dir *d, *du;
2749a747e4fSDavid du Colombier char file[128];
275219b2ee8SDavid du Colombier int i, n, fd;
276219b2ee8SDavid du Colombier
277219b2ee8SDavid du Colombier fd = open("/cron", OREAD);
278219b2ee8SDavid du Colombier if(fd < 0)
279*4377bcc7SDavid du Colombier fatal("can't open /cron: %r");
2809a747e4fSDavid du Colombier while((n = dirread(fd, &d)) > 0){
281219b2ee8SDavid du Colombier for(i = 0; i < n; i++){
282e329647eSDavid du Colombier if(strcmp(d[i].name, "log") == 0 ||
283e329647eSDavid du Colombier !(d[i].qid.type & QTDIR))
284219b2ee8SDavid du Colombier continue;
285219b2ee8SDavid du Colombier if(strcmp(d[i].name, d[i].uid) != 0){
286684b447eSDavid du Colombier syslog(1, CRONLOG, "cron for %s owned by %s",
28753b9a848SDavid du Colombier d[i].name, d[i].uid);
288219b2ee8SDavid du Colombier continue;
289219b2ee8SDavid du Colombier }
290219b2ee8SDavid du Colombier u = newuser(d[i].name);
291f54edc78SDavid du Colombier snprint(file, sizeof file, "/cron/%s/cron", d[i].name);
2929a747e4fSDavid du Colombier du = dirstat(file);
2939a747e4fSDavid du Colombier if(du == nil || qidcmp(u->lastqid, du->qid) != 0){
294219b2ee8SDavid du Colombier freejobs(u->jobs);
295219b2ee8SDavid du Colombier u->jobs = readjobs(file, u);
296219b2ee8SDavid du Colombier }
2979a747e4fSDavid du Colombier free(du);
298219b2ee8SDavid du Colombier }
2999a747e4fSDavid du Colombier free(d);
300219b2ee8SDavid du Colombier }
301219b2ee8SDavid du Colombier close(fd);
302219b2ee8SDavid du Colombier }
303219b2ee8SDavid du Colombier
304219b2ee8SDavid du Colombier /*
305219b2ee8SDavid du Colombier * parse user's cron file
306219b2ee8SDavid du Colombier * other lines: minute hour monthday month weekday host command
307219b2ee8SDavid du Colombier */
308219b2ee8SDavid du Colombier Job *
readjobs(char * file,User * user)309219b2ee8SDavid du Colombier readjobs(char *file, User *user)
310219b2ee8SDavid du Colombier {
311219b2ee8SDavid du Colombier Biobuf *b;
312219b2ee8SDavid du Colombier Job *j, *jobs;
3139a747e4fSDavid du Colombier Dir *d;
314219b2ee8SDavid du Colombier int line;
315219b2ee8SDavid du Colombier
3169a747e4fSDavid du Colombier d = dirstat(file);
3179a747e4fSDavid du Colombier if(!d)
3187dd7cddfSDavid du Colombier return nil;
3199a747e4fSDavid du Colombier b = Bopen(file, OREAD);
3209a747e4fSDavid du Colombier if(!b){
3219a747e4fSDavid du Colombier free(d);
3229a747e4fSDavid du Colombier return nil;
3239a747e4fSDavid du Colombier }
3247dd7cddfSDavid du Colombier jobs = nil;
3259a747e4fSDavid du Colombier user->lastqid = d->qid;
3269a747e4fSDavid du Colombier free(d);
327219b2ee8SDavid du Colombier for(line = 1; savec = Brdline(b, '\n'); line++){
328219b2ee8SDavid du Colombier savec[Blinelen(b) - 1] = '\0';
329219b2ee8SDavid du Colombier while(*savec == ' ' || *savec == '\t')
330219b2ee8SDavid du Colombier savec++;
331219b2ee8SDavid du Colombier if(*savec == '#' || *savec == '\0')
332219b2ee8SDavid du Colombier continue;
333219b2ee8SDavid du Colombier if(strlen(savec) > 1024){
3343179bee6SDavid du Colombier clog("%s: line %d: line too long", user->name, line);
335219b2ee8SDavid du Colombier continue;
336219b2ee8SDavid du Colombier }
337219b2ee8SDavid du Colombier j = emalloc(sizeof *j);
33853b9a848SDavid du Colombier j->time.min = gettime(0, 59);
33953b9a848SDavid du Colombier if(j->time.min && (j->time.hour = gettime(0, 23))
340219b2ee8SDavid du Colombier && (j->time.mday = gettime(1, 31))
341219b2ee8SDavid du Colombier && (j->time.mon = gettime(1, 12))
342219b2ee8SDavid du Colombier && (j->time.wday = gettime(0, 6))
3439a747e4fSDavid du Colombier && getname(&j->host)){
344219b2ee8SDavid du Colombier j->cmd = emalloc(strlen(savec) + 1);
345219b2ee8SDavid du Colombier strcpy(j->cmd, savec);
346219b2ee8SDavid du Colombier j->next = jobs;
347219b2ee8SDavid du Colombier jobs = j;
348219b2ee8SDavid du Colombier }else{
3493179bee6SDavid du Colombier clog("%s: line %d: syntax error", user->name, line);
350219b2ee8SDavid du Colombier free(j);
351219b2ee8SDavid du Colombier }
352219b2ee8SDavid du Colombier }
353219b2ee8SDavid du Colombier Bterm(b);
354219b2ee8SDavid du Colombier return jobs;
355219b2ee8SDavid du Colombier }
356219b2ee8SDavid du Colombier
357219b2ee8SDavid du Colombier void
printjobs(void)358219b2ee8SDavid du Colombier printjobs(void)
359219b2ee8SDavid du Colombier {
360219b2ee8SDavid du Colombier char buf[8*1024];
361219b2ee8SDavid du Colombier Job *j;
362219b2ee8SDavid du Colombier int i;
363219b2ee8SDavid du Colombier
364219b2ee8SDavid du Colombier for(i = 0; i < nuser; i++){
365219b2ee8SDavid du Colombier print("user %s\n", users[i].name);
36653b9a848SDavid du Colombier for(j = users[i].jobs; j; j = j->next)
367219b2ee8SDavid du Colombier if(!mkcmd(j->cmd, buf, sizeof buf))
36853b9a848SDavid du Colombier print("\tbad job %s on host %s\n",
36953b9a848SDavid du Colombier j->cmd, j->host);
370219b2ee8SDavid du Colombier else
371219b2ee8SDavid du Colombier print("\tjob %s on host %s\n", buf, j->host);
372219b2ee8SDavid du Colombier }
373219b2ee8SDavid du Colombier }
374219b2ee8SDavid du Colombier
375219b2ee8SDavid du Colombier User *
newuser(char * name)376219b2ee8SDavid du Colombier newuser(char *name)
377219b2ee8SDavid du Colombier {
378219b2ee8SDavid du Colombier int i;
379219b2ee8SDavid du Colombier
380219b2ee8SDavid du Colombier for(i = 0; i < nuser; i++)
381219b2ee8SDavid du Colombier if(strcmp(users[i].name, name) == 0)
382219b2ee8SDavid du Colombier return &users[i];
383219b2ee8SDavid du Colombier if(nuser == maxuser){
384219b2ee8SDavid du Colombier maxuser += 32;
385219b2ee8SDavid du Colombier users = erealloc(users, maxuser * sizeof *users);
386219b2ee8SDavid du Colombier }
3877dd7cddfSDavid du Colombier memset(&users[nuser], 0, sizeof(users[nuser]));
3889a747e4fSDavid du Colombier users[nuser].name = strdup(name);
389219b2ee8SDavid du Colombier users[nuser].jobs = 0;
3909a747e4fSDavid du Colombier users[nuser].lastqid.type = QTFILE;
3919a747e4fSDavid du Colombier users[nuser].lastqid.path = ~0LL;
3929a747e4fSDavid du Colombier users[nuser].lastqid.vers = ~0L;
393219b2ee8SDavid du Colombier return &users[nuser++];
394219b2ee8SDavid du Colombier }
395219b2ee8SDavid du Colombier
396219b2ee8SDavid du Colombier void
freejobs(Job * j)397219b2ee8SDavid du Colombier freejobs(Job *j)
398219b2ee8SDavid du Colombier {
3997dd7cddfSDavid du Colombier Job *next;
400219b2ee8SDavid du Colombier
4017dd7cddfSDavid du Colombier for(; j; j = next){
4027dd7cddfSDavid du Colombier next = j->next;
4037dd7cddfSDavid du Colombier free(j->cmd);
4049a747e4fSDavid du Colombier free(j->host);
4057dd7cddfSDavid du Colombier free(j);
406219b2ee8SDavid du Colombier }
407219b2ee8SDavid du Colombier }
408219b2ee8SDavid du Colombier
409219b2ee8SDavid du Colombier int
getname(char ** namep)4109a747e4fSDavid du Colombier getname(char **namep)
411219b2ee8SDavid du Colombier {
412219b2ee8SDavid du Colombier int c;
4139a747e4fSDavid du Colombier char buf[64], *p;
414219b2ee8SDavid du Colombier
415219b2ee8SDavid du Colombier if(!savec)
416219b2ee8SDavid du Colombier return 0;
417219b2ee8SDavid du Colombier while(*savec == ' ' || *savec == '\t')
418219b2ee8SDavid du Colombier savec++;
4199a747e4fSDavid du Colombier for(p = buf; (c = *savec) && c != ' ' && c != '\t'; p++){
4209a747e4fSDavid du Colombier if(p >= buf+sizeof buf -1)
421219b2ee8SDavid du Colombier return 0;
4229a747e4fSDavid du Colombier *p = *savec++;
423219b2ee8SDavid du Colombier }
4249a747e4fSDavid du Colombier *p = '\0';
4259a747e4fSDavid du Colombier *namep = strdup(buf);
4269a747e4fSDavid du Colombier if(*namep == 0){
4273179bee6SDavid du Colombier clog("internal error: strdup failure");
4289a747e4fSDavid du Colombier _exits(0);
4299a747e4fSDavid du Colombier }
430219b2ee8SDavid du Colombier while(*savec == ' ' || *savec == '\t')
431219b2ee8SDavid du Colombier savec++;
4329a747e4fSDavid du Colombier return p > buf;
433219b2ee8SDavid du Colombier }
434219b2ee8SDavid du Colombier
435219b2ee8SDavid du Colombier /*
43653b9a848SDavid du Colombier * return the next time range (as a bit vector) in the file:
437219b2ee8SDavid du Colombier * times: '*'
438219b2ee8SDavid du Colombier * | range
439219b2ee8SDavid du Colombier * range: number
440219b2ee8SDavid du Colombier * | number '-' number
441219b2ee8SDavid du Colombier * | range ',' range
442219b2ee8SDavid du Colombier * a return of zero means a syntax error was discovered
443219b2ee8SDavid du Colombier */
44453b9a848SDavid du Colombier uvlong
gettime(int min,int max)445219b2ee8SDavid du Colombier gettime(int min, int max)
446219b2ee8SDavid du Colombier {
44753b9a848SDavid du Colombier uvlong n, m, e;
448219b2ee8SDavid du Colombier
449219b2ee8SDavid du Colombier if(gettok(min, max) == '*')
45053b9a848SDavid du Colombier return ~0ULL;
451219b2ee8SDavid du Colombier n = 0;
452219b2ee8SDavid du Colombier while(tok == '1'){
45353b9a848SDavid du Colombier m = 1ULL << lexval;
454219b2ee8SDavid du Colombier n |= m;
455219b2ee8SDavid du Colombier if(gettok(0, 0) == '-'){
456219b2ee8SDavid du Colombier if(gettok(lexval, max) != '1')
457219b2ee8SDavid du Colombier return 0;
45853b9a848SDavid du Colombier e = 1ULL << lexval;
459219b2ee8SDavid du Colombier for( ; m <= e; m <<= 1)
460219b2ee8SDavid du Colombier n |= m;
461219b2ee8SDavid du Colombier gettok(min, max);
462219b2ee8SDavid du Colombier }
463219b2ee8SDavid du Colombier if(tok != ',')
464219b2ee8SDavid du Colombier break;
465219b2ee8SDavid du Colombier if(gettok(min, max) != '1')
466219b2ee8SDavid du Colombier return 0;
467219b2ee8SDavid du Colombier }
468219b2ee8SDavid du Colombier pushtok();
469219b2ee8SDavid du Colombier return n;
470219b2ee8SDavid du Colombier }
471219b2ee8SDavid du Colombier
472219b2ee8SDavid du Colombier void
pushtok(void)473219b2ee8SDavid du Colombier pushtok(void)
474219b2ee8SDavid du Colombier {
475219b2ee8SDavid du Colombier savec = savetok;
476219b2ee8SDavid du Colombier }
477219b2ee8SDavid du Colombier
478219b2ee8SDavid du Colombier int
gettok(int min,int max)479219b2ee8SDavid du Colombier gettok(int min, int max)
480219b2ee8SDavid du Colombier {
481219b2ee8SDavid du Colombier char c;
482219b2ee8SDavid du Colombier
483219b2ee8SDavid du Colombier savetok = savec;
484219b2ee8SDavid du Colombier if(!savec)
485219b2ee8SDavid du Colombier return tok = 0;
486219b2ee8SDavid du Colombier while((c = *savec) == ' ' || c == '\t')
487219b2ee8SDavid du Colombier savec++;
488219b2ee8SDavid du Colombier switch(c){
489219b2ee8SDavid du Colombier case '0': case '1': case '2': case '3': case '4':
490219b2ee8SDavid du Colombier case '5': case '6': case '7': case '8': case '9':
491219b2ee8SDavid du Colombier lexval = strtoul(savec, &savec, 10);
492219b2ee8SDavid du Colombier if(lexval < min || lexval > max)
493219b2ee8SDavid du Colombier return tok = 0;
494219b2ee8SDavid du Colombier return tok = '1';
495219b2ee8SDavid du Colombier case '*': case '-': case ',':
496219b2ee8SDavid du Colombier savec++;
497219b2ee8SDavid du Colombier return tok = c;
498219b2ee8SDavid du Colombier default:
499219b2ee8SDavid du Colombier return tok = 0;
500219b2ee8SDavid du Colombier }
501219b2ee8SDavid du Colombier }
502219b2ee8SDavid du Colombier
503219b2ee8SDavid du Colombier int
call(char * host)504219b2ee8SDavid du Colombier call(char *host)
505219b2ee8SDavid du Colombier {
506219b2ee8SDavid du Colombier char *na, *p;
507219b2ee8SDavid du Colombier
508219b2ee8SDavid du Colombier na = netmkaddr(host, 0, "rexexec");
509219b2ee8SDavid du Colombier p = utfrune(na, L'!');
510219b2ee8SDavid du Colombier if(!p)
511219b2ee8SDavid du Colombier return -1;
512219b2ee8SDavid du Colombier p = utfrune(p+1, L'!');
513219b2ee8SDavid du Colombier if(!p)
514219b2ee8SDavid du Colombier return -1;
515219b2ee8SDavid du Colombier if(strcmp(p, "!rexexec") != 0)
516219b2ee8SDavid du Colombier return -2;
517219b2ee8SDavid du Colombier return dial(na, 0, 0, 0);
518219b2ee8SDavid du Colombier }
519219b2ee8SDavid du Colombier
520219b2ee8SDavid du Colombier /*
521219b2ee8SDavid du Colombier * convert command to run properly on the remote machine
522208510e1SDavid du Colombier * need to escape the quotes so they don't get stripped
523219b2ee8SDavid du Colombier */
524219b2ee8SDavid du Colombier int
mkcmd(char * cmd,char * buf,int len)525219b2ee8SDavid du Colombier mkcmd(char *cmd, char *buf, int len)
526219b2ee8SDavid du Colombier {
527219b2ee8SDavid du Colombier char *p;
528219b2ee8SDavid du Colombier int n, m;
529219b2ee8SDavid du Colombier
530219b2ee8SDavid du Colombier n = sizeof "exec rc -c '" -1;
531219b2ee8SDavid du Colombier if(n >= len)
532219b2ee8SDavid du Colombier return 0;
533219b2ee8SDavid du Colombier strcpy(buf, "exec rc -c '");
534219b2ee8SDavid du Colombier while(p = utfrune(cmd, L'\'')){
535219b2ee8SDavid du Colombier p++;
536219b2ee8SDavid du Colombier m = p - cmd;
537219b2ee8SDavid du Colombier if(n + m + 1 >= len)
538219b2ee8SDavid du Colombier return 0;
539219b2ee8SDavid du Colombier strncpy(&buf[n], cmd, m);
540219b2ee8SDavid du Colombier n += m;
541219b2ee8SDavid du Colombier buf[n++] = '\'';
542219b2ee8SDavid du Colombier cmd = p;
543219b2ee8SDavid du Colombier }
544219b2ee8SDavid du Colombier m = strlen(cmd);
545219b2ee8SDavid du Colombier if(n + m + sizeof "'</dev/null>/dev/null>[2=1]" >= len)
546219b2ee8SDavid du Colombier return 0;
547219b2ee8SDavid du Colombier strcpy(&buf[n], cmd);
548219b2ee8SDavid du Colombier strcpy(&buf[n+m], "'</dev/null>/dev/null>[2=1]");
549219b2ee8SDavid du Colombier return 1;
550219b2ee8SDavid du Colombier }
551219b2ee8SDavid du Colombier
552219b2ee8SDavid du Colombier void
rexec(User * user,Job * j)553219b2ee8SDavid du Colombier rexec(User *user, Job *j)
554219b2ee8SDavid du Colombier {
5553ff48bf5SDavid du Colombier char buf[8*1024];
5567dd7cddfSDavid du Colombier int n, fd;
5573ff48bf5SDavid du Colombier AuthInfo *ai;
558219b2ee8SDavid du Colombier
559219b2ee8SDavid du Colombier switch(rfork(RFPROC|RFNOWAIT|RFNAMEG|RFENVG|RFFDG)){
560219b2ee8SDavid du Colombier case 0:
561219b2ee8SDavid du Colombier break;
562219b2ee8SDavid du Colombier case -1:
5633179bee6SDavid du Colombier clog("can't fork a job for %s: %r\n", user->name);
564219b2ee8SDavid du Colombier default:
565219b2ee8SDavid du Colombier return;
566219b2ee8SDavid du Colombier }
567219b2ee8SDavid du Colombier
568219b2ee8SDavid du Colombier if(!mkcmd(j->cmd, buf, sizeof buf)){
5693179bee6SDavid du Colombier clog("internal error: cmd buffer overflow");
5703179bee6SDavid du Colombier _exits(0);
5713179bee6SDavid du Colombier }
5723179bee6SDavid du Colombier
5733179bee6SDavid du Colombier /*
5743179bee6SDavid du Colombier * local call, auth, cmd with no i/o
5753179bee6SDavid du Colombier */
5763179bee6SDavid du Colombier if(strcmp(j->host, "local") == 0){
5773179bee6SDavid du Colombier if(becomeuser(user->name) < 0){
5783179bee6SDavid du Colombier clog("%s: can't change uid for %s on %s: %r",
5793179bee6SDavid du Colombier user->name, j->cmd, j->host);
5803179bee6SDavid du Colombier _exits(0);
5813179bee6SDavid du Colombier }
5823179bee6SDavid du Colombier putenv("service", "rx");
5833179bee6SDavid du Colombier clog("%s: ran '%s' on %s", user->name, j->cmd, j->host);
5843179bee6SDavid du Colombier execl("/bin/rc", "rc", "-lc", buf, nil);
5853179bee6SDavid du Colombier clog("%s: exec failed for %s on %s: %r",
5863179bee6SDavid du Colombier user->name, j->cmd, j->host);
587219b2ee8SDavid du Colombier _exits(0);
588219b2ee8SDavid du Colombier }
589219b2ee8SDavid du Colombier
590219b2ee8SDavid du Colombier /*
591219b2ee8SDavid du Colombier * remote call, auth, cmd with no i/o
592219b2ee8SDavid du Colombier * give it 2 min to complete
593219b2ee8SDavid du Colombier */
59453b9a848SDavid du Colombier alarm(2*Minute*1000);
595219b2ee8SDavid du Colombier fd = call(j->host);
596219b2ee8SDavid du Colombier if(fd < 0){
59753b9a848SDavid du Colombier if(fd == -2)
5983179bee6SDavid du Colombier clog("%s: dangerous host %s", user->name, j->host);
5993179bee6SDavid du Colombier clog("%s: can't call %s: %r", user->name, j->host);
600219b2ee8SDavid du Colombier _exits(0);
601219b2ee8SDavid du Colombier }
6023179bee6SDavid du Colombier clog("%s: called %s on %s", user->name, j->cmd, j->host);
6033ff48bf5SDavid du Colombier if(becomeuser(user->name) < 0){
6043179bee6SDavid du Colombier clog("%s: can't change uid for %s on %s: %r",
60553b9a848SDavid du Colombier user->name, j->cmd, j->host);
6063ff48bf5SDavid du Colombier _exits(0);
6073ff48bf5SDavid du Colombier }
6083ff48bf5SDavid du Colombier ai = auth_proxy(fd, nil, "proto=p9any role=client");
6093ff48bf5SDavid du Colombier if(ai == nil){
6103179bee6SDavid du Colombier clog("%s: can't authenticate for %s on %s: %r",
61153b9a848SDavid du Colombier user->name, j->cmd, j->host);
612219b2ee8SDavid du Colombier _exits(0);
613219b2ee8SDavid du Colombier }
6143179bee6SDavid du Colombier clog("%s: authenticated %s on %s", user->name, j->cmd, j->host);
615219b2ee8SDavid du Colombier write(fd, buf, strlen(buf)+1);
616219b2ee8SDavid du Colombier write(fd, buf, 0);
6177dd7cddfSDavid du Colombier while((n = read(fd, buf, sizeof(buf)-1)) > 0){
6187dd7cddfSDavid du Colombier buf[n] = 0;
6193179bee6SDavid du Colombier clog("%s: %s\n", j->cmd, buf);
6207dd7cddfSDavid du Colombier }
621219b2ee8SDavid du Colombier _exits(0);
622219b2ee8SDavid du Colombier }
623219b2ee8SDavid du Colombier
624219b2ee8SDavid du Colombier void *
emalloc(ulong n)625219b2ee8SDavid du Colombier emalloc(ulong n)
626219b2ee8SDavid du Colombier {
627219b2ee8SDavid du Colombier void *p;
628219b2ee8SDavid du Colombier
6297dd7cddfSDavid du Colombier if(p = mallocz(n, 1))
630219b2ee8SDavid du Colombier return p;
631e329647eSDavid du Colombier fatal("out of memory");
632219b2ee8SDavid du Colombier return 0;
633219b2ee8SDavid du Colombier }
634219b2ee8SDavid du Colombier
635219b2ee8SDavid du Colombier void *
erealloc(void * p,ulong n)636219b2ee8SDavid du Colombier erealloc(void *p, ulong n)
637219b2ee8SDavid du Colombier {
638219b2ee8SDavid du Colombier if(p = realloc(p, n))
639219b2ee8SDavid du Colombier return p;
640e329647eSDavid du Colombier fatal("out of memory");
641219b2ee8SDavid du Colombier return 0;
642219b2ee8SDavid du Colombier }
643219b2ee8SDavid du Colombier
644219b2ee8SDavid du Colombier void
usage(void)645219b2ee8SDavid du Colombier usage(void)
646219b2ee8SDavid du Colombier {
647219b2ee8SDavid du Colombier fprint(2, "usage: cron [-c]\n");
648219b2ee8SDavid du Colombier exits("usage");
649219b2ee8SDavid du Colombier }
650219b2ee8SDavid du Colombier
651219b2ee8SDavid du Colombier int
qidcmp(Qid a,Qid b)6527dd7cddfSDavid du Colombier qidcmp(Qid a, Qid b)
6537dd7cddfSDavid du Colombier {
6547dd7cddfSDavid du Colombier /* might be useful to know if a > b, but not for cron */
6557dd7cddfSDavid du Colombier return(a.path != b.path || a.vers != b.vers);
6567dd7cddfSDavid du Colombier }
6573ff48bf5SDavid du Colombier
6583ff48bf5SDavid du Colombier void
memrandom(void * p,int n)6593ff48bf5SDavid du Colombier memrandom(void *p, int n)
6603ff48bf5SDavid du Colombier {
6613ff48bf5SDavid du Colombier uchar *cp;
6623ff48bf5SDavid du Colombier
6633ff48bf5SDavid du Colombier for(cp = (uchar*)p; n > 0; n--)
6643ff48bf5SDavid du Colombier *cp++ = fastrand();
6653ff48bf5SDavid du Colombier }
6663ff48bf5SDavid du Colombier
6673ff48bf5SDavid du Colombier /*
6683ff48bf5SDavid du Colombier * keep caphash fd open since opens of it could be disabled
6693ff48bf5SDavid du Colombier */
6703ff48bf5SDavid du Colombier static int caphashfd;
6713ff48bf5SDavid du Colombier
6723ff48bf5SDavid du Colombier void
initcap(void)6733ff48bf5SDavid du Colombier initcap(void)
6743ff48bf5SDavid du Colombier {
6754dc626cdSDavid du Colombier caphashfd = open("#¤/caphash", OCEXEC|OWRITE);
6763ff48bf5SDavid du Colombier if(caphashfd < 0)
67717629263SDavid du Colombier fprint(2, "%s: opening #¤/caphash: %r\n", argv0);
6783ff48bf5SDavid du Colombier }
6793ff48bf5SDavid du Colombier
6803ff48bf5SDavid du Colombier /*
6813ff48bf5SDavid du Colombier * create a change uid capability
6823ff48bf5SDavid du Colombier */
6833ff48bf5SDavid du Colombier char*
mkcap(char * from,char * to)6843ff48bf5SDavid du Colombier mkcap(char *from, char *to)
6853ff48bf5SDavid du Colombier {
6863ff48bf5SDavid du Colombier uchar rand[20];
6873ff48bf5SDavid du Colombier char *cap;
6883ff48bf5SDavid du Colombier char *key;
689f54edc78SDavid du Colombier int nfrom, nto, ncap;
6903ff48bf5SDavid du Colombier uchar hash[SHA1dlen];
6913ff48bf5SDavid du Colombier
6923ff48bf5SDavid du Colombier if(caphashfd < 0)
6933ff48bf5SDavid du Colombier return nil;
6943ff48bf5SDavid du Colombier
6953ff48bf5SDavid du Colombier /* create the capability */
6963ff48bf5SDavid du Colombier nto = strlen(to);
6973ff48bf5SDavid du Colombier nfrom = strlen(from);
698f54edc78SDavid du Colombier ncap = nfrom + 1 + nto + 1 + sizeof(rand)*3 + 1;
699f54edc78SDavid du Colombier cap = emalloc(ncap);
700f54edc78SDavid du Colombier snprint(cap, ncap, "%s@%s", from, to);
7013ff48bf5SDavid du Colombier memrandom(rand, sizeof(rand));
7023ff48bf5SDavid du Colombier key = cap+nfrom+1+nto+1;
7033ff48bf5SDavid du Colombier enc64(key, sizeof(rand)*3, rand, sizeof(rand));
7043ff48bf5SDavid du Colombier
7053ff48bf5SDavid du Colombier /* hash the capability */
7063ff48bf5SDavid du Colombier hmac_sha1((uchar*)cap, strlen(cap), (uchar*)key, strlen(key), hash, nil);
7073ff48bf5SDavid du Colombier
7083ff48bf5SDavid du Colombier /* give the kernel the hash */
7093ff48bf5SDavid du Colombier key[-1] = '@';
7103ff48bf5SDavid du Colombier if(write(caphashfd, hash, SHA1dlen) < 0){
7113ff48bf5SDavid du Colombier free(cap);
7123ff48bf5SDavid du Colombier return nil;
7133ff48bf5SDavid du Colombier }
7143ff48bf5SDavid du Colombier
7153ff48bf5SDavid du Colombier return cap;
7163ff48bf5SDavid du Colombier }
7173ff48bf5SDavid du Colombier
7183ff48bf5SDavid du Colombier int
usecap(char * cap)7193ff48bf5SDavid du Colombier usecap(char *cap)
7203ff48bf5SDavid du Colombier {
7213ff48bf5SDavid du Colombier int fd, rv;
7223ff48bf5SDavid du Colombier
7233ff48bf5SDavid du Colombier fd = open("#¤/capuse", OWRITE);
7243ff48bf5SDavid du Colombier if(fd < 0)
7253ff48bf5SDavid du Colombier return -1;
7263ff48bf5SDavid du Colombier rv = write(fd, cap, strlen(cap));
7273ff48bf5SDavid du Colombier close(fd);
7283ff48bf5SDavid du Colombier return rv;
7293ff48bf5SDavid du Colombier }
7303ff48bf5SDavid du Colombier
7313ff48bf5SDavid du Colombier int
becomeuser(char * new)7323ff48bf5SDavid du Colombier becomeuser(char *new)
7333ff48bf5SDavid du Colombier {
7343ff48bf5SDavid du Colombier char *cap;
7353ff48bf5SDavid du Colombier int rv;
7363179bee6SDavid du Colombier
7373ff48bf5SDavid du Colombier cap = mkcap(getuser(), new);
7383ff48bf5SDavid du Colombier if(cap == nil)
7393ff48bf5SDavid du Colombier return -1;
7403ff48bf5SDavid du Colombier rv = usecap(cap);
7413ff48bf5SDavid du Colombier free(cap);
7423ff48bf5SDavid du Colombier
7433ff48bf5SDavid du Colombier newns(new, nil);
7443ff48bf5SDavid du Colombier return rv;
7453ff48bf5SDavid du Colombier }
746