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®EXP) { /* 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®EXP) && 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