#include #include #include #include #include #include #include #include #include "fd.h" #include "list.h" #include "util.h" list_t fd_all=list_initial(fd_all); fd_t *fd_stdin=NULL, *fd_stdout=NULL, *fd_stderr=NULL; list_t fd_active_stdin=list_initial(fd_active_stdin); list_t fd_active_stdout=list_initial(fd_active_stdout); list_t fd_active_stderr=list_initial(fd_active_stderr); notifier_t *notifier_alloc(void) { return xmalloc(sizeof(notifier_t)); } notifier_t *notifier_init(notifier_t *notifier, void *notify_obj, notify_func_t *notify_func) { notifier->object=notify_obj; notifier->function=notify_func; list_init(¬ifier->list); return notifier; } fd_t *fd_alloc() { return xmalloc(sizeof(fd_t)); } void fd_init(fd_t *fd, int fd_no, int in, int out, const char *name) { fd->fd_no = fd_no; fd->fd_in = in; fd->fd_out = out; fd->fd_flags=fd->fd_fdflags=fd->fd_flagsvalid=0; fd->fd_read_error=fd->fd_write_error=0; if (name) fd->fd_name = xstrdup(name); else snprintf(fd->fd_name=xmalloc(32), 32, "[fd %i]", fd->fd_no); list_init(&fd->fd_all); list_init(&fd->fd_open_readers); list_init(&fd->fd_open_writers); list_init(&fd->fd_notify_readok); list_init(&fd->fd_notify_writeok); list_init(&fd->fd_notify_misc); list_add(&fd_all, &fd->fd_all); } void fd_getflags(fd_t *fd) { int flags, fdflags; if ((flags=fcntl(fd->fd_no, F_GETFL))==-1) die("F_GETFL %s: %s\n", fd->fd_name, strerror(errno)); if ((fdflags=fcntl(fd->fd_no, F_GETFD))==-1) die("F_GETFD %s: %s\n", fd->fd_name, strerror(errno)); fd->fd_flags=flags; fd->fd_fdflags=fdflags; fd->fd_flagsvalid=1; } void fd_set_close_on_exec(fd_t *fd) { if (!fd->fd_flagsvalid) fd_getflags(fd); if (fcntl(fd->fd_no, F_SETFD, fd->fd_fdflags|FD_CLOEXEC)==-1) die("F_SETFD %s FD_CLOEXEC: %s\n", fd->fd_name, strerror(errno)); fd->fd_fdflags|=FD_CLOEXEC; } void fd_set_nonblock(fd_t *fd) { if (!fd->fd_flagsvalid) fd_getflags(fd); if (fcntl(fd->fd_no, F_SETFL, fd->fd_flags|O_NONBLOCK)==-1) die("F_SETFL %s O_NONBLOCK: %s\n", fd->fd_name, strerror(errno)); fd->fd_flags|=O_NONBLOCK; } void fd_dup(fd_t **dst, fd_t *src, const char *name) { int new_fd_no; if ((new_fd_no=dup(src->fd_no))==-1) die("dup %s: %s\n", strerror(errno)); *dst=fd_alloc(); fd_init(*dst, new_fd_no, src->fd_in, src->fd_out, name); } int fd_accept(fd_t **dst, fd_t *listener, struct sockaddr *addr, socklen_t *addrlen, const char *name) { int new_fd_no; if ((new_fd_no=accept(listener->fd_no, addr, addrlen))==-1) { if (errno==EAGAIN || errno==EINTR) return -1; die("accept %s: %s\n", listener->fd_name, strerror(errno)); } *dst=fd_alloc(); fd_init(*dst, new_fd_no, 1, 1, name); return 0; } /* void fd_send_notify(fd_t *fd, void *arg1, int arg2) */ /* { */ /* list_t *item, *head=&fd->fd_notify; */ /* for (item=head->next; item!=head; item=item->next) */ /* { */ /* notifier_t *notifier=(notifier_t *)item; */ /* (*notifier->function)(notifier->object, arg1, arg2); */ /* } */ /* } */ int fd_shutdown(fd_t *fd, int how) { if (shutdown(fd->fd_no, how)) { if (errno==ENOTSOCK || errno==ENOTCONN) return -1; die("shutdown %s (%i): %s\n", fd->fd_name, fd->fd_no, strerror(errno)); } if (how==SHUT_RD || how==SHUT_RDWR) fd->fd_in=0; if (how==SHUT_WR || how==SHUT_RDWR) fd->fd_out=0; return 0; } void fd_close(fd_t *fd) { list_t *item, *head=&fd->fd_notify_misc, *next; int oldfd=fd->fd_no; if (oldfd==-1) return; fd->fd_no=-1; fd->fd_no=-1; fd->fd_in=0; fd->fd_out=0; for (item=head->next; item!=head; item=next) { notifier_t *n=(notifier_t *)item; next=item->next; (*n->function)(n->object, fd, FD_CLOSE); } if (fd->fd_notify_readok.next != &fd->fd_notify_readok) die_hard("Closing %s with readers active\n", fd->fd_name); if (fd->fd_notify_writeok.next != &fd->fd_notify_writeok) die_hard("Closing %s with writers active\n", fd->fd_name); if (close(oldfd)) die("close %s (%i): %s\n", fd->fd_name, oldfd, strerror(errno)); } void fd_add_reader(fd_t *fd, list_t *user) { list_add(&fd->fd_open_readers, user); } void fd_add_writer(fd_t *fd, list_t *user) { list_add(&fd->fd_open_writers, user); } void fd_del_reader(fd_t *fd, list_t *user) { list_del(user); if (fd->fd_open_readers.next==&fd->fd_open_readers) { if (fd->fd_no!=-1) { fd_shutdown(fd, SHUT_RD); if (fd->fd_open_writers.next==&fd->fd_open_writers) fd_close(fd); } } } void fd_del_writer(fd_t *fd, list_t *user) { list_del(user); if (fd->fd_open_writers.next==&fd->fd_open_writers) { if (fd->fd_no!=-1) { fd_shutdown(fd, SHUT_WR); if (fd->fd_open_readers.next==&fd->fd_open_readers) fd_close(fd); } } } void fd_request_read(fd_t *fd, notifier_t *notifier) { if (!fd->fd_in) die_hard("read from %s requested (read not permitted)\n", fd->fd_name); list_add(&fd->fd_notify_readok, ¬ifier->list); } void fd_request_write(fd_t *fd, notifier_t *notifier) { if (!fd->fd_out) die_hard("write to %s requested (write not permitted)\n", fd->fd_name); list_add(&fd->fd_notify_writeok, ¬ifier->list); } void fd_request_misc(fd_t *fd, notifier_t *notifier) { list_add(&fd->fd_notify_misc, ¬ifier->list); } void fd_unrequest_read(notifier_t *notifier) { list_del(¬ifier->list); } void fd_unrequest_write(notifier_t *notifier) { list_del(¬ifier->list); } void fd_unrequest_misc(notifier_t *notifier) { list_del(¬ifier->list); } void fd_preselect(fd_t *fd, int *maxfd, fd_set *rfds, fd_set *wfds) { int read_active=fd->fd_open_readers.next!=&fd->fd_open_readers; int write_active=fd->fd_open_writers.next!=&fd->fd_open_writers; if (fd->fd_notify_readok.next != &fd->fd_notify_readok && !fd->fd_read_error) { if (!read_active) die_hard("BUG! %s has an active reader " "but open_readers is empty\n"); FD_SET(fd->fd_no, rfds); if (*maxfd < fd->fd_no) *maxfd = fd->fd_no; } if (fd->fd_notify_writeok.next != &fd->fd_notify_writeok && !fd->fd_write_error) { if (!write_active) die_hard("BUG! %s has an active writer " "but open_writers is empty\n"); FD_SET(fd->fd_no, wfds); if (*maxfd < fd->fd_no) *maxfd = fd->fd_no; } if (!read_active && !write_active) { fd_close(fd); list_del(&fd->fd_all); free(fd); } } void fd_postselect(fd_t *fd, fd_set *rfds, fd_set *wfds) { if (fd->fd_notify_readok.next != &fd->fd_notify_readok && FD_ISSET(fd->fd_no, rfds)) { notifier_t *n=(notifier_t *)(fd->fd_notify_readok.next); (*n->function)(n->object, fd, 0); } if (fd->fd_notify_writeok.next != &fd->fd_notify_writeok && FD_ISSET(fd->fd_no, wfds)) { list_t *item, *head=&fd->fd_notify_writeok, *next; for (item=head->next; item!=head; item=next) { notifier_t *n=(notifier_t *)item; next=item->next; (*n->function)(n->object, fd, 0); } } } void fd_all_preselect(int *maxfd, fd_set *rfds, fd_set *wfds) { list_t *item, *head=&fd_all, *next; for (item=head->next; item!=head; item=next) { fd_t *fd=list_narrow(item, fd_t, fd_all); next=item->next; fd_preselect(fd, maxfd, rfds, wfds); } } void fd_all_postselect(fd_set *rfds, fd_set *wfds) { list_t *item, *head=&fd_all, *next; for (item=head->next; item!=head; item=next) { fd_t *fd=list_narrow(item, fd_t, fd_all); next=item->next; fd_postselect(fd, rfds, wfds); } } ssize_t fd_read(fd_t *fd, void *buf, size_t len) { ssize_t ret=read(fd->fd_no, buf, len); if (ret==-1 && errno!=EAGAIN && errno!=EINTR) { fd->fd_read_error=errno; if (errno==EBADF && !fd->fd_write_error) fd->fd_write_error=errno; } return ret; } ssize_t fd_readv(fd_t *fd, const struct iovec *vec, size_t veclen) { ssize_t ret=readv(fd->fd_no, vec, veclen); if (ret==-1 && errno!=EAGAIN && errno!=EINTR) { fd->fd_read_error=errno; if (errno==EBADF && !fd->fd_write_error) fd->fd_write_error=errno; } return ret; } ssize_t fd_write(fd_t *fd, void *buf, size_t len) { ssize_t ret=write(fd->fd_no, buf, len); if (ret==-1 && errno!=EAGAIN && errno!=EINTR) { fd->fd_write_error=errno; if (errno==EBADF && !fd->fd_read_error) fd->fd_read_error=errno; } return ret; } ssize_t fd_writev(fd_t *fd, const struct iovec *vec, size_t veclen) { ssize_t ret=writev(fd->fd_no, vec, veclen); if (ret==-1 && errno!=EAGAIN && errno!=EINTR) { fd->fd_write_error=errno; if (errno==EBADF && !fd->fd_read_error) fd->fd_read_error=errno; } return ret; } void fd_prog_init(void) { fd_stdin=fd_alloc(); fd_stdout=fd_alloc(); fd_stderr=fd_alloc(); fd_init(fd_stdin, STDIN_FILENO, 1, 0, "stdin"); fd_init(fd_stdout, STDOUT_FILENO, 0, 1, "stdout"); fd_init(fd_stderr, STDERR_FILENO, 0, 1, "stderr"); fd_getflags(fd_stdin); fd_getflags(fd_stdout); fd_getflags(fd_stderr); fd_add_reader(fd_stdin, &fd_active_stdin); fd_add_writer(fd_stdout, &fd_active_stdout); fd_add_writer(fd_stderr, &fd_active_stderr); }