services: Fix a typo.
[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 CDECL __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 = 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 = 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 {
189     struct sc_manager_handle *manager;
190     struct service_entry *entry;
191     DWORD err;
192
193     WINE_TRACE("(%s, %d)\n", wine_dbgstr_w(lpServiceName), *cchBufSize);
194
195     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
196         return err;
197
198     scmdatabase_lock_shared(manager->db);
199
200     entry = scmdatabase_find_service(manager->db, lpServiceName);
201     if (entry != NULL)
202     {
203         LPCWSTR name;
204         int len;
205         service_lock_shared(entry);
206         name = get_display_name(entry);
207         len = strlenW(name);
208         if (len <= *cchBufSize)
209         {
210             err = ERROR_SUCCESS;
211             memcpy(lpBuffer, name, (len + 1)*sizeof(*name));
212         }
213         else
214             err = ERROR_INSUFFICIENT_BUFFER;
215         *cchBufSize = len;
216         service_unlock(entry);
217     }
218     else
219         err = ERROR_SERVICE_DOES_NOT_EXIST;
220
221     scmdatabase_unlock(manager->db);
222
223     if (err != ERROR_SUCCESS)
224         lpBuffer[0] = 0;
225
226     return err;
227 }
228
229 DWORD svcctl_GetServiceKeyNameW(
230     SC_RPC_HANDLE hSCManager,
231     LPCWSTR lpServiceDisplayName,
232     WCHAR *lpBuffer,
233     DWORD *cchBufSize)
234 {
235     struct service_entry *entry;
236     struct sc_manager_handle *manager;
237     DWORD err;
238
239     WINE_TRACE("(%s, %d)\n", wine_dbgstr_w(lpServiceDisplayName), *cchBufSize);
240
241     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
242         return err;
243
244     scmdatabase_lock_shared(manager->db);
245
246     entry = scmdatabase_find_service_by_displayname(manager->db, lpServiceDisplayName);
247     if (entry != NULL)
248     {
249         int len;
250         service_lock_shared(entry);
251         len = strlenW(entry->name);
252         if (len <= *cchBufSize)
253         {
254             err = ERROR_SUCCESS;
255             memcpy(lpBuffer, entry->name, (len + 1)*sizeof(*entry->name));
256         }
257         else
258             err = ERROR_INSUFFICIENT_BUFFER;
259         *cchBufSize = len;
260         service_unlock(entry);
261     }
262     else
263         err = ERROR_SERVICE_DOES_NOT_EXIST;
264
265     scmdatabase_unlock(manager->db);
266
267     if (err != ERROR_SUCCESS)
268         lpBuffer[0] = 0;
269
270     return err;
271 }
272
273 static DWORD create_handle_for_service(struct service_entry *entry, DWORD dwDesiredAccess, SC_RPC_HANDLE *phService)
274 {
275     struct sc_service_handle *service;
276
277     if (!(service = HeapAlloc(GetProcessHeap(), 0, sizeof(*service))))
278     {
279         release_service(entry);
280         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
281     }
282
283     service->hdr.type = SC_HTYPE_SERVICE;
284     service->hdr.access = dwDesiredAccess;
285     RtlMapGenericMask(&service->hdr.access, &g_svc_generic);
286     service->service_entry = entry;
287     if (dwDesiredAccess & MAXIMUM_ALLOWED)
288         dwDesiredAccess |= SERVICE_ALL_ACCESS;
289
290     *phService = &service->hdr;
291     return ERROR_SUCCESS;
292 }
293
294 DWORD svcctl_OpenServiceW(
295     SC_RPC_HANDLE hSCManager,
296     LPCWSTR lpServiceName,
297     DWORD dwDesiredAccess,
298     SC_RPC_HANDLE *phService)
299 {
300     struct sc_manager_handle *manager;
301     struct service_entry *entry;
302     DWORD err;
303
304     WINE_TRACE("(%s, 0x%x)\n", wine_dbgstr_w(lpServiceName), dwDesiredAccess);
305
306     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
307         return err;
308     if (!validate_service_name(lpServiceName))
309         return ERROR_INVALID_NAME;
310
311     scmdatabase_lock_shared(manager->db);
312     entry = scmdatabase_find_service(manager->db, lpServiceName);
313     if (entry != NULL)
314         InterlockedIncrement(&entry->ref_count);
315     scmdatabase_unlock(manager->db);
316
317     if (entry == NULL)
318         return ERROR_SERVICE_DOES_NOT_EXIST;
319
320     return create_handle_for_service(entry, dwDesiredAccess, phService);
321 }
322
323 DWORD svcctl_CreateServiceW(
324     SC_RPC_HANDLE hSCManager,
325     LPCWSTR lpServiceName,
326     LPCWSTR lpDisplayName,
327     DWORD dwDesiredAccess,
328     DWORD dwServiceType,
329     DWORD dwStartType,
330     DWORD dwErrorControl,
331     LPCWSTR lpBinaryPathName,
332     LPCWSTR lpLoadOrderGroup,
333     DWORD *lpdwTagId,
334     const BYTE *lpDependencies,
335     DWORD dwDependenciesSize,
336     LPCWSTR lpServiceStartName,
337     const BYTE *lpPassword,
338     DWORD dwPasswordSize,
339     SC_RPC_HANDLE *phService)
340 {
341     struct sc_manager_handle *manager;
342     struct service_entry *entry;
343     DWORD err;
344
345     WINE_TRACE("(%s, %s, 0x%x, %s)\n", wine_dbgstr_w(lpServiceName), wine_dbgstr_w(lpDisplayName), dwDesiredAccess, wine_dbgstr_w(lpBinaryPathName));
346
347     if ((err = validate_scm_handle(hSCManager, SC_MANAGER_CREATE_SERVICE, &manager)) != ERROR_SUCCESS)
348         return err;
349
350     if (!validate_service_name(lpServiceName))
351         return ERROR_INVALID_NAME;
352     if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize) || !lpServiceName[0] || !lpBinaryPathName[0])
353         return ERROR_INVALID_PARAMETER;
354
355     if (lpPassword)
356         WINE_FIXME("Don't know how to add a password\n");   /* I always get ERROR_GEN_FAILURE */
357     if (lpDependencies)
358         WINE_FIXME("Dependencies not supported yet\n");
359
360     err = service_create(lpServiceName, &entry);
361     if (err != ERROR_SUCCESS)
362         return err;
363     entry->ref_count = 1;
364     entry->config.dwServiceType = entry->status.dwServiceType = dwServiceType;
365     entry->config.dwStartType = dwStartType;
366     entry->config.dwErrorControl = dwErrorControl;
367     entry->config.lpBinaryPathName = strdupW(lpBinaryPathName);
368     entry->config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
369     entry->config.lpServiceStartName = strdupW(lpServiceStartName);
370     entry->config.lpDisplayName = strdupW(lpDisplayName);
371
372     if (lpdwTagId)      /* TODO: In most situations a non-NULL TagId will generate an ERROR_INVALID_PARAMETER. */
373         entry->config.dwTagId = *lpdwTagId;
374     else
375         entry->config.dwTagId = 0;
376
377     /* other fields NULL*/
378
379     if (!validate_service_config(entry))
380     {
381         WINE_ERR("Invalid data while trying to create service\n");
382         free_service_entry(entry);
383         return ERROR_INVALID_PARAMETER;
384     }
385
386     scmdatabase_lock_exclusive(manager->db);
387
388     if (scmdatabase_find_service(manager->db, lpServiceName))
389     {
390         scmdatabase_unlock(manager->db);
391         free_service_entry(entry);
392         return ERROR_SERVICE_EXISTS;
393     }
394
395     if (scmdatabase_find_service_by_displayname(manager->db, get_display_name(entry)))
396     {
397         scmdatabase_unlock(manager->db);
398         free_service_entry(entry);
399         return ERROR_DUPLICATE_SERVICE_NAME;
400     }
401
402     err = scmdatabase_add_service(manager->db, entry);
403     if (err != ERROR_SUCCESS)
404     {
405         scmdatabase_unlock(manager->db);
406         free_service_entry(entry);
407         return err;
408     }
409     scmdatabase_unlock(manager->db);
410
411     return create_handle_for_service(entry, dwDesiredAccess, phService);
412 }
413
414 DWORD svcctl_DeleteService(
415     SC_RPC_HANDLE hService)
416 {
417     struct sc_service_handle *service;
418     DWORD err;
419
420     if ((err = validate_service_handle(hService, DELETE, &service)) != ERROR_SUCCESS)
421         return err;
422
423     scmdatabase_lock_exclusive(service->service_entry->db);
424     service_lock_exclusive(service->service_entry);
425
426     if (!is_marked_for_delete(service->service_entry))
427         err = scmdatabase_remove_service(service->service_entry->db, service->service_entry);
428     else
429         err = ERROR_SERVICE_MARKED_FOR_DELETE;
430
431     service_unlock(service->service_entry);
432     scmdatabase_unlock(service->service_entry->db);
433
434     return err;
435 }
436
437 DWORD svcctl_QueryServiceConfigW(
438         SC_RPC_HANDLE hService,
439         QUERY_SERVICE_CONFIGW *config)
440 {
441     struct sc_service_handle *service;
442     DWORD err;
443
444     WINE_TRACE("(%p)\n", config);
445
446     if ((err = validate_service_handle(hService, SERVICE_QUERY_CONFIG, &service)) != 0)
447         return err;
448
449     service_lock_shared(service->service_entry);
450     config->dwServiceType = service->service_entry->config.dwServiceType;
451     config->dwStartType = service->service_entry->config.dwStartType;
452     config->dwErrorControl = service->service_entry->config.dwErrorControl;
453     config->lpBinaryPathName = strdupW(service->service_entry->config.lpBinaryPathName);
454     config->lpLoadOrderGroup = strdupW(service->service_entry->config.lpLoadOrderGroup);
455     config->dwTagId = service->service_entry->config.dwTagId;
456     config->lpDependencies = NULL; /* TODO */
457     config->lpServiceStartName = strdupW(service->service_entry->config.lpServiceStartName);
458     config->lpDisplayName = strdupW(service->service_entry->config.lpDisplayName);
459     service_unlock(service->service_entry);
460
461     return ERROR_SUCCESS;
462 }
463
464 DWORD svcctl_ChangeServiceConfigW(
465         SC_RPC_HANDLE hService,
466         DWORD dwServiceType,
467         DWORD dwStartType,
468         DWORD dwErrorControl,
469         LPCWSTR lpBinaryPathName,
470         LPCWSTR lpLoadOrderGroup,
471         DWORD *lpdwTagId,
472         const BYTE *lpDependencies,
473         DWORD dwDependenciesSize,
474         LPCWSTR lpServiceStartName,
475         const BYTE *lpPassword,
476         DWORD dwPasswordSize,
477         LPCWSTR lpDisplayName)
478 {
479     struct service_entry new_entry, *entry;
480     struct sc_service_handle *service;
481     DWORD err;
482
483     WINE_TRACE("\n");
484
485     if ((err = validate_service_handle(hService, SERVICE_CHANGE_CONFIG, &service)) != 0)
486         return err;
487
488     if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize))
489         return ERROR_INVALID_PARAMETER;
490
491     /* first check if the new configuration is correct */
492     service_lock_exclusive(service->service_entry);
493
494     if (is_marked_for_delete(service->service_entry))
495     {
496         service_unlock(service->service_entry);
497         return ERROR_SERVICE_MARKED_FOR_DELETE;
498     }
499
500     if (lpDisplayName != NULL &&
501         (entry = scmdatabase_find_service_by_displayname(service->service_entry->db, lpDisplayName)) &&
502         (entry != service->service_entry))
503     {
504         service_unlock(service->service_entry);
505         return ERROR_DUPLICATE_SERVICE_NAME;
506     }
507
508     new_entry = *service->service_entry;
509
510     if (dwServiceType != SERVICE_NO_CHANGE)
511         new_entry.config.dwServiceType = dwServiceType;
512
513     if (dwStartType != SERVICE_NO_CHANGE)
514         new_entry.config.dwStartType = dwStartType;
515
516     if (dwErrorControl != SERVICE_NO_CHANGE)
517         new_entry.config.dwErrorControl = dwErrorControl;
518
519     if (lpBinaryPathName != NULL)
520         new_entry.config.lpBinaryPathName = (LPWSTR)lpBinaryPathName;
521
522     if (lpLoadOrderGroup != NULL)
523         new_entry.config.lpLoadOrderGroup = (LPWSTR)lpLoadOrderGroup;
524
525     if (lpdwTagId != NULL)
526         WINE_FIXME("Changing tag id not supported\n");
527
528     if (lpDependencies != NULL)
529         WINE_FIXME("Changing dependencies not supported\n");
530
531     if (lpServiceStartName != NULL)
532         new_entry.config.lpServiceStartName = (LPWSTR)lpServiceStartName;
533
534     if (lpPassword != NULL)
535         WINE_FIXME("Setting password not supported\n");
536
537     if (lpDisplayName != NULL)
538         new_entry.config.lpDisplayName = (LPWSTR)lpDisplayName;
539
540     if (!validate_service_config(&new_entry))
541     {
542         WINE_ERR("The configuration after the change wouldn't be valid\n");
543         service_unlock(service->service_entry);
544         return ERROR_INVALID_PARAMETER;
545     }
546
547     /* configuration OK. The strings needs to be duplicated */
548     if (lpBinaryPathName != NULL)
549     {
550         HeapFree(GetProcessHeap(), 0, service->service_entry->config.lpBinaryPathName);
551         new_entry.config.lpBinaryPathName = strdupW(lpBinaryPathName);
552     }
553
554     if (lpLoadOrderGroup != NULL)
555     {
556         HeapFree(GetProcessHeap(), 0, service->service_entry->config.lpLoadOrderGroup);
557         new_entry.config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
558     }
559
560     if (lpServiceStartName != NULL)
561     {
562         HeapFree(GetProcessHeap(), 0, service->service_entry->config.lpServiceStartName);
563         new_entry.config.lpServiceStartName = strdupW(lpServiceStartName);
564     }
565
566     if (lpDisplayName != NULL)
567     {
568         HeapFree(GetProcessHeap(), 0, service->service_entry->config.lpDisplayName);
569         new_entry.config.lpDisplayName = strdupW(lpDisplayName);
570     }
571
572     *service->service_entry = new_entry;
573     save_service_config(service->service_entry);
574     service_unlock(service->service_entry);
575
576     return ERROR_SUCCESS;
577 }
578
579 DWORD svcctl_SetServiceStatus(
580     SC_RPC_HANDLE hServiceStatus,
581     LPSERVICE_STATUS lpServiceStatus)
582 {
583     struct sc_service_handle *service;
584     DWORD err;
585
586     WINE_TRACE("(%p, %p)\n", hServiceStatus, lpServiceStatus);
587
588     if ((err = validate_service_handle(hServiceStatus, SERVICE_SET_STATUS, &service)) != 0)
589         return err;
590
591     service_lock_exclusive(service->service_entry);
592     /* FIXME: be a bit more discriminant about what parts of the status we set
593      * and check that fields are valid */
594     service->service_entry->status.dwServiceType = lpServiceStatus->dwServiceType;
595     service->service_entry->status.dwCurrentState = lpServiceStatus->dwCurrentState;
596     service->service_entry->status.dwControlsAccepted = lpServiceStatus->dwControlsAccepted;
597     service->service_entry->status.dwWin32ExitCode = lpServiceStatus->dwWin32ExitCode;
598     service->service_entry->status.dwServiceSpecificExitCode = lpServiceStatus->dwServiceSpecificExitCode;
599     service->service_entry->status.dwCheckPoint = lpServiceStatus->dwCheckPoint;
600     service->service_entry->status.dwWaitHint = lpServiceStatus->dwWaitHint;
601     service_unlock(service->service_entry);
602
603     if (service->service_entry->status_changed_event)
604         SetEvent(service->service_entry->status_changed_event);
605
606     return ERROR_SUCCESS;
607 }
608
609 DWORD svcctl_ChangeServiceConfig2W( SC_RPC_HANDLE hService, DWORD level, SERVICE_CONFIG2W *config )
610 {
611     struct sc_service_handle *service;
612     DWORD err;
613
614     if ((err = validate_service_handle(hService, SERVICE_CHANGE_CONFIG, &service)) != 0)
615         return err;
616
617     switch (level)
618     {
619     case SERVICE_CONFIG_DESCRIPTION:
620         {
621             WCHAR *descr = NULL;
622
623             if (config->descr.lpDescription[0])
624             {
625                 if (!(descr = strdupW( config->descr.lpDescription )))
626                     return ERROR_NOT_ENOUGH_MEMORY;
627             }
628
629             WINE_TRACE( "changing service %p descr to %s\n", service, wine_dbgstr_w(descr) );
630             service_lock_exclusive( service->service_entry );
631             HeapFree( GetProcessHeap(), 0, service->service_entry->description );
632             service->service_entry->description = descr;
633             save_service_config( service->service_entry );
634             service_unlock( service->service_entry );
635         }
636         break;
637     case SERVICE_CONFIG_FAILURE_ACTIONS:
638         WINE_FIXME( "SERVICE_CONFIG_FAILURE_ACTIONS not implemented: period %u msg %s cmd %s\n",
639                     config->actions.dwResetPeriod,
640                     wine_dbgstr_w(config->actions.lpRebootMsg),
641                     wine_dbgstr_w(config->actions.lpCommand) );
642         break;
643     default:
644         WINE_FIXME("level %u not implemented\n", level);
645         err = ERROR_INVALID_LEVEL;
646         break;
647     }
648     return err;
649 }
650
651 DWORD svcctl_QueryServiceConfig2W( SC_RPC_HANDLE hService, DWORD level,
652                                    BYTE *buffer, DWORD size, LPDWORD needed )
653 {
654     struct sc_service_handle *service;
655     DWORD err;
656
657     memset(buffer, 0, size);
658
659     if ((err = validate_service_handle(hService, SERVICE_QUERY_STATUS, &service)) != 0)
660         return err;
661
662     switch (level)
663     {
664     case SERVICE_CONFIG_DESCRIPTION:
665         {
666             SERVICE_DESCRIPTIONW *descr = (SERVICE_DESCRIPTIONW *)buffer;
667
668             service_lock_shared(service->service_entry);
669             *needed = sizeof(*descr);
670             if (service->service_entry->description)
671                 *needed += (strlenW(service->service_entry->description) + 1) * sizeof(WCHAR);
672             if (size >= *needed)
673             {
674                 if (service->service_entry->description)
675                 {
676                     /* store a buffer offset instead of a pointer */
677                     descr->lpDescription = (WCHAR *)((BYTE *)(descr + 1) - buffer);
678                     strcpyW( (WCHAR *)(descr + 1), service->service_entry->description );
679                 }
680                 else descr->lpDescription = NULL;
681             }
682             else err = ERROR_INSUFFICIENT_BUFFER;
683             service_unlock(service->service_entry);
684         }
685         break;
686
687     default:
688         WINE_FIXME("level %u not implemented\n", level);
689         err = ERROR_INVALID_LEVEL;
690         break;
691     }
692     return err;
693 }
694
695 DWORD svcctl_QueryServiceStatusEx(
696     SC_RPC_HANDLE hService,
697     SC_STATUS_TYPE InfoLevel,
698     BYTE *lpBuffer,
699     DWORD cbBufSize,
700     LPDWORD pcbBytesNeeded)
701 {
702     struct sc_service_handle *service;
703     DWORD err;
704     LPSERVICE_STATUS_PROCESS pSvcStatusData;
705
706     memset(lpBuffer, 0, cbBufSize);
707
708     if ((err = validate_service_handle(hService, SERVICE_QUERY_STATUS, &service)) != 0)
709         return err;
710
711     if (InfoLevel != SC_STATUS_PROCESS_INFO)
712         return ERROR_INVALID_LEVEL;
713
714     pSvcStatusData = (LPSERVICE_STATUS_PROCESS) lpBuffer;
715     if (pSvcStatusData == NULL)
716         return ERROR_INVALID_PARAMETER;
717
718     if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS))
719     {
720         if( pcbBytesNeeded != NULL)
721             *pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);
722
723         return ERROR_INSUFFICIENT_BUFFER;
724     }
725
726     service_lock_shared(service->service_entry);
727
728     pSvcStatusData->dwServiceType = service->service_entry->status.dwServiceType;
729     pSvcStatusData->dwCurrentState = service->service_entry->status.dwCurrentState;
730     pSvcStatusData->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
731     pSvcStatusData->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
732     pSvcStatusData->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
733     pSvcStatusData->dwCheckPoint = service->service_entry->status.dwCheckPoint;
734     pSvcStatusData->dwWaitHint = service->service_entry->status.dwWaitHint;
735     pSvcStatusData->dwProcessId = service->service_entry->status.dwProcessId;
736     pSvcStatusData->dwServiceFlags = service->service_entry->status.dwServiceFlags;
737
738     service_unlock(service->service_entry);
739
740     return ERROR_SUCCESS;
741 }
742
743 /******************************************************************************
744  * service_accepts_control
745  */
746 static BOOL service_accepts_control(const struct service_entry *service, DWORD dwControl)
747 {
748     DWORD a = service->status.dwControlsAccepted;
749
750     switch (dwControl)
751     {
752     case SERVICE_CONTROL_INTERROGATE:
753         return TRUE;
754     case SERVICE_CONTROL_STOP:
755         if (a&SERVICE_ACCEPT_STOP)
756             return TRUE;
757         break;
758     case SERVICE_CONTROL_SHUTDOWN:
759         if (a&SERVICE_ACCEPT_SHUTDOWN)
760             return TRUE;
761         break;
762     case SERVICE_CONTROL_PAUSE:
763     case SERVICE_CONTROL_CONTINUE:
764         if (a&SERVICE_ACCEPT_PAUSE_CONTINUE)
765             return TRUE;
766         break;
767     case SERVICE_CONTROL_PARAMCHANGE:
768         if (a&SERVICE_ACCEPT_PARAMCHANGE)
769             return TRUE;
770         break;
771     case SERVICE_CONTROL_NETBINDADD:
772     case SERVICE_CONTROL_NETBINDREMOVE:
773     case SERVICE_CONTROL_NETBINDENABLE:
774     case SERVICE_CONTROL_NETBINDDISABLE:
775         if (a&SERVICE_ACCEPT_NETBINDCHANGE)
776             return TRUE;
777     case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
778         if (a&SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
779             return TRUE;
780         break;
781     case SERVICE_CONTROL_POWEREVENT:
782         if (a&SERVICE_ACCEPT_POWEREVENT)
783             return TRUE;
784         break;
785     case SERVICE_CONTROL_SESSIONCHANGE:
786         if (a&SERVICE_ACCEPT_SESSIONCHANGE)
787             return TRUE;
788         break;
789     }
790     return FALSE;
791 }
792
793 /******************************************************************************
794  * service_send_control
795  */
796 static BOOL service_send_control(struct service_entry *service, HANDLE pipe, DWORD dwControl, DWORD *result)
797 {
798     service_start_info *ssi;
799     DWORD len, count = 0;
800     BOOL r;
801
802     /* calculate how much space we need to send the startup info */
803     len = strlenW(service->name) + 1;
804
805     ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, data[len]));
806     ssi->cmd = WINESERV_SENDCONTROL;
807     ssi->control = dwControl;
808     ssi->total_size = FIELD_OFFSET(service_start_info, data[len]);
809     ssi->name_size = strlenW(service->name) + 1;
810     strcpyW( ssi->data, service->name );
811
812     r = WriteFile(pipe, ssi, ssi->total_size, &count, NULL);
813     if (!r || count != ssi->total_size)
814     {
815         WINE_ERR("service protocol error - failed to write pipe!\n");
816         return r;
817     }
818     r = ReadFile(pipe, result, sizeof *result, &count, NULL);
819     if (!r || count != sizeof *result)
820         WINE_ERR("service protocol error - failed to read pipe "
821             "r = %d  count = %d!\n", r, count);
822     return r;
823 }
824
825 DWORD svcctl_StartServiceW(
826     SC_RPC_HANDLE hService,
827     DWORD dwNumServiceArgs,
828     LPCWSTR *lpServiceArgVectors)
829 {
830     struct sc_service_handle *service;
831     DWORD err;
832
833     WINE_TRACE("(%p, %d, %p)\n", hService, dwNumServiceArgs, lpServiceArgVectors);
834
835     if ((err = validate_service_handle(hService, SERVICE_START, &service)) != 0)
836         return err;
837
838     err = service_start(service->service_entry, dwNumServiceArgs, lpServiceArgVectors);
839
840     return err;
841 }
842
843 DWORD svcctl_ControlService(
844     SC_RPC_HANDLE hService,
845     DWORD dwControl,
846     SERVICE_STATUS *lpServiceStatus)
847 {
848     DWORD access_required;
849     struct sc_service_handle *service;
850     DWORD err;
851     BOOL ret;
852     HANDLE control_mutex;
853     HANDLE control_pipe;
854
855     WINE_TRACE("(%p, %d, %p)\n", hService, dwControl, lpServiceStatus);
856
857     switch (dwControl)
858     {
859     case SERVICE_CONTROL_CONTINUE:
860     case SERVICE_CONTROL_NETBINDADD:
861     case SERVICE_CONTROL_NETBINDDISABLE:
862     case SERVICE_CONTROL_NETBINDENABLE:
863     case SERVICE_CONTROL_NETBINDREMOVE:
864     case SERVICE_CONTROL_PARAMCHANGE:
865     case SERVICE_CONTROL_PAUSE:
866         access_required = SERVICE_PAUSE_CONTINUE;
867         break;
868     case SERVICE_CONTROL_INTERROGATE:
869         access_required = SERVICE_INTERROGATE;
870         break;
871     case SERVICE_CONTROL_STOP:
872         access_required = SERVICE_STOP;
873         break;
874     default:
875         if (dwControl >= 128 && dwControl <= 255)
876             access_required = SERVICE_USER_DEFINED_CONTROL;
877         else
878             return ERROR_INVALID_PARAMETER;
879     }
880
881     if ((err = validate_service_handle(hService, access_required, &service)) != 0)
882         return err;
883
884     service_lock_exclusive(service->service_entry);
885
886     if (lpServiceStatus)
887     {
888         lpServiceStatus->dwServiceType = service->service_entry->status.dwServiceType;
889         lpServiceStatus->dwCurrentState = service->service_entry->status.dwCurrentState;
890         lpServiceStatus->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
891         lpServiceStatus->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
892         lpServiceStatus->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
893         lpServiceStatus->dwCheckPoint = service->service_entry->status.dwCheckPoint;
894         lpServiceStatus->dwWaitHint = service->service_entry->status.dwWaitHint;
895     }
896
897     if (!service_accepts_control(service->service_entry, dwControl))
898     {
899         service_unlock(service->service_entry);
900         return ERROR_INVALID_SERVICE_CONTROL;
901     }
902
903     switch (service->service_entry->status.dwCurrentState)
904     {
905     case SERVICE_STOPPED:
906         service_unlock(service->service_entry);
907         return ERROR_SERVICE_NOT_ACTIVE;
908     case SERVICE_START_PENDING:
909         if (dwControl==SERVICE_CONTROL_STOP)
910             break;
911         /* fall thru */
912     case SERVICE_STOP_PENDING:
913         service_unlock(service->service_entry);
914         return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
915     }
916
917     /* prevent races by caching these variables and clearing them on
918      * stop here instead of outside the services lock */
919     control_mutex = service->service_entry->control_mutex;
920     control_pipe = service->service_entry->control_pipe;
921     if (dwControl == SERVICE_CONTROL_STOP)
922     {
923         service->service_entry->control_mutex = NULL;
924         service->service_entry->control_pipe = INVALID_HANDLE_VALUE;
925     }
926
927     service_unlock(service->service_entry);
928
929     ret = WaitForSingleObject(control_mutex, 30000);
930     if (ret == WAIT_OBJECT_0)
931     {
932         DWORD result = ERROR_SUCCESS;
933
934         ret = service_send_control(service->service_entry, control_pipe, dwControl, &result);
935
936         if (dwControl == SERVICE_CONTROL_STOP)
937         {
938             CloseHandle(control_mutex);
939             CloseHandle(control_pipe);
940         }
941         else
942             ReleaseMutex(control_mutex);
943
944         return result;
945     }
946     else
947     {
948         if (dwControl == SERVICE_CONTROL_STOP)
949         {
950             CloseHandle(control_mutex);
951             CloseHandle(control_pipe);
952         }
953         return ERROR_SERVICE_REQUEST_TIMEOUT;
954     }
955 }
956
957 DWORD svcctl_CloseServiceHandle(
958     SC_RPC_HANDLE *handle)
959 {
960     WINE_TRACE("(&%p)\n", *handle);
961
962     SC_RPC_HANDLE_destroy(*handle);
963     *handle = NULL;
964
965     return ERROR_SUCCESS;
966 }
967
968 static void SC_RPC_LOCK_destroy(SC_RPC_LOCK hLock)
969 {
970     struct sc_lock *lock = hLock;
971     scmdatabase_unlock_startup(lock->db);
972     HeapFree(GetProcessHeap(), 0, lock);
973 }
974
975 void __RPC_USER SC_RPC_LOCK_rundown(SC_RPC_LOCK hLock)
976 {
977     SC_RPC_LOCK_destroy(hLock);
978 }
979
980 DWORD svcctl_LockServiceDatabase(
981     SC_RPC_HANDLE hSCManager,
982     SC_RPC_LOCK *phLock)
983 {
984     struct sc_manager_handle *manager;
985     struct sc_lock *lock;
986     DWORD err;
987
988     WINE_TRACE("(%p, %p)\n", hSCManager, phLock);
989
990     if ((err = validate_scm_handle(hSCManager, SC_MANAGER_LOCK, &manager)) != ERROR_SUCCESS)
991         return err;
992
993     err = scmdatabase_lock_startup(manager->db);
994     if (err != ERROR_SUCCESS)
995         return err;
996
997     lock = HeapAlloc(GetProcessHeap(), 0, sizeof(struct sc_lock));
998     if (!lock)
999     {
1000         scmdatabase_unlock_startup(manager->db);
1001         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
1002     }
1003
1004     lock->db = manager->db;
1005     *phLock = lock;
1006
1007     return ERROR_SUCCESS;
1008 }
1009
1010 DWORD svcctl_UnlockServiceDatabase(
1011     SC_RPC_LOCK *phLock)
1012 {
1013     WINE_TRACE("(&%p)\n", *phLock);
1014
1015     SC_RPC_LOCK_destroy(*phLock);
1016     *phLock = NULL;
1017
1018     return ERROR_SUCCESS;
1019 }
1020
1021 DWORD svcctl_QueryServiceObjectSecurity(
1022     void)
1023 {
1024     WINE_FIXME("\n");
1025     return ERROR_CALL_NOT_IMPLEMENTED;
1026 }
1027
1028 DWORD svcctl_SetServiceObjectSecurity(
1029     void)
1030 {
1031     WINE_FIXME("\n");
1032     return ERROR_CALL_NOT_IMPLEMENTED;
1033 }
1034
1035 DWORD svcctl_QueryServiceStatus(
1036     void)
1037 {
1038     WINE_FIXME("\n");
1039     return ERROR_CALL_NOT_IMPLEMENTED;
1040 }
1041
1042
1043 DWORD svcctl_NotifyBootConfigStatus(
1044     void)
1045 {
1046     WINE_FIXME("\n");
1047     return ERROR_CALL_NOT_IMPLEMENTED;
1048 }
1049
1050 DWORD svcctl_SCSetServiceBitsW(
1051     void)
1052 {
1053     WINE_FIXME("\n");
1054     return ERROR_CALL_NOT_IMPLEMENTED;
1055 }
1056
1057
1058 DWORD svcctl_EnumDependentServicesW(
1059     void)
1060 {
1061     WINE_FIXME("\n");
1062     return ERROR_CALL_NOT_IMPLEMENTED;
1063 }
1064
1065 DWORD svcctl_EnumServicesStatusW(
1066     void)
1067 {
1068     WINE_FIXME("\n");
1069     return ERROR_CALL_NOT_IMPLEMENTED;
1070 }
1071
1072
1073 DWORD svcctl_QueryServiceLockStatusW(
1074     void)
1075 {
1076     WINE_FIXME("\n");
1077     return ERROR_CALL_NOT_IMPLEMENTED;
1078 }
1079
1080 DWORD svcctl_SCSetServiceBitsA(
1081     void)
1082 {
1083     WINE_FIXME("\n");
1084     return ERROR_CALL_NOT_IMPLEMENTED;
1085 }
1086
1087 DWORD svcctl_ChangeServiceConfigA(
1088     void)
1089 {
1090     WINE_FIXME("\n");
1091     return ERROR_CALL_NOT_IMPLEMENTED;
1092 }
1093
1094 DWORD svcctl_CreateServiceA(
1095     void)
1096 {
1097     WINE_FIXME("\n");
1098     return ERROR_CALL_NOT_IMPLEMENTED;
1099 }
1100
1101 DWORD svcctl_EnumDependentServicesA(
1102     void)
1103 {
1104     WINE_FIXME("\n");
1105     return ERROR_CALL_NOT_IMPLEMENTED;
1106 }
1107
1108 DWORD svcctl_EnumServicesStatusA(
1109     void)
1110 {
1111     WINE_FIXME("\n");
1112     return ERROR_CALL_NOT_IMPLEMENTED;
1113 }
1114
1115 DWORD svcctl_OpenSCManagerA(
1116     void)
1117 {
1118     WINE_FIXME("\n");
1119     return ERROR_CALL_NOT_IMPLEMENTED;
1120 }
1121
1122 DWORD svcctl_OpenServiceA(
1123     void)
1124 {
1125     WINE_FIXME("\n");
1126     return ERROR_CALL_NOT_IMPLEMENTED;
1127 }
1128
1129 DWORD svcctl_QueryServiceConfigA(
1130     void)
1131 {
1132     WINE_FIXME("\n");
1133     return ERROR_CALL_NOT_IMPLEMENTED;
1134 }
1135
1136 DWORD svcctl_QueryServiceLockStatusA(
1137     void)
1138 {
1139     WINE_FIXME("\n");
1140     return ERROR_CALL_NOT_IMPLEMENTED;
1141 }
1142
1143 DWORD svcctl_StartServiceA(
1144     void)
1145 {
1146     WINE_FIXME("\n");
1147     return ERROR_CALL_NOT_IMPLEMENTED;
1148 }
1149
1150 DWORD svcctl_GetServiceDisplayNameA(
1151     void)
1152 {
1153     WINE_FIXME("\n");
1154     return ERROR_CALL_NOT_IMPLEMENTED;
1155 }
1156
1157 DWORD svcctl_GetServiceKeyNameA(
1158     void)
1159 {
1160     WINE_FIXME("\n");
1161     return ERROR_CALL_NOT_IMPLEMENTED;
1162 }
1163
1164 DWORD svcctl_GetCurrentGroupStateW(
1165     void)
1166 {
1167     WINE_FIXME("\n");
1168     return ERROR_CALL_NOT_IMPLEMENTED;
1169 }
1170
1171 DWORD svcctl_EnumServiceGroupW(
1172     void)
1173 {
1174     WINE_FIXME("\n");
1175     return ERROR_CALL_NOT_IMPLEMENTED;
1176 }
1177
1178 DWORD svcctl_ChangeServiceConfig2A(
1179     void)
1180 {
1181     WINE_FIXME("\n");
1182     return ERROR_CALL_NOT_IMPLEMENTED;
1183 }
1184
1185 DWORD svcctl_QueryServiceConfig2A(
1186     void)
1187 {
1188     WINE_FIXME("\n");
1189     return ERROR_CALL_NOT_IMPLEMENTED;
1190 }
1191
1192
1193 DWORD RPC_Init(void)
1194 {
1195     WCHAR transport[] = SVCCTL_TRANSPORT;
1196     WCHAR endpoint[] = SVCCTL_ENDPOINT;
1197     DWORD err;
1198
1199     if ((err = RpcServerUseProtseqEpW(transport, 0, endpoint, NULL)) != ERROR_SUCCESS)
1200     {
1201         WINE_ERR("RpcServerUseProtseq failed with error %u\n", err);
1202         return err;
1203     }
1204
1205     if ((err = RpcServerRegisterIf(svcctl_v2_0_s_ifspec, 0, 0)) != ERROR_SUCCESS)
1206     {
1207         WINE_ERR("RpcServerRegisterIf failed with error %u\n", err);
1208         return err;
1209     }
1210
1211     if ((err = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE)) != ERROR_SUCCESS)
1212     {
1213         WINE_ERR("RpcServerListen failed with error %u\n", err);
1214         return err;
1215     }
1216     return ERROR_SUCCESS;
1217 }
1218
1219 DWORD RPC_MainLoop(void)
1220 {
1221     DWORD err;
1222     HANDLE hExitEvent = __wine_make_process_system();
1223
1224     SetEvent(g_hStartedEvent);
1225
1226     WINE_TRACE("Entered main loop\n");
1227
1228     do
1229     {
1230         err = WaitForSingleObjectEx(hExitEvent, INFINITE, TRUE);
1231         WINE_TRACE("Wait returned %d\n", err);
1232     } while (err != WAIT_OBJECT_0);
1233
1234     WINE_TRACE("Object signaled - wine shutdown\n");
1235     CloseHandle(hExitEvent);
1236     return ERROR_SUCCESS;
1237 }
1238
1239 void __RPC_USER SC_RPC_HANDLE_rundown(SC_RPC_HANDLE handle)
1240 {
1241     SC_RPC_HANDLE_destroy(handle);
1242 }
1243
1244 void  __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len)
1245 {
1246     return HeapAlloc(GetProcessHeap(), 0, len);
1247 }
1248
1249 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
1250 {
1251     HeapFree(GetProcessHeap(), 0, ptr);
1252 }