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 
33 using 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()
47 namespace omniORB {
48  void setLogFunction(void (*logFunction)(const char*));
49 }
50 
51 namespace OmniEvents {
52 
54 class Win
55 {
56 public:
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;
90 private:
92  RegistryKey(HKEY hkey, bool open=true):_hkey(hkey),_open(open){}
93 public:
94  RegistryKey(RegistryKey& right);
95  RegistryKey(HKEY hkey, const char* subkey, REGSAM samDesired=KEY_QUERY_VALUE);
96  ~RegistryKey();
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 
103 RegistryKey::RegistryKey(RegistryKey& right):
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 
136 int 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 
150 char* 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 
168 Daemon::Daemon(int& argc,char**& argv) { service.start(argc,argv); }
169 void Daemon::tracefile(const char* val) { service.tracefile(val); }
170 void Daemon::pidfile(const char* val) { service.pidfile(val); }
171 void Daemon::foreground(bool val) { service.foreground(val); }
172 void Daemon::daemonize() { service.daemonize(); }
173 void Daemon::runningOk() { service.runningOk(); }
175 
176 void 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 
202 void Service::tracefile(const char* val)
203 {
204  delete[] _tracefile;
205  _tracefile=::strdup(val);
206 }
207 
208 
209 void Service::pidfile(const char* val)
210 {
211  Service::log("Option -P not supported on windows.\n");
212  ::exit(1);
213 }
214 
215 
216 void Service::foreground(bool val)
217 {
218  Service::log("Option -f not supported on windows.\n");
219  ::exit(1);
220 }
221 
222 
223 void 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(
232  PACKAGE_NAME,
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  {
253  Win::perror(HERE);
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  {
272  readParameters();
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 {
311  if(_serviceRunning)
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  }
326  if(_serviceRunning)
327  {
328  setServiceStatus(SERVICE_STOPPED,NO_ERROR,0,0,0);
329  _serviceRunning=false;
330  }
331 }
332 
333 // static callback
334 void Service::log(const char* message)
335 {
336  (*service._logstream)<<message<<flush;
337 }
338 
339 // static callback
340 void 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.
350  ::OmniEvents_Orb_bumpTraceLevel(controlCode);
351  break;
352  default:
353  break;
354  }
355 }
356 
357 
358 void Service::setArgcArgv(int& argc,char**& argv)
359 {
360  readParameters();
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 
383 void 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  {
390  Win::perror(HERE);
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  {
399  Win::perror(HERE);
400  ::exit(1);
401  }
402  SC_HANDLE service =
403  ::CreateService(
404  scmanager,
405  PACKAGE_NAME,
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  {
416  Win::perror(HERE);
417  ::exit(1);
418  }
419  if(0== ::CloseServiceHandle(service) )
420  {
421  Win::perror(HERE);
422  ::exit(1);
423  }
424  if(0== ::CloseServiceHandle(scmanager) )
425  {
426  Win::perror(HERE);
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 
448 void Service::uninstall() const
449 {
450  SC_HANDLE scmanager =::OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
451  if(!scmanager)
452  {
453  Win::perror(HERE);
454  ::exit(1);
455  }
456  SC_HANDLE service =
457  ::OpenService(
458  scmanager,
459  PACKAGE_NAME,
460  SC_MANAGER_ALL_ACCESS
461  );
462  if(!service)
463  {
464  Win::perror(HERE);
465  ::exit(1);
466  }
467  if(0== ::DeleteService(service) )
468  {
469  Win::perror(HERE);
470  ::exit(1);
471  }
472  if(0== ::CloseServiceHandle(service) )
473  {
474  Win::perror(HERE);
475  ::exit(1);
476  }
477  if(0== ::CloseServiceHandle(scmanager) )
478  {
479  Win::perror(HERE);
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 
502 void 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 
525 bool Service::setServiceStatus(
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 PACKAGE_NAME
Definition: config.h:158
#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
static Service service
Singleton - only at file scope.
void shutdown0(void)
Param to atexit().
Definition: daemon_unix.cc:114
void setLogFunction(void(*logFunction)(const char *))
void pidfile(const char *val)
Set _pidfile.
Definition: daemon_unix.cc:108
Daemon()
No implementation.
void foreground(bool val)
Set _foreground.
Definition: daemon_unix.cc:109
void runningOk()
Called to signal that all startup operations have completed OK.
Definition: daemon_unix.cc:111
void tracefile(const char *val)
Set _tracefile.
Definition: daemon_unix.cc:107
void daemonize()
Redirects output streams to tracefile.
Definition: daemon_unix.cc:110
Utility class, contains functions that Windows should have, but doesn't.
static const char * strerror(DWORD e)
static void perror(const char *s=NULL)
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 install(int argc, char **argv) const
std::ostream * _logstream
void pidfile(const char *val)
Set _pidfile.
void foreground(bool val)
Set _foreground.
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.
void uninstall() const