| #include "stdinc.h" |
| |
| #include "9.h" |
| |
| typedef struct Lstn Lstn; |
| struct Lstn { |
| int afd; |
| int flags; |
| char* address; |
| char dir[NETPATHLEN]; |
| |
| Lstn* next; |
| Lstn* prev; |
| }; |
| |
| static struct { |
| RWLock lock; |
| |
| Lstn* head; |
| Lstn* tail; |
| } lbox; |
| |
| static void |
| lstnFree(Lstn* lstn) |
| { |
| wlock(&lbox.lock); |
| if(lstn->prev != nil) |
| lstn->prev->next = lstn->next; |
| else |
| lbox.head = lstn->next; |
| if(lstn->next != nil) |
| lstn->next->prev = lstn->prev; |
| else |
| lbox.tail = lstn->prev; |
| wunlock(&lbox.lock); |
| |
| if(lstn->afd != -1) |
| close(lstn->afd); |
| vtfree(lstn->address); |
| vtfree(lstn); |
| } |
| |
| static void |
| lstnListen(void* a) |
| { |
| Lstn *lstn; |
| int dfd, lfd; |
| char newdir[NETPATHLEN]; |
| |
| threadsetname("listen"); |
| |
| lstn = a; |
| for(;;){ |
| if((lfd = listen(lstn->dir, newdir)) < 0){ |
| fprint(2, "listen: listen '%s': %r", lstn->dir); |
| break; |
| } |
| if((dfd = accept(lfd, newdir)) >= 0) |
| conAlloc(dfd, newdir, lstn->flags); |
| else |
| fprint(2, "listen: accept %s: %r\n", newdir); |
| close(lfd); |
| } |
| lstnFree(lstn); |
| } |
| |
| static Lstn* |
| lstnAlloc(char* address, int flags) |
| { |
| int afd; |
| Lstn *lstn; |
| char dir[NETPATHLEN]; |
| |
| wlock(&lbox.lock); |
| for(lstn = lbox.head; lstn != nil; lstn = lstn->next){ |
| if(strcmp(lstn->address, address) != 0) |
| continue; |
| werrstr("listen: already serving '%s'", address); |
| wunlock(&lbox.lock); |
| return nil; |
| } |
| |
| if((afd = announce(address, dir)) < 0){ |
| werrstr("listen: announce '%s': %r", address); |
| wunlock(&lbox.lock); |
| return nil; |
| } |
| |
| lstn = vtmallocz(sizeof(Lstn)); |
| lstn->afd = afd; |
| lstn->address = vtstrdup(address); |
| lstn->flags = flags; |
| memmove(lstn->dir, dir, NETPATHLEN); |
| |
| if(lbox.tail != nil){ |
| lstn->prev = lbox.tail; |
| lbox.tail->next = lstn; |
| } |
| else{ |
| lbox.head = lstn; |
| lstn->prev = nil; |
| } |
| lbox.tail = lstn; |
| wunlock(&lbox.lock); |
| |
| if(proccreate(lstnListen, lstn, STACK) < 0){ |
| werrstr("listen: thread '%s': %r", lstn->address); |
| lstnFree(lstn); |
| return nil; |
| } |
| |
| return lstn; |
| } |
| |
| static int |
| cmdLstn(int argc, char* argv[]) |
| { |
| int dflag, flags; |
| Lstn *lstn; |
| char *usage = "usage: listen [-dIN] [address]"; |
| |
| dflag = 0; |
| flags = 0; |
| ARGBEGIN{ |
| default: |
| return cliError(usage); |
| case 'd': |
| dflag = 1; |
| break; |
| case 'I': |
| flags |= ConIPCheck; |
| break; |
| case 'N': |
| flags |= ConNoneAllow; |
| break; |
| }ARGEND |
| |
| switch(argc){ |
| default: |
| return cliError(usage); |
| case 0: |
| rlock(&lbox.lock); |
| for(lstn = lbox.head; lstn != nil; lstn = lstn->next) |
| consPrint("\t%s\t%s\n", lstn->address, lstn->dir); |
| runlock(&lbox.lock); |
| break; |
| case 1: |
| if(!dflag){ |
| if(lstnAlloc(argv[0], flags) == nil) |
| return 0; |
| break; |
| } |
| |
| wlock(&lbox.lock); |
| for(lstn = lbox.head; lstn != nil; lstn = lstn->next){ |
| if(strcmp(lstn->address, argv[0]) != 0) |
| continue; |
| if(lstn->afd != -1){ |
| close(lstn->afd); |
| lstn->afd = -1; |
| } |
| break; |
| } |
| wunlock(&lbox.lock); |
| |
| if(lstn == nil){ |
| werrstr("listen: '%s' not found", argv[0]); |
| return 0; |
| } |
| break; |
| } |
| |
| return 1; |
| } |
| |
| int |
| lstnInit(void) |
| { |
| cliAddCmd("listen", cmdLstn); |
| |
| return 1; |
| } |