diff options
author | Gunnar Beutner <gbeutner@serenityos.org> | 2021-05-16 19:56:11 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-05-17 13:32:19 +0200 |
commit | 89956cb0d66f24bfcee312dcd35f78b5dbc9e1b9 (patch) | |
tree | dd7f38132b4cb91410271b4948cdfe99d28778d0 | |
parent | 529f605ac801a541b1206ce06d497f8835ca7412 (diff) | |
download | serenity-89956cb0d66f24bfcee312dcd35f78b5dbc9e1b9.zip |
Kernel+Userspace: Implement the accept4() system call
Unlike accept() the new accept4() system call lets the caller specify
flags for the newly accepted socket file descriptor, such as
SOCK_CLOEXEC and SOCK_NONBLOCK.
-rw-r--r-- | Kernel/API/Syscall.h | 9 | ||||
-rw-r--r-- | Kernel/Process.h | 2 | ||||
-rw-r--r-- | Kernel/Syscalls/socket.cpp | 18 | ||||
-rw-r--r-- | Userland/DevTools/UserspaceEmulator/Emulator.h | 2 | ||||
-rw-r--r-- | Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp | 27 | ||||
-rw-r--r-- | Userland/Libraries/LibC/sys/socket.cpp | 8 | ||||
-rw-r--r-- | Userland/Libraries/LibC/sys/socket.h | 1 |
7 files changed, 49 insertions, 18 deletions
diff --git a/Kernel/API/Syscall.h b/Kernel/API/Syscall.h index 2d4e839d35..80f6ce9747 100644 --- a/Kernel/API/Syscall.h +++ b/Kernel/API/Syscall.h @@ -99,7 +99,7 @@ namespace Kernel { S(chmod) \ S(socket) \ S(bind) \ - S(accept) \ + S(accept4) \ S(listen) \ S(connect) \ S(link) \ @@ -269,6 +269,13 @@ struct SC_clock_nanosleep_params { struct timespec* remaining_sleep; }; +struct SC_accept4_params { + int sockfd; + sockaddr* addr; + socklen_t* addrlen; + int flags; +}; + struct SC_getsockopt_params { int sockfd; int level; diff --git a/Kernel/Process.h b/Kernel/Process.h index 537528a149..ed53a536a6 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -364,7 +364,7 @@ public: KResultOr<int> sys$socket(int domain, int type, int protocol); KResultOr<int> sys$bind(int sockfd, Userspace<const sockaddr*> addr, socklen_t); KResultOr<int> sys$listen(int sockfd, int backlog); - KResultOr<int> sys$accept(int sockfd, Userspace<sockaddr*>, Userspace<socklen_t*>); + KResultOr<int> sys$accept4(Userspace<const Syscall::SC_accept4_params*>); KResultOr<int> sys$connect(int sockfd, Userspace<const sockaddr*>, socklen_t); KResultOr<int> sys$shutdown(int sockfd, int how); KResultOr<ssize_t> sys$sendmsg(int sockfd, Userspace<const struct msghdr*>, int flags); diff --git a/Kernel/Syscalls/socket.cpp b/Kernel/Syscalls/socket.cpp index 03288bb4d5..32f6c3e8cf 100644 --- a/Kernel/Syscalls/socket.cpp +++ b/Kernel/Syscalls/socket.cpp @@ -79,10 +79,19 @@ KResultOr<int> Process::sys$listen(int sockfd, int backlog) return socket.listen(backlog); } -KResultOr<int> Process::sys$accept(int accepting_socket_fd, Userspace<sockaddr*> user_address, Userspace<socklen_t*> user_address_size) +KResultOr<int> Process::sys$accept4(Userspace<const Syscall::SC_accept4_params*> user_params) { REQUIRE_PROMISE(accept); + Syscall::SC_accept4_params params; + if (!copy_from_user(¶ms, user_params)) + return EFAULT; + + int accepting_socket_fd = params.sockfd; + Userspace<sockaddr*> user_address((FlatPtr)params.addr); + Userspace<socklen_t*> user_address_size((FlatPtr)params.addrlen); + int flags = params.flags; + socklen_t address_size = 0; if (user_address && !copy_from_user(&address_size, static_ptr_cast<const socklen_t*>(user_address_size))) return EFAULT; @@ -125,7 +134,12 @@ KResultOr<int> Process::sys$accept(int accepting_socket_fd, Userspace<sockaddr*> accepted_socket_description_result.value()->set_readable(true); accepted_socket_description_result.value()->set_writable(true); - m_fds[accepted_socket_fd].set(accepted_socket_description_result.release_value(), 0); + if (flags & SOCK_NONBLOCK) + accepted_socket_description_result.value()->set_blocking(false); + int fd_flags = 0; + if (flags & SOCK_CLOEXEC) + fd_flags |= FD_CLOEXEC; + m_fds[accepted_socket_fd].set(accepted_socket_description_result.release_value(), fd_flags); // NOTE: Moving this state to Completed is what causes connect() to unblock on the client side. accepted_socket->set_setup_state(Socket::SetupState::Completed); diff --git a/Userland/DevTools/UserspaceEmulator/Emulator.h b/Userland/DevTools/UserspaceEmulator/Emulator.h index 3533180ae8..70badf8a78 100644 --- a/Userland/DevTools/UserspaceEmulator/Emulator.h +++ b/Userland/DevTools/UserspaceEmulator/Emulator.h @@ -137,7 +137,7 @@ private: int virt$getpeername(FlatPtr); int virt$select(FlatPtr); int virt$get_stack_bounds(FlatPtr, FlatPtr); - int virt$accept(int sockfd, FlatPtr address, FlatPtr address_length); + int virt$accept4(FlatPtr); int virt$bind(int sockfd, FlatPtr address, socklen_t address_length); int virt$recvmsg(int sockfd, FlatPtr msg_addr, int flags); int virt$sendmsg(int sockfd, FlatPtr msg_addr, int flags); diff --git a/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp b/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp index 1fd6af2c83..e3ed2ddc3a 100644 --- a/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp +++ b/Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp @@ -168,8 +168,8 @@ u32 Emulator::virt_syscall(u32 function, u32 arg1, u32 arg2, u32 arg3) return virt$fchmod(arg1, arg2); case SC_fchown: return virt$fchown(arg1, arg2, arg3); - case SC_accept: - return virt$accept(arg1, arg2, arg3); + case SC_accept4: + return virt$accept4(arg1); case SC_setsockopt: return virt$setsockopt(arg1); case SC_getsockname: @@ -452,17 +452,20 @@ mode_t Emulator::virt$umask(mode_t mask) return syscall(SC_umask, mask); } -int Emulator::virt$accept(int sockfd, FlatPtr address, FlatPtr address_length) +int Emulator::virt$accept4(FlatPtr params_addr) { - socklen_t host_address_length = 0; - mmu().copy_from_vm(&host_address_length, address_length, sizeof(host_address_length)); - auto host_buffer = ByteBuffer::create_zeroed(host_address_length); - int rc = syscall(SC_accept, sockfd, host_buffer.data(), &host_address_length); - if (rc < 0) - return rc; - mmu().copy_to_vm(address, host_buffer.data(), min((socklen_t)host_buffer.size(), host_address_length)); - mmu().copy_to_vm(address_length, &host_address_length, sizeof(host_address_length)); - return rc; + Syscall::SC_accept4_params params; + mmu().copy_from_vm(¶ms, params_addr, sizeof(params)); + sockaddr_storage addr = {}; + socklen_t addrlen; + mmu().copy_from_vm(&addrlen, (FlatPtr)params.addrlen, sizeof(socklen_t)); + VERIFY(addrlen <= sizeof(addr)); + int rc = accept4(params.sockfd, (sockaddr*)&addr, &addrlen, params.flags); + if (rc == 0) { + mmu().copy_to_vm((FlatPtr)params.addr, &addr, addrlen); + mmu().copy_to_vm((FlatPtr)params.addrlen, &addrlen, sizeof(socklen_t)); + } + return rc < 0 ? -errno : rc; } int Emulator::virt$bind(int sockfd, FlatPtr address, socklen_t address_length) diff --git a/Userland/Libraries/LibC/sys/socket.cpp b/Userland/Libraries/LibC/sys/socket.cpp index 96211141b1..ba9cdd38ec 100644 --- a/Userland/Libraries/LibC/sys/socket.cpp +++ b/Userland/Libraries/LibC/sys/socket.cpp @@ -34,7 +34,13 @@ int listen(int sockfd, int backlog) int accept(int sockfd, sockaddr* addr, socklen_t* addrlen) { - int rc = syscall(SC_accept, sockfd, addr, addrlen); + return accept4(sockfd, addr, addrlen, 0); +} + +int accept4(int sockfd, sockaddr* addr, socklen_t* addrlen, int flags) +{ + Syscall::SC_accept4_params params { sockfd, addr, addrlen, flags }; + int rc = syscall(SC_accept4, ¶ms); __RETURN_WITH_ERRNO(rc, rc, -1); } diff --git a/Userland/Libraries/LibC/sys/socket.h b/Userland/Libraries/LibC/sys/socket.h index 44b673546d..0ff9aaff14 100644 --- a/Userland/Libraries/LibC/sys/socket.h +++ b/Userland/Libraries/LibC/sys/socket.h @@ -127,6 +127,7 @@ int socket(int domain, int type, int protocol); int bind(int sockfd, const struct sockaddr* addr, socklen_t); int listen(int sockfd, int backlog); int accept(int sockfd, struct sockaddr*, socklen_t*); +int accept4(int sockfd, struct sockaddr*, socklen_t*, int); int connect(int sockfd, const struct sockaddr*, socklen_t); int shutdown(int sockfd, int how); ssize_t send(int sockfd, const void*, size_t, int flags); |