00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00031
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();
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 ,siginfo_t* , void* )
00157 {
00159
00160 }
00161
00167 void container::CServDemon::supervisorTerm(int ,siginfo_t* , void* )
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
00196
00197
00198
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
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)
00225 exit(0);
00226
00227
00228
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
00264
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
00274 int pid;
00275 std::cerr<<"Starting supervisor"<<std::endl;
00276 while(true) {
00277 pid=fork();
00278 if(pid==-1) {
00279 std::cerr<<"Ihsa! Superwisor couldn't fork worker child!"<<std::endl;
00280 exit(1);
00281 }
00282 if(pid==0) {
00283 std::cerr<<"Starting worker child"<<std::endl;
00284 m_pidF = -1;
00285 break;
00286 }
00287 int status=0;
00288 int waitret=waitpid(pid, &status, 0);
00289 if(waitret==-1) {
00290 if(errno==EINTR) {
00291 std::cerr<<"Got interrupted. Killing the child."<<std::endl;
00292 kill(pid, SIGTERM);
00293
00294
00295
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);
00300 waitpid(pid, &status, 0);
00301 }
00302 }
00303 return;
00304 }
00305
00306
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
00320 act.sa_flags=0;
00321
00322 sigaction(SIGPIPE,&act,0);
00323
00324
00325
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
00332 run();
00333 }