1 #include "dat.h" 2 #include "fns.h" 3 #include "error.h" 4 5 enum 6 { 7 Qtopdir, /* top level directory */ 8 Qcmd, 9 Qclonus, 10 Qconvdir, 11 Qconvbase, 12 Qdata = Qconvbase, 13 Qstderr, 14 Qctl, 15 Qstatus, 16 Qwait, 17 18 Debug=0 /* to help debug os.c */ 19 }; 20 #define TYPE(x) ((ulong)(x).path & 0xf) 21 #define CONV(x) (((ulong)(x).path >> 4)&0xfff) 22 #define QID(c, y) (((c)<<4) | (y)) 23 24 typedef struct Conv Conv; 25 struct Conv 26 { 27 int x; 28 int inuse; 29 int fd[3]; /* stdin, stdout, and stderr */ 30 int count[3]; /* number of readers on stdin/stdout/stderr */ 31 int perm; 32 char* owner; 33 char* state; 34 Cmdbuf* cmd; 35 char* dir; 36 QLock l; /* protects state changes */ 37 Queue* waitq; 38 void* child; 39 char* error; /* on start up */ 40 int nice; 41 short killonclose; 42 short killed; 43 Rendez startr; 44 }; 45 46 static struct 47 { 48 QLock l; 49 int nc; 50 int maxconv; 51 Conv** conv; 52 } cmd; 53 54 static Conv* cmdclone(char*); 55 static void cmdproc(void*); 56 57 static int 58 cmd3gen(Chan *c, int i, Dir *dp) 59 { 60 Qid q; 61 Conv *cv; 62 63 cv = cmd.conv[CONV(c->qid)]; 64 switch(i){ 65 default: 66 return -1; 67 case Qdata: 68 mkqid(&q, QID(CONV(c->qid), Qdata), 0, QTFILE); 69 devdir(c, q, "data", 0, cv->owner, cv->perm, dp); 70 return 1; 71 case Qstderr: 72 mkqid(&q, QID(CONV(c->qid), Qstderr), 0, QTFILE); 73 devdir(c, q, "stderr", 0, cv->owner, 0444, dp); 74 return 1; 75 case Qctl: 76 mkqid(&q, QID(CONV(c->qid), Qctl), 0, QTFILE); 77 devdir(c, q, "ctl", 0, cv->owner, cv->perm, dp); 78 return 1; 79 case Qstatus: 80 mkqid(&q, QID(CONV(c->qid), Qstatus), 0, QTFILE); 81 devdir(c, q, "status", 0, cv->owner, 0444, dp); 82 return 1; 83 case Qwait: 84 mkqid(&q, QID(CONV(c->qid), Qwait), 0, QTFILE); 85 devdir(c, q, "wait", 0, cv->owner, 0444, dp); 86 return 1; 87 } 88 } 89 90 static int 91 cmdgen(Chan *c, char *name, Dirtab *d, int nd, int s, Dir *dp) 92 { 93 Qid q; 94 Conv *cv; 95 96 USED(name); 97 USED(nd); 98 USED(d); 99 100 if(s == DEVDOTDOT){ 101 switch(TYPE(c->qid)){ 102 case Qtopdir: 103 case Qcmd: 104 mkqid(&q, QID(0, Qtopdir), 0, QTDIR); 105 devdir(c, q, "#C", 0, eve, DMDIR|0555, dp); 106 break; 107 case Qconvdir: 108 mkqid(&q, QID(0, Qcmd), 0, QTDIR); 109 devdir(c, q, "cmd", 0, eve, DMDIR|0555, dp); 110 break; 111 default: 112 panic("cmdgen %llux", c->qid.path); 113 } 114 return 1; 115 } 116 117 switch(TYPE(c->qid)) { 118 case Qtopdir: 119 if(s >= 1) 120 return -1; 121 mkqid(&q, QID(0, Qcmd), 0, QTDIR); 122 devdir(c, q, "cmd", 0, "cmd", DMDIR|0555, dp); 123 return 1; 124 case Qcmd: 125 if(s < cmd.nc) { 126 cv = cmd.conv[s]; 127 mkqid(&q, QID(s, Qconvdir), 0, QTDIR); 128 sprint(up->genbuf, "%d", s); 129 devdir(c, q, up->genbuf, 0, cv->owner, DMDIR|0555, dp); 130 return 1; 131 } 132 s -= cmd.nc; 133 if(s == 0){ 134 mkqid(&q, QID(0, Qclonus), 0, QTFILE); 135 devdir(c, q, "clone", 0, "cmd", 0666, dp); 136 return 1; 137 } 138 return -1; 139 case Qclonus: 140 if(s == 0){ 141 mkqid(&q, QID(0, Qclonus), 0, QTFILE); 142 devdir(c, q, "clone", 0, "cmd", 0666, dp); 143 return 1; 144 } 145 return -1; 146 case Qconvdir: 147 return cmd3gen(c, Qconvbase+s, dp); 148 case Qdata: 149 case Qstderr: 150 case Qctl: 151 case Qstatus: 152 case Qwait: 153 return cmd3gen(c, TYPE(c->qid), dp); 154 } 155 return -1; 156 } 157 158 static void 159 cmdinit(void) 160 { 161 cmd.maxconv = 1000; 162 cmd.conv = mallocz(sizeof(Conv*)*(cmd.maxconv+1), 1); 163 /* cmd.conv is checked by cmdattach, below */ 164 } 165 166 static Chan * 167 cmdattach(char *spec) 168 { 169 Chan *c; 170 171 if(cmd.conv == nil) 172 error(Enomem); 173 c = devattach('C', spec); 174 mkqid(&c->qid, QID(0, Qtopdir), 0, QTDIR); 175 return c; 176 } 177 178 static Walkqid* 179 cmdwalk(Chan *c, Chan *nc, char **name, int nname) 180 { 181 return devwalk(c, nc, name, nname, 0, 0, cmdgen); 182 } 183 184 static int 185 cmdstat(Chan *c, uchar *db, int n) 186 { 187 return devstat(c, db, n, 0, 0, cmdgen); 188 } 189 190 static Chan * 191 cmdopen(Chan *c, int omode) 192 { 193 int perm; 194 Conv *cv; 195 char *user; 196 197 perm = 0; 198 omode = openmode(omode); 199 switch(omode) { 200 case OREAD: 201 perm = 4; 202 break; 203 case OWRITE: 204 perm = 2; 205 break; 206 case ORDWR: 207 perm = 6; 208 break; 209 } 210 211 switch(TYPE(c->qid)) { 212 default: 213 break; 214 case Qtopdir: 215 case Qcmd: 216 case Qconvdir: 217 case Qstatus: 218 if(omode != OREAD) 219 error(Eperm); 220 break; 221 case Qclonus: 222 qlock(&cmd.l); 223 if(waserror()){ 224 qunlock(&cmd.l); 225 nexterror(); 226 } 227 cv = cmdclone(up->env->user); 228 poperror(); 229 qunlock(&cmd.l); 230 if(cv == 0) 231 error(Enodev); 232 mkqid(&c->qid, QID(cv->x, Qctl), 0, QTFILE); 233 break; 234 case Qdata: 235 case Qstderr: 236 case Qctl: 237 case Qwait: 238 qlock(&cmd.l); 239 cv = cmd.conv[CONV(c->qid)]; 240 qlock(&cv->l); 241 if(waserror()){ 242 qunlock(&cv->l); 243 qunlock(&cmd.l); 244 nexterror(); 245 } 246 user = up->env->user; 247 if((perm & (cv->perm>>6)) != perm) { 248 if(strcmp(user, cv->owner) != 0 || 249 (perm & cv->perm) != perm) 250 error(Eperm); 251 } 252 switch(TYPE(c->qid)){ 253 case Qdata: 254 if(omode == OWRITE || omode == ORDWR) 255 cv->count[0]++; 256 if(omode == OREAD || omode == ORDWR) 257 cv->count[1]++; 258 break; 259 case Qstderr: 260 if(omode != OREAD) 261 error(Eperm); 262 cv->count[2]++; 263 break; 264 case Qwait: 265 if(cv->waitq == nil) 266 cv->waitq = qopen(1024, Qmsg, nil, 0); 267 break; 268 } 269 cv->inuse++; 270 if(cv->inuse == 1) { 271 cv->state = "Open"; 272 kstrdup(&cv->owner, user); 273 cv->perm = 0660; 274 cv->nice = 0; 275 } 276 poperror(); 277 qunlock(&cv->l); 278 qunlock(&cmd.l); 279 break; 280 } 281 c->mode = omode; 282 c->flag |= COPEN; 283 c->offset = 0; 284 return c; 285 } 286 287 static void 288 closeconv(Conv *c) 289 { 290 kstrdup(&c->owner, "cmd"); 291 kstrdup(&c->dir, rootdir); 292 c->perm = 0666; 293 c->state = "Closed"; 294 c->killonclose = 0; 295 c->killed = 0; 296 c->nice = 0; 297 free(c->cmd); 298 c->cmd = nil; 299 if(c->waitq != nil){ 300 qfree(c->waitq); 301 c->waitq = nil; 302 } 303 free(c->error); 304 c->error = nil; 305 } 306 307 static void 308 cmdfdclose(Conv *c, int fd) 309 { 310 if(--c->count[fd] == 0 && c->fd[fd] != -1){ 311 close(c->fd[fd]); 312 c->fd[fd] = -1; 313 } 314 } 315 316 static void 317 cmdclose(Chan *c) 318 { 319 Conv *cc; 320 int r; 321 322 if((c->flag & COPEN) == 0) 323 return; 324 325 switch(TYPE(c->qid)) { 326 case Qctl: 327 case Qdata: 328 case Qstderr: 329 case Qwait: 330 cc = cmd.conv[CONV(c->qid)]; 331 qlock(&cc->l); 332 if(TYPE(c->qid) == Qdata){ 333 if(c->mode == OWRITE || c->mode == ORDWR) 334 cmdfdclose(cc, 0); 335 if(c->mode == OREAD || c->mode == ORDWR) 336 cmdfdclose(cc, 1); 337 }else if(TYPE(c->qid) == Qstderr) 338 cmdfdclose(cc, 2); 339 340 r = --cc->inuse; 341 if(cc->child != nil){ 342 if(!cc->killed) 343 if(r == 0 || (cc->killonclose && TYPE(c->qid) == Qctl)){ 344 oscmdkill(cc->child); 345 cc->killed = 1; 346 } 347 }else if(r == 0) 348 closeconv(cc); 349 350 qunlock(&cc->l); 351 break; 352 } 353 } 354 355 static long 356 cmdread(Chan *ch, void *a, long n, vlong offset) 357 { 358 Conv *c; 359 char *p, *cmds; 360 int fd; 361 362 USED(offset); 363 364 p = a; 365 switch(TYPE(ch->qid)) { 366 default: 367 error(Eperm); 368 case Qcmd: 369 case Qtopdir: 370 case Qconvdir: 371 return devdirread(ch, a, n, 0, 0, cmdgen); 372 case Qctl: 373 sprint(up->genbuf, "%ld", CONV(ch->qid)); 374 return readstr(offset, p, n, up->genbuf); 375 case Qstatus: 376 c = cmd.conv[CONV(ch->qid)]; 377 cmds = ""; 378 if(c->cmd != nil) 379 cmds = c->cmd->f[1]; 380 snprint(up->genbuf, sizeof(up->genbuf), "cmd/%d %d %s %q %q\n", 381 c->x, c->inuse, c->state, c->dir, cmds); 382 return readstr(offset, p, n, up->genbuf); 383 case Qdata: 384 case Qstderr: 385 fd = 1; 386 if(TYPE(ch->qid) == Qstderr) 387 fd = 2; 388 c = cmd.conv[CONV(ch->qid)]; 389 qlock(&c->l); 390 if(c->fd[fd] == -1){ 391 qunlock(&c->l); 392 return 0; 393 } 394 qunlock(&c->l); 395 osenter(); 396 n = read(c->fd[fd], a, n); 397 osleave(); 398 if(n < 0) 399 oserror(); 400 return n; 401 case Qwait: 402 c = cmd.conv[CONV(ch->qid)]; 403 return qread(c->waitq, a, n); 404 } 405 } 406 407 static int 408 cmdstarted(void *a) 409 { 410 Conv *c; 411 412 c = a; 413 return c->child != nil || c->error != nil || strcmp(c->state, "Execute") != 0; 414 } 415 416 enum 417 { 418 CMdir, 419 CMexec, 420 CMkill, 421 CMnice, 422 CMkillonclose 423 }; 424 425 static 426 Cmdtab cmdtab[] = { 427 CMdir, "dir", 2, 428 CMexec, "exec", 0, 429 CMkill, "kill", 1, 430 CMnice, "nice", 0, 431 CMkillonclose, "killonclose", 0, 432 }; 433 434 static long 435 cmdwrite(Chan *ch, void *a, long n, vlong offset) 436 { 437 int i, r; 438 Conv *c; 439 Cmdbuf *cb; 440 Cmdtab *ct; 441 442 USED(offset); 443 444 switch(TYPE(ch->qid)) { 445 default: 446 error(Eperm); 447 case Qctl: 448 c = cmd.conv[CONV(ch->qid)]; 449 cb = parsecmd(a, n); 450 if(waserror()){ 451 free(cb); 452 nexterror(); 453 } 454 ct = lookupcmd(cb, cmdtab, nelem(cmdtab)); 455 switch(ct->index){ 456 case CMdir: 457 kstrdup(&c->dir, cb->f[1]); 458 break; 459 case CMexec: 460 poperror(); /* cb */ 461 qlock(&c->l); 462 if(waserror()){ 463 qunlock(&c->l); 464 free(cb); 465 nexterror(); 466 } 467 if(c->child != nil || c->cmd != nil) 468 error(Einuse); 469 for(i = 0; i < nelem(c->fd); i++) 470 if(c->fd[i] != -1) 471 error(Einuse); 472 if(cb->nf < 1) 473 error(Etoosmall); 474 kproc("cmdproc", cmdproc, c, 0); /* cmdproc held back until unlock below */ 475 free(c->cmd); 476 c->cmd = cb; /* don't free cb */ 477 c->state = "Execute"; 478 poperror(); 479 qunlock(&c->l); 480 while(waserror()) 481 ; 482 Sleep(&c->startr, cmdstarted, c); 483 poperror(); 484 if(c->error) 485 error(c->error); 486 return n; /* avoid free(cb) below */ 487 case CMkill: 488 qlock(&c->l); 489 if(waserror()){ 490 qunlock(&c->l); 491 nexterror(); 492 } 493 if(c->child == nil) 494 error("not started"); 495 if(oscmdkill(c->child) < 0) 496 oserror(); 497 poperror(); 498 qunlock(&c->l); 499 break; 500 case CMnice: 501 c->nice = cb->nf > 1? atoi(cb->f[1]): 1; 502 break; 503 case CMkillonclose: 504 c->killonclose = 1; 505 break; 506 } 507 poperror(); 508 free(cb); 509 break; 510 case Qdata: 511 c = cmd.conv[CONV(ch->qid)]; 512 qlock(&c->l); 513 if(c->fd[0] == -1){ 514 qunlock(&c->l); 515 error(Ehungup); 516 } 517 qunlock(&c->l); 518 osenter(); 519 r = write(c->fd[0], a, n); 520 osleave(); 521 if(r == 0) 522 error(Ehungup); 523 if(r < 0) { 524 /* XXX perhaps should kill writer "write on closed pipe" here, 2nd time around? */ 525 oserror(); 526 } 527 return r; 528 } 529 return n; 530 } 531 532 static int 533 cmdwstat(Chan *c, uchar *dp, int n) 534 { 535 Dir *d; 536 Conv *cv; 537 538 switch(TYPE(c->qid)){ 539 default: 540 error(Eperm); 541 case Qctl: 542 case Qdata: 543 case Qstderr: 544 d = malloc(sizeof(*d)+n); 545 if(d == nil) 546 error(Enomem); 547 if(waserror()){ 548 free(d); 549 nexterror(); 550 } 551 n = convM2D(dp, n, d, (char*)&d[1]); 552 if(n == 0) 553 error(Eshortstat); 554 cv = cmd.conv[CONV(c->qid)]; 555 if(!iseve() && strcmp(up->env->user, cv->owner) != 0) 556 error(Eperm); 557 if(!emptystr(d->uid)) 558 kstrdup(&cv->owner, d->uid); 559 if(d->mode != ~0UL) 560 cv->perm = d->mode & 0777; 561 poperror(); 562 free(d); 563 break; 564 } 565 return n; 566 } 567 568 static Conv* 569 cmdclone(char *user) 570 { 571 Conv *c, **pp, **ep; 572 int i; 573 574 c = nil; 575 ep = &cmd.conv[cmd.maxconv]; 576 for(pp = cmd.conv; pp < ep; pp++) { 577 c = *pp; 578 if(c == nil) { 579 c = malloc(sizeof(Conv)); 580 if(c == nil) 581 error(Enomem); 582 qlock(&c->l); 583 c->inuse = 1; 584 c->x = pp - cmd.conv; 585 cmd.nc++; 586 *pp = c; 587 break; 588 } 589 if(canqlock(&c->l)){ 590 if(c->inuse == 0 && c->child == nil) 591 break; 592 qunlock(&c->l); 593 } 594 } 595 if(pp >= ep) 596 return nil; 597 598 c->inuse = 1; 599 kstrdup(&c->owner, user); 600 kstrdup(&c->dir, rootdir); 601 c->perm = 0660; 602 c->state = "Closed"; 603 for(i=0; i<nelem(c->fd); i++) 604 c->fd[i] = -1; 605 606 qunlock(&c->l); 607 return c; 608 } 609 610 static void 611 cmdproc(void *a) 612 { 613 Conv *c; 614 int n; 615 char status[ERRMAX]; 616 void *t; 617 618 c = a; 619 qlock(&c->l); 620 if(Debug) 621 print("f[0]=%q f[1]=%q\n", c->cmd->f[0], c->cmd->f[1]); 622 if(waserror()){ 623 if(Debug) 624 print("failed: %q\n", up->env->errstr); 625 kstrdup(&c->error, up->env->errstr); 626 c->state = "Done"; 627 qunlock(&c->l); 628 Wakeup(&c->startr); 629 pexit("cmdproc", 0); 630 } 631 t = oscmd(c->cmd->f+1, c->nice, c->dir, c->fd); 632 if(t == nil) 633 oserror(); 634 c->child = t; /* to allow oscmdkill */ 635 poperror(); 636 qunlock(&c->l); 637 Wakeup(&c->startr); 638 if(Debug) 639 print("started\n"); 640 while(waserror()) 641 oscmdkill(t); 642 osenter(); 643 n = oscmdwait(t, status, sizeof(status)); 644 osleave(); 645 if(n < 0){ 646 oserrstr(up->genbuf, sizeof(up->genbuf)); 647 n = snprint(status, sizeof(status), "0 0 0 0 %q", up->genbuf); 648 } 649 qlock(&c->l); 650 c->child = nil; 651 oscmdfree(t); 652 if(Debug){ 653 status[n]=0; 654 print("done %d %d %d: %q\n", c->fd[0], c->fd[1], c->fd[2], status); 655 } 656 if(c->inuse > 0){ 657 c->state = "Done"; 658 if(c->waitq != nil) 659 qproduce(c->waitq, status, n); 660 }else 661 closeconv(c); 662 qunlock(&c->l); 663 pexit("", 0); 664 } 665 666 Dev cmddevtab = { 667 'C', 668 "cmd", 669 670 cmdinit, 671 cmdattach, 672 cmdwalk, 673 cmdstat, 674 cmdopen, 675 devcreate, 676 cmdclose, 677 cmdread, 678 devbread, 679 cmdwrite, 680 devbwrite, 681 devremove, 682 cmdwstat 683 }; 684