xref: /plan9-contrib/sys/src/cmd/upas/q/runq.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 
5 void	doalldirs(void);
6 void	dodir(char*);
7 void	dofile(Dir*);
8 void	rundir(char*);
9 char*	file(char*, char);
10 void	warning(char*, void*);
11 void	error(char*, void*);
12 int	returnmail(char**, char*, char*);
13 
14 #define HUNK 32
15 char	*cmd;
16 char	*root;
17 int	debug;
18 int	giveup = 2*24*60*60;	/*
19 
20 /* the current directory */
21 Dir	*dirbuf;
22 long	ndirbuf = 0;
23 int	nfiles;
24 
25 
26 
27 void
28 usage(void)
29 {
30 	fprint(2, "usage: runq [-a] q-root cmd\n");
31 	exits("");
32 }
33 
34 void
35 main(int argc, char **argv)
36 {
37 	char *user;
38 	int all;
39 
40 	user = getenv("user");
41 	all = 0;
42 
43 	ARGBEGIN{
44 	case 'a':
45 		all++;
46 		break;
47 	case 'd':
48 		debug++;
49 		break;
50 	case 't':
51 		giveup = 60*60*atoi(ARGF());
52 		break;
53 	}ARGEND;
54 
55 	if(argc != 2)
56 		usage();
57 
58 	root = argv[0];
59 	cmd = argv[1];
60 
61 	if(chdir(root) < 0)
62 		error("can't cd to %s", root);
63 
64 	if(all)
65 		doalldirs();
66 	else
67 		dodir(user);
68 	exits(0);
69 }
70 
71 void
72 setuser(char *name)
73 {
74 	int fd;
75 	char buf[256];
76 
77 	/*
78 	 *  become THAT user
79 	 */
80 	fd = create("/dev/user", OWRITE, 0600);
81 	if(fd < 0)
82 		error("opening /dev/user", 0);
83 	if(write(fd, name, strlen(name))<0)
84 		error("couldn't set user name %s", name);
85 	close(fd);
86 
87 	/*
88 	 *  mount /srv/boot /mnt
89 	 */
90 	fd = open("/srv/boot", 2);
91 	if(fd < 0)
92 		error("opening /srv/boot", 0);
93 	if(mount(fd, "/mnt", MREPL, "") < 0)
94 		error("mounting", 0);
95 	close(fd);
96 
97 	/*
98 	 *   bind /mnt/<root> /<root>
99 	 */
100 	sprint(buf, "/mnt/%s", root);
101 	if(bind(buf, root, MREPL) < 0)
102 		error("binding", 0);
103 }
104 
105 int
106 emptydir(char *name)
107 {
108 	int fd;
109 	long n;
110 	Dir db2;
111 
112 	fd = open(name, OREAD);
113 	if(fd < 0)
114 		return 1;
115 	n = read(fd, &db2, sizeof db2);
116 	close(fd);
117 	if(n <= 0)
118 		return 1;
119 	return 0;
120 }
121 
122 /*
123  *  run all user directories, must be bootes to do this
124  */
125 void
126 doalldirs(void)
127 {
128 	Dir db[HUNK];
129 	int fd;
130 	long i, n;
131 
132 	fd = open(".", OREAD);
133 	if(fd == -1){
134 		warning("reading %s", root);
135 		return;
136 	}
137 	while((n=dirread(fd, db, sizeof db)) > 0){
138 		n /= sizeof(Dir);
139 		for(i=0; i<n; i++){
140 			if(db[i].qid.path&CHDIR){
141 				if(emptydir(db[i].name))
142 					continue;
143 				switch(fork()){
144 				case -1:
145 					warning("couldn't fork", 0);
146 					break;
147 				case 0:
148 					if(rfork(RFENVG|RFNAMEG|RFNOTEG)<0)
149 						error("rfork failed", 0);
150 /*					setuser(db[i].name); /**/
151 					dodir(db[i].name);
152 					exits(0);
153 				default:
154 					wait(0);
155 					break;
156 				}
157 			}
158 		}
159 	}
160 	close(fd);
161 }
162 
163 /*
164  * Read a whole directory before removing anything as the holes formed
165  * by removing affect the read offset.
166  */
167 long
168 readdirect(int fd)
169 {
170 	enum
171 	{
172 		N = 32
173 	};
174 	long m, n;
175 
176 	m = 1;	/* prime the loop */
177 	for(n=0; m>0; n+=m/sizeof(Dir)){
178 		if(n == ndirbuf){
179 			dirbuf = realloc(dirbuf, (ndirbuf+N)*sizeof(Dir));
180 			if(dirbuf == 0){
181 				warning("memory allocation", 0);
182 				return 0;
183 			}
184 			ndirbuf += N;
185 		}
186 		m = dirread(fd, dirbuf+n, (ndirbuf-n)*sizeof(Dir));
187 	}
188 	return n;
189 }
190 
191 /*
192  *  cd to a user directory and run it
193  */
194 void
195 dodir(char *name)
196 {
197 	if(chdir(name) < 0){
198 		warning("cd to %s", name);
199 		return;
200 	}
201 	if(debug)
202 		fprint(2, "running %s\n", name);
203 	rundir(name);
204 	chdir("..");
205 }
206 
207 /*
208  *  run the current directory
209  */
210 void
211 rundir(char *name)
212 {
213 	int fd;
214 	long i;
215 
216 	fd = open(".", OREAD);
217 	if(fd == -1){
218 		warning("reading %s", name);
219 		return;
220 	}
221 	nfiles = readdirect(fd);
222 	close(fd);
223 
224 	for(i=0; i<nfiles; i++){
225 		if(dirbuf[i].name[0]!='C' || dirbuf[i].name[1]!='.')
226 			continue;
227 		dofile(&dirbuf[i]);
228 	}
229 }
230 
231 /*
232  *  free files matching name in the current directory
233  */
234 void
235 remmatch(char *name)
236 {
237 	long i;
238 
239 	for(i=0; i<nfiles; i++){
240 		if(strcmp(&dirbuf[i].name[1], &name[1]) == 0)
241 			remove(dirbuf[i].name);
242 	}
243 
244 	/* error file (may have) appeared after we read the directory */
245 	remove(file(name, 'E'));
246 }
247 
248 /*
249  *  try a message
250  */
251 void
252 dofile(Dir *dp)
253 {
254 	Dir d;
255 	int dfd;
256 	int cfd;
257 	char *buf;
258 	char *cp;
259 	int ac;
260 	char **av;
261 	Waitmsg wm;
262 	int dtime;
263 	int efd;
264 
265 	if(debug)
266 		fprint(2, "dofile %s\n", dp->name);
267 
268 	/*
269 	 *  if no data file, just clean up
270 	 */
271 	if(dirstat(file(dp->name, 'D'), &d) < 0){
272 		remmatch(dp->name);
273 		return;
274 	}
275 	dtime = d.mtime;
276 
277 	/*
278 	 *  retry times depend on the age of the errors file
279 	 */
280 	if(dirstat(file(dp->name, 'E'), &d) >= 0){
281 		if(d.mtime - dtime < 60*60){
282 			/* up to the first hour, try every 15 minutes */
283 			if(time(0) - d.mtime < 15*60)
284 				return;
285 		} else {
286 			/* after the first hour, try once an hour */
287 			if(time(0) - d.mtime < 60*60)
288 				return;
289 		}
290 	}
291 
292 	/*
293 	 *  open control and data
294 	 */
295 	cfd = open(file(dp->name, 'C'), OREAD);
296 	if(cfd < 0)
297 		return;
298 	dfd = open(file(dp->name, 'D'), OREAD);
299 	if(dfd < 0){
300 		close(cfd);
301 		return;
302 	}
303 
304 	/*
305 	 *  make arg list
306 	 *	- read args into (malloc'd) buffer
307 	 *	- count args
308 	 *	- malloc a vector and copy pointers to args into it
309 	 */
310 	buf = malloc(dp->length+1);
311 	if(read(cfd, buf, dp->length) != dp->length){
312 		warning("reading control file %s\n", dp->name);
313 		goto out;
314 	}
315 	buf[dp->length] = 0;
316 	for(ac = 0, cp = buf; *cp; ac++){
317 		while(isspace(*cp))
318 			cp++;
319 		while(*cp && !isspace(*cp))
320 			cp++;
321 	}
322 	av = malloc((ac+2)*sizeof(char *));
323 	av[0] = cmd;
324 	for(ac = 1, cp = buf; *cp; ac++){
325 		while(isspace(*cp))
326 			*cp++ = 0;
327 		av[ac] = cp;
328 		if(*cp == '"'){
329 			cp++;
330 			while(*cp && *cp!='"')
331 				cp++;
332 			if(*cp)
333 				cp++;
334 		} else {
335 			while(*cp && !isspace(*cp))
336 				cp++;
337 		}
338 		while(isspace(*cp))
339 			*cp++ = 0;
340 	}
341 	av[ac] = 0;
342 
343 	if(time(0) - dtime > giveup){
344 		if(returnmail(av, dp->name, wm.msg) == 0)
345 			remmatch(dp->name);
346 		return;
347 	}
348 
349 	/*
350 	 *  transfer
351 	 */
352 	switch(fork()){
353 	case -1:
354 		return;
355 	case 0:
356 		close(0);
357 		dup(dfd, 0);
358 		close(2);
359 		efd = open(dp->name, OWRITE);
360 		if(efd < 0)
361 			efd = create(file(dp->name, 'E'), OWRITE, 0664);
362 		if(efd < 0)
363 			exits("");
364 		seek(efd, 0, 2);
365 		close(dfd);
366 		close(cfd);
367 		exec(cmd, av);
368 		error("can't exec %s", cmd);
369 		break;
370 	default:
371 		wait(&wm);
372 		if(wm.msg[0]){
373 			if(debug)
374 				fprint(2, "wm.msg == %s\n", wm.msg);
375 			if(strstr(wm.msg, "Retry")==0){
376 				/* return the message and remove it */
377 				if(returnmail(av, dp->name, wm.msg) == 0)
378 					remmatch(dp->name);
379 			} else {
380 				/* try again later */
381 			}
382 		} else {
383 			/* it worked remove the message */
384 			remmatch(dp->name);
385 		}
386 
387 	}
388 	return;
389 out:
390 	close(cfd);
391 	close(dfd);
392 }
393 
394 /*
395  *  return a name starting with the given character
396  */
397 char*
398 file(char *name, char type)
399 {
400 	static char nname[NAMELEN+1];
401 
402 	strcpy(nname, name);
403 	nname[0] = type;
404 	return nname;
405 }
406 
407 /*
408  *  send back the mail with an error message
409  *
410  *  return 0 if successful
411  */
412 int
413 returnmail(char **av, char *name, char *msg)
414 {
415 	int pfd[2];
416 	Waitmsg wm;
417 	char *sender;
418 	int fd;
419 	char buf[256];
420 	int i;
421 	long n;
422 
423 	sender = av[2];
424 
425 	if(pipe(pfd) < 0)
426 		return -1;
427 
428 	switch(fork()){
429 	case -1:
430 		return -1;
431 	case 0:
432 		close(pfd[1]);
433 		close(0);
434 		dup(pfd[0], 0);
435 		close(pfd[0]);
436 		execl("/bin/upas/sendmail", "sendmail", sender, 0);
437 		error("can't exec", 0);
438 		break;
439 	default:
440 		break;
441 	}
442 
443 	close(pfd[0]);
444 	if(av[1]){
445 		fprint(pfd[1], "Your request ``%20.20s ", av[1]);
446 		for(n = 3; av[n]; n++)
447 			fprint(pfd[1], "%s ", av[n]);
448 	}
449 	fprint(pfd[1], "'' failed (code %s).\nThe symptom was:\n\n", msg);
450 	fd = open(file(name, 'E'), OREAD);
451 	if(fd >= 0){
452 		for(;;){
453 			n = read(fd, buf, sizeof(buf));
454 			if(n <= 0)
455 				break;
456 			if(write(pfd[1], buf, n) != n){
457 				close(fd);
458 				goto out;
459 			}
460 		}
461 		close(fd);
462 	}
463 	fprint(pfd[1], "\nThe request began:\n\n");
464 	fd = open(file(name, 'D'), OREAD);
465 	if(fd >= 0){
466 		for(i=0; i<4*16; i++){
467 			n = read(fd, buf, sizeof(buf));
468 			if(n <= 0)
469 				break;
470 			if(write(pfd[1], buf, n) != n){
471 				close(fd);
472 				goto out;
473 			}
474 		}
475 		close(fd);
476 	}
477 	close(pfd[1]);
478 out:
479 	wait(&wm);
480 	return wm.msg[0] ? -1 : 0;
481 }
482 
483 /*
484  *  print a warning and continue
485  */
486 void
487 warning(char *f, void *a)
488 {
489 	char err[65];
490 	char buf[256];
491 
492 	errstr(err);
493 	sprint(buf, f, a);
494 	fprint(2, "runq: %s: %s\n", buf, err);
495 }
496 
497 /*
498  *  print an error and die
499  */
500 void
501 error(char *f, void *a)
502 {
503 	char err[ERRLEN+1];
504 	char buf[256];
505 
506 	errstr(err);
507 	sprint(buf, f, a);
508 	fprint(2, "runq: %s: %s\n", buf, err);
509 	exits(buf);
510 }
511 
512