int main(int argc, char *argv[]) { errval_t err; printf("Child. will print out file descriptors\n"); /* Inherit all the FDs sent over */ err = get_inherited_fds(); if (err_is_fail(err)) { DEBUG_ERR(err, "could not get inherited FDs\n"); return EXIT_FAILURE; } /* print out all of our FDs */ int i; struct fdtab_entry *fde; for (i = MIN_FD; i < MAX_FD; i++) { fde = fdtab_get(i); if (fde->type != FDTAB_TYPE_AVAILABLE) { printf("fd[%d]: type: %d, handle: %p\n", i, fde->type, fde->handle); } } return EXIT_SUCCESS; }
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { struct fdtab_entry *e = fdtab_get(sockfd); ssize_t ret = 0; switch(e->type) { case FDTAB_TYPE_UNIX_SOCKET: assert(!"NYI"); return -1; break; case FDTAB_TYPE_LWIP_SOCKET: lwip_mutex_lock(); ret = lwip_sendto(e->fd, buf, len, flags, dest_addr, addrlen); lwip_mutex_unlock(); break; case FDTAB_TYPE_AVAILABLE: errno = EBADF; ret = -1; break; default: errno = ENOTSOCK; ret = -1; break; } return ret; }
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) { struct fdtab_entry *e = fdtab_get(sockfd); switch(e->type) { case FDTAB_TYPE_UNIX_SOCKET: assert(!"NYI"); return -1; break; case FDTAB_TYPE_LWIP_SOCKET: lwip_mutex_lock(); ssize_t ret = lwip_recvfrom(e->fd, buf, len, flags, src_addr, addrlen); lwip_mutex_unlock(); return ret; case FDTAB_TYPE_AVAILABLE: errno = EBADF; return -1; default: errno = ENOTSOCK; return -1; } }
int close(int fd) { int ret; struct fdtab_entry *e = fdtab_get(fd); if (e->type == FDTAB_TYPE_AVAILABLE) { return -1; } if (e->type == FDTAB_TYPE_LWIP_SOCKET) { if(e->inherited) { // Perform shallow close on lwip so that it will not terminate // the TCP session printf("close: Inherited socket, not closing completely\n"); ret = 0; } else { ret = lwip_close(e->fd); if(ret < 0) { POSIXCOMPAT_DEBUG("[%d]error in lwip_close\n", disp_get_domain_id()); return -1; } } fdtab_free(fd); } else { ret = vfsfd_close(fd); } return ret; }
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { struct fdtab_entry *e = fdtab_get(sockfd); ssize_t ret = 0; switch(e->type) { case FDTAB_TYPE_UNIX_SOCKET: assert(!"NYI"); return -1; break; case FDTAB_TYPE_LWIP_SOCKET: assert(msg != NULL); assert(msg->msg_control == NULL); assert(msg->msg_controllen == 0); #if 0 // XXX: Copy all buffers into one. Should instead have an lwIP interface for this. size_t totalsize = 0; for(int i = 0; i < msg->msg_iovlen; i++) { totalsize += msg->msg_iov[i].iov_len; } char *buf = malloc(totalsize); size_t pos = 0; for(int i = 0; i < msg->msg_iovlen; i++) { memcpy(&buf[pos], msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len); pos += msg->msg_iov[i].iov_len; } lwip_mutex_lock(); ret = lwip_sendto(e->fd, buf, totalsize, flags, msg->msg_name, msg->msg_namelen); lwip_mutex_unlock(); free(buf); #else lwip_mutex_lock(); ret = lwip_sendmsg(e->fd, msg, flags); lwip_mutex_unlock(); #endif break; case FDTAB_TYPE_AVAILABLE: errno = EBADF; ret = -1; break; default: errno = ENOTSOCK; ret = -1; break; } return ret; }
off_t vfsfd_lseek(int fd, off_t offset, int whence) { struct fdtab_entry *e = fdtab_get(fd); switch(e->type) { case FDTAB_TYPE_FILE: { enum vfs_seekpos vfs_whence; errval_t err; size_t retpos; switch(whence) { case SEEK_SET: vfs_whence = VFS_SEEK_SET; break; case SEEK_CUR: vfs_whence = VFS_SEEK_CUR; break; case SEEK_END: vfs_whence = VFS_SEEK_END; break; default: return -1; } err = vfs_seek((vfs_handle_t)e->handle, vfs_whence, offset); if(err_is_fail(err)) { DEBUG_ERR(err, "vfs_seek"); return -1; } err = vfs_tell((vfs_handle_t)e->handle, &retpos); if(err_is_fail(err)) { DEBUG_ERR(err, "vfs_tell"); return -1; } VFSFD_DEBUG("lseek(%d, %lld, %d) = %lu\n", fd, offset, whence, retpos); return retpos; } break; default: return -1; } }
int ioctl(int fd, unsigned long request, ...) { va_list arg; va_start(arg, request); struct fdtab_entry *e = fdtab_get(fd); switch (e->type) { case FDTAB_TYPE_AVAILABLE: { errno = EBADF; return -1; } case FDTAB_TYPE_PTS: { struct _pty *state = e->handle; switch (request) { case TIOCGWINSZ: /* get window size */ { struct winsize *w = va_arg(arg, struct winsize *); memcpy(w, &state->winsize, sizeof(struct winsize)); return 0; } case TIOCSWINSZ: /* set window size */ { struct winsize *w = va_arg(arg, struct winsize *); memcpy(&state->winsize, w, sizeof(struct winsize)); return 0; } default: assert(!"NYI"); errno = EINVAL; return -1; } } default: { assert("NYI"); return -1; } } va_end(arg); }
int close(int fd) { int ret; struct fdtab_entry *e = fdtab_get(fd); if (e->type == FDTAB_TYPE_AVAILABLE) { return -1; } // Might need to remove from epoll list if(e->epoll_fd != -1) { ret = epoll_ctl(e->epoll_fd, EPOLL_CTL_DEL, fd, NULL); assert(ret == 0); } switch(e->type) { case FDTAB_TYPE_LWIP_SOCKET: if (e->inherited) { // Perform shallow close on lwip so that it will not terminate // the TCP session printf("close: Inherited socket, not closing completely\n"); ret = 0; } else { ret = lwip_close(e->fd); if(ret < 0) { POSIXCOMPAT_DEBUG("[%d]error in lwip_close\n", disp_get_domain_id()); return -1; } } fdtab_free(fd); break; case FDTAB_TYPE_PTM: ret = ptm_close(fd); break; case FDTAB_TYPE_PTS: ret = pts_close(fd); break; default: ret = vfsfd_close(fd); } return ret; }
int listen(int sockfd, int backlog) { struct fdtab_entry *e = fdtab_get(sockfd); switch(e->type) { case FDTAB_TYPE_UNIX_SOCKET: POSIXCOMPAT_DEBUG("listen(%d, %d)\n", sockfd, backlog); struct _unix_socket *us = e->handle; errval_t err = unixsock_export(us, unixsock_listening, unixsock_connected, get_default_waitset(), IDC_EXPORT_FLAGS_DEFAULT); if(err_is_fail(err)) { DEBUG_ERR(err, "unixsock_export failed"); return -1; } while(us->u.passive.listen_iref == NULL_IREF) { // XXX: Should wait only on monitor event_dispatch(get_default_waitset()); } us->passive = true; us->u.passive.max_backlog = backlog; us->u.passive.backlog = calloc(backlog, sizeof(struct unixsock_binding *)); char str[128]; snprintf(str, 128, "%"PRIuIREF, us->u.passive.listen_iref); err = vfs_write(us->vfs_handle, str, strlen(str), NULL); if(err_is_fail(err)) { USER_PANIC_ERR(err, "vfs_write"); } break; case FDTAB_TYPE_LWIP_SOCKET: lwip_mutex_lock(); int ret = lwip_listen(e->fd, backlog); lwip_mutex_unlock(); return ret; default: return -1; } return 0; }
int fsync(int fd) { struct fdtab_entry *e = fdtab_get(fd); switch(e->type) { case FDTAB_TYPE_FILE: { errval_t err = vfs_flush((vfs_handle_t)e->handle); if(err_is_fail(err)) { return -1; } } break; default: errno = EINVAL; return -1; } return 0; }
/** * \brief Find the pathname of a terminal. */ char *ttyname(int fd) { struct fdtab_entry *e = fdtab_get(fd); switch(e->type) { case FDTAB_TYPE_AVAILABLE: { errno = EBADF; return NULL; } case FDTAB_TYPE_PTS: { struct _pty *pty = e->handle; return pty->ptsname; } case FDTAB_TYPE_PTM: case FDTAB_TYPE_STDIN: case FDTAB_TYPE_STDOUT: case FDTAB_TYPE_STDERR: { /* * We do not create a file in the filesystem for these types of * file descriptors. */ assert(!"NYI"); return NULL; } default: { errno = ENOTTY; return 0; } } }
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { struct fdtab_entry *e = fdtab_get(sockfd); switch(e->type) { case FDTAB_TYPE_UNIX_SOCKET: assert(addrlen >= sizeof(struct sockaddr_un)); assert(addr->sa_family == AF_UNIX); struct _unix_socket *us = e->handle; memcpy(&us->sockaddr, addr, sizeof(struct sockaddr_un)); POSIXCOMPAT_DEBUG("bind(%d, %p (%s), %u)\n", sockfd, addr, us->sockaddr.sun_path, addrlen); // XXX: Should fail if file already exists // Create socket file errval_t err = vfs_create(us->sockaddr.sun_path, &us->vfs_handle); if(err_is_fail(err)) { DEBUG_ERR(err, "vfs_create"); return -1; } break; case FDTAB_TYPE_LWIP_SOCKET: lwip_mutex_lock(); int ret = lwip_bind(e->fd, addr, addrlen); lwip_mutex_unlock(); return ret; default: return -1; } return 0; }
static errval_t get_inherited_fds(void) { errval_t err; /* Map the FD buffer into our address space. * It stays there since the FD data structures will remain in there and be * referenced from the FD table. */ struct capref frame = { .cnode = cnode_task, .slot = TASKCN_SLOT_FDSPAGE, }; void *fdspg; err = vspace_map_one_frame(&fdspg, FDS_SIZE, frame, NULL, NULL); if (err_is_fail(err)) { return err_push(err, SPAWN_ERR_MAP_FDSPG_TO_SELF); } /* Set up to read the table */ char *p = fdspg; printf("fds at: %p\n", p); int num_fds = *((int*)p); printf("num fds: %d\n", num_fds); struct fd_store *fd; p += sizeof(int); fd = (struct fd_store*)p; p += (sizeof(struct fd_store)*num_fds); /* Process all the FDs passed in the buffer */ int i; for (i = 0; i < num_fds; i++, fd++) { /* add each to our fd table - replacing any fds already there */ struct fdtab_entry fde; fde.type = fd->type; fde.handle = fd->handle; if (fdtab_get(fd->num)->type != FDTAB_TYPE_AVAILABLE) { fdtab_free(fd->num); } fdtab_alloc_from(&fde, fd->num); /* print out some info about the FD */ char *s = ""; switch (fd->type) { case FDTAB_TYPE_AVAILABLE: s = "available"; break; case FDTAB_TYPE_FILE: s = "file"; break; case FDTAB_TYPE_UNIX_SOCKET: s = "unix socket"; break; case FDTAB_TYPE_STDIN: s = "stdin"; break; case FDTAB_TYPE_STDOUT: s = "stdout"; break; case FDTAB_TYPE_STDERR: s = "stderr"; break; case FDTAB_TYPE_LWIP_SOCKET: s = "lwip socket"; break; case FDTAB_TYPE_EPOLL_INSTANCE: s = "epoll instance"; break; case FDTAB_TYPE_PTM: s = "pseudo-terminal master"; break; case FDTAB_TYPE_PTS: s = "pseudo-terminal slave"; break; } printf("fd_store %d: num: %d, type: %d:%s handle: %p\n", i, fd->num, fd->type, s, fd->handle); switch (fd->type) { case FDTAB_TYPE_FILE: print_file_fd((void*)(p + (genpaddr_t)fd->handle)); break; case FDTAB_TYPE_UNIX_SOCKET: print_unixsock_fd((void*)(p + (genpaddr_t)fd->handle)); break; default: printf("[no handle data]\n"); break; } } return SYS_ERR_OK; }
//XXX: flags are ignored... int vfsfd_open(const char *pathname, int flags) { vfs_handle_t vh; errval_t err; char *path = vfs_path_mkabs(pathname); assert(path != NULL); // If O_CREAT was given, we use vfs_create() if(flags & O_CREAT) { // If O_EXCL was also given, we check whether we can open() first if(flags & O_EXCL) { err = vfs_open(path, &vh); if(err_is_ok(err)) { vfs_close(vh); errno = EEXIST; return -1; } assert(err_no(err) == FS_ERR_NOTFOUND); } err = vfs_create(path, &vh); } else { // Regular open() err = vfs_open(path, &vh); } free(path); if (err_is_fail(err)) { VFSFD_DEBUG("open('%s') failed\n", pathname); switch(err_no(err)) { case FS_ERR_NOTFOUND: errno = ENOENT; break; default: #ifdef VFSFD_DEBUG_ENABLED DEBUG_ERR(err, "vfs_open"); #endif break; } return -1; } struct fdtab_entry e = { .type = FDTAB_TYPE_FILE, .handle = vh, .epoll_fd = -1, }; int fd = fdtab_alloc(&e); VFSFD_DEBUG("open(%s) as fd %d\n", pathname, fd); if (fd < 0) { vfs_close(vh); return -1; } else { return fd; } } int vfsfd_read(int fd, void *buf, size_t len) { struct fdtab_entry *e = fdtab_get(fd); size_t retlen = 0; switch(e->type) { case FDTAB_TYPE_FILE: { errval_t err = vfs_read((vfs_handle_t)e->handle, buf, len, &retlen); VFSFD_DEBUG("%d : read(%d, %d) = %lu\n", disp_get_domain_id(), fd, len, retlen); if (err_is_fail(err)) { DEBUG_ERR(err, "error in vfs_read"); return -1; } } break; case FDTAB_TYPE_STDIN: retlen = terminal_read((char *)buf, len); break; case FDTAB_TYPE_STDOUT: case FDTAB_TYPE_STDERR: case FDTAB_TYPE_AVAILABLE: default: return -1; } return retlen; } int vfsfd_write(int fd, const void *buf, size_t len) { struct fdtab_entry *e = fdtab_get(fd); if (e->type == FDTAB_TYPE_AVAILABLE) { return -1; } size_t retlen = 0; switch(e->type) { case FDTAB_TYPE_FILE: { errval_t err = vfs_write((vfs_handle_t)e->handle, buf, len, &retlen); VFSFD_DEBUG("write(%d, %d) = %lu\n", fd, len, retlen); if (err_is_fail(err)) { DEBUG_ERR(err, "error in vfs_write"); return -1; } } break; case FDTAB_TYPE_STDOUT: /* only support writting to terminal */ retlen = terminal_write((const char*) buf, len); break; case FDTAB_TYPE_STDERR: retlen = terminal_write((const char*) buf, len); break; case FDTAB_TYPE_STDIN: case FDTAB_TYPE_AVAILABLE: default: return -1; } return retlen; } int vfsfd_close(int fd) { errval_t err; struct fdtab_entry *e = fdtab_get(fd); if (e->type == FDTAB_TYPE_AVAILABLE) { return -1; } VFSFD_DEBUG("close(%d)\n", fd); switch(e->type) { case FDTAB_TYPE_FILE: err = vfs_close((vfs_handle_t)e->handle); if (err_is_fail(err)) { DEBUG_ERR(err, "error in vfs_close"); return -1; } break; case FDTAB_TYPE_STDIN: case FDTAB_TYPE_STDOUT: case FDTAB_TYPE_STDERR: // XXX: Should call fclose() when closing last FD break; default: return -1; } // end switch fdtab_free(fd); return 0; }
ssize_t recv(int sockfd, void *buf, size_t len, int flags) { struct fdtab_entry *e = fdtab_get(sockfd); switch(e->type) { case FDTAB_TYPE_UNIX_SOCKET: { struct _unix_socket *us = e->handle; // XXX: Don't support flags assert(flags == 0); thread_mutex_lock(&us->mutex); if(us->passive || us->u.active.mode != _UNIX_SOCKET_MODE_CONNECTED) { errno = ENOTCONN; thread_mutex_unlock(&us->mutex); return -1; } if(us->recv_buf_valid == 0) { // No more data if(us->nonblocking) { errno = EAGAIN; thread_mutex_unlock(&us->mutex); return -1; } else { struct waitset ws; errval_t err; waitset_init(&ws); err = us->u.active.binding->change_waitset (us->u.active.binding, &ws); if(err_is_fail(err)) { USER_PANIC_ERR(err, "change_waitset"); } while(us->recv_buf_valid == 0) { err = event_dispatch(&ws); if(err_is_fail(err)) { USER_PANIC_ERR(err, "waitset_destroy"); } } // XXX: Assume it was on the default waitset err = us->u.active.binding->change_waitset (us->u.active.binding, get_default_waitset()); if(err_is_fail(err)) { USER_PANIC_ERR(err, "change_waitset"); } err = waitset_destroy(&ws); if(err_is_fail(err)) { USER_PANIC_ERR(err, "waitset_destroy"); } } } size_t recved = 0; while(recved < len && us->recv_list != NULL) { struct _unix_socket_recv *usr = us->recv_list; size_t consume = MIN(len - recved, usr->size - usr->consumed); memcpy(buf + recved, &usr->msg[usr->consumed], consume); usr->consumed += consume; us->recv_buf_valid -= consume; recved += consume; if(usr->consumed == usr->size) { us->recv_list = usr->next; if(us->recv_list == NULL) { us->recv_list_end = NULL; } free(usr->msg); free(usr); } else { assert(recved == len); } } thread_mutex_unlock(&us->mutex); return recved; } case FDTAB_TYPE_LWIP_SOCKET: lwip_mutex_lock(); ssize_t ret = lwip_recv(e->fd, buf, len, flags); lwip_mutex_unlock(); return ret; case FDTAB_TYPE_AVAILABLE: errno = EBADF; return -1; default: errno = ENOTSOCK; return -1; } }
ssize_t send(int sockfd, const void *buf, size_t len, int flags) { struct fdtab_entry *e = fdtab_get(sockfd); switch(e->type) { case FDTAB_TYPE_UNIX_SOCKET: { struct _unix_socket *us = e->handle; // XXX: Don't support flags assert(flags == 0); thread_mutex_lock(&us->mutex); if(us->passive || us->u.active.mode != _UNIX_SOCKET_MODE_CONNECTED) { errno = ENOTCONN; thread_mutex_unlock(&us->mutex); return -1; } if(us->send_buf != NULL) { if(us->nonblocking) { errno = EAGAIN; thread_mutex_unlock(&us->mutex); return -1; } else { assert(!"NYI"); } } // Bleh. Gotta copy here. I can't just wait until the // message is fully sent, as that might block // indefinitely. us->send_buf = malloc(len); memcpy(us->send_buf, buf, len); struct event_closure ec = { .handler = unixsock_sent, .arg = us, }; errval_t err = us->u.active.binding->tx_vtbl. send(us->u.active.binding, ec, us->send_buf, len); if(err_is_fail(err)) { USER_PANIC_ERR(err, "unixsock->send"); thread_mutex_unlock(&us->mutex); return -1; } // Wait until all data sent if blocking if(!us->nonblocking) { struct waitset ws; waitset_init(&ws); err = us->u.active.binding->change_waitset (us->u.active.binding, &ws); if(err_is_fail(err)) { USER_PANIC_ERR(err, "change_waitset"); } while(us->send_buf != NULL) { err = event_dispatch(&ws); if(err_is_fail(err)) { USER_PANIC_ERR(err, "waitset_destroy"); } } // XXX: Assume it was on the default waitset err = us->u.active.binding->change_waitset (us->u.active.binding, get_default_waitset()); if(err_is_fail(err)) { USER_PANIC_ERR(err, "change_waitset"); } err = waitset_destroy(&ws); if(err_is_fail(err)) { USER_PANIC_ERR(err, "waitset_destroy"); } } // XXX: We send all or nothing thread_mutex_unlock(&us->mutex); return len; } case FDTAB_TYPE_LWIP_SOCKET: lwip_mutex_lock(); ssize_t ret = lwip_send(e->fd, buf, len, flags); lwip_mutex_unlock(); return ret; case FDTAB_TYPE_AVAILABLE: errno = EBADF; return -1; default: errno = ENOTSOCK; return -1; } }
return -1; } } return newfd; } default: return -1; } } int getsockopt(int sockfd, int level, int optname, void *restrict optval, socklen_t *restrict optlen) { struct fdtab_entry *e = fdtab_get(sockfd); switch(e->type) { case FDTAB_TYPE_UNIX_SOCKET: { /* struct _unix_socket *us = e->handle; */ switch(level) { case SOL_SOCKET: switch(optname) { case SO_ERROR: { int *val = optval; if(*optlen < sizeof(int)) { // XXX: Store nothing if we can't store it all
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) { struct fdtab_entry *mye = fdtab_get(epfd); assert(mye->type == FDTAB_TYPE_EPOLL_INSTANCE); struct _epoll_fd *efd = mye->handle; int ret = 0; if(op != EPOLL_CTL_DEL) { assert(!(event->events & EPOLLRDHUP)); assert(!(event->events & EPOLLPRI)); assert(!(event->events & EPOLLERR)); assert(!(event->events & EPOLLHUP)); assert(!(event->events & EPOLLET)); assert(!(event->events & EPOLLONESHOT)); } switch(op) { case EPOLL_CTL_ADD: // Add event/FD to events/FDs list { // Check that it's not already in the list /* for(struct _epoll_events_list *i = efd->events; i != NULL; i = i->next) { */ /* assert(i->fd != fd); */ /* } */ struct fdtab_entry *e = fdtab_get(fd); if(e->epoll_fd == epfd) { errno = EEXIST; ret = -1; break; } assert(e->epoll_fd == -1); e->epoll_fd = epfd; struct _epoll_events_list *li = &e->epoll_events; li->prev = NULL; li->next = efd->events; if(li->next != NULL) { li->next->prev = li; } li->event = *event; li->fd = fd; efd->events = li; } break; case EPOLL_CTL_DEL: { #if 0 struct _epoll_events_list *lasti = NULL, *i; for(i = efd->events; i != NULL; i = i->next) { if(i->fd == fd) { if(lasti == NULL) { // First entry in list efd->events = i->next; } else { // Anywhere else lasti->next = i->next; } free(i); struct fdtab_entry *e = fdtab_get(fd); assert(e->epoll_fd != -1); e->epoll_fd = -1; break; } lasti = i; } if(i == NULL) { errno = ENOENT; ret = -1; } #else struct fdtab_entry *e = fdtab_get(fd); assert(e->epoll_fd != -1); e->epoll_fd = -1; if(&e->epoll_events == efd->events) { // First entry in list -- update head efd->events = e->epoll_events.next; } if(e->epoll_events.next != NULL) { e->epoll_events.next->prev = e->epoll_events.prev; } if(e->epoll_events.prev != NULL) { e->epoll_events.prev->next = e->epoll_events.next; } #endif } break; case EPOLL_CTL_MOD: { struct _epoll_events_list *i; for(i = efd->events; i != NULL; i = i->next) { if(i->fd == fd) { // Found i->event = *event; break; } } if(i == NULL) { errno = ENOENT; ret = -1; } } break; default: errno = EINVAL; return -1; } return ret; }
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { struct fdtab_entry *e = fdtab_get(sockfd); POSIXCOMPAT_DEBUG("%s:%s:%d: accept(sockfd=%d , e->type=%d)\n", __FILE__, __FUNCTION__, __LINE__, sockfd, e->type); switch(e->type) { case FDTAB_TYPE_UNIX_SOCKET: { struct _unix_socket *us = e->handle; int i; if(!us->passive) { return -1; } for(;;) { for(i = 0; i < us->u.passive.max_backlog; i++) { if(us->u.passive.backlog[i] != NULL) { break; } } if(i != us->u.passive.max_backlog) { // Found a pending connection break; } if(us->nonblocking) { errno = EAGAIN; return -1; } // XXX: This should wait only on the monitor event_dispatch(get_default_waitset()); } // XXX: Using socket() to create new socket. Probably dangerous. int newfd = socket(AF_UNIX, us->type, us->protocol); assert(newfd != -1); struct _unix_socket *newus = fdtab_get(newfd)->handle; newus->u.active.binding = us->u.passive.backlog[i]; newus->u.active.mode = _UNIX_SOCKET_MODE_CONNECTED; memcpy(&newus->sockaddr, &us->sockaddr, sizeof(struct sockaddr_un)); // Associate binding with new socket and remove from backlog assert(newus->u.active.binding->st == NULL); newus->u.active.binding->st = newus; us->u.passive.backlog[i] = NULL; if(addr != NULL) { assert(addrlen != NULL); // TODO: Should request address from peer (if peer is bound) if(*addrlen > sizeof(struct sockaddr_un)) { *addrlen = sizeof(struct sockaddr_un); } memcpy(addr, &newus->peer, *addrlen); } return newfd; } case FDTAB_TYPE_LWIP_SOCKET: { lwip_mutex_lock(); int newfd = lwip_accept(e->fd, addr, addrlen); lwip_mutex_unlock(); if(newfd != -1) { struct fdtab_entry newe; newe.type = FDTAB_TYPE_LWIP_SOCKET; newe.fd = newfd; newe.inherited = false; newe.epoll_fd = -1; newfd = fdtab_alloc(&newe); POSIXCOMPAT_DEBUG("accept(%d, _, _) as fd %d\n", sockfd, newfd); if (newfd < 0) { return -1; } } return newfd; } default: return -1; } }
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) { struct fdtab_entry *mye = fdtab_get(epfd); assert(mye->type == FDTAB_TYPE_EPOLL_INSTANCE); struct _epoll_fd *efd = mye->handle; struct monitor_binding *mb = get_monitor_binding(); errval_t err; /* waitset_init(&efd->ws); */ assert(maxevents >= 1); for(struct _epoll_events_list *i = efd->events; i != NULL; i = i->next) { struct fdtab_entry *e = fdtab_get(i->fd); struct epoll_event *event = &i->event; switch (e->type) { case FDTAB_TYPE_LWIP_SOCKET: { int retval; lwip_mutex_lock(); if(event->events & EPOLLIN) { retval = lwip_sock_waitset_register_read(e->fd, &efd->ws); assert(retval == 0); } if(event->events & EPOLLOUT) { retval = lwip_sock_waitset_register_write(e->fd, &efd->ws); assert(retval == 0); } lwip_mutex_unlock(); } break; case FDTAB_TYPE_UNIX_SOCKET: { struct _unix_socket *us = e->handle; if(event->events & EPOLLIN) { if (us->passive) { /* passive side */ int j; /* Check for pending connection requests. */ for (j = 0; j < us->u.passive.max_backlog; j++) { if (us->u.passive.backlog[j] != NULL) { break; } } /* * If there are not pending connection request * wait on monitor binding. */ if (j == us->u.passive.max_backlog) { /* wait on monitor */ err = mb->change_waitset(mb, &efd->ws); if (err_is_fail(err)) { USER_PANIC_ERR(err, "change_waitset"); } } } } if(event->events & EPOLLOUT) { assert(!us->passive); if(us->u.active.mode == _UNIX_SOCKET_MODE_CONNECTING) { /* wait on monitor */ err = mb->change_waitset(mb, &efd->ws); if (err_is_fail(err)) { USER_PANIC_ERR(err, "change_waitset"); } } } assert(event->events & (EPOLLIN | EPOLLOUT)); // Change waitset err = us->u.active.binding->change_waitset (us->u.active.binding, &efd->ws); if (err_is_fail(err)) { USER_PANIC_ERR(err, "change waitset"); } } break; default: { fprintf(stderr, "change waitset on FD type %d NYI.\n", e->type); assert(!"NYI"); errno = EBADF; return -1; } } } // Timeout handling struct timeout_event toe = { .fired = false }; struct deferred_event timeout_event; if (timeout > 0) { deferred_event_init(&timeout_event); err = deferred_event_register(&timeout_event, &efd->ws, timeout, MKCLOSURE(timeout_fired, &toe)); if (err_is_fail(err)) { errno = EINVAL; return -1; } } int retevents = 0; while(!toe.fired && retevents == 0) { if(timeout == 0) { // Just poll once, don't block err = event_dispatch_non_block(&efd->ws); assert(err_is_ok(err) || err_no(err) == LIB_ERR_NO_EVENT); toe.fired = true; } else { err = event_dispatch(&efd->ws); if (err_is_fail(err)) { USER_PANIC_ERR(err, "Error in event_dispatch."); } } // Return ready file descriptors for(struct _epoll_events_list *i = efd->events; i != NULL; i = i->next) { struct epoll_event *event = &i->event; struct fdtab_entry *e = fdtab_get(i->fd); assert(retevents < maxevents); events[retevents] = *event; events[retevents].events = 0; // Check errors (hangup) { switch (e->type) { case FDTAB_TYPE_LWIP_SOCKET: { lwip_mutex_lock(); if (!lwip_sock_is_open(e->fd)) { events[retevents].events |= EPOLLHUP; } lwip_mutex_unlock(); } break; default: // No-Op break; } } // Check readable FDs if(event->events & EPOLLIN) { switch (e->type) { case FDTAB_TYPE_LWIP_SOCKET: { lwip_mutex_lock(); if (lwip_sock_ready_read(e->fd)) { events[retevents].events |= EPOLLIN; } lwip_mutex_unlock(); } break; case FDTAB_TYPE_UNIX_SOCKET: { struct _unix_socket *us = e->handle; if (us->passive) { /* passive side */ /* Check for pending connection requests. */ for (int j = 0; j < us->u.passive.max_backlog; j++) { if (us->u.passive.backlog[j] != NULL) { events[retevents].events |= EPOLLIN; break; } } } else { /* active side */ /* Check for incoming data. */ if (us->recv_buf_valid > 0) { events[retevents].events |= EPOLLIN; } } } break; default: { fprintf(stderr, "epoll_wait() on FD type %d NYI.\n", e->type); assert(!"NYI"); errno = EBADF; return -1; } } } // Check writeable FDs if(event->events & EPOLLOUT) { switch (e->type) { case FDTAB_TYPE_LWIP_SOCKET: { lwip_mutex_lock(); if (lwip_sock_ready_write(e->fd)) { events[retevents].events |= EPOLLOUT; } lwip_mutex_unlock(); } break; case FDTAB_TYPE_UNIX_SOCKET: { struct _unix_socket *us = e->handle; assert(!us->passive); switch (us->u.active.mode) { case _UNIX_SOCKET_MODE_CONNECTING: break; case _UNIX_SOCKET_MODE_CONNECTED: if (us->send_buf == NULL) { events[retevents].events |= EPOLLOUT; } break; } } break; default: { fprintf(stderr, "epoll_wait() on FD type %d NYI.\n", e->type); assert(!"NYI"); errno = EBADF; return -1; } } } // If any events were returned, go to next entry in array if(events[retevents].events != 0) { retevents++; } } } // Remove timeout from waitset if it was set and not fired if(timeout > 0 && !toe.fired) { deferred_event_cancel(&timeout_event); } // Restore old waitsets for(struct _epoll_events_list *i = efd->events; i != NULL; i = i->next) { struct fdtab_entry *e = fdtab_get(i->fd); struct epoll_event *event = &i->event; switch (e->type) { case FDTAB_TYPE_LWIP_SOCKET: { lwip_mutex_lock(); if(event->events & EPOLLIN) { err = lwip_sock_waitset_deregister_read(e->fd); if (err_is_fail(err) && err_no(err) != LIB_ERR_CHAN_NOT_REGISTERED) { USER_PANIC_ERR(err, "error deregister read channel for " "lwip socket"); } } if(event->events & EPOLLOUT) { err = lwip_sock_waitset_deregister_write(e->fd); if (err_is_fail(err) && err_no(err) != LIB_ERR_CHAN_NOT_REGISTERED) { USER_PANIC_ERR(err, "error deregister write channel for " "lwip socket"); } } lwip_mutex_unlock(); } break; case FDTAB_TYPE_UNIX_SOCKET: { // NYI } break; default: { fprintf(stderr, "change waitset on FD type %d NYI.\n", e->type); assert(!"NYI"); errno = EBADF; return -1; } } } return retevents; } int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask) { assert(!"NYI"); }