Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

daemon_windows.cc

Go to the documentation of this file.
00001 //                            Package   : omniEvents
00002 //  daemon_windows.cc         Created   : 2004/07/23
00003 //                            Author    : Alex Tingle
00004 //
00005 //    Copyright (C) 2004 Alex Tingle
00006 //
00007 //    This file is part of the omniEvents application.
00008 //
00009 //    omniEvents is free software; you can redistribute it and/or
00010 //    modify it under the terms of the GNU Lesser General Public
00011 //    License as published by the Free Software Foundation; either
00012 //    version 2.1 of the License, or (at your option) any later version.
00013 //
00014 //    omniEvents is distributed in the hope that it will be useful,
00015 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 //    Lesser General Public License for more details.
00018 //
00019 //    You should have received a copy of the GNU Lesser General Public
00020 //    License along with this library; if not, write to the Free Software
00021 //    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 
00024 #include "daemon.h"
00025 #include "daemon_windows.h"
00026 #include "main.h"
00027 
00028 #define NEED_PACKAGE_INFO
00029 #ifdef HAVE_CONFIG_H
00030 #  include "config.h"
00031 #endif
00032 
00033 using namespace std;
00034 
00035 #include <fstream>
00036 #include <stdlib.h> // exit, on_exit
00037 #include <errno.h> // errno
00038 #include <string>
00039 #include <vector>
00040 
00041 #define AS_STR_2(x) #x
00042 #define AS_STR_1(x) AS_STR_2(x)
00043 
00044 #define HERE __FILE__ ":" AS_STR_1(__LINE__)
00045 
00046 // Forward declaration of omniORB::setLogFunction()
00047 namespace omniORB {
00048   void setLogFunction(void (*logFunction)(const char*));
00049 }
00050 
00051 namespace OmniEvents {
00052 
00054 class Win
00055 {
00056 public:
00057   static const char* strerror(DWORD e)
00058   {
00059     LPVOID buf;
00060     ::FormatMessage(
00061       FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
00062       NULL,
00063       e,
00064       MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
00065       (LPTSTR)&buf,
00066       0, NULL
00067     );
00068     return (const char*)(buf);
00069   }
00070 
00071   static void perror(const char* s=NULL)
00072   {
00073     if(s)
00074     {
00075       Service::log(s);
00076       Service::log(": ");
00077     }
00078     Service::log(Win::strerror(::GetLastError()));
00079   }
00080 };
00081 
00086 class RegistryKey
00087 {
00088   HKEY _hkey;
00089   bool _open;
00090 private:
00091   RegistryKey(); 
00092   RegistryKey(HKEY hkey, bool open=true):_hkey(hkey),_open(open){}
00093 public:
00094   RegistryKey(RegistryKey& right);
00095   RegistryKey(HKEY hkey, const char* subkey, REGSAM samDesired=KEY_QUERY_VALUE);
00096   ~RegistryKey();
00097   operator bool() const { return _open; }
00098   int setValueStr(const char* name, const char* data);
00099   char* queryValueStr(const char* name, const int maxlen=2048) const;
00100 };
00101 
00103 RegistryKey::RegistryKey(RegistryKey& right):
00104   _hkey(right._hkey),_open(right._open)
00105 {
00106   right._open=false;
00107 }
00108 
00110 RegistryKey::RegistryKey(
00111   HKEY hkey,
00112   const char* subkey,
00113   REGSAM samDesired
00114 ):_hkey(), _open(false)
00115 {
00116   long ret=::RegOpenKeyEx(hkey,subkey,0,samDesired,&_hkey);
00117   ::SetLastError(ret);
00118   if(ret==ERROR_SUCCESS)
00119       _open=true;
00120 }
00121 
00123 RegistryKey::~RegistryKey()
00124 {
00125   // Windows - why use two lines, when seven will do??
00126   // RegCloseKey() does not set last error, so complexity ensues...
00127   if(_open)
00128   {
00129     long ret =::RegCloseKey(_hkey);
00130     ::SetLastError(ret);
00131     if(ret!=ERROR_SUCCESS)
00132         Win::perror("Warning at " HERE);
00133   }
00134 }
00135 
00136 int RegistryKey::setValueStr(const char* name, const char* data)
00137 {
00138   long ret=::RegSetValueEx(
00139       _hkey,name,0,REG_SZ,
00140       (const BYTE*)(data),
00141       1+::strlen(data)
00142     );
00143   ::SetLastError(ret);
00144   if(ret==ERROR_SUCCESS)
00145       return 0;
00146   else
00147       return 1;
00148 }
00149 
00150 char* RegistryKey::queryValueStr(const char* name, const int maxlen) const
00151 {
00152   char* result =NULL;
00153   char* buf =new char[maxlen];
00154   DWORD len =maxlen;
00155 
00156   long ret=::RegQueryValueEx(_hkey,name,NULL,NULL,(LPBYTE)buf,&len);
00157   ::SetLastError(ret);
00158   if(ret==ERROR_SUCCESS && len<=maxlen)
00159       result=::strdup(buf); // MSVC6 has no strndup()!!
00160   delete[] buf;
00161   return result;
00162 }
00164 
00166 static Service service;
00167 
00168 Daemon::Daemon(int& argc,char**& argv)  { service.start(argc,argv); }
00169 void Daemon::tracefile(const char* val) { service.tracefile(val); }
00170 void Daemon::pidfile(const char* val)   { service.pidfile(val); }
00171 void Daemon::foreground(bool val)       { service.foreground(val); }
00172 void Daemon::daemonize()                { service.daemonize(); }
00173 void Daemon::runningOk()                { service.runningOk(); }
00174 Daemon::~Daemon()                       { service.shutdown(); }
00175 
00176 void shutdown0(void){ service.shutdown(); } 
00177 
00179 
00180 Service::Service():
00181   _tracefile(NULL),
00182   _regSubKey("SYSTEM\\CurrentControlSet\\Services\\" PACKAGE_NAME),
00183   _serviceRunning(false),
00184   _callCount(0),
00185   _parameters(NULL),
00186   _argv(NULL),
00187   _logstream(&cerr),
00188   _serviceStatusHandle()
00189 {}
00190 
00191 
00192 Service::~Service()
00193 {
00194   delete[] _tracefile;
00195   delete[] _parameters;
00196   delete[] _argv;
00197   if(_logstream!=&cerr)
00198       delete _logstream;
00199 }
00200 
00201 
00202 void Service::tracefile(const char* val)
00203 {
00204   delete[] _tracefile;
00205   _tracefile=::strdup(val);
00206 }
00207 
00208 
00209 void Service::pidfile(const char* val)
00210 {
00211   Service::log("Option -P not supported on windows.\n");
00212   ::exit(1);
00213 }
00214 
00215 
00216 void Service::foreground(bool val)
00217 {
00218   Service::log("Option -f not supported on windows.\n");
00219   ::exit(1);
00220 }
00221 
00222 
00223 void Service::start(int& argc,char**& argv)
00224 {
00225   ++_callCount;
00226   if(_callCount>1)
00227   {
00228     // This is a re-entrant call. We are inside 'ServiceMain()'.
00229     setArgcArgv(argc,argv); // Set argv & argc from the registry.
00230     _serviceStatusHandle=
00231       ::RegisterServiceCtrlHandler(
00232         PACKAGE_NAME,
00233         (LPHANDLER_FUNCTION)Service::ctrlHandler
00234       );
00235     if(!_serviceStatusHandle)
00236         ::exit(1);
00237     if(! setServiceStatus(SERVICE_START_PENDING,NO_ERROR,0,1,3000) )
00238         ::exit(1);
00239     _serviceRunning=true;
00240     // ...and return to main().
00241   }
00242   else if(argc>=2 && 0==::strcmp(argv[1],"service"))
00243   {
00244     // Start service.
00245     char* name =::strdup(PACKAGE_NAME);
00246     SERVICE_TABLE_ENTRY servicetable[]=
00247     {
00248       {name,(LPSERVICE_MAIN_FUNCTION)::main},
00249       {NULL,NULL}
00250     };
00251     if(! ::StartServiceCtrlDispatcher(servicetable) )
00252     {
00253       Win::perror(HERE);
00254       ::exit(1);
00255     }
00256     ::exit(0);
00257   }
00258   else if(argc>=2 && 0==::strcmp(argv[1],"install"))
00259   {
00260     install(argc,argv);
00261     cout<<"Service '" PACKAGE_NAME "' installed OK."<<endl;
00262     ::exit(0);
00263   }
00264   else if(argc>=2 && 0==::strcmp(argv[1],"uninstall"))
00265   {
00266     uninstall();
00267     cout<<"Service '" PACKAGE_NAME "' removed."<<endl;
00268     ::exit(0);
00269   }
00270   else if(argc>=2 && 0==::strcmp(argv[1],"getoptions"))
00271   {
00272     readParameters();
00273     cout<<_parameters<<endl;
00274     ::exit(0);
00275   }
00276   else if(argc>=2 && 0==::strcmp(argv[1],"setoptions"))
00277   {
00278     writeParameters(argc,argv);
00279     ::exit(0);
00280   }
00281   else if(argc>=2 && 0==::strcmp(argv[1],"run"))
00282   {
00283     setArgcArgv(argc,argv); // Set argv & argc from the registry.
00284   }
00285   else
00286   {
00287     ; // Just run the program in the foreground.
00288   }
00289 }
00290 
00291 
00292 void Service::daemonize()
00293 {
00294   if(_tracefile && _tracefile[0]!='\0')
00295   {
00296     _logstream=new ofstream(_tracefile,ios::out|ios::app);
00297     omniORB::setLogFunction(Service::log);
00298   }
00299 
00300   // Register the shutdown function.
00301   if( ::atexit(shutdown0) <0) // Windows has atexit()
00302   {
00303     Service::log("Failed to set exit handler.");
00304     ::exit(-1);
00305   }
00306 }
00307 
00308 
00309 void Service::runningOk()
00310 {
00311   if(_serviceRunning)
00312   {
00313     if(! setServiceStatus(SERVICE_RUNNING,NO_ERROR,0,0,0) )
00314         ::exit(1);
00315   }
00316 }
00317 
00318 
00319 void Service::shutdown()
00320 {
00321   if(_logstream!=&cerr)
00322   {
00323     delete _logstream;
00324     _logstream=&cerr;
00325   }
00326   if(_serviceRunning)
00327   {
00328     setServiceStatus(SERVICE_STOPPED,NO_ERROR,0,0,0);
00329     _serviceRunning=false;
00330   }
00331 }
00332 
00333 // static callback
00334 void Service::log(const char* message)
00335 {
00336   (*service._logstream)<<message<<flush;
00337 }
00338 
00339 // static callback
00340 void Service::ctrlHandler(DWORD controlCode)
00341 {
00342   switch(controlCode)
00343   {  
00344   case SERVICE_CONTROL_SHUTDOWN:
00345   case SERVICE_CONTROL_STOP:
00346       ::OmniEvents_Orb_shutdown(controlCode);
00347       service.setServiceStatus(SERVICE_STOP_PENDING,NO_ERROR,0,1,6000);
00348       break;
00349   case 128: // User defined code.
00350       ::OmniEvents_Orb_bumpTraceLevel(controlCode);
00351       break;
00352   default:
00353       break;
00354   }
00355 }
00356 
00357 
00358 void Service::setArgcArgv(int& argc,char**& argv)
00359 {
00360   readParameters();
00361   vector<char*> args;
00362   char* param =::strtok(_parameters,"\t ");
00363   while(param)
00364   {
00365     args.push_back(param);
00366     param=::strtok(NULL,"\t ");
00367   }
00368   if(!args.empty())
00369   {
00370     _argv=new char*[argc+args.size()]; // deleted by ~Service()
00371     int i=0;
00372     _argv[i++]=argv[0];
00373     for(int j=0; j<args.size(); ++j)
00374         _argv[i++]=args[j];
00375     for(int k=1; k<argc; ++k)
00376         _argv[i++]=argv[k];
00377     argv=_argv;
00378     argc=i;
00379   }
00380 }
00381 
00382 
00383 void Service::install(int argc,char** argv) const
00384 {
00385   //
00386   // Install service
00387   char exe_file_name[MAX_PATH];
00388   if(0== ::GetModuleFileName(0, exe_file_name, MAX_PATH) )
00389   {
00390     Win::perror(HERE);
00391     ::exit(1);
00392   }
00393 
00394   string command =string(exe_file_name)+" service";
00395 
00396   SC_HANDLE scmanager =::OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
00397   if(!scmanager)
00398   {
00399     Win::perror(HERE);
00400     ::exit(1);
00401   }
00402   SC_HANDLE service =
00403     ::CreateService(
00404       scmanager,
00405       PACKAGE_NAME,
00406       "CORBA Event Daemon",
00407       SERVICE_ALL_ACCESS,
00408       SERVICE_WIN32_OWN_PROCESS,
00409       SERVICE_AUTO_START,
00410       SERVICE_ERROR_NORMAL,
00411       command.c_str(),
00412       0,0,0,0,0
00413     );
00414   if(!service)
00415   {
00416     Win::perror(HERE);
00417     ::exit(1);
00418   }
00419   if(0== ::CloseServiceHandle(service) )
00420   {
00421     Win::perror(HERE);
00422     ::exit(1);
00423   }
00424   if(0== ::CloseServiceHandle(scmanager) )
00425   {
00426     Win::perror(HERE);
00427     ::exit(1);
00428   }
00429 
00430   //
00431   // Set the service's parameters & description.
00432   writeParameters(argc,argv);
00433   RegistryKey rkey(HKEY_LOCAL_MACHINE,_regSubKey,KEY_SET_VALUE);
00434   if(!rkey)
00435   {
00436     Win::perror("Can't open registry key at " HERE);
00437     ::exit(1);
00438   }
00439   if(0!= rkey.setValueStr("Description",
00440            "Asynchronous broadcast channels for CORBA applications.") )
00441   {
00442     Win::perror("Can't set registry value 'Description' at " HERE);
00443     ::exit(1);
00444   }
00445 }
00446 
00447 
00448 void Service::uninstall() const
00449 {
00450   SC_HANDLE scmanager =::OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
00451   if(!scmanager)
00452   {
00453     Win::perror(HERE);
00454     ::exit(1);
00455   }
00456   SC_HANDLE service =
00457     ::OpenService(
00458       scmanager,
00459       PACKAGE_NAME,
00460       SC_MANAGER_ALL_ACCESS
00461     );
00462   if(!service)
00463   {
00464     Win::perror(HERE);
00465     ::exit(1);
00466   }
00467   if(0== ::DeleteService(service) )
00468   {
00469     Win::perror(HERE);
00470     ::exit(1);
00471   }
00472   if(0== ::CloseServiceHandle(service) )
00473   {
00474     Win::perror(HERE);
00475     ::exit(1);
00476   }
00477   if(0== ::CloseServiceHandle(scmanager) )
00478   {
00479     Win::perror(HERE);
00480     ::exit(1);
00481   }
00482 }
00483 
00484 
00485 void Service::readParameters()
00486 {
00487   RegistryKey rkey(HKEY_LOCAL_MACHINE,_regSubKey);
00488   if(!rkey)
00489   {
00490     Win::perror("Can't open registry key at " HERE);
00491     ::exit(1);
00492   }
00493   _parameters=rkey.queryValueStr("Parameters"); // deleted by ~Service()
00494   if(_parameters==NULL)
00495   {
00496     Win::perror("Can't get Parameters at " HERE);
00497     ::exit(1);
00498   }
00499 }
00500 
00501 
00502 void Service::writeParameters(int argc, char** argv) const
00503 {
00504   RegistryKey rkey(HKEY_LOCAL_MACHINE,_regSubKey,KEY_SET_VALUE);
00505   if(!rkey)
00506   {
00507     Win::perror("Can't open registry key at " HERE);
00508     ::exit(1);
00509   }
00510   string parameters ="";
00511   for(int i=2; i<argc; ++i)
00512   {
00513     if(!parameters.empty())
00514         parameters+=" ";
00515     parameters+=argv[i];
00516   }
00517   if(0!= rkey.setValueStr("Parameters",parameters.c_str()) )
00518   {
00519     Win::perror("Can't set registry value 'Parameters' at " HERE);
00520     ::exit(1);
00521   }
00522 }
00523 
00524 
00525 bool Service::setServiceStatus(
00526   DWORD currentState,
00527   DWORD win32ExitCode,
00528   DWORD serviceSpecificExitCode,
00529   DWORD checkPoint,
00530   DWORD waitHint)
00531 {
00532   SERVICE_STATUS s;
00533   s.dwServiceType            =SERVICE_WIN32_OWN_PROCESS;
00534   s.dwCurrentState           =currentState;
00535   s.dwServiceSpecificExitCode=serviceSpecificExitCode;
00536   s.dwCheckPoint             =checkPoint;
00537   s.dwWaitHint               =waitHint;
00538 
00539   if(currentState==SERVICE_START_PENDING)
00540       s.dwControlsAccepted=0;
00541   else
00542       s.dwControlsAccepted=SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN;
00543 
00544   if(serviceSpecificExitCode==0)
00545       s.dwWin32ExitCode=win32ExitCode;
00546   else
00547       s.dwWin32ExitCode=ERROR_SERVICE_SPECIFIC_ERROR;
00548 
00549   return (0!= ::SetServiceStatus(_serviceStatusHandle,&s) );
00550 }
00551 
00552 } // end namespace OmniEvents

Generated on Fri Aug 26 20:56:14 2005 for OmniEvents by  doxygen 1.4.3-20050530