/*
 * thr.c
 *
 */
#include "thr.h"


/*******************************************************************************
 *
 * detach()
 *
 * Detach from the shell process.
 *
 ******************************************************************************/

void detach(void) {
  switch(fork1()) {
  case -1:
    perror("detach: fork failed:");
    exit(1);
  case 0:
    break;
  default:
    _exit(0);
  }
  if(setsid() < 0) {
    perror("detach: setsid failed:");
    exit(1);
  }
}



/*******************************************************************************
 *
 * catcher()
 *
 * Start routine for the daemon thread that waits for signals.
 *
 ******************************************************************************/

#if defined(SOLARIS_THREADS) || defined(POSIX_THREADS)

static
void *catcher(void *vsigmask)
{
  sigset_t	*sigmask = (sigset_t*)vsigmask;
  uint		signum;

  for(;;) {

    signum = sigwait(sigmask);

    switch(signum) {

    case SIGINT:
    case SIGTERM:
      psignal(signum, "Caught signal");
      exit(0);

    case SIGHUP:
    case SIGQUIT:
    default:
      break;
    }
  }
}

#elif defined(NO_THREADS)

static
void catcher(int signum)
{
  switch(signum) {

  case SIGINT:
  case SIGTERM:
    psignal(signum, "Caught signal");
    exit(0);

  case SIGHUP:
  case SIGQUIT:
  case SIGCHLD:
  default:
    break;
  }

  signal(signum, catcher);
}
#endif


/*******************************************************************************
 *
 * spawnCatcher()
 *
 * Starts thread to catch signals
 *
 ******************************************************************************/

int spawnCatcher(sigset_t *sigmask)
{
#if defined(SOLARIS_THREADS)

  thread_t	tid;
  thr_sigsetmask(SIG_BLOCK, sigmask, NULL);
  return thr_create(NULL, 0, catcher, (void*)sigmask, THR_DAEMON|THR_DETACHED, &tid);

#elif defined(POSIX_THREADS)

  pthread_attr_t	attr;
  pthread_t		tid;
  int			ret;
  if (ret = pthread_sigmask(SIG_BLOCK, sigmask, NULL)) return ret;
  if (ret = pthread_attr_init(&attr)) return ret;
  if (ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) return ret;
  if (ret = pthread_create(&tid, &attr, catcher, (void*)&sigmask)) return ret;
  return 0;

#elif defined(NO_THREADS)

  sigset_t	mask, NULL_SIGSET;
  int		signum;

  sigemptyset(&NULL_SIGSET);

  for(signum=1, mask=*sigmask; memcmp(mask,NULL_SIGSET,sizeof(mask)); ++signum)
    if (sigismember(&mask, signum)) {
      sigdelset(&mask, signum);
      signal(signum, catcher);
    }
  return 0;

#endif
}


/*******************************************************************************
 *
 * spawnThread()
 *
 * Starts thread to service connection
 *
 ******************************************************************************/

int spawnThread(void *(*start_routine)(void *), int sock)
{
#if defined(SOLARIS_THREADS)

  thread_t	tid;
  return thr_create(NULL, 0, start_routine, (void*)sock, THR_DETACHED, &tid);

#elif defined(POSIX_THREADS)

  pthread_attr_t	attr;
  pthread_t		tid;
  int			ret;
  if (ret = pthread_attr_init(&attr)) return ret;
  if (ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) return ret;
  if (ret = pthread_create(&tid, &attr, start_routine, (void*)sock)) return ret;
  return 0;

#elif defined(NO_THREADS)

  switch(fork()) {
  case -1:
    return -1;
  case 0:
    start_routine((void*)sock);
    exit(0);
  default:
    close(sock);
    return 0;
  }

#endif
}
