-/* Non-zero if the kernel supports PTRACE_O_TRACEFORK. */
-static int linux_supports_tracefork_flag;
-
-static void
-linux_enable_event_reporting (int pid)
-{
- if (!linux_supports_tracefork_flag)
- return;
-
- ptrace (PTRACE_SETOPTIONS, pid, 0, (PTRACE_ARG4_TYPE) PTRACE_O_TRACECLONE);
-}
-
-/* Helper functions for linux_test_for_tracefork, called via clone (). */
-
-static int
-linux_tracefork_grandchild (void *arg)
-{
- _exit (0);
-}
-
-#define STACK_SIZE 4096
-
-static int
-linux_tracefork_child (void *arg)
-{
- ptrace (PTRACE_TRACEME, 0, 0, 0);
- kill (getpid (), SIGSTOP);
-
-#if !(defined(__UCLIBC__) && defined(HAS_NOMMU))
-
- if (fork () == 0)
- linux_tracefork_grandchild (NULL);
-
-#else /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
-
-#ifdef __ia64__
- __clone2 (linux_tracefork_grandchild, arg, STACK_SIZE,
- CLONE_VM | SIGCHLD, NULL);
-#else
- clone (linux_tracefork_grandchild, (char *) arg + STACK_SIZE,
- CLONE_VM | SIGCHLD, NULL);
-#endif
-
-#endif /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
-
- _exit (0);
-}
-
-/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events. Make
- sure that we can enable the option, and that it had the desired
- effect. */
-
-static void
-linux_test_for_tracefork (void)
-{
- int child_pid, ret, status;
- long second_pid;
-#if defined(__UCLIBC__) && defined(HAS_NOMMU)
- char *stack = xmalloc (STACK_SIZE * 4);
-#endif /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
-
- linux_supports_tracefork_flag = 0;
-
-#if !(defined(__UCLIBC__) && defined(HAS_NOMMU))
-
- child_pid = fork ();
- if (child_pid == 0)
- linux_tracefork_child (NULL);
-
-#else /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
-
- /* Use CLONE_VM instead of fork, to support uClinux (no MMU). */
-#ifdef __ia64__
- child_pid = __clone2 (linux_tracefork_child, stack, STACK_SIZE,
- CLONE_VM | SIGCHLD, stack + STACK_SIZE * 2);
-#else /* !__ia64__ */
- child_pid = clone (linux_tracefork_child, stack + STACK_SIZE,
- CLONE_VM | SIGCHLD, stack + STACK_SIZE * 2);
-#endif /* !__ia64__ */
-
-#endif /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
-
- if (child_pid == -1)
- perror_with_name ("clone");
-
- ret = my_waitpid (child_pid, &status, 0);
- if (ret == -1)
- perror_with_name ("waitpid");
- else if (ret != child_pid)
- error ("linux_test_for_tracefork: waitpid: unexpected result %d.", ret);
- if (! WIFSTOPPED (status))
- error ("linux_test_for_tracefork: waitpid: unexpected status %d.", status);
-
- ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0,
- (PTRACE_ARG4_TYPE) PTRACE_O_TRACEFORK);
- if (ret != 0)
- {
- ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
- if (ret != 0)
- {
- warning ("linux_test_for_tracefork: failed to kill child");
- return;
- }
-
- ret = my_waitpid (child_pid, &status, 0);
- if (ret != child_pid)
- warning ("linux_test_for_tracefork: failed to wait for killed child");
- else if (!WIFSIGNALED (status))
- warning ("linux_test_for_tracefork: unexpected wait status 0x%x from "
- "killed child", status);
-
- return;
- }
-
- ret = ptrace (PTRACE_CONT, child_pid, 0, 0);
- if (ret != 0)
- warning ("linux_test_for_tracefork: failed to resume child");
-
- ret = my_waitpid (child_pid, &status, 0);
-
- if (ret == child_pid && WIFSTOPPED (status)
- && status >> 16 == PTRACE_EVENT_FORK)
- {
- second_pid = 0;
- ret = ptrace (PTRACE_GETEVENTMSG, child_pid, 0, &second_pid);
- if (ret == 0 && second_pid != 0)
- {
- int second_status;
-
- linux_supports_tracefork_flag = 1;
- my_waitpid (second_pid, &second_status, 0);
- ret = ptrace (PTRACE_KILL, second_pid, 0, 0);
- if (ret != 0)
- warning ("linux_test_for_tracefork: failed to kill second child");
- my_waitpid (second_pid, &status, 0);
- }
- }
- else
- warning ("linux_test_for_tracefork: unexpected result from waitpid "
- "(%d, status 0x%x)", ret, status);
-
- do
- {
- ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
- if (ret != 0)
- warning ("linux_test_for_tracefork: failed to kill child");
- my_waitpid (child_pid, &status, 0);
- }
- while (WIFSTOPPED (status));
-
-#if defined(__UCLIBC__) && defined(HAS_NOMMU)
- free (stack);
-#endif /* defined(__UCLIBC__) && defined(HAS_NOMMU) */
-}
-
-