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