diff --git a/src/daemonizer/posix_daemonizer.inl b/src/daemonizer/posix_daemonizer.inl index f8be15dd..506c7766 100644 --- a/src/daemonizer/posix_daemonizer.inl +++ b/src/daemonizer/posix_daemonizer.inl @@ -43,6 +43,10 @@ namespace daemonizer "detach" , "Run as daemon" }; + const command_line::arg_descriptor arg_pidfile = { + "pidfile" + , "File path to write the daemon's PID to (optional, requires --detach)" + }; const command_line::arg_descriptor arg_non_interactive = { "non-interactive" , "Run non-interactive" @@ -55,6 +59,7 @@ namespace daemonizer ) { command_line::add_arg(normal_options, arg_detach); + command_line::add_arg(normal_options, arg_pidfile); command_line::add_arg(normal_options, arg_non_interactive); } @@ -80,7 +85,12 @@ namespace daemonizer if (command_line::has_arg(vm, arg_detach)) { tools::success_msg_writer() << "Forking to background..."; - posix::fork(); + std::string pidfile; + if (command_line::has_arg(vm, arg_pidfile)) + { + pidfile = command_line::get_arg(vm, arg_pidfile); + } + posix::fork(pidfile); auto daemon = executor.create_daemon(vm); return daemon.run(); } diff --git a/src/daemonizer/posix_fork.cpp b/src/daemonizer/posix_fork.cpp index d9b4a6d0..4dff04f3 100644 --- a/src/daemonizer/posix_fork.cpp +++ b/src/daemonizer/posix_fork.cpp @@ -21,15 +21,43 @@ namespace posix { namespace { - void quit(std::string const & message) + void quit(const std::string & message) { LOG_ERROR(message); throw std::runtime_error(message); } } -void fork() +void fork(const std::string & pidfile) { + // If a PID file is specified, we open the file here, because + // we can't report errors after the fork operation. + // When we fork, we close thise file in each of the parent + // processes. + // Only in the final child process do we write the PID to the + // file (and close it). + std::ofstream pidofs; + if (! pidfile.empty ()) + { + int oldpid; + std::ifstream pidrifs; + pidrifs.open(pidfile, std::fstream::in); + if (! pidrifs.fail()) + { + // Read the PID and send signal 0 to see if the process exists. + if (pidrifs >> oldpid && oldpid > 1 && kill(oldpid, 0) == 0) + { + quit("PID file " + pidfile + " already exists and the PID therein is valid"); + } + pidrifs.close(); + } + + pidofs.open(pidfile, std::fstream::out | std::fstream::trunc); + if (pidofs.fail()) + { + quit("Failed to open specified PID file for writing"); + } + } // Fork the process and have the parent exit. If the process was started // from a shell, this returns control to the user. Forking a new process is // also a prerequisite for the subsequent call to setsid(). @@ -38,7 +66,7 @@ void fork() if (pid > 0) { // We're in the parent process and need to exit. - // + pidofs.close(); // When the exit() function is used, the program terminates without // invoking local variables' destructors. Only global variables are // destroyed. @@ -59,6 +87,7 @@ void fork() { if (pid > 0) { + pidofs.close(); exit(0); } else @@ -67,6 +96,13 @@ void fork() } } + if (! pidofs.fail()) + { + int pid = ::getpid(); + pidofs << pid << std::endl; + pidofs.close(); + } + // Close the standard streams. This decouples the daemon from the terminal // that started it. close(0); diff --git a/src/daemonizer/posix_fork.h b/src/daemonizer/posix_fork.h index 459417d2..77ef4cb1 100644 --- a/src/daemonizer/posix_fork.h +++ b/src/daemonizer/posix_fork.h @@ -1,21 +1,21 @@ // Copyright (c) 2014-2017, The Monero Project -// +// // All rights reserved. -// +// // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. -// +// // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL @@ -27,12 +27,13 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once +#include #ifndef WIN32 namespace posix { -void fork(); +void fork(const std::string & pidfile); } diff --git a/utils/systemd/monerod.service b/utils/systemd/monerod.service index 182878eb..12395eb8 100644 --- a/utils/systemd/monerod.service +++ b/utils/systemd/monerod.service @@ -8,15 +8,10 @@ Group=monero WorkingDirectory=~ Type=forking -ExecStart=/usr/bin/monerod --config-file /etc/monerod.conf --detach +PIDFile=/var/run/monerod.pid -# This is necessary because monerod does not yet support -# writing a PID file, which means systemd tries to guess the PID -# by default, but it guesses wrong (sometimes, depending on -# random timing of events), because the daemon forks twice. -# The ultimate fix is for the daemon to write a PID file, and -# a workaround is to disable the guessing feature in systemd. -GuessMainPID=no +ExecStart=/usr/bin/monerod --config-file /etc/monerod.conf \ + --detach --pidfile /var/run/monerod.pid [Install] WantedBy=multi-user.target