/ daemon / UnixDaemon.cpp
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