diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4e682b99..666105f46 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,7 +4,10 @@ file(GLOB_RECURSE COMMON common/*) file(GLOB_RECURSE CRYPTO crypto/*) file(GLOB_RECURSE CRYPTONOTE_CORE cryptonote_core/*) file(GLOB_RECURSE CRYPTONOTE_PROTOCOL cryptonote_protocol/*) -file(GLOB_RECURSE DAEMON daemon/*) +file(GLOB_RECURSE DAEMON daemon/*.cpp) +if(WIN32) + list(REMOVE_ITEM DAEMON "daemon/posix_fork.cpp") +endif() file(GLOB_RECURSE P2P p2p/*) file(GLOB_RECURSE RPC rpc/*) file(GLOB_RECURSE SIMPLEWALLET simplewallet/*) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index ba3efb3f4..79669f750 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -23,9 +23,9 @@ using namespace epee; #include "rpc/core_rpc_server.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" -//#ifndef WIN32 -//#include "posix_daemonize.h" -//#endif +#ifndef WIN32 +#include "posix_fork.h" +#endif #ifdef WIN32 #include @@ -166,8 +166,8 @@ int main(int argc, char* argv[]) #ifndef WIN32 if (command_line::arg_present(vm, arg_detach)) { - std::cout << "start daemon" << std::endl; - return 0; + std::cout << "forking to background..." << std::endl; + posix_fork(); } #endif @@ -225,9 +225,10 @@ int main(int argc, char* argv[]) CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core"); LOG_PRINT_L0("Core initialized OK"); - // start components - if(!command_line::has_arg(vm, arg_console)) + // Start handling console input if requested and not detached + if(!command_line::arg_present(vm, arg_console) && !command_line::arg_present(vm, arg_detach)) { + LOG_PRINT_L0("Begin handling console input"); console_command_thread.start(); } diff --git a/src/daemon/posix_fork.cpp b/src/daemon/posix_fork.cpp new file mode 100644 index 000000000..3b926ed19 --- /dev/null +++ b/src/daemon/posix_fork.cpp @@ -0,0 +1,104 @@ +#include "daemon/posix_fork.h" + +#include +#include +#include +#include +#include + +namespace daemonize { + +void posix_fork() +{ + // 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(). + if (pid_t pid = fork()) + { + if (pid > 0) + { + // We're in the parent process and need to exit. + // + // When the exit() function is used, the program terminates without + // invoking local variables' destructors. Only global variables are + // destroyed. As the io_service object is a local variable, this means + // we do not have to call: + // + // io_service.notify_fork(boost::asio::io_service::fork_parent); + // + // However, this line should be added before each call to exit() if + // using a global io_service object. An additional call: + // + // io_service.notify_fork(boost::asio::io_service::fork_prepare); + // + // should also precede the second fork(). + exit(0); + } + else + { + syslog(LOG_ERR | LOG_USER, "First fork failed: %m"); + exit(1); + } + } + + // Make the process a new session leader. This detaches it from the + // terminal. + setsid(); + + // A process inherits its working directory from its parent. This could be + // on a mounted filesystem, which means that the running daemon would + // prevent this filesystem from being unmounted. Changing to the root + // directory avoids this problem. + chdir("/"); + + // The file mode creation mask is also inherited from the parent process. + // We don't want to restrict the permissions on files created by the + // daemon, so the mask is cleared. + umask(0); + + // A second fork ensures the process cannot acquire a controlling terminal. + if (pid_t pid = fork()) + { + if (pid > 0) + { + exit(0); + } + else + { + syslog(LOG_ERR | LOG_USER, "Second fork failed: %m"); + exit(1); + } + } + + // Close the standard streams. This decouples the daemon from the terminal + // that started it. + close(0); + close(1); + close(2); + + // We don't want the daemon to have any standard input. + if (open("/dev/null", O_RDONLY) < 0) + { + syslog(LOG_ERR | LOG_USER, "Unable to open /dev/null: %m"); + exit(1); + } + + // Send standard output to a log file. + const char* output = "/tmp/bitmonero.daemon.out"; + const int flags = O_WRONLY | O_CREAT | O_APPEND; + const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + if (open(output, flags, mode) < 0) + { + syslog(LOG_ERR | LOG_USER, "Unable to open output file %s: %m", output); + exit(1); + } + + // Also send standard error to the same log file. + if (dup(1) < 0) + { + syslog(LOG_ERR | LOG_USER, "Unable to dup output descriptor: %m"); + exit(1); + } +} + +} diff --git a/src/daemon/posix_fork.h b/src/daemon/posix_fork.h new file mode 100644 index 000000000..1883b1851 --- /dev/null +++ b/src/daemon/posix_fork.h @@ -0,0 +1,7 @@ +#pragma once + +namespace daemonize { + +void posix_fork(); + +} // namespace daemonize