#!/usr/bin/env compileAndGo # -*- coding: utf-8 mode: c -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 language=c !# #include #include #include #include #include #include #include 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)); }