(root)/
glibc-2.38/
posix/
tst-wait-skeleton.c
       1  /* Test framework for wait3 and wait4.
       2     Copyright (C) 2020-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 <sys/resource.h>
      25  #include <signal.h>
      26  #include <time.h>
      27  #include <stdatomic.h>
      28  #include <stdbool.h>
      29  
      30  #include <support/xsignal.h>
      31  #include <support/xunistd.h>
      32  #include <support/check.h>
      33  #include <support/process_state.h>
      34  
      35  static void
      36  test_child (void)
      37  {
      38    /* First thing, we stop ourselves.  */
      39    raise (SIGSTOP);
      40  
      41    /* Hey, we got continued!  */
      42    while (1)
      43      pause ();
      44  }
      45  
      46  #ifndef WEXITED
      47  # define WEXITED        0
      48  # define WCONTINUED     0
      49  # define WSTOPPED       WUNTRACED
      50  #endif
      51  
      52  /* Set with only SIGCHLD on do_test_waitid.  */
      53  static sigset_t chldset;
      54  
      55  #ifdef SA_SIGINFO
      56  static void
      57  sigchld (int signo, siginfo_t *info, void *ctx)
      58  {
      59  }
      60  #endif
      61  
      62  static void
      63  check_sigchld (int code, int status, pid_t pid)
      64  {
      65  #ifdef SA_SIGINFO
      66    siginfo_t siginfo;
      67    TEST_COMPARE (sigwaitinfo (&chldset, &siginfo), SIGCHLD);
      68  
      69    TEST_COMPARE (siginfo.si_signo, SIGCHLD);
      70    TEST_COMPARE (siginfo.si_code, code);
      71    TEST_COMPARE (siginfo.si_status, status);
      72    TEST_COMPARE (siginfo.si_pid, pid);
      73  #endif
      74  }
      75  
      76  static int
      77  do_test_wait (pid_t pid)
      78  {
      79    /* Adding process_state_tracing_stop ('t') allows the test to work under
      80       trace programs such as ptrace.  */
      81    enum support_process_state stop_state = support_process_state_stopped
      82                                            | support_process_state_tracing_stop;
      83  
      84    support_process_state_wait (pid, stop_state);
      85  
      86    check_sigchld (CLD_STOPPED, SIGSTOP, pid);
      87  
      88    pid_t ret;
      89    int wstatus;
      90    struct rusage rusage;
      91  
      92    ret = WAIT_CALL (pid, &wstatus, WUNTRACED|WCONTINUED|WNOHANG, NULL);
      93    if (ret == -1 && errno == ENOTSUP)
      94      FAIL_RET ("waitid WNOHANG on stopped: %m");
      95    TEST_COMPARE (ret, pid);
      96    TEST_VERIFY (WIFSTOPPED (wstatus));
      97  
      98    /* Issue again but with struct rusage input.  */
      99    ret = WAIT_CALL (pid, &wstatus, WUNTRACED|WCONTINUED|WNOHANG, &rusage);
     100    /* With WNOHANG and WUNTRACED, if the children has not changes its state
     101       since previous call the expected result it 0.  */
     102    TEST_COMPARE (ret, 0);
     103  
     104    /* Some sanity tests to check if 'wtatus' and 'rusage' possible
     105       input values.  */
     106    ret = WAIT_CALL (pid, NULL, WUNTRACED|WCONTINUED|WNOHANG, &rusage);
     107    TEST_COMPARE (ret, 0);
     108    ret = WAIT_CALL (pid, NULL, WUNTRACED|WCONTINUED|WNOHANG, NULL);
     109    TEST_COMPARE (ret, 0);
     110  
     111    if (kill (pid, SIGCONT) != 0)
     112      FAIL_RET ("kill (%d, SIGCONT): %m\n", pid);
     113  
     114    /* Wait for the child to have continued.  */
     115    support_process_state_wait (pid, support_process_state_sleeping);
     116  
     117  #if WCONTINUED != 0
     118    check_sigchld (CLD_CONTINUED, SIGCONT, pid);
     119  
     120    ret = WAIT_CALL (pid, &wstatus, WCONTINUED|WNOHANG, NULL);
     121    TEST_COMPARE (ret, pid);
     122    TEST_VERIFY (WIFCONTINUED (wstatus));
     123  
     124    /* Issue again but with struct rusage input.  */
     125    ret = WAIT_CALL (pid, &wstatus, WUNTRACED|WCONTINUED|WNOHANG, &rusage);
     126    /* With WNOHANG and WUNTRACED, if the children has not changes its state
     127       since previous call the expected result it 0.  */
     128    TEST_COMPARE (ret, 0);
     129  
     130    /* Now stop him again and test waitpid with WCONTINUED.  */
     131    if (kill (pid, SIGSTOP) != 0)
     132      FAIL_RET ("kill (%d, SIGSTOP): %m\n", pid);
     133  
     134    /* Wait the child stop.  The waitid call below will block until it has
     135       stopped, but if we are real quick and enter the waitid system call
     136       before the SIGCHLD has been generated, then it will be discarded and
     137       never delivered.  */
     138    support_process_state_wait (pid, stop_state);
     139  
     140    ret = WAIT_CALL (pid, &wstatus, WUNTRACED|WNOHANG, &rusage);
     141    TEST_COMPARE (ret, pid);
     142  
     143    check_sigchld (CLD_STOPPED, SIGSTOP, pid);
     144  
     145    if (kill (pid, SIGCONT) != 0)
     146      FAIL_RET ("kill (%d, SIGCONT): %m\n", pid);
     147  
     148    /* Wait for the child to have continued.  */
     149    support_process_state_wait (pid, support_process_state_sleeping);
     150  
     151    check_sigchld (CLD_CONTINUED, SIGCONT, pid);
     152  
     153    ret = WAIT_CALL (pid, &wstatus, WCONTINUED|WNOHANG, NULL);
     154    TEST_COMPARE (ret, pid);
     155    TEST_VERIFY (WIFCONTINUED (wstatus));
     156  #endif
     157  
     158    /* Die, child, die!  */
     159    if (kill (pid, SIGKILL) != 0)
     160      FAIL_RET ("kill (%d, SIGKILL): %m\n", pid);
     161  
     162    support_process_state_wait (pid, support_process_state_zombie);
     163  
     164    ret = WAIT_CALL (pid, &wstatus, 0, &rusage);
     165    TEST_COMPARE (ret, pid);
     166    TEST_VERIFY (WIFSIGNALED (wstatus));
     167    TEST_VERIFY (WTERMSIG (wstatus) == SIGKILL);
     168  
     169    check_sigchld (CLD_KILLED, SIGKILL, pid);
     170  
     171    return 0;
     172  }
     173  
     174  static int
     175  do_test (void)
     176  {
     177  #ifdef SA_SIGINFO
     178    {
     179      struct sigaction sa;
     180      sa.sa_flags = SA_SIGINFO | SA_RESTART;
     181      sa.sa_sigaction = sigchld;
     182      sigemptyset (&sa.sa_mask);
     183      xsigaction (SIGCHLD, &sa, NULL);
     184    }
     185  #endif
     186  
     187    sigemptyset (&chldset);
     188    sigaddset (&chldset, SIGCHLD);
     189  
     190    /* The SIGCHLD shall has blocked at the time of the call to sigwait;
     191       otherwise, the behavior is undefined.  */
     192    sigprocmask (SIG_BLOCK, &chldset, NULL);
     193  
     194    pid_t pid = xfork ();
     195    if (pid == 0)
     196      {
     197        test_child ();
     198        _exit (127);
     199      }
     200  
     201    do_test_wait (pid);
     202  
     203    xsignal (SIGCHLD, SIG_IGN);
     204    kill (pid, SIGKILL);          /* Make sure it's dead if we bailed early.  */
     205  
     206    return 0;
     207  }
     208  
     209  #include <support/test-driver.c>