OmniEvents
daemon_windows.cc
Go to the documentation of this file.
1// Package : omniEvents
2// daemon_windows.cc Created : 2004/07/23
3// Author : Alex Tingle
4//
5// Copyright (C) 2004 Alex Tingle
6//
7// This file is part of the omniEvents application.
8//
9// omniEvents is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Lesser General Public
11// License as published by the Free Software Foundation; either
12// version 2.1 of the License, or (at your option) any later version.
13//
14// omniEvents is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// Lesser General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22//
23
24#include "daemon.h"
25#include "daemon_windows.h"
26#include "main.h"
27
28#define NEED_PACKAGE_INFO
29#ifdef HAVE_CONFIG_H
30# include "config.h"
31#endif
32
33using namespace std;
34
35#include <fstream>
36#include <stdlib.h> // exit, on_exit
37#include <errno.h> // errno
38#include <string>
39#include <vector>
40
41#define AS_STR_2(x) #x
42#define AS_STR_1(x) AS_STR_2(x)
44#define HERE __FILE__ ":" AS_STR_1(__LINE__)
45
46// Forward declaration of omniORB::setLogFunction()
47namespace omniORB {
48 void setLogFunction(void (*logFunction)(const char*));
49}
50
51namespace OmniEvents {
52
54class Win
55{
56public:
57 static const char* strerror(DWORD e)
58 {
59 LPVOID buf;
60 ::FormatMessage(
61 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
62 NULL,
63 e,
64 MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
65 (LPTSTR)&buf,
66 0, NULL
67 );
68 return (const char*)(buf);
69 }
70
71 static void perror(const char* s=NULL)
72 {
73 if(s)
74 {
75 Service::log(s);
76 Service::log(": ");
77 }
78 Service::log(Win::strerror(::GetLastError()));
79 }
80};
81
87{
88 HKEY _hkey;
89 bool _open;
90private:
92 RegistryKey(HKEY hkey, bool open=true):_hkey(hkey),_open(open){}
93public:
95 RegistryKey(HKEY hkey, const char* subkey, REGSAM samDesired=KEY_QUERY_VALUE);
97 operator bool() const { return _open; }
98 int setValueStr(const char* name, const char* data);
99 char* queryValueStr(const char* name, const int maxlen=2048) const;
100};
101
104 _hkey(right._hkey),_open(right._open)
105{
106 right._open=false;
107}
108
111 HKEY hkey,
112 const char* subkey,
113 REGSAM samDesired
114):_hkey(), _open(false)
115{
116 long ret=::RegOpenKeyEx(hkey,subkey,0,samDesired,&_hkey);
117 ::SetLastError(ret);
118 if(ret==ERROR_SUCCESS)
119 _open=true;
120}
121
124{
125 // Windows - why use two lines, when seven will do??
126 // RegCloseKey() does not set last error, so complexity ensues...
127 if(_open)
128 {
129 long ret =::RegCloseKey(_hkey);
130 ::SetLastError(ret);
131 if(ret!=ERROR_SUCCESS)
132 Win::perror("Warning at " HERE);
133 }
134}
135
136int RegistryKey::setValueStr(const char* name, const char* data)
137{
138 long ret=::RegSetValueEx(
139 _hkey,name,0,REG_SZ,
140 (const BYTE*)(data),
141 1+::strlen(data)
142 );
143 ::SetLastError(ret);
144 if(ret==ERROR_SUCCESS)
145 return 0;
146 else
147 return 1;
148}
149
150char* RegistryKey::queryValueStr(const char* name, const int maxlen) const
151{
152 char* result =NULL;
153 char* buf =new char[maxlen];
154 DWORD len =maxlen;
155
156 long ret=::RegQueryValueEx(_hkey,name,NULL,NULL,(LPBYTE)buf,&len);
157 ::SetLastError(ret);
158 if(ret==ERROR_SUCCESS && len<=maxlen)
159 result=::strdup(buf); // MSVC6 has no strndup()!!
160 delete[] buf;
161 return result;
162}
164
167
168Daemon::Daemon(int& argc,char**& argv) { service.start(argc,argv); }
169void Daemon::tracefile(const char* val) { service.tracefile(val); }
170void Daemon::pidfile(const char* val) { service.pidfile(val); }
171void Daemon::foreground(bool val) { service.foreground(val); }
175
176void shutdown0(void){ service.shutdown(); }
177
179
181 _tracefile(NULL),
182 _regSubKey("SYSTEM\\CurrentControlSet\\Services\\" PACKAGE_NAME),
183 _serviceRunning(false),
184 _callCount(0),
185 _parameters(NULL),
186 _argv(NULL),
187 _logstream(&cerr),
188 _serviceStatusHandle()
189{}
190
191
193{
194 delete[] _tracefile;
195 delete[] _parameters;
196 delete[] _argv;
197 if(_logstream!=&cerr)
198 delete _logstream;
199}
200
201
202void Service::tracefile(const char* val)
203{
204 delete[] _tracefile;
205 _tracefile=::strdup(val);
206}
207
208
209void Service::pidfile(const char* val)
210{
211 Service::log("Option -P not supported on windows.\n");
212 ::exit(1);
213}
214
215
217{
218 Service::log("Option -f not supported on windows.\n");
219 ::exit(1);
220}
221
222
223void Service::start(int& argc,char**& argv)
224{
225 ++_callCount;
226 if(_callCount>1)
227 {
228 // This is a re-entrant call. We are inside 'ServiceMain()'.
229 setArgcArgv(argc,argv); // Set argv & argc from the registry.
231 ::RegisterServiceCtrlHandler(
233 (LPHANDLER_FUNCTION)Service::ctrlHandler
234 );
236 ::exit(1);
237 if(! setServiceStatus(SERVICE_START_PENDING,NO_ERROR,0,1,3000) )
238 ::exit(1);
239 _serviceRunning=true;
240 // ...and return to main().
241 }
242 else if(argc>=2 && 0==::strcmp(argv[1],"service"))
243 {
244 // Start service.
245 char* name =::strdup(PACKAGE_NAME);
246 SERVICE_TABLE_ENTRY servicetable[]=
247 {
248 {name,(LPSERVICE_MAIN_FUNCTION)::main},
249 {NULL,NULL}
250 };
251 if(! ::StartServiceCtrlDispatcher(servicetable) )
252 {
254 ::exit(1);
255 }
256 ::exit(0);
257 }
258 else if(argc>=2 && 0==::strcmp(argv[1],"install"))
259 {
260 install(argc,argv);
261 cout<<"Service '" PACKAGE_NAME "' installed OK."<<endl;
262 ::exit(0);
263 }
264 else if(argc>=2 && 0==::strcmp(argv[1],"uninstall"))
265 {
266 uninstall();
267 cout<<"Service '" PACKAGE_NAME "' removed."<<endl;
268 ::exit(0);
269 }
270 else if(argc>=2 && 0==::strcmp(argv[1],"getoptions"))
271 {
273 cout<<_parameters<<endl;
274 ::exit(0);
275 }
276 else if(argc>=2 && 0==::strcmp(argv[1],"setoptions"))
277 {
278 writeParameters(argc,argv);
279 ::exit(0);
280 }
281 else if(argc>=2 && 0==::strcmp(argv[1],"run"))
282 {
283 setArgcArgv(argc,argv); // Set argv & argc from the registry.
284 }
285 else
286 {
287 ; // Just run the program in the foreground.
288 }
289}
290
291
293{
294 if(_tracefile && _tracefile[0]!='\0')
295 {
296 _logstream=new ofstream(_tracefile,ios::out|ios::app);
298 }
299
300 // Register the shutdown function.
301 if( ::atexit(shutdown0) <0) // Windows has atexit()
302 {
303 Service::log("Failed to set exit handler.");
304 ::exit(-1);
305 }
306}
307
308
310{
312 {
313 if(! setServiceStatus(SERVICE_RUNNING,NO_ERROR,0,0,0) )
314 ::exit(1);
315 }
316}
317
318
320{
321 if(_logstream!=&cerr)
322 {
323 delete _logstream;
324 _logstream=&cerr;
325 }
327 {
328 setServiceStatus(SERVICE_STOPPED,NO_ERROR,0,0,0);
329 _serviceRunning=false;
330 }
331}
332
333// static callback
334void Service::log(const char* message)
335{
336 (*service._logstream)<<message<<flush;
337}
338
339// static callback
340void Service::ctrlHandler(DWORD controlCode)
341{
342 switch(controlCode)
343 {
344 case SERVICE_CONTROL_SHUTDOWN:
345 case SERVICE_CONTROL_STOP:
346 ::OmniEvents_Orb_shutdown(controlCode);
347 service.setServiceStatus(SERVICE_STOP_PENDING,NO_ERROR,0,1,6000);
348 break;
349 case 128: // User defined code.
351 break;
352 default:
353 break;
354 }
355}
356
357
358void Service::setArgcArgv(int& argc,char**& argv)
359{
361 vector<char*> args;
362 char* param =::strtok(_parameters,"\t ");
363 while(param)
364 {
365 args.push_back(param);
366 param=::strtok(NULL,"\t ");
367 }
368 if(!args.empty())
369 {
370 _argv=new char*[argc+args.size()]; // deleted by ~Service()
371 int i=0;
372 _argv[i++]=argv[0];
373 for(int j=0; j<args.size(); ++j)
374 _argv[i++]=args[j];
375 for(int k=1; k<argc; ++k)
376 _argv[i++]=argv[k];
377 argv=_argv;
378 argc=i;
379 }
380}
381
382
383void Service::install(int argc,char** argv) const
384{
385 //
386 // Install service
387 char exe_file_name[MAX_PATH];
388 if(0== ::GetModuleFileName(0, exe_file_name, MAX_PATH) )
389 {
391 ::exit(1);
392 }
393
394 string command =string(exe_file_name)+" service";
395
396 SC_HANDLE scmanager =::OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
397 if(!scmanager)
398 {
400 ::exit(1);
401 }
402 SC_HANDLE service =
403 ::CreateService(
404 scmanager,
406 "CORBA Event Daemon",
407 SERVICE_ALL_ACCESS,
408 SERVICE_WIN32_OWN_PROCESS,
409 SERVICE_AUTO_START,
410 SERVICE_ERROR_NORMAL,
411 command.c_str(),
412 0,0,0,0,0
413 );
414 if(!service)
415 {
417 ::exit(1);
418 }
419 if(0== ::CloseServiceHandle(service) )
420 {
422 ::exit(1);
423 }
424 if(0== ::CloseServiceHandle(scmanager) )
425 {
427 ::exit(1);
428 }
429
430 //
431 // Set the service's parameters & description.
432 writeParameters(argc,argv);
433 RegistryKey rkey(HKEY_LOCAL_MACHINE,_regSubKey,KEY_SET_VALUE);
434 if(!rkey)
435 {
436 Win::perror("Can't open registry key at " HERE);
437 ::exit(1);
438 }
439 if(0!= rkey.setValueStr("Description",
440 "Asynchronous broadcast channels for CORBA applications.") )
441 {
442 Win::perror("Can't set registry value 'Description' at " HERE);
443 ::exit(1);
444 }
445}
446
447
449{
450 SC_HANDLE scmanager =::OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
451 if(!scmanager)
452 {
454 ::exit(1);
455 }
456 SC_HANDLE service =
457 ::OpenService(
458 scmanager,
460 SC_MANAGER_ALL_ACCESS
461 );
462 if(!service)
463 {
465 ::exit(1);
466 }
467 if(0== ::DeleteService(service) )
468 {
470 ::exit(1);
471 }
472 if(0== ::CloseServiceHandle(service) )
473 {
475 ::exit(1);
476 }
477 if(0== ::CloseServiceHandle(scmanager) )
478 {
480 ::exit(1);
481 }
482}
483
484
486{
487 RegistryKey rkey(HKEY_LOCAL_MACHINE,_regSubKey);
488 if(!rkey)
489 {
490 Win::perror("Can't open registry key at " HERE);
491 ::exit(1);
492 }
493 _parameters=rkey.queryValueStr("Parameters"); // deleted by ~Service()
494 if(_parameters==NULL)
495 {
496 Win::perror("Can't get Parameters at " HERE);
497 ::exit(1);
498 }
499}
500
501
502void Service::writeParameters(int argc, char** argv) const
503{
504 RegistryKey rkey(HKEY_LOCAL_MACHINE,_regSubKey,KEY_SET_VALUE);
505 if(!rkey)
506 {
507 Win::perror("Can't open registry key at " HERE);
508 ::exit(1);
509 }
510 string parameters ="";
511 for(int i=2; i<argc; ++i)
512 {
513 if(!parameters.empty())
514 parameters+=" ";
515 parameters+=argv[i];
516 }
517 if(0!= rkey.setValueStr("Parameters",parameters.c_str()) )
518 {
519 Win::perror("Can't set registry value 'Parameters' at " HERE);
520 ::exit(1);
521 }
522}
523
524
526 DWORD currentState,
527 DWORD win32ExitCode,
528 DWORD serviceSpecificExitCode,
529 DWORD checkPoint,
530 DWORD waitHint)
531{
532 SERVICE_STATUS s;
533 s.dwServiceType =SERVICE_WIN32_OWN_PROCESS;
534 s.dwCurrentState =currentState;
535 s.dwServiceSpecificExitCode=serviceSpecificExitCode;
536 s.dwCheckPoint =checkPoint;
537 s.dwWaitHint =waitHint;
538
539 if(currentState==SERVICE_START_PENDING)
540 s.dwControlsAccepted=0;
541 else
542 s.dwControlsAccepted=SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN;
543
544 if(serviceSpecificExitCode==0)
545 s.dwWin32ExitCode=win32ExitCode;
546 else
547 s.dwWin32ExitCode=ERROR_SERVICE_SPECIFIC_ERROR;
548
549 return (0!= ::SetServiceStatus(_serviceStatusHandle,&s) );
550}
551
552} // end namespace OmniEvents
#define HERE
Generates a string literal that describes the filename and line number.
int main(int argc, char **argv)
The main process entry point.
Definition main.cc:66
void OmniEvents_Orb_shutdown(int signum)
Signal handler, sets Orb::_shutdownRequested.
Definition main.cc:312
void OmniEvents_Orb_bumpTraceLevel(int signum)
Signal handler, each call to this method 'bumps' up the trace level by 5, modulo 45.
Definition main.cc:317
#define PACKAGE_NAME
Definition config.h:158
static Service service
Singleton - only at file scope.
void shutdown0(void)
Param to atexit().
void setLogFunction(void(*logFunction)(const char *))
void pidfile(const char *val)
Set _pidfile.
Daemon()
No implementation.
void foreground(bool val)
Set _foreground.
void runningOk()
Called to signal that all startup operations have completed OK.
void tracefile(const char *val)
Set _tracefile.
void daemonize()
Redirects output streams to tracefile.
Utility class, contains functions that Windows should have, but doesn't.
static void perror(const char *s=NULL)
static const char * strerror(DWORD e)
Opens a windows registry key, and closed it upon destruction.
~RegistryKey()
Destructor, closes the key.
char * queryValueStr(const char *name, const int maxlen=2048) const
RegistryKey()
No implementation.
RegistryKey(HKEY hkey, bool open=true)
int setValueStr(const char *name, const char *data)
Singleton class that contains various methods for running a Windows service.
void tracefile(const char *val)
Set _tracefile.
void Service::setArgcArgv(int &argc, char **&argv)
void install(int argc, char **argv) const
std::ostream * _logstream
void pidfile(const char *val)
Set _pidfile.
void foreground(bool val)
Set _foreground.
bool Service::setServiceStatus(DWORD currentState, DWORD win32ExitCode, DWORD serviceSpecificExitCode, DWORD checkPoint, DWORD waitHint)
char * _tracefile
The tracefile name (if any).
void start(int &argc, char **&argv)
void shutdown()
Exit handler set with ::on_exit() - shuts down the service.
const char * _regSubKey
void writeParameters(int argc, char **argv) const
Writes args 2+ to the Registry.
SERVICE_STATUS_HANDLE _serviceStatusHandle
Windows thing.
char * _parameters
Stores parameters read from the registry.
static void log(const char *message)
Callback, used as a parameter to omniORB::setLogFunction().
void runningOk()
Called to signal that all startup operations have completed OK.
void readParameters()
Populates _parameters from the Registry.
void daemonize()
Redirects output streams to tracefile.
static void ctrlHandler(DWORD controlCode)
Handles control codes from the Service Control Manager.
char ** _argv
Replacement argv array, read from registry.