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