e354a5
commit dfe9aa91564c1bf2a23b5589a5db42f9da5d29b5
e354a5
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
e354a5
Date:   Tue Nov 19 17:17:05 2019 -0300
e354a5
e354a5
    support: Add support_process_state_wait
e354a5
    
e354a5
    It allows parent process to wait for child state using a polling
e354a5
    strategy over procfs on Linux.  The polling is used over ptrace to
e354a5
    avoid the need to handle signals on the target pid and to handle some
e354a5
    system specific limitation (such as YAMA).
e354a5
    
e354a5
    The polling has some limitations, such as resource consumption due
e354a5
    the procfs read in a loop and the lack of synchronization after the
e354a5
    state is obtained.
e354a5
    
e354a5
    The interface idea is to simplify some sleep synchronization waitid
e354a5
    tests and is to reduce timeouts by replacing arbitrary waits.
e354a5
e354a5
diff --git a/support/Makefile b/support/Makefile
e354a5
index 79d03bd6bfe02540..117cfdd4f22fc405 100644
e354a5
--- a/support/Makefile
e354a5
+++ b/support/Makefile
e354a5
@@ -58,6 +58,7 @@ libsupport-routines = \
e354a5
   support_format_hostent \
e354a5
   support_format_netent \
e354a5
   support_isolate_in_subprocess \
e354a5
+  support_process_state \
e354a5
   support_ptrace \
e354a5
   support_openpty \
e354a5
   support_paths \
e354a5
@@ -90,6 +91,7 @@ libsupport-routines = \
e354a5
   xfopen \
e354a5
   xfork \
e354a5
   xftruncate \
e354a5
+  xgetline \
e354a5
   xgetsockname \
e354a5
   xlisten \
e354a5
   xlseek \
e354a5
@@ -217,6 +219,7 @@ tests = \
e354a5
   tst-support_capture_subprocess \
e354a5
   tst-support_descriptors \
e354a5
   tst-support_format_dns_packet \
e354a5
+  tst-support-process_state \
e354a5
   tst-support_quote_blob \
e354a5
   tst-support_quote_string \
e354a5
   tst-support_record_failure \
e354a5
diff --git a/support/process_state.h b/support/process_state.h
e354a5
new file mode 100644
e354a5
index 0000000000000000..6c19afdbb7462277
e354a5
--- /dev/null
e354a5
+++ b/support/process_state.h
e354a5
@@ -0,0 +1,43 @@
e354a5
+/* Wait for process state.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#ifndef SUPPORT_PROCESS_STATE_H
e354a5
+#define SUPPORT_PROCESS_STATE_H
e354a5
+
e354a5
+#include <sys/types.h>
e354a5
+
e354a5
+enum support_process_state
e354a5
+{
e354a5
+  support_process_state_running      = 0x01,  /* R (running).  */
e354a5
+  support_process_state_sleeping     = 0x02,  /* S (sleeping).  */
e354a5
+  support_process_state_disk_sleep   = 0x04,  /* D (disk sleep).  */
e354a5
+  support_process_state_stopped      = 0x08,  /* T (stopped).  */
e354a5
+  support_process_state_tracing_stop = 0x10,  /* t (tracing stop).  */
e354a5
+  support_process_state_dead         = 0x20,  /* X (dead).  */
e354a5
+  support_process_state_zombie       = 0x40,  /* Z (zombie).  */
e354a5
+  support_process_state_parked       = 0x80,  /* P (parked).  */
e354a5
+};
e354a5
+
e354a5
+/* Wait for process PID to reach state STATE.  It can be a combination of
e354a5
+   multiple possible states ('process_state_running | process_state_sleeping')
e354a5
+   where the function return when any of these state are observed.
e354a5
+   For an invalid state not represented by SUPPORT_PROCESS_STATE, it fallbacks
e354a5
+   to a 2 second sleep.  */
e354a5
+void support_process_state_wait (pid_t pid, enum support_process_state state);
e354a5
+
e354a5
+#endif
e354a5
diff --git a/support/support_process_state.c b/support/support_process_state.c
e354a5
new file mode 100644
e354a5
index 0000000000000000..76dc798728ece0d9
e354a5
--- /dev/null
e354a5
+++ b/support/support_process_state.c
e354a5
@@ -0,0 +1,92 @@
e354a5
+/* Wait for process state.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#include <stdlib.h>
e354a5
+#include <time.h>
e354a5
+#include <string.h>
e354a5
+
e354a5
+#include <array_length.h>
e354a5
+
e354a5
+#include <support/process_state.h>
e354a5
+#include <support/xstdio.h>
e354a5
+#include <support/check.h>
e354a5
+
e354a5
+void
e354a5
+support_process_state_wait (pid_t pid, enum support_process_state state)
e354a5
+{
e354a5
+#ifdef __linux__
e354a5
+  /* For Linux it does a polling check on /proc/<pid>/status checking on
e354a5
+     third field.  */
e354a5
+
e354a5
+  /* It mimics the kernel states from fs/proc/array.c  */
e354a5
+  static const struct process_states
e354a5
+  {
e354a5
+    enum support_process_state s;
e354a5
+    char v;
e354a5
+  } process_states[] = {
e354a5
+    { support_process_state_running,      'R' },
e354a5
+    { support_process_state_sleeping,     'S' },
e354a5
+    { support_process_state_disk_sleep,   'D' },
e354a5
+    { support_process_state_stopped,      'T' },
e354a5
+    { support_process_state_tracing_stop, 't' },
e354a5
+    { support_process_state_dead,         'X' },
e354a5
+    { support_process_state_zombie,       'Z' },
e354a5
+    { support_process_state_parked,       'P' },
e354a5
+  };
e354a5
+
e354a5
+  char spath[sizeof ("/proc/" + 3) * sizeof (pid_t) + sizeof ("/status") + 1];
e354a5
+  snprintf (spath, sizeof (spath), "/proc/%i/status", pid);
e354a5
+
e354a5
+  FILE *fstatus = xfopen (spath, "r");
e354a5
+  char *line = NULL;
e354a5
+  size_t linesiz = 0;
e354a5
+
e354a5
+  for (;;)
e354a5
+    {
e354a5
+      char cur_state = -1;
e354a5
+      while (xgetline (&line, &linesiz, fstatus) != -1)
e354a5
+	if (strncmp (line, "State:", strlen ("State:")) == 0)
e354a5
+	  {
e354a5
+	    TEST_COMPARE (sscanf (line, "%*s %c", &cur_state), 1);
e354a5
+	    break;
e354a5
+	  }
e354a5
+      /* Fallback to nanosleep for invalid state.  */
e354a5
+      if (cur_state == -1)
e354a5
+	break;
e354a5
+
e354a5
+      for (size_t i = 0; i < array_length (process_states); ++i)
e354a5
+	if (state & process_states[i].s && cur_state == process_states[i].v)
e354a5
+	  {
e354a5
+	    free (line);
e354a5
+	    xfclose (fstatus);
e354a5
+	    return;
e354a5
+	  }
e354a5
+
e354a5
+      rewind (fstatus);
e354a5
+      fflush (fstatus);
e354a5
+
e354a5
+      if (nanosleep (&(struct timespec) { 0, 10000000 }, NULL) != 0)
e354a5
+	FAIL_EXIT1 ("nanosleep: %m");
e354a5
+    }
e354a5
+
e354a5
+  free (line);
e354a5
+  xfclose (fstatus);
e354a5
+  /* Fallback to nanosleep if an invalid state is found.  */
e354a5
+#endif
e354a5
+  nanosleep (&(struct timespec) { 2, 0 }, NULL);
e354a5
+}
e354a5
diff --git a/support/tst-support-process_state.c b/support/tst-support-process_state.c
e354a5
new file mode 100644
e354a5
index 0000000000000000..3fc103ab9205ddb0
e354a5
--- /dev/null
e354a5
+++ b/support/tst-support-process_state.c
e354a5
@@ -0,0 +1,105 @@
e354a5
+/* Wait for process state tests.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#include <stdio.h>
e354a5
+#include <sys/wait.h>
e354a5
+#include <unistd.h>
e354a5
+#include <errno.h>
e354a5
+
e354a5
+#include <support/test-driver.h>
e354a5
+#include <support/process_state.h>
e354a5
+#include <support/check.h>
e354a5
+#include <support/xsignal.h>
e354a5
+#include <support/xunistd.h>
e354a5
+
e354a5
+#ifndef WEXITED
e354a5
+# define WEXITED	0
e354a5
+#endif
e354a5
+
e354a5
+static void
e354a5
+sigusr1_handler (int signo)
e354a5
+{
e354a5
+}
e354a5
+
e354a5
+static void
e354a5
+test_child (void)
e354a5
+{
e354a5
+  xsignal (SIGUSR1, sigusr1_handler);
e354a5
+
e354a5
+  raise (SIGSTOP);
e354a5
+
e354a5
+  TEST_COMPARE (pause (), -1);
e354a5
+  TEST_COMPARE (errno, EINTR);
e354a5
+
e354a5
+  while (1)
e354a5
+    asm ("");
e354a5
+}
e354a5
+
e354a5
+static int
e354a5
+do_test (void)
e354a5
+{
e354a5
+  pid_t pid = xfork ();
e354a5
+  if (pid == 0)
e354a5
+    {
e354a5
+      test_child ();
e354a5
+      _exit (127);
e354a5
+    }
e354a5
+
e354a5
+  /* Adding process_state_tracing_stop ('t') allows the test to work under
e354a5
+     trace programs such as ptrace.  */
e354a5
+  enum support_process_state stop_state = support_process_state_stopped
e354a5
+				    | support_process_state_tracing_stop;
e354a5
+
e354a5
+  if (test_verbose)
e354a5
+    printf ("info: waiting pid %d, state_stopped/state_tracing_stop\n",
e354a5
+	    (int) pid);
e354a5
+  support_process_state_wait (pid, stop_state);
e354a5
+
e354a5
+  if (kill (pid, SIGCONT) != 0)
e354a5
+    FAIL_RET ("kill (%d, SIGCONT): %m\n", pid);
e354a5
+
e354a5
+  if (test_verbose)
e354a5
+    printf ("info: waiting pid %d, state_sleeping\n", (int) pid);
e354a5
+  support_process_state_wait (pid, support_process_state_sleeping);
e354a5
+
e354a5
+  if (kill (pid, SIGUSR1) != 0)
e354a5
+    FAIL_RET ("kill (%d, SIGUSR1): %m\n", pid);
e354a5
+
e354a5
+  if (test_verbose)
e354a5
+    printf ("info: waiting pid %d, state_running\n", (int) pid);
e354a5
+  support_process_state_wait (pid, support_process_state_running);
e354a5
+
e354a5
+  if (kill (pid, SIGKILL) != 0)
e354a5
+    FAIL_RET ("kill (%d, SIGKILL): %m\n", pid);
e354a5
+
e354a5
+  if (test_verbose)
e354a5
+    printf ("info: waiting pid %d, state_zombie\n", (int) pid);
e354a5
+  support_process_state_wait (pid, support_process_state_zombie);
e354a5
+
e354a5
+  siginfo_t info;
e354a5
+  int r = waitid (P_PID, pid, &info, WEXITED);
e354a5
+  TEST_COMPARE (r, 0);
e354a5
+  TEST_COMPARE (info.si_signo, SIGCHLD);
e354a5
+  TEST_COMPARE (info.si_code, CLD_KILLED);
e354a5
+  TEST_COMPARE (info.si_status, SIGKILL);
e354a5
+  TEST_COMPARE (info.si_pid, pid);
e354a5
+
e354a5
+  return 0;
e354a5
+}
e354a5
+
e354a5
+#include <support/test-driver.c>
e354a5
diff --git a/support/xgetline.c b/support/xgetline.c
e354a5
new file mode 100644
e354a5
index 0000000000000000..180bc2db95a9c5d4
e354a5
--- /dev/null
e354a5
+++ b/support/xgetline.c
e354a5
@@ -0,0 +1,33 @@
e354a5
+/* fopen with error checking.
e354a5
+   Copyright (C) 2020 Free Software Foundation, Inc.
e354a5
+   This file is part of the GNU C Library.
e354a5
+
e354a5
+   The GNU C Library is free software; you can redistribute it and/or
e354a5
+   modify it under the terms of the GNU Lesser General Public
e354a5
+   License as published by the Free Software Foundation; either
e354a5
+   version 2.1 of the License, or (at your option) any later version.
e354a5
+
e354a5
+   The GNU C Library is distributed in the hope that it will be useful,
e354a5
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
e354a5
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
e354a5
+   Lesser General Public License for more details.
e354a5
+
e354a5
+   You should have received a copy of the GNU Lesser General Public
e354a5
+   License along with the GNU C Library; if not, see
e354a5
+   <https://www.gnu.org/licenses/>.  */
e354a5
+
e354a5
+#include <support/xstdio.h>
e354a5
+#include <support/check.h>
e354a5
+#include <errno.h>
e354a5
+
e354a5
+ssize_t
e354a5
+xgetline (char **lineptr, size_t *n, FILE *stream)
e354a5
+{
e354a5
+  int old_errno = errno;
e354a5
+  errno = 0;
e354a5
+  size_t ret = getline (lineptr, n, stream);
e354a5
+  if (!feof (stream) && ferror (stream))
e354a5
+    FAIL_EXIT1 ("getline failed: %m");
e354a5
+  errno = old_errno;
e354a5
+  return ret;
e354a5
+}
e354a5
diff --git a/support/xstdio.h b/support/xstdio.h
e354a5
index e7d0274474706380..9446b1f27b0f881e 100644
e354a5
--- a/support/xstdio.h
e354a5
+++ b/support/xstdio.h
e354a5
@@ -27,6 +27,8 @@ __BEGIN_DECLS
e354a5
 FILE *xfopen (const char *path, const char *mode);
e354a5
 void xfclose (FILE *);
e354a5
 
e354a5
+ssize_t xgetline (char **lineptr, size_t *n, FILE *stream);
e354a5
+
e354a5
 __END_DECLS
e354a5
 
e354a5
 #endif /* SUPPORT_XSTDIO_H */