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
cmd3gen(Chan * c,int i,Dir * dp)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
cmdgen(Chan * c,char * name,Dirtab * d,int nd,int s,Dir * dp)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
cmdinit(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 *
cmdattach(char * spec)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*
cmdwalk(Chan * c,Chan * nc,char ** name,int nname)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
cmdstat(Chan * c,uchar * db,int n)185 cmdstat(Chan *c, uchar *db, int n)
186 {
187 return devstat(c, db, n, 0, 0, cmdgen);
188 }
189
190 static Chan *
cmdopen(Chan * c,int omode)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
closeconv(Conv * c)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
cmdfdclose(Conv * c,int fd)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
cmdclose(Chan * c)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
cmdread(Chan * ch,void * a,long n,vlong offset)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
cmdstarted(void * a)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
cmdwrite(Chan * ch,void * a,long n,vlong offset)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
cmdwstat(Chan * c,uchar * dp,int n)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*
cmdclone(char * user)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
cmdproc(void * a)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