xref: /reactos/dll/win32/advapi32/service/sctrl.c (revision 595b846d)
1 /*
2  * PROJECT:     ReactOS advapi32
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        dll/win32/advapi32/service/sctrl.c
5  * PURPOSE:     Service control manager functions
6  * COPYRIGHT:   Copyright 1999 Emanuele Aliberti
7  *              Copyright 2007 Ged Murphy <gedmurphy@reactos.org>
8  *                             Gregor Brunmar <gregor.brunmar@home.se>
9  */
10 
11 
12 /* INCLUDES ******************************************************************/
13 
14 #include <advapi32.h>
15 WINE_DEFAULT_DEBUG_CHANNEL(advapi_service);
16 
17 
18 /* TYPES *********************************************************************/
19 
20 typedef struct _SERVICE_THREAD_PARAMSA
21 {
22     LPSERVICE_MAIN_FUNCTIONA lpServiceMain;
23     DWORD dwArgCount;
24     LPSTR *lpArgVector;
25 } SERVICE_THREAD_PARAMSA, *PSERVICE_THREAD_PARAMSA;
26 
27 
28 typedef struct _SERVICE_THREAD_PARAMSW
29 {
30     LPSERVICE_MAIN_FUNCTIONW lpServiceMain;
31     DWORD dwArgCount;
32     LPWSTR *lpArgVector;
33 } SERVICE_THREAD_PARAMSW, *PSERVICE_THREAD_PARAMSW;
34 
35 
36 typedef struct _ACTIVE_SERVICE
37 {
38     SERVICE_STATUS_HANDLE hServiceStatus;
39     UNICODE_STRING ServiceName;
40     union
41     {
42         LPSERVICE_MAIN_FUNCTIONA A;
43         LPSERVICE_MAIN_FUNCTIONW W;
44     } ServiceMain;
45     LPHANDLER_FUNCTION HandlerFunction;
46     LPHANDLER_FUNCTION_EX HandlerFunctionEx;
47     LPVOID HandlerContext;
48     BOOL bUnicode;
49     BOOL bOwnProcess;
50 } ACTIVE_SERVICE, *PACTIVE_SERVICE;
51 
52 
53 /* GLOBALS *******************************************************************/
54 
55 static DWORD dwActiveServiceCount = 0;
56 static PACTIVE_SERVICE lpActiveServices = NULL;
57 static handle_t hStatusBinding = NULL;
58 
59 
60 /* FUNCTIONS *****************************************************************/
61 
62 handle_t __RPC_USER
63 RPC_SERVICE_STATUS_HANDLE_bind(RPC_SERVICE_STATUS_HANDLE hServiceStatus)
64 {
65     return hStatusBinding;
66 }
67 
68 
69 void __RPC_USER
70 RPC_SERVICE_STATUS_HANDLE_unbind(RPC_SERVICE_STATUS_HANDLE hServiceStatus,
71                                  handle_t hBinding)
72 {
73 }
74 
75 
76 static RPC_STATUS
77 ScCreateStatusBinding(VOID)
78 {
79     LPWSTR pszStringBinding;
80     RPC_STATUS status;
81 
82     TRACE("ScCreateStatusBinding() called\n");
83 
84     status = RpcStringBindingComposeW(NULL,
85                                       L"ncacn_np",
86                                       NULL,
87                                       L"\\pipe\\ntsvcs",
88                                       NULL,
89                                       &pszStringBinding);
90     if (status != RPC_S_OK)
91     {
92         ERR("RpcStringBindingCompose returned 0x%x\n", status);
93         return status;
94     }
95 
96     /* Set the binding handle that will be used to bind to the server. */
97     status = RpcBindingFromStringBindingW(pszStringBinding,
98                                           &hStatusBinding);
99     if (status != RPC_S_OK)
100     {
101         ERR("RpcBindingFromStringBinding returned 0x%x\n", status);
102     }
103 
104     status = RpcStringFreeW(&pszStringBinding);
105     if (status != RPC_S_OK)
106     {
107         ERR("RpcStringFree returned 0x%x\n", status);
108     }
109 
110     return status;
111 }
112 
113 
114 static RPC_STATUS
115 ScDestroyStatusBinding(VOID)
116 {
117     RPC_STATUS status;
118 
119     TRACE("ScDestroyStatusBinding() called\n");
120 
121     if (hStatusBinding == NULL)
122         return RPC_S_OK;
123 
124     status = RpcBindingFree(&hStatusBinding);
125     if (status != RPC_S_OK)
126     {
127         ERR("RpcBindingFree returned 0x%x\n", status);
128     }
129     else
130     {
131         hStatusBinding = NULL;
132     }
133 
134     return status;
135 }
136 
137 
138 static DWORD
139 ScLookupServiceByServiceName(IN LPCWSTR lpServiceName,
140                              OUT PACTIVE_SERVICE* pService)
141 {
142     DWORD i;
143 
144     TRACE("ScLookupServiceByServiceName(%S) called\n", lpServiceName);
145 
146     if (lpActiveServices[0].bOwnProcess)
147     {
148         *pService = &lpActiveServices[0];
149         return ERROR_SUCCESS;
150     }
151 
152     for (i = 0; i < dwActiveServiceCount; i++)
153     {
154         TRACE("Checking %S\n", lpActiveServices[i].ServiceName.Buffer);
155         if (_wcsicmp(lpActiveServices[i].ServiceName.Buffer, lpServiceName) == 0)
156         {
157             TRACE("Found!\n");
158             *pService = &lpActiveServices[i];
159             return ERROR_SUCCESS;
160         }
161     }
162 
163     TRACE("No service found!\n");
164     *pService = NULL;
165     return ERROR_SERVICE_NOT_IN_EXE;
166 }
167 
168 
169 static DWORD WINAPI
170 ScServiceMainStubA(LPVOID Context)
171 {
172     PSERVICE_THREAD_PARAMSA ThreadParams = Context;
173 
174     TRACE("ScServiceMainStubA() called\n");
175 
176     /* Call the main service routine and free the arguments vector */
177     (ThreadParams->lpServiceMain)(ThreadParams->dwArgCount,
178                                   ThreadParams->lpArgVector);
179 
180     if (ThreadParams->lpArgVector != NULL)
181     {
182         HeapFree(GetProcessHeap(), 0, ThreadParams->lpArgVector);
183     }
184     HeapFree(GetProcessHeap(), 0, ThreadParams);
185 
186     return ERROR_SUCCESS;
187 }
188 
189 
190 static DWORD WINAPI
191 ScServiceMainStubW(LPVOID Context)
192 {
193     PSERVICE_THREAD_PARAMSW ThreadParams = Context;
194 
195     TRACE("ScServiceMainStubW() called\n");
196 
197     /* Call the main service routine and free the arguments vector */
198     (ThreadParams->lpServiceMain)(ThreadParams->dwArgCount,
199                                   ThreadParams->lpArgVector);
200 
201     if (ThreadParams->lpArgVector != NULL)
202     {
203         HeapFree(GetProcessHeap(), 0, ThreadParams->lpArgVector);
204     }
205     HeapFree(GetProcessHeap(), 0, ThreadParams);
206 
207     return ERROR_SUCCESS;
208 }
209 
210 
211 static DWORD
212 ScConnectControlPipe(HANDLE *hPipe)
213 {
214     DWORD dwBytesWritten;
215     DWORD dwState;
216     DWORD dwServiceCurrent = 0;
217     NTSTATUS Status;
218     WCHAR NtControlPipeName[MAX_PATH + 1];
219     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
220     DWORD dwProcessId;
221 
222     /* Get the service number and create the named pipe */
223     RtlZeroMemory(&QueryTable,
224                   sizeof(QueryTable));
225 
226     QueryTable[0].Name = L"";
227     QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
228     QueryTable[0].EntryContext = &dwServiceCurrent;
229 
230     Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
231                                     L"ServiceCurrent",
232                                     QueryTable,
233                                     NULL,
234                                     NULL);
235     if (!NT_SUCCESS(Status))
236     {
237         ERR("RtlQueryRegistryValues() failed (Status %lx)\n", Status);
238         return RtlNtStatusToDosError(Status);
239     }
240 
241     swprintf(NtControlPipeName, L"\\\\.\\pipe\\net\\NtControlPipe%u", dwServiceCurrent);
242 
243     if (!WaitNamedPipeW(NtControlPipeName, 30000))
244     {
245         ERR("WaitNamedPipe(%S) failed (Error %lu)\n", NtControlPipeName, GetLastError());
246         return ERROR_FAILED_SERVICE_CONTROLLER_CONNECT;
247     }
248 
249     *hPipe = CreateFileW(NtControlPipeName,
250                          GENERIC_READ | GENERIC_WRITE,
251                          FILE_SHARE_READ | FILE_SHARE_WRITE,
252                          NULL,
253                          OPEN_EXISTING,
254                          FILE_ATTRIBUTE_NORMAL,
255                          NULL);
256     if (*hPipe == INVALID_HANDLE_VALUE)
257     {
258         ERR("CreateFileW() failed for pipe %S (Error %lu)\n", NtControlPipeName, GetLastError());
259         return ERROR_FAILED_SERVICE_CONTROLLER_CONNECT;
260     }
261 
262     dwState = PIPE_READMODE_MESSAGE;
263     if (!SetNamedPipeHandleState(*hPipe, &dwState, NULL, NULL))
264     {
265         CloseHandle(*hPipe);
266         *hPipe = INVALID_HANDLE_VALUE;
267         return ERROR_FAILED_SERVICE_CONTROLLER_CONNECT;
268     }
269 
270     /* Pass the ProcessId to the SCM */
271     dwProcessId = GetCurrentProcessId();
272     WriteFile(*hPipe,
273               &dwProcessId,
274               sizeof(dwProcessId),
275               &dwBytesWritten,
276               NULL);
277 
278     TRACE("Sent Process ID %lu\n", dwProcessId);
279 
280     return ERROR_SUCCESS;
281 }
282 
283 
284 /*
285  * Ansi/Unicode argument layout of the vector passed to a service at startup,
286  * depending on the different versions of Windows considered:
287  *
288  * - XP/2003:
289  *   [argv array of pointers][parameter 1][parameter 2]...[service name]
290  *
291  * - Vista:
292  *   [argv array of pointers][align to 8 bytes]
293  *   [parameter 1][parameter 2]...[service name]
294  *
295  * - Win7/8:
296  *   [argv array of pointers][service name]
297  *   [parameter 1][align to 4 bytes][parameter 2][align to 4 bytes]...
298  *
299  * Space for parameters and service name is always enough to store
300  * both the Ansi and the Unicode versions including NULL terminator.
301  */
302 
303 static DWORD
304 ScBuildUnicodeArgsVector(PSCM_CONTROL_PACKET ControlPacket,
305                          LPDWORD lpArgCount,
306                          LPWSTR **lpArgVector)
307 {
308     PWSTR *lpVector;
309     PWSTR pszServiceName;
310     DWORD cbServiceName;
311     DWORD cbArguments;
312     DWORD cbTotal;
313     DWORD i;
314 
315     if (ControlPacket == NULL || lpArgCount == NULL || lpArgVector == NULL)
316         return ERROR_INVALID_PARAMETER;
317 
318     *lpArgCount  = 0;
319     *lpArgVector = NULL;
320 
321     /* Retrieve and count the start command line (NULL-terminated) */
322     pszServiceName = (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset);
323     cbServiceName  = lstrlenW(pszServiceName) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
324 
325     /*
326      * The total size of the argument vector is equal to the entry for
327      * the service name, plus the size of the original argument vector.
328      */
329     cbTotal = sizeof(PWSTR) + cbServiceName;
330     if (ControlPacket->dwArgumentsCount > 0)
331         cbArguments = ControlPacket->dwSize - ControlPacket->dwArgumentsOffset;
332     else
333         cbArguments = 0;
334     cbTotal += cbArguments;
335 
336     /* Allocate the new argument vector */
337     lpVector = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbTotal);
338     if (lpVector == NULL)
339         return ERROR_NOT_ENOUGH_MEMORY;
340 
341     /*
342      * The first argument is reserved for the service name, which
343      * will be appended to the end of the argument string list.
344      */
345 
346     /* Copy the remaining arguments */
347     if (ControlPacket->dwArgumentsCount > 0)
348     {
349         memcpy(&lpVector[1],
350                (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwArgumentsOffset),
351                cbArguments);
352 
353         for (i = 0; i < ControlPacket->dwArgumentsCount; i++)
354         {
355             lpVector[i + 1] = (PWSTR)((ULONG_PTR)&lpVector[1] + (ULONG_PTR)lpVector[i + 1]);
356             TRACE("Unicode lpVector[%lu] = '%ls'\n", i + 1, lpVector[i + 1]);
357         }
358     }
359 
360     /* Now copy the service name */
361     lpVector[0] = (PWSTR)((ULONG_PTR)&lpVector[1] + cbArguments);
362     memcpy(lpVector[0], pszServiceName, cbServiceName);
363     TRACE("Unicode lpVector[%lu] = '%ls'\n", 0, lpVector[0]);
364 
365     *lpArgCount  = ControlPacket->dwArgumentsCount + 1;
366     *lpArgVector = lpVector;
367 
368     return ERROR_SUCCESS;
369 }
370 
371 
372 static DWORD
373 ScBuildAnsiArgsVector(PSCM_CONTROL_PACKET ControlPacket,
374                       LPDWORD lpArgCount,
375                       LPSTR **lpArgVector)
376 {
377     DWORD dwError;
378     NTSTATUS Status;
379     DWORD ArgCount, i;
380     PWSTR *lpVectorW;
381     PSTR  *lpVectorA;
382     UNICODE_STRING UnicodeString;
383     ANSI_STRING AnsiString;
384 
385     if (ControlPacket == NULL || lpArgCount == NULL || lpArgVector == NULL)
386         return ERROR_INVALID_PARAMETER;
387 
388     *lpArgCount  = 0;
389     *lpArgVector = NULL;
390 
391     /* Build the UNICODE arguments vector */
392     dwError = ScBuildUnicodeArgsVector(ControlPacket, &ArgCount, &lpVectorW);
393     if (dwError != ERROR_SUCCESS)
394         return dwError;
395 
396     /* Convert the vector to ANSI in place */
397     lpVectorA = (PSTR*)lpVectorW;
398     for (i = 0; i < ArgCount; i++)
399     {
400         RtlInitUnicodeString(&UnicodeString, lpVectorW[i]);
401         RtlInitEmptyAnsiString(&AnsiString, lpVectorA[i], UnicodeString.MaximumLength);
402         Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE);
403         if (!NT_SUCCESS(Status))
404         {
405             /* Failed to convert to ANSI; free the allocated vector and return */
406             dwError = RtlNtStatusToDosError(Status);
407             HeapFree(GetProcessHeap(), 0, lpVectorW);
408             return dwError;
409         }
410 
411         /* NULL-terminate the string */
412         AnsiString.Buffer[AnsiString.Length / sizeof(CHAR)] = ANSI_NULL;
413 
414         TRACE("Ansi lpVector[%lu] = '%s'\n", i, lpVectorA[i]);
415     }
416 
417     *lpArgCount  = ArgCount;
418     *lpArgVector = lpVectorA;
419 
420     return ERROR_SUCCESS;
421 }
422 
423 
424 static DWORD
425 ScStartService(PACTIVE_SERVICE lpService,
426                PSCM_CONTROL_PACKET ControlPacket)
427 {
428     HANDLE ThreadHandle;
429     DWORD ThreadId;
430     DWORD dwError;
431     PSERVICE_THREAD_PARAMSA ThreadParamsA;
432     PSERVICE_THREAD_PARAMSW ThreadParamsW;
433 
434     if (lpService == NULL || ControlPacket == NULL)
435         return ERROR_INVALID_PARAMETER;
436 
437     TRACE("ScStartService(Size: %lu, Service: '%S') called\n",
438           ControlPacket->dwSize,
439           (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset));
440 
441     /* Set the service status handle */
442     lpService->hServiceStatus = ControlPacket->hServiceStatus;
443 
444     /* Build the arguments vector */
445     if (lpService->bUnicode != FALSE)
446     {
447         ThreadParamsW = HeapAlloc(GetProcessHeap(), 0, sizeof(*ThreadParamsW));
448         if (ThreadParamsW == NULL)
449             return ERROR_NOT_ENOUGH_MEMORY;
450         dwError = ScBuildUnicodeArgsVector(ControlPacket,
451                                            &ThreadParamsW->dwArgCount,
452                                            &ThreadParamsW->lpArgVector);
453         if (dwError != ERROR_SUCCESS)
454         {
455             HeapFree(GetProcessHeap(), 0, ThreadParamsW);
456             return dwError;
457         }
458         ThreadParamsW->lpServiceMain = lpService->ServiceMain.W;
459         ThreadHandle = CreateThread(NULL,
460                                     0,
461                                     ScServiceMainStubW,
462                                     ThreadParamsW,
463                                     0,
464                                     &ThreadId);
465         if (ThreadHandle == NULL)
466         {
467             if (ThreadParamsW->lpArgVector != NULL)
468             {
469                 HeapFree(GetProcessHeap(), 0, ThreadParamsW->lpArgVector);
470             }
471             HeapFree(GetProcessHeap(), 0, ThreadParamsW);
472 
473             return ERROR_SERVICE_NO_THREAD;
474         }
475 
476         CloseHandle(ThreadHandle);
477     }
478     else
479     {
480         ThreadParamsA = HeapAlloc(GetProcessHeap(), 0, sizeof(*ThreadParamsA));
481         if (ThreadParamsA == NULL)
482             return ERROR_NOT_ENOUGH_MEMORY;
483         dwError = ScBuildAnsiArgsVector(ControlPacket,
484                                         &ThreadParamsA->dwArgCount,
485                                         &ThreadParamsA->lpArgVector);
486         if (dwError != ERROR_SUCCESS)
487         {
488             HeapFree(GetProcessHeap(), 0, ThreadParamsA);
489             return dwError;
490         }
491         ThreadParamsA->lpServiceMain = lpService->ServiceMain.A;
492         ThreadHandle = CreateThread(NULL,
493                                     0,
494                                     ScServiceMainStubA,
495                                     ThreadParamsA,
496                                     0,
497                                     &ThreadId);
498         if (ThreadHandle == NULL)
499         {
500             if (ThreadParamsA->lpArgVector != NULL)
501             {
502                 HeapFree(GetProcessHeap(), 0, ThreadParamsA->lpArgVector);
503             }
504             HeapFree(GetProcessHeap(), 0, ThreadParamsA);
505 
506             return ERROR_SERVICE_NO_THREAD;
507         }
508 
509         CloseHandle(ThreadHandle);
510     }
511 
512     return ERROR_SUCCESS;
513 }
514 
515 
516 static DWORD
517 ScControlService(PACTIVE_SERVICE lpService,
518                  PSCM_CONTROL_PACKET ControlPacket)
519 {
520     DWORD dwError;
521 
522     if (lpService == NULL || ControlPacket == NULL)
523         return ERROR_INVALID_PARAMETER;
524 
525     TRACE("ScControlService(Size: %lu, Service: '%S') called\n",
526           ControlPacket->dwSize,
527           (PWSTR)((ULONG_PTR)ControlPacket + ControlPacket->dwServiceNameOffset));
528 
529     if (lpService->HandlerFunction)
530     {
531         (lpService->HandlerFunction)(ControlPacket->dwControl);
532         dwError = ERROR_SUCCESS;
533     }
534     else if (lpService->HandlerFunctionEx)
535     {
536         /* FIXME: Send correct 2nd and 3rd parameters */
537         dwError = (lpService->HandlerFunctionEx)(ControlPacket->dwControl,
538                                                  0, NULL,
539                                                  lpService->HandlerContext);
540     }
541 
542     TRACE("ScControlService() done (error %lu)\n", dwError);
543 
544     return dwError;
545 }
546 
547 
548 static BOOL
549 ScServiceDispatcher(HANDLE hPipe,
550                     PSCM_CONTROL_PACKET ControlPacket,
551                     DWORD dwBufferSize)
552 {
553     DWORD Count;
554     BOOL bResult;
555     BOOL bRunning = TRUE;
556     LPWSTR lpServiceName;
557     PACTIVE_SERVICE lpService;
558     SCM_REPLY_PACKET ReplyPacket;
559     DWORD dwError;
560 
561     TRACE("ScDispatcherLoop() called\n");
562 
563     if (ControlPacket == NULL || dwBufferSize < sizeof(SCM_CONTROL_PACKET))
564         return FALSE;
565 
566     while (bRunning)
567     {
568         /* Read command from the control pipe */
569         bResult = ReadFile(hPipe,
570                            ControlPacket,
571                            dwBufferSize,
572                            &Count,
573                            NULL);
574         if (bResult == FALSE)
575         {
576             ERR("Pipe read failed (Error: %lu)\n", GetLastError());
577             return FALSE;
578         }
579 
580         lpServiceName = (LPWSTR)((PBYTE)ControlPacket + ControlPacket->dwServiceNameOffset);
581         TRACE("Service: %S\n", lpServiceName);
582 
583         if (lpServiceName[0] == UNICODE_NULL)
584         {
585             ERR("Stop dispatcher thread\n");
586             bRunning = FALSE;
587             dwError = ERROR_SUCCESS;
588         }
589         else
590         {
591             if (ControlPacket->dwControl == SERVICE_CONTROL_START_OWN)
592                 lpActiveServices[0].bOwnProcess = TRUE;
593 
594             dwError = ScLookupServiceByServiceName(lpServiceName, &lpService);
595             if ((dwError == ERROR_SUCCESS) && (lpService != NULL))
596             {
597                 /* Execute command */
598                 switch (ControlPacket->dwControl)
599                 {
600                     case SERVICE_CONTROL_START_SHARE:
601                     case SERVICE_CONTROL_START_OWN:
602                         TRACE("Start command - received SERVICE_CONTROL_START\n");
603                         dwError = ScStartService(lpService, ControlPacket);
604                         break;
605 
606                     case SERVICE_CONTROL_STOP:
607                         TRACE("Stop command - received SERVICE_CONTROL_STOP\n");
608                         dwError = ScControlService(lpService, ControlPacket);
609                         break;
610 
611                     default:
612                         TRACE("Command %lu received", ControlPacket->dwControl);
613                         dwError = ScControlService(lpService, ControlPacket);
614                         break;
615                 }
616             }
617         }
618 
619         ReplyPacket.dwError = dwError;
620 
621         /* Send the reply packet */
622         bResult = WriteFile(hPipe,
623                             &ReplyPacket,
624                             sizeof(ReplyPacket),
625                             &Count,
626                             NULL);
627         if (bResult == FALSE)
628         {
629             ERR("Pipe write failed (Error: %lu)\n", GetLastError());
630             return FALSE;
631         }
632     }
633 
634     return TRUE;
635 }
636 
637 
638 /**********************************************************************
639  *  RegisterServiceCtrlHandlerA
640  *
641  * @implemented
642  */
643 SERVICE_STATUS_HANDLE WINAPI
644 RegisterServiceCtrlHandlerA(LPCSTR lpServiceName,
645                             LPHANDLER_FUNCTION lpHandlerProc)
646 {
647     ANSI_STRING ServiceNameA;
648     UNICODE_STRING ServiceNameU;
649     SERVICE_STATUS_HANDLE hServiceStatus;
650 
651     RtlInitAnsiString(&ServiceNameA, lpServiceName);
652     if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ServiceNameU, &ServiceNameA, TRUE)))
653     {
654         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
655         return NULL;
656     }
657 
658     hServiceStatus = RegisterServiceCtrlHandlerW(ServiceNameU.Buffer,
659                                                  lpHandlerProc);
660 
661     RtlFreeUnicodeString(&ServiceNameU);
662 
663     return hServiceStatus;
664 }
665 
666 
667 /**********************************************************************
668  *  RegisterServiceCtrlHandlerW
669  *
670  * @implemented
671  */
672 SERVICE_STATUS_HANDLE WINAPI
673 RegisterServiceCtrlHandlerW(LPCWSTR lpServiceName,
674                             LPHANDLER_FUNCTION lpHandlerProc)
675 {
676     DWORD dwError;
677     PACTIVE_SERVICE Service;
678 
679     dwError = ScLookupServiceByServiceName(lpServiceName, &Service);
680     if ((dwError != ERROR_SUCCESS) || (Service == NULL))
681     {
682         SetLastError(dwError);
683         return NULL;
684     }
685 
686     if (!lpHandlerProc)
687     {
688         SetLastError(ERROR_INVALID_PARAMETER);
689         return NULL;
690     }
691 
692     Service->HandlerFunction   = lpHandlerProc;
693     Service->HandlerFunctionEx = NULL;
694 
695     TRACE("RegisterServiceCtrlHandler returning 0x%p\n", Service->hServiceStatus);
696 
697     return Service->hServiceStatus;
698 }
699 
700 
701 /**********************************************************************
702  *  RegisterServiceCtrlHandlerExA
703  *
704  * @implemented
705  */
706 SERVICE_STATUS_HANDLE WINAPI
707 RegisterServiceCtrlHandlerExA(LPCSTR lpServiceName,
708                               LPHANDLER_FUNCTION_EX lpHandlerProc,
709                               LPVOID lpContext)
710 {
711     ANSI_STRING ServiceNameA;
712     UNICODE_STRING ServiceNameU;
713     SERVICE_STATUS_HANDLE hServiceStatus;
714 
715     RtlInitAnsiString(&ServiceNameA, lpServiceName);
716     if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ServiceNameU, &ServiceNameA, TRUE)))
717     {
718         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
719         return NULL;
720     }
721 
722     hServiceStatus = RegisterServiceCtrlHandlerExW(ServiceNameU.Buffer,
723                                                    lpHandlerProc,
724                                                    lpContext);
725 
726     RtlFreeUnicodeString(&ServiceNameU);
727 
728     return hServiceStatus;
729 }
730 
731 
732 /**********************************************************************
733  *  RegisterServiceCtrlHandlerExW
734  *
735  * @implemented
736  */
737 SERVICE_STATUS_HANDLE WINAPI
738 RegisterServiceCtrlHandlerExW(LPCWSTR lpServiceName,
739                               LPHANDLER_FUNCTION_EX lpHandlerProc,
740                               LPVOID lpContext)
741 {
742     DWORD dwError;
743     PACTIVE_SERVICE Service;
744 
745     dwError = ScLookupServiceByServiceName(lpServiceName, &Service);
746     if ((dwError != ERROR_SUCCESS) || (Service == NULL))
747     {
748         SetLastError(dwError);
749         return NULL;
750     }
751 
752     if (!lpHandlerProc)
753     {
754         SetLastError(ERROR_INVALID_PARAMETER);
755         return NULL;
756     }
757 
758     Service->HandlerFunction   = NULL;
759     Service->HandlerFunctionEx = lpHandlerProc;
760     Service->HandlerContext    = lpContext;
761 
762     TRACE("RegisterServiceCtrlHandlerEx returning 0x%p\n", Service->hServiceStatus);
763 
764     return Service->hServiceStatus;
765 }
766 
767 
768 /**********************************************************************
769  *  I_ScIsSecurityProcess
770  *
771  * Undocumented
772  *
773  * @unimplemented
774  */
775 VOID
776 WINAPI
777 I_ScIsSecurityProcess(VOID)
778 {
779 }
780 
781 
782 /**********************************************************************
783  *  I_ScPnPGetServiceName
784  *
785  * Undocumented
786  *
787  * @implemented
788  */
789 DWORD
790 WINAPI
791 I_ScPnPGetServiceName(IN SERVICE_STATUS_HANDLE hServiceStatus,
792                       OUT LPWSTR lpServiceName,
793                       IN DWORD cchServiceName)
794 {
795     DWORD i;
796 
797     for (i = 0; i < dwActiveServiceCount; i++)
798     {
799         if (lpActiveServices[i].hServiceStatus == hServiceStatus)
800         {
801             wcscpy(lpServiceName, lpActiveServices[i].ServiceName.Buffer);
802             return ERROR_SUCCESS;
803         }
804     }
805 
806     return ERROR_SERVICE_NOT_IN_EXE;
807 }
808 
809 
810 /**********************************************************************
811  *  I_ScSetServiceBitsA
812  *
813  * Undocumented
814  *
815  * @implemented
816  */
817 BOOL WINAPI
818 I_ScSetServiceBitsA(SERVICE_STATUS_HANDLE hServiceStatus,
819                     DWORD dwServiceBits,
820                     BOOL bSetBitsOn,
821                     BOOL bUpdateImmediately,
822                     LPSTR lpString)
823 {
824     BOOL bResult;
825 
826     RpcTryExcept
827     {
828         bResult = RI_ScSetServiceBitsA((RPC_SERVICE_STATUS_HANDLE)hServiceStatus,
829                                        dwServiceBits,
830                                        bSetBitsOn,
831                                        bUpdateImmediately,
832                                        lpString);
833     }
834     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
835     {
836         SetLastError(ScmRpcStatusToWinError(RpcExceptionCode()));
837         bResult = FALSE;
838     }
839     RpcEndExcept;
840 
841     return bResult;
842 }
843 
844 
845 /**********************************************************************
846  *  I_ScSetServiceBitsW
847  *
848  * Undocumented
849  *
850  * @implemented
851  */
852 BOOL WINAPI
853 I_ScSetServiceBitsW(SERVICE_STATUS_HANDLE hServiceStatus,
854                     DWORD dwServiceBits,
855                     BOOL bSetBitsOn,
856                     BOOL bUpdateImmediately,
857                     LPWSTR lpString)
858 {
859     BOOL bResult;
860 
861     RpcTryExcept
862     {
863         bResult = RI_ScSetServiceBitsW((RPC_SERVICE_STATUS_HANDLE)hServiceStatus,
864                                        dwServiceBits,
865                                        bSetBitsOn,
866                                        bUpdateImmediately,
867                                        lpString);
868     }
869     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
870     {
871         SetLastError(ScmRpcStatusToWinError(RpcExceptionCode()));
872         bResult = FALSE;
873     }
874     RpcEndExcept;
875 
876     return bResult;
877 }
878 
879 
880 /**********************************************************************
881  *  SetServiceBits
882  *
883  * @implemented
884  */
885 BOOL WINAPI
886 SetServiceBits(SERVICE_STATUS_HANDLE hServiceStatus,
887                DWORD dwServiceBits,
888                BOOL bSetBitsOn,
889                BOOL bUpdateImmediately)
890 {
891     return I_ScSetServiceBitsW(hServiceStatus,
892                                dwServiceBits,
893                                bSetBitsOn,
894                                bUpdateImmediately,
895                                NULL);
896 }
897 
898 
899 /**********************************************************************
900  *  SetServiceStatus
901  *
902  * @implemented
903  */
904 BOOL WINAPI
905 SetServiceStatus(SERVICE_STATUS_HANDLE hServiceStatus,
906                  LPSERVICE_STATUS lpServiceStatus)
907 {
908     DWORD dwError;
909 
910     TRACE("SetServiceStatus(hServiceStatus %lu) called\n", hServiceStatus);
911 
912     RpcTryExcept
913     {
914         dwError = RSetServiceStatus((RPC_SERVICE_STATUS_HANDLE)hServiceStatus,
915                                     lpServiceStatus);
916     }
917     RpcExcept(EXCEPTION_EXECUTE_HANDLER)
918     {
919         dwError = ScmRpcStatusToWinError(RpcExceptionCode());
920     }
921     RpcEndExcept;
922 
923     if (dwError != ERROR_SUCCESS)
924     {
925         ERR("RSetServiceStatus() failed (Error %lu)\n", dwError);
926         SetLastError(dwError);
927         return FALSE;
928     }
929 
930     TRACE("SetServiceStatus() done (ret %lu)\n", dwError);
931 
932     return TRUE;
933 }
934 
935 
936 /**********************************************************************
937  *  StartServiceCtrlDispatcherA
938  *
939  * @implemented
940  */
941 BOOL WINAPI
942 StartServiceCtrlDispatcherA(const SERVICE_TABLE_ENTRYA *lpServiceStartTable)
943 {
944     ULONG i;
945     HANDLE hPipe;
946     DWORD dwError;
947     PSCM_CONTROL_PACKET ControlPacket;
948     DWORD dwBufSize;
949     BOOL bRet = TRUE;
950 
951     TRACE("StartServiceCtrlDispatcherA() called\n");
952 
953     i = 0;
954     while (lpServiceStartTable[i].lpServiceProc != NULL)
955     {
956         i++;
957     }
958 
959     dwActiveServiceCount = i;
960 
961     /* Initialize the service table */
962     lpActiveServices = RtlAllocateHeap(RtlGetProcessHeap(),
963                                        HEAP_ZERO_MEMORY,
964                                        dwActiveServiceCount * sizeof(ACTIVE_SERVICE));
965     if (lpActiveServices == NULL)
966     {
967         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
968         return FALSE;
969     }
970 
971     /* Copy service names and start procedure */
972     for (i = 0; i < dwActiveServiceCount; i++)
973     {
974         RtlCreateUnicodeStringFromAsciiz(&lpActiveServices[i].ServiceName,
975                                          lpServiceStartTable[i].lpServiceName);
976         lpActiveServices[i].ServiceMain.A = lpServiceStartTable[i].lpServiceProc;
977         lpActiveServices[i].hServiceStatus = NULL;
978         lpActiveServices[i].bUnicode = FALSE;
979         lpActiveServices[i].bOwnProcess = FALSE;
980     }
981 
982     /* Initialize the connection to the SCM */
983 
984     dwError = ScConnectControlPipe(&hPipe);
985     if (dwError != ERROR_SUCCESS)
986     {
987         bRet = FALSE;
988         goto Done;
989     }
990 
991     dwBufSize = sizeof(SCM_CONTROL_PACKET) +
992                 (MAX_SERVICE_NAME_LENGTH + 1) * sizeof(WCHAR);
993 
994     ControlPacket = RtlAllocateHeap(RtlGetProcessHeap(),
995                                     HEAP_ZERO_MEMORY,
996                                     dwBufSize);
997     if (ControlPacket == NULL)
998     {
999         dwError = ERROR_NOT_ENOUGH_MEMORY;
1000         bRet = FALSE;
1001         goto Done;
1002     }
1003 
1004     ScCreateStatusBinding();
1005 
1006     /* Start the dispatcher loop */
1007     ScServiceDispatcher(hPipe, ControlPacket, dwBufSize);
1008 
1009     /* Close the connection */
1010     ScDestroyStatusBinding();
1011     CloseHandle(hPipe);
1012 
1013     /* Free the control packet */
1014     RtlFreeHeap(RtlGetProcessHeap(), 0, ControlPacket);
1015 
1016 Done:
1017     /* Free the service table */
1018     for (i = 0; i < dwActiveServiceCount; i++)
1019     {
1020         RtlFreeUnicodeString(&lpActiveServices[i].ServiceName);
1021     }
1022     RtlFreeHeap(RtlGetProcessHeap(), 0, lpActiveServices);
1023     lpActiveServices = NULL;
1024     dwActiveServiceCount = 0;
1025 
1026     if (!bRet) SetLastError(dwError);
1027 
1028     return bRet;
1029 }
1030 
1031 
1032 /**********************************************************************
1033  *  StartServiceCtrlDispatcherW
1034  *
1035  * @implemented
1036  */
1037 BOOL WINAPI
1038 StartServiceCtrlDispatcherW(const SERVICE_TABLE_ENTRYW *lpServiceStartTable)
1039 {
1040     ULONG i;
1041     HANDLE hPipe;
1042     DWORD dwError;
1043     PSCM_CONTROL_PACKET ControlPacket;
1044     DWORD dwBufSize;
1045     BOOL bRet = TRUE;
1046 
1047     TRACE("StartServiceCtrlDispatcherW() called\n");
1048 
1049     i = 0;
1050     while (lpServiceStartTable[i].lpServiceProc != NULL)
1051     {
1052         i++;
1053     }
1054 
1055     dwActiveServiceCount = i;
1056 
1057     /* Initialize the service table */
1058     lpActiveServices = RtlAllocateHeap(RtlGetProcessHeap(),
1059                                        HEAP_ZERO_MEMORY,
1060                                        dwActiveServiceCount * sizeof(ACTIVE_SERVICE));
1061     if (lpActiveServices == NULL)
1062     {
1063         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1064         return FALSE;
1065     }
1066 
1067     /* Copy service names and start procedure */
1068     for (i = 0; i < dwActiveServiceCount; i++)
1069     {
1070         RtlCreateUnicodeString(&lpActiveServices[i].ServiceName,
1071                                lpServiceStartTable[i].lpServiceName);
1072         lpActiveServices[i].ServiceMain.W = lpServiceStartTable[i].lpServiceProc;
1073         lpActiveServices[i].hServiceStatus = NULL;
1074         lpActiveServices[i].bUnicode = TRUE;
1075         lpActiveServices[i].bOwnProcess = FALSE;
1076     }
1077 
1078     /* Initialize the connection to the SCM */
1079 
1080     dwError = ScConnectControlPipe(&hPipe);
1081     if (dwError != ERROR_SUCCESS)
1082     {
1083         bRet = FALSE;
1084         goto Done;
1085     }
1086 
1087     dwBufSize = sizeof(SCM_CONTROL_PACKET) +
1088                 (MAX_SERVICE_NAME_LENGTH + 1) * sizeof(WCHAR);
1089 
1090     ControlPacket = RtlAllocateHeap(RtlGetProcessHeap(),
1091                                     HEAP_ZERO_MEMORY,
1092                                     dwBufSize);
1093     if (ControlPacket == NULL)
1094     {
1095         dwError = ERROR_NOT_ENOUGH_MEMORY;
1096         bRet = FALSE;
1097         goto Done;
1098     }
1099 
1100     ScCreateStatusBinding();
1101 
1102     /* Start the dispatcher loop */
1103     ScServiceDispatcher(hPipe, ControlPacket, dwBufSize);
1104 
1105     /* Close the connection */
1106     ScDestroyStatusBinding();
1107     CloseHandle(hPipe);
1108 
1109     /* Free the control packet */
1110     RtlFreeHeap(RtlGetProcessHeap(), 0, ControlPacket);
1111 
1112 Done:
1113     /* Free the service table */
1114     for (i = 0; i < dwActiveServiceCount; i++)
1115     {
1116         RtlFreeUnicodeString(&lpActiveServices[i].ServiceName);
1117     }
1118     RtlFreeHeap(RtlGetProcessHeap(), 0, lpActiveServices);
1119     lpActiveServices = NULL;
1120     dwActiveServiceCount = 0;
1121 
1122     if (!bRet) SetLastError(dwError);
1123 
1124     return bRet;
1125 }
1126 
1127 /* EOF */
1128