#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <asm/io.h>
#include <asm/ptrace.h>

#include <linux/ptrace.h>

void debugger(char **argv)
{
	pid_t pid = 0;		/* PID of executable */
	int status;
	int traced_children = 1;
	unsigned long npid;

	/* divide in two */
	pid = fork();

	if (pid == 0) {

		/* This process will become the traced proggy */

		/* prepare for debugging */
		ptrace(PTRACE_TRACEME, 0, 0, 0);

		/* execute scan */
		execv(argv[0], argv);

		/* just in case exec failed */
		return;
	}

	/* debugger process */


	wait(&status);

	if (WIFEXITED(status) || WIFSIGNALED(status))
		return;

	/* trace forks, clones, ... */
	ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACEFORK 
					| PTRACE_O_TRACEVFORK
					| PTRACE_O_TRACECLONE);

	printf("PID %u added\n", pid);

	/* continue... */
	ptrace(PTRACE_CONT, pid, 0, 0);

	for (;;) {

		pid = wait(&status);

		if (WIFEXITED(status) || WIFSIGNALED(status) ||
		    (WIFSTOPPED(status)
		     && ((WSTOPSIG(status) & SIGTRAP) == 0))) {

			printf("PID %u exited\n", pid);
			traced_children--;

			if (traced_children == 0)
				return;

		}

		switch (status >> 16) {
			case PTRACE_EVENT_FORK:
			case PTRACE_EVENT_VFORK:
			case PTRACE_EVENT_CLONE:


				ptrace(PTRACE_GETEVENTMSG, pid, 0, &npid);
				printf("PID %u added\n", npid);
				traced_children++;

			default:
				/* continue... */
				ptrace(PTRACE_CONT, pid, 0, 0);

		}


	}
}

int main(int argc, char **argv)
{

	char **param;
	int ctr;

	if (argc == 1) {

		printf("usage: %s program [params]", argv[0]);
		return;
	}

	param = malloc(sizeof(char *) * argc);
	memset(param, 0, sizeof(char *) * argc);

	for (ctr = 1; ctr < argc; ctr++)
		param[ctr - 1] = strdup(argv[ctr]);

	debugger(param);

	return 0;

}

