packages feed

voyeur-0.1.0.0: libvoyeur/src/voyeur-exit.c

#ifndef __APPLE__
#define _GNU_SOURCE
#endif

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

#include "dyld.h"
#include "env.h"
#include "net.h"

static char did_exit_already = 0;

//////////////////////////////////////////////////
// Shared code for all exit*() functions.
//////////////////////////////////////////////////

static void write_exit_event(int status)
{
  if (!did_exit_already) {
    did_exit_already = 1;

    // In the case of exit we don't bother caching anything; we are about to exit,
    // after all!
    const char* sockpath = getenv("LIBVOYEUR_SOCKET");
    int sock = voyeur_create_client_socket(sockpath);
    if (sock >= 0) {
      voyeur_write_msg_type(sock, VOYEUR_MSG_EVENT);
      voyeur_write_event_type(sock, VOYEUR_EVENT_EXIT);
      voyeur_write_int(sock, status);
      voyeur_write_pid(sock, getpid());
      voyeur_write_pid(sock, getppid());

      // We might as well close the socket since there's no chance we'll
      // ever be called a second time by the same process.
      voyeur_write_msg_type(sock, VOYEUR_MSG_DONE);
      voyeur_close_socket(sock);
    }
  }
}


//////////////////////////////////////////////////
// exit*() variants.
//////////////////////////////////////////////////

// There are a lot of variants here because none of them seem to be perfectly
// reliable, especially on Linux. We are still likely to miss the case where a
// process gets signaled, unfortunately.

typedef void (*exit_fptr_t)(int);

void VOYEUR_FUNC(exit)(int status)
{
  write_exit_event(status);

  // Pass through the call to the real exit.
  VOYEUR_DECLARE_NEXT(exit_fptr_t, exit);
  VOYEUR_LOOKUP_NEXT(exit_fptr_t, exit);
  return VOYEUR_CALL_NEXT(exit, status);
}

VOYEUR_INTERPOSE(exit)

void VOYEUR_FUNC(_exit)(int status)
{
  write_exit_event(status);

  // Pass through the call to the real _exit.
  VOYEUR_DECLARE_NEXT(exit_fptr_t, _exit);
  VOYEUR_LOOKUP_NEXT(exit_fptr_t, _exit);
  return VOYEUR_CALL_NEXT(_exit, status);
}

VOYEUR_INTERPOSE(_exit)

void VOYEUR_FUNC(_Exit)(int status)
{
  write_exit_event(status);

  // Pass through the call to the real _Exit.
  VOYEUR_DECLARE_NEXT(exit_fptr_t, _Exit);
  VOYEUR_LOOKUP_NEXT(exit_fptr_t, _Exit);
  return VOYEUR_CALL_NEXT(_Exit, status);
}

VOYEUR_INTERPOSE(_Exit)

#ifdef __linux__

void VOYEUR_FUNC(exit_group)(int status)
{
  write_exit_event(status);

  // Pass through the call to the real exit_group.
  VOYEUR_DECLARE_NEXT(exit_fptr_t, exit_group);
  VOYEUR_LOOKUP_NEXT(exit_fptr_t, exit_group);
  return VOYEUR_CALL_NEXT(exit_group, status);
}

VOYEUR_INTERPOSE(exit_group)

static void voyeur_exit_handler(int status, void* unused)
{
  write_exit_event(status);
}

__attribute__((constructor)) void voyeur_init_exit_handler()
{
  on_exit(voyeur_exit_handler, 0);
}

#endif