#include #include #include #include #include #include #include #include #include #include #include #define printf_deb printf static int create_tracer(pid_t pid); static void *tracer_routine(void *arg); int main(int argc, char *argv[]) { int pid, ret, status; /* CREATE THE CHILDREN */ switch(pid = fork()) { case -1: perror("first process fork() failed"); return 1; case 0: /* child */ printf_deb("child: pid=%d\n", getpid()); if(ptrace(PTRACE_TRACEME, 0, 0, 0) != 0){ perror("first ptrace() failed"); return 1; } printf_deb("child: self sigstopping\n"); ret = kill(getpid(), SIGSTOP); assert(ret == 0); printf_deb("child: going execvp\n"); execvp(argv[0], argv); perror("execvp() failed"); return 1; default: /* father */ printf_deb("father: pid=%d\n", getpid()); printf_deb("father: waiting child's selfstopping\n"); if(waitpid(pid, &status, WUNTRACED) != pid) { perror("waitpid() for first child's stop failed"); return 1; } printf_deb("father: making child go again under ptrace\n"); break; } /* CREATE TRACER AND PASS PTRACE STATUS */ if((ret = create_tracer(pid)) != 0) return ret; return 0; } /*! Structure used to pass data to the tracer thread so that it can * cooperate on creation. */ struct tracer_creation_data { /*! Semaphore used to know when the tracer has finished * initialization */ sem_t s; /*! PID of the process which is going to be managed */ pid_t pid; }; /*! Creates a tracer. * * tracer creation is made of two parts: thread creation, and ptrace * status passing. In the second one, current thread detach the process while * stopping it at the same time, and then the tracer attach to the * process, and awakes it. * * \param pid The PID of the process for which the tracer is * created. * \return An error code, or 0 */ static int create_tracer(pid_t pid) { pthread_t ga; struct tracer_creation_data tracer_cd; /* create the new thread in detached state (we don't care about thread * termination, they care about themselves) */ tracer_cd.pid = pid; if(sem_init(&tracer_cd.s, 0, 0) != 0) { perror("semaphore creation failed"); return 1; } /* detach process and stop it, and then tell the tracer he can * attach */ printf_deb("father: detaching\n"); if(ptrace(PTRACE_DETACH, pid, 0, SIGSTOP) != 0) { printf_deb("father: error while detaching %d\n", pid); perror("child detach failed"); return 1; } printf_deb("father: thread creation\n"); if((errno = pthread_create(&ga, NULL, tracer_routine, &tracer_cd)) != 0) { perror("tracer thread creation failed"); return 1; } /* wait for the tracer to be ready */ printf_deb("father: waiting for end\n"); if(sem_wait(&tracer_cd.s) != 0) { perror("dispatcher mutex problem"); return 1; } /* destroy semaphores */ printf_deb("father: destroying semaphore\n"); if(sem_destroy(&tracer_cd.s) != 0) { perror("failed to complete tracer creation\n"); return 1; } printf_deb("father: all done\n"); return 0; } /*! Routine common to all tracers. * * Cooperate with create_tracer to complete ptrace passing process, * then waits for the dispatching of signals and manage them. * * \param arg A struct tracer_creation_data which is used to complete * startup * \return Not used, required by pthread_create signature. */ static void *tracer_routine(void *arg) { struct tracer_creation_data *tracer_cd = (struct tracer_creation_data*)arg; pid_t pid = tracer_cd->pid; pthread_t tid = pthread_self(); /* wait for the dispatcher to detach the process, and then attach */ printf_deb("thread %lu: attaching\n", tid); if(ptrace(PTRACE_ATTACH, pid, 0, 0) != 0) { perror("child attach failed"); exit(1); } /* tell the dispatcher he's done, and can dispose the data */ printf_deb("thread %lu: unlocking\n", tid); if(sem_post(&tracer_cd->s) != 0) { perror("tracer failed to unlock"); exit(1); } /* ok - we've attached it, now it can go until next syscall */ printf_deb("thread %lu: unlocking child\n", tid); if(ptrace(PTRACE_SYSCALL, pid, 0, 0) != 0) { perror("failed to make first child start"); exit(1); } for(;;) pause(); return NULL; }