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 respond(r, nil); 251 } 252 253 static void 254 fsattach(Req *r) 255 { 256 if(r->ifcall.aname && r->ifcall.aname[0]){ 257 respond(r, "invalid attach specifier"); 258 return; 259 } 260 r->fid->qid.path = PATH(Qroot, 0); 261 r->fid->qid.type = QTDIR; 262 r->fid->qid.vers = 0; 263 r->ofcall.qid = r->fid->qid; 264 respond(r, nil); 265 } 266 267 static char* 268 fswalk1(Fid *fid, char *name, Qid *qid) 269 { 270 char buf[32]; 271 int i, n; 272 ulong path; 273 274 if(!(fid->qid.type&QTDIR)) 275 return "walk in non-directory"; 276 277 path = fid->qid.path; 278 if(strcmp(name, "..") == 0){ 279 switch(TYPE(path)){ 280 case Qn: 281 qid->path = PATH(Qexec, 0); 282 qid->type = QTDIR; 283 return nil; 284 case Qroot: 285 case Qexec: 286 qid->path = PATH(Qroot, 0); 287 qid->type = QTDIR; 288 return nil; 289 default: 290 return "bug in fswalk1"; 291 } 292 } 293 294 i = TYPE(path)+1; 295 for(; i<nelem(tab); i++){ 296 if(i==Qn){ 297 n = atoi(name); 298 snprint(buf, sizeof buf, "%d", n); 299 if(n < nclient && strcmp(buf, name) == 0){ 300 qid->path = PATH(Qn, n); 301 qid->type = QTDIR; 302 return nil; 303 } 304 break; 305 } 306 if(strcmp(tab[i].name, name) == 0){ 307 qid->path = PATH(i, NUM(path)); 308 qid->type = tab[i].mode>>24; 309 return nil; 310 } 311 if(tab[i].mode&DMDIR) 312 break; 313 } 314 return "directory entry not found"; 315 } 316 317 static void 318 fsopen(Req *r) 319 { 320 static int need[4] = { 4, 2, 6, 1 }; 321 ulong path; 322 int n; 323 Tab *t; 324 325 /* 326 * lib9p already handles the blatantly obvious. 327 * we just have to enforce the permissions we have set. 328 */ 329 path = r->fid->qid.path; 330 t = &tab[TYPE(path)]; 331 n = need[r->ifcall.mode&3]; 332 if((n&t->mode) != n){ 333 respond(r, "permission denied"); 334 return; 335 } 336 337 switch(TYPE(path)){ 338 case Qclone: 339 n = newclient(); 340 path = PATH(Qctl, n); 341 r->fid->qid.path = path; 342 r->ofcall.qid.path = path; 343 if(fsdebug) 344 fprint(2, "open clone => path=%lux\n", path); 345 t = &tab[Qctl]; 346 /* fall through */ 347 default: 348 if(t-tab >= Qn) 349 client[NUM(path)]->ref++; 350 respond(r, nil); 351 break; 352 } 353 } 354 355 Channel *cclunk; 356 Channel *cclunkwait; 357 Channel *creq; 358 Channel *creqwait; 359 360 static void 361 fsthread(void*) 362 { 363 ulong path; 364 Alt a[3]; 365 Fid *fid; 366 Req *r; 367 368 threadsetname("fsthread"); 369 370 a[0].op = CHANRCV; 371 a[0].c = cclunk; 372 a[0].v = &fid; 373 a[1].op = CHANRCV; 374 a[1].c = creq; 375 a[1].v = &r; 376 a[2].op = CHANEND; 377 378 for(;;){ 379 switch(alt(a)){ 380 case 0: 381 path = fid->qid.path; 382 if(fid->omode != -1 && TYPE(path) >= Qn) 383 closeclient(client[NUM(path)]); 384 sendp(cclunkwait, nil); 385 break; 386 case 1: 387 switch(r->ifcall.type){ 388 case Tattach: 389 fsattach(r); 390 break; 391 case Topen: 392 fsopen(r); 393 break; 394 case Tread: 395 fsread(r); 396 break; 397 case Twrite: 398 fswrite(r); 399 break; 400 case Tstat: 401 fsstat(r); 402 break; 403 case Tflush: 404 fsflush(r); 405 break; 406 default: 407 respond(r, "bug in fsthread"); 408 break; 409 } 410 sendp(creqwait, 0); 411 break; 412 } 413 } 414 } 415 416 static void 417 fsdestroyfid(Fid *fid) 418 { 419 sendp(cclunk, fid); 420 recvp(cclunkwait); 421 } 422 423 static void 424 fssend(Req *r) 425 { 426 sendp(creq, r); 427 recvp(creqwait); /* avoids need to deal with spurious flushes */ 428 } 429 430 void 431 initfs(void) 432 { 433 time0 = time(0); 434 creq = chancreate(sizeof(void*), 0); 435 creqwait = chancreate(sizeof(void*), 0); 436 cclunk = chancreate(sizeof(void*), 0); 437 cclunkwait = chancreate(sizeof(void*), 0); 438 procrfork(fsthread, nil, STACK, RFNAMEG); 439 } 440 441 Srv fs = 442 { 443 .attach= fssend, 444 .destroyfid= fsdestroyfid, 445 .walk1= fswalk1, 446 .open= fssend, 447 .read= fssend, 448 .write= fssend, 449 .stat= fssend, 450 .flush= fssend, 451 }; 452