/* * A Simple Wrapper for Starting and Stopping of Daemons * * (c) 2003 Martin Mares * * It would seem that we are reinventing the wheel and the * start-stop-daemon command present in most Linux distributions * is just what we need, but the usual "does the process already * exist?" strategies fail in presence of multiple running daemons. * * Return codes: * 101 already running * 102 not running * * NOTE: This utility is obsolete and has been replaced by ucw-daemon-control. * You need to enable CONFIG_UCW_OBSOLETE_DAEMON_HELPER to compile it. */ #include #include #include #include #include #include #include #include #include #include enum action { ACTION_NONE, ACTION_START, ACTION_STOP, ACTION_FORCE_STOP, ACTION_CHECK, ACTION_RELOAD }; static int action; static struct option options[] = { { "pid-file", required_argument, NULL, 'p' }, { "status-file", required_argument, NULL, 's' }, { "start", no_argument, &action, ACTION_START }, { "stop", no_argument, &action, ACTION_STOP }, { "force-stop", no_argument, &action, ACTION_FORCE_STOP }, { "check", no_argument, &action, ACTION_CHECK }, { "reload", no_argument, &action, ACTION_RELOAD }, { NULL, no_argument, NULL, 0 } }; static void NONRET usage(void) { fputs("\n\ Usage: ucw-daemon-helper --start -- \n\ or: ucw-daemon-helper --stop \n\ or: ucw-daemon-helper --force-stop \n\ or: ucw-daemon-helper --reload \n\ or: ucw-daemon-helper --check \n\ \n\ Options:\n\ --pid-file Name of PID file for this daemon (mandatory)\n\ --status-file Status file used by the daemon (deleted just before starting)\n\ ", stderr); exit(1); } int main(int argc, char **argv) { int c, fd; char *pidfile = NULL; char *statfile = NULL; struct flock fl; char buf[64]; while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) switch (c) { case 0: break; case 'p': pidfile = optarg; break; case 's': statfile = optarg; break; default: usage(); } if (!pidfile) usage(); bzero(&fl, sizeof(fl)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; switch (action) { case ACTION_START: if (optind >= argc) usage(); fd = open(pidfile, O_RDWR | O_CREAT, 0666); if (fd < 0) die("Unable to create %s: %m", pidfile); if ((c = fcntl(fd, F_SETLK, &fl)) < 0) { if (errno == EAGAIN || errno == EACCES) return 101; else die("fcntl lock on %s failed: %m", pidfile); } c = sprintf(buf, "%d\n", getpid()); if (write(fd, buf, c) != c) die("write on %s failed: %m", pidfile); if (ftruncate(fd, c) < 0) die("truncate on %s failed: %m", pidfile); if (statfile && unlink(statfile) < 0 && errno != ENOENT) die("unlink(%s) failed: %m", statfile); setsid(); /* Disconnect from stdin and stdout, leave stderr to the daemon. */ close(0); open("/dev/null", O_RDWR, 0); dup2(0, 1); argv += optind; argc -= optind; char **a = alloca(sizeof(char *) * (argc+1)); memcpy(a, argv, sizeof(char *) * argc); a[argc] = NULL; execv(a[0], a); die("Cannot execute %s: %m", a[0]); case ACTION_STOP: case ACTION_FORCE_STOP: case ACTION_CHECK: case ACTION_RELOAD: if (optind < argc) usage(); fd = open(pidfile, O_RDWR); if (fd < 0) { if (errno == ENOENT) return 102; else die("Unable to open %s: %m", pidfile); } if ((c = fcntl(fd, F_SETLK, &fl)) >= 0) { nopid: unlink(pidfile); return 102; } if (errno != EAGAIN && errno != EACCES) die("fcntl lock on %s failed: %m", pidfile); if ((c = read(fd, buf, sizeof(buf))) < 0) die("read on %s failed: %m", pidfile); if (!c) goto nopid; if (c >= (int) sizeof(buf) || sscanf(buf, "%d", &c) != 1) die("PID file syntax error"); int sig = 0; if (action == ACTION_CHECK || action == ACTION_RELOAD) { if (action == ACTION_RELOAD) sig = SIGHUP; if (kill(c, sig) < 0 && errno == ESRCH) goto nopid; return 0; } sig = (action == ACTION_STOP) ? SIGTERM : SIGQUIT; if (kill(c, sig) < 0) { if (errno == ESRCH) goto nopid; die("Cannot kill process %d: %m", c); } if ((c = fcntl(fd, F_SETLKW, &fl)) < 0) die("Cannot lock %s: %m", pidfile); if (statfile) unlink(statfile); if (unlink(pidfile) < 0) die("Cannot unlink %s: %m", pidfile); return 0; default: usage(); } }