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; }
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 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 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; }
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 socket(int domain, int type, int protocol) { struct fdtab_entry e; switch(domain) { case AF_UNIX: if(type != SOCK_STREAM) { errno = EPROTOTYPE; return -1; } if(protocol != 0) { errno = EPROTONOSUPPORT; return -1; } struct _unix_socket *us = malloc(sizeof(struct _unix_socket)); assert(us != NULL); memset(us, 0, sizeof(struct _unix_socket)); us->type = type; us->protocol = protocol; us->recv_buf_size = _UNIX_SOCKET_RECV_BUF_SIZE; /* * XXX: Even though POSIX says the result of * get{sock,peer}name() shall be unspecified if not bound, * some programs still expect its type to be of AF_UNIX. */ us->sockaddr.sun_len = us->peer.sun_len = sizeof(struct sockaddr_un); us->sockaddr.sun_family = us->peer.sun_family = AF_UNIX; e.type = FDTAB_TYPE_UNIX_SOCKET; e.handle = us; e.inherited = false; e.epoll_fd = -1; break; case AF_INET: { lwip_mutex_lock(); int fd = lwip_socket(domain, type, protocol); lwip_mutex_unlock(); if(fd == -1) { return fd; } e.type = FDTAB_TYPE_LWIP_SOCKET; e.fd = fd; e.inherited = false; e.epoll_fd = -1; } break; default: errno = EAFNOSUPPORT; return -1; } int fd = fdtab_alloc(&e); POSIXCOMPAT_DEBUG("socket(%d, %d, %d) as fd %d\n", domain, type, protocol, fd); if (fd < 0) { return -1; } else { return fd; } }
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; } }
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"); }
static void call_tcp_tmr(void) { lwip_mutex_lock(); tcp_tmr(); lwip_mutex_unlock(); }