src/debug.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. sigalrm
  2. alrm_set
  3. debug_watch
  4. debug_call_gdb
  5. debug_pid
  6. watch_call_gdb
  7. sigchld
  8. watch_main
  9. watch_init

/* $Id: debug.c,v 1.5 2000/06/11 19:29:28 proff Exp $
 * $Copyright$
 */

#include "nglobal.h"

#include "debug.h"


static bool alrm_caught;

static RETSIGTYPE sigalrm (int sig)
/* [<][>][^][v][top][bottom][index][help] */
{
    alrm_caught = TRUE;
}

static void
alrm_set()
/* [<][>][^][v][top][bottom][index][help] */
{
    alrm_caught = FALSE;
    signal(SIGALRM, sigalrm);
    alarm(100);
}
 
static void
debug_watch()
/* [<][>][^][v][top][bottom][index][help] */
{
    char buf[512];
    sprintf(buf, "%d %s\n", getpid(), logPrintP);
    if (write(Watch_fd, buf, strlen(buf)) != strlen(buf))
        {
            loge (("couldn't inform watch"));
            return;
        }
    alrm_set();
    pause();
}    

static void
debug_call_gdb(pid_t pid)
/* [<][>][^][v][top][bottom][index][help] */
{
    char pid_asc[80];
    sprintf(pid_asc, "%d", (int)pid);
    execl("/bin/sh", "sh", PATH_DEBUG_SH, Argv0, pid_asc, Version, logPrintP, NULL);
    _exit(1);
}

EXPORT void debug_pid()
/* [<][>][^][v][top][bottom][index][help] */
{
    pid_t ptrace_child;
    if (Watch_fd >= 0)
        {
            debug_watch();
            return;
        }
    ptrace_child = fork();
    if (ptrace_child == 0)
        {
            sigset_t set;
            pid_t gdb_child;
            ptrace_child = getpid();
            sigemptyset(&set);
            sigprocmask(SIG_SETMASK, &set, NULL);
            signal (SIGPIPE, SIG_DFL);
            signal (SIGCHLD, SIG_DFL);
            signal (SIGTERM, SIG_DFL);
            signal (SIGHUP, SIG_DFL);
            gdb_child = fork();
            if (gdb_child == 0)
                {
                    debug_call_gdb(ptrace_child);
                    NOTREACHED;
                }
            if (gdb_child < 1)
                _exit(1);
            waitpid(gdb_child, NULL, 0);
            _exit(0);
        }
    if (ptrace_child < 1)
        return;
    alrm_set();
    waitpid(ptrace_child, NULL, 0);
    if (alrm_caught)
        {
            kill(SIGKILL, ptrace_child);
        }
    else
        alarm(0);
}

/*
 * all the following frigging hocus-pocus is to hack around
 * the fact that process can't be ptraced() if has *ever*
 * had an ancestor that called setuid(), without an intervening
 * exec*(). grrr.
 */
            
EXPORT void watch_call_gdb(pid_t pid, char *msg)
/* [<][>][^][v][top][bottom][index][help] */
{
    pid_t child = fork();
    if (child == 0)
        {
            char pid_asc[80];
            sprintf(pid_asc, "%d", (int)pid);
            execl("/bin/sh", "sh", PATH_DEBUG_SH, Argv0, pid_asc, Version, msg, NULL);
            _exit(1);
        }
    if (child == -1)
        {
            loge (("fork()"));
            return;
        }
}
    
static RETSIGTYPE
sigchld(int sig)
/* [<][>][^][v][top][bottom][index][help] */
{
    waitpid(-1, NULL, WNOHANG);
    signal(SIGCHLD, sigchld);
}

static void
watch_main(int fd)
/* [<][>][^][v][top][bottom][index][help] */
{
    settaskinfo("watch: ready");
    signal(SIGCHLD, sigchld);
    for (;;)
        {
            char buf[512];
            int pid;
            char *msg;
            FILE *fh = fdopen(fd, "r");
            if (!fgets(buf, sizeof buf, fh))
                retire_vm_proc(1);
            waitpid(-1, NULL, WNOHANG);
            msg = strchr(buf, ' ');
            sscanf(buf, "%d", &pid);
            if (!msg || pid < 1)
                {
                    logen (("bad pid: %s", buf));
                    continue;
                }
            msg++;
            watch_call_gdb((pid_t)pid, msg);
        }
}           

EXPORT void watch_init()
/* [<][>][^][v][top][bottom][index][help] */
{
    int p[2];
    if (pipe(p) != 0)
        {
            loge (("pipe()"));
            Exit(1);
        }
    Watch_fd = p[1];
    if (make_vm_proc(nc_watch, -1, "watch") == 0) /* 0 == child */
        {
            watch_main(p[0]);
            NOTREACHED;
        }
}
                                                             

/* [<][>][^][v][top][bottom][index][help] */