OmniEvents
|
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