#include "stdinc.h" #include "9h.h" typedef struct Srv Srv; struct Srv { int fd; int srvfd; char* service; char* mntpnt; Srv* next; Srv* prev; }; static struct { VtLock* lock; Srv* head; Srv* tail; } sbox; static int srvFd(char* name, int mode, int fd, char** mntpnt) { int n, srvfd; char *p, buf[10]; /* * Drop a file descriptor with given name and mode into /srv. * Create with ORCLOSE and don't close srvfd so it will be removed * automatically on process exit. */ p = smprint("/srv/%s", name); if((srvfd = create(p, ORCLOSE|OWRITE, mode)) < 0){ vtMemFree(p); p = smprint("#s/%s", name); if((srvfd = create(p, ORCLOSE|OWRITE, mode)) < 0){ vtSetError("create %s: %r", p); vtMemFree(p); return -1; } } n = snprint(buf, sizeof(buf), "%d", fd); if(write(srvfd, buf, n) < 0){ close(srvfd); vtSetError("write %s: %r", p); vtMemFree(p); return -1; } *mntpnt = p; return srvfd; } static void srvFree(Srv* srv) { if(srv->prev != nil) srv->prev->next = srv->next; else sbox.head = srv->next; if(srv->next != nil) srv->next->prev = srv->prev; else sbox.tail = srv->prev; if(srv->srvfd != -1) close(srv->srvfd); vtMemFree(srv->service); vtMemFree(srv->mntpnt); vtMemFree(srv); } static Srv* srvAlloc(char* service, int mode, int fd) { Dir *dir; Srv *srv; int srvfd; char *mntpnt; vtLock(sbox.lock); for(srv = sbox.head; srv != nil; srv = srv->next){ if(strcmp(srv->service, service) != 0) continue; /* * If the service exists, but is stale, * free it up and let the name be reused. */ if((dir = dirfstat(srv->srvfd)) != nil){ free(dir); vtSetError("srv: already serving '%s'", service); vtUnlock(sbox.lock); return nil; } srvFree(srv); break; } if((srvfd = srvFd(service, mode, fd, &mntpnt)) < 0){ vtUnlock(sbox.lock); return nil; } close(fd); srv = vtMemAllocZ(sizeof(Srv)); srv->srvfd = srvfd; srv->service = vtStrDup(service); srv->mntpnt = mntpnt; if(sbox.tail != nil){ srv->prev = sbox.tail; sbox.tail->next = srv; } else{ sbox.head = srv; srv->prev = nil; } sbox.tail = srv; vtUnlock(sbox.lock); return srv; } static int cmdSrv(int argc, char* argv[]) { Con *con; Srv *srv; char *usage = "usage: srv [-APWdp] [service]"; int conflags, dflag, fd[2], mode, pflag, r; dflag = 0; pflag = 0; conflags = 0; mode = 0666; ARGBEGIN{ default: return cliError(usage); case 'A': conflags |= ConNoAuthCheck; break; case 'I': conflags |= ConIPCheck; break; case 'N': conflags |= ConNoneAllow; break; case 'P': conflags |= ConNoPermCheck; mode = 0600; break; case 'W': conflags |= ConWstatAllow; mode = 0600; break; case 'd': dflag = 1; break; case 'p': pflag = 1; mode = 0600; break; }ARGEND if(pflag && (conflags&ConNoPermCheck)){ vtSetError("srv: cannot use -P with -p"); return 0; } switch(argc){ default: return cliError(usage); case 0: vtRLock(sbox.lock); for(srv = sbox.head; srv != nil; srv = srv->next) consPrint("\t%s\t%d\n", srv->service, srv->srvfd); vtRUnlock(sbox.lock); return 1; case 1: if(!dflag) break; vtLock(sbox.lock); for(srv = sbox.head; srv != nil; srv = srv->next){ if(strcmp(srv->service, argv[0]) != 0) continue; srvFree(srv); break; } vtUnlock(sbox.lock); if(srv == nil){ vtSetError("srv: '%s' not found", argv[0]); return 0; } return 1; } if(pipe(fd) < 0){ vtSetError("srv pipe: %r"); return 0; } if((srv = srvAlloc(argv[0], mode, fd[0])) == nil){ close(fd[0]); close(fd[1]); return 0; } if(pflag) r = consOpen(fd[1], srv->srvfd, -1); else{ con = conAlloc(fd[1], srv->mntpnt, conflags); if(con == nil) r = 0; else r = 1; } if(r == 0){ close(fd[1]); vtLock(sbox.lock); srvFree(srv); vtUnlock(sbox.lock); } return r; } int srvInit(void) { sbox.lock = vtLockAlloc(); cliAddCmd("srv", cmdSrv); return 1; }