1 /* 2 * Plan 9 versions of system-specific functions 3 * By convention, exported routines herein have names beginning with an 4 * upper case letter. 5 */ 6 #include "rc.h" 7 #include "exec.h" 8 #include "io.h" 9 #include "fns.h" 10 #include "getflags.h" 11 12 enum { 13 Maxenvname = 256, /* undocumented limit */ 14 }; 15 16 char *Signame[] = { 17 "sigexit", "sighup", "sigint", "sigquit", 18 "sigalrm", "sigkill", "sigfpe", "sigterm", 19 0 20 }; 21 char *syssigname[] = { 22 "exit", /* can't happen */ 23 "hangup", 24 "interrupt", 25 "quit", /* can't happen */ 26 "alarm", 27 "kill", 28 "sys: fp: ", 29 "term", 30 0 31 }; 32 char *Rcmain = "/rc/lib/rcmain"; 33 char *Fdprefix = "/fd/"; 34 35 void execfinit(void); 36 void execbind(void); 37 void execmount(void); 38 void execnewpgrp(void); 39 40 builtin Builtin[] = { 41 "cd", execcd, 42 "whatis", execwhatis, 43 "eval", execeval, 44 "exec", execexec, /* but with popword first */ 45 "exit", execexit, 46 "shift", execshift, 47 "wait", execwait, 48 ".", execdot, 49 "finit", execfinit, 50 "flag", execflag, 51 "rfork", execnewpgrp, 52 0 53 }; 54 55 void 56 execnewpgrp(void) 57 { 58 int arg; 59 char *s; 60 switch(count(runq->argv->words)){ 61 case 1: 62 arg = RFENVG|RFNAMEG|RFNOTEG; 63 break; 64 case 2: 65 arg = 0; 66 for(s = runq->argv->words->next->word;*s;s++) switch(*s){ 67 default: 68 goto Usage; 69 case 'n': 70 arg|=RFNAMEG; break; 71 case 'N': 72 arg|=RFCNAMEG; 73 break; 74 case 'm': 75 arg|=RFNOMNT; break; 76 case 'e': 77 arg|=RFENVG; break; 78 case 'E': 79 arg|=RFCENVG; break; 80 case 's': 81 arg|=RFNOTEG; break; 82 case 'f': 83 arg|=RFFDG; break; 84 case 'F': 85 arg|=RFCFDG; break; 86 } 87 break; 88 default: 89 Usage: 90 pfmt(err, "Usage: %s [fnesFNEm]\n", runq->argv->words->word); 91 setstatus("rfork usage"); 92 poplist(); 93 return; 94 } 95 if(rfork(arg)==-1){ 96 pfmt(err, "rc: %s failed\n", runq->argv->words->word); 97 setstatus("rfork failed"); 98 } 99 else 100 setstatus(""); 101 poplist(); 102 } 103 104 void 105 Vinit(void) 106 { 107 int dir, f, len, i, n, nent; 108 char *buf, *s; 109 char envname[Maxenvname]; 110 word *val; 111 Dir *ent; 112 113 dir = open("/env", OREAD); 114 if(dir<0){ 115 pfmt(err, "rc: can't open /env: %r\n"); 116 return; 117 } 118 ent = nil; 119 for(;;){ 120 nent = dirread(dir, &ent); 121 if(nent <= 0) 122 break; 123 for(i = 0; i<nent; i++){ 124 len = ent[i].length; 125 if(len && strncmp(ent[i].name, "fn#", 3)!=0){ 126 snprint(envname, sizeof envname, "/env/%s", ent[i].name); 127 if((f = open(envname, 0))>=0){ 128 buf = emalloc(len+1); 129 n = readn(f, buf, len); 130 if (n <= 0) 131 buf[0] = '\0'; 132 else 133 buf[n] = '\0'; 134 val = 0; 135 /* Charitably add a 0 at the end if need be */ 136 if(buf[len-1]) 137 buf[len++]='\0'; 138 s = buf+len-1; 139 for(;;){ 140 while(s!=buf && s[-1]!='\0') --s; 141 val = newword(s, val); 142 if(s==buf) 143 break; 144 --s; 145 } 146 setvar(ent[i].name, val); 147 vlook(ent[i].name)->changed = 0; 148 close(f); 149 efree(buf); 150 } 151 } 152 } 153 free(ent); 154 } 155 close(dir); 156 } 157 int envdir; 158 159 void 160 Xrdfn(void) 161 { 162 int f, len; 163 Dir *e; 164 char envname[Maxenvname]; 165 static Dir *ent, *allocent; 166 static int nent; 167 168 for(;;){ 169 if(nent == 0){ 170 free(allocent); 171 nent = dirread(envdir, &allocent); 172 ent = allocent; 173 } 174 if(nent <= 0) 175 break; 176 while(nent){ 177 e = ent++; 178 nent--; 179 len = e->length; 180 if(len && strncmp(e->name, "fn#", 3)==0){ 181 snprint(envname, sizeof envname, "/env/%s", e->name); 182 if((f = open(envname, 0))>=0){ 183 execcmds(openfd(f)); 184 return; 185 } 186 } 187 } 188 } 189 close(envdir); 190 Xreturn(); 191 } 192 union code rdfns[4]; 193 194 void 195 execfinit(void) 196 { 197 static int first = 1; 198 if(first){ 199 rdfns[0].i = 1; 200 rdfns[1].f = Xrdfn; 201 rdfns[2].f = Xjump; 202 rdfns[3].i = 1; 203 first = 0; 204 } 205 Xpopm(); 206 envdir = open("/env", 0); 207 if(envdir<0){ 208 pfmt(err, "rc: can't open /env: %r\n"); 209 return; 210 } 211 start(rdfns, 1, runq->local); 212 } 213 214 int 215 Waitfor(int pid, int) 216 { 217 thread *p; 218 Waitmsg *w; 219 char errbuf[ERRMAX]; 220 221 if(pid >= 0 && !havewaitpid(pid)) 222 return 0; 223 224 while((w = wait()) != nil){ 225 delwaitpid(w->pid); 226 if(w->pid==pid){ 227 setstatus(w->msg); 228 free(w); 229 return 0; 230 } 231 for(p = runq->ret;p;p = p->ret) 232 if(p->pid==w->pid){ 233 p->pid=-1; 234 strcpy(p->status, w->msg); 235 } 236 free(w); 237 } 238 239 errstr(errbuf, sizeof errbuf); 240 if(strcmp(errbuf, "interrupted")==0) return -1; 241 return 0; 242 } 243 244 char ** 245 mkargv(word *a) 246 { 247 char **argv = (char **)emalloc((count(a)+2)*sizeof(char *)); 248 char **argp = argv+1; /* leave one at front for runcoms */ 249 for(;a;a = a->next) *argp++=a->word; 250 *argp = 0; 251 return argv; 252 } 253 254 void 255 addenv(var *v) 256 { 257 char envname[Maxenvname]; 258 word *w; 259 int f; 260 io *fd; 261 if(v->changed){ 262 v->changed = 0; 263 snprint(envname, sizeof envname, "/env/%s", v->name); 264 if((f = Creat(envname))<0) 265 pfmt(err, "rc: can't open %s: %r\n", envname); 266 else{ 267 for(w = v->val;w;w = w->next) 268 write(f, w->word, strlen(w->word)+1L); 269 close(f); 270 } 271 } 272 if(v->fnchanged){ 273 v->fnchanged = 0; 274 snprint(envname, sizeof envname, "/env/fn#%s", v->name); 275 if((f = Creat(envname))<0) 276 pfmt(err, "rc: can't open %s: %r\n", envname); 277 else{ 278 if(v->fn){ 279 fd = openfd(f); 280 pfmt(fd, "fn %q %s\n", v->name, v->fn[v->pc-1].s); 281 closeio(fd); 282 } 283 close(f); 284 } 285 } 286 } 287 288 void 289 updenvlocal(var *v) 290 { 291 if(v){ 292 updenvlocal(v->next); 293 addenv(v); 294 } 295 } 296 297 void 298 Updenv(void) 299 { 300 var *v, **h; 301 for(h = gvar;h!=&gvar[NVAR];h++) 302 for(v=*h;v;v = v->next) 303 addenv(v); 304 if(runq) 305 updenvlocal(runq->local); 306 } 307 308 /* not used on plan 9 */ 309 int 310 ForkExecute(char *file, char **argv, int sin, int sout, int serr) 311 { 312 int pid; 313 314 if(access(file, 1) != 0) 315 return -1; 316 switch(pid = fork()){ 317 case -1: 318 return -1; 319 case 0: 320 if(sin >= 0) 321 dup(sin, 0); 322 else 323 close(0); 324 if(sout >= 0) 325 dup(sout, 1); 326 else 327 close(1); 328 if(serr >= 0) 329 dup(serr, 2); 330 else 331 close(2); 332 exec(file, argv); 333 exits(file); 334 } 335 return pid; 336 } 337 338 void 339 Execute(word *args, word *path) 340 { 341 char **argv = mkargv(args); 342 char file[1024], errstr[1024]; 343 int nc; 344 345 Updenv(); 346 errstr[0] = '\0'; 347 for(;path;path = path->next){ 348 nc = strlen(path->word); 349 if(nc < sizeof file - 1){ /* 1 for / */ 350 strcpy(file, path->word); 351 if(file[0]){ 352 strcat(file, "/"); 353 nc++; 354 } 355 if(nc + strlen(argv[1]) < sizeof file){ 356 strcat(file, argv[1]); 357 exec(file, argv+1); 358 rerrstr(errstr, sizeof errstr); 359 /* 360 * if file exists and is executable, exec should 361 * have worked, unless it's a directory or an 362 * executable for another architecture. in 363 * particular, if it failed due to lack of 364 * swap/vm (e.g., arg. list too long) or other 365 * allocation failure, stop searching and print 366 * the reason for failure. 367 */ 368 if (strstr(errstr, " allocat") != nil || 369 strstr(errstr, " full") != nil) 370 break; 371 } 372 else werrstr("command name too long"); 373 } 374 } 375 pfmt(err, "%s: %s\n", argv[1], errstr); 376 efree((char *)argv); 377 } 378 #define NDIR 256 /* shoud be a better way */ 379 380 int 381 Globsize(char *p) 382 { 383 int isglob = 0, globlen = NDIR+1; 384 for(;*p;p++){ 385 if(*p==GLOB){ 386 p++; 387 if(*p!=GLOB) 388 isglob++; 389 globlen+=*p=='*'?NDIR:1; 390 } 391 else 392 globlen++; 393 } 394 return isglob?globlen:0; 395 } 396 #define NFD 50 397 398 struct{ 399 Dir *dbuf; 400 int i; 401 int n; 402 }dir[NFD]; 403 404 int 405 Opendir(char *name) 406 { 407 Dir *db; 408 int f; 409 f = open(name, 0); 410 if(f==-1) 411 return f; 412 db = dirfstat(f); 413 if(db!=nil && (db->mode&DMDIR)){ 414 if(f<NFD){ 415 dir[f].i = 0; 416 dir[f].n = 0; 417 } 418 free(db); 419 return f; 420 } 421 free(db); 422 close(f); 423 return -1; 424 } 425 426 static int 427 trimdirs(Dir *d, int nd) 428 { 429 int r, w; 430 431 for(r=w=0; r<nd; r++) 432 if(d[r].mode&DMDIR) 433 d[w++] = d[r]; 434 return w; 435 } 436 437 /* 438 * onlydirs is advisory -- it means you only 439 * need to return the directories. it's okay to 440 * return files too (e.g., on unix where you can't 441 * tell during the readdir), but that just makes 442 * the globber work harder. 443 */ 444 int 445 Readdir(int f, void *p, int onlydirs) 446 { 447 int n; 448 449 if(f<0 || f>=NFD) 450 return 0; 451 Again: 452 if(dir[f].i==dir[f].n){ /* read */ 453 free(dir[f].dbuf); 454 dir[f].dbuf = 0; 455 n = dirread(f, &dir[f].dbuf); 456 if(n>0){ 457 if(onlydirs){ 458 n = trimdirs(dir[f].dbuf, n); 459 if(n == 0) 460 goto Again; 461 } 462 dir[f].n = n; 463 }else 464 dir[f].n = 0; 465 dir[f].i = 0; 466 } 467 if(dir[f].i == dir[f].n) 468 return 0; 469 strcpy(p, dir[f].dbuf[dir[f].i].name); 470 dir[f].i++; 471 return 1; 472 } 473 474 void 475 Closedir(int f) 476 { 477 if(f>=0 && f<NFD){ 478 free(dir[f].dbuf); 479 dir[f].i = 0; 480 dir[f].n = 0; 481 dir[f].dbuf = 0; 482 } 483 close(f); 484 } 485 int interrupted = 0; 486 void 487 notifyf(void*, char *s) 488 { 489 int i; 490 for(i = 0;syssigname[i];i++) if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){ 491 if(strncmp(s, "sys: ", 5)!=0) interrupted = 1; 492 goto Out; 493 } 494 pfmt(err, "rc: note: %s\n", s); 495 noted(NDFLT); 496 return; 497 Out: 498 if(strcmp(s, "interrupt")!=0 || trap[i]==0){ 499 trap[i]++; 500 ntrap++; 501 } 502 if(ntrap>=32){ /* rc is probably in a trap loop */ 503 pfmt(err, "rc: Too many traps (trap %s), aborting\n", s); 504 abort(); 505 } 506 noted(NCONT); 507 } 508 509 void 510 Trapinit(void) 511 { 512 notify(notifyf); 513 } 514 515 void 516 Unlink(char *name) 517 { 518 remove(name); 519 } 520 521 long 522 Write(int fd, void *buf, long cnt) 523 { 524 return write(fd, buf, cnt); 525 } 526 527 long 528 Read(int fd, void *buf, long cnt) 529 { 530 return read(fd, buf, cnt); 531 } 532 533 long 534 Seek(int fd, long cnt, long whence) 535 { 536 return seek(fd, cnt, whence); 537 } 538 539 int 540 Executable(char *file) 541 { 542 Dir *statbuf; 543 int ret; 544 545 statbuf = dirstat(file); 546 if(statbuf == nil) 547 return 0; 548 ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0); 549 free(statbuf); 550 return ret; 551 } 552 553 int 554 Creat(char *file) 555 { 556 return create(file, 1, 0666L); 557 } 558 559 int 560 Dup(int a, int b) 561 { 562 return dup(a, b); 563 } 564 565 int 566 Dup1(int) 567 { 568 return -1; 569 } 570 571 void 572 Exit(char *stat) 573 { 574 Updenv(); 575 setstatus(stat); 576 exits(truestatus()?"":getstatus()); 577 } 578 579 int 580 Eintr(void) 581 { 582 return interrupted; 583 } 584 585 void 586 Noerror(void) 587 { 588 interrupted = 0; 589 } 590 591 int 592 Isatty(int fd) 593 { 594 char buf[64]; 595 596 if(fd2path(fd, buf, sizeof buf) != 0) 597 return 0; 598 599 /* might be #c/cons during boot - fixed 22 april 2005, remove this later */ 600 if(strcmp(buf, "#c/cons") == 0) 601 return 1; 602 603 /* might be /mnt/term/dev/cons */ 604 return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0; 605 } 606 607 void 608 Abort(void) 609 { 610 pfmt(err, "aborting\n"); 611 flush(err); 612 Exit("aborting"); 613 } 614 615 void 616 Memcpy(void *a, void *b, long n) 617 { 618 memmove(a, b, n); 619 } 620 621 void* 622 Malloc(ulong n) 623 { 624 return mallocz(n, 1); 625 } 626 627 int *waitpids; 628 int nwaitpids; 629 630 void 631 addwaitpid(int pid) 632 { 633 waitpids = realloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]); 634 if(waitpids == 0) 635 panic("Can't realloc %d waitpids", nwaitpids+1); 636 waitpids[nwaitpids++] = pid; 637 } 638 639 void 640 delwaitpid(int pid) 641 { 642 int r, w; 643 644 for(r=w=0; r<nwaitpids; r++) 645 if(waitpids[r] != pid) 646 waitpids[w++] = waitpids[r]; 647 nwaitpids = w; 648 } 649 650 void 651 clearwaitpids(void) 652 { 653 nwaitpids = 0; 654 } 655 656 int 657 havewaitpid(int pid) 658 { 659 int i; 660 661 for(i=0; i<nwaitpids; i++) 662 if(waitpids[i] == pid) 663 return 1; 664 return 0; 665 } 666 667 /* avoid loading any floating-point library code */ 668 int 669 _efgfmt(Fmt *) 670 { 671 return -1; 672 } 673