1 /* 2 * ``Exec'' network device. Mounted on net, provides /net/exec. 3 * 4 * exec protocol directory 5 * n connection directory 6 * ctl control messages (like connect) 7 * data data 8 * err errors 9 * local local address (pid of command) 10 * remote remote address (command) 11 * status status 12 */ 13 14 #include <u.h> 15 #include <libc.h> 16 #include <fcall.h> 17 #include <thread.h> 18 #include <9p.h> 19 #include "dat.h" 20 21 int fsdebug; 22 23 enum 24 { 25 Qroot, 26 Qexec, 27 Qclone, 28 Qn, 29 Qctl, 30 Qdata, 31 Qlocal, 32 Qremote, 33 Qstatus, 34 }; 35 36 #define PATH(type, n) ((type)|((n)<<8)) 37 #define TYPE(path) ((int)(path) & 0xFF) 38 #define NUM(path) ((uint)(path)>>8) 39 40 typedef struct Tab Tab; 41 struct Tab 42 { 43 char *name; 44 ulong mode; 45 }; 46 47 Tab tab[] = 48 { 49 "/", DMDIR|0555, 50 "exec", DMDIR|0555, 51 "clone", 0666, 52 nil, DMDIR|0555, 53 "ctl", 0666, 54 "data", 0666, 55 "local", 0444, 56 "remote", 0444, 57 "status", 0444, 58 }; 59 60 void 61 setexecname(char *s) 62 { 63 tab[Qexec].name = s; 64 } 65 66 ulong time0; 67 68 static void 69 fillstat(Dir *d, ulong path) 70 { 71 Tab *t; 72 int type; 73 char buf[32]; 74 75 memset(d, 0, sizeof(*d)); 76 d->uid = estrdup("exec"); 77 d->gid = estrdup("exec"); 78 d->qid.path = path; 79 d->atime = d->mtime = time0; 80 d->length = 0; 81 82 type = TYPE(path); 83 t = &tab[type]; 84 if(t->name) 85 d->name = estrdup(t->name); 86 else{ 87 snprint(buf, sizeof buf, "%ud", NUM(path)); 88 d->name = estrdup(buf); 89 } 90 d->qid.type = t->mode>>24; 91 d->mode = t->mode; 92 } 93 94 static void 95 fsstat(Req *r) 96 { 97 fillstat(&r->d, r->fid->qid.path); 98 respond(r, nil); 99 } 100 101 static int 102 rootgen(int i, Dir *d, void*) 103 { 104 if(i < 1){ 105 fillstat(d, PATH(Qexec, 0)); 106 return 0; 107 } 108 return -1; 109 } 110 111 static int 112 execgen(int i, Dir *d, void*) 113 { 114 if(i < 1){ 115 fillstat(d, PATH(Qclone, 0)); 116 return 0; 117 } 118 i -= 1; 119 120 if(i < nclient){ 121 fillstat(d, PATH(Qn, i)); 122 return 0; 123 } 124 return -1; 125 } 126 127 static int 128 conngen(int i, Dir *d, void *aux) 129 { 130 Client *c; 131 132 c = aux; 133 i += Qn+1; 134 if(i <= Qstatus){ 135 fillstat(d, PATH(i, c->num)); 136 return 0; 137 } 138 return -1; 139 } 140 141 char *statusstr[] = 142 { 143 "Closed", 144 "Exec", 145 "Established", 146 "Hangup", 147 }; 148 149 static void 150 fsread(Req *r) 151 { 152 char e[ERRMAX], *s; 153 ulong path; 154 155 path = r->fid->qid.path; 156 switch(TYPE(path)){ 157 default: 158 snprint(e, sizeof e, "bug in execnet path=%lux", path); 159 respond(r, e); 160 break; 161 162 case Qroot: 163 dirread9p(r, rootgen, nil); 164 respond(r, nil); 165 break; 166 167 case Qexec: 168 dirread9p(r, execgen, nil); 169 respond(r, nil); 170 break; 171 172 case Qn: 173 dirread9p(r, conngen, client[NUM(path)]); 174 respond(r, nil); 175 break; 176 177 case Qctl: 178 snprint(e, sizeof e, "%ud", NUM(path)); 179 readstr(r, e); 180 respond(r, nil); 181 break; 182 183 case Qdata: 184 dataread(r, client[NUM(path)]); 185 break; 186 187 case Qlocal: 188 snprint(e, sizeof e, "%d", client[NUM(path)]->pid); 189 readstr(r, e); 190 respond(r, nil); 191 break; 192 193 case Qremote: 194 s = client[NUM(path)]->cmd; 195 if(strlen(s) >= 5) /* "exec " */ 196 readstr(r, s+5); 197 else 198 readstr(r, s); 199 respond(r, nil); 200 break; 201 202 case Qstatus: 203 readstr(r, statusstr[client[NUM(path)]->status]); 204 respond(r, nil); 205 break; 206 } 207 } 208 209 static void 210 fswrite(Req *r) 211 { 212 char e[ERRMAX]; 213 ulong path; 214 215 path = r->fid->qid.path; 216 switch(TYPE(path)){ 217 default: 218 snprint(e, sizeof e, "bug in execnet path=%lux", path); 219 respond(r, e); 220 break; 221 222 case Qctl: 223 ctlwrite(r, client[NUM(path)]); 224 break; 225 226 case Qdata: 227 datawrite(r, client[NUM(path)]); 228 break; 229 } 230 } 231 232 233 static void 234 fsflush(Req *r) 235 { 236 ulong path; 237 Req *or; 238 239 for(or=r; or->ifcall.type==Tflush; or=or->oldreq) 240 ; 241 242 if(or->ifcall.type != Tread && or->ifcall.type != Twrite) 243 abort(); 244 245 path = or->fid->qid.path; 246 if(TYPE(path) != Qdata) 247 abort(); 248 249 clientflush(or, client[NUM(path)]); 250 } 251 252 static void 253 fsattach(Req *r) 254 { 255 if(r->ifcall.aname && r->ifcall.aname[0]){ 256 respond(r, "invalid attach specifier"); 257 return; 258 } 259 r->fid->qid.path = PATH(Qroot, 0); 260 r->fid->qid.type = QTDIR; 261 r->fid->qid.vers = 0; 262 r->ofcall.qid = r->fid->qid; 263 respond(r, nil); 264 } 265 266 static char* 267 fswalk1(Fid *fid, char *name, Qid *qid) 268 { 269 char buf[32]; 270 int i, n; 271 ulong path; 272 273 if(!(fid->qid.type&QTDIR)) 274 return "walk in non-directory"; 275 276 path = fid->qid.path; 277 if(strcmp(name, "..") == 0){ 278 switch(TYPE(path)){ 279 case Qn: 280 qid->path = PATH(Qexec, 0); 281 qid->type = QTDIR; 282 return nil; 283 case Qroot: 284 case Qexec: 285 qid->path = PATH(Qroot, 0); 286 qid->type = QTDIR; 287 return nil; 288 default: 289 return "bug in fswalk1"; 290 } 291 } 292 293 i = TYPE(path)+1; 294 for(; i<nelem(tab); i++){ 295 if(i==Qn){ 296 n = atoi(name); 297 snprint(buf, sizeof buf, "%d", n); 298 if(n < nclient && strcmp(buf, name) == 0){ 299 qid->path = PATH(Qn, n); 300 qid->type = QTDIR; 301 return nil; 302 } 303 break; 304 } 305 if(strcmp(tab[i].name, name) == 0){ 306 qid->path = PATH(i, NUM(path)); 307 qid->type = tab[i].mode>>24; 308 return nil; 309 } 310 if(tab[i].mode&DMDIR) 311 break; 312 } 313 return "directory entry not found"; 314 } 315 316 static void 317 fsopen(Req *r) 318 { 319 static int need[4] = { 4, 2, 6, 1 }; 320 ulong path; 321 int n; 322 Tab *t; 323 324 /* 325 * lib9p already handles the blatantly obvious. 326 * we just have to enforce the permissions we have set. 327 */ 328 path = r->fid->qid.path; 329 t = &tab[TYPE(path)]; 330 n = need[r->ifcall.mode&3]; 331 if((n&t->mode) != n){ 332 respond(r, "permission denied"); 333 return; 334 } 335 336 switch(TYPE(path)){ 337 case Qclone: 338 n = newclient(); 339 path = PATH(Qctl, n); 340 r->fid->qid.path = path; 341 r->ofcall.qid.path = path; 342 if(fsdebug) 343 fprint(2, "open clone => path=%lux\n", path); 344 t = &tab[Qctl]; 345 /* fall through */ 346 default: 347 if(t-tab >= Qn) 348 client[NUM(path)]->ref++; 349 respond(r, nil); 350 break; 351 } 352 } 353 354 Channel *cclunk; 355 Channel *cclunkwait; 356 Channel *creq; 357 Channel *creqwait; 358 359 static void 360 fsthread(void*) 361 { 362 ulong path; 363 Alt a[3]; 364 Fid *fid; 365 Req *r; 366 367 threadsetname("fsthread"); 368 369 a[0].op = CHANRCV; 370 a[0].c = cclunk; 371 a[0].v = &fid; 372 a[1].op = CHANRCV; 373 a[1].c = creq; 374 a[1].v = &r; 375 a[2].op = CHANEND; 376 377 for(;;){ 378 switch(alt(a)){ 379 case 0: 380 path = fid->qid.path; 381 if(fid->omode != -1 && TYPE(path) >= Qn) 382 closeclient(client[NUM(path)]); 383 sendp(cclunkwait, nil); 384 break; 385 case 1: 386 switch(r->ifcall.type){ 387 case Tattach: 388 fsattach(r); 389 break; 390 case Topen: 391 fsopen(r); 392 break; 393 case Tread: 394 fsread(r); 395 break; 396 case Twrite: 397 fswrite(r); 398 break; 399 case Tstat: 400 fsstat(r); 401 break; 402 case Tflush: 403 fsflush(r); 404 break; 405 default: 406 respond(r, "bug in fsthread"); 407 break; 408 } 409 sendp(creqwait, 0); 410 break; 411 } 412 } 413 } 414 415 static void 416 fsdestroyfid(Fid *fid) 417 { 418 sendp(cclunk, fid); 419 recvp(cclunkwait); 420 } 421 422 static void 423 fssend(Req *r) 424 { 425 sendp(creq, r); 426 recvp(creqwait); /* avoids need to deal with spurious flushes */ 427 } 428 429 void 430 initfs(void) 431 { 432 time0 = time(0); 433 creq = chancreate(sizeof(void*), 0); 434 creqwait = chancreate(sizeof(void*), 0); 435 cclunk = chancreate(sizeof(void*), 0); 436 cclunkwait = chancreate(sizeof(void*), 0); 437 procrfork(fsthread, nil, STACK, RFNAMEG); 438 } 439 440 Srv fs = 441 { 442 .attach= fssend, 443 .destroyfid= fsdestroyfid, 444 .walk1= fswalk1, 445 .open= fssend, 446 .read= fssend, 447 .write= fssend, 448 .stat= fssend, 449 .flush= fssend, 450 }; 451