#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "common.h" #include "sys-queue.h" #include "fd.h" void fd_vlog(const char *fmt, va_list args) { vfprintf(stderr, fmt, args); } void fd_log(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); } int fd_open_logfile(const char *path) { int nullfd, logfd; char *oldpath; /* Redirect stdin and stdout to /dev/null */ nullfd = open("/dev/null", O_RDWR); if (nullfd < 0) { perror("Failed to open /dev/null"); return -1; } dup2(nullfd, 0); dup2(nullfd, 1); /* Attempt to save the old logfile */ if (asprintf(&oldpath, "%s.1", path) > 0) { rename(path, oldpath); free(oldpath); } /* Attempt to redirect stderr to the logfile */ logfd = open(path, O_WRONLY|O_CREAT|O_TRUNC); if (logfd < 0) { fprintf(stderr, "Warning: failed to open logfile '%s': %m\n", path); dup2(nullfd, 2); } else { dup2(logfd, 2); close(logfd); } close(nullfd); return 0; } /* -------------------------------------------------------------------- */ struct listener { LIST_ENTRY(listener) next; int fd; int deleted; fd_handler callback; void *closure; }; LIST_HEAD(listener_list, listener); static struct listener_list listeners; static fd_set fdset, readset; static int maxfd; int fd_set_handler(int fd, fd_handler callback, void *closure) { struct listener *l; if (callback == NULL) { LIST_FOREACH(l, &listeners, next) if (l->fd == fd) { l->deleted = 1; FD_CLR(fd, &fdset); } return 0; } l = calloc(1, sizeof(struct listener)); if (!l) return -1; l->fd = fd; l->callback = callback; l->closure = closure; LIST_INSERT_HEAD(&listeners, l, next); FD_SET(fd, &fdset); maxfd = MAX(maxfd, fd + 1); return 0; } int fd_run_select(void) { struct listener *l; int rc; memcpy(&readset, &fdset, sizeof(fd_set)); rc = select(maxfd, &readset, NULL, NULL, NULL); if (rc < 0) { if (errno == EAGAIN || errno == EINTR) return 0; FD_LOG(0, "select() failed: %m\n"); return -1; } loop: LIST_FOREACH(l, &listeners, next) { if (l->deleted) { LIST_REMOVE(l, next); free(l); goto loop; } if (FD_ISSET(l->fd, &readset)) { l->callback(l->closure); FD_CLR(l->fd, &readset); } } return 0; } void fd_shutdown(void) { struct listener *l; while (!LIST_EMPTY(&listeners)) { l = LIST_FIRST(&listeners); LIST_REMOVE(l, next); free(l); } }