| /* |
| * Check decoding of waitid syscall. |
| * |
| * Copyright (c) 2015-2016 Dmitry V. Levin <ldv@altlinux.org> |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "tests.h" |
| #include <assert.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/wait.h> |
| #include <sys/resource.h> |
| #include <sys/syscall.h> |
| |
| static const char * |
| sprint_rusage(const struct rusage *const ru) |
| { |
| static char buf[1024]; |
| snprintf(buf, sizeof(buf), |
| "{ru_utime={%llu, %llu}" |
| ", ru_stime={%llu, %llu}" |
| #ifdef VERBOSE_RUSAGE |
| ", ru_maxrss=%llu" |
| ", ru_ixrss=%llu" |
| ", ru_idrss=%llu" |
| ", ru_isrss=%llu" |
| ", ru_minflt=%llu" |
| ", ru_majflt=%llu" |
| ", ru_nswap=%llu" |
| ", ru_inblock=%llu" |
| ", ru_oublock=%llu" |
| ", ru_msgsnd=%llu" |
| ", ru_msgrcv=%llu" |
| ", ru_nsignals=%llu" |
| ", ru_nvcsw=%llu" |
| ", ru_nivcsw=%llu}" |
| #else |
| ", ...}" |
| #endif |
| , widen_to_ull(ru->ru_utime.tv_sec) |
| , widen_to_ull(ru->ru_utime.tv_usec) |
| , widen_to_ull(ru->ru_stime.tv_sec) |
| , widen_to_ull(ru->ru_stime.tv_usec) |
| #ifdef VERBOSE_RUSAGE |
| , widen_to_ull(ru->ru_maxrss) |
| , widen_to_ull(ru->ru_ixrss) |
| , widen_to_ull(ru->ru_idrss) |
| , widen_to_ull(ru->ru_isrss) |
| , widen_to_ull(ru->ru_minflt) |
| , widen_to_ull(ru->ru_majflt) |
| , widen_to_ull(ru->ru_nswap) |
| , widen_to_ull(ru->ru_inblock) |
| , widen_to_ull(ru->ru_oublock) |
| , widen_to_ull(ru->ru_msgsnd) |
| , widen_to_ull(ru->ru_msgrcv) |
| , widen_to_ull(ru->ru_nsignals) |
| , widen_to_ull(ru->ru_nvcsw) |
| , widen_to_ull(ru->ru_nivcsw) |
| #endif |
| ); |
| return buf; |
| } |
| |
| #define CASE(x) case x: return #x |
| |
| static const char * |
| si_code_2_name(const int code) |
| { |
| switch (code) { |
| #ifdef CLD_EXITED |
| CASE(CLD_EXITED); |
| #endif |
| #ifdef CLD_KILLED |
| CASE(CLD_KILLED); |
| #endif |
| #ifdef CLD_DUMPED |
| CASE(CLD_DUMPED); |
| #endif |
| #ifdef CLD_TRAPPED |
| CASE(CLD_TRAPPED); |
| #endif |
| #ifdef CLD_STOPPED |
| CASE(CLD_STOPPED); |
| #endif |
| #ifdef CLD_CONTINUED |
| CASE(CLD_CONTINUED); |
| #endif |
| default: perror_msg_and_fail("unknown si_code %d", code); |
| } |
| } |
| |
| static const char * |
| sprint_siginfo(const siginfo_t *const si, const char *const status_text) |
| { |
| static char buf[1024]; |
| snprintf(buf, sizeof(buf), |
| "{si_signo=SIGCHLD" |
| ", si_code=%s" |
| ", si_pid=%u" |
| ", si_uid=%u" |
| ", si_status=%s" |
| ", si_utime=%llu" |
| ", si_stime=%llu}", |
| si_code_2_name(si->si_code), |
| si->si_pid, |
| si->si_uid, |
| status_text, |
| widen_to_ull(si->si_utime), |
| widen_to_ull(si->si_stime)); |
| return buf; |
| } |
| |
| static unsigned long |
| poison(unsigned int v) |
| { |
| return (unsigned long) 0xfacefeed00000000 | v; |
| } |
| |
| static long |
| do_waitid(const unsigned int idtype, |
| const unsigned int id, |
| const siginfo_t const *infop, |
| const unsigned int options, |
| const struct rusage *const rusage) |
| { |
| sigset_t mask = {}; |
| sigaddset(&mask, SIGCHLD); |
| |
| assert(sigprocmask(SIG_BLOCK, &mask, NULL) == 0); |
| long rc = syscall(__NR_waitid, poison(idtype), poison(id), |
| infop, poison(options), rusage); |
| assert(sigprocmask(SIG_UNBLOCK, &mask, NULL) == 0); |
| return rc; |
| } |
| |
| int |
| main(void) |
| { |
| tprintf("%s", ""); |
| |
| int fds[2]; |
| if (pipe(fds)) |
| perror_msg_and_fail("pipe"); |
| |
| pid_t pid; |
| pid = fork(); |
| if (pid < 0) |
| perror_msg_and_fail("fork"); |
| |
| if (!pid) { |
| char c; |
| (void) close(1); |
| assert(read(0, &c, sizeof(c)) == 1); |
| return 42; |
| } |
| |
| (void) close(0); |
| |
| if (do_waitid(P_PID, pid, 0, WNOHANG|WEXITED, 0)) |
| perror_msg_and_fail("waitid #1"); |
| tprintf("waitid(P_PID, %d, NULL, WNOHANG|WEXITED, NULL) = 0\n", pid); |
| |
| siginfo_t *const sinfo = tail_alloc(sizeof(*sinfo)); |
| memset(sinfo, 0, sizeof(*sinfo)); |
| struct rusage *const rusage = tail_alloc(sizeof(*rusage)); |
| if (do_waitid(P_PID, pid, sinfo, WNOHANG|WEXITED|WSTOPPED, rusage)) |
| perror_msg_and_fail("waitid #2"); |
| tprintf("waitid(P_PID, %d, {}, WNOHANG|WEXITED|WSTOPPED, %s) = 0\n", |
| pid, sprint_rusage(rusage)); |
| |
| assert(write(1, "", 1) == 1); |
| (void) close(1); |
| |
| if (do_waitid(P_PID, pid, sinfo, WEXITED, rusage)) |
| perror_msg_and_fail("waitid #3"); |
| tprintf("waitid(P_PID, %d, %s, WEXITED, %s) = 0\n", |
| pid, sprint_siginfo(sinfo, "42"), sprint_rusage(rusage)); |
| |
| pid = fork(); |
| if (pid < 0) |
| perror_msg_and_fail("fork"); |
| |
| if (!pid) { |
| (void) raise(SIGUSR1); |
| return 1; |
| } |
| |
| if (do_waitid(P_PID, pid, sinfo, WEXITED, rusage)) |
| perror_msg_and_fail("waitid #4"); |
| tprintf("waitid(P_PID, %d, %s, WEXITED, %s) = 0\n", |
| pid, sprint_siginfo(sinfo, "SIGUSR1"), sprint_rusage(rusage)); |
| |
| if (pipe(fds)) |
| perror_msg_and_fail("pipe"); |
| pid = fork(); |
| if (pid < 0) |
| perror_msg_and_fail("fork"); |
| |
| if (!pid) { |
| (void) close(1); |
| raise(SIGSTOP); |
| char c; |
| assert(read(0, &c, sizeof(c)) == 1); |
| return 0; |
| } |
| |
| (void) close(0); |
| |
| if (do_waitid(P_PID, pid, sinfo, WSTOPPED, rusage)) |
| perror_msg_and_fail("waitid #5"); |
| tprintf("waitid(P_PID, %d, %s, WSTOPPED, %s) = 0\n", |
| pid, sprint_siginfo(sinfo, "SIGSTOP"), sprint_rusage(rusage)); |
| |
| if (kill(pid, SIGCONT)) |
| perror_msg_and_fail("kill(SIGCONT)"); |
| |
| #if defined WCONTINUED |
| if (do_waitid(P_PID, pid, sinfo, WCONTINUED, rusage)) |
| perror_msg_and_fail("waitid #6"); |
| tprintf("waitid(P_PID, %d, %s, WCONTINUED, %s) = 0\n", |
| pid, sprint_siginfo(sinfo, "SIGCONT"), sprint_rusage(rusage)); |
| #endif /* WCONTINUED */ |
| |
| assert(write(1, "", 1) == 1); |
| (void) close(1); |
| |
| if (do_waitid(P_PID, pid, sinfo, WEXITED, rusage)) |
| perror_msg_and_fail("waitid #7"); |
| tprintf("waitid(P_PID, %d, %s, WEXITED, %s) = 0\n", |
| pid, sprint_siginfo(sinfo, "0"), sprint_rusage(rusage)); |
| |
| long rc = do_waitid(P_ALL, -1, sinfo, WEXITED|WSTOPPED, rusage); |
| tprintf("waitid(P_ALL, -1, %p, WEXITED|WSTOPPED, %p)" |
| " = %ld %s (%m)\n", sinfo, rusage, rc, errno2name()); |
| |
| tprintf("%s\n", "+++ exited with 0 +++"); |
| return 0; |
| } |