import posix import os except sleep ## Daemonize 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): Pid = if fileExists(pidfile): raise newException(IOError, "pidfile " & pidfile & " already exists, daemon already running?") # We're about to do a little dance to end up with a process that is # disconnected from the calling program's process group and is not a session # leader (a daemon process). # # Remember, after a call to fork the code is now running in two processes, # the parent process and the child process. So the returned PID will be a # positive value if we are still on the parent process, and represents the # PID of the newly created process. If we are on the newly created process, # the PID returned with be 0. 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. Note that # this child process will not actually live long. As part of our setup, # we're going to create a new process session, resulting in this child # process being the process group owner and session leader. We don't our # daemon process to be a session leader because that allows it to be # connected to a terminal. So after we've setup the new process group we # will fork again and actually run the logic we're daemonizing in the # grandchild process. # Ignore whatever the original working directory was. discard chdir("/") # Create a new session to decouple us from the original terminal. discard setsid() # Set the process file mode creation mask to 0000 discard umask(0) # Fork again so the grandchild process will not be the session leader. let pid2 = fork() # If we're still on the child process, we're done. if pid2 > 0: quit(QuitSuccess) # Otherwise, if we are the grandchild let's finish setting up our # environment. flushFile(stdout) flushFile(stderr) # Setup our STDIN, STDERR, and STDOUT pipes (if given) if si.len > 0: fi = open(si, fmRead) discard dup2(getFileHandle(fi), getFileHandle(stdin)) if so.len > 0: fo = open(so, fmAppend) discard dup2(getFileHandle(fo), getFileHandle(stdout)) if se.len > 0: fe = open(se, fmAppend) discard dup2(getFileHandle(fe), getFileHandle(stderr)) # Keep a record of the PID filename in our process memory so we can # reference it in our cleanup hooks. 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 to the PID file. let childPid = getpid() writeFile(pidfile, $childPid) # Finally, execute the logic given to us to daemonize daemonMain() # If we're not the child proces, return the new PID of the daemonized process return pid1