import docopt, json, posix, nre, strutils import os except sleep type CombinedConfig* = object docopt*: Table[string, Value] json*: JsonNode proc getVal*(cfg: CombinedConfig, key, default: string): string = let argKey = "--" & key let envKey = key.replace('-', '_').toUpper let jsonKey = key.replace(re"(-\w)", proc (m: RegexMatch): string = ($m)[1..1].toUpper) if cfg.docopt[argKey]: return $cfg.docopt[argKey] elif existsEnv(envKey): return getEnv(envKey) elif cfg.json.hasKey(jsonKey): return cfg.json[jsonKey].getStr else: return default var pidFileInner: string fi, fo, fe: File proc onStop(sig: cint) {.noconv.} = close(fi) close(fo) close(fe) removeFile(pidFileInner) quit(QuitSuccess) proc daemonize*(pidfile, si, so, se: string, daemonMain: proc(): void): int = if fileExists(pidfile): raise newException(IOError, "pidfile " & pidfile & " already exists, daemon already running?") let pid1 = fork() # Are we the child process? if pid1 == 0: # Yes, so let's get ready to execute the main code given to us. discard chdir("/") discard setsid() discard umask(0) # Fork again... but I'm not sure why. let pid2 = fork() # We don't need the intermediate process, so if we are not the child this # time, lets just quit if pid2 > 0: quit(QuitSuccess) # If we are the grandchild let's set up our environment. flushFile(stdout) flushFile(stderr) if not si.isNil and si != "": fi = open(si, fmRead) discard dup2(getFileHandle(fi), getFileHandle(stdin)) if not so.isNil and so != "": fo = open(so, fmAppend) discard dup2(getFileHandle(fo), getFileHandle(stdout)) if not se.isNil and so != "": fe = open(se, fmAppend) discard dup2(getFileHandle(fe), getFileHandle(stderr)) pidFileInner = pidfile # Add hooks to cleanup after ourselves when we're asked to die. signal(SIGINT, onStop) signal(SIGTERM, onStop) # Find out what our actual PID is and save it let childPid = getpid() writeFile(pidfile, $childPid) # Finally, execute our main daemonMain() return pid1