Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/ScopeGuard.h>
8#include <AK/Time.h>
9#include <Kernel/API/POSIX/select.h>
10#include <Kernel/Debug.h>
11#include <Kernel/FileSystem/OpenFileDescription.h>
12#include <Kernel/Process.h>
13
14namespace Kernel {
15
16using BlockFlags = Thread::FileBlocker::BlockFlags;
17
18ErrorOr<FlatPtr> Process::sys$poll(Userspace<Syscall::SC_poll_params const*> user_params)
19{
20 VERIFY_PROCESS_BIG_LOCK_ACQUIRED(this);
21 TRY(require_promise(Pledge::stdio));
22
23 auto params = TRY(copy_typed_from_user(user_params));
24
25 if (params.nfds >= OpenFileDescriptions::max_open())
26 return ENOBUFS;
27
28 Thread::BlockTimeout timeout;
29 if (params.timeout) {
30 auto timeout_time = TRY(copy_time_from_user(params.timeout));
31 timeout = Thread::BlockTimeout(false, &timeout_time);
32 }
33
34 sigset_t sigmask = {};
35 if (params.sigmask)
36 TRY(copy_from_user(&sigmask, params.sigmask));
37
38 Vector<pollfd, FD_SETSIZE> fds_copy;
39 if (params.nfds > 0) {
40 Checked<size_t> nfds_checked = sizeof(pollfd);
41 nfds_checked *= params.nfds;
42 if (nfds_checked.has_overflow())
43 return EFAULT;
44 TRY(fds_copy.try_resize(params.nfds));
45 TRY(copy_from_user(fds_copy.data(), ¶ms.fds[0], nfds_checked.value()));
46 }
47
48 Thread::SelectBlocker::FDVector fds_info;
49 TRY(fds_info.try_ensure_capacity(params.nfds));
50
51 TRY(m_fds.with_shared([&](auto& fds) -> ErrorOr<void> {
52 for (size_t i = 0; i < params.nfds; i++) {
53 auto& pfd = fds_copy[i];
54 RefPtr<OpenFileDescription> description;
55 auto description_or_error = fds.open_file_description(pfd.fd);
56 if (!description_or_error.is_error())
57 description = description_or_error.release_value();
58 BlockFlags block_flags = BlockFlags::WriteError | BlockFlags::WriteHangUp; // always want POLLERR, POLLHUP, POLLNVAL
59 if (pfd.events & POLLIN)
60 block_flags |= BlockFlags::Read;
61 if (pfd.events & POLLOUT)
62 block_flags |= BlockFlags::Write;
63 if (pfd.events & POLLPRI)
64 block_flags |= BlockFlags::ReadPriority;
65 if (pfd.events & POLLWRBAND)
66 block_flags |= BlockFlags::WritePriority;
67 if (pfd.events & POLLRDHUP)
68 block_flags |= BlockFlags::ReadHangUp;
69 fds_info.unchecked_append({ move(description), block_flags });
70 }
71 return {};
72 }));
73
74 auto* current_thread = Thread::current();
75
76 u32 previous_signal_mask = 0;
77 if (params.sigmask)
78 previous_signal_mask = current_thread->update_signal_mask(sigmask);
79 ScopeGuard rollback_signal_mask([&]() {
80 if (params.sigmask)
81 current_thread->update_signal_mask(previous_signal_mask);
82 });
83
84 if constexpr (IO_DEBUG || POLL_SELECT_DEBUG)
85 dbgln("polling on {} fds, timeout={}", fds_info.size(), params.timeout);
86
87 if (current_thread->block<Thread::SelectBlocker>(timeout, fds_info).was_interrupted())
88 return EINTR;
89
90 int fds_with_revents = 0;
91
92 for (unsigned i = 0; i < params.nfds; ++i) {
93 auto& pfd = fds_copy[i];
94 auto& fds_entry = fds_info[i];
95
96 pfd.revents = 0;
97 if (fds_entry.unblocked_flags == BlockFlags::None)
98 continue;
99
100 if (has_flag(fds_entry.unblocked_flags, BlockFlags::WriteHangUp))
101 pfd.revents |= POLLHUP;
102 if (has_flag(fds_entry.unblocked_flags, BlockFlags::WriteError) || !fds_entry.description) {
103 if (has_flag(fds_entry.unblocked_flags, BlockFlags::WriteError))
104 pfd.revents |= POLLERR;
105 if (!fds_entry.description)
106 pfd.revents |= POLLNVAL;
107 } else {
108 if (has_flag(fds_entry.unblocked_flags, BlockFlags::Read)) {
109 VERIFY(pfd.events & POLLIN);
110 pfd.revents |= POLLIN;
111 }
112 if (has_flag(fds_entry.unblocked_flags, BlockFlags::ReadPriority)) {
113 VERIFY(pfd.events & POLLPRI);
114 pfd.revents |= POLLPRI;
115 }
116 if (!has_flag(fds_entry.unblocked_flags, BlockFlags::WriteHangUp) && has_flag(fds_entry.unblocked_flags, BlockFlags::Write)) {
117 VERIFY(pfd.events & POLLOUT);
118 pfd.revents |= POLLOUT;
119 }
120 if (has_flag(fds_entry.unblocked_flags, BlockFlags::WritePriority)) {
121 VERIFY(pfd.events & POLLWRBAND);
122 pfd.revents |= POLLWRBAND;
123 }
124 if (has_flag(fds_entry.unblocked_flags, BlockFlags::ReadHangUp)) {
125 VERIFY(pfd.events & POLLRDHUP);
126 pfd.revents |= POLLRDHUP;
127 }
128 }
129 if (pfd.revents)
130 fds_with_revents++;
131 }
132
133 if (params.nfds > 0)
134 TRY(copy_to_user(¶ms.fds[0], fds_copy.data(), params.nfds * sizeof(pollfd)));
135
136 return fds_with_revents;
137}
138
139}