summaryrefslogtreecommitdiff
path: root/ace/NT_Service.h
blob: c266454568eff39391d11d28e54f7890d2d3612a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
/* -*- C++ -*- */
// $Id$

// ============================================================================
//
// = LIBRARY
//    ace
//
// = FILENAME
//    NT_Service.h
//
// = AUTHOR
//    Steve Huston
//
// ============================================================================

#ifndef ACE_NT_SERVICE_H
#define ACE_NT_SERVICE_H

#include "ace/inc_user_config.h"

#if !defined (ACE_LACKS_PRAGMA_ONCE)
# pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */

#if defined (ACE_HAS_WINNT4) && ACE_HAS_WINNT4 != 0

#include "ace/Service_Object.h"
#include "ace/Synch.h"
#include "ace/Task.h"

// NT Services can be implemented using the framework defined by the
// ACE_NT_Service class, and the macros defined in this file.  Some quick
// refresher notes on NT Services:
//   - The main program defines an array of entries describing the services
//     offered.  The ACE_NT_SERVICE_ENTRY macro can help with this.
//   - For each service, a separate ServiceMain and Handler function need to
//     be defined.  These are taken care of by the ACE_NT_SERVICE_DEFINE
//     macro.
//   - When the main program/thread calls StartServiceCtrlDispatcher, NT
//     creates a thread for each service, and runs the ServiceMain function
//     for the service in that new thread.  When that thread exits, the service
//     is gone.
//
// To use this facility, you could derive a class from ACE_Service_Object (if
// you want to start via ACE's service configurator), or use any other class
// to run when the image starts (assuming that NT runs the image).  You must
// set up an NT SERVICE_TABLE_ENTRY array to define your service(s).  You
// can use the ACE_NT_SERVICE_... macros defined below for this.
// A SERVICE_TABLE might look like this:
//       ACE_NT_SERVICE_REFERENCE(Svc1);  // If service is in another file
//       SERVICE_TABLE_ENTRY myServices[] = {
//                    ACE_NT_SERVICE_ENTRY ("MyNeatService", Svc1),
//                    { 0, 0 } };
//
// In the file where your service(s) are implemented, use the
// ACE_NT_SERVICE_DEFINE macro to set up the following:
//    1. A pointer to the service's implementation object (must be derived
//       from ACE_NT_Service).
//    2. The service's Handler function (forwards all requests to the
//       ACE_NT_Service-derived object's handle_control function).
//    3. The service's ServiceMain function.  Creates a new instance of the
//       ACE_NT_Service-derived class SVCCLASS, unless one has been created
//       already.
//
// If you are using all the default constructor values, you
// can let the generated ServiceMain function create the object, else
// you need to create it by hand before calling StartServiceCtrlDispatcher.
// Set the pointer so ServiceMain won't create another one.
// Another reason you may want to do the object creation yourself is if you
// want to also implement suspend and resume functions (the ones inherited
// from ACE_Service_Object) to do something intelligent to the services which
// are running, like call their handle_control functions to request suspend
// and resume actions, similar to what NT would do if a Services control panel
// applet would do if the user clicks on Suspend.


// ACE_NT_SERVICE_START_TIMEOUT is an estimate of the number of milliseconds
// your service will take to start.  Default is 5 seconds; you can pass
// a different value (or set one) when you create the ACE_NT_Service object
// for your service.
#if !defined ACE_NT_SERVICE_START_TIMEOUT
#define ACE_NT_SERVICE_START_TIMEOUT  5000
#endif /* ACE_NT_SERVICE_TIMEOUT */

class ACE_Export ACE_NT_Service : public ACE_Task<ACE_MT_SYNCH>
{
  // = TITLE
  //     Provide the base class which defines the interface for controlling
  //     an NT service.
  //
  // = DESCRIPTION
public:
  // = Initialization and termination methods.
  ACE_NT_Service (DWORD start_timeout = ACE_NT_SERVICE_START_TIMEOUT,
                  DWORD service_type = SERVICE_WIN32_OWN_PROCESS,
                  DWORD controls_mask = SERVICE_ACCEPT_STOP);
  // Constructor primarily for use when running the service.

  ACE_NT_Service (LPCTSTR name,
                  LPCTSTR desc = 0,
                  DWORD start_timeout = ACE_NT_SERVICE_START_TIMEOUT,
                  DWORD service_type = SERVICE_WIN32_OWN_PROCESS,
                  DWORD controls_mask = SERVICE_ACCEPT_STOP);
  // Constructor primarily for use when inserting/removing/controlling
  // the service.

  virtual ~ACE_NT_Service (void);

  // = Functions to operate the service

  virtual int open (void *args = 0);
  // Hook called to open the service.  By default, will set the status to
  // START_PENDING, svc(), wait(), then set the status to STOPPED.

  virtual int svc (void);
  // The actual service implementation.  This function need not be overridden
  // by applications that are just using SCM capabilities, but must be
  // by subclasses when actually running the service.  It is expected that
  // this function will set the status to RUNNING.

  virtual void  handle_control (DWORD control_code);
  // This function is called in response to a request from the Service
  // Dispatcher.  It must interact with the svc() function to effect the
  // requested control operation.  The default implementation handles
  // all requests as follows:
  //    SERVICE_CONTROL_STOP: set stop pending, set cancel flag
  //    SERVICE_CONTROL_PAUSE: set pause pending, suspend(), set paused
  //    SERVICE_CONTROL_CONTINUE: set continue pending, resume(), set running
  //    SERVICE_CONTROL_INTERROGATE: reports current status
  //    SERVICE_CONTROL_SHUTDOWN: same as SERVICE_CONTROL_STOP.

  void svc_handle (const SERVICE_STATUS_HANDLE new_svc_handle);
  // Set the svc_handle_ member.  This is only a public function because
  // the macro-generated service function calls it.


  // = Methods which can be used to do SCP-like functions. The first group
  // are used to register/insert and remove the service's definition in the
  // SCM registry.

  void name (LPCTSTR name, LPCTSTR desc = 0);
  // Sets the name and description for the service.
  // If desc is 0, it takes the same value as name.

  LPCTSTR name (void) const;
  // Get the service name.

  LPCTSTR desc (void) const;
  // Get the service description.

  int insert (DWORD start_type = SERVICE_DEMAND_START,
              DWORD error_control = SERVICE_ERROR_IGNORE,
              LPCTSTR exe_path = 0,
              LPCTSTR group_name = 0,
              LPDWORD tag_id = 0,
              LPCTSTR dependencies = 0,
              LPCTSTR account_name = 0,
              LPCTSTR password = 0);
  // Insert (create) the service in the NT Service Control Manager,
  // with the given creation values.  exe_path defaults to the path name
  // of the program that calls the function.  All other 0-defaulted arguments
  // pass 0 into the service creation, taking NT_specified defaults.
  // Returns -1 on error, 0 on success.

  int remove (void);
  // Remove the service from the NT Service Control Manager.  Returns -1 on
  // error, 0 on success.  This just affects the SCM and registry - the
  // can and will keep running fine if it is already running.

  int startup (DWORD startup);
  // Sets the startup type for the service.  Returns -1 on error, 0 on success.

  DWORD startup (void);
  // Returns the current startup type.


  // = Methods which control the service's execution.
  // These methods to start/pause/resume/stop/check the service all have the
  // following common behavior with respect to <wait_time> and return value.
  //   <wait_time> is a pointer to an ACE_Time_Value object.  If not supplied
  //   (a zero pointer) the function will wait indefinitely for the action
  //   to be finalized (service reach running state, completely shut down,
  //   etc.) or get "stuck" before returning.  If the time is supplied, it
  //   specifies how long to wait for the service to reach a steady state,
  //   and on return, it is updated to the service's last reported wait hint.
  //   So, if you want to control the waiting yourself (for example, you want
  //   to react to UI events during the wait) specify a <wait_time> of (0, 0)
  //   and use the updated time to know when to check the service's state
  //   again.
  //   NOTE!!!! The wait_time things don't work yet.  The calls always
  //   check status once, and do not wait for it to change.
  //
  //   The return value from start_svc, stop_svc, pause_svc, continue_svc is
  //   0 if the request to NT to effect the change was made successfully.  The
  //   service may refuse to change, or not do what you wanted; so if you
  //   need to know, supply a <svc_state> pointer to receive the service's
  //   reported last state on return and check it to see if it's what you
  //   want.  The functions only return -1 when the actual request to the
  //   service is refused - this would include privilege restrictions and
  //   if the service is not configured to receive the request (this is most
  //   likely to happen in the case of pause and continue).

  int start_svc (ACE_Time_Value *wait_time = 0,
                 DWORD *svc_state = 0,
                 DWORD argc = 0, LPCTSTR *argv = 0);
  // Start the service (must have been inserted before).  wait_time is the
  // time to wait for the service to reach a steady state before returning.
  // If it is 0, the function waits as long as it takes for the service to
  // reach the 'running' state, or gets stuck in some other state, or exits.
  // If <wait_time> is supplied, it is updated on return to hold the service's
  // last reported wait hint.
  // svc_state can be used to receive the state which the service settled in.
  // If the value is 0, the service never ran.  argc/argv are passed
  // to the service's ServiceMain function when it starts.  Returns 0 for
  // success, -1 for error.

  int stop_svc (ACE_Time_Value *wait_time = 0, DWORD *svc_state = 0);
  // Requests the service to stop.  Will wait up to <wait_time> for the service
  // to actually stop.  If not specified, the function waits until the service
  // either stops or gets stuck in some other state before it stops.
  // If <svc_state> is specified, it receives the last reported state of the
  // service.  Returns 0 if the request was made successfully, -1 if not.

  int pause_svc (ACE_Time_Value *wait_time = 0, DWORD *svc_state = 0);
  // Pause the service.

  int continue_svc (ACE_Time_Value *wait_time = 0, DWORD *svc_state = 0);
  // Continue the service.

  DWORD state (ACE_Time_Value *wait_hint = 0);
  // Get the current state for the service.  If <wait_hint> is not 0, it
  // receives the service's reported wait hint.
  // Note that this function returns 0 on failure (not -1 as is usual in ACE).
  // A zero return would (probably) only be returned if there is either no
  // service with the given name in the SCM database, or the caller does not
  // have sufficient rights to access the service state.  The set of valid
  // service state values are all greater than 0.

  int state (DWORD *pstate, ACE_Time_Value *wait_hint = 0);
  // A version of state() which returns -1 for failure, 0 for success.
  // The DWORD pointed to by pstate receives the state value.

  int test_access (DWORD desired_access = SERVICE_ALL_ACCESS);
  // Test access to the object's service in the SCM.  The service must already
  // have been inserted in the SCM database.  This function has no affect on
  // the service itself.  Returns 0 if the specified access is allowed, -1
  // otherwise (either the access is denied, or there is a problem with the
  // service's definition - check ACE_OS::last_error to get the specific
  // error indication.

  ACE_ALLOC_HOOK_DECLARE;
  // Declare the dynamic allocation hooks.

protected:
  int report_status (DWORD new_status, DWORD time_hint = 0);

  SC_HANDLE svc_sc_handle (void);
  // Return the svc_sc_handle_ member. If the member is null,
  // it retrieves the handle from the Service Control Manager and caches it.

  void wait_for_service_state (DWORD desired_state, ACE_Time_Value *wait_time);
  // Waits for the service to reach <desired_state> or get (apparently) stuck
  // before it reaches that state.  Will wait at most <wait_time> to get to
  // the desired state.  If <wait_time> is 0, then the function keeps waiting
  // until the desired state is reached or the service doesn't update its
  // state any further.
  // The svc_status_ class member is updated upon return.
  // NOTE - the timeout doesn't currently work - it always acts like
  // ACE_Time_Value::zero is passed - it checks the state once but doesn't
  // wait after that.

  virtual void stop_requested (DWORD control_code);
  // Called by handle_control () when a stop/shutdown was requested.

  virtual void pause_requested (DWORD control_code);
  // Called by handle_control () when a pause was requested.

  virtual void continue_requested (DWORD control_code);
  // Called by handle_control () when a continue was requested.

  virtual void interrogate_requested (DWORD control_code);
  // Called by handle_control () when a interrogate was requested.

protected:
  DWORD                  start_time_;   // Estimate of init time needed
  SERVICE_STATUS_HANDLE  svc_handle_;   // Service handle - doesn't need close.
  SERVICE_STATUS         svc_status_;

  SC_HANDLE              svc_sc_handle_;// Service's SCM handle
  LPTSTR                 name_;
  LPTSTR                 desc_;

};

// These macros help to get things set up correctly at compile time
// and to take most of the grudge work out of creating the proper
// functions and doing the registrations.
//
// ACE_NT_SERVICE_DEFINE - defines the 'ServiceMain' function which NT will
//                         call in its own thread when the service control
//                         dispatcher starts.

#define ACE_NT_SERVICE_DEFINE(SVCNAME, SVCCLASS, SVCDESC)                   \
  ACE_NT_Service * _ace_nt_svc_obj_##SVCNAME = 0;                           \
  VOID WINAPI ace_nt_svc_handler_##SVCNAME (DWORD fdwControl) {             \
    _ace_nt_svc_obj_##SVCNAME->handle_control(fdwControl);                  \
  }                                                                         \
  VOID WINAPI ace_nt_svc_main_##SVCNAME (DWORD dwArgc, LPTSTR *lpszArgv) {  \
    int delete_svc_obj = 0;                                                 \
    if (_ace_nt_svc_obj_##SVCNAME == 0) {                                   \
      ACE_NEW (_ace_nt_svc_obj_##SVCNAME, SVCCLASS);                        \
      if (_ace_nt_svc_obj_##SVCNAME == 0)                                   \
        return;                                                             \
      delete_svc_obj = 1;                                                   \
    }                                                                       \
    _ace_nt_svc_obj_##SVCNAME->init(dwArgc, lpszArgv);                      \
    _ace_nt_svc_obj_##SVCNAME->svc_handle(                                  \
                  RegisterServiceCtrlHandler(SVCDESC,                       \
                                          &ace_nt_svc_handler_##SVCNAME));  \
    _ace_nt_svc_obj_##SVCNAME->open();                                      \
    _ace_nt_svc_obj_##SVCNAME->wait();                                      \
    _ace_nt_svc_obj_##SVCNAME->fini();                                      \
    if (delete_svc_obj) {                                                   \
      delete _ace_nt_svc_obj_##SVCNAME;                                     \
      _ace_nt_svc_obj_##SVCNAME = 0;                                        \
    }                                                                       \
    return;                                                                 \
  }

#define ACE_NT_SERVICE_REFERENCE(SVCNAME)                                  \
extern VOID WINAPI ace_nt_svc_main_##SVCNAME (DWORD dwArgc, LPTSTR *lpszArgv);

#define ACE_NT_SERVICE_ENTRY(SVCDESC, SVCNAME)                             \
                      { SVCDESC, &ace_nt_svc_main_##SVCNAME }

#define ACE_NT_SERVICE_RUN(SVCNAME, SVCINSTANCE, RET)                      \
  SERVICE_TABLE_ENTRY _ace_nt_svc_table[2] =                               \
  {                                                                        \
    ACE_NT_SERVICE_ENTRY(#SVCNAME, SVCNAME),                               \
    { 0, 0 }                                                               \
  };                                                                       \
  _ace_nt_svc_obj_##SVCNAME = SVCINSTANCE;                                 \
  ACE_OS::last_error (0);                                                  \
  int RET = StartServiceCtrlDispatcher(_ace_nt_svc_table);

#if defined (__ACE_INLINE__)
#include "ace/NT_Service.i"
#endif /* __ACE_INLINE__ */

#endif /* ACE_HAS_WINNT4 */
#endif /* ACE_SERVICE_OBJECT_H */