1 /*
2  * Copyright (C) 2006 Tommi Maekitalo
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * As a special exception, you may use this file as part of a free
10  * software library without restriction. Specifically, if other files
11  * instantiate templates or use macros or inline functions from this
12  * file, or you compile this file and link it with other files to
13  * produce an executable, this file does not by itself cause the
14  * resulting executable to be covered by the GNU General Public
15  * License. This exception does not however invalidate any other
16  * reasons why the executable file might be covered by the GNU Library
17  * General Public License.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
27  */
28 
29 
30 #include "tnt/process.h"
31 #include "tnt/tntconfig.h"
32 #include <cxxtools/systemerror.h>
33 #include <cxxtools/posix/fork.h>
34 #include <pwd.h>
35 #include <grp.h>
36 #include <cxxtools/log.h>
37 #include <vector>
38 #include <fstream>
39 #include <errno.h>
40 #include <signal.h>
41 
42 log_define("tntnet.process")
43 
44 namespace
45 {
46   tnt::Process* theProcess = 0;
47 
sigEnd(int)48   extern "C" void sigEnd(int)
49   {
50     signal(SIGTERM, sigEnd);
51     if (theProcess)
52       theProcess->shutdown();
53   }
54 
sigReload(int)55   extern "C" void sigReload(int)
56   {
57     signal(SIGHUP, sigReload);
58     if (theProcess)
59       theProcess->restart();
60   }
61 
setGroup(const std::string & group)62   void setGroup(const std::string& group)
63   {
64     struct group * gr = ::getgrnam(group.c_str());
65     if (gr == 0)
66       throw std::runtime_error("unknown group " + group);
67 
68     log_debug("change group to " << group << '(' << gr->gr_gid << ')');
69 
70     int ret = ::setgid(gr->gr_gid);
71     if (ret != 0)
72       throw cxxtools::SystemError("setgid");
73   }
74 
setUser(const std::string & user)75   void setUser(const std::string& user)
76   {
77     struct passwd * pw = getpwnam(user.c_str());
78     if (pw == 0)
79       throw std::runtime_error("unknown user " + user);
80 
81     log_debug("change user to " << user << '(' << pw->pw_uid << ')');
82 
83     int ret = ::setuid(pw->pw_uid);
84     if (ret != 0)
85       throw cxxtools::SystemError("getuid");
86   }
87 
setDir(const std::string & dir)88   void setDir(const std::string& dir)
89   {
90     log_debug("chdir(" << dir << ')');
91     if (::chdir(dir.c_str()) == -1)
92       throw cxxtools::SystemError("chdir");
93   }
94 
setRootdir(const std::string & dir)95   void setRootdir(const std::string& dir)
96   {
97     if (::chroot(dir.c_str()) == -1)
98       throw cxxtools::SystemError("chroot");
99   }
100 
101   class PidFile
102   {
103       std::string pidFileName;
104     public:
105       PidFile(const std::string& pidFileName, pid_t pid);
106       ~PidFile();
107 
releasePidFile()108       void releasePidFile()  { pidFileName.clear(); }
109   };
110 
PidFile(const std::string & pidFileName_,pid_t pid)111   PidFile::PidFile(const std::string& pidFileName_, pid_t pid)
112     : pidFileName(pidFileName_)
113   {
114     if (pidFileName.empty())
115       return;
116 
117     if (pidFileName[0] != '/')
118     {
119       // prepend current working-directory to pidfilename if not absolute
120       std::vector<char> buf(256);
121       const char* cwd;
122       while (true)
123       {
124         cwd = ::getcwd(&buf[0], buf.size());
125         if (cwd)
126           break;
127         else if (errno == ERANGE)
128           buf.resize(buf.size() * 2);
129         else
130           throw cxxtools::SystemError("getcwd");
131       }
132       pidFileName = std::string(cwd) + '/' + pidFileName;
133       log_debug("pidfile=" << pidFileName);
134     }
135 
136     std::ofstream pidfile(pidFileName.c_str());
137     if (pidfile.fail())
138       throw std::runtime_error("unable to open pid-file " + pidFileName);
139 
140     pidfile << pid;
141 
142     if (pidfile.fail())
143       throw std::runtime_error("error writing to pid-file " + pidFileName);
144   }
145 
~PidFile()146   PidFile::~PidFile()
147   {
148     if (!pidFileName.empty())
149       ::unlink(pidFileName.c_str());
150   }
151 
closeStdHandles(const std::string & errorLog=std::string ())152   void closeStdHandles(const std::string& errorLog = std::string())
153   {
154     if (::freopen("/dev/null", "r", stdin) == 0)
155       throw cxxtools::SystemError("freopen(stdin)");
156 
157     if (::freopen("/dev/null", "w", stdout) == 0)
158       throw cxxtools::SystemError("freopen(stdout)");
159 
160     if (!errorLog.empty())
161     {
162       if (::freopen(errorLog.c_str(), "a+", stderr) == 0)
163         throw cxxtools::SystemError("freopen(stderr)");
164     }
165   }
166 
167 }
168 
169 namespace tnt
170 {
Process()171   Process::Process()
172   {
173     theProcess = this;
174   }
175 
~Process()176   Process::~Process()
177   {
178     if (theProcess == this)
179       theProcess = 0;
180   }
181 
runMonitor(cxxtools::posix::Pipe & mainPipe)182   void Process::runMonitor(cxxtools::posix::Pipe& mainPipe)
183   {
184     log_debug("run monitor");
185 
186     // setsid
187     if (setsid() == -1)
188       throw cxxtools::SystemError("setsid");
189 
190     bool first = true;
191 
192     while (true)
193     {
194       cxxtools::posix::Pipe monitorPipe;
195 
196       cxxtools::posix::Fork fork;
197 
198       if (fork.child())
199       {
200         // worker-process
201 
202         log_debug("close read-fd of monitor-pipe");
203         monitorPipe.closeReadFd();
204 
205         initWorker();
206         if (first)
207         {
208           log_debug("signal initialization ready");
209           mainPipe.write('1');
210           log_debug("close write-fd of main-pipe");
211           mainPipe.closeWriteFd();
212         }
213 
214         log_debug("close standard-handles");
215         closeStdHandles(tnt::TntConfig::it().errorLog);
216 
217         exitRestart = false;
218         log_debug("do work");
219         doWork();
220 
221         // normal shutdown
222         if (exitRestart)
223           log_debug("restart");
224         else
225         {
226           log_debug("signal shutdown");
227           try
228           {
229             monitorPipe.write('s');
230           }
231           catch (const std::exception& e)
232           {
233             log_debug("ingore exception from monitor pipe: " << e.what());
234           }
235         }
236         return;
237       }
238 
239       // monitor-process
240 
241       log_debug("write pid " << fork.getPid() << " to \"" << tnt::TntConfig::it().pidfile << '"');
242       PidFile p(tnt::TntConfig::it().pidfile, fork.getPid());
243 
244       if (first)
245       {
246         log_debug("close standard-handles");
247         closeStdHandles();
248         first = false;
249       }
250 
251       monitorPipe.closeWriteFd();
252       try
253       {
254         log_debug("monitor child");
255         char dummy;
256         size_t c = monitorPipe.read(&dummy, 1);
257         if (c > 0)
258         {
259           log_debug("child terminated normally");
260           return;
261         }
262         log_debug("nothing read from monitor-pipe - restart child");
263       }
264       catch (const cxxtools::SystemError&)
265       {
266         log_debug("child exited without notification");
267       }
268 
269       log_debug("wait for child-termination");
270       fork.wait();
271 
272       ::sleep(1);
273     }
274   }
275 
initWorker()276   void Process::initWorker()
277   {
278     log_debug("init worker");
279 
280     log_debug("onInit");
281     onInit();
282 
283     if (!tnt::TntConfig::it().group.empty())
284     {
285       log_debug("set group to \"" << tnt::TntConfig::it().group << '"');
286       ::setGroup(tnt::TntConfig::it().group);
287     }
288 
289     if (!tnt::TntConfig::it().user.empty())
290     {
291       log_debug("set user to \"" << tnt::TntConfig::it().user << '"');
292       ::setUser(tnt::TntConfig::it().user);
293     }
294 
295     if (!tnt::TntConfig::it().dir.empty())
296     {
297       log_debug("set dir to \"" << tnt::TntConfig::it().dir << '"');
298       ::setDir(tnt::TntConfig::it().dir);
299     }
300 
301     if (!tnt::TntConfig::it().chrootdir.empty())
302     {
303       log_debug("change root to \"" << tnt::TntConfig::it().chrootdir << '"');
304       ::setRootdir(tnt::TntConfig::it().chrootdir);
305     }
306 
307     signal(SIGTERM, sigEnd);
308     signal(SIGINT, sigEnd);
309     signal(SIGHUP, sigReload);
310   }
311 
run()312   void Process::run()
313   {
314     if (tnt::TntConfig::it().daemon)
315     {
316       log_debug("run daemon-mode");
317 
318       // We receive the writing-end of the notify pipe.
319       // After successful initialization we need to write a byte to this fd.
320       cxxtools::posix::Pipe mainPipe;
321       cxxtools::posix::Fork fork;
322       if (fork.parent())
323       {
324         log_debug("close write-fd of main-pipe");
325         mainPipe.closeWriteFd();
326 
327         log_debug("wait for child to initialize");
328         mainPipe.read();
329 
330         log_debug("child initialized");
331 
332         fork.setNowait();
333       }
334       else
335       {
336         log_debug("close read-fd of main-pipe");
337         mainPipe.closeReadFd();
338 
339         runMonitor(mainPipe);
340       }
341     }
342     else
343     {
344       log_debug("run");
345       initWorker();
346 
347       log_debug("do work");
348       doWork();
349     }
350   }
351 
shutdown()352   void Process::shutdown()
353   {
354     doShutdown();
355   }
356 
restart()357   void Process::restart()
358   {
359     exitRestart = true;
360     doShutdown();
361   }
362 }
363