xref: /plan9-contrib/sys/src/cmd/auth/cron.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1*219b2ee8SDavid du Colombier #include <u.h>
2*219b2ee8SDavid du Colombier #include <libc.h>
3*219b2ee8SDavid du Colombier #include <bio.h>
4*219b2ee8SDavid du Colombier #include <auth.h>
5*219b2ee8SDavid du Colombier #include "authsrv.h"
6*219b2ee8SDavid du Colombier 
7*219b2ee8SDavid du Colombier char CRONLOG[] = "cron";
8*219b2ee8SDavid du Colombier 
9*219b2ee8SDavid du Colombier typedef struct Job	Job;
10*219b2ee8SDavid du Colombier typedef struct Time	Time;
11*219b2ee8SDavid du Colombier typedef struct User	User;
12*219b2ee8SDavid du Colombier 
13*219b2ee8SDavid du Colombier struct Time{			/* bit masks for each valid time */
14*219b2ee8SDavid du Colombier 	ulong	min;			/* actually 1 bit for every 2 min */
15*219b2ee8SDavid du Colombier 	ulong	hour;
16*219b2ee8SDavid du Colombier 	ulong	mday;
17*219b2ee8SDavid du Colombier 	ulong	wday;
18*219b2ee8SDavid du Colombier 	ulong	mon;
19*219b2ee8SDavid du Colombier };
20*219b2ee8SDavid du Colombier 
21*219b2ee8SDavid du Colombier struct Job{
22*219b2ee8SDavid du Colombier 	char	host[NAMELEN];		/* where ... */
23*219b2ee8SDavid du Colombier 	Time	time;			/* when ... */
24*219b2ee8SDavid du Colombier 	char	*cmd;			/* and what to execute */
25*219b2ee8SDavid du Colombier 	Job	*next;
26*219b2ee8SDavid du Colombier };
27*219b2ee8SDavid du Colombier 
28*219b2ee8SDavid du Colombier struct User{
29*219b2ee8SDavid du Colombier 	char	name[NAMELEN];		/* who ... */
30*219b2ee8SDavid du Colombier 	Job	*jobs;			/* wants to execute these jobs */
31*219b2ee8SDavid du Colombier };
32*219b2ee8SDavid du Colombier 
33*219b2ee8SDavid du Colombier User	*users;
34*219b2ee8SDavid du Colombier int	nuser;
35*219b2ee8SDavid du Colombier int	maxuser;
36*219b2ee8SDavid du Colombier char	*savec;
37*219b2ee8SDavid du Colombier char	*savetok;
38*219b2ee8SDavid du Colombier int	tok;
39*219b2ee8SDavid du Colombier int	debug;
40*219b2ee8SDavid du Colombier ulong	lexval;
41*219b2ee8SDavid du Colombier 
42*219b2ee8SDavid du Colombier void	rexec(User*, Job*);
43*219b2ee8SDavid du Colombier void	readalljobs(void);
44*219b2ee8SDavid du Colombier Job	*readjobs(char*, User*);
45*219b2ee8SDavid du Colombier int	getname(char*);
46*219b2ee8SDavid du Colombier ulong	gettime(int, int);
47*219b2ee8SDavid du Colombier int	gettok(int, int);
48*219b2ee8SDavid du Colombier void	pushtok(void);
49*219b2ee8SDavid du Colombier void	usage(void);
50*219b2ee8SDavid du Colombier void	freejobs(Job*);
51*219b2ee8SDavid du Colombier User	*newuser(char*);
52*219b2ee8SDavid du Colombier void	*emalloc(ulong);
53*219b2ee8SDavid du Colombier void	*erealloc(void*, ulong);
54*219b2ee8SDavid du Colombier int	myauth(int, char*);
55*219b2ee8SDavid du Colombier void	createuser(void);
56*219b2ee8SDavid du Colombier int	mkcmd(char*, char*, int);
57*219b2ee8SDavid du Colombier void	printjobs(void);
58*219b2ee8SDavid du Colombier 
59*219b2ee8SDavid du Colombier void
60*219b2ee8SDavid du Colombier main(int argc, char *argv[])
61*219b2ee8SDavid du Colombier {
62*219b2ee8SDavid du Colombier 	Job *j;
63*219b2ee8SDavid du Colombier 	Tm tm;
64*219b2ee8SDavid du Colombier 	Time t;
65*219b2ee8SDavid du Colombier 	ulong now, last, x;
66*219b2ee8SDavid du Colombier 	int i;
67*219b2ee8SDavid du Colombier 
68*219b2ee8SDavid du Colombier 	debug = 0;
69*219b2ee8SDavid du Colombier 	ARGBEGIN{
70*219b2ee8SDavid du Colombier 	case 'c':
71*219b2ee8SDavid du Colombier 		createuser();
72*219b2ee8SDavid du Colombier 		exits(0);
73*219b2ee8SDavid du Colombier 	case 'd':
74*219b2ee8SDavid du Colombier 		debug = 1;
75*219b2ee8SDavid du Colombier 		break;
76*219b2ee8SDavid du Colombier 	default:
77*219b2ee8SDavid du Colombier 		usage();
78*219b2ee8SDavid du Colombier 	}ARGEND
79*219b2ee8SDavid du Colombier 	USED(argc, argv);
80*219b2ee8SDavid du Colombier 
81*219b2ee8SDavid du Colombier 	if(debug){
82*219b2ee8SDavid du Colombier 		readalljobs();
83*219b2ee8SDavid du Colombier 		printjobs();
84*219b2ee8SDavid du Colombier 		exits(0);
85*219b2ee8SDavid du Colombier 	}
86*219b2ee8SDavid du Colombier 
87*219b2ee8SDavid du Colombier 	switch(fork()){
88*219b2ee8SDavid du Colombier 	case -1:
89*219b2ee8SDavid du Colombier 		error("can't fork");
90*219b2ee8SDavid du Colombier 	case 0:
91*219b2ee8SDavid du Colombier 		break;
92*219b2ee8SDavid du Colombier 	default:
93*219b2ee8SDavid du Colombier 		exits(0);
94*219b2ee8SDavid du Colombier 	}
95*219b2ee8SDavid du Colombier 
96*219b2ee8SDavid du Colombier 	argv0 = "cron";
97*219b2ee8SDavid du Colombier 	srand(getpid()*time(0));
98*219b2ee8SDavid du Colombier 	last = time(0) / 60;
99*219b2ee8SDavid du Colombier 	for(;;){
100*219b2ee8SDavid du Colombier 		readalljobs();
101*219b2ee8SDavid du Colombier 		now = time(0) / 60;
102*219b2ee8SDavid du Colombier 		for(; last <= now; last += 2){
103*219b2ee8SDavid du Colombier 			tm = *localtime(last*60);
104*219b2ee8SDavid du Colombier 			t.min = 1 << tm.min/2;
105*219b2ee8SDavid du Colombier 			t.hour = 1 << tm.hour;
106*219b2ee8SDavid du Colombier 			t.wday = 1 << tm.wday;
107*219b2ee8SDavid du Colombier 			t.mday = 1 << tm.mday;
108*219b2ee8SDavid du Colombier 			t.mon = 1 << (tm.mon + 1);
109*219b2ee8SDavid du Colombier 			for(i = 0; i < nuser; i++)
110*219b2ee8SDavid du Colombier 				for(j = users[i].jobs; j; j = j->next)
111*219b2ee8SDavid du Colombier 					if(j->time.min & t.min && j->time.hour & t.hour
112*219b2ee8SDavid du Colombier 					&& j->time.wday & t.wday
113*219b2ee8SDavid du Colombier 					&& j->time.mday & t.mday
114*219b2ee8SDavid du Colombier 					&& j->time.mon & t.mon)
115*219b2ee8SDavid du Colombier 						rexec(&users[i], j);
116*219b2ee8SDavid du Colombier 		}
117*219b2ee8SDavid du Colombier 		x = time(0) / 60;
118*219b2ee8SDavid du Colombier 		if(x - now < 2)
119*219b2ee8SDavid du Colombier 			sleep((2 - (x - now))*60*1000);
120*219b2ee8SDavid du Colombier 	}
121*219b2ee8SDavid du Colombier 	exits(0);
122*219b2ee8SDavid du Colombier }
123*219b2ee8SDavid du Colombier 
124*219b2ee8SDavid du Colombier void
125*219b2ee8SDavid du Colombier createuser(void)
126*219b2ee8SDavid du Colombier {
127*219b2ee8SDavid du Colombier 	Dir d;
128*219b2ee8SDavid du Colombier 	char file[3*NAMELEN], *user;
129*219b2ee8SDavid du Colombier 	int fd;
130*219b2ee8SDavid du Colombier 
131*219b2ee8SDavid du Colombier 	user = getuser();
132*219b2ee8SDavid du Colombier 	sprint(file, "/cron/%s", user);
133*219b2ee8SDavid du Colombier 	fd = create(file, OREAD, 0755|CHDIR);
134*219b2ee8SDavid du Colombier 	if(fd < 0){
135*219b2ee8SDavid du Colombier 		fprint(2, "couldn't create %s: %r\n", file);
136*219b2ee8SDavid du Colombier 		exits("create");
137*219b2ee8SDavid du Colombier 	}
138*219b2ee8SDavid du Colombier 	dirfstat(fd, &d);
139*219b2ee8SDavid du Colombier 	strncpy(d.gid, user, NAMELEN);
140*219b2ee8SDavid du Colombier 	dirfwstat(fd, &d);
141*219b2ee8SDavid du Colombier 	close(fd);
142*219b2ee8SDavid du Colombier 	sprint(file, "/cron/%s/cron", user);
143*219b2ee8SDavid du Colombier 	fd = create(file, OREAD, 0644);
144*219b2ee8SDavid du Colombier 	if(fd < 0){
145*219b2ee8SDavid du Colombier 		fprint(2, "couldn't create %s: %r\n", file);
146*219b2ee8SDavid du Colombier 		exits("create");
147*219b2ee8SDavid du Colombier 	}
148*219b2ee8SDavid du Colombier 	dirfstat(fd, &d);
149*219b2ee8SDavid du Colombier 	strncpy(d.gid, user, NAMELEN);
150*219b2ee8SDavid du Colombier 	dirfwstat(fd, &d);
151*219b2ee8SDavid du Colombier 	close(fd);
152*219b2ee8SDavid du Colombier }
153*219b2ee8SDavid du Colombier 
154*219b2ee8SDavid du Colombier void
155*219b2ee8SDavid du Colombier readalljobs(void)
156*219b2ee8SDavid du Colombier {
157*219b2ee8SDavid du Colombier 	User *u;
158*219b2ee8SDavid du Colombier 	Dir d[64], db;
159*219b2ee8SDavid du Colombier 	char file[3*NAMELEN];
160*219b2ee8SDavid du Colombier 	int i, n, fd;
161*219b2ee8SDavid du Colombier 	ulong now;
162*219b2ee8SDavid du Colombier 	static ulong lasttime;
163*219b2ee8SDavid du Colombier 
164*219b2ee8SDavid du Colombier 	now = time(0);
165*219b2ee8SDavid du Colombier 	fd = open("/cron", OREAD);
166*219b2ee8SDavid du Colombier 	if(fd < 0)
167*219b2ee8SDavid du Colombier 		error("can't open /cron\n");
168*219b2ee8SDavid du Colombier 	while((n = dirread(fd, d, sizeof d)) > 0){
169*219b2ee8SDavid du Colombier 		n /= sizeof d[0];
170*219b2ee8SDavid du Colombier 		for(i = 0; i < n; i++){
171*219b2ee8SDavid du Colombier 			if(strcmp(d[i].name, "log") == 0)
172*219b2ee8SDavid du Colombier 				continue;
173*219b2ee8SDavid du Colombier 			if(strcmp(d[i].name, d[i].uid) != 0){
174*219b2ee8SDavid du Colombier 				syslog(1, CRONLOG, "cron for %s owned by %s\n", d[i].name, d[i].uid);
175*219b2ee8SDavid du Colombier 				continue;
176*219b2ee8SDavid du Colombier 			}
177*219b2ee8SDavid du Colombier 			u = newuser(d[i].name);
178*219b2ee8SDavid du Colombier 			sprint(file, "/cron/%s/cron", d[i].name);
179*219b2ee8SDavid du Colombier 			if(dirstat(file, &db) < 0)
180*219b2ee8SDavid du Colombier 				continue;
181*219b2ee8SDavid du Colombier 			if(lasttime < db.mtime){
182*219b2ee8SDavid du Colombier 				freejobs(u->jobs);
183*219b2ee8SDavid du Colombier 				u->jobs = readjobs(file, u);
184*219b2ee8SDavid du Colombier 			}
185*219b2ee8SDavid du Colombier 		}
186*219b2ee8SDavid du Colombier 	}
187*219b2ee8SDavid du Colombier 	lasttime = now;
188*219b2ee8SDavid du Colombier 	close(fd);
189*219b2ee8SDavid du Colombier }
190*219b2ee8SDavid du Colombier 
191*219b2ee8SDavid du Colombier /*
192*219b2ee8SDavid du Colombier  * parse user's cron file
193*219b2ee8SDavid du Colombier  * other lines: minute hour monthday month weekday host command
194*219b2ee8SDavid du Colombier  */
195*219b2ee8SDavid du Colombier Job *
196*219b2ee8SDavid du Colombier readjobs(char *file, User *user)
197*219b2ee8SDavid du Colombier {
198*219b2ee8SDavid du Colombier 	Biobuf *b;
199*219b2ee8SDavid du Colombier 	Job *j, *jobs;
200*219b2ee8SDavid du Colombier 	int line;
201*219b2ee8SDavid du Colombier 
202*219b2ee8SDavid du Colombier 	b = Bopen(file, OREAD);
203*219b2ee8SDavid du Colombier 	if(!b)
204*219b2ee8SDavid du Colombier 		return 0;
205*219b2ee8SDavid du Colombier 	jobs = 0;
206*219b2ee8SDavid du Colombier 	for(line = 1; savec = Brdline(b, '\n'); line++){
207*219b2ee8SDavid du Colombier 		savec[Blinelen(b) - 1] = '\0';
208*219b2ee8SDavid du Colombier 		while(*savec == ' ' || *savec == '\t')
209*219b2ee8SDavid du Colombier 			savec++;
210*219b2ee8SDavid du Colombier 		if(*savec == '#' || *savec == '\0')
211*219b2ee8SDavid du Colombier 			continue;
212*219b2ee8SDavid du Colombier 		if(strlen(savec) > 1024){
213*219b2ee8SDavid du Colombier 			syslog(0, CRONLOG, "%s: line %d: line too long", user->name, line);
214*219b2ee8SDavid du Colombier 			continue;
215*219b2ee8SDavid du Colombier 		}
216*219b2ee8SDavid du Colombier 		j = emalloc(sizeof *j);
217*219b2ee8SDavid du Colombier 		if((j->time.min = gettime(0, 59))
218*219b2ee8SDavid du Colombier 		&& (j->time.hour = gettime(0, 23))
219*219b2ee8SDavid du Colombier 		&& (j->time.mday = gettime(1, 31))
220*219b2ee8SDavid du Colombier 		&& (j->time.mon = gettime(1, 12))
221*219b2ee8SDavid du Colombier 		&& (j->time.wday = gettime(0, 6))
222*219b2ee8SDavid du Colombier 		&& getname(j->host)){
223*219b2ee8SDavid du Colombier 			j->cmd = emalloc(strlen(savec) + 1);
224*219b2ee8SDavid du Colombier 			strcpy(j->cmd, savec);
225*219b2ee8SDavid du Colombier 			j->next = jobs;
226*219b2ee8SDavid du Colombier 			jobs = j;
227*219b2ee8SDavid du Colombier 		}else{
228*219b2ee8SDavid du Colombier 			syslog(0, CRONLOG, "%s: line %d: syntax error", user->name, line);
229*219b2ee8SDavid du Colombier 			free(j);
230*219b2ee8SDavid du Colombier 		}
231*219b2ee8SDavid du Colombier 	}
232*219b2ee8SDavid du Colombier 	Bterm(b);
233*219b2ee8SDavid du Colombier 	return jobs;
234*219b2ee8SDavid du Colombier }
235*219b2ee8SDavid du Colombier 
236*219b2ee8SDavid du Colombier void
237*219b2ee8SDavid du Colombier printjobs(void)
238*219b2ee8SDavid du Colombier {
239*219b2ee8SDavid du Colombier 	char buf[8*1024];
240*219b2ee8SDavid du Colombier 	Job *j;
241*219b2ee8SDavid du Colombier 	int i;
242*219b2ee8SDavid du Colombier 
243*219b2ee8SDavid du Colombier 	for(i = 0; i < nuser; i++){
244*219b2ee8SDavid du Colombier 		print("user %s\n", users[i].name);
245*219b2ee8SDavid du Colombier 		for(j = users[i].jobs; j; j = j->next){
246*219b2ee8SDavid du Colombier 			if(!mkcmd(j->cmd, buf, sizeof buf))
247*219b2ee8SDavid du Colombier 				print("\tbad job %s on host %s\n", j->cmd, j->host);
248*219b2ee8SDavid du Colombier 			else
249*219b2ee8SDavid du Colombier 				print("\tjob %s on host %s\n", buf, j->host);
250*219b2ee8SDavid du Colombier 		}
251*219b2ee8SDavid du Colombier 	}
252*219b2ee8SDavid du Colombier }
253*219b2ee8SDavid du Colombier 
254*219b2ee8SDavid du Colombier User *
255*219b2ee8SDavid du Colombier newuser(char *name)
256*219b2ee8SDavid du Colombier {
257*219b2ee8SDavid du Colombier 	int i;
258*219b2ee8SDavid du Colombier 
259*219b2ee8SDavid du Colombier 	for(i = 0; i < nuser; i++)
260*219b2ee8SDavid du Colombier 		if(strcmp(users[i].name, name) == 0)
261*219b2ee8SDavid du Colombier 			return &users[i];
262*219b2ee8SDavid du Colombier 	if(nuser == maxuser){
263*219b2ee8SDavid du Colombier 		maxuser += 32;
264*219b2ee8SDavid du Colombier 		users = erealloc(users, maxuser * sizeof *users);
265*219b2ee8SDavid du Colombier 	}
266*219b2ee8SDavid du Colombier 	strcpy(users[nuser].name, name);
267*219b2ee8SDavid du Colombier 	users[nuser].jobs = 0;
268*219b2ee8SDavid du Colombier 	return &users[nuser++];
269*219b2ee8SDavid du Colombier }
270*219b2ee8SDavid du Colombier 
271*219b2ee8SDavid du Colombier void
272*219b2ee8SDavid du Colombier freejobs(Job *j)
273*219b2ee8SDavid du Colombier {
274*219b2ee8SDavid du Colombier 	Job *fj;
275*219b2ee8SDavid du Colombier 
276*219b2ee8SDavid du Colombier 	for(fj = j; fj; fj = j){
277*219b2ee8SDavid du Colombier 		j = j->next;
278*219b2ee8SDavid du Colombier 		free(fj);
279*219b2ee8SDavid du Colombier 	}
280*219b2ee8SDavid du Colombier }
281*219b2ee8SDavid du Colombier 
282*219b2ee8SDavid du Colombier int
283*219b2ee8SDavid du Colombier getname(char *name)
284*219b2ee8SDavid du Colombier {
285*219b2ee8SDavid du Colombier 	int c;
286*219b2ee8SDavid du Colombier 	int i;
287*219b2ee8SDavid du Colombier 
288*219b2ee8SDavid du Colombier 	if(!savec)
289*219b2ee8SDavid du Colombier 		return 0;
290*219b2ee8SDavid du Colombier 	while(*savec == ' ' || *savec == '\t')
291*219b2ee8SDavid du Colombier 		savec++;
292*219b2ee8SDavid du Colombier 	for(i = 0; (c = *savec) && c != ' ' && c != '\t'; i++){
293*219b2ee8SDavid du Colombier 		if(i == NAMELEN - 1)
294*219b2ee8SDavid du Colombier 			return 0;
295*219b2ee8SDavid du Colombier 		name[i] = *savec++;
296*219b2ee8SDavid du Colombier 	}
297*219b2ee8SDavid du Colombier 	name[i] = '\0';
298*219b2ee8SDavid du Colombier 	while(*savec == ' ' || *savec == '\t')
299*219b2ee8SDavid du Colombier 		savec++;
300*219b2ee8SDavid du Colombier 	return i;
301*219b2ee8SDavid du Colombier }
302*219b2ee8SDavid du Colombier 
303*219b2ee8SDavid du Colombier /*
304*219b2ee8SDavid du Colombier  * return the next time range in the file:
305*219b2ee8SDavid du Colombier  * times: '*'
306*219b2ee8SDavid du Colombier  * 	| range
307*219b2ee8SDavid du Colombier  * range: number
308*219b2ee8SDavid du Colombier  *	| number '-' number
309*219b2ee8SDavid du Colombier  *	| range ',' range
310*219b2ee8SDavid du Colombier  * a return of zero means a syntax error was discovered
311*219b2ee8SDavid du Colombier  */
312*219b2ee8SDavid du Colombier ulong
313*219b2ee8SDavid du Colombier gettime(int min, int max)
314*219b2ee8SDavid du Colombier {
315*219b2ee8SDavid du Colombier 	ulong n, m, e;
316*219b2ee8SDavid du Colombier 
317*219b2ee8SDavid du Colombier 	if(gettok(min, max) == '*')
318*219b2ee8SDavid du Colombier 		return ~0;
319*219b2ee8SDavid du Colombier 	n = 0;
320*219b2ee8SDavid du Colombier 	while(tok == '1'){
321*219b2ee8SDavid du Colombier 		m = 1 << lexval;
322*219b2ee8SDavid du Colombier 		n |= m;
323*219b2ee8SDavid du Colombier 		if(gettok(0, 0) == '-'){
324*219b2ee8SDavid du Colombier 			if(gettok(lexval, max) != '1')
325*219b2ee8SDavid du Colombier 				return 0;
326*219b2ee8SDavid du Colombier 			e = 1 << lexval;
327*219b2ee8SDavid du Colombier 			for( ; m <= e; m <<= 1)
328*219b2ee8SDavid du Colombier 				n |= m;
329*219b2ee8SDavid du Colombier 			gettok(min, max);
330*219b2ee8SDavid du Colombier 		}
331*219b2ee8SDavid du Colombier 		if(tok != ',')
332*219b2ee8SDavid du Colombier 			break;
333*219b2ee8SDavid du Colombier 		if(gettok(min, max) != '1')
334*219b2ee8SDavid du Colombier 			return 0;
335*219b2ee8SDavid du Colombier 	}
336*219b2ee8SDavid du Colombier 	pushtok();
337*219b2ee8SDavid du Colombier 	return n;
338*219b2ee8SDavid du Colombier }
339*219b2ee8SDavid du Colombier 
340*219b2ee8SDavid du Colombier void
341*219b2ee8SDavid du Colombier pushtok(void)
342*219b2ee8SDavid du Colombier {
343*219b2ee8SDavid du Colombier 	savec = savetok;
344*219b2ee8SDavid du Colombier }
345*219b2ee8SDavid du Colombier 
346*219b2ee8SDavid du Colombier int
347*219b2ee8SDavid du Colombier gettok(int min, int max)
348*219b2ee8SDavid du Colombier {
349*219b2ee8SDavid du Colombier 	char c;
350*219b2ee8SDavid du Colombier 
351*219b2ee8SDavid du Colombier 	savetok = savec;
352*219b2ee8SDavid du Colombier 	if(!savec)
353*219b2ee8SDavid du Colombier 		return tok = 0;
354*219b2ee8SDavid du Colombier 	while((c = *savec) == ' ' || c == '\t')
355*219b2ee8SDavid du Colombier 		savec++;
356*219b2ee8SDavid du Colombier 	switch(c){
357*219b2ee8SDavid du Colombier 	case '0': case '1': case '2': case '3': case '4':
358*219b2ee8SDavid du Colombier 	case '5': case '6': case '7': case '8': case '9':
359*219b2ee8SDavid du Colombier 		lexval = strtoul(savec, &savec, 10);
360*219b2ee8SDavid du Colombier 		if(lexval < min || lexval > max)
361*219b2ee8SDavid du Colombier 			return tok = 0;
362*219b2ee8SDavid du Colombier 		if(max > 32)
363*219b2ee8SDavid du Colombier 			lexval /= 2;			/* yuk: correct min by / 2 */
364*219b2ee8SDavid du Colombier 		return tok = '1';
365*219b2ee8SDavid du Colombier 	case '*': case '-': case ',':
366*219b2ee8SDavid du Colombier 		savec++;
367*219b2ee8SDavid du Colombier 		return tok = c;
368*219b2ee8SDavid du Colombier 	default:
369*219b2ee8SDavid du Colombier 		return tok = 0;
370*219b2ee8SDavid du Colombier 	}
371*219b2ee8SDavid du Colombier }
372*219b2ee8SDavid du Colombier 
373*219b2ee8SDavid du Colombier int
374*219b2ee8SDavid du Colombier call(char *host)
375*219b2ee8SDavid du Colombier {
376*219b2ee8SDavid du Colombier 	char *na, *p;
377*219b2ee8SDavid du Colombier 
378*219b2ee8SDavid du Colombier 	na = netmkaddr(host, 0, "rexexec");
379*219b2ee8SDavid du Colombier 	p = utfrune(na, L'!');
380*219b2ee8SDavid du Colombier 	if(!p)
381*219b2ee8SDavid du Colombier 		return -1;
382*219b2ee8SDavid du Colombier 	p = utfrune(p+1, L'!');
383*219b2ee8SDavid du Colombier 	if(!p)
384*219b2ee8SDavid du Colombier 		return -1;
385*219b2ee8SDavid du Colombier 	if(strcmp(p, "!rexexec") != 0)
386*219b2ee8SDavid du Colombier 		return -2;
387*219b2ee8SDavid du Colombier 	return dial(na, 0, 0, 0);
388*219b2ee8SDavid du Colombier }
389*219b2ee8SDavid du Colombier 
390*219b2ee8SDavid du Colombier /*
391*219b2ee8SDavid du Colombier  * convert command to run properly on the remote machine
392*219b2ee8SDavid du Colombier  * need to escape the quotes wo they don't get stripped
393*219b2ee8SDavid du Colombier  */
394*219b2ee8SDavid du Colombier int
395*219b2ee8SDavid du Colombier mkcmd(char *cmd, char *buf, int len)
396*219b2ee8SDavid du Colombier {
397*219b2ee8SDavid du Colombier 	char *p;
398*219b2ee8SDavid du Colombier 	int n, m;
399*219b2ee8SDavid du Colombier 
400*219b2ee8SDavid du Colombier 	n = sizeof "exec rc -c '" -1;
401*219b2ee8SDavid du Colombier 	if(n >= len)
402*219b2ee8SDavid du Colombier 		return 0;
403*219b2ee8SDavid du Colombier 	strcpy(buf, "exec rc -c '");
404*219b2ee8SDavid du Colombier 	while(p = utfrune(cmd, L'\'')){
405*219b2ee8SDavid du Colombier 		p++;
406*219b2ee8SDavid du Colombier 		m = p - cmd;
407*219b2ee8SDavid du Colombier 		if(n + m + 1 >= len)
408*219b2ee8SDavid du Colombier 			return 0;
409*219b2ee8SDavid du Colombier 		strncpy(&buf[n], cmd, m);
410*219b2ee8SDavid du Colombier 		n += m;
411*219b2ee8SDavid du Colombier 		buf[n++] = '\'';
412*219b2ee8SDavid du Colombier 		cmd = p;
413*219b2ee8SDavid du Colombier 	}
414*219b2ee8SDavid du Colombier 	m = strlen(cmd);
415*219b2ee8SDavid du Colombier 	if(n + m + sizeof "'</dev/null>/dev/null>[2=1]" >= len)
416*219b2ee8SDavid du Colombier 		return 0;
417*219b2ee8SDavid du Colombier 	strcpy(&buf[n], cmd);
418*219b2ee8SDavid du Colombier 	strcpy(&buf[n+m], "'</dev/null>/dev/null>[2=1]");
419*219b2ee8SDavid du Colombier 	return 1;
420*219b2ee8SDavid du Colombier }
421*219b2ee8SDavid du Colombier 
422*219b2ee8SDavid du Colombier void
423*219b2ee8SDavid du Colombier rexec(User *user, Job *j)
424*219b2ee8SDavid du Colombier {
425*219b2ee8SDavid du Colombier 	char buf[8*1024], key[DESKEYLEN], err[ERRLEN];
426*219b2ee8SDavid du Colombier 	int fd;
427*219b2ee8SDavid du Colombier 
428*219b2ee8SDavid du Colombier 	switch(rfork(RFPROC|RFNOWAIT|RFNAMEG|RFENVG|RFFDG)){
429*219b2ee8SDavid du Colombier 	case 0:
430*219b2ee8SDavid du Colombier 		break;
431*219b2ee8SDavid du Colombier 	case -1:
432*219b2ee8SDavid du Colombier 		syslog(0, CRONLOG, "can't fork a job for %s: %r\n", user->name);
433*219b2ee8SDavid du Colombier 	default:
434*219b2ee8SDavid du Colombier 		return;
435*219b2ee8SDavid du Colombier 	}
436*219b2ee8SDavid du Colombier 	if(findkey(KEYDB, user->name, key) == 0){
437*219b2ee8SDavid du Colombier 		syslog(0, CRONLOG, "%s: key not found", user->name);
438*219b2ee8SDavid du Colombier 		_exits(0);
439*219b2ee8SDavid du Colombier 	}
440*219b2ee8SDavid du Colombier 
441*219b2ee8SDavid du Colombier 	if(!mkcmd(j->cmd, buf, sizeof buf)){
442*219b2ee8SDavid du Colombier 		syslog(0, CRONLOG, "internal error: cmd buffer overflow");
443*219b2ee8SDavid du Colombier 		_exits(0);
444*219b2ee8SDavid du Colombier 	}
445*219b2ee8SDavid du Colombier 
446*219b2ee8SDavid du Colombier 	/*
447*219b2ee8SDavid du Colombier 	 * remote call, auth, cmd with no i/o
448*219b2ee8SDavid du Colombier 	 * give it 2 min to complete
449*219b2ee8SDavid du Colombier 	 */
450*219b2ee8SDavid du Colombier 	alarm(2*60*1000);
451*219b2ee8SDavid du Colombier 	fd = call(j->host);
452*219b2ee8SDavid du Colombier 	if(fd < 0){
453*219b2ee8SDavid du Colombier 		if(fd == -2){
454*219b2ee8SDavid du Colombier 			syslog(0, AUTHLOG, "%s: dangerous host %s", user->name, j->host);
455*219b2ee8SDavid du Colombier 			syslog(0, CRONLOG, "%s: dangerous host %s", user->name, j->host);
456*219b2ee8SDavid du Colombier 		}
457*219b2ee8SDavid du Colombier 		syslog(0, CRONLOG, "%s: can't call '%s'", user->name, j->host);
458*219b2ee8SDavid du Colombier 		_exits(0);
459*219b2ee8SDavid du Colombier 	}
460*219b2ee8SDavid du Colombier 	if(myauth(fd, user->name) < 0){
461*219b2ee8SDavid du Colombier 		errstr(err);
462*219b2ee8SDavid du Colombier 		syslog(0, CRONLOG, "%s: can't auth %s on %s: %s", user->name, j->cmd, j->host, err);
463*219b2ee8SDavid du Colombier 		_exits(0);
464*219b2ee8SDavid du Colombier 	}
465*219b2ee8SDavid du Colombier 	write(fd, buf, strlen(buf)+1);
466*219b2ee8SDavid du Colombier 	write(fd, buf, 0);
467*219b2ee8SDavid du Colombier 	while(read(fd, buf, sizeof buf) > 0)
468*219b2ee8SDavid du Colombier 		;
469*219b2ee8SDavid du Colombier 	_exits(0);
470*219b2ee8SDavid du Colombier }
471*219b2ee8SDavid du Colombier 
472*219b2ee8SDavid du Colombier void *
473*219b2ee8SDavid du Colombier emalloc(ulong n)
474*219b2ee8SDavid du Colombier {
475*219b2ee8SDavid du Colombier 	void *p;
476*219b2ee8SDavid du Colombier 
477*219b2ee8SDavid du Colombier 	if(p = malloc(n))
478*219b2ee8SDavid du Colombier 		return p;
479*219b2ee8SDavid du Colombier 	error("out of memory");
480*219b2ee8SDavid du Colombier 	return 0;
481*219b2ee8SDavid du Colombier }
482*219b2ee8SDavid du Colombier 
483*219b2ee8SDavid du Colombier void *
484*219b2ee8SDavid du Colombier erealloc(void *p, ulong n)
485*219b2ee8SDavid du Colombier {
486*219b2ee8SDavid du Colombier 	if(p = realloc(p, n))
487*219b2ee8SDavid du Colombier 		return p;
488*219b2ee8SDavid du Colombier 	error("out of memory");
489*219b2ee8SDavid du Colombier 	return 0;
490*219b2ee8SDavid du Colombier }
491*219b2ee8SDavid du Colombier 
492*219b2ee8SDavid du Colombier void
493*219b2ee8SDavid du Colombier usage(void)
494*219b2ee8SDavid du Colombier {
495*219b2ee8SDavid du Colombier 	fprint(2, "usage: cron [-c]\n");
496*219b2ee8SDavid du Colombier 	exits("usage");
497*219b2ee8SDavid du Colombier }
498*219b2ee8SDavid du Colombier 
499*219b2ee8SDavid du Colombier int
500*219b2ee8SDavid du Colombier myauth(int fd, char *user)
501*219b2ee8SDavid du Colombier {
502*219b2ee8SDavid du Colombier 	int i;
503*219b2ee8SDavid du Colombier 	char hkey[DESKEYLEN];
504*219b2ee8SDavid du Colombier 	char buf[512];
505*219b2ee8SDavid du Colombier 	Ticketreq tr;
506*219b2ee8SDavid du Colombier 	Ticket t;
507*219b2ee8SDavid du Colombier 	Authenticator a;
508*219b2ee8SDavid du Colombier 
509*219b2ee8SDavid du Colombier 	/* get ticket request from remote machine */
510*219b2ee8SDavid du Colombier 	if(readn(fd, buf, TICKREQLEN) < 0){
511*219b2ee8SDavid du Colombier 		werrstr("bad request");
512*219b2ee8SDavid du Colombier 		return -1;
513*219b2ee8SDavid du Colombier 	}
514*219b2ee8SDavid du Colombier 	convM2TR(buf, &tr);
515*219b2ee8SDavid du Colombier 	if(tr.type != AuthTreq){
516*219b2ee8SDavid du Colombier 		werrstr("bad request");
517*219b2ee8SDavid du Colombier 		return -1;
518*219b2ee8SDavid du Colombier 	}
519*219b2ee8SDavid du Colombier 	if(findkey(KEYDB, tr.authid, hkey) == 0){
520*219b2ee8SDavid du Colombier 		werrstr("no key for authid %s", tr.authid);
521*219b2ee8SDavid du Colombier 		return -1;
522*219b2ee8SDavid du Colombier 	}
523*219b2ee8SDavid du Colombier 
524*219b2ee8SDavid du Colombier 	/* create ticket+authenticator and send to destination */
525*219b2ee8SDavid du Colombier 	memset(&t, 0, sizeof t);
526*219b2ee8SDavid du Colombier 	memmove(t.chal, tr.chal, CHALLEN);
527*219b2ee8SDavid du Colombier 	strcpy(t.cuid, user);
528*219b2ee8SDavid du Colombier 	strcpy(t.suid, user);
529*219b2ee8SDavid du Colombier 	srand(time(0));
530*219b2ee8SDavid du Colombier 	for(i = 0; i < DESKEYLEN; i++)
531*219b2ee8SDavid du Colombier 		t.key[i] = nrand(256);
532*219b2ee8SDavid du Colombier 	t.num = AuthTs;
533*219b2ee8SDavid du Colombier 	convT2M(&t, buf, hkey);
534*219b2ee8SDavid du Colombier 	memmove(a.chal, tr.chal, CHALLEN);
535*219b2ee8SDavid du Colombier 	a.id = 0;
536*219b2ee8SDavid du Colombier 	a.num = AuthAc;
537*219b2ee8SDavid du Colombier 	convA2M(&a, buf+TICKETLEN, t.key);
538*219b2ee8SDavid du Colombier 	if(write(fd, buf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN){
539*219b2ee8SDavid du Colombier 		werrstr("connection dropped: %r");
540*219b2ee8SDavid du Colombier 		return -1;
541*219b2ee8SDavid du Colombier 	}
542*219b2ee8SDavid du Colombier 
543*219b2ee8SDavid du Colombier 	/* get authenticator from server and check */
544*219b2ee8SDavid du Colombier 	if(readn(fd, buf, AUTHENTLEN) < 0){
545*219b2ee8SDavid du Colombier 		werrstr("connection dropped: %r");
546*219b2ee8SDavid du Colombier 		return -1;
547*219b2ee8SDavid du Colombier 	}
548*219b2ee8SDavid du Colombier 	convM2A(buf, &a, t.key);
549*219b2ee8SDavid du Colombier 	if(a.num != AuthAs){
550*219b2ee8SDavid du Colombier 		werrstr("bad reply authenticator");
551*219b2ee8SDavid du Colombier 		return -1;
552*219b2ee8SDavid du Colombier 	}
553*219b2ee8SDavid du Colombier 	return 0;
554*219b2ee8SDavid du Colombier }
555