CPPSERV


Home Projects Jobs Clientele Contact
CPPSERV Documentation Download TODO Mailing lists Bug tracker News RSS Feed Browse source

cservdemon.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (C) 2004-2--6 by Ilya A. Volynets-Evenbakh                  *
00003  *   ilya@total-knowledge.com                                              *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU General Public License as published by  *
00007  *   the Free Software Foundation; either version 2 of the License, or     *
00008  *   (at your option) any later version.                                   *
00009  *                                                                         *
00010  *   This program is distributed in the hope that it will be useful,       *
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00013  *   GNU General Public License for more details.                          *
00014  *                                                                         *
00015  *   You should have received a copy of the GNU General Public License     *
00016  *   along with this program; if not, write to the                         *
00017  *   Free Software Foundation, Inc.,                                       *
00018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00019  ***************************************************************************/
00020 #include <cservdemon.h>
00021 #include <iostream>
00022 #include <unistd.h>
00023 #include <signal.h>
00024 #include <pthread.h>
00025 #include <requestlistener.h>
00026 #include <serverconfig/xmlserverconfig.h>
00027 #include <servlet/IOError.h>
00028 #include <servlet/cppserv-ver.h>
00029 
00030 //TODO: Path resolution (for phys_base calculation)
00031 //TODO: for now Unix-specific. Replace with TR2 when available
00032 #include <limits.h>
00033 #include <stdlib.h>
00034 #include <sys/file.h>
00035 #include <sys/wait.h>
00036 #include <sys/types.h>
00037 
00038 namespace container {
00039 
00040 CServDemon::CServDemon()
00041     : m_cinName("/dev/zero")
00042     , m_coutName("./servlet.log")
00043     , m_cerrName(m_coutName)
00044     , m_detach(false)
00045     , m_pidF(-1)
00046     , m_pidFPath("/var/run/cppserv.pid")
00047     , m_lockedPidFile(false)
00048 {
00049 }
00050 
00051 
00052 CServDemon::~CServDemon()
00053 {
00054     if(m_pidF != -1) {
00055         ::flock(m_pidF,LOCK_UN);
00056         ::close(m_pidF);
00057         if(m_lockedPidFile)
00058             ::unlink(m_pidFPath.c_str());
00059     }
00060 }
00061 
00062 
00063 static void version()
00064 {
00065     std::cout<<"CPPSERV version "<<CPPSERV_VERSION<<std::endl;
00066 }
00067 
00068 }
00069 
00070 
00074 void container::CServDemon::run()
00075 {
00076     try {
00077         m_conf.load();
00078         m_conf.processConfig();//This should create and load all application objects...
00079     } catch (servlet::ServletException& e) {
00080         std::cerr<<"Error parsing config file: "<<e.getMsg()<<std::endl;
00081         e.printStackTrace(std::cerr);
00082         exit(1);
00083     }
00084     try {
00085         m_conf.getListener()->acceptRequests();
00086     } catch (servlet::Traceable& e) {
00087         std::cerr<<"Error while processing requests: "<<e.getMsg()<<std::endl;
00088         e.printStackTrace(std::cerr);
00089     } catch (...) {
00090         std::cerr<<"Unknown error while processing requests"<<std::endl;
00091     }
00092 }
00093 
00094 
00098 void container::CServDemon::parseOptions(int argc, char** argv)
00099 {
00100     const char* opts="c:dl:p:P:t:D:ho:v";
00101     char o;
00102     while((o=getopt(argc,argv,opts))!=-1){
00103         switch(o){
00104         case 'c':
00105             setConfigPath(optarg);
00106             break;
00107         case 'd':
00108             m_detach=true;
00109             break;
00110         case 'P':
00111             m_pidFPath=optarg;
00112             break;
00113         case 't':
00114             m_conf.setGlobalParam("num_threads", optarg);
00115             break;
00116         case 'D':
00117             m_conf.setGlobalParam("queue_depth", optarg);
00118             break;
00119         case 'o':
00120             m_coutName = m_cerrName = optarg;
00121             break;
00122         case 'v':
00123             version();
00124             exit(0);
00125         case '?':
00126         default:
00127             std::cerr<<"Unknown option: "<<o<<std::endl;
00128         case 'h':
00129             help();
00130             break;
00131         }
00132     }
00133 }
00134 
00135 
00145 void container::CServDemon::handleSig11(int,siginfo_t*, void*)
00146 {
00147     std::cerr<<"Received sig11\n";
00148     pthread_exit(0);
00149 }
00150 
00156 void container::CServDemon::handleTermSig(int /*signum*/,siginfo_t* /*info*/, void* /*par*/)
00157 {
00159     //  disp->stop();
00160 }
00161 
00167 void container::CServDemon::supervisorTerm(int /*signum*/,siginfo_t* /*info*/, void* /*par*/)
00168 {
00169 }
00170 
00171 
00175 void container::CServDemon::help()
00176 {
00177     version();
00178     std::cout<<"Usage:\n"
00179         "cppserv [-c <config.xml>] [-d] [-l <hostname>] [-p <num>] [-P <pidfile>] [-t <num>] [-h]\n"
00180         " -c allows to specify path to configuration file. Default is /etc/engine.xml\n"
00181         " -d casuses engine to detach from controlling terminal and run in daemon mode\n"
00182         " -l <hostname> specify hostname/IP to listen on. Default is to listen on all\n"
00183         " -p <port> port to listen on. Default is 9004\n"
00184         " -P <pidfile> file to save PID in. Default /var/run/cppserv.pid\n"
00185         " -t <num> number of threads in task queue. Default 15.\n"
00186         " -D <num> queue depth. Default 15\n"
00187         " -o <path> stdout and stderr output file when daemonizing (default ./servlet.log).\n"
00188         " -h Display this help message and exit\n";
00189     exit(1);
00190 }
00191 
00192 void container::CServDemon::setConfigPath(const char* path)
00193 {
00194     m_conf.setPath(path);
00195     //boost::filesystem::path cfgfile(path);
00196     //m_conf.setGlobalParam("phys_path", cfgfile.parent_path().directory_string());
00197     //TODO: right now boost::filesystem API is unstable
00198     //TODO: replace this with TR2 implementation later, when it's available
00199     char rpath[PATH_MAX];
00200     if(!::realpath(path, rpath))
00201         throw servlet::IOError("Unable to figure out absolute base path for config file");
00202     char* sep = ::strrchr(rpath, '/');
00203     if(!sep)
00204         sep = rpath;
00205     *sep=0;
00206     m_conf.setGlobalParam("phys_base", rpath);
00207 }
00208 
00212 void container::CServDemon::start()
00213 {
00214     int pid;
00215     if(m_detach){
00216         // Change session, to completely detach from controlling terminal
00217         ::setsid();
00218         pid=fork();
00219         if(pid==-1) {
00220             std::string err("Error forking while detaching:");
00221             err += strerror(errno);
00222             throw std::runtime_error(err);
00223         }
00224         if(pid!=0) //Detached
00225             exit(0);
00226         // HACK ALERT! This may result in some problems with buffering!
00227         // This most likely does screw up system-level buffering options
00228         //std::cin.flush();
00229         std::cout.flush();
00230         std::cerr.flush();
00231         int cin=::open(m_cinName.c_str(),O_RDONLY|O_CREAT,S_IRUSR|S_IWUSR);
00232         if(cin == -1)std::cout<<"Unable to open CIN file ("<<m_cinName<<"): "<<strerror(errno)<<std::endl;
00233         int cout=::open(m_coutName.c_str(),O_WRONLY|O_CREAT|O_APPEND,S_IRUSR|S_IWUSR);
00234         if(cout == -1)std::cout<<"Unable to open COUT file ("<<m_coutName<<"): "<<strerror(errno)<<std::endl;
00235         int cerr=::open(m_cerrName.c_str(),O_WRONLY|O_CREAT|O_APPEND,S_IRUSR|S_IWUSR);
00236         if(cerr == -1)std::cout<<"Unable to open CIN file ("<<m_coutName<<"): "<<strerror(errno)<<std::endl;
00237         if(dup2(cin,0)==-1)std::cout<<"Couldn't reopen CIN! "<<errno<<':'<<strerror(errno)<<std::endl;
00238         if(dup2(cout,1)==-1)std::cout<<"Couldn't reopen COUT! "<<errno<<':'<<strerror(errno)<<std::endl;
00239         if(dup2(cerr,2)==-1)std::cout<<"Couldn't reopen CERR! "<<errno<<':'<<strerror(errno)<<std::endl;
00240         ::close(cin);
00241         ::close(cout);
00242         ::close(cerr);
00243     }
00244     pid=getpid();
00245     m_pidF=::open(m_pidFPath.c_str(),O_RDWR|O_CREAT,S_IRUSR|S_IWUSR);
00246     if(m_pidF<0){
00247         std::cerr<<"Unable to open pid file: \""<<m_pidFPath<<"\": "<<strerror(errno)<<std::endl;
00248         return;
00249     }
00250     if(::flock(m_pidF,LOCK_EX|LOCK_NB)<0){
00251         std::cerr<<"Unable to lock pid file: \""<<m_pidFPath<<"\": "<<strerror(errno)<<std::endl;
00252         std::cerr<<"Is another cppserv already running?\n";
00253         return;
00254     } else {
00255         m_lockedPidFile = true;
00256     }
00257     char buf[20];
00258     ::snprintf(buf,sizeof(buf)-1,"%d",pid);
00259     ::ftruncate(m_pidF,0);
00260     ::write(m_pidF,buf,strlen(buf));
00261     ::fsync(m_pidF);
00262     if(m_detach) {
00263         // Handle SIGTERM and SIGINT in supervisor - we detect
00264         // them when waitpid() gets interrupted and exit then
00265         struct sigaction act;
00266         memset(&act, 0, sizeof(act));
00267         act.sa_handler=0;
00268         act.sa_sigaction=supervisorTerm;
00269         act.sa_flags=SA_SIGINFO;
00270         sigaction(SIGTERM,&act,0);
00271         sigaction(SIGINT,&act,0);
00272 
00273         // Now start the superwisor
00274         int pid;
00275         std::cerr<<"Starting supervisor"<<std::endl;
00276         while(true) {
00277             pid=fork();
00278             if(pid==-1) { // Ugh...
00279                 std::cerr<<"Ihsa! Superwisor couldn't fork worker child!"<<std::endl;
00280                 exit(1);
00281             }
00282             if(pid==0) { // child - go and do some real work
00283                 std::cerr<<"Starting worker child"<<std::endl;
00284                 m_pidF = -1; // Do not destroy PID file when child exits
00285                 break;
00286             }
00287             int status=0;
00288             int waitret=waitpid(pid, &status, 0);
00289             if(waitret==-1) {
00290                 if(errno==EINTR) { // Try to kill the child
00291                     std::cerr<<"Got interrupted. Killing the child."<<std::endl;
00292                     kill(pid, SIGTERM);
00293                     // Sleep five seconds or until exit of the child, since it should deliver SIGCHLD to us.
00294                     // NOTE: What happens if the child have exited by the time we got to sleep?
00295                     // Worst case scenario - we sleep for nothing
00296                     sleep(5);
00297                     if(waitpid(pid, &status, WNOHANG)==0) {
00298                         std::cerr<<"Child is sturdy one. I had to get harsh."<<std::endl;
00299                         kill(pid, SIGKILL); // If this doesn't kill it, we are in big trouble...
00300                         waitpid(pid, &status, 0);
00301                     }
00302                 }
00303                 return;
00304             }
00305             // if waitpid returned valid value, we just continue,
00306             // log exit reason for previous child and restart worker process
00307             if(WIFEXITED(status)) {
00308                 std::cerr<<"Worker exited with status "<<WEXITSTATUS(status)<<std::endl;
00309             } else if(WIFSIGNALED(status)){
00310                 std::cerr<<"Worker was killed by signal "<<WTERMSIG(status)<<std::endl;
00311             } else {
00312                 std::cerr<<"Worker terminated by aliens (or other means, unknown to me)"<<std::endl;
00313             }
00314         }
00315     }
00316     struct sigaction act;
00317     memset(&act, 0, sizeof(act));
00318     act.sa_handler=SIG_IGN;
00319     //act.sa_mask=0;
00320     act.sa_flags=0;//SA_RESTART;
00321     //Ignore SIGPIPE
00322     sigaction(SIGPIPE,&act,0);
00323     //FIXME: This is an ugly hack. We need to do something more reasonable
00324     //for SIGTERM handling
00325     //  CServDemon::disp=m_conf.getDisp();
00326     act.sa_handler=0;
00327     act.sa_sigaction=handleTermSig;
00328     act.sa_flags=SA_SIGINFO;
00329     sigaction(SIGTERM,&act,0);
00330     sigaction(SIGINT,&act,0);
00331     // Start working..
00332     run();
00333 }

SourceForge.net Logo