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