xref: /plan9-contrib/sys/src/cmd/mk/run.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include	"mk.h"
2 
3 typedef struct Event
4 {
5 	int pid;
6 	Job *job;
7 } Event;
8 static Event *events;
9 static int nevents, nrunning;
10 typedef struct Process
11 {
12 	int pid;
13 	int status;
14 	struct Process *b, *f;
15 } Process;
16 static Process *phead, *pfree;
17 static void sched(void);
18 static void pnew(int, int), pdelete(Process *);
19 static Envy *envy;
20 static int special;
21 #define	ENVQUANTA	10
22 static int envsize;
23 static int nextv;
24 
25 static int pidslot(int);
26 
27 int
28 Execl(char *p, char *a, ...)
29 {
30 	if (envy)
31 		exportenv(envy, nextv);
32 	exec(p, &a);
33 	return -1;
34 }
35 
36 void
37 run(Job *j)
38 {
39 	Job *jj;
40 
41 	if(jobs){
42 		for(jj = jobs; jj->next; jj = jj->next)
43 			;
44 		jj->next = j;
45 	} else
46 		jobs = j;
47 	j->next = 0;
48 	/* this code also in waitup after parse redirect */
49 	if(nrunning < nproclimit)
50 		sched();
51 }
52 
53 static void
54 sched(void)
55 {
56 	Job *j;
57 	Bufblock *buf;
58 	int slot, pip[2], pid;
59 	Node *n;
60 
61 	if(jobs == 0){
62 		usage();
63 		return;
64 	}
65 	j = jobs;
66 	jobs = j->next;
67 	if(DEBUG(D_EXEC))
68 		fprint(1, "firing up job for target %s\n", wtos(j->t));
69 	slot = nextslot();
70 	events[slot].job = j;
71 	dovars(j, slot);
72 	buf = newbuf();
73 	shprint(j->r->recipe, envy, buf);
74 	if(!tflag && (nflag || !(j->r->attr&QUIET)))
75 		Bwrite(&stdout, buf->start, (long)strlen(buf->start));
76 	freebuf(buf);
77 	if(nflag||tflag){
78 		for(n = j->n; n; n = n->next){
79 			if(tflag){
80 				if(!(n->flags&VIRTUAL))
81 					touch(n->name);
82 				else if(explain)
83 					Bprint(&stdout, "no touch of virtual '%s'\n", n->name);
84 			}
85 			n->time = time((long *)0);
86 			MADESET(n, MADE);
87 		}
88 	} else {
89 /*Bprint(&stdout, "recipe='%s'\n", j->r->recipe);/**/
90 		Bflush(&stdout);
91 		if(j->r->attr&RED){
92 			if(pipe(pip) < 0){
93 				perror("pipe");
94 				Exit();
95 			}
96 		}
97 		if((pid = rfork(RFPROC|RFFDG|RFENVG)) < 0){
98 			perror("mk rfork");
99 			Exit();
100 		}
101 		if(pid == 0){
102 			if(j->r->attr&RED){
103 				close(pip[0]);
104 				dup(pip[1], 1);
105 				close(pip[1]);
106 			}
107 			if(pipe(pip) < 0){
108 				perror("pipe-i");
109 				Exit();
110 			}
111 			if((pid = fork()) < 0){
112 				perror("mk fork");
113 				Exit();
114 			}
115 			if(pid != 0){
116 				close(pip[1]);
117 				dup(pip[0], 0);
118 				close(pip[0]);
119 				if(j->r->attr&NOMINUSE)
120 
121 					Execl(shell, shellname, "-I", (char *)0);
122 				else
123 					Execl(shell, shellname, "-eI", (char *)0);
124 				perror(shell);
125 				_exits("exec");
126 			} else {
127 				int k;
128 				char *s, *send;
129 
130 				close(pip[0]);
131 				s = j->r->recipe;
132 				send = s+strlen(s);
133 				while(s < send){
134 					if((k = write(pip[1], s, send-s)) < 0)
135 						break;
136 					s += k;
137 				}
138 				_exits(0);
139 			}
140 		}
141 		usage();
142 		nrunning++;
143 		if(j->r->attr&RED)
144 			close(pip[1]), j->fd = pip[0];
145 		else
146 			j->fd = -1;
147 		if(DEBUG(D_EXEC))
148 			fprint(1, "pid for target %s = %d\n", wtos(j->t), pid);
149 		events[slot].pid = pid;
150 	}
151 }
152 
153 int
154 waitup(int echildok, int *retstatus)
155 {
156 	int pid;
157 	int slot;
158 	Symtab *s;
159 	Word *w;
160 	Job *j;
161 	char buf[ERRLEN];
162 	Bufblock *bp;
163 	int uarg = 0;
164 	int done;
165 	Node *n;
166 	Process *p;
167 	extern int runerrs;
168 
169 	/* first check against the proces slist */
170 	if(retstatus)
171 		for(p = phead; p; p = p->f)
172 			if(p->pid == *retstatus){
173 				*retstatus = p->status;
174 				pdelete(p);
175 				return(-1);
176 			}
177 again:		/* rogue processes */
178 	if((pid = waitfor(buf)) < 0){
179 		if(echildok > 0)
180 			return(1);
181 		else {
182 			fprint(2, "mk: (waitup %d) ", echildok);
183 			perror("mk wait");
184 			Exit();
185 		}
186 	}
187 	if(DEBUG(D_EXEC))
188 		fprint(1, "waitup got pid=%d, status=%s\n", pid, buf);
189 	if(retstatus && (pid == *retstatus)){
190 		*retstatus = buf[0]? 1:0;
191 		return(-1);
192 	}
193 	slot = pidslot(pid);
194 	if(slot < 0){
195 		if(DEBUG(D_EXEC))
196 			fprint(2, "mk: wait returned unexpected process %d\n", pid);
197 		pnew(pid, buf[0]? 1:0);
198 		goto again;
199 	}
200 	j = events[slot].job;
201 	usage();
202 	nrunning--;
203 	events[slot].pid = -1;
204 	if(buf[0]){
205 		dovars(j, slot);
206 		bp = newbuf();
207 		shprint(j->r->recipe, envy, bp);
208 		front(bp->start);
209 		fprint(2, "mk: %s: exit status=%s", bp->start, buf);
210 		freebuf(bp);
211 		for(n = j->n, done = 0; n; n = n->next)
212 			if(n->flags&DELETE){
213 				if(done++ == 0)
214 					fprint(2, ", deleting");
215 				fprint(2, " '%s'", n->name);
216 			}
217 		fprint(2, "\n");
218 		for(n = j->n, done = 0; n; n = n->next)
219 			if(n->flags&DELETE){
220 				if(done++ == 0)
221 					/*Fflush(2)*/;
222 				delete(n->name);
223 			}
224 		if(kflag){
225 			runerrs++;
226 			uarg = 1;
227 		} else {
228 			jobs = 0;
229 			Exit();
230 		}
231 	}
232 	if(j->fd >= 0){
233 		sprint(buf, "process %d", pid);
234 		parse(buf, j->fd, 0, 0);
235 		execinit();	/* reread environ */
236 		nproc();
237 		while(jobs && (nrunning < nproclimit))
238 			sched();
239 	}
240 	for(w = j->t; w; w = w->next){
241 		if((s = symlook(w->s, S_NODE, (char *)0)) == 0)
242 			continue;	/* not interested in this node */
243 		update(uarg, (Node *)s->value);
244 	}
245 	if(nrunning < nproclimit)
246 		sched();
247 	return(0);
248 }
249 
250 enum {
251 	TARGET,
252 	STEM,
253 	PREREQ,
254 	PID,
255 	NPROC,
256 	NEWPREREQ,
257 	ALLTARGET,
258 	STEM0,
259 	STEM1,
260 	STEM2,
261 	STEM3,
262 	STEM4,
263 	STEM5,
264 	STEM6,
265 	STEM7,
266 	STEM8,
267 	STEM9,
268 };
269 
270 struct Myenv {
271 	char *name;
272 	Word w;
273 } myenv[] =
274 {
275 	[TARGET]	"target",	{"",0},		/* really sleazy */
276 	[STEM]		"stem",		{"",0},
277 	[PREREQ]	"prereq",	{"",0},
278 	[PID]		"pid",		{"",0},
279 	[NPROC]		"nproc",	{"",0},
280 	[NEWPREREQ]	"newprereq",	{"",0},
281 	[ALLTARGET]	"alltarget",	{"",0},
282 	[STEM0]		"stem0",	{"",0},	/* retain order of rest */
283 	[STEM1]		"stem1",	{"",0},
284 	[STEM2]		"stem2",	{"",0},
285 	[STEM3]		"stem3",	{"",0},
286 	[STEM4]		"stem4",	{"",0},
287 	[STEM5]		"stem5",	{"",0},
288 	[STEM6]		"stem6",	{"",0},
289 	[STEM7]		"stem7",	{"",0},
290 	[STEM8]		"stem8",	{"",0},
291 	[STEM9]		"stem9",	{"",0},
292 			0,		{0,0},
293 };
294 
295 void
296 execinit(void)
297 {
298 	struct Myenv *mp;
299 
300 	nextv = 0;		/* fill env from beginning */
301 	vardump();
302 	special = nextv-1;	/* pointer to last original env*/
303 	for (mp = myenv; mp->name; mp++)
304 		symlook(mp->name, S_WESET, "");
305 }
306 
307 int
308 internalvar(char *name, Word *w)
309 {
310 	struct Myenv *mp;
311 
312 	for (mp = myenv; mp->name; mp++)
313 		if (strcmp(name, mp->name) == 0) {
314 			mp->w.s = w->s;
315 			mp->w.next = w->next;
316 			return 1;
317 		}
318 	return 0;
319 }
320 
321 void
322 dovars(Job *j, int slot)
323 {
324 	int c;
325 	char *s;
326 	struct Myenv *mp;
327 	int i;
328 	char buf[20];
329 
330 	nextv = special;
331 	envinsert(myenv[TARGET].name, j->t);
332 	envinsert(myenv[STEM].name, &myenv[STEM].w);
333 	if(j->r->attr&REGEXP) {			/* memory leak */
334 		if (j->match[1].sp && j->match[1].ep) {
335 			s = j->match[1].ep+1;
336 			c = *s;
337 			*s = 0;
338 			myenv[STEM].w.s = strdup(j->match[1].sp);
339 			*s = c;
340 		} else
341 			myenv[STEM].w.s = "";
342 	} else
343 		myenv[STEM].w.s = j->stem;
344 	envinsert(myenv[PREREQ].name, j->p);
345 	sprint(buf, "%d", getpid());
346 	myenv[PID].w.s = strdup(buf);		/* memory leak */
347 	envinsert(myenv[PID].name, &myenv[PID].w);
348 	sprint(buf, "%d", slot);
349 	myenv[NPROC].w.s = strdup(buf);		/* memory leak */
350 	envinsert(myenv[NPROC].name, &myenv[NPROC].w);
351 	envinsert(myenv[NEWPREREQ].name, j->np);
352 	envinsert(myenv[ALLTARGET].name, j->at);
353 	mp = &myenv[STEM0];		/* careful - assumed order*/
354 	for(i = 0; i <= 9; i++){
355 		if((j->r->attr&REGEXP) && j->match[i].sp && j->match[i].ep) {
356 			s = j->match[i].ep;
357 			c = *s;
358 			*s = 0;
359 			mp->w.s = strdup(j->match[i].sp);	/*leak*/
360 			*s = c;
361 			envinsert(mp->name, &mp->w);
362 		} else
363 			envinsert(mp->name, 0);
364 		mp++;
365 	}
366 	envinsert(0, 0);
367 }
368 
369 void
370 nproc(void)
371 {
372 	Symtab *sym;
373 	Word *w;
374 
375 	if(sym = symlook("NPROC", S_VAR, (char *)0)) {
376 		w = (Word *) sym->value;
377 		if (w && w->s && w->s[0])
378 			nproclimit = atoi(w->s);
379 	}
380 	if(nproclimit < 1)
381 		nproclimit = 1;
382 	if(DEBUG(D_EXEC))
383 		fprint(1, "nprocs = %d\n", nproclimit);
384 	if(nproclimit > nevents){
385 		if(nevents)
386 			events = (Event *)Realloc((char *)events, nproclimit*sizeof(Event));
387 		else
388 			events = (Event *)Malloc(nproclimit*sizeof(Event));
389 		while(nevents < nproclimit)
390 			events[nevents++].pid = 0;
391 	}
392 }
393 
394 int
395 nextslot(void)
396 {
397 	int i;
398 
399 	for(i = 0; i < nproclimit; i++)
400 		if(events[i].pid <= 0) return i;
401 	assert("out of slots!!", 0);
402 	return 0;	/* cyntax */
403 }
404 
405 int
406 pidslot(int pid)
407 {
408 	int i;
409 
410 	for(i = 0; i < nevents; i++)
411 		if(events[i].pid == pid) return(i);
412 	if(DEBUG(D_EXEC))
413 		fprint(2, "mk: wait returned unexpected process %d\n", pid);
414 	return(-1);
415 }
416 
417 
418 static void
419 pnew(int pid, int status)
420 {
421 	Process *p;
422 
423 	if(pfree){
424 		p = pfree;
425 		pfree = p->f;
426 	} else
427 		p = (Process *)Malloc(sizeof(Process));
428 	p->pid = pid;
429 	p->status = status;
430 	p->f = phead;
431 	phead = p;
432 	if(p->f)
433 		p->f->b = p;
434 	p->b = 0;
435 }
436 
437 static void
438 pdelete(Process *p)
439 {
440 	if(p->f)
441 		p->f->b = p->b;
442 	if(p->b)
443 		p->b->f = p->f;
444 	else
445 		phead = p->f;
446 	p->f = pfree;
447 	pfree = p;
448 }
449 
450 void
451 killchildren(char *msg)
452 {
453 	Process *p;
454 
455 	for(p = phead; p; p = p->f)
456 		expunge(p->pid, msg);
457 }
458 
459 int
460 notifyf(void *a, char *msg)
461 {
462 	static int nnote;
463 
464 	USED(a);
465 	if(++nnote > 100){	/* until andrew fixes his program */
466 		fprint(2, "mk: too many notes\n");
467 		notify(0);
468 		abort();
469 	}
470 	if(strcmp(msg, "interrupt")!=0 && strcmp(msg, "hangup")!=0)
471 		return 0;
472 	kflag = 1;	/* to make sure waitup doesn't exit */
473 	jobs = 0;	/* make sure no more get scheduled */
474 	killchildren(msg);
475 	while(waitup(1, (int *)0) == 0)
476 		;
477 	Bprint(&stdout, "mk: %s\n", msg);
478 	Exit();
479 	return -1;
480 }
481 /*
482  *	execute a shell command capturing the output into the buffer.
483  */
484 void
485 rcexec(char *cstart, char *cend, Bufblock *buf)
486 {
487 	int childin[2], childout[2], pid;
488 	int tot, n;
489 
490 	Bflush(&stdout);
491 	if(pipe(childin) < 0){
492 		SYNERR(-1); perror("pipe1");
493 		Exit();
494 	}
495 	if(pipe(childout) < 0){
496 		SYNERR(-1); perror("pipe2");
497 		Exit();
498 	}
499 	if((pid = rfork(RFPROC|RFFDG|RFENVG)) < 0){
500 		SYNERR(-1); perror("fork");
501 		Exit();
502 	}
503 	if(pid){	/* parent */
504 		close(childin[0]);
505 		close(childout[1]);
506 		if(cstart < cend)
507 			write(childin[1], cstart, cend-cstart+1);
508 		close(childin[1]);
509 		tot = n = 0;
510 		do {
511 			tot += n;
512 			buf->current += n;
513 			if (buf->current >= buf->end)
514 				growbuf(buf);
515 		} while((n = read(childout[0], buf->current, buf->end-buf->current)) > 0);
516 		if (tot && buf->current[-1] == '\n')
517 			buf->current--;
518 		close(childout[0]);
519 	} else {
520 		dup(childin[0], 0);
521 		dup(childout[1], 1);
522 		close(childin[0]);
523 		close(childin[1]);
524 		close(childout[0]);
525 		close(childout[1]);
526 		Execl(shell, shellname, "-I", (char *)0);
527 		perror(shell);
528 		_exits("exec");
529 	}
530 }
531 
532 void
533 envinsert(char *name, Word *value)
534 {
535 	if (nextv >= envsize) {
536 		envsize += ENVQUANTA;
537 		envy = (Envy *) Realloc((char *) envy, envsize*sizeof(Envy));
538 	}
539 	envy[nextv].name = name;
540 	envy[nextv++].values = value;
541 }
542 
543 static long tslot[1000];
544 static long tick;
545 
546 void
547 usage(void)
548 {
549 	long t;
550 
551 	time(&t);
552 	if(tick)
553 		tslot[nrunning] += (t-tick);
554 	tick = t;
555 }
556 
557 void
558 prusage(void)
559 {
560 	int i;
561 
562 	usage();
563 	for(i = 0; i <= nevents; i++)
564 		fprint(1, "%d: %ld\n", i, tslot[i]);
565 }
566