| /* |
| * Spawn a child and set it up for ptrace()-ing |
| * |
| * Copyright (c) 2008 Analog Devices Inc. |
| * |
| * Licensed under the GPL-2 or later |
| */ |
| |
| /* |
| * To use: |
| * - add this line after your normal includes: |
| * #include "spawn_ptrace_child.c" |
| * - add this line to the top of your main(): |
| * make_a_baby(argc, argv); |
| * - access the child pid via the "pid" variable |
| */ |
| |
| #include <errno.h> /* errno */ |
| #include <signal.h> /* signal() */ |
| #include <stdbool.h> /* true */ |
| #include <string.h> /* strcmp() */ |
| #include <unistd.h> /* execlp() sleep() vfork() */ |
| #include <sys/ptrace.h> /* ptrace() */ |
| #include <sys/wait.h> |
| |
| #include "test.h" |
| |
| static pid_t pid; |
| |
| #ifdef __sparc__ |
| /* sparce swaps addr/data for get/set regs */ |
| # define maybe_swap(request, addr, data) \ |
| do { \ |
| if (request == PTRACE_GETREGS || request == PTRACE_SETREGS) { \ |
| void *__s = addr; \ |
| addr = data; \ |
| data = __s; \ |
| } \ |
| } while (0) |
| #else |
| # define maybe_swap(...) |
| #endif |
| #define vptrace(request, pid, addr, data) \ |
| ({ \ |
| errno = 0; \ |
| long __ret; \ |
| void *__addr = (void *)(addr); \ |
| void *__data = (void *)(data); \ |
| maybe_swap(request, __addr, __data); \ |
| __ret = ptrace(request, pid, __addr, __data); \ |
| if (__ret && errno) \ |
| perror("ptrace(" #request ", " #pid ", " #addr ", " #data ")"); \ |
| __ret; \ |
| }) |
| |
| static void make_a_baby(int argc, char *argv[]) |
| { |
| if (argc > 1 && !strcmp(argv[1], "child")) { |
| /* if we're the child, just sit around doing nothing */ |
| int i = 60; |
| while (i--) { |
| close(-100); |
| sleep(1); |
| } |
| exit(1); |
| } |
| |
| signal(SIGCHLD, SIG_IGN); |
| |
| pid = vfork(); |
| if (pid == -1) { |
| tst_resm(TFAIL, "vfork() failed"); |
| tst_exit(); |
| } else if (pid) { |
| int status; |
| |
| if (wait(&status) != pid) { |
| tst_brkm(TBROK | TERRNO, NULL, "wait(%i) failed: %#x", pid, status); |
| kill(pid, SIGKILL); |
| exit(1); |
| } |
| if (!WIFSTOPPED(status)) { |
| tst_brkm(TBROK, NULL, "child status not stopped: %#x", status); |
| kill(pid, SIGKILL); |
| exit(1); |
| } |
| |
| return; |
| } |
| |
| errno = 0; |
| long ret = ptrace(PTRACE_TRACEME, 0, NULL, NULL); |
| if (ret && errno) { |
| tst_resm(TFAIL, "PTRACE_TRACEME failed"); |
| tst_exit(); |
| } |
| |
| execlp(argv[0], argv[0], "child", NULL); |
| tst_resm(TFAIL, "execlp() failed"); |
| tst_exit(); |
| } |
| |
| #define SPT(x) [PTRACE_##x] = #x, |
| static char *strings[] = { |
| SPT(TRACEME) |
| SPT(PEEKTEXT) |
| SPT(PEEKDATA) |
| SPT(PEEKUSER) |
| SPT(POKETEXT) |
| SPT(POKEDATA) |
| SPT(POKEUSER) |
| #ifdef PTRACE_GETREGS |
| SPT(GETREGS) |
| #endif |
| #ifdef PTRACE_SETREGS |
| SPT(SETREGS) |
| #endif |
| #ifdef PTRACE_GETSIGINFO |
| SPT(GETSIGINFO) |
| #endif |
| #ifdef PTRACE_SETSIGINFO |
| SPT(SETSIGINFO) |
| #endif |
| #ifdef PTRACE_GETFGREGS |
| SPT(GETFGREGS) |
| #endif |
| #ifdef PTRACE_SETFGREGS |
| SPT(SETFGREGS) |
| #endif |
| SPT(KILL) |
| SPT(SINGLESTEP) |
| }; |
| static inline char *strptrace(enum __ptrace_request request) |
| { |
| return strings[request]; |
| } |