wordpad: Apply changes on dropdown selection for comboboxes on toolbar.
[wine] / programs / services / rpc.c
1 /*
2  * Services.exe - RPC functions
3  *
4  * Copyright 2007 Google (Mikolaj Zalewski)
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define WIN32_LEAN_AND_MEAN
22
23 #include <stdarg.h>
24 #include <windows.h>
25 #include <winternl.h>
26 #include <winsvc.h>
27 #include <ntsecapi.h>
28 #include <rpc.h>
29
30 #include "wine/list.h"
31 #include "wine/unicode.h"
32 #include "wine/debug.h"
33
34 #include "services.h"
35 #include "svcctl.h"
36
37 extern HANDLE __wine_make_process_system(void);
38
39 WINE_DEFAULT_DEBUG_CHANNEL(service);
40
41 static const GENERIC_MAPPING g_scm_generic =
42 {
43     (STANDARD_RIGHTS_READ | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_QUERY_LOCK_STATUS),
44     (STANDARD_RIGHTS_WRITE | SC_MANAGER_CREATE_SERVICE | SC_MANAGER_MODIFY_BOOT_CONFIG),
45     (STANDARD_RIGHTS_EXECUTE | SC_MANAGER_CONNECT | SC_MANAGER_LOCK),
46     SC_MANAGER_ALL_ACCESS
47 };
48
49 static const GENERIC_MAPPING g_svc_generic =
50 {
51     (STANDARD_RIGHTS_READ | SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS),
52     (STANDARD_RIGHTS_WRITE | SERVICE_CHANGE_CONFIG),
53     (STANDARD_RIGHTS_EXECUTE | SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_USER_DEFINED_CONTROL),
54     SERVICE_ALL_ACCESS
55 };
56
57 typedef enum
58 {
59     SC_HTYPE_DONT_CARE = 0,
60     SC_HTYPE_MANAGER,
61     SC_HTYPE_SERVICE
62 } SC_HANDLE_TYPE;
63
64 struct sc_handle
65 {
66     SC_HANDLE_TYPE type;
67     DWORD access;
68 };
69
70 struct sc_manager_handle       /* service control manager handle */
71 {
72     struct sc_handle hdr;
73     struct scmdatabase *db;
74 };
75
76 struct sc_service_handle       /* service handle */
77 {
78     struct sc_handle hdr;
79     struct service_entry *service_entry;
80 };
81
82 struct sc_lock
83 {
84     struct scmdatabase *db;
85 };
86
87 /* Check if the given handle is of the required type and allows the requested access. */
88 static DWORD validate_context_handle(SC_RPC_HANDLE handle, DWORD type, DWORD needed_access, struct sc_handle **out_hdr)
89 {
90     struct sc_handle *hdr = (struct sc_handle *)handle;
91
92     if (type != SC_HTYPE_DONT_CARE && hdr->type != type)
93     {
94         WINE_ERR("Handle is of an invalid type (%d, %d)\n", hdr->type, type);
95         return ERROR_INVALID_HANDLE;
96     }
97
98     if ((needed_access & hdr->access) != needed_access)
99     {
100         WINE_ERR("Access denied - handle created with access %x, needed %x\n", hdr->access, needed_access);
101         return ERROR_ACCESS_DENIED;
102     }
103
104     *out_hdr = hdr;
105     return ERROR_SUCCESS;
106 }
107
108 static DWORD validate_scm_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_manager_handle **manager)
109 {
110     struct sc_handle *hdr;
111     DWORD err = validate_context_handle(handle, SC_HTYPE_MANAGER, needed_access, &hdr);
112     if (err == ERROR_SUCCESS)
113         *manager = (struct sc_manager_handle *)hdr;
114     return err;
115 }
116
117 static DWORD validate_service_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_service_handle **service)
118 {
119     struct sc_handle *hdr;
120     DWORD err = validate_context_handle(handle, SC_HTYPE_SERVICE, needed_access, &hdr);
121     if (err == ERROR_SUCCESS)
122         *service = (struct sc_service_handle *)hdr;
123     return err;
124 }
125
126 DWORD svcctl_OpenSCManagerW(
127     MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */
128     LPCWSTR DatabaseName,
129     DWORD dwAccessMask,
130     SC_RPC_HANDLE *handle)
131 {
132     struct sc_manager_handle *manager;
133
134     WINE_TRACE("(%s, %s, %x)\n", wine_dbgstr_w(MachineName), wine_dbgstr_w(DatabaseName), dwAccessMask);
135
136     if (DatabaseName != NULL && DatabaseName[0])
137     {
138         if (strcmpW(DatabaseName, SERVICES_FAILED_DATABASEW) == 0)
139             return ERROR_DATABASE_DOES_NOT_EXIST;
140         if (strcmpW(DatabaseName, SERVICES_ACTIVE_DATABASEW) != 0)
141             return ERROR_INVALID_NAME;
142     }
143
144     if (!(manager = HeapAlloc(GetProcessHeap(), 0, sizeof(*manager))))
145         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
146
147     manager->hdr.type = SC_HTYPE_MANAGER;
148
149     if (dwAccessMask & MAXIMUM_ALLOWED)
150         dwAccessMask |= SC_MANAGER_ALL_ACCESS;
151     manager->hdr.access = dwAccessMask;
152     RtlMapGenericMask(&manager->hdr.access, &g_scm_generic);
153     manager->db = active_database;
154     *handle = &manager->hdr;
155
156     return ERROR_SUCCESS;
157 }
158
159 static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle)
160 {
161     struct sc_handle *hdr = (struct sc_handle *)handle;
162     switch (hdr->type)
163     {
164         case SC_HTYPE_MANAGER:
165         {
166             struct sc_manager_handle *manager = (struct sc_manager_handle *)hdr;
167             HeapFree(GetProcessHeap(), 0, manager);
168             break;
169         }
170         case SC_HTYPE_SERVICE:
171         {
172             struct sc_service_handle *service = (struct sc_service_handle *)hdr;
173             release_service(service->service_entry);
174             HeapFree(GetProcessHeap(), 0, service);
175             break;
176         }
177         default:
178             WINE_ERR("invalid handle type %d\n", hdr->type);
179             RpcRaiseException(ERROR_INVALID_HANDLE);
180     }
181 }
182
183 DWORD svcctl_GetServiceDisplayNameW(
184     SC_RPC_HANDLE hSCManager,
185     LPCWSTR lpServiceName,
186     WCHAR *lpBuffer,
187     DWORD cchBufSize,
188     DWORD *cchLength)
189 {
190     struct sc_manager_handle *manager;
191     struct service_entry *entry;
192     DWORD err;
193
194     WINE_TRACE("(%s, %d)\n", wine_dbgstr_w(lpServiceName), cchBufSize);
195
196     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
197         return err;
198
199     scmdatabase_lock_shared(manager->db);
200
201     entry = scmdatabase_find_service(manager->db, lpServiceName);
202     if (entry != NULL)
203     {
204         LPCWSTR name;
205         service_lock_shared(entry);
206         name = get_display_name(entry);
207         *cchLength = strlenW(name);
208         if (*cchLength < cchBufSize)
209         {
210             err = ERROR_SUCCESS;
211             lstrcpyW(lpBuffer, name);
212         }
213         else
214             err = ERROR_INSUFFICIENT_BUFFER;
215         service_unlock(entry);
216     }
217     else
218     {
219         *cchLength = 1;
220         err = ERROR_SERVICE_DOES_NOT_EXIST;
221     }
222
223     scmdatabase_unlock(manager->db);
224
225     if (err != ERROR_SUCCESS && cchBufSize > 0)
226         lpBuffer[0] = 0;
227
228     return err;
229 }
230
231 DWORD svcctl_GetServiceKeyNameW(
232     SC_RPC_HANDLE hSCManager,
233     LPCWSTR lpServiceDisplayName,
234     WCHAR *lpBuffer,
235     DWORD cchBufSize,
236     DWORD *cchLength)
237 {
238     struct service_entry *entry;
239     struct sc_manager_handle *manager;
240     DWORD err;
241
242     WINE_TRACE("(%s, %d)\n", wine_dbgstr_w(lpServiceDisplayName), cchBufSize);
243
244     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
245         return err;
246
247     scmdatabase_lock_shared(manager->db);
248
249     entry = scmdatabase_find_service_by_displayname(manager->db, lpServiceDisplayName);
250     if (entry != NULL)
251     {
252         service_lock_shared(entry);
253         *cchLength = strlenW(entry->name);
254         if (*cchLength < cchBufSize)
255         {
256             err = ERROR_SUCCESS;
257             lstrcpyW(lpBuffer, entry->name);
258         }
259         else
260             err = ERROR_INSUFFICIENT_BUFFER;
261         service_unlock(entry);
262     }
263     else
264     {
265         *cchLength = 1;
266         err = ERROR_SERVICE_DOES_NOT_EXIST;
267     }
268
269     scmdatabase_unlock(manager->db);
270
271     if (err != ERROR_SUCCESS && cchBufSize > 0)
272         lpBuffer[0] = 0;
273
274     return err;
275 }
276
277 static DWORD create_handle_for_service(struct service_entry *entry, DWORD dwDesiredAccess, SC_RPC_HANDLE *phService)
278 {
279     struct sc_service_handle *service;
280
281     if (!(service = HeapAlloc(GetProcessHeap(), 0, sizeof(*service))))
282     {
283         release_service(entry);
284         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
285     }
286
287     service->hdr.type = SC_HTYPE_SERVICE;
288     service->hdr.access = dwDesiredAccess;
289     RtlMapGenericMask(&service->hdr.access, &g_svc_generic);
290     service->service_entry = entry;
291     if (dwDesiredAccess & MAXIMUM_ALLOWED)
292         dwDesiredAccess |= SERVICE_ALL_ACCESS;
293
294     *phService = &service->hdr;
295     return ERROR_SUCCESS;
296 }
297
298 DWORD svcctl_OpenServiceW(
299     SC_RPC_HANDLE hSCManager,
300     LPCWSTR lpServiceName,
301     DWORD dwDesiredAccess,
302     SC_RPC_HANDLE *phService)
303 {
304     struct sc_manager_handle *manager;
305     struct service_entry *entry;
306     DWORD err;
307
308     WINE_TRACE("(%s, 0x%x)\n", wine_dbgstr_w(lpServiceName), dwDesiredAccess);
309
310     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
311         return err;
312     if (!validate_service_name(lpServiceName))
313         return ERROR_INVALID_NAME;
314
315     scmdatabase_lock_shared(manager->db);
316     entry = scmdatabase_find_service(manager->db, lpServiceName);
317     if (entry != NULL)
318         entry->ref_count++;
319     scmdatabase_unlock(manager->db);
320
321     if (entry == NULL)
322         return ERROR_SERVICE_DOES_NOT_EXIST;
323
324     return create_handle_for_service(entry, dwDesiredAccess, phService);
325 }
326
327 DWORD svcctl_CreateServiceW(
328     SC_RPC_HANDLE hSCManager,
329     LPCWSTR lpServiceName,
330     LPCWSTR lpDisplayName,
331     DWORD dwDesiredAccess,
332     DWORD dwServiceType,
333     DWORD dwStartType,
334     DWORD dwErrorControl,
335     LPCWSTR lpBinaryPathName,
336     LPCWSTR lpLoadOrderGroup,
337     DWORD *lpdwTagId,
338     const BYTE *lpDependencies,
339     DWORD dwDependenciesSize,
340     LPCWSTR lpServiceStartName,
341     const BYTE *lpPassword,
342     DWORD dwPasswordSize,
343     SC_RPC_HANDLE *phService)
344 {
345     struct sc_manager_handle *manager;
346     struct service_entry *entry;
347     DWORD err;
348
349     WINE_TRACE("(%s, %s, 0x%x, %s)\n", wine_dbgstr_w(lpServiceName), wine_dbgstr_w(lpDisplayName), dwDesiredAccess, wine_dbgstr_w(lpBinaryPathName));
350
351     if ((err = validate_scm_handle(hSCManager, SC_MANAGER_CREATE_SERVICE, &manager)) != ERROR_SUCCESS)
352         return err;
353
354     if (!validate_service_name(lpServiceName))
355         return ERROR_INVALID_NAME;
356     if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize) || !lpServiceName[0] || !lpBinaryPathName[0])
357         return ERROR_INVALID_PARAMETER;
358
359     if (lpPassword)
360         WINE_FIXME("Don't know how to add a password\n");   /* I always get ERROR_GEN_FAILURE */
361     if (lpDependencies)
362         WINE_FIXME("Dependencies not supported yet\n");
363
364     err = service_create(lpServiceName, &entry);
365     if (err != ERROR_SUCCESS)
366         return err;
367     entry->config.dwServiceType = dwServiceType;
368     entry->config.dwStartType = dwStartType;
369     entry->config.dwErrorControl = dwErrorControl;
370     entry->config.lpBinaryPathName = strdupW(lpBinaryPathName);
371     entry->config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
372     entry->config.lpServiceStartName = strdupW(lpServiceStartName);
373     entry->config.lpDisplayName = strdupW(lpDisplayName);
374
375     if (lpdwTagId)      /* TODO: In most situations a non-NULL TagId will generate an ERROR_INVALID_PARAMETER. */
376         entry->config.dwTagId = *lpdwTagId;
377     else
378         entry->config.dwTagId = 0;
379
380     /* other fields NULL*/
381
382     if (!validate_service_config(entry))
383     {
384         WINE_ERR("Invalid data while trying to create service\n");
385         free_service_entry(entry);
386         return ERROR_INVALID_PARAMETER;
387     }
388
389     scmdatabase_lock_exclusive(manager->db);
390
391     if (scmdatabase_find_service(manager->db, lpServiceName))
392     {
393         scmdatabase_unlock(manager->db);
394         free_service_entry(entry);
395         return ERROR_SERVICE_EXISTS;
396     }
397
398     if (scmdatabase_find_service_by_displayname(manager->db, get_display_name(entry)))
399     {
400         scmdatabase_unlock(manager->db);
401         free_service_entry(entry);
402         return ERROR_DUPLICATE_SERVICE_NAME;
403     }
404
405     err = scmdatabase_add_service(manager->db, entry);
406     if (err != ERROR_SUCCESS)
407     {
408         scmdatabase_unlock(manager->db);
409         free_service_entry(entry);
410         return err;
411     }
412     scmdatabase_unlock(manager->db);
413
414     return create_handle_for_service(entry, dwDesiredAccess, phService);
415 }
416
417 DWORD svcctl_DeleteService(
418     SC_RPC_HANDLE hService)
419 {
420     struct sc_service_handle *service;
421     DWORD err;
422
423     if ((err = validate_service_handle(hService, DELETE, &service)) != ERROR_SUCCESS)
424         return err;
425
426     scmdatabase_lock_exclusive(service->service_entry->db);
427     service_lock_exclusive(service->service_entry);
428
429     if (!is_marked_for_delete(service->service_entry))
430         err = scmdatabase_remove_service(service->service_entry->db, service->service_entry);
431     else
432         err = ERROR_SERVICE_MARKED_FOR_DELETE;
433
434     service_unlock(service->service_entry);
435     scmdatabase_unlock(service->service_entry->db);
436
437     return err;
438 }
439
440 DWORD svcctl_QueryServiceConfigW(
441         SC_RPC_HANDLE hService,
442         QUERY_SERVICE_CONFIGW *config)
443 {
444     struct sc_service_handle *service;
445     DWORD err;
446
447     WINE_TRACE("(%p)\n", config);
448
449     if ((err = validate_service_handle(hService, SERVICE_QUERY_CONFIG, &service)) != 0)
450         return err;
451
452     service_lock_shared(service->service_entry);
453     config->dwServiceType = service->service_entry->config.dwServiceType;
454     config->dwStartType = service->service_entry->config.dwStartType;
455     config->dwErrorControl = service->service_entry->config.dwErrorControl;
456     config->lpBinaryPathName = strdupW(service->service_entry->config.lpBinaryPathName);
457     config->lpLoadOrderGroup = strdupW(service->service_entry->config.lpLoadOrderGroup);
458     config->dwTagId = service->service_entry->config.dwTagId;
459     config->lpDependencies = NULL; /* TODO */
460     config->lpServiceStartName = strdupW(service->service_entry->config.lpServiceStartName);
461     config->lpDisplayName = strdupW(service->service_entry->config.lpDisplayName);
462     service_unlock(service->service_entry);
463
464     return ERROR_SUCCESS;
465 }
466
467 DWORD svcctl_ChangeServiceConfigW(
468         SC_RPC_HANDLE hService,
469         DWORD dwServiceType,
470         DWORD dwStartType,
471         DWORD dwErrorControl,
472         LPCWSTR lpBinaryPathName,
473         LPCWSTR lpLoadOrderGroup,
474         DWORD *lpdwTagId,
475         const BYTE *lpDependencies,
476         DWORD dwDependenciesSize,
477         LPCWSTR lpServiceStartName,
478         const BYTE *lpPassword,
479         DWORD dwPasswordSize,
480         LPCWSTR lpDisplayName)
481 {
482     struct service_entry new_entry, *entry;
483     struct sc_service_handle *service;
484     DWORD err;
485
486     WINE_TRACE("\n");
487
488     if ((err = validate_service_handle(hService, SERVICE_CHANGE_CONFIG, &service)) != 0)
489         return err;
490
491     if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize))
492         return ERROR_INVALID_PARAMETER;
493
494     /* first check if the new configuration is correct */
495     service_lock_exclusive(service->service_entry);
496
497     if (is_marked_for_delete(service->service_entry))
498     {
499         service_unlock(service->service_entry);
500         return ERROR_SERVICE_MARKED_FOR_DELETE;
501     }
502
503     if (lpDisplayName != NULL &&
504         (entry = scmdatabase_find_service_by_displayname(service->service_entry->db, lpDisplayName)) &&
505         (entry != service->service_entry))
506     {
507         service_unlock(service->service_entry);
508         return ERROR_DUPLICATE_SERVICE_NAME;
509     }
510
511     new_entry = *service->service_entry;
512
513     if (dwServiceType != SERVICE_NO_CHANGE)
514         new_entry.config.dwServiceType = dwServiceType;
515
516     if (dwStartType != SERVICE_NO_CHANGE)
517         new_entry.config.dwStartType = dwStartType;
518
519     if (dwErrorControl != SERVICE_NO_CHANGE)
520         new_entry.config.dwErrorControl = dwErrorControl;
521
522     if (lpBinaryPathName != NULL)
523         new_entry.config.lpBinaryPathName = (LPWSTR)lpBinaryPathName;
524
525     if (lpLoadOrderGroup != NULL)
526         new_entry.config.lpLoadOrderGroup = (LPWSTR)lpLoadOrderGroup;
527
528     if (lpdwTagId != NULL)
529         WINE_FIXME("Changing tag id not supported\n");
530
531     if (lpDependencies != NULL)
532         WINE_FIXME("Chainging dependencies not supported\n");
533
534     if (lpServiceStartName != NULL)
535         new_entry.config.lpServiceStartName = (LPWSTR)lpServiceStartName;
536
537     if (lpPassword != NULL)
538         WINE_FIXME("Setting password not supported\n");
539
540     if (lpDisplayName != NULL)
541         new_entry.config.lpDisplayName = (LPWSTR)lpDisplayName;
542
543     if (!validate_service_config(&new_entry))
544     {
545         WINE_ERR("The configuration after the change wouldn't be valid\n");
546         service_unlock(service->service_entry);
547         return ERROR_INVALID_PARAMETER;
548     }
549
550     /* configuration OK. The strings needs to be duplicated */
551     if (lpBinaryPathName != NULL)
552     {
553         HeapFree(GetProcessHeap(), 0, service->service_entry->config.lpBinaryPathName);
554         new_entry.config.lpBinaryPathName = strdupW(lpBinaryPathName);
555     }
556
557     if (lpLoadOrderGroup != NULL)
558     {
559         HeapFree(GetProcessHeap(), 0, service->service_entry->config.lpLoadOrderGroup);
560         new_entry.config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
561     }
562
563     if (lpServiceStartName != NULL)
564     {
565         HeapFree(GetProcessHeap(), 0, service->service_entry->config.lpServiceStartName);
566         new_entry.config.lpServiceStartName = strdupW(lpServiceStartName);
567     }
568
569     if (lpDisplayName != NULL)
570     {
571         HeapFree(GetProcessHeap(), 0, service->service_entry->config.lpDisplayName);
572         new_entry.config.lpDisplayName = strdupW(lpDisplayName);
573     }
574
575     *service->service_entry = new_entry;
576     save_service_config(service->service_entry);
577     service_unlock(service->service_entry);
578
579     return ERROR_SUCCESS;
580 }
581
582 DWORD svcctl_SetServiceStatus(
583     SC_RPC_HANDLE hServiceStatus,
584     LPSERVICE_STATUS lpServiceStatus)
585 {
586     struct sc_service_handle *service;
587     DWORD err;
588
589     WINE_TRACE("(%p, %p)\n", hServiceStatus, lpServiceStatus);
590
591     if ((err = validate_service_handle(hServiceStatus, SERVICE_SET_STATUS, &service)) != 0)
592         return err;
593
594     service_lock_exclusive(service->service_entry);
595     /* FIXME: be a bit more discriminant about what parts of the status we set
596      * and check that fields are valid */
597     service->service_entry->status.dwServiceType = lpServiceStatus->dwServiceType;
598     service->service_entry->status.dwCurrentState = lpServiceStatus->dwCurrentState;
599     service->service_entry->status.dwControlsAccepted = lpServiceStatus->dwControlsAccepted;
600     service->service_entry->status.dwWin32ExitCode = lpServiceStatus->dwWin32ExitCode;
601     service->service_entry->status.dwServiceSpecificExitCode = lpServiceStatus->dwServiceSpecificExitCode;
602     service->service_entry->status.dwCheckPoint = lpServiceStatus->dwCheckPoint;
603     service->service_entry->status.dwWaitHint = lpServiceStatus->dwWaitHint;
604     service_unlock(service->service_entry);
605
606     if (service->service_entry->status_changed_event)
607         SetEvent(service->service_entry->status_changed_event);
608
609     return ERROR_SUCCESS;
610 }
611
612 DWORD svcctl_QueryServiceStatusEx(
613     SC_RPC_HANDLE hService,
614     SC_STATUS_TYPE InfoLevel,
615     BYTE *lpBuffer,
616     DWORD cbBufSize,
617     LPDWORD pcbBytesNeeded)
618 {
619     struct sc_service_handle *service;
620     DWORD err;
621     LPSERVICE_STATUS_PROCESS pSvcStatusData;
622
623     if ((err = validate_service_handle(hService, SERVICE_QUERY_STATUS, &service)) != 0)
624         return err;
625
626     if (InfoLevel != SC_STATUS_PROCESS_INFO)
627         return ERROR_INVALID_LEVEL;
628
629     pSvcStatusData = (LPSERVICE_STATUS_PROCESS) lpBuffer;
630     if (pSvcStatusData == NULL)
631         return ERROR_INVALID_PARAMETER;
632
633     if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS))
634     {
635         if( pcbBytesNeeded != NULL)
636             *pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);
637
638         return ERROR_INSUFFICIENT_BUFFER;
639     }
640
641     service_lock_shared(service->service_entry);
642
643     pSvcStatusData->dwServiceType = service->service_entry->status.dwServiceType;
644     pSvcStatusData->dwCurrentState = service->service_entry->status.dwCurrentState;
645     pSvcStatusData->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
646     pSvcStatusData->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
647     pSvcStatusData->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
648     pSvcStatusData->dwCheckPoint = service->service_entry->status.dwCheckPoint;
649     pSvcStatusData->dwWaitHint = service->service_entry->status.dwWaitHint;
650     pSvcStatusData->dwProcessId = service->service_entry->status.dwProcessId;
651     pSvcStatusData->dwServiceFlags = service->service_entry->status.dwServiceFlags;
652
653     service_unlock(service->service_entry);
654
655     return ERROR_SUCCESS;
656 }
657
658 /******************************************************************************
659  * service_accepts_control
660  */
661 static BOOL service_accepts_control(const struct service_entry *service, DWORD dwControl)
662 {
663     DWORD a = service->status.dwControlsAccepted;
664
665     switch (dwControl)
666     {
667     case SERVICE_CONTROL_INTERROGATE:
668         return TRUE;
669     case SERVICE_CONTROL_STOP:
670         if (a&SERVICE_ACCEPT_STOP)
671             return TRUE;
672         break;
673     case SERVICE_CONTROL_SHUTDOWN:
674         if (a&SERVICE_ACCEPT_SHUTDOWN)
675             return TRUE;
676         break;
677     case SERVICE_CONTROL_PAUSE:
678     case SERVICE_CONTROL_CONTINUE:
679         if (a&SERVICE_ACCEPT_PAUSE_CONTINUE)
680             return TRUE;
681         break;
682     case SERVICE_CONTROL_PARAMCHANGE:
683         if (a&SERVICE_ACCEPT_PARAMCHANGE)
684             return TRUE;
685         break;
686     case SERVICE_CONTROL_NETBINDADD:
687     case SERVICE_CONTROL_NETBINDREMOVE:
688     case SERVICE_CONTROL_NETBINDENABLE:
689     case SERVICE_CONTROL_NETBINDDISABLE:
690         if (a&SERVICE_ACCEPT_NETBINDCHANGE)
691             return TRUE;
692     case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
693         if (a&SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
694             return TRUE;
695         break;
696     case SERVICE_CONTROL_POWEREVENT:
697         if (a&SERVICE_ACCEPT_POWEREVENT)
698             return TRUE;
699         break;
700     case SERVICE_CONTROL_SESSIONCHANGE:
701         if (a&SERVICE_ACCEPT_SESSIONCHANGE)
702             return TRUE;
703         break;
704     }
705     return FALSE;
706 }
707
708 /******************************************************************************
709  * service_send_control
710  */
711 static BOOL service_send_control(struct service_entry *service, HANDLE pipe, DWORD dwControl, DWORD *result)
712 {
713     service_start_info *ssi;
714     DWORD len, count = 0;
715     BOOL r;
716
717     /* calculate how much space we need to send the startup info */
718     len = strlenW(service->name) + 1;
719
720     ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, data[len]));
721     ssi->cmd = WINESERV_SENDCONTROL;
722     ssi->control = dwControl;
723     ssi->total_size = FIELD_OFFSET(service_start_info, data[len]);
724     ssi->name_size = strlenW(service->name) + 1;
725     strcpyW( ssi->data, service->name );
726
727     r = WriteFile(pipe, ssi, ssi->total_size, &count, NULL);
728     if (!r || count != ssi->total_size)
729     {
730         WINE_ERR("service protocol error - failed to write pipe!\n");
731         return r;
732     }
733     r = ReadFile(pipe, result, sizeof *result, &count, NULL);
734     if (!r || count != sizeof *result)
735         WINE_ERR("service protocol error - failed to read pipe "
736             "r = %d  count = %d!\n", r, count);
737     return r;
738 }
739
740 DWORD svcctl_StartServiceW(
741     SC_RPC_HANDLE hService,
742     DWORD dwNumServiceArgs,
743     LPCWSTR *lpServiceArgVectors)
744 {
745     struct sc_service_handle *service;
746     DWORD err;
747
748     WINE_TRACE("(%p, %d, %p)\n", hService, dwNumServiceArgs, lpServiceArgVectors);
749
750     if ((err = validate_service_handle(hService, SERVICE_START, &service)) != 0)
751         return err;
752
753     err = service_start(service->service_entry, dwNumServiceArgs, lpServiceArgVectors);
754
755     return err;
756 }
757
758 DWORD svcctl_ControlService(
759     SC_RPC_HANDLE hService,
760     DWORD dwControl,
761     SERVICE_STATUS *lpServiceStatus)
762 {
763     DWORD access_required;
764     struct sc_service_handle *service;
765     DWORD err;
766     BOOL ret;
767     HANDLE control_mutex;
768     HANDLE control_pipe;
769
770     WINE_TRACE("(%p, %d, %p)\n", hService, dwControl, lpServiceStatus);
771
772     switch (dwControl)
773     {
774     case SERVICE_CONTROL_CONTINUE:
775     case SERVICE_CONTROL_NETBINDADD:
776     case SERVICE_CONTROL_NETBINDDISABLE:
777     case SERVICE_CONTROL_NETBINDENABLE:
778     case SERVICE_CONTROL_NETBINDREMOVE:
779     case SERVICE_CONTROL_PARAMCHANGE:
780     case SERVICE_CONTROL_PAUSE:
781         access_required = SERVICE_PAUSE_CONTINUE;
782         break;
783     case SERVICE_CONTROL_INTERROGATE:
784         access_required = SERVICE_INTERROGATE;
785         break;
786     case SERVICE_CONTROL_STOP:
787         access_required = SERVICE_STOP;
788         break;
789     default:
790         if (dwControl >= 128 && dwControl <= 255)
791             access_required = SERVICE_USER_DEFINED_CONTROL;
792         else
793             return ERROR_INVALID_PARAMETER;
794     }
795
796     if ((err = validate_service_handle(hService, access_required, &service)) != 0)
797         return err;
798
799     service_lock_exclusive(service->service_entry);
800
801     if (lpServiceStatus)
802     {
803         lpServiceStatus->dwServiceType = service->service_entry->status.dwServiceType;
804         lpServiceStatus->dwCurrentState = service->service_entry->status.dwCurrentState;
805         lpServiceStatus->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
806         lpServiceStatus->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
807         lpServiceStatus->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
808         lpServiceStatus->dwCheckPoint = service->service_entry->status.dwCheckPoint;
809         lpServiceStatus->dwWaitHint = service->service_entry->status.dwWaitHint;
810     }
811
812     if (!service_accepts_control(service->service_entry, dwControl))
813     {
814         service_unlock(service->service_entry);
815         return ERROR_INVALID_SERVICE_CONTROL;
816     }
817
818     switch (service->service_entry->status.dwCurrentState)
819     {
820     case SERVICE_STOPPED:
821         service_unlock(service->service_entry);
822         return ERROR_SERVICE_NOT_ACTIVE;
823     case SERVICE_START_PENDING:
824         if (dwControl==SERVICE_CONTROL_STOP)
825             break;
826         /* fall thru */
827     case SERVICE_STOP_PENDING:
828         service_unlock(service->service_entry);
829         return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
830     }
831
832     /* prevent races by caching these variables and clearing them on
833      * stop here instead of outside the services lock */
834     control_mutex = service->service_entry->control_mutex;
835     control_pipe = service->service_entry->control_pipe;
836     if (dwControl == SERVICE_CONTROL_STOP)
837     {
838         service->service_entry->control_mutex = NULL;
839         service->service_entry->control_pipe = INVALID_HANDLE_VALUE;
840     }
841
842     service_unlock(service->service_entry);
843
844     ret = WaitForSingleObject(control_mutex, 30000);
845     if (ret == WAIT_OBJECT_0)
846     {
847         DWORD result = ERROR_SUCCESS;
848
849         ret = service_send_control(service->service_entry, control_pipe, dwControl, &result);
850
851         if (dwControl == SERVICE_CONTROL_STOP)
852         {
853             CloseHandle(control_mutex);
854             CloseHandle(control_pipe);
855         }
856         else
857             ReleaseMutex(control_mutex);
858
859         return result;
860     }
861     else
862     {
863         if (dwControl == SERVICE_CONTROL_STOP)
864         {
865             CloseHandle(control_mutex);
866             CloseHandle(control_pipe);
867         }
868         return ERROR_SERVICE_REQUEST_TIMEOUT;
869     }
870 }
871
872 DWORD svcctl_CloseServiceHandle(
873     SC_RPC_HANDLE *handle)
874 {
875     WINE_TRACE("(&%p)\n", *handle);
876
877     SC_RPC_HANDLE_destroy(*handle);
878     *handle = NULL;
879
880     return ERROR_SUCCESS;
881 }
882
883 static void SC_RPC_LOCK_destroy(SC_RPC_LOCK hLock)
884 {
885     struct sc_lock *lock = hLock;
886     scmdatabase_unlock_startup(lock->db);
887     HeapFree(GetProcessHeap(), 0, lock);
888 }
889
890 void __RPC_USER SC_RPC_LOCK_rundown(SC_RPC_LOCK hLock)
891 {
892     SC_RPC_LOCK_destroy(hLock);
893 }
894
895 DWORD svcctl_LockServiceDatabase(
896     SC_RPC_HANDLE hSCManager,
897     SC_RPC_LOCK *phLock)
898 {
899     struct sc_manager_handle *manager;
900     DWORD err;
901
902     WINE_TRACE("(%p, %p)\n", hSCManager, phLock);
903
904     if ((err = validate_scm_handle(hSCManager, SC_MANAGER_LOCK, &manager)) != ERROR_SUCCESS)
905         return err;
906
907     err = scmdatabase_lock_startup(manager->db);
908     if (err != ERROR_SUCCESS)
909         return err;
910
911     *phLock = HeapAlloc(GetProcessHeap(), 0, sizeof(struct sc_lock));
912     if (!*phLock)
913     {
914         scmdatabase_unlock_startup(manager->db);
915         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
916     }
917
918     return ERROR_SUCCESS;
919 }
920
921 DWORD svcctl_UnlockServiceDatabase(
922     SC_RPC_LOCK *phLock)
923 {
924     WINE_TRACE("(&%p)\n", *phLock);
925
926     SC_RPC_LOCK_destroy(*phLock);
927     *phLock = NULL;
928
929     return ERROR_SUCCESS;
930 }
931
932 DWORD svcctl_QueryServiceObjectSecurity(
933     void)
934 {
935     WINE_FIXME("\n");
936     return ERROR_CALL_NOT_IMPLEMENTED;
937 }
938
939 DWORD svcctl_SetServiceObjectSecurity(
940     void)
941 {
942     WINE_FIXME("\n");
943     return ERROR_CALL_NOT_IMPLEMENTED;
944 }
945
946 DWORD svcctl_QueryServiceStatus(
947     void)
948 {
949     WINE_FIXME("\n");
950     return ERROR_CALL_NOT_IMPLEMENTED;
951 }
952
953
954 DWORD svcctl_NotifyBootConfigStatus(
955     void)
956 {
957     WINE_FIXME("\n");
958     return ERROR_CALL_NOT_IMPLEMENTED;
959 }
960
961 DWORD svcctl_SCSetServiceBitsW(
962     void)
963 {
964     WINE_FIXME("\n");
965     return ERROR_CALL_NOT_IMPLEMENTED;
966 }
967
968
969 DWORD svcctl_EnumDependentServicesW(
970     void)
971 {
972     WINE_FIXME("\n");
973     return ERROR_CALL_NOT_IMPLEMENTED;
974 }
975
976 DWORD svcctl_EnumServicesStatusW(
977     void)
978 {
979     WINE_FIXME("\n");
980     return ERROR_CALL_NOT_IMPLEMENTED;
981 }
982
983
984 DWORD svcctl_QueryServiceLockStatusW(
985     void)
986 {
987     WINE_FIXME("\n");
988     return ERROR_CALL_NOT_IMPLEMENTED;
989 }
990
991 DWORD svcctl_SCSetServiceBitsA(
992     void)
993 {
994     WINE_FIXME("\n");
995     return ERROR_CALL_NOT_IMPLEMENTED;
996 }
997
998 DWORD svcctl_ChangeServiceConfigA(
999     void)
1000 {
1001     WINE_FIXME("\n");
1002     return ERROR_CALL_NOT_IMPLEMENTED;
1003 }
1004
1005 DWORD svcctl_CreateServiceA(
1006     void)
1007 {
1008     WINE_FIXME("\n");
1009     return ERROR_CALL_NOT_IMPLEMENTED;
1010 }
1011
1012 DWORD svcctl_EnumDependentServicesA(
1013     void)
1014 {
1015     WINE_FIXME("\n");
1016     return ERROR_CALL_NOT_IMPLEMENTED;
1017 }
1018
1019 DWORD svcctl_EnumServicesStatusA(
1020     void)
1021 {
1022     WINE_FIXME("\n");
1023     return ERROR_CALL_NOT_IMPLEMENTED;
1024 }
1025
1026 DWORD svcctl_OpenSCManagerA(
1027     void)
1028 {
1029     WINE_FIXME("\n");
1030     return ERROR_CALL_NOT_IMPLEMENTED;
1031 }
1032
1033 DWORD svcctl_OpenServiceA(
1034     void)
1035 {
1036     WINE_FIXME("\n");
1037     return ERROR_CALL_NOT_IMPLEMENTED;
1038 }
1039
1040 DWORD svcctl_QueryServiceConfigA(
1041     void)
1042 {
1043     WINE_FIXME("\n");
1044     return ERROR_CALL_NOT_IMPLEMENTED;
1045 }
1046
1047 DWORD svcctl_QueryServiceLockStatusA(
1048     void)
1049 {
1050     WINE_FIXME("\n");
1051     return ERROR_CALL_NOT_IMPLEMENTED;
1052 }
1053
1054 DWORD svcctl_StartServiceA(
1055     void)
1056 {
1057     WINE_FIXME("\n");
1058     return ERROR_CALL_NOT_IMPLEMENTED;
1059 }
1060
1061 DWORD svcctl_GetServiceDisplayNameA(
1062     void)
1063 {
1064     WINE_FIXME("\n");
1065     return ERROR_CALL_NOT_IMPLEMENTED;
1066 }
1067
1068 DWORD svcctl_GetServiceKeyNameA(
1069     void)
1070 {
1071     WINE_FIXME("\n");
1072     return ERROR_CALL_NOT_IMPLEMENTED;
1073 }
1074
1075 DWORD svcctl_GetCurrentGroupStateW(
1076     void)
1077 {
1078     WINE_FIXME("\n");
1079     return ERROR_CALL_NOT_IMPLEMENTED;
1080 }
1081
1082 DWORD svcctl_EnumServiceGroupW(
1083     void)
1084 {
1085     WINE_FIXME("\n");
1086     return ERROR_CALL_NOT_IMPLEMENTED;
1087 }
1088
1089 DWORD svcctl_ChangeServiceConfig2A(
1090     void)
1091 {
1092     WINE_FIXME("\n");
1093     return ERROR_CALL_NOT_IMPLEMENTED;
1094 }
1095
1096 DWORD svcctl_ChangeServiceConfig2W(
1097     void)
1098 {
1099     WINE_FIXME("\n");
1100     return ERROR_CALL_NOT_IMPLEMENTED;
1101 }
1102
1103 DWORD svcctl_QueryServiceConfig2A(
1104     void)
1105 {
1106     WINE_FIXME("\n");
1107     return ERROR_CALL_NOT_IMPLEMENTED;
1108 }
1109
1110 DWORD svcctl_QueryServiceConfig2W(
1111     void)
1112 {
1113     WINE_FIXME("\n");
1114     return ERROR_CALL_NOT_IMPLEMENTED;
1115 }
1116
1117
1118 DWORD RPC_Init(void)
1119 {
1120     WCHAR transport[] = SVCCTL_TRANSPORT;
1121     WCHAR endpoint[] = SVCCTL_ENDPOINT;
1122     DWORD err;
1123
1124     if ((err = RpcServerUseProtseqEpW(transport, 0, endpoint, NULL)) != ERROR_SUCCESS)
1125     {
1126         WINE_ERR("RpcServerUseProtseq failed with error %u\n", err);
1127         return err;
1128     }
1129
1130     if ((err = RpcServerRegisterIf(svcctl_v2_0_s_ifspec, 0, 0)) != ERROR_SUCCESS)
1131     {
1132         WINE_ERR("RpcServerRegisterIf failed with error %u\n", err);
1133         return err;
1134     }
1135
1136     if ((err = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE)) != ERROR_SUCCESS)
1137     {
1138         WINE_ERR("RpcServerListen failed with error %u\n", err);
1139         return err;
1140     }
1141     return ERROR_SUCCESS;
1142 }
1143
1144 DWORD RPC_MainLoop(void)
1145 {
1146     DWORD err;
1147     HANDLE hExitEvent = __wine_make_process_system();
1148
1149     SetEvent(g_hStartedEvent);
1150
1151     WINE_TRACE("Entered main loop\n");
1152
1153     do
1154     {
1155         err = WaitForSingleObjectEx(hExitEvent, INFINITE, TRUE);
1156         WINE_TRACE("Wait returned %d\n", err);
1157     } while (err != WAIT_OBJECT_0);
1158
1159     WINE_TRACE("Object signaled - wine shutdown\n");
1160     CloseHandle(hExitEvent);
1161     return ERROR_SUCCESS;
1162 }
1163
1164 void __RPC_USER SC_RPC_HANDLE_rundown(SC_RPC_HANDLE handle)
1165 {
1166     SC_RPC_HANDLE_destroy(handle);
1167 }
1168
1169 void  __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t len)
1170 {
1171     return HeapAlloc(GetProcessHeap(), 0, len);
1172 }
1173
1174 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
1175 {
1176     HeapFree(GetProcessHeap(), 0, ptr);
1177 }