/* This file is in the public domain. */ #include #include #include #include #include #include #include extern const char *__progname; #include "util.h" #include "netfd.h" #include "config.h" #include "structs.h" #include "pollloop.h" typedef struct listen_local_priv LISTEN_LOCAL_PRIV; struct listen_local_priv { LISTEN *l; char *path; int fd; int ioid; LISTEN_LOCAL_PRIV *xfer; } ; extern const LEP_OPS lep_ops_local; static int lep_local_add(CONFIG *cfg, LISTEN *l, const char *key, int keylen, const char *rest) { LEP *lep; LISTEN_LOCAL_PRIV *llp; if ((keylen != 5) || bcmp(key,"local",5)) return(0); if (! *rest) config_err(cfg,"missing path on `local' listen line"); llp = malloc(sizeof(LISTEN_LOCAL_PRIV)); llp->l = l; llp->path = strdup(rest); llp->fd = -1; llp->ioid = PL_NOID; lep = malloc(sizeof(LEP)); lep->ops = &lep_ops_local; lep->priv = llp; lep->link = l->endpoints; l->endpoints = lep; return(1); } static void lep_local_free(void *pv) { LISTEN_LOCAL_PRIV *p; p = pv; free(p->path); if (p->fd >= 0) close(p->fd); if (p->ioid != PL_NOID) remove_poll_id(p->ioid); free(p); } static void lep_local_pre_transfer(void *pv) { ((LISTEN_LOCAL_PRIV *)pv)->xfer = 0; } static void lep_local_setup_transfer(void *fv, void *tv) { LISTEN_LOCAL_PRIV *f; LISTEN_LOCAL_PRIV *t; f = fv; t = tv; if (f->xfer || t->xfer) return; if (! strcmp(f->path,t->path)) { f->xfer = t; t->xfer = f; } } static void lep_local_do_transfer(void *pv) { LISTEN_LOCAL_PRIV *p; p = pv; if (! p->xfer) return; if (p->xfer->xfer != p) abort(); p->fd = p->xfer->fd; p->xfer->fd = -1; p->xfer->xfer = 0; p->xfer = 0; } static void lep_local_no_transfer(void *pv) { ((LISTEN_LOCAL_PRIV *)pv)->xfer = 0; } static int lep_local_openfds(void *pv) { LISTEN_LOCAL_PRIV *p; struct sockaddr_un *s_un; int len; p = pv; if (p->xfer) return(0); if (p->fd >= 0) abort(); s_un = 0; do <"err"> { p->fd = socket(AF_LOCAL,SOCK_STREAM,0); if (p->fd < 0) { fprintf(stderr,"%s: %s: socket: %s\n",__progname,p->path,strerror(errno)); break <"err">; } len = strlen(p->path); s_un = malloc(sizeof(*s_un)-sizeof(s_un->sun_path)+len+1); s_un->sun_family = AF_LOCAL; s_un->sun_len = sizeof(*s_un) - sizeof(s_un->sun_path) + len; bcopy(p->path,&s_un->sun_path[0],len); do <"bound"> { if (bind(p->fd,(void *)s_un,s_un->sun_len) >= 0) break <"bound">; if (errno == EADDRINUSE) { struct stat stb; if ( (stat(p->path,&stb) >= 0) && ((stb.st_mode & S_IFMT) == S_IFSOCK) ) { unlink(p->path); if (bind(p->fd,(void *)s_un,s_un->sun_len) >= 0) break <"bound">; } errno = EEXIST; } fprintf(stderr,"%s: %s: bind: %s\n",__progname,p->path,strerror(errno)); break <"err">; } while (0); if (listen(p->fd,25) < 0) { fprintf(stderr,"%s: %s: listen: %s\n",__progname,p->path,strerror(errno)); break <"err">; } return(0); } while (0); if (p->fd >= 0) { close(p->fd); p->fd = -1; } if (s_un) free(s_un); return(1); } static void accept_local(void *pv) { LISTEN_LOCAL_PRIV *p; int new; struct sockaddr_un s_un; socklen_t sunlen; p = pv; sunlen = sizeof(s_un); new = accept(p->fd,(void *)&s_un,&sunlen); if (new < 0) { switch (errno) { case EINTR: case NONBLOCKING: return; break; } fprintf(stderr,"%s: %s: accept: %s\n",__progname,p->path,strerror(errno)); return; } netfd_new(new,p->l,strdup(p->path)); } static void lep_local_setup_poll(void *pv) { LISTEN_LOCAL_PRIV *p; p = pv; set_nonblock(p->fd); p->ioid = add_poll_fd(p->fd,&rwtest_always,&rwtest_never,&accept_local,0,p); } const LEP_OPS lep_ops_local = LEP_OPS_INIT(local);