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 %s %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 int 307 ForkExecute(char *file, char **argv, int sin, int sout, int serr) 308 { 309 int pid; 310 311 if(access(file, 1) != 0) 312 return -1; 313 switch(pid = fork()){ 314 case -1: 315 return -1; 316 case 0: 317 if(sin >= 0) 318 dup(sin, 0); 319 else 320 close(0); 321 if(sout >= 0) 322 dup(sout, 1); 323 else 324 close(1); 325 if(serr >= 0) 326 dup(serr, 2); 327 else 328 close(2); 329 exec(file, argv); 330 exits(file); 331 } 332 return pid; 333 } 334 335 void 336 Execute(word *args, word *path) 337 { 338 char **argv = mkargv(args); 339 char file[1024]; 340 int nc; 341 Updenv(); 342 for(;path;path = path->next){ 343 nc = strlen(path->word); 344 if(nc < sizeof file - 1){ /* 1 for / */ 345 strcpy(file, path->word); 346 if(file[0]){ 347 strcat(file, "/"); 348 nc++; 349 } 350 if(nc + strlen(argv[1]) < sizeof file){ 351 strcat(file, argv[1]); 352 exec(file, argv+1); 353 } 354 else werrstr("command name too long"); 355 } 356 } 357 rerrstr(file, sizeof file); 358 pfmt(err, "%s: %s\n", argv[1], file); 359 efree((char *)argv); 360 } 361 #define NDIR 256 /* shoud be a better way */ 362 363 int 364 Globsize(char *p) 365 { 366 int isglob = 0, globlen = NDIR+1; 367 for(;*p;p++){ 368 if(*p==GLOB){ 369 p++; 370 if(*p!=GLOB) 371 isglob++; 372 globlen+=*p=='*'?NDIR:1; 373 } 374 else 375 globlen++; 376 } 377 return isglob?globlen:0; 378 } 379 #define NFD 50 380 381 struct{ 382 Dir *dbuf; 383 int i; 384 int n; 385 }dir[NFD]; 386 387 int 388 Opendir(char *name) 389 { 390 Dir *db; 391 int f; 392 f = open(name, 0); 393 if(f==-1) 394 return f; 395 db = dirfstat(f); 396 if(db!=nil && (db->mode&DMDIR)){ 397 if(f<NFD){ 398 dir[f].i = 0; 399 dir[f].n = 0; 400 } 401 free(db); 402 return f; 403 } 404 free(db); 405 close(f); 406 return -1; 407 } 408 409 static int 410 trimdirs(Dir *d, int nd) 411 { 412 int r, w; 413 414 for(r=w=0; r<nd; r++) 415 if(d[r].mode&DMDIR) 416 d[w++] = d[r]; 417 return w; 418 } 419 420 /* 421 * onlydirs is advisory -- it means you only 422 * need to return the directories. it's okay to 423 * return files too (e.g., on unix where you can't 424 * tell during the readdir), but that just makes 425 * the globber work harder. 426 */ 427 int 428 Readdir(int f, void *p, int onlydirs) 429 { 430 int n; 431 432 if(f<0 || f>=NFD) 433 return 0; 434 Again: 435 if(dir[f].i==dir[f].n){ /* read */ 436 free(dir[f].dbuf); 437 dir[f].dbuf = 0; 438 n = dirread(f, &dir[f].dbuf); 439 if(n>0){ 440 if(onlydirs){ 441 n = trimdirs(dir[f].dbuf, n); 442 if(n == 0) 443 goto Again; 444 } 445 dir[f].n = n; 446 }else 447 dir[f].n = 0; 448 dir[f].i = 0; 449 } 450 if(dir[f].i == dir[f].n) 451 return 0; 452 strcpy(p, dir[f].dbuf[dir[f].i].name); 453 dir[f].i++; 454 return 1; 455 } 456 457 void 458 Closedir(int f) 459 { 460 if(f>=0 && f<NFD){ 461 free(dir[f].dbuf); 462 dir[f].i = 0; 463 dir[f].n = 0; 464 dir[f].dbuf = 0; 465 } 466 close(f); 467 } 468 int interrupted = 0; 469 void 470 notifyf(void*, char *s) 471 { 472 int i; 473 for(i = 0;syssigname[i];i++) if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){ 474 if(strncmp(s, "sys: ", 5)!=0) interrupted = 1; 475 goto Out; 476 } 477 pfmt(err, "rc: note: %s\n", s); 478 noted(NDFLT); 479 return; 480 Out: 481 if(strcmp(s, "interrupt")!=0 || trap[i]==0){ 482 trap[i]++; 483 ntrap++; 484 } 485 if(ntrap>=32){ /* rc is probably in a trap loop */ 486 pfmt(err, "rc: Too many traps (trap %s), aborting\n", s); 487 abort(); 488 } 489 noted(NCONT); 490 } 491 492 void 493 Trapinit(void) 494 { 495 notify(notifyf); 496 } 497 498 void 499 Unlink(char *name) 500 { 501 remove(name); 502 } 503 504 long 505 Write(int fd, void *buf, long cnt) 506 { 507 return write(fd, buf, cnt); 508 } 509 510 long 511 Read(int fd, void *buf, long cnt) 512 { 513 return read(fd, buf, cnt); 514 } 515 516 long 517 Seek(int fd, long cnt, long whence) 518 { 519 return seek(fd, cnt, whence); 520 } 521 522 int 523 Executable(char *file) 524 { 525 Dir *statbuf; 526 int ret; 527 528 statbuf = dirstat(file); 529 if(statbuf == nil) 530 return 0; 531 ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0); 532 free(statbuf); 533 return ret; 534 } 535 536 int 537 Creat(char *file) 538 { 539 return create(file, 1, 0666L); 540 } 541 542 int 543 Dup(int a, int b) 544 { 545 return dup(a, b); 546 } 547 548 int 549 Dup1(int) 550 { 551 return -1; 552 } 553 554 void 555 Exit(char *stat) 556 { 557 Updenv(); 558 setstatus(stat); 559 exits(truestatus()?"":getstatus()); 560 } 561 562 int 563 Eintr(void) 564 { 565 return interrupted; 566 } 567 568 void 569 Noerror(void) 570 { 571 interrupted = 0; 572 } 573 574 int 575 Isatty(int fd) 576 { 577 char buf[64]; 578 579 if(fd2path(fd, buf, sizeof buf) != 0) 580 return 0; 581 582 /* might be #c/cons during boot - fixed 22 april 2005, remove this later */ 583 if(strcmp(buf, "#c/cons") == 0) 584 return 1; 585 586 /* might be /mnt/term/dev/cons */ 587 return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0; 588 } 589 590 void 591 Abort(void) 592 { 593 pfmt(err, "aborting\n"); 594 flush(err); 595 Exit("aborting"); 596 } 597 598 void 599 Memcpy(void *a, void *b, long n) 600 { 601 memmove(a, b, n); 602 } 603 604 void* 605 Malloc(ulong n) 606 { 607 return malloc(n); 608 } 609 610 int *waitpids; 611 int nwaitpids; 612 613 void 614 addwaitpid(int pid) 615 { 616 waitpids = realloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]); 617 if(waitpids == 0) 618 panic("Can't realloc %d waitpids", nwaitpids+1); 619 waitpids[nwaitpids++] = pid; 620 } 621 622 void 623 delwaitpid(int pid) 624 { 625 int r, w; 626 627 for(r=w=0; r<nwaitpids; r++) 628 if(waitpids[r] != pid) 629 waitpids[w++] = waitpids[r]; 630 nwaitpids = w; 631 } 632 633 void 634 clearwaitpids(void) 635 { 636 nwaitpids = 0; 637 } 638 639 int 640 havewaitpid(int pid) 641 { 642 int i; 643 644 for(i=0; i<nwaitpids; i++) 645 if(waitpids[i] == pid) 646 return 1; 647 return 0; 648 } 649