/*
 * sb_gdb.c
 *
 * Helpers for autolaunching gdb.
 *
 * Copyright 1999-2012 Gentoo Foundation
 * Licensed under the GPL-2
 */

#include "headers.h"
#include "sbutil.h"

static int __sb_connect_gdb(void *vpid)
{
	char pid[10];
	snprintf(pid, sizeof(pid), "%i", (pid_t)(uintptr_t)vpid);
	unsetenv(ENV_LD_PRELOAD);
	return /*sb_unwrapped_*/execlp("gdb", "gdb",
		"--quiet",
		"--pid", pid,
		"-ex", "set _gdb_poller = 0",
		"-ex", "fin",
		"-ex", "bt full",
		"-ex", "echo \\n",
		"-ex", "f",
		NULL);
}

#define STACK_SIZE 4096

#ifndef HAVE_CLONE
# ifdef HAVE___CLONE2
/* Hrm, maybe they have clone2 */
#  define clone(fn, stack, flags, arg) \
	__clone2(fn, stack, STACK_SIZE, flags, arg, NULL, NULL, NULL)
# else
/* Fake it with fork() ... not as safe, but not much else we can do */
static int fake_clone(int (*fn)(void *), void *child_stack, int flags, void *arg)
{
	pid_t pid = fork();
	switch (pid) {
	case 0:
		_exit(fn(arg));
	default:
		return pid;
	}
}
#  define clone(...) fake_clone(__VA_ARGS__)
# endif
#endif

void sb_gdb(void)
{
	char stack[STACK_SIZE * 2];
	pid_t pid = getpid();
	/* Put the child stack in the middle so we don't have to worry about
	 * the direction of growth.  Most grown down, but some grow up!@
	 */
	if (clone(__sb_connect_gdb, stack + STACK_SIZE, 0, (void *)(uintptr_t)pid) != -1) {
		volatile int _gdb_poller = 1;
		while (_gdb_poller);
	}
}

#ifndef NDEBUG
void sb_maybe_gdb(void)
{
	if (is_env_on("SANDBOX_GDB")) {
		sb_einfo("attempting to autolaunch gdb; please wait ...\n\n");
		sb_gdb();
	}
}
#endif
