Serenity Operating System

Kernel+LibC: Add sys$waitid(), and make sys$waitpid() wrap it

sys$waitid() takes an explicit description of whether it's waiting for a single
process with the given PID, all of the children, a group, etc., and returns its
info as a siginfo_t.

It also doesn't automatically imply WEXITED, which clears up the confusion in
the kernel.

authored by

Sergey Bugaev and committed by
Andreas Kling
b3a24d73 a6cb7f75

+140 -55
+78 -50
Kernel/Process.cpp
··· 2266 2266 return old_mask; 2267 2267 } 2268 2268 2269 - int Process::reap(Process& process) 2269 + siginfo_t Process::reap(Process& process) 2270 2270 { 2271 - int exit_status; 2271 + siginfo_t siginfo; 2272 + siginfo.si_signo = SIGCHLD; 2273 + siginfo.si_pid = process.pid(); 2274 + siginfo.si_uid = process.uid(); 2275 + 2276 + if (process.m_termination_signal) { 2277 + siginfo.si_status = process.m_termination_signal; 2278 + siginfo.si_code = CLD_KILLED; 2279 + } else { 2280 + siginfo.si_status = process.m_termination_status; 2281 + siginfo.si_code = CLD_EXITED; 2282 + } 2283 + 2272 2284 { 2273 2285 InterruptDisabler disabler; 2274 - exit_status = (process.m_termination_status << 8) | process.m_termination_signal; 2275 2286 2276 2287 if (process.ppid()) { 2277 2288 auto* parent = Process::from_pid(process.ppid()); ··· 2288 2299 g_processes->remove(&process); 2289 2300 } 2290 2301 delete &process; 2291 - return exit_status; 2302 + return siginfo; 2292 2303 } 2293 2304 2294 - pid_t Process::sys$waitpid(pid_t waitee, int* wstatus, int options) 2305 + KResultOr<siginfo_t> Process::do_waitid(idtype_t idtype, int id, int options) 2295 2306 { 2296 - REQUIRE_PROMISE(stdio); 2297 - 2298 - #ifdef PROCESS_DEBUG 2299 - dbg() << "sys$waitpid(" << waitee << ", " << wstatus << ", " << options << ")"; 2300 - #endif 2301 - 2302 - if (!options) { 2303 - // FIXME: This can't be right.. can it? Figure out how this should actually work. 2304 - options = WEXITED; 2305 - } 2306 - 2307 - if (wstatus && !validate_write_typed(wstatus)) 2308 - return -EFAULT; 2309 - 2310 - int exit_status = 0; 2311 - 2312 - { 2307 + if (idtype == P_PID) { 2313 2308 InterruptDisabler disabler; 2314 - if (waitee != -1 && !Process::from_pid(waitee)) 2315 - return -ECHILD; 2309 + if (idtype == P_PID && !Process::from_pid(id)) 2310 + return KResult(-ECHILD); 2316 2311 } 2317 2312 2318 2313 if (options & WNOHANG) { 2319 2314 // FIXME: Figure out what WNOHANG should do with stopped children. 2320 - if (waitee == -1) { 2321 - pid_t reaped_pid = 0; 2315 + if (idtype == P_ALL) { 2322 2316 InterruptDisabler disabler; 2323 - for_each_child([&reaped_pid, &exit_status](Process& process) { 2324 - if (process.is_dead()) { 2325 - reaped_pid = process.pid(); 2326 - exit_status = reap(process); 2327 - } 2317 + siginfo_t siginfo; 2318 + for_each_child([&siginfo](Process& process) { 2319 + if (process.is_dead()) 2320 + siginfo = reap(process); 2328 2321 return IterationDecision::Continue; 2329 2322 }); 2330 - return reaped_pid; 2331 - } else { 2332 - ASSERT(waitee > 0); // FIXME: Implement other PID specs. 2323 + return siginfo; 2324 + } else if (idtype == P_PID) { 2333 2325 InterruptDisabler disabler; 2334 - auto* waitee_process = Process::from_pid(waitee); 2326 + auto* waitee_process = Process::from_pid(id); 2335 2327 if (!waitee_process) 2336 - return -ECHILD; 2337 - if (waitee_process->is_dead()) { 2338 - exit_status = reap(*waitee_process); 2339 - return waitee; 2340 - } 2341 - return 0; 2328 + return KResult(-ECHILD); 2329 + if (waitee_process->is_dead()) 2330 + return reap(*waitee_process); 2331 + } else { 2332 + // FIXME: Implement other PID specs. 2333 + return KResult(-EINVAL); 2342 2334 } 2343 2335 } 2344 2336 2345 - pid_t waitee_pid = waitee; 2337 + pid_t waitee_pid; 2338 + 2339 + // FIXME: WaitBlocker should support idtype/id specs directly. 2340 + if (idtype == P_ALL) { 2341 + waitee_pid = -1; 2342 + } else if (idtype == P_PID) { 2343 + waitee_pid = id; 2344 + } else { 2345 + // FIXME: Implement other PID specs. 2346 + return KResult(-EINVAL); 2347 + } 2348 + 2346 2349 if (current->block<Thread::WaitBlocker>(options, waitee_pid) != Thread::BlockResult::WokeNormally) 2347 - return -EINTR; 2350 + return KResult(-EINTR); 2348 2351 2349 2352 InterruptDisabler disabler; 2350 2353 2351 2354 // NOTE: If waitee was -1, m_waitee_pid will have been filled in by the scheduler. 2352 2355 Process* waitee_process = Process::from_pid(waitee_pid); 2353 2356 if (!waitee_process) 2354 - return -ECHILD; 2357 + return KResult(-ECHILD); 2355 2358 2356 2359 ASSERT(waitee_process); 2357 2360 if (waitee_process->is_dead()) { 2358 - exit_status = reap(*waitee_process); 2361 + return reap(*waitee_process); 2359 2362 } else { 2360 2363 auto* waitee_thread = Thread::from_tid(waitee_pid); 2361 2364 if (!waitee_thread) 2362 - return -ECHILD; 2365 + return KResult(-ECHILD); 2363 2366 ASSERT(waitee_thread->state() == Thread::State::Stopped); 2364 - exit_status = (waitee_thread->m_stop_signal << 8) | 0x7f; 2367 + siginfo_t siginfo; 2368 + siginfo.si_signo = SIGCHLD; 2369 + siginfo.si_pid = waitee_process->pid(); 2370 + siginfo.si_uid = waitee_process->uid(); 2371 + siginfo.si_status = CLD_STOPPED; 2372 + siginfo.si_code = waitee_thread->m_stop_signal; 2373 + return siginfo; 2365 2374 } 2375 + } 2366 2376 2367 - if (wstatus) 2368 - copy_to_user(wstatus, &exit_status); 2369 - return waitee_pid; 2377 + pid_t Process::sys$waitid(const Syscall::SC_waitid_params* user_params) 2378 + { 2379 + REQUIRE_PROMISE(stdio); 2380 + 2381 + Syscall::SC_waitid_params params; 2382 + if (!validate_read_and_copy_typed(&params, user_params)) 2383 + return -EFAULT; 2384 + 2385 + if (!validate_write_typed(params.infop)) 2386 + return -EFAULT; 2387 + 2388 + //#ifdef PROCESS_DEBUG 2389 + dbg() << "sys$waitid(" << params.idtype << ", " << params.id << ", " << params.infop << ", " << params.options << ")"; 2390 + //#endif 2391 + 2392 + auto siginfo_or_error = do_waitid(static_cast<idtype_t>(params.idtype), params.id, params.options); 2393 + if (siginfo_or_error.is_error()) 2394 + return siginfo_or_error.error(); 2395 + 2396 + copy_to_user(params.infop, &siginfo_or_error.value()); 2397 + return 0; 2370 2398 } 2371 2399 2372 2400 bool Process::validate_read_from_kernel(VirtualAddress vaddr, size_t size) const
+4 -2
Kernel/Process.h
··· 201 201 int sys$kill(pid_t pid, int sig); 202 202 [[noreturn]] void sys$exit(int status); 203 203 int sys$sigreturn(RegisterDump& registers); 204 - pid_t sys$waitpid(pid_t, int* wstatus, int options); 204 + pid_t sys$waitid(const Syscall::SC_waitid_params*); 205 205 void* sys$mmap(const Syscall::SC_mmap_params*); 206 206 int sys$munmap(void*, size_t size); 207 207 int sys$set_mmap_name(const Syscall::SC_set_mmap_name_params*); ··· 310 310 static void initialize(); 311 311 312 312 [[noreturn]] void crash(int signal, u32 eip); 313 - [[nodiscard]] static int reap(Process&); 313 + [[nodiscard]] static siginfo_t reap(Process&); 314 314 315 315 const TTY* tty() const { return m_tty; } 316 316 void set_tty(TTY* tty) { m_tty = tty; } ··· 429 429 430 430 KResult do_kill(Process&, int signal); 431 431 KResult do_killpg(pid_t pgrp, int signal); 432 + 433 + KResultOr<siginfo_t> do_waitid(idtype_t idtype, int id, int options); 432 434 433 435 KResultOr<String> get_syscall_path_argument(const char* user_path, size_t path_length) const; 434 436 KResultOr<String> get_syscall_path_argument(const Syscall::StringArgument&) const;
+9 -1
Kernel/Syscall.h
··· 36 36 struct timeval; 37 37 struct timespec; 38 38 struct sockaddr; 39 + struct siginfo; 39 40 typedef u32 socklen_t; 40 41 } 41 42 ··· 51 52 __ENUMERATE_SYSCALL(exit) \ 52 53 __ENUMERATE_SYSCALL(getgid) \ 53 54 __ENUMERATE_SYSCALL(getpid) \ 54 - __ENUMERATE_SYSCALL(waitpid) \ 55 + __ENUMERATE_SYSCALL(waitid) \ 55 56 __ENUMERATE_SYSCALL(mmap) \ 56 57 __ENUMERATE_SYSCALL(munmap) \ 57 58 __ENUMERATE_SYSCALL(get_dir_entries) \ ··· 402 403 struct SC_unveil_params { 403 404 StringArgument path; 404 405 StringArgument permissions; 406 + }; 407 + 408 + struct SC_waitid_params { 409 + int idtype; 410 + int id; 411 + struct siginfo* infop; 412 + int options; 405 413 }; 406 414 407 415 void initialize();
+46 -1
Libraries/LibC/sys/wait.cpp
··· 38 38 39 39 pid_t waitpid(pid_t waitee, int* wstatus, int options) 40 40 { 41 - int rc = syscall(SC_waitpid, waitee, wstatus, options); 41 + siginfo_t siginfo; 42 + idtype_t idtype; 43 + id_t id; 44 + 45 + if (waitee < -1) { 46 + idtype = P_PGID; 47 + id = -waitee; 48 + } else if (waitee == -1) { 49 + idtype = P_ALL; 50 + id = 0; 51 + } else if (waitee == 0) { 52 + idtype = P_PGID; 53 + id = getgid(); 54 + } else { 55 + idtype = P_PID; 56 + id = waitee; 57 + } 58 + 59 + int rc = waitid(idtype, id, &siginfo, options | WEXITED); 60 + 61 + if (rc < 0) 62 + return rc; 63 + 64 + if (wstatus) { 65 + switch (siginfo.si_code) { 66 + case CLD_EXITED: 67 + *wstatus = siginfo.si_status << 8; 68 + break; 69 + case CLD_KILLED: 70 + *wstatus = siginfo.si_status; 71 + break; 72 + case CLD_STOPPED: 73 + *wstatus = siginfo.si_status << 8 | 0x7f; 74 + break; 75 + default: 76 + ASSERT_NOT_REACHED(); 77 + } 78 + } 79 + 80 + return siginfo.si_pid; 81 + } 82 + 83 + int waitid(idtype_t idtype, id_t id, siginfo_t* infop, int options) 84 + { 85 + Syscall::SC_waitid_params params { idtype, id, infop, options }; 86 + int rc = syscall(SC_waitid, &params); 42 87 __RETURN_WITH_ERRNO(rc, rc, -1); 43 88 } 44 89 }
+3 -1
Libraries/LibC/sys/wait.h
··· 26 26 27 27 #pragma once 28 28 29 + #include <signal.h> 29 30 #include <sys/cdefs.h> 30 31 #include <sys/types.h> 31 32 ··· 35 36 #define WSTOPSIG(status) WEXITSTATUS(status) 36 37 #define WTERMSIG(status) ((status)&0x7f) 37 38 #define WIFEXITED(status) (WTERMSIG(status) == 0) 38 - #define WIFSTOPPED(status) (((status) & 0xff) == 0x7f) 39 + #define WIFSTOPPED(status) (((status)&0xff) == 0x7f) 39 40 #define WIFSIGNALED(status) (((char)(((status)&0x7f) + 1) >> 1) > 0) 40 41 41 42 #define WNOHANG 1 ··· 52 53 53 54 pid_t waitpid(pid_t, int* wstatus, int options); 54 55 pid_t wait(int* wstatus); 56 + int waitid(idtype_t idtype, id_t id, siginfo_t* infop, int options); 55 57 56 58 __END_DECLS