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