services: svcctl_GetServiceDisplayNameW and svcctl_GetServiceKeyNameW should have...
[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("Chainging 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     if ((err = validate_service_handle(hService, SERVICE_QUERY_STATUS, &service)) != 0)
658         return err;
659
660     switch (level)
661     {
662     case SERVICE_CONFIG_DESCRIPTION:
663         {
664             SERVICE_DESCRIPTIONW *descr = (SERVICE_DESCRIPTIONW *)buffer;
665
666             service_lock_shared(service->service_entry);
667             *needed = sizeof(*descr);
668             if (service->service_entry->description)
669                 *needed += (strlenW(service->service_entry->description) + 1) * sizeof(WCHAR);
670             if (size >= *needed)
671             {
672                 if (service->service_entry->description)
673                 {
674                     /* store a buffer offset instead of a pointer */
675                     descr->lpDescription = (WCHAR *)((BYTE *)(descr + 1) - buffer);
676                     strcpyW( (WCHAR *)(descr + 1), service->service_entry->description );
677                 }
678                 else descr->lpDescription = NULL;
679             }
680             else err = ERROR_INSUFFICIENT_BUFFER;
681             service_unlock(service->service_entry);
682         }
683         break;
684
685     default:
686         WINE_FIXME("level %u not implemented\n", level);
687         err = ERROR_INVALID_LEVEL;
688         break;
689     }
690     return err;
691 }
692
693 DWORD svcctl_QueryServiceStatusEx(
694     SC_RPC_HANDLE hService,
695     SC_STATUS_TYPE InfoLevel,
696     BYTE *lpBuffer,
697     DWORD cbBufSize,
698     LPDWORD pcbBytesNeeded)
699 {
700     struct sc_service_handle *service;
701     DWORD err;
702     LPSERVICE_STATUS_PROCESS pSvcStatusData;
703
704     if ((err = validate_service_handle(hService, SERVICE_QUERY_STATUS, &service)) != 0)
705         return err;
706
707     if (InfoLevel != SC_STATUS_PROCESS_INFO)
708         return ERROR_INVALID_LEVEL;
709
710     pSvcStatusData = (LPSERVICE_STATUS_PROCESS) lpBuffer;
711     if (pSvcStatusData == NULL)
712         return ERROR_INVALID_PARAMETER;
713
714     if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS))
715     {
716         if( pcbBytesNeeded != NULL)
717             *pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);
718
719         return ERROR_INSUFFICIENT_BUFFER;
720     }
721
722     service_lock_shared(service->service_entry);
723
724     pSvcStatusData->dwServiceType = service->service_entry->status.dwServiceType;
725     pSvcStatusData->dwCurrentState = service->service_entry->status.dwCurrentState;
726     pSvcStatusData->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
727     pSvcStatusData->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
728     pSvcStatusData->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
729     pSvcStatusData->dwCheckPoint = service->service_entry->status.dwCheckPoint;
730     pSvcStatusData->dwWaitHint = service->service_entry->status.dwWaitHint;
731     pSvcStatusData->dwProcessId = service->service_entry->status.dwProcessId;
732     pSvcStatusData->dwServiceFlags = service->service_entry->status.dwServiceFlags;
733
734     service_unlock(service->service_entry);
735
736     return ERROR_SUCCESS;
737 }
738
739 /******************************************************************************
740  * service_accepts_control
741  */
742 static BOOL service_accepts_control(const struct service_entry *service, DWORD dwControl)
743 {
744     DWORD a = service->status.dwControlsAccepted;
745
746     switch (dwControl)
747     {
748     case SERVICE_CONTROL_INTERROGATE:
749         return TRUE;
750     case SERVICE_CONTROL_STOP:
751         if (a&SERVICE_ACCEPT_STOP)
752             return TRUE;
753         break;
754     case SERVICE_CONTROL_SHUTDOWN:
755         if (a&SERVICE_ACCEPT_SHUTDOWN)
756             return TRUE;
757         break;
758     case SERVICE_CONTROL_PAUSE:
759     case SERVICE_CONTROL_CONTINUE:
760         if (a&SERVICE_ACCEPT_PAUSE_CONTINUE)
761             return TRUE;
762         break;
763     case SERVICE_CONTROL_PARAMCHANGE:
764         if (a&SERVICE_ACCEPT_PARAMCHANGE)
765             return TRUE;
766         break;
767     case SERVICE_CONTROL_NETBINDADD:
768     case SERVICE_CONTROL_NETBINDREMOVE:
769     case SERVICE_CONTROL_NETBINDENABLE:
770     case SERVICE_CONTROL_NETBINDDISABLE:
771         if (a&SERVICE_ACCEPT_NETBINDCHANGE)
772             return TRUE;
773     case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
774         if (a&SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
775             return TRUE;
776         break;
777     case SERVICE_CONTROL_POWEREVENT:
778         if (a&SERVICE_ACCEPT_POWEREVENT)
779             return TRUE;
780         break;
781     case SERVICE_CONTROL_SESSIONCHANGE:
782         if (a&SERVICE_ACCEPT_SESSIONCHANGE)
783             return TRUE;
784         break;
785     }
786     return FALSE;
787 }
788
789 /******************************************************************************
790  * service_send_control
791  */
792 static BOOL service_send_control(struct service_entry *service, HANDLE pipe, DWORD dwControl, DWORD *result)
793 {
794     service_start_info *ssi;
795     DWORD len, count = 0;
796     BOOL r;
797
798     /* calculate how much space we need to send the startup info */
799     len = strlenW(service->name) + 1;
800
801     ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, data[len]));
802     ssi->cmd = WINESERV_SENDCONTROL;
803     ssi->control = dwControl;
804     ssi->total_size = FIELD_OFFSET(service_start_info, data[len]);
805     ssi->name_size = strlenW(service->name) + 1;
806     strcpyW( ssi->data, service->name );
807
808     r = WriteFile(pipe, ssi, ssi->total_size, &count, NULL);
809     if (!r || count != ssi->total_size)
810     {
811         WINE_ERR("service protocol error - failed to write pipe!\n");
812         return r;
813     }
814     r = ReadFile(pipe, result, sizeof *result, &count, NULL);
815     if (!r || count != sizeof *result)
816         WINE_ERR("service protocol error - failed to read pipe "
817             "r = %d  count = %d!\n", r, count);
818     return r;
819 }
820
821 DWORD svcctl_StartServiceW(
822     SC_RPC_HANDLE hService,
823     DWORD dwNumServiceArgs,
824     LPCWSTR *lpServiceArgVectors)
825 {
826     struct sc_service_handle *service;
827     DWORD err;
828
829     WINE_TRACE("(%p, %d, %p)\n", hService, dwNumServiceArgs, lpServiceArgVectors);
830
831     if ((err = validate_service_handle(hService, SERVICE_START, &service)) != 0)
832         return err;
833
834     err = service_start(service->service_entry, dwNumServiceArgs, lpServiceArgVectors);
835
836     return err;
837 }
838
839 DWORD svcctl_ControlService(
840     SC_RPC_HANDLE hService,
841     DWORD dwControl,
842     SERVICE_STATUS *lpServiceStatus)
843 {
844     DWORD access_required;
845     struct sc_service_handle *service;
846     DWORD err;
847     BOOL ret;
848     HANDLE control_mutex;
849     HANDLE control_pipe;
850
851     WINE_TRACE("(%p, %d, %p)\n", hService, dwControl, lpServiceStatus);
852
853     switch (dwControl)
854     {
855     case SERVICE_CONTROL_CONTINUE:
856     case SERVICE_CONTROL_NETBINDADD:
857     case SERVICE_CONTROL_NETBINDDISABLE:
858     case SERVICE_CONTROL_NETBINDENABLE:
859     case SERVICE_CONTROL_NETBINDREMOVE:
860     case SERVICE_CONTROL_PARAMCHANGE:
861     case SERVICE_CONTROL_PAUSE:
862         access_required = SERVICE_PAUSE_CONTINUE;
863         break;
864     case SERVICE_CONTROL_INTERROGATE:
865         access_required = SERVICE_INTERROGATE;
866         break;
867     case SERVICE_CONTROL_STOP:
868         access_required = SERVICE_STOP;
869         break;
870     default:
871         if (dwControl >= 128 && dwControl <= 255)
872             access_required = SERVICE_USER_DEFINED_CONTROL;
873         else
874             return ERROR_INVALID_PARAMETER;
875     }
876
877     if ((err = validate_service_handle(hService, access_required, &service)) != 0)
878         return err;
879
880     service_lock_exclusive(service->service_entry);
881
882     if (lpServiceStatus)
883     {
884         lpServiceStatus->dwServiceType = service->service_entry->status.dwServiceType;
885         lpServiceStatus->dwCurrentState = service->service_entry->status.dwCurrentState;
886         lpServiceStatus->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
887         lpServiceStatus->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
888         lpServiceStatus->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
889         lpServiceStatus->dwCheckPoint = service->service_entry->status.dwCheckPoint;
890         lpServiceStatus->dwWaitHint = service->service_entry->status.dwWaitHint;
891     }
892
893     if (!service_accepts_control(service->service_entry, dwControl))
894     {
895         service_unlock(service->service_entry);
896         return ERROR_INVALID_SERVICE_CONTROL;
897     }
898
899     switch (service->service_entry->status.dwCurrentState)
900     {
901     case SERVICE_STOPPED:
902         service_unlock(service->service_entry);
903         return ERROR_SERVICE_NOT_ACTIVE;
904     case SERVICE_START_PENDING:
905         if (dwControl==SERVICE_CONTROL_STOP)
906             break;
907         /* fall thru */
908     case SERVICE_STOP_PENDING:
909         service_unlock(service->service_entry);
910         return ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
911     }
912
913     /* prevent races by caching these variables and clearing them on
914      * stop here instead of outside the services lock */
915     control_mutex = service->service_entry->control_mutex;
916     control_pipe = service->service_entry->control_pipe;
917     if (dwControl == SERVICE_CONTROL_STOP)
918     {
919         service->service_entry->control_mutex = NULL;
920         service->service_entry->control_pipe = INVALID_HANDLE_VALUE;
921     }
922
923     service_unlock(service->service_entry);
924
925     ret = WaitForSingleObject(control_mutex, 30000);
926     if (ret == WAIT_OBJECT_0)
927     {
928         DWORD result = ERROR_SUCCESS;
929
930         ret = service_send_control(service->service_entry, control_pipe, dwControl, &result);
931
932         if (dwControl == SERVICE_CONTROL_STOP)
933         {
934             CloseHandle(control_mutex);
935             CloseHandle(control_pipe);
936         }
937         else
938             ReleaseMutex(control_mutex);
939
940         return result;
941     }
942     else
943     {
944         if (dwControl == SERVICE_CONTROL_STOP)
945         {
946             CloseHandle(control_mutex);
947             CloseHandle(control_pipe);
948         }
949         return ERROR_SERVICE_REQUEST_TIMEOUT;
950     }
951 }
952
953 DWORD svcctl_CloseServiceHandle(
954     SC_RPC_HANDLE *handle)
955 {
956     WINE_TRACE("(&%p)\n", *handle);
957
958     SC_RPC_HANDLE_destroy(*handle);
959     *handle = NULL;
960
961     return ERROR_SUCCESS;
962 }
963
964 static void SC_RPC_LOCK_destroy(SC_RPC_LOCK hLock)
965 {
966     struct sc_lock *lock = hLock;
967     scmdatabase_unlock_startup(lock->db);
968     HeapFree(GetProcessHeap(), 0, lock);
969 }
970
971 void __RPC_USER SC_RPC_LOCK_rundown(SC_RPC_LOCK hLock)
972 {
973     SC_RPC_LOCK_destroy(hLock);
974 }
975
976 DWORD svcctl_LockServiceDatabase(
977     SC_RPC_HANDLE hSCManager,
978     SC_RPC_LOCK *phLock)
979 {
980     struct sc_manager_handle *manager;
981     struct sc_lock *lock;
982     DWORD err;
983
984     WINE_TRACE("(%p, %p)\n", hSCManager, phLock);
985
986     if ((err = validate_scm_handle(hSCManager, SC_MANAGER_LOCK, &manager)) != ERROR_SUCCESS)
987         return err;
988
989     err = scmdatabase_lock_startup(manager->db);
990     if (err != ERROR_SUCCESS)
991         return err;
992
993     lock = HeapAlloc(GetProcessHeap(), 0, sizeof(struct sc_lock));
994     if (!lock)
995     {
996         scmdatabase_unlock_startup(manager->db);
997         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
998     }
999
1000     lock->db = manager->db;
1001     *phLock = lock;
1002
1003     return ERROR_SUCCESS;
1004 }
1005
1006 DWORD svcctl_UnlockServiceDatabase(
1007     SC_RPC_LOCK *phLock)
1008 {
1009     WINE_TRACE("(&%p)\n", *phLock);
1010
1011     SC_RPC_LOCK_destroy(*phLock);
1012     *phLock = NULL;
1013
1014     return ERROR_SUCCESS;
1015 }
1016
1017 DWORD svcctl_QueryServiceObjectSecurity(
1018     void)
1019 {
1020     WINE_FIXME("\n");
1021     return ERROR_CALL_NOT_IMPLEMENTED;
1022 }
1023
1024 DWORD svcctl_SetServiceObjectSecurity(
1025     void)
1026 {
1027     WINE_FIXME("\n");
1028     return ERROR_CALL_NOT_IMPLEMENTED;
1029 }
1030
1031 DWORD svcctl_QueryServiceStatus(
1032     void)
1033 {
1034     WINE_FIXME("\n");
1035     return ERROR_CALL_NOT_IMPLEMENTED;
1036 }
1037
1038
1039 DWORD svcctl_NotifyBootConfigStatus(
1040     void)
1041 {
1042     WINE_FIXME("\n");
1043     return ERROR_CALL_NOT_IMPLEMENTED;
1044 }
1045
1046 DWORD svcctl_SCSetServiceBitsW(
1047     void)
1048 {
1049     WINE_FIXME("\n");
1050     return ERROR_CALL_NOT_IMPLEMENTED;
1051 }
1052
1053
1054 DWORD svcctl_EnumDependentServicesW(
1055     void)
1056 {
1057     WINE_FIXME("\n");
1058     return ERROR_CALL_NOT_IMPLEMENTED;
1059 }
1060
1061 DWORD svcctl_EnumServicesStatusW(
1062     void)
1063 {
1064     WINE_FIXME("\n");
1065     return ERROR_CALL_NOT_IMPLEMENTED;
1066 }
1067
1068
1069 DWORD svcctl_QueryServiceLockStatusW(
1070     void)
1071 {
1072     WINE_FIXME("\n");
1073     return ERROR_CALL_NOT_IMPLEMENTED;
1074 }
1075
1076 DWORD svcctl_SCSetServiceBitsA(
1077     void)
1078 {
1079     WINE_FIXME("\n");
1080     return ERROR_CALL_NOT_IMPLEMENTED;
1081 }
1082
1083 DWORD svcctl_ChangeServiceConfigA(
1084     void)
1085 {
1086     WINE_FIXME("\n");
1087     return ERROR_CALL_NOT_IMPLEMENTED;
1088 }
1089
1090 DWORD svcctl_CreateServiceA(
1091     void)
1092 {
1093     WINE_FIXME("\n");
1094     return ERROR_CALL_NOT_IMPLEMENTED;
1095 }
1096
1097 DWORD svcctl_EnumDependentServicesA(
1098     void)
1099 {
1100     WINE_FIXME("\n");
1101     return ERROR_CALL_NOT_IMPLEMENTED;
1102 }
1103
1104 DWORD svcctl_EnumServicesStatusA(
1105     void)
1106 {
1107     WINE_FIXME("\n");
1108     return ERROR_CALL_NOT_IMPLEMENTED;
1109 }
1110
1111 DWORD svcctl_OpenSCManagerA(
1112     void)
1113 {
1114     WINE_FIXME("\n");
1115     return ERROR_CALL_NOT_IMPLEMENTED;
1116 }
1117
1118 DWORD svcctl_OpenServiceA(
1119     void)
1120 {
1121     WINE_FIXME("\n");
1122     return ERROR_CALL_NOT_IMPLEMENTED;
1123 }
1124
1125 DWORD svcctl_QueryServiceConfigA(
1126     void)
1127 {
1128     WINE_FIXME("\n");
1129     return ERROR_CALL_NOT_IMPLEMENTED;
1130 }
1131
1132 DWORD svcctl_QueryServiceLockStatusA(
1133     void)
1134 {
1135     WINE_FIXME("\n");
1136     return ERROR_CALL_NOT_IMPLEMENTED;
1137 }
1138
1139 DWORD svcctl_StartServiceA(
1140     void)
1141 {
1142     WINE_FIXME("\n");
1143     return ERROR_CALL_NOT_IMPLEMENTED;
1144 }
1145
1146 DWORD svcctl_GetServiceDisplayNameA(
1147     void)
1148 {
1149     WINE_FIXME("\n");
1150     return ERROR_CALL_NOT_IMPLEMENTED;
1151 }
1152
1153 DWORD svcctl_GetServiceKeyNameA(
1154     void)
1155 {
1156     WINE_FIXME("\n");
1157     return ERROR_CALL_NOT_IMPLEMENTED;
1158 }
1159
1160 DWORD svcctl_GetCurrentGroupStateW(
1161     void)
1162 {
1163     WINE_FIXME("\n");
1164     return ERROR_CALL_NOT_IMPLEMENTED;
1165 }
1166
1167 DWORD svcctl_EnumServiceGroupW(
1168     void)
1169 {
1170     WINE_FIXME("\n");
1171     return ERROR_CALL_NOT_IMPLEMENTED;
1172 }
1173
1174 DWORD svcctl_ChangeServiceConfig2A(
1175     void)
1176 {
1177     WINE_FIXME("\n");
1178     return ERROR_CALL_NOT_IMPLEMENTED;
1179 }
1180
1181 DWORD svcctl_QueryServiceConfig2A(
1182     void)
1183 {
1184     WINE_FIXME("\n");
1185     return ERROR_CALL_NOT_IMPLEMENTED;
1186 }
1187
1188
1189 DWORD RPC_Init(void)
1190 {
1191     WCHAR transport[] = SVCCTL_TRANSPORT;
1192     WCHAR endpoint[] = SVCCTL_ENDPOINT;
1193     DWORD err;
1194
1195     if ((err = RpcServerUseProtseqEpW(transport, 0, endpoint, NULL)) != ERROR_SUCCESS)
1196     {
1197         WINE_ERR("RpcServerUseProtseq failed with error %u\n", err);
1198         return err;
1199     }
1200
1201     if ((err = RpcServerRegisterIf(svcctl_v2_0_s_ifspec, 0, 0)) != ERROR_SUCCESS)
1202     {
1203         WINE_ERR("RpcServerRegisterIf failed with error %u\n", err);
1204         return err;
1205     }
1206
1207     if ((err = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE)) != ERROR_SUCCESS)
1208     {
1209         WINE_ERR("RpcServerListen failed with error %u\n", err);
1210         return err;
1211     }
1212     return ERROR_SUCCESS;
1213 }
1214
1215 DWORD RPC_MainLoop(void)
1216 {
1217     DWORD err;
1218     HANDLE hExitEvent = __wine_make_process_system();
1219
1220     SetEvent(g_hStartedEvent);
1221
1222     WINE_TRACE("Entered main loop\n");
1223
1224     do
1225     {
1226         err = WaitForSingleObjectEx(hExitEvent, INFINITE, TRUE);
1227         WINE_TRACE("Wait returned %d\n", err);
1228     } while (err != WAIT_OBJECT_0);
1229
1230     WINE_TRACE("Object signaled - wine shutdown\n");
1231     CloseHandle(hExitEvent);
1232     return ERROR_SUCCESS;
1233 }
1234
1235 void __RPC_USER SC_RPC_HANDLE_rundown(SC_RPC_HANDLE handle)
1236 {
1237     SC_RPC_HANDLE_destroy(handle);
1238 }
1239
1240 void  __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len)
1241 {
1242     return HeapAlloc(GetProcessHeap(), 0, len);
1243 }
1244
1245 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
1246 {
1247     HeapFree(GetProcessHeap(), 0, ptr);
1248 }