1 karl 1.2 //%2006////////////////////////////////////////////////////////////////////////
|
2 h.sterling 1.1 //
3 // Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development
4 // Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems.
5 // Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.;
6 // IBM Corp.; EMC Corporation, The Open Group.
7 // Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.;
8 // IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group.
9 // Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.;
10 // EMC Corporation; VERITAS Software Corporation; The Open Group.
|
11 karl 1.2 // Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.;
12 // EMC Corporation; Symantec Corporation; The Open Group.
|
13 h.sterling 1.1 //
14 // Permission is hereby granted, free of charge, to any person obtaining a copy
15 // of this software and associated documentation files (the "Software"), to
16 // deal in the Software without restriction, including without limitation the
17 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
18 // sell copies of the Software, and to permit persons to whom the Software is
19 // furnished to do so, subject to the following conditions:
20 //
21 // THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
22 // ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
23 // "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
24 // LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
25 // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
26 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
27 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 //==============================================================================
31 //
32 // Author: Tony Fiorentino (fiorentino_tony@emc.com)
33 //
34 h.sterling 1.1 //%/////////////////////////////////////////////////////////////////////////////
35 #include <time.h>
36 #include <stdio.h>
37 #include <windows.h>
38 #include <process.h>
39
40 #include "Service.h"
41
42 //-------------------------------------------------------------------------
43 // G L O B A L S
44 //-------------------------------------------------------------------------
45 int Service::g_argc = 0;
46 char **Service::g_argv = NULL;
47 char *Service::g_service_name = NULL;
48 char *Service::g_event_source = NULL;
49 DWORD Service::g_flags = 0;
50 DWORD Service::g_current_state = 0;
51 SERVICE_STATUS_HANDLE Service::g_service_status_handle = 0;
52 SERVICE_MAIN_T Service::g_service_main = NULL;
53
54 //-------------------------------------------------------------------------
55 h.sterling 1.1 // P U B L I C
56 //-------------------------------------------------------------------------
|
57 kumpf 1.3 Service::Service()
|
58 h.sterling 1.1 {
59 }
60
|
61 kumpf 1.3 Service::Service(const char* service_name)
|
62 h.sterling 1.1 {
|
63 kumpf 1.3 // ATTN: I have to allocate memory here, unless I want to change the
64 // char* g_service_name to a char[]. Changing to an array of char
65 // affects a lot more code. Previously, this method had a char* for its
66 // input parameter, and since we were using #define's the values were
67 // always in memory.
68 // Now we are using an interface method that returns const char* -hns PEP222
|
69 h.sterling 1.1 g_service_name = (char*) malloc(strlen(service_name)+1);
70 memset(g_service_name, '\0', strlen(service_name)+1);
71 strncpy(g_service_name, service_name, strlen(service_name));
72 }
73
|
74 kumpf 1.3 Service::Service(
75 const char* service_name,
76 char* event_source)
|
77 h.sterling 1.1 {
78 g_event_source = event_source;
79
80 g_service_name = (char*) malloc(strlen(service_name)+1);
81 memset(g_service_name, '\0', strlen(service_name)+1);
82 strncpy(g_service_name, service_name, strlen(service_name));
83 }
84
|
85 kumpf 1.3 Service::~Service()
|
86 h.sterling 1.1 {
|
87 kumpf 1.3 // ATTN: Will open Bugzilla on this. I need to change this to either use
88 // char arrays OR deallocate the memory elsewhere. I cannot use any fancy
89 // AutoPtr stuff since Pegasus code is kept out of this OS-specific file
|
90 h.sterling 1.1 // -hns PEP#222
91 /*if (g_service_name != NULL)
92 {
93 free(g_service_name);
94 }*/
95 }
96
|
97 kumpf 1.3 void Service::SetServiceName(char* service_name)
|
98 h.sterling 1.1 {
99 if (g_service_name != NULL)
100 {
101 free(g_service_name);
102 }
103
104 g_service_name = (char*) malloc(strlen(service_name)+1);
105 memset(g_service_name, '\0', strlen(service_name)+1);
106 strncpy(g_service_name, service_name, strlen(service_name));
107 }
108
109 /*-------------------------------------------------------------------------*
110 * Method: Install *
111 * *
112 * Args: *
113 * display_name *
114 * The service's display name (hopefully more descriptive, will show *
115 * up in the Win32 Services MMC snapin as "Service Name"). *
116 * *
117 * exe_name: *
118 * The service's executable name (full path, please) *
119 h.sterling 1.1 * *
120 * flags: *
121 * Reserved. Currently unused. *
122 * *
123 * Description: *
124 * This function creates the service. *
125 * *
126 * NOTE: If the process is successfully launched as a Win32 service, this *
127 * function never returns, but calls exit() instead. *
128 *-------------------------------------------------------------------------*/
|
129 kumpf 1.3 Service::ReturnCode Service::Install(
|
130 h.sterling 1.1 char *display_name,
131 char *description,
132 char *exe_name)
133 {
|
134 kumpf 1.3 ReturnCode status = SERVICE_RETURN_SUCCESS;
135 SC_HANDLE sch;
|
136 h.sterling 1.1
|
137 kumpf 1.3 if (g_service_name == NULL || display_name == NULL || exe_name == NULL)
138 return SERVICE_ERROR_NOT_FOUND; // SERVICE_ERROR_NOT_FOUND
139 else if ((sch = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)) ==
140 NULL)
141 status = get_error(GetLastError(), "open");
142 else
143 {
144 SC_HANDLE service = CreateService(
145 sch, // SCManager database
146 g_service_name, // name of service
147 display_name, // service name to display
148 SERVICE_ALL_ACCESS, // desired access
149 SERVICE_WIN32_OWN_PROCESS, // service type
150 SERVICE_DEMAND_START, // start type
151 SERVICE_ERROR_NORMAL, // error control type
152 exe_name, // service's binary
153 NULL, // no load ordering group
154 NULL, // no tag identifier
155 NULL, // no dependencies
156 NULL, // LocalSystem account
157 NULL); // no password
158 kumpf 1.3
159 if (service == NULL)
|
160 h.sterling 1.1 {
|
161 kumpf 1.3 status = get_error(GetLastError(), "create");
162 return status;
|
163 h.sterling 1.1 }
|
164 kumpf 1.3 else
|
165 h.sterling 1.1 {
|
166 kumpf 1.3 change_service_description(service, description);
167 CloseServiceHandle(service);
|
168 h.sterling 1.1 }
|
169 kumpf 1.3
170 CloseServiceHandle(sch);
|
171 h.sterling 1.1 }
172
|
173 kumpf 1.3 return status;
|
174 h.sterling 1.1 }
175
176 /*-------------------------------------------------------------------------*
177 * Method: Remove *
178 * *
179 * Description: *
180 * Removes the service. *
181 *-------------------------------------------------------------------------*/
|
182 kumpf 1.3 Service::ReturnCode Service::Remove()
|
183 h.sterling 1.1 {
|
184 kumpf 1.3 ReturnCode status = SERVICE_RETURN_SUCCESS;
185 SC_HANDLE sch;
|
186 h.sterling 1.1
|
187 kumpf 1.3 if (g_service_name == NULL)
188 return SERVICE_ERROR_NOT_FOUND; /* SERVICE_ERROR_NOT_FOUND */
189 else if ((sch = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE)) ==
190 NULL)
191 status = get_error(GetLastError(), "open");
192 else
193 {
194 SC_HANDLE service = OpenService(sch, g_service_name, DELETE);
195
196 if (service == NULL)
|
197 h.sterling 1.1 {
|
198 kumpf 1.3 status = get_error(GetLastError(), "open");
|
199 h.sterling 1.1 }
|
200 kumpf 1.3 else
|
201 h.sterling 1.1 {
|
202 kumpf 1.3 if (!DeleteService(service))
|
203 h.sterling 1.1 {
|
204 kumpf 1.3 status = get_error(GetLastError(), "remove");
|
205 h.sterling 1.1 }
206
|
207 kumpf 1.3 CloseServiceHandle(service);
|
208 h.sterling 1.1 }
|
209 kumpf 1.3
210 CloseServiceHandle(sch);
|
211 h.sterling 1.1 }
212
|
213 kumpf 1.3 return status;
|
214 h.sterling 1.1 }
215
216 /*-------------------------------------------------------------------------*
217 * Method: Start *
218 * *
219 * Args: *
220 * wait_time: *
221 * The user supplied wait (~1 second per QueryServiceStatus() attempt) *
222 * *
223 * Description: *
224 * Attempt to start the service. *
225 *-------------------------------------------------------------------------*/
|
226 kumpf 1.3 Service::ReturnCode Service::Start(int wait_time)
|
227 h.sterling 1.1 {
|
228 kumpf 1.3 ReturnCode status = SERVICE_RETURN_SUCCESS;
229 SERVICE_STATUS service_status;
230 SC_HANDLE sch;
231
232 if (g_service_name == NULL)
233 return SERVICE_ERROR_NOT_FOUND; // SERVICE_ERROR_NOT_FOUND
234 else if ((sch = OpenSCManager(NULL, NULL, GENERIC_READ)) == NULL)
|
235 h.sterling 1.1 status = get_error(GetLastError(), "open");
|
236 kumpf 1.3 else
237 {
238 SC_HANDLE service = OpenService(
239 sch, g_service_name, SERVICE_START | SERVICE_QUERY_STATUS);
240
241 if (service == NULL)
242 status = get_error(GetLastError(), "open");
243 else if (!StartService(service, g_argc, (const char **)g_argv))
244 status = get_error(GetLastError(), "start");
245 else
|
246 h.sterling 1.1 {
|
247 kumpf 1.3 int i, max = (wait_time > 0) ? wait_time : 5;
|
248 h.sterling 1.1
|
249 kumpf 1.3 // Loop up to max times waiting for the service
250 // state to change to RUNNING
|
251 h.sterling 1.1
|
252 kumpf 1.3 for (i = 0; i < max; i++)
|
253 h.sterling 1.1 {
|
254 kumpf 1.3 if (!QueryServiceStatus(service, &service_status))
|
255 h.sterling 1.1 {
|
256 kumpf 1.3 status = get_error(GetLastError(), "query");
257 return status; // QUERY_FAIL
|
258 h.sterling 1.1 }
259
|
260 kumpf 1.3 if (service_status.dwCurrentState == SERVICE_RUNNING)
261 break;
|
262 h.sterling 1.1
|
263 kumpf 1.3 Sleep(1 * CLOCKS_PER_SEC);
|
264 h.sterling 1.1 }
265
|
266 kumpf 1.3 status = (i < max) ?
267 SERVICE_RETURN_SUCCESS : SERVICE_ERROR_REQUEST_TIMEOUT;
268
269 CloseServiceHandle(service);
|
270 h.sterling 1.1 }
|
271 kumpf 1.3
272 CloseServiceHandle(sch);
|
273 h.sterling 1.1 }
274
|
275 kumpf 1.3 return status;
276 }
|
277 h.sterling 1.1
278 /*-------------------------------------------------------------------------*
279 * Method: Stop *
280 * *
281 * Args: *
282 * wait_time: *
283 * The user supplied wait (~1 second per QueryServiceStatus() attempt) *
284 * *
285 * Description: *
286 * Attempt to stop the service. *
287 *-------------------------------------------------------------------------*/
|
288 kumpf 1.3 Service::ReturnCode Service::Stop(int wait_time)
|
289 h.sterling 1.1 {
|
290 kumpf 1.3 ReturnCode status = SERVICE_RETURN_SUCCESS;
291 SERVICE_STATUS service_status;
292 SC_HANDLE sch;
293
294 if (g_service_name == NULL)
295 return SERVICE_ERROR_NOT_FOUND; // SERVICE_ERROR_NOT_FOUND
296 else if ((sch = OpenSCManager(NULL, NULL, GENERIC_READ)) == NULL)
|
297 h.sterling 1.1 status = get_error(GetLastError(), "open");
|
298 kumpf 1.3 // show_error("OpenSCMManager", "service", GetLastError());
299 else
300 {
301 SC_HANDLE service = OpenService(
302 sch, g_service_name, SERVICE_STOP | SERVICE_QUERY_STATUS);
303
304 if (service == NULL)
305 status = get_error(GetLastError(), "open");
306 else if (!ControlService(
307 service, SERVICE_CONTROL_STOP, &service_status))
308 status = get_error(GetLastError(), "stop");
309 else
|
310 h.sterling 1.1 {
|
311 kumpf 1.3 int i, max = (wait_time > 0) ? wait_time : 5;
|
312 h.sterling 1.1
|
313 kumpf 1.3 // Loop up to max times waiting for the service
314 // state to change to STOPPED
|
315 h.sterling 1.1
|
316 kumpf 1.3 for (i = 0; i < max; i++)
|
317 h.sterling 1.1 {
|
318 kumpf 1.3 if (!QueryServiceStatus(service, &service_status))
|
319 h.sterling 1.1 {
|
320 kumpf 1.3 status = get_error(GetLastError(), "query");
321 return status; // QUERY_FAIL
|
322 h.sterling 1.1 }
323
|
324 kumpf 1.3 if (service_status.dwCurrentState == SERVICE_STOPPED)
325 break;
|
326 h.sterling 1.1
|
327 kumpf 1.3 Sleep(1 * CLOCKS_PER_SEC);
|
328 h.sterling 1.1 }
329
|
330 kumpf 1.3 status = (i < max) ?
331 SERVICE_RETURN_SUCCESS : SERVICE_ERROR_REQUEST_TIMEOUT;
332
333 CloseServiceHandle(service);
|
334 h.sterling 1.1 }
|
335 kumpf 1.3
336 CloseServiceHandle(sch);
|
337 h.sterling 1.1 }
338
|
339 kumpf 1.3 return status;
|
340 h.sterling 1.1 }
341
342 /*-------------------------------------------------------------------------*
343 * Method: Run *
344 * *
345 * Args: *
346 * service_main: *
347 * The user supplied service_main function (not to be confused with *
348 * real_service_main above) *
349 * *
350 * flags: *
351 * Reserved. Currently unused. *
352 * *
353 * Description: *
354 * This function interacts with the SCM to run the current process *
355 * as a Win32 service. *
356 * *
357 * NOTE: If the process is successfully launched as a Win32 service, this *
358 * function never returns, but calls exit() instead. *
359 *-------------------------------------------------------------------------*/
360
|
361 kumpf 1.3 Service::ReturnCode Service::Run(SERVICE_MAIN_T service_main, DWORD flags)
|
362 h.sterling 1.1 {
|
363 kumpf 1.3 ReturnCode status = SERVICE_RETURN_SUCCESS;
|
364 h.sterling 1.1
|
365 kumpf 1.3 SERVICE_TABLE_ENTRY dispatchTable[] =
366 {
367 { g_service_name, real_service_main },
368 { NULL, NULL }
369 };
|
370 h.sterling 1.1
|
371 kumpf 1.3 // Validate the arguments as best we can
|
372 h.sterling 1.1
|
373 kumpf 1.3 if (g_service_name == NULL || service_main == NULL)
374 return SERVICE_ERROR_NOT_FOUND; // SERVICE_ERROR_NOT_FOUND
|
375 h.sterling 1.1
|
376 kumpf 1.3 // Save parameters in global variables
|
377 h.sterling 1.1
|
378 kumpf 1.3 g_flags = flags;
379 g_service_main = service_main;
|
380 h.sterling 1.1
|
381 kumpf 1.3 // Kick off the service
|
382 h.sterling 1.1
|
383 kumpf 1.3 if (!StartServiceCtrlDispatcher(dispatchTable))
|
384 h.sterling 1.1 {
|
385 kumpf 1.3 status = get_error(GetLastError(), "start");
386 return status; // FAIL
|
387 h.sterling 1.1 }
388
|
389 kumpf 1.3 // Don't call exit()
390 return status;
|
391 h.sterling 1.1 }
392
393 /*-------------------------------------------------------------------------*
394 * Method: GetState *
395 * *
396 * Description: *
397 * Returns the state of the service into "state". *
398 *-------------------------------------------------------------------------*/
|
399 kumpf 1.3 Service::ReturnCode Service::GetState(State* state)
|
400 h.sterling 1.1 {
|
401 kumpf 1.3 ReturnCode status = SERVICE_RETURN_SUCCESS;
402 SERVICE_STATUS service_status;
403 SC_HANDLE sch;
404
405 if (g_service_name == NULL)
406 return SERVICE_ERROR_NOT_FOUND; // SERVICE_ERROR_NOT_FOUND
407 else if ((sch = OpenSCManager(NULL, NULL, GENERIC_READ)) == NULL)
|
408 h.sterling 1.1 status = get_error(GetLastError(), "open");
|
409 kumpf 1.3 else
410 {
411 SC_HANDLE service =
412 OpenService(sch, g_service_name, SERVICE_QUERY_STATUS);
413
414 if (service == NULL)
415 status = get_error(GetLastError(), "open");
416 else if (!QueryServiceStatus(service, &service_status))
417 status = get_error(GetLastError(), "query");
418 else
|
419 h.sterling 1.1 {
|
420 kumpf 1.3 *state = get_state(service_status.dwCurrentState);
421 CloseServiceHandle(service);
|
422 h.sterling 1.1 }
|
423 kumpf 1.3
424 CloseServiceHandle(sch);
|
425 h.sterling 1.1 }
426
|
427 kumpf 1.3 return status;
|
428 h.sterling 1.1 }
429
430 /*-------------------------------------------------------------------------*
431 * Method: LogEvent *
432 * *
433 * Args: *
434 * event_type: *
435 * The Win32 event type. Valid event types are: *
436 * EVENTLOG_SUCCESS : Success event *
437 * EVENTLOG_ERROR_TYPE : Error event *
438 * EVENTLOG_WARNING_TYPE : Warning event *
439 * EVENTLOG_INFORMATION_TYPE : Information event *
440 * EVENTLOG_AUDIT_SUCCESS : Success audit event *
441 * EVENTLOG_AUDIT_FAILURE : Failure audit event *
442 * *
443 * event_id: *
444 * A fancy name for error code or error number. *
445 * *
446 * string: *
447 * String to be logged or merged in with the error string in the *
448 * message DLL. *
449 h.sterling 1.1 * *
450 * Description: *
451 * This function provides a simple layer over the Win32 error logging *
452 * API's. *
453 * *
454 * Returns: *
455 * true if event was successfully logged *
456 * false if the event could not be logged *
457 *-------------------------------------------------------------------------*/
|
458 kumpf 1.3 bool Service::LogEvent(WORD event_type, DWORD event_id, const char *string)
|
459 h.sterling 1.1 {
|
460 kumpf 1.3 BOOL status;
461 HANDLE h_event_source = RegisterEventSource(NULL, g_event_source);
|
462 h.sterling 1.1
|
463 kumpf 1.3 if (h_event_source == NULL)
464 FALSE;
|
465 h.sterling 1.1
|
466 kumpf 1.3 status = ReportEvent(
467 h_event_source,
468 event_type,
469 0,
470 event_id,
471 NULL,
472 1,
473 0,
474 &string,
475 NULL);
|
476 h.sterling 1.1
|
477 kumpf 1.3 DeregisterEventSource(h_event_source);
|
478 h.sterling 1.1
|
479 kumpf 1.3 return (status == TRUE);
|
480 h.sterling 1.1 }
481
482 //-------------------------------------------------------------------------
483 // P R I V A T E
484 //-------------------------------------------------------------------------
485 /*-------------------------------------------------------------------------*
486 * Routine: real_service_main *
487 * *
488 * Args: *
489 * argc: *
490 * The number of arguments in the argv array *
491 * argv: *
492 * An array of strings representing the command line arguments *
493 * *
494 * Description: *
495 * This function is the real service main (as opposed to the user *
496 * supplied service main, which is called service_main). *
497 * *
498 * Returns: *
499 * nothing *
500 *-------------------------------------------------------------------------*/
501 h.sterling 1.1 void __stdcall
502 Service::real_service_main(DWORD argc, LPTSTR *argv)
503 {
|
504 kumpf 1.3 // Let the SCM know we're alive and kicking
|
505 h.sterling 1.1
|
506 kumpf 1.3 report_status(SERVICE_START_PENDING, NO_ERROR, 0, 5000);
|
507 h.sterling 1.1
|
508 kumpf 1.3 // If the command line arguments include the string "-debug" then
509 // invoke the debugger
|
510 h.sterling 1.1
|
511 kumpf 1.3 if (check_args_for_string("-debug"))
512 DebugBreak();
|
513 h.sterling 1.1
|
514 kumpf 1.3 // Save copy of argc and argc in global variables
|
515 h.sterling 1.1
|
516 kumpf 1.3 g_argc = argc;
517 g_argv = argv;
|
518 h.sterling 1.1
|
519 kumpf 1.3 // Start service actions
|
520 h.sterling 1.1
|
521 kumpf 1.3 g_service_status_handle =
522 RegisterServiceCtrlHandler(g_service_name, service_control_handler);
|
523 h.sterling 1.1
|
524 kumpf 1.3 if (g_service_status_handle == 0)
|
525 h.sterling 1.1 {
|
526 kumpf 1.3 show_error("register", "Service Control Handler", GetLastError());
527 report_status(SERVICE_STOPPED, NO_ERROR, 0, 5000);
528 return;
|
529 h.sterling 1.1 }
530
|
531 kumpf 1.3 // Change our state to RUNNING, then invoke the user supplied
532 // service_main function. After the user's service_main exits,
533 // change the service state to STOPPED.
|
534 h.sterling 1.1
|
535 kumpf 1.3 report_status(SERVICE_RUNNING, NO_ERROR, 0, 5000);
536 g_service_main(STARTUP_FLAG, argc, argv);
537 report_status(SERVICE_STOPPED, NO_ERROR, 0, 5000);
|
538 h.sterling 1.1
|
539 kumpf 1.3 return;
|
540 h.sterling 1.1 }
541
542 /*-------------------------------------------------------------------------*
543 * Routine: check_args_for_string *
544 * *
545 * Args: *
546 * string: *
547 * The string to match. *
548 * *
549 * Description: *
550 * This function iterates through the command line arguments searching *
551 * for the string specified by the string parameter. *
552 * *
553 * Returns: *
554 * true if the string was found *
555 * false if the string was not found *
556 *-------------------------------------------------------------------------*/
|
557 kumpf 1.3 bool Service::check_args_for_string(char* string)
|
558 h.sterling 1.1 {
|
559 kumpf 1.3 int i;
|
560 h.sterling 1.1
|
561 kumpf 1.3 for (i = 1; i < g_argc; i++)
|
562 h.sterling 1.1 {
|
563 kumpf 1.3 if (strcmp(g_argv[i], string) == 0)
564 return true;
|
565 h.sterling 1.1 }
566
|
567 kumpf 1.3 return false;
|
568 h.sterling 1.1 }
569
570 /*-------------------------------------------------------------------------*
571 * Method: service_control_handler *
572 * *
573 * Args: *
574 * control: *
575 * The control sent from the SCM telling us what action to take: *
576 * start, stop, pause, continue, etc. *
577 * *
578 * Description: *
579 * This function handles control messages sent from the SCM. Currently *
580 * the only message that is handled differently is the STOP message, *
581 * which invokes the user's service main function passing to it the *
582 * SHUTDOWN_FLAG. The user is then responsible to perform all shutdown *
583 * related tasks. *
584 * *
585 * Returns: *
586 * Nothing *
587 *-------------------------------------------------------------------------*/
588
589 h.sterling 1.1 void WINAPI
590 Service::service_control_handler(DWORD control)
591 {
|
592 kumpf 1.3 /* Currently, only the stop contol requires special handling */
|
593 h.sterling 1.1
|
594 kumpf 1.3 if (control == SERVICE_CONTROL_STOP)
|
595 h.sterling 1.1 {
|
596 kumpf 1.3 report_status(SERVICE_STOP_PENDING, NO_ERROR, 0, 5000);
597 g_service_main(SHUTDOWN_FLAG, g_argc, g_argv);
598 return;
|
599 h.sterling 1.1 }
600
|
601 kumpf 1.3 /* For every other control, just send back our current state */
|
602 h.sterling 1.1
|
603 kumpf 1.3 report_status(g_current_state, NO_ERROR, 0, 5000);
|
604 h.sterling 1.1 }
605
606 /*-------------------------------------------------------------------------*
607 * Method: report_status *
608 * *
609 * Args: *
610 * current_state: *
611 * The service's new state. May be any valid Win32 service state. *
612 * *
613 * exit_code: *
614 * This must be a Win32 exit code. If the server is exiting due to *
615 * an error returned from the Win32 API, then this is the place to *
616 * report the error status. Most of the time, this will be NO_ERROR. *
617 * *
618 * check_point: *
619 * An integer value that should start at zero and increment as each *
620 * discrete step within a phase is completed. For example, if startup *
621 * consists of 3 steps, then the startup will issue its first check- *
622 * point of zero, then increment up through and including 2 as each *
623 * step is finished. *
624 * *
625 h.sterling 1.1 * wait_hint: *
626 * Tells the SCM how long to expect to wait for the next status *
627 * update. *
628 * *
629 * Description: *
630 * This function provides an even higher level of abstraction over *
631 * the Win32 event logging API's. *
632 * *
633 * Returns: *
634 * true if the status was successfully reported *
635 * false if the status could not be reported *
636 *-------------------------------------------------------------------------*/
|
637 kumpf 1.3 bool Service::report_status(
638 DWORD current_state,
639 DWORD exit_code,
640 DWORD check_point,
641 DWORD wait_hint)
642 {
643 SERVICE_STATUS current_status =
644 {
645 SERVICE_WIN32,
646 current_state,
647 SERVICE_CONTROL_INTERROGATE,
648 exit_code,
649 0,
650 check_point,
651 wait_hint
652 };
653
654 /* Wait until we're started before we accept a stop control */
655
656 if (current_state == SERVICE_RUNNING)
657 current_status.dwControlsAccepted += SERVICE_ACCEPT_STOP;
658 kumpf 1.3
659 /* Save new state */
660
661 g_current_state = current_state;
662
663 return (SetServiceStatus(g_service_status_handle, ¤t_status) == TRUE);
|
664 h.sterling 1.1 }
665
666 /*-------------------------------------------------------------------------*
667 * Method: change_service_description *
668 * *
669 * Args: *
670 * service: *
671 * Handle to the service *
672 * *
673 * description: *
674 * The service's description *
675 * *
676 * Description: *
677 * This function sets the description for the service. The description *
678 * text shows up in the Services mmc snapin. The description is added *
679 * as a benefit to users, but does not affect the service's operation. *
680 * Therefore, if this function should fail for any reason, there is *
681 * no need to stop. That's why this function has no return value. *
682 * *
683 * Returns: *
684 * Nothing *
685 h.sterling 1.1 * *
686 * Notes: *
687 * This function uses the ChangeServiceConfig2() API function which *
688 * first appeared in Windows2000. Therefore, this code checks the *
689 * OS version, before calling ChangeServiceConfig2(). *
690 *-------------------------------------------------------------------------*/
|
691 kumpf 1.3 void Service::change_service_description(SC_HANDLE service, char *description)
|
692 h.sterling 1.1 {
|
693 kumpf 1.3 OSVERSIONINFO osvi;
694
695 /*
696 * Test for Windows 2000 or greater
697 */
|
698 h.sterling 1.1
|
699 kumpf 1.3 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
700 h.sterling 1.1
|
701 kumpf 1.3 if (GetVersionEx(&osvi) != 0 &&
702 osvi.dwPlatformId == VER_PLATFORM_WIN32_NT &&
703 osvi.dwMajorVersion >= 5)
|
704 h.sterling 1.1 {
|
705 kumpf 1.3 typedef BOOL
706 (WINAPI *CHANGE_SERVICE_CONFIG2_T)(SC_HANDLE, DWORD, LPVOID);
|
707 h.sterling 1.1
|
708 kumpf 1.3 HINSTANCE hdll = LoadLibrary("advapi32.dll");
|
709 h.sterling 1.1
|
710 kumpf 1.3 if (hdll != NULL)
|
711 h.sterling 1.1 {
|
712 kumpf 1.3 SERVICE_DESCRIPTION sd;
713 CHANGE_SERVICE_CONFIG2_T csc;
|
714 h.sterling 1.1
|
715 kumpf 1.3 csc = (CHANGE_SERVICE_CONFIG2_T)
716 GetProcAddress(hdll, "ChangeServiceConfig2A");
|
717 h.sterling 1.1
|
718 kumpf 1.3 if (csc)
|
719 h.sterling 1.1 {
|
720 kumpf 1.3 sd.lpDescription = description;
721 csc(service, SERVICE_CONFIG_DESCRIPTION, &sd);
|
722 h.sterling 1.1 }
723
|
724 kumpf 1.3 FreeLibrary(hdll);
|
725 h.sterling 1.1 }
726 }
727 }
728
729 /*-------------------------------------------------------------------------*
730 * Method: show_error *
731 * *
732 * Args: *
733 * action: *
734 * A single verb decribing the action going on when the error *
735 * occurred. For example, "opening", "creating", etc. *
736 * *
737 * object: *
738 * Description of the object on which was action occurred. Examples *
739 * are "file", "service", etc. *
740 * *
741 * hr: *
742 * The error status. Can be an hresult, or Win32 error status. *
743 * *
744 * Description: *
745 * This function provides an even higher level of abstraction over *
746 h.sterling 1.1 * the Win32 event logging API's. *
747 * *
748 * Returns: *
749 * true if event was successfully logged *
750 * false if the event could not be logged *
751 *-------------------------------------------------------------------------*/
|
752 kumpf 1.3 bool Service::show_error(const char *action, const char *object, DWORD hr)
|
753 h.sterling 1.1 {
|
754 kumpf 1.3 char console_title[_MAX_PATH] = {0};
755 char txt[_MAX_PATH] = "";
756 char msg[_MAX_PATH] = "";
757 DWORD nchars;
758
759 nchars = FormatMessage(
760 FORMAT_MESSAGE_FROM_SYSTEM,
761 NULL,
762 hr,
763 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
764 msg,
765 sizeof(msg),
766 NULL);
767
768 if (nchars == 0)
769 sprintf(msg, "Unknown error code - %%X%x", hr);
770 else if (nchars > 1)
771 {
772 if (msg[nchars - 1] == '\n') msg[nchars - 1] = '\0';
773 if (msg[nchars - 2] == '\r') msg[nchars - 2] = '\0';
774 }
775 kumpf 1.3
776 sprintf(txt,
777 "Failed to %s %s %s. Reason: %s",
778 action,
779 object,
780 g_service_name,
781 msg);
782
783 // Running from a console window
784 // send courtesy message txt to stderr
785 if (GetConsoleTitle(console_title, _MAX_PATH) > 0)
|
786 h.sterling 1.1 {
787 PEGASUS_STD(cerr) << txt << PEGASUS_STD(endl);
788 }
789
|
790 kumpf 1.3 return LogEvent(EVENTLOG_ERROR_TYPE, 1, txt);
|
791 h.sterling 1.1 }
792
|
793 kumpf 1.3 Service::State Service::get_state(DWORD scm_state)
|
794 h.sterling 1.1 {
|
795 kumpf 1.3 return (State)scm_state;
|
796 h.sterling 1.1 }
797
|
798 kumpf 1.3 Service::ReturnCode Service::get_error(DWORD error_status, const char *action)
|
799 h.sterling 1.1 {
|
800 kumpf 1.3 switch (error_status)
|
801 h.sterling 1.1 {
802 /*
|
803 kumpf 1.3 // INFO: Could add cases to suppress error message.
804 case ERROR_SERVICE_DOES_NOT_EXIST:
805 case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
806 break;
|
807 h.sterling 1.1 */
|
808 kumpf 1.3 case SERVICE_RETURN_SUCCESS:
809 break;
|
810 h.sterling 1.1
|
811 kumpf 1.3 default:
812 show_error(action, "service", error_status);
813 break;
|
814 h.sterling 1.1 }
|
815 kumpf 1.3 return (ReturnCode)error_status;
|
816 h.sterling 1.1 }
817
|