UnixDaemon.cpp
1 /* 2 * Copyright (c) 2013-2025, The PurpleI2P Project 3 * 4 * This file is part of Purple i2pd project and licensed under BSD3 5 * 6 * See full license text in LICENSE file at top of project tree 7 */ 8 9 #include "Daemon.h" 10 11 #ifndef _WIN32 12 13 #include <signal.h> 14 #include <stdlib.h> 15 #include <thread> 16 #include <unistd.h> 17 #include <fcntl.h> 18 #include <sys/stat.h> 19 #include <sys/resource.h> 20 21 #include "Config.h" 22 #include "FS.h" 23 #include "Log.h" 24 #include "Tunnel.h" 25 #include "RouterContext.h" 26 #include "ClientContext.h" 27 #include "Transports.h" 28 #include "util.h" 29 30 void handle_signal(int sig) 31 { 32 switch (sig) 33 { 34 case SIGHUP: 35 LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening tunnel configuration..."); 36 i2p::client::context.ReloadConfig(); 37 break; 38 case SIGUSR1: 39 LogPrint(eLogInfo, "Daemon: Got SIGUSR1, reopening logs..."); 40 i2p::log::Logger().Reopen (); 41 break; 42 case SIGINT: 43 if (i2p::context.AcceptsTunnels () && !Daemon.gracefulShutdownInterval) 44 { 45 i2p::context.SetAcceptsTunnels (false); 46 Daemon.gracefulShutdownInterval = 10*60; // 10 minutes 47 LogPrint(eLogInfo, "Graceful shutdown after ", Daemon.gracefulShutdownInterval, " seconds"); 48 } 49 else 50 Daemon.running = 0; 51 break; 52 case SIGABRT: 53 case SIGTERM: 54 Daemon.running = 0; // Exit loop 55 break; 56 case SIGPIPE: 57 LogPrint(eLogInfo, "SIGPIPE received"); 58 break; 59 case SIGTSTP: 60 LogPrint(eLogInfo, "Daemon: Got SIGTSTP, disconnecting from network..."); 61 i2p::transport::transports.SetOnline(false); 62 break; 63 case SIGCONT: 64 LogPrint(eLogInfo, "Daemon: Got SIGCONT, restoring connection to network..."); 65 i2p::transport::transports.SetOnline(true); 66 break; 67 } 68 } 69 70 namespace i2p 71 { 72 namespace util 73 { 74 bool DaemonUnix::start() 75 { 76 if (isDaemon) 77 { 78 pid_t pid; 79 pid = fork(); 80 if (pid > 0) // parent 81 ::exit (EXIT_SUCCESS); 82 83 if (pid < 0) // error 84 { 85 LogPrint(eLogError, "Daemon: Could not fork: ", strerror(errno)); 86 std::cerr << "i2pd: Could not fork: " << strerror(errno) << std::endl; 87 return false; 88 } 89 90 // child 91 umask(S_IWGRP | S_IRWXO); // 0027 92 int sid = setsid(); 93 if (sid < 0) 94 { 95 LogPrint(eLogError, "Daemon: Could not create process group."); 96 std::cerr << "i2pd: Could not create process group." << std::endl; 97 return false; 98 } 99 std::string d = i2p::fs::GetDataDir(); 100 if (chdir(d.c_str()) != 0) 101 { 102 LogPrint(eLogError, "Daemon: Could not chdir: ", strerror(errno)); 103 std::cerr << "i2pd: Could not chdir: " << strerror(errno) << std::endl; 104 return false; 105 } 106 107 // point std{in,out,err} descriptors to /dev/null 108 freopen("/dev/null", "r", stdin); 109 freopen("/dev/null", "w", stdout); 110 freopen("/dev/null", "w", stderr); 111 } 112 113 // set proc limits 114 struct rlimit limit; 115 uint16_t nfiles; i2p::config::GetOption("limits.openfiles", nfiles); 116 getrlimit(RLIMIT_NOFILE, &limit); 117 if (nfiles == 0) { 118 LogPrint(eLogInfo, "Daemon: Using system limit in ", limit.rlim_cur, " max open files"); 119 } else if (nfiles <= limit.rlim_max) { 120 limit.rlim_cur = nfiles; 121 if (setrlimit(RLIMIT_NOFILE, &limit) == 0) { 122 LogPrint(eLogInfo, "Daemon: Set max number of open files to ", 123 nfiles, " (system limit is ", limit.rlim_max, ")"); 124 } else { 125 LogPrint(eLogError, "Daemon: Can't set max number of open files: ", strerror(errno)); 126 } 127 } else { 128 LogPrint(eLogError, "Daemon: limits.openfiles exceeds system limit: ", limit.rlim_max); 129 } 130 uint32_t cfsize; i2p::config::GetOption("limits.coresize", cfsize); 131 if (cfsize) // core file size set 132 { 133 cfsize *= 1024; 134 getrlimit(RLIMIT_CORE, &limit); 135 if (cfsize <= limit.rlim_max) { 136 limit.rlim_cur = cfsize; 137 if (setrlimit(RLIMIT_CORE, &limit) != 0) { 138 LogPrint(eLogError, "Daemon: Can't set max size of coredump: ", strerror(errno)); 139 } else if (cfsize == 0) { 140 LogPrint(eLogInfo, "Daemon: coredumps disabled"); 141 } else { 142 LogPrint(eLogInfo, "Daemon: Set max size of core files to ", cfsize / 1024, "Kb"); 143 } 144 } else { 145 LogPrint(eLogError, "Daemon: limits.coresize exceeds system limit: ", limit.rlim_max); 146 } 147 } 148 149 // Pidfile 150 // this code is c-styled and a bit ugly, but we need fd for locking pidfile 151 std::string pidfile; i2p::config::GetOption("pidfile", pidfile); 152 if (pidfile == "") { 153 pidfile = i2p::fs::DataDirPath("i2pd.pid"); 154 } 155 if (pidfile != "") { 156 pidFH = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600); 157 if (pidFH < 0) 158 { 159 LogPrint(eLogError, "Daemon: Could not create pid file ", pidfile, ": ", strerror(errno)); 160 std::cerr << "i2pd: Could not create pid file " << pidfile << ": " << strerror(errno) << std::endl; 161 return false; 162 } 163 164 #ifndef ANDROID 165 if (lockf(pidFH, F_TLOCK, 0) != 0) 166 #else 167 struct flock fl; 168 fl.l_len = 0; 169 fl.l_type = F_WRLCK; 170 fl.l_whence = SEEK_SET; 171 fl.l_start = 0; 172 173 if (fcntl(pidFH, F_SETLK, &fl) != 0) 174 #endif 175 { 176 LogPrint(eLogError, "Daemon: Could not lock pid file ", pidfile, ": ", strerror(errno)); 177 std::cerr << "i2pd: Could not lock pid file " << pidfile << ": " << strerror(errno) << std::endl; 178 return false; 179 } 180 181 char pid[16]; 182 snprintf(pid, 16, "%d\n", getpid()); 183 ftruncate(pidFH, 0); 184 if (write(pidFH, pid, strlen(pid)) < 0) 185 { 186 LogPrint(eLogCritical, "Daemon: Could not write pidfile ", pidfile, ": ", strerror(errno)); 187 std::cerr << "i2pd: Could not write pidfile " << pidfile << ": " << strerror(errno) << std::endl; 188 return false; 189 } 190 } 191 gracefulShutdownInterval = 0; // not specified 192 193 // handle signal TSTP 194 bool handleTSTP; i2p::config::GetOption("unix.handle_sigtstp", handleTSTP); 195 196 // Signal handler 197 struct sigaction sa; 198 sa.sa_handler = handle_signal; 199 sigemptyset(&sa.sa_mask); 200 sa.sa_flags = SA_RESTART; 201 sigaction(SIGHUP, &sa, 0); 202 sigaction(SIGUSR1, &sa, 0); 203 sigaction(SIGABRT, &sa, 0); 204 sigaction(SIGTERM, &sa, 0); 205 sigaction(SIGINT, &sa, 0); 206 sigaction(SIGPIPE, &sa, 0); 207 if (handleTSTP) 208 { 209 sigaction(SIGTSTP, &sa, 0); 210 sigaction(SIGCONT, &sa, 0); 211 } 212 213 return Daemon_Singleton::start(); 214 } 215 216 bool DaemonUnix::stop() 217 { 218 i2p::fs::Remove(pidfile); 219 return Daemon_Singleton::stop(); 220 } 221 222 void DaemonUnix::run () 223 { 224 i2p::util::SetThreadName ("i2pd-daemon"); 225 while (running) 226 { 227 std::this_thread::sleep_for (std::chrono::seconds(1)); 228 if (gracefulShutdownInterval) 229 { 230 gracefulShutdownInterval--; // - 1 second 231 if (gracefulShutdownInterval <= 0 || i2p::tunnel::tunnels.CountTransitTunnels() <= 0) 232 { 233 LogPrint(eLogInfo, "Graceful shutdown"); 234 return; 235 } 236 } 237 } 238 } 239 } 240 } 241 #endif