#!/usr/bin/env compileAndGo
# -*- coding: utf-8 mode: c -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
language=c
!#

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <errno.h>

static void die(const char *format, ...) {
  va_list ap;

  va_start(ap, format);
  vfprintf(stderr, format, ap);
  va_end(ap);

  exit(1);
}

static int dofork() {
  switch(fork()) {
  case 0: return 0;
  case -1: return -1;
  default:
    _exit(0);
  }
}

int main(int argc, char **argv) {
  if (argc == 2 && !strcmp(argv[1], "--help")) {
    printf("udaemon.cgo: start a program as a daemon\n"
           "\n"
           "USAGE\n"
           "    udaemon.cgo /path/to/prog [args...]\n"
           "\n"
           "OPTIONS\n"
           "    -d DESTDIR\n"
           "        Change to DESTDIR instead of \"/\" before starting the program\n");
    exit(0);
  }

  char *destdir = "/";
  int opt;
  while((opt = getopt(argc, argv, "d:")) != -1) {
    switch(opt) {
    case 'd':
      destdir = optarg;
      break;
    default:
      exit(1);
    }
  }
  if (argc - optind < 1) die("The first argument is required\n");
  char **cmd = &argv[optind];

  if (cmd[0][0] != '/') die("%s: argument must be absolute\n", cmd[0]);
  if (access(cmd[0], X_OK) == -1) die("%s: not executable\n", cmd[0]);
  struct stat st;
  if (stat(cmd[0], &st)) die("%s: does not exist\n", cmd[0]);
  if(!S_ISREG(st.st_mode)) die("%s: not a regular file\n", cmd[0]);
  if (chdir(destdir) != 0) die("%s: %s\n", destdir, strerror(errno));

  int status = 0;
  if ((status = dofork()) < 0) {
  } else if (setsid() < 0) {
    status = -1;
  } else if ((status = dofork()) < 0) {
  } else {
    umask(0);
    chdir(destdir);
    close(0);
    close(1);
    close(2);
    if (open("/dev/null", O_RDWR) != 0) exit(1);
    dup(0);
    dup(0);
  }
  if (status != 0) {
    die("Can't daemonize: %s\n", strerror(errno));
  }

  if (getenv("IFS") == NULL) setenv("IFS", " \t\n", 1);
  if (getenv("PATH") == NULL) setenv("PATH", "/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin", 1);

  execvp(cmd[0], cmd);
  die("Can't exec \"%s\": %s\n", cmd[0], strerror(errno));
}
