static void control_close(struct ctl_conn *c) { TAILQ_REMOVE(&ctl_conns, c, entry); event_del(&c->iev.ev); close(c->iev.ibuf.fd); imsg_clear(&c->iev.ibuf); free(c); stat_backend->decrement("control.session", 1); if (available_fds(CONTROL_FD_RESERVE)) return; if (!event_pending(&control_state.ev, EV_READ, NULL)) { log_warnx("re-enabling ctl connections"); event_add(&control_state.ev, NULL); } }
/* ARGSUSED */ static void control_accept(int listenfd, short event, void *arg) { int connfd; socklen_t len; struct sockaddr_un sun; struct ctl_conn *c; if (available_fds(CONTROL_FD_RESERVE)) goto pause; len = sizeof(sun); if ((connfd = accept(listenfd, (struct sockaddr *)&sun, &len)) == -1) { if (errno == ENFILE || errno == EMFILE) goto pause; if (errno == EINTR || errno == ECONNABORTED) return; fatal("control_accept: accept"); } session_socket_blockmode(connfd, BM_NONBLOCK); c = xcalloc(1, sizeof(*c), "control_accept"); imsg_init(&c->iev.ibuf, connfd); c->iev.handler = control_dispatch_ext; c->iev.events = EV_READ; event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, c->iev.handler, NULL); event_add(&c->iev.ev, NULL); TAILQ_INSERT_TAIL(&ctl_conns, c, entry); stat_backend->increment("control.session", 1); return; pause: log_warnx("ctl client limit hit, disabling new connections"); event_del(&control_state.ev); }
ssize_t imsg_read(struct imsgbuf *ibuf) { struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int) * 1)]; } cmsgbuf; struct iovec iov; ssize_t n = -1; int fd; struct imsg_fd *ifd; bzero(&msg, sizeof(msg)); iov.iov_base = ibuf->r.buf + ibuf->r.wpos; iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) return (-1); again: if (available_fds(imsg_fd_overhead + (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))) { errno = EAGAIN; return (-1); } if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { if (errno == EMSGSIZE) goto fail; if (errno != EINTR && errno != EAGAIN) goto fail; goto again; } ibuf->r.wpos += n; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { int i; int j; /* * We only accept one file descriptor. Due to C * padding rules, our control buffer might contain * more than one fd, and we must close them. */ j = ((char *)cmsg + cmsg->cmsg_len - (char *)CMSG_DATA(cmsg)) / sizeof(int); for (i = 0; i < j; i++) { fd = ((int *)CMSG_DATA(cmsg))[i]; if (ifd != NULL) { ifd->fd = fd; TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry); ifd = NULL; } else close(fd); } } /* we do not handle other ctl data level */ } fail: if (ifd) free(ifd); return (n); }