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