summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGunnar Beutner <gbeutner@serenityos.org>2021-05-16 19:56:11 +0200
committerAndreas Kling <kling@serenityos.org>2021-05-17 13:32:19 +0200
commit89956cb0d66f24bfcee312dcd35f78b5dbc9e1b9 (patch)
treedd7f38132b4cb91410271b4948cdfe99d28778d0
parent529f605ac801a541b1206ce06d497f8835ca7412 (diff)
downloadserenity-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.h9
-rw-r--r--Kernel/Process.h2
-rw-r--r--Kernel/Syscalls/socket.cpp18
-rw-r--r--Userland/DevTools/UserspaceEmulator/Emulator.h2
-rw-r--r--Userland/DevTools/UserspaceEmulator/Emulator_syscalls.cpp27
-rw-r--r--Userland/Libraries/LibC/sys/socket.cpp8
-rw-r--r--Userland/Libraries/LibC/sys/socket.h1
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(&params, 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(&params, 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, &params);
__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);