(root)/
glibc-2.38/
posix/
tst-waitid.c
       1  /* Tests for waitid.
       2     Copyright (C) 2004-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <errno.h>
      20  #include <stdio.h>
      21  #include <stdlib.h>
      22  #include <unistd.h>
      23  #include <sys/wait.h>
      24  #include <signal.h>
      25  #include <time.h>
      26  #include <stdatomic.h>
      27  #include <stdbool.h>
      28  
      29  #include <support/xsignal.h>
      30  #include <support/xunistd.h>
      31  #include <support/check.h>
      32  #include <support/process_state.h>
      33  
      34  static void
      35  test_child (bool setgroup)
      36  {
      37    if (setgroup)
      38      TEST_COMPARE (setpgid (0, 0), 0);
      39  
      40    /* First thing, we stop ourselves.  */
      41    raise (SIGSTOP);
      42  
      43    /* Hey, we got continued!  */
      44    while (1)
      45      pause ();
      46  }
      47  
      48  #ifndef WEXITED
      49  # define WEXITED	0
      50  # define WCONTINUED	0
      51  # define WSTOPPED	WUNTRACED
      52  #endif
      53  
      54  /* Set with only SIGCHLD on do_test_waitid.  */
      55  static sigset_t chldset;
      56  
      57  #ifdef SA_SIGINFO
      58  static void
      59  sigchld (int signo, siginfo_t *info, void *ctx)
      60  {
      61  }
      62  #endif
      63  
      64  static void
      65  check_sigchld (int code, int status, pid_t pid)
      66  {
      67  #ifdef SA_SIGINFO
      68    siginfo_t siginfo;
      69    TEST_COMPARE (sigwaitinfo (&chldset, &siginfo), SIGCHLD);
      70  
      71    TEST_COMPARE (siginfo.si_signo, SIGCHLD);
      72    TEST_COMPARE (siginfo.si_code, code);
      73    TEST_COMPARE (siginfo.si_status, status);
      74    TEST_COMPARE (siginfo.si_pid, pid);
      75  #endif
      76  }
      77  
      78  static int
      79  do_test_waitd_common (idtype_t type, pid_t pid)
      80  {
      81    /* Adding process_state_tracing_stop ('t') allows the test to work under
      82       trace programs such as ptrace.  */
      83    enum support_process_state stop_state = support_process_state_stopped
      84  					  | support_process_state_tracing_stop;
      85  
      86    support_process_state_wait (pid, stop_state);
      87  
      88    check_sigchld (CLD_STOPPED, SIGSTOP, pid);
      89  
      90    /* Now try a wait that should not succeed.  */
      91    siginfo_t info;
      92    int fail;
      93  
      94    info.si_signo = 0;		/* A successful call sets it to SIGCHLD.  */
      95    fail = waitid (P_PID, pid, &info, WEXITED|WCONTINUED|WNOHANG);
      96    if (fail == -1 && errno == ENOTSUP)
      97      FAIL_RET ("waitid WNOHANG on stopped: %m");
      98    TEST_COMPARE (fail, 0);
      99    TEST_COMPARE (info.si_signo, 0);
     100  
     101    /* Next the wait that should succeed right away.  */
     102    info.si_signo = 0;		/* A successful call sets it to SIGCHLD.  */
     103    info.si_pid = -1;
     104    info.si_status = -1;
     105    fail = waitid (P_PID, pid, &info, WSTOPPED|WNOHANG);
     106    if (fail == -1 && errno == ENOTSUP)
     107      FAIL_RET ("waitid WNOHANG on stopped: %m");
     108    TEST_COMPARE (fail, 0);
     109    TEST_COMPARE (info.si_signo, SIGCHLD);
     110    TEST_COMPARE (info.si_code, CLD_STOPPED);
     111    TEST_COMPARE (info.si_status, SIGSTOP);
     112    TEST_COMPARE (info.si_pid, pid);
     113  
     114    if (kill (pid, SIGCONT) != 0)
     115      FAIL_RET ("kill (%d, SIGCONT): %m\n", pid);
     116  
     117    /* Wait for the child to have continued.  */
     118    support_process_state_wait (pid, support_process_state_sleeping);
     119  
     120  #if WCONTINUED != 0
     121    check_sigchld (CLD_CONTINUED, SIGCONT, pid);
     122  
     123    info.si_signo = 0;		/* A successful call sets it to SIGCHLD.  */
     124    info.si_pid = -1;
     125    info.si_status = -1;
     126    fail = waitid (P_PID, pid, &info, WCONTINUED|WNOWAIT);
     127    if (fail == -1 && errno == ENOTSUP)
     128      FAIL_RET ("waitid WCONTINUED|WNOWAIT on continued: %m");
     129    TEST_COMPARE (fail, 0);
     130    TEST_COMPARE (info.si_signo, SIGCHLD);
     131    TEST_COMPARE (info.si_code, CLD_CONTINUED);
     132    TEST_COMPARE (info.si_status, SIGCONT);
     133    TEST_COMPARE (info.si_pid, pid);
     134  
     135    /* That should leave the CLD_CONTINUED state waiting to be seen again.  */
     136    info.si_signo = 0;		/* A successful call sets it to SIGCHLD.  */
     137    info.si_pid = -1;
     138    info.si_status = -1;
     139    fail = waitid (P_PID, pid, &info, WCONTINUED);
     140    if (fail == -1 && errno == ENOTSUP)
     141      FAIL_RET ("waitid WCONTINUED on continued: %m");
     142    TEST_COMPARE (fail, 0);
     143    TEST_COMPARE (info.si_signo, SIGCHLD);
     144    TEST_COMPARE (info.si_code, CLD_CONTINUED);
     145    TEST_COMPARE (info.si_status, SIGCONT);
     146    TEST_COMPARE (info.si_pid, pid);
     147  
     148    /* Now try a wait that should not succeed.  */
     149    info.si_signo = 0;		/* A successful call sets it to SIGCHLD.  */
     150    fail = waitid (P_PID, pid, &info, WCONTINUED|WNOHANG);
     151    if (fail == -1 && errno == ENOTSUP)
     152      FAIL_RET ("waitid WCONTINUED|WNOHANG on waited continued: %m");
     153    TEST_COMPARE (fail, 0);
     154    TEST_COMPARE (info.si_signo, 0);
     155  
     156    /* Now stop him again and test waitpid with WCONTINUED.  */
     157    if (kill (pid, SIGSTOP) != 0)
     158      FAIL_RET ("kill (%d, SIGSTOP): %m\n", pid);
     159  
     160    /* Wait the child stop.  The waitid call below will block until it has
     161       stopped, but if we are real quick and enter the waitid system call
     162       before the SIGCHLD has been generated, then it will be discarded and
     163       never delivered.  */
     164    support_process_state_wait (pid, stop_state);
     165  
     166    fail = waitid (type, pid, &info, WEXITED|WSTOPPED);
     167    TEST_COMPARE (fail, 0);
     168    TEST_COMPARE (info.si_signo, SIGCHLD);
     169    TEST_COMPARE (info.si_code, CLD_STOPPED);
     170    TEST_COMPARE (info.si_status, SIGSTOP);
     171    TEST_COMPARE (info.si_pid, pid);
     172  
     173    check_sigchld (CLD_STOPPED, SIGSTOP, pid);
     174  
     175    if (kill (pid, SIGCONT) != 0)
     176      FAIL_RET ("kill (%d, SIGCONT): %m\n", pid);
     177  
     178    /* Wait for the child to have continued.  */
     179    support_process_state_wait (pid, support_process_state_sleeping);
     180  
     181    check_sigchld (CLD_CONTINUED, SIGCONT, pid);
     182  
     183    fail = waitid (type, pid, &info, WCONTINUED);
     184    TEST_COMPARE (fail, 0);
     185    TEST_COMPARE (info.si_signo, SIGCHLD);
     186    TEST_COMPARE (info.si_code, CLD_CONTINUED);
     187    TEST_COMPARE (info.si_status, SIGCONT);
     188    TEST_COMPARE (info.si_pid, pid);
     189  #endif
     190  
     191    /* Die, child, die!  */
     192    if (kill (pid, SIGKILL) != 0)
     193      FAIL_RET ("kill (%d, SIGKILL): %m\n", pid);
     194  
     195  #ifdef WNOWAIT
     196    info.si_signo = 0;		/* A successful call sets it to SIGCHLD.  */
     197    info.si_pid = -1;
     198    info.si_status = -1;
     199    fail = waitid (type, pid, &info, WEXITED|WNOWAIT);
     200    if (fail == -1 && errno == ENOTSUP)
     201      FAIL_RET ("waitid WNOHANG on killed: %m");
     202    TEST_COMPARE (fail, 0);
     203    TEST_COMPARE (info.si_signo, SIGCHLD);
     204    TEST_COMPARE (info.si_code, CLD_KILLED);
     205    TEST_COMPARE (info.si_status, SIGKILL);
     206    TEST_COMPARE (info.si_pid, pid);
     207  #else
     208    support_process_state_wait (pid, support_process_state_zombie);
     209  #endif
     210    check_sigchld (CLD_KILLED, SIGKILL, pid);
     211  
     212    info.si_signo = 0;		/* A successful call sets it to SIGCHLD.  */
     213    info.si_pid = -1;
     214    info.si_status = -1;
     215    fail = waitid (type, pid, &info, WEXITED | WNOHANG);
     216    TEST_COMPARE (fail, 0);
     217    TEST_COMPARE (info.si_signo, SIGCHLD);
     218    TEST_COMPARE (info.si_code, CLD_KILLED);
     219    TEST_COMPARE (info.si_status, SIGKILL);
     220    TEST_COMPARE (info.si_pid, pid);
     221  
     222    fail = waitid (P_PID, pid, &info, WEXITED);
     223    TEST_COMPARE (fail, -1);
     224    TEST_COMPARE (errno, ECHILD);
     225  
     226    return 0;
     227  }
     228  
     229  static int
     230  do_test_waitid (idtype_t type)
     231  {
     232  #ifdef SA_SIGINFO
     233    {
     234      struct sigaction sa;
     235      sa.sa_flags = SA_SIGINFO | SA_RESTART;
     236      sa.sa_sigaction = sigchld;
     237      sigemptyset (&sa.sa_mask);
     238      xsigaction (SIGCHLD, &sa, NULL);
     239    }
     240  #endif
     241  
     242    sigemptyset (&chldset);
     243    sigaddset (&chldset, SIGCHLD);
     244  
     245    /* The SIGCHLD shall has blocked at the time of the call to sigwait;
     246       otherwise, the behavior is undefined.  */
     247    sigprocmask (SIG_BLOCK, &chldset, NULL);
     248  
     249    pid_t pid = xfork ();
     250    if (pid == 0)
     251      {
     252        test_child (type == P_PGID || type == P_ALL);
     253        _exit (127);
     254      }
     255  
     256    int ret = do_test_waitd_common (type, pid);
     257  
     258    xsignal (SIGCHLD, SIG_IGN);
     259    kill (pid, SIGKILL);		/* Make sure it's dead if we bailed early.  */
     260    return ret;
     261  }
     262  
     263  static int
     264  do_test (void)
     265  {
     266    int ret = 0;
     267  
     268    ret |= do_test_waitid (P_PID);
     269    ret |= do_test_waitid (P_PGID);
     270    ret |= do_test_waitid (P_ALL);
     271  
     272    return ret;
     273  }
     274  
     275  #include <support/test-driver.c>