Release 1.5.29.
[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 static HANDLE timeout_queue_event;
88 static CRITICAL_SECTION timeout_queue_cs;
89 static CRITICAL_SECTION_DEBUG timeout_queue_cs_debug =
90 {
91     0, 0, &timeout_queue_cs,
92     { &timeout_queue_cs_debug.ProcessLocksList, &timeout_queue_cs_debug.ProcessLocksList },
93     0, 0, { (DWORD_PTR)(__FILE__ ": timeout_queue_cs") }
94 };
95 static CRITICAL_SECTION timeout_queue_cs = { &timeout_queue_cs_debug, -1, 0, 0, 0, 0 };
96 static struct list timeout_queue = LIST_INIT(timeout_queue);
97 struct timeout_queue_elem
98 {
99     struct list entry;
100
101     FILETIME time;
102     void (*func)(struct service_entry*);
103     struct service_entry *service_entry;
104 };
105
106 static void run_after_timeout(void (*func)(struct service_entry*), struct service_entry *service, DWORD timeout)
107 {
108     struct timeout_queue_elem *elem = HeapAlloc(GetProcessHeap(), 0, sizeof(struct timeout_queue_elem));
109     ULARGE_INTEGER time;
110
111     if(!elem) {
112         func(service);
113         return;
114     }
115
116     service->ref_count++;
117     elem->func = func;
118     elem->service_entry = service;
119
120     GetSystemTimeAsFileTime(&elem->time);
121     time.u.LowPart = elem->time.dwLowDateTime;
122     time.u.HighPart = elem->time.dwHighDateTime;
123     time.QuadPart += timeout*10000000;
124     elem->time.dwLowDateTime = time.u.LowPart;
125     elem->time.dwHighDateTime = time.u.HighPart;
126
127     EnterCriticalSection(&timeout_queue_cs);
128     list_add_head(&timeout_queue, &elem->entry);
129     LeaveCriticalSection(&timeout_queue_cs);
130
131     SetEvent(timeout_queue_event);
132 }
133
134 static void free_service_strings(struct service_entry *old, struct service_entry *new)
135 {
136     QUERY_SERVICE_CONFIGW *old_cfg = &old->config;
137     QUERY_SERVICE_CONFIGW *new_cfg = &new->config;
138
139     if (old_cfg->lpBinaryPathName != new_cfg->lpBinaryPathName)
140         HeapFree(GetProcessHeap(), 0, old_cfg->lpBinaryPathName);
141
142     if (old_cfg->lpLoadOrderGroup != new_cfg->lpLoadOrderGroup)
143         HeapFree(GetProcessHeap(), 0, old_cfg->lpLoadOrderGroup);
144
145     if (old_cfg->lpServiceStartName != new_cfg->lpServiceStartName)
146         HeapFree(GetProcessHeap(), 0, old_cfg->lpServiceStartName);
147
148     if (old_cfg->lpDisplayName != new_cfg->lpDisplayName)
149         HeapFree(GetProcessHeap(), 0, old_cfg->lpDisplayName);
150
151     if (old->dependOnServices != new->dependOnServices)
152         HeapFree(GetProcessHeap(), 0, old->dependOnServices);
153
154     if (old->dependOnGroups != new->dependOnGroups)
155         HeapFree(GetProcessHeap(), 0, old->dependOnGroups);
156 }
157
158 /* Check if the given handle is of the required type and allows the requested access. */
159 static DWORD validate_context_handle(SC_RPC_HANDLE handle, DWORD type, DWORD needed_access, struct sc_handle **out_hdr)
160 {
161     struct sc_handle *hdr = handle;
162
163     if (type != SC_HTYPE_DONT_CARE && hdr->type != type)
164     {
165         WINE_ERR("Handle is of an invalid type (%d, %d)\n", hdr->type, type);
166         return ERROR_INVALID_HANDLE;
167     }
168
169     if ((needed_access & hdr->access) != needed_access)
170     {
171         WINE_ERR("Access denied - handle created with access %x, needed %x\n", hdr->access, needed_access);
172         return ERROR_ACCESS_DENIED;
173     }
174
175     *out_hdr = hdr;
176     return ERROR_SUCCESS;
177 }
178
179 static DWORD validate_scm_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_manager_handle **manager)
180 {
181     struct sc_handle *hdr;
182     DWORD err = validate_context_handle(handle, SC_HTYPE_MANAGER, needed_access, &hdr);
183     if (err == ERROR_SUCCESS)
184         *manager = (struct sc_manager_handle *)hdr;
185     return err;
186 }
187
188 static DWORD validate_service_handle(SC_RPC_HANDLE handle, DWORD needed_access, struct sc_service_handle **service)
189 {
190     struct sc_handle *hdr;
191     DWORD err = validate_context_handle(handle, SC_HTYPE_SERVICE, needed_access, &hdr);
192     if (err == ERROR_SUCCESS)
193         *service = (struct sc_service_handle *)hdr;
194     return err;
195 }
196
197 DWORD __cdecl svcctl_OpenSCManagerW(
198     MACHINE_HANDLEW MachineName, /* Note: this parameter is ignored */
199     LPCWSTR DatabaseName,
200     DWORD dwAccessMask,
201     SC_RPC_HANDLE *handle)
202 {
203     struct sc_manager_handle *manager;
204
205     WINE_TRACE("(%s, %s, %x)\n", wine_dbgstr_w(MachineName), wine_dbgstr_w(DatabaseName), dwAccessMask);
206
207     if (DatabaseName != NULL && DatabaseName[0])
208     {
209         if (strcmpW(DatabaseName, SERVICES_FAILED_DATABASEW) == 0)
210             return ERROR_DATABASE_DOES_NOT_EXIST;
211         if (strcmpW(DatabaseName, SERVICES_ACTIVE_DATABASEW) != 0)
212             return ERROR_INVALID_NAME;
213     }
214
215     if (!(manager = HeapAlloc(GetProcessHeap(), 0, sizeof(*manager))))
216         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
217
218     manager->hdr.type = SC_HTYPE_MANAGER;
219
220     if (dwAccessMask & MAXIMUM_ALLOWED)
221         dwAccessMask |= SC_MANAGER_ALL_ACCESS;
222     manager->hdr.access = dwAccessMask;
223     RtlMapGenericMask(&manager->hdr.access, &g_scm_generic);
224     manager->db = active_database;
225     *handle = &manager->hdr;
226
227     return ERROR_SUCCESS;
228 }
229
230 static void SC_RPC_HANDLE_destroy(SC_RPC_HANDLE handle)
231 {
232     struct sc_handle *hdr = handle;
233     switch (hdr->type)
234     {
235         case SC_HTYPE_MANAGER:
236         {
237             struct sc_manager_handle *manager = (struct sc_manager_handle *)hdr;
238             HeapFree(GetProcessHeap(), 0, manager);
239             break;
240         }
241         case SC_HTYPE_SERVICE:
242         {
243             struct sc_service_handle *service = (struct sc_service_handle *)hdr;
244             release_service(service->service_entry);
245             HeapFree(GetProcessHeap(), 0, service);
246             break;
247         }
248         default:
249             WINE_ERR("invalid handle type %d\n", hdr->type);
250             RpcRaiseException(ERROR_INVALID_HANDLE);
251     }
252 }
253
254 DWORD __cdecl svcctl_GetServiceDisplayNameW(
255     SC_RPC_HANDLE hSCManager,
256     LPCWSTR lpServiceName,
257     WCHAR *lpBuffer,
258     DWORD *cchBufSize)
259 {
260     struct sc_manager_handle *manager;
261     struct service_entry *entry;
262     DWORD err;
263
264     WINE_TRACE("(%s, %d)\n", wine_dbgstr_w(lpServiceName), *cchBufSize);
265
266     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
267         return err;
268
269     scmdatabase_lock_shared(manager->db);
270
271     entry = scmdatabase_find_service(manager->db, lpServiceName);
272     if (entry != NULL)
273     {
274         LPCWSTR name;
275         int len;
276         service_lock_shared(entry);
277         name = get_display_name(entry);
278         len = strlenW(name);
279         if (len <= *cchBufSize)
280         {
281             err = ERROR_SUCCESS;
282             memcpy(lpBuffer, name, (len + 1)*sizeof(*name));
283         }
284         else
285             err = ERROR_INSUFFICIENT_BUFFER;
286         *cchBufSize = len;
287         service_unlock(entry);
288     }
289     else
290         err = ERROR_SERVICE_DOES_NOT_EXIST;
291
292     scmdatabase_unlock(manager->db);
293
294     if (err != ERROR_SUCCESS)
295         lpBuffer[0] = 0;
296
297     return err;
298 }
299
300 DWORD __cdecl svcctl_GetServiceKeyNameW(
301     SC_RPC_HANDLE hSCManager,
302     LPCWSTR lpServiceDisplayName,
303     WCHAR *lpBuffer,
304     DWORD *cchBufSize)
305 {
306     struct service_entry *entry;
307     struct sc_manager_handle *manager;
308     DWORD err;
309
310     WINE_TRACE("(%s, %d)\n", wine_dbgstr_w(lpServiceDisplayName), *cchBufSize);
311
312     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
313         return err;
314
315     scmdatabase_lock_shared(manager->db);
316
317     entry = scmdatabase_find_service_by_displayname(manager->db, lpServiceDisplayName);
318     if (entry != NULL)
319     {
320         int len;
321         service_lock_shared(entry);
322         len = strlenW(entry->name);
323         if (len <= *cchBufSize)
324         {
325             err = ERROR_SUCCESS;
326             memcpy(lpBuffer, entry->name, (len + 1)*sizeof(*entry->name));
327         }
328         else
329             err = ERROR_INSUFFICIENT_BUFFER;
330         *cchBufSize = len;
331         service_unlock(entry);
332     }
333     else
334         err = ERROR_SERVICE_DOES_NOT_EXIST;
335
336     scmdatabase_unlock(manager->db);
337
338     if (err != ERROR_SUCCESS)
339         lpBuffer[0] = 0;
340
341     return err;
342 }
343
344 static DWORD create_handle_for_service(struct service_entry *entry, DWORD dwDesiredAccess, SC_RPC_HANDLE *phService)
345 {
346     struct sc_service_handle *service;
347
348     if (!(service = HeapAlloc(GetProcessHeap(), 0, sizeof(*service))))
349     {
350         release_service(entry);
351         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
352     }
353
354     service->hdr.type = SC_HTYPE_SERVICE;
355     service->hdr.access = dwDesiredAccess;
356     RtlMapGenericMask(&service->hdr.access, &g_svc_generic);
357     service->service_entry = entry;
358     if (dwDesiredAccess & MAXIMUM_ALLOWED)
359         dwDesiredAccess |= SERVICE_ALL_ACCESS;
360
361     *phService = &service->hdr;
362     return ERROR_SUCCESS;
363 }
364
365 DWORD __cdecl svcctl_OpenServiceW(
366     SC_RPC_HANDLE hSCManager,
367     LPCWSTR lpServiceName,
368     DWORD dwDesiredAccess,
369     SC_RPC_HANDLE *phService)
370 {
371     struct sc_manager_handle *manager;
372     struct service_entry *entry;
373     DWORD err;
374
375     WINE_TRACE("(%s, 0x%x)\n", wine_dbgstr_w(lpServiceName), dwDesiredAccess);
376
377     if ((err = validate_scm_handle(hSCManager, 0, &manager)) != ERROR_SUCCESS)
378         return err;
379     if (!validate_service_name(lpServiceName))
380         return ERROR_INVALID_NAME;
381
382     scmdatabase_lock_shared(manager->db);
383     entry = scmdatabase_find_service(manager->db, lpServiceName);
384     if (entry != NULL)
385         InterlockedIncrement(&entry->ref_count);
386     scmdatabase_unlock(manager->db);
387
388     if (entry == NULL)
389         return ERROR_SERVICE_DOES_NOT_EXIST;
390
391     return create_handle_for_service(entry, dwDesiredAccess, phService);
392 }
393
394 static DWORD parse_dependencies(const WCHAR *dependencies, struct service_entry *entry)
395 {
396     WCHAR *services = NULL, *groups, *s;
397     DWORD len, len_services = 0, len_groups = 0;
398     const WCHAR *ptr = dependencies;
399
400     if (!dependencies || !dependencies[0])
401     {
402         entry->dependOnServices = NULL;
403         entry->dependOnGroups = NULL;
404         return ERROR_SUCCESS;
405     }
406
407     while (*ptr)
408     {
409         len = strlenW(ptr) + 1;
410         if (ptr[0] == '+' && ptr[1])
411             len_groups += len - 1;
412         else
413             len_services += len;
414         ptr += len;
415     }
416     if (!len_services) entry->dependOnServices = NULL;
417     else
418     {
419         services = HeapAlloc(GetProcessHeap(), 0, (len_services + 1) * sizeof(WCHAR));
420         if (!services)
421             return ERROR_OUTOFMEMORY;
422
423         s = services;
424         ptr = dependencies;
425         while (*ptr)
426         {
427             len = strlenW(ptr) + 1;
428             if (*ptr != '+')
429             {
430                 strcpyW(s, ptr);
431                 s += len;
432             }
433             ptr += len;
434         }
435         *s = 0;
436         entry->dependOnServices = services;
437     }
438     if (!len_groups) entry->dependOnGroups = NULL;
439     else
440     {
441         groups = HeapAlloc(GetProcessHeap(), 0, (len_groups + 1) * sizeof(WCHAR));
442         if (!groups)
443         {
444             HeapFree(GetProcessHeap(), 0, services);
445             return ERROR_OUTOFMEMORY;
446         }
447         s = groups;
448         ptr = dependencies;
449         while (*ptr)
450         {
451             len = strlenW(ptr) + 1;
452             if (ptr[0] == '+' && ptr[1])
453             {
454                 strcpyW(s, ptr + 1);
455                 s += len - 1;
456             }
457             ptr += len;
458         }
459         *s = 0;
460         entry->dependOnGroups = groups;
461     }
462
463     return ERROR_SUCCESS;
464 }
465
466 DWORD __cdecl svcctl_CreateServiceW(
467     SC_RPC_HANDLE hSCManager,
468     LPCWSTR lpServiceName,
469     LPCWSTR lpDisplayName,
470     DWORD dwDesiredAccess,
471     DWORD dwServiceType,
472     DWORD dwStartType,
473     DWORD dwErrorControl,
474     LPCWSTR lpBinaryPathName,
475     LPCWSTR lpLoadOrderGroup,
476     DWORD *lpdwTagId,
477     const BYTE *lpDependencies,
478     DWORD dwDependenciesSize,
479     LPCWSTR lpServiceStartName,
480     const BYTE *lpPassword,
481     DWORD dwPasswordSize,
482     SC_RPC_HANDLE *phService)
483 {
484     struct sc_manager_handle *manager;
485     struct service_entry *entry;
486     DWORD err;
487
488     WINE_TRACE("(%s, %s, 0x%x, %s)\n", wine_dbgstr_w(lpServiceName), wine_dbgstr_w(lpDisplayName), dwDesiredAccess, wine_dbgstr_w(lpBinaryPathName));
489
490     if ((err = validate_scm_handle(hSCManager, SC_MANAGER_CREATE_SERVICE, &manager)) != ERROR_SUCCESS)
491         return err;
492
493     if (!validate_service_name(lpServiceName))
494         return ERROR_INVALID_NAME;
495     if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize) || !lpServiceName[0] || !lpBinaryPathName[0])
496         return ERROR_INVALID_PARAMETER;
497
498     if (lpPassword)
499         WINE_FIXME("Don't know how to add a password\n");   /* I always get ERROR_GEN_FAILURE */
500
501     err = service_create(lpServiceName, &entry);
502     if (err != ERROR_SUCCESS)
503         return err;
504
505     err = parse_dependencies((LPCWSTR)lpDependencies, entry);
506     if (err != ERROR_SUCCESS) {
507         free_service_entry(entry);
508         return err;
509     }
510
511     entry->ref_count = 1;
512     entry->config.dwServiceType = entry->status.dwServiceType = dwServiceType;
513     entry->config.dwStartType = dwStartType;
514     entry->config.dwErrorControl = dwErrorControl;
515     entry->config.lpBinaryPathName = strdupW(lpBinaryPathName);
516     entry->config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
517     entry->config.lpServiceStartName = strdupW(lpServiceStartName);
518     entry->config.lpDisplayName = strdupW(lpDisplayName);
519
520     if (lpdwTagId)      /* TODO: In most situations a non-NULL TagId will generate an ERROR_INVALID_PARAMETER. */
521         entry->config.dwTagId = *lpdwTagId;
522     else
523         entry->config.dwTagId = 0;
524
525     /* other fields NULL*/
526
527     if (!validate_service_config(entry))
528     {
529         WINE_ERR("Invalid data while trying to create service\n");
530         free_service_entry(entry);
531         return ERROR_INVALID_PARAMETER;
532     }
533
534     scmdatabase_lock_exclusive(manager->db);
535
536     if (scmdatabase_find_service(manager->db, lpServiceName))
537     {
538         scmdatabase_unlock(manager->db);
539         free_service_entry(entry);
540         return ERROR_SERVICE_EXISTS;
541     }
542
543     if (scmdatabase_find_service_by_displayname(manager->db, get_display_name(entry)))
544     {
545         scmdatabase_unlock(manager->db);
546         free_service_entry(entry);
547         return ERROR_DUPLICATE_SERVICE_NAME;
548     }
549
550     err = scmdatabase_add_service(manager->db, entry);
551     if (err != ERROR_SUCCESS)
552     {
553         scmdatabase_unlock(manager->db);
554         free_service_entry(entry);
555         return err;
556     }
557     scmdatabase_unlock(manager->db);
558
559     return create_handle_for_service(entry, dwDesiredAccess, phService);
560 }
561
562 DWORD __cdecl svcctl_DeleteService(
563     SC_RPC_HANDLE hService)
564 {
565     struct sc_service_handle *service;
566     DWORD err;
567
568     if ((err = validate_service_handle(hService, DELETE, &service)) != ERROR_SUCCESS)
569         return err;
570
571     scmdatabase_lock_exclusive(service->service_entry->db);
572     service_lock_exclusive(service->service_entry);
573
574     if (!is_marked_for_delete(service->service_entry))
575         err = scmdatabase_remove_service(service->service_entry->db, service->service_entry);
576     else
577         err = ERROR_SERVICE_MARKED_FOR_DELETE;
578
579     service_unlock(service->service_entry);
580     scmdatabase_unlock(service->service_entry->db);
581
582     return err;
583 }
584
585 DWORD __cdecl svcctl_QueryServiceConfigW(
586         SC_RPC_HANDLE hService,
587         QUERY_SERVICE_CONFIGW *config)
588 {
589     struct sc_service_handle *service;
590     DWORD err;
591
592     WINE_TRACE("(%p)\n", config);
593
594     if ((err = validate_service_handle(hService, SERVICE_QUERY_CONFIG, &service)) != 0)
595         return err;
596
597     service_lock_shared(service->service_entry);
598     config->dwServiceType = service->service_entry->config.dwServiceType;
599     config->dwStartType = service->service_entry->config.dwStartType;
600     config->dwErrorControl = service->service_entry->config.dwErrorControl;
601     config->lpBinaryPathName = strdupW(service->service_entry->config.lpBinaryPathName);
602     config->lpLoadOrderGroup = strdupW(service->service_entry->config.lpLoadOrderGroup);
603     config->dwTagId = service->service_entry->config.dwTagId;
604     config->lpDependencies = NULL; /* TODO */
605     config->lpServiceStartName = strdupW(service->service_entry->config.lpServiceStartName);
606     config->lpDisplayName = strdupW(service->service_entry->config.lpDisplayName);
607     service_unlock(service->service_entry);
608
609     return ERROR_SUCCESS;
610 }
611
612 DWORD __cdecl svcctl_ChangeServiceConfigW(
613         SC_RPC_HANDLE hService,
614         DWORD dwServiceType,
615         DWORD dwStartType,
616         DWORD dwErrorControl,
617         LPCWSTR lpBinaryPathName,
618         LPCWSTR lpLoadOrderGroup,
619         DWORD *lpdwTagId,
620         const BYTE *lpDependencies,
621         DWORD dwDependenciesSize,
622         LPCWSTR lpServiceStartName,
623         const BYTE *lpPassword,
624         DWORD dwPasswordSize,
625         LPCWSTR lpDisplayName)
626 {
627     struct service_entry new_entry, *entry;
628     struct sc_service_handle *service;
629     DWORD err;
630
631     WINE_TRACE("\n");
632
633     if ((err = validate_service_handle(hService, SERVICE_CHANGE_CONFIG, &service)) != 0)
634         return err;
635
636     if (!check_multisz((LPCWSTR)lpDependencies, dwDependenciesSize))
637         return ERROR_INVALID_PARAMETER;
638
639     /* first check if the new configuration is correct */
640     service_lock_exclusive(service->service_entry);
641
642     if (is_marked_for_delete(service->service_entry))
643     {
644         service_unlock(service->service_entry);
645         return ERROR_SERVICE_MARKED_FOR_DELETE;
646     }
647
648     if (lpDisplayName != NULL &&
649         (entry = scmdatabase_find_service_by_displayname(service->service_entry->db, lpDisplayName)) &&
650         (entry != service->service_entry))
651     {
652         service_unlock(service->service_entry);
653         return ERROR_DUPLICATE_SERVICE_NAME;
654     }
655
656     new_entry = *service->service_entry;
657
658     if (dwServiceType != SERVICE_NO_CHANGE)
659         new_entry.config.dwServiceType = dwServiceType;
660
661     if (dwStartType != SERVICE_NO_CHANGE)
662         new_entry.config.dwStartType = dwStartType;
663
664     if (dwErrorControl != SERVICE_NO_CHANGE)
665         new_entry.config.dwErrorControl = dwErrorControl;
666
667     if (lpBinaryPathName != NULL)
668         new_entry.config.lpBinaryPathName = (LPWSTR)lpBinaryPathName;
669
670     if (lpLoadOrderGroup != NULL)
671         new_entry.config.lpLoadOrderGroup = (LPWSTR)lpLoadOrderGroup;
672
673     if (lpdwTagId != NULL)
674         WINE_FIXME("Changing tag id not supported\n");
675
676     if (lpServiceStartName != NULL)
677         new_entry.config.lpServiceStartName = (LPWSTR)lpServiceStartName;
678
679     if (lpPassword != NULL)
680         WINE_FIXME("Setting password not supported\n");
681
682     if (lpDisplayName != NULL)
683         new_entry.config.lpDisplayName = (LPWSTR)lpDisplayName;
684
685     err = parse_dependencies((LPCWSTR)lpDependencies, &new_entry);
686     if (err != ERROR_SUCCESS)
687     {
688         service_unlock(service->service_entry);
689         return err;
690     }
691
692     if (!validate_service_config(&new_entry))
693     {
694         WINE_ERR("The configuration after the change wouldn't be valid\n");
695         service_unlock(service->service_entry);
696         return ERROR_INVALID_PARAMETER;
697     }
698
699     /* configuration OK. The strings needs to be duplicated */
700     if (lpBinaryPathName != NULL)
701         new_entry.config.lpBinaryPathName = strdupW(lpBinaryPathName);
702
703     if (lpLoadOrderGroup != NULL)
704         new_entry.config.lpLoadOrderGroup = strdupW(lpLoadOrderGroup);
705
706     if (lpServiceStartName != NULL)
707         new_entry.config.lpServiceStartName = strdupW(lpServiceStartName);
708
709     if (lpDisplayName != NULL)
710         new_entry.config.lpDisplayName = strdupW(lpDisplayName);
711
712     /* try to save to Registry, commit or rollback depending on success */
713     err = save_service_config(&new_entry);
714     if (ERROR_SUCCESS == err)
715     {
716         free_service_strings(service->service_entry, &new_entry);
717         *service->service_entry = new_entry;
718     }
719     else free_service_strings(&new_entry, service->service_entry);
720     service_unlock(service->service_entry);
721
722     return err;
723 }
724
725 DWORD __cdecl svcctl_SetServiceStatus(
726     SC_RPC_HANDLE hServiceStatus,
727     LPSERVICE_STATUS lpServiceStatus)
728 {
729     struct sc_service_handle *service;
730     DWORD err;
731
732     WINE_TRACE("(%p, %p)\n", hServiceStatus, lpServiceStatus);
733
734     if ((err = validate_service_handle(hServiceStatus, SERVICE_SET_STATUS, &service)) != 0)
735         return err;
736
737     service_lock_exclusive(service->service_entry);
738     /* FIXME: be a bit more discriminant about what parts of the status we set
739      * and check that fields are valid */
740     service->service_entry->status.dwServiceType = lpServiceStatus->dwServiceType;
741     service->service_entry->status.dwCurrentState = lpServiceStatus->dwCurrentState;
742     service->service_entry->status.dwControlsAccepted = lpServiceStatus->dwControlsAccepted;
743     service->service_entry->status.dwWin32ExitCode = lpServiceStatus->dwWin32ExitCode;
744     service->service_entry->status.dwServiceSpecificExitCode = lpServiceStatus->dwServiceSpecificExitCode;
745     service->service_entry->status.dwCheckPoint = lpServiceStatus->dwCheckPoint;
746     service->service_entry->status.dwWaitHint = lpServiceStatus->dwWaitHint;
747     service_unlock(service->service_entry);
748
749     if (lpServiceStatus->dwCurrentState == SERVICE_STOPPED)
750         run_after_timeout(service_terminate, service->service_entry, service_kill_timeout);
751     else if (service->service_entry->status_changed_event)
752         SetEvent(service->service_entry->status_changed_event);
753
754     return ERROR_SUCCESS;
755 }
756
757 DWORD __cdecl svcctl_ChangeServiceConfig2W( SC_RPC_HANDLE hService, DWORD level, SERVICE_CONFIG2W *config )
758 {
759     struct sc_service_handle *service;
760     DWORD err;
761
762     if ((err = validate_service_handle(hService, SERVICE_CHANGE_CONFIG, &service)) != 0)
763         return err;
764
765     switch (level)
766     {
767     case SERVICE_CONFIG_DESCRIPTION:
768         {
769             WCHAR *descr = NULL;
770
771             if (config->descr.lpDescription[0])
772             {
773                 if (!(descr = strdupW( config->descr.lpDescription )))
774                     return ERROR_NOT_ENOUGH_MEMORY;
775             }
776
777             WINE_TRACE( "changing service %p descr to %s\n", service, wine_dbgstr_w(descr) );
778             service_lock_exclusive( service->service_entry );
779             HeapFree( GetProcessHeap(), 0, service->service_entry->description );
780             service->service_entry->description = descr;
781             save_service_config( service->service_entry );
782             service_unlock( service->service_entry );
783         }
784         break;
785     case SERVICE_CONFIG_FAILURE_ACTIONS:
786         WINE_FIXME( "SERVICE_CONFIG_FAILURE_ACTIONS not implemented: period %u msg %s cmd %s\n",
787                     config->actions.dwResetPeriod,
788                     wine_dbgstr_w(config->actions.lpRebootMsg),
789                     wine_dbgstr_w(config->actions.lpCommand) );
790         break;
791     case SERVICE_CONFIG_PRESHUTDOWN_INFO:
792         WINE_TRACE( "changing service %p preshutdown timeout to %d\n",
793                 service, config->preshutdown.dwPreshutdownTimeout );
794         service_lock_exclusive( service->service_entry );
795         service->service_entry->preshutdown_timeout = config->preshutdown.dwPreshutdownTimeout;
796         save_service_config( service->service_entry );
797         service_unlock( service->service_entry );
798         break;
799     default:
800         WINE_FIXME("level %u not implemented\n", level);
801         err = ERROR_INVALID_LEVEL;
802         break;
803     }
804     return err;
805 }
806
807 DWORD __cdecl svcctl_QueryServiceConfig2W( SC_RPC_HANDLE hService, DWORD level,
808                                            BYTE *buffer, DWORD size, LPDWORD needed )
809 {
810     struct sc_service_handle *service;
811     DWORD err;
812
813     memset(buffer, 0, size);
814
815     if ((err = validate_service_handle(hService, SERVICE_QUERY_STATUS, &service)) != 0)
816         return err;
817
818     switch (level)
819     {
820     case SERVICE_CONFIG_DESCRIPTION:
821         {
822             SERVICE_DESCRIPTIONW *descr = (SERVICE_DESCRIPTIONW *)buffer;
823
824             service_lock_shared(service->service_entry);
825             *needed = sizeof(*descr);
826             if (service->service_entry->description)
827                 *needed += (strlenW(service->service_entry->description) + 1) * sizeof(WCHAR);
828             if (size >= *needed)
829             {
830                 if (service->service_entry->description)
831                 {
832                     /* store a buffer offset instead of a pointer */
833                     descr->lpDescription = (WCHAR *)((BYTE *)(descr + 1) - buffer);
834                     strcpyW( (WCHAR *)(descr + 1), service->service_entry->description );
835                 }
836                 else descr->lpDescription = NULL;
837             }
838             else err = ERROR_INSUFFICIENT_BUFFER;
839             service_unlock(service->service_entry);
840         }
841         break;
842
843     case SERVICE_CONFIG_PRESHUTDOWN_INFO:
844         service_lock_shared(service->service_entry);
845
846         *needed = sizeof(SERVICE_PRESHUTDOWN_INFO);
847         if (size >= *needed)
848             ((LPSERVICE_PRESHUTDOWN_INFO)buffer)->dwPreshutdownTimeout =
849                 service->service_entry->preshutdown_timeout;
850         else err = ERROR_INSUFFICIENT_BUFFER;
851
852         service_unlock(service->service_entry);
853         break;
854
855     default:
856         WINE_FIXME("level %u not implemented\n", level);
857         err = ERROR_INVALID_LEVEL;
858         break;
859     }
860     return err;
861 }
862
863 DWORD __cdecl svcctl_QueryServiceStatusEx(
864     SC_RPC_HANDLE hService,
865     SC_STATUS_TYPE InfoLevel,
866     BYTE *lpBuffer,
867     DWORD cbBufSize,
868     LPDWORD pcbBytesNeeded)
869 {
870     struct sc_service_handle *service;
871     DWORD err;
872     LPSERVICE_STATUS_PROCESS pSvcStatusData;
873
874     memset(lpBuffer, 0, cbBufSize);
875
876     if ((err = validate_service_handle(hService, SERVICE_QUERY_STATUS, &service)) != 0)
877         return err;
878
879     if (InfoLevel != SC_STATUS_PROCESS_INFO)
880         return ERROR_INVALID_LEVEL;
881
882     pSvcStatusData = (LPSERVICE_STATUS_PROCESS) lpBuffer;
883     if (pSvcStatusData == NULL)
884         return ERROR_INVALID_PARAMETER;
885
886     if (cbBufSize < sizeof(SERVICE_STATUS_PROCESS))
887     {
888         if( pcbBytesNeeded != NULL)
889             *pcbBytesNeeded = sizeof(SERVICE_STATUS_PROCESS);
890
891         return ERROR_INSUFFICIENT_BUFFER;
892     }
893
894     service_lock_shared(service->service_entry);
895
896     pSvcStatusData->dwServiceType = service->service_entry->status.dwServiceType;
897     pSvcStatusData->dwCurrentState = service->service_entry->status.dwCurrentState;
898     pSvcStatusData->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
899     pSvcStatusData->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
900     pSvcStatusData->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
901     pSvcStatusData->dwCheckPoint = service->service_entry->status.dwCheckPoint;
902     pSvcStatusData->dwWaitHint = service->service_entry->status.dwWaitHint;
903     pSvcStatusData->dwProcessId = service->service_entry->status.dwProcessId;
904     pSvcStatusData->dwServiceFlags = service->service_entry->status.dwServiceFlags;
905
906     service_unlock(service->service_entry);
907
908     return ERROR_SUCCESS;
909 }
910
911 /******************************************************************************
912  * service_accepts_control
913  */
914 static BOOL service_accepts_control(const struct service_entry *service, DWORD dwControl)
915 {
916     DWORD a = service->status.dwControlsAccepted;
917
918     switch (dwControl)
919     {
920     case SERVICE_CONTROL_INTERROGATE:
921         return TRUE;
922     case SERVICE_CONTROL_STOP:
923         if (a&SERVICE_ACCEPT_STOP)
924             return TRUE;
925         break;
926     case SERVICE_CONTROL_SHUTDOWN:
927         if (a&SERVICE_ACCEPT_SHUTDOWN)
928             return TRUE;
929         break;
930     case SERVICE_CONTROL_PAUSE:
931     case SERVICE_CONTROL_CONTINUE:
932         if (a&SERVICE_ACCEPT_PAUSE_CONTINUE)
933             return TRUE;
934         break;
935     case SERVICE_CONTROL_PARAMCHANGE:
936         if (a&SERVICE_ACCEPT_PARAMCHANGE)
937             return TRUE;
938         break;
939     case SERVICE_CONTROL_NETBINDADD:
940     case SERVICE_CONTROL_NETBINDREMOVE:
941     case SERVICE_CONTROL_NETBINDENABLE:
942     case SERVICE_CONTROL_NETBINDDISABLE:
943         if (a&SERVICE_ACCEPT_NETBINDCHANGE)
944             return TRUE;
945     case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
946         if (a&SERVICE_ACCEPT_HARDWAREPROFILECHANGE)
947             return TRUE;
948         break;
949     case SERVICE_CONTROL_POWEREVENT:
950         if (a&SERVICE_ACCEPT_POWEREVENT)
951             return TRUE;
952         break;
953     case SERVICE_CONTROL_SESSIONCHANGE:
954         if (a&SERVICE_ACCEPT_SESSIONCHANGE)
955             return TRUE;
956         break;
957     }
958     return FALSE;
959 }
960
961 /******************************************************************************
962  * service_send_command
963  */
964 BOOL service_send_command( struct service_entry *service, HANDLE pipe,
965                            const void *data, DWORD size, DWORD *result )
966 {
967     OVERLAPPED overlapped;
968     DWORD count, ret;
969     BOOL r;
970
971     overlapped.hEvent = service->overlapped_event;
972     r = WriteFile(pipe, data, size, &count, &overlapped);
973     if (!r && GetLastError() == ERROR_IO_PENDING)
974     {
975         ret = WaitForSingleObject( service->overlapped_event, service_pipe_timeout );
976         if (ret == WAIT_TIMEOUT)
977         {
978             WINE_ERR("sending command timed out\n");
979             *result = ERROR_SERVICE_REQUEST_TIMEOUT;
980             return FALSE;
981         }
982         r = GetOverlappedResult( pipe, &overlapped, &count, FALSE );
983     }
984     if (!r || count != size)
985     {
986         WINE_ERR("service protocol error - failed to write pipe!\n");
987         *result  = (!r ? GetLastError() : ERROR_WRITE_FAULT);
988         return FALSE;
989     }
990     r = ReadFile(pipe, result, sizeof *result, &count, &overlapped);
991     if (!r && GetLastError() == ERROR_IO_PENDING)
992     {
993         ret = WaitForSingleObject( service->overlapped_event, service_pipe_timeout );
994         if (ret == WAIT_TIMEOUT)
995         {
996             WINE_ERR("receiving command result timed out\n");
997             *result = ERROR_SERVICE_REQUEST_TIMEOUT;
998             return FALSE;
999         }
1000         r = GetOverlappedResult( pipe, &overlapped, &count, FALSE );
1001     }
1002     if (!r || count != sizeof *result)
1003     {
1004         WINE_ERR("service protocol error - failed to read pipe "
1005             "r = %d  count = %d!\n", r, count);
1006         *result = (!r ? GetLastError() : ERROR_READ_FAULT);
1007         return FALSE;
1008     }
1009
1010     *result = ERROR_SUCCESS;
1011     return TRUE;
1012 }
1013
1014 /******************************************************************************
1015  * service_send_control
1016  */
1017 static BOOL service_send_control(struct service_entry *service, HANDLE pipe, DWORD dwControl, DWORD *result)
1018 {
1019     service_start_info *ssi;
1020     DWORD len;
1021     BOOL r;
1022
1023     /* calculate how much space we need to send the startup info */
1024     len = strlenW(service->name) + 1;
1025
1026     ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, data[len]));
1027     ssi->cmd = WINESERV_SENDCONTROL;
1028     ssi->control = dwControl;
1029     ssi->total_size = FIELD_OFFSET(service_start_info, data[len]);
1030     ssi->name_size = strlenW(service->name) + 1;
1031     strcpyW( ssi->data, service->name );
1032
1033     r = service_send_command( service, pipe, ssi, ssi->total_size, result );
1034     HeapFree( GetProcessHeap(), 0, ssi );
1035     return r;
1036 }
1037
1038 DWORD __cdecl svcctl_StartServiceW(
1039     SC_RPC_HANDLE hService,
1040     DWORD dwNumServiceArgs,
1041     LPCWSTR *lpServiceArgVectors)
1042 {
1043     struct sc_service_handle *service;
1044     DWORD err;
1045
1046     WINE_TRACE("(%p, %d, %p)\n", hService, dwNumServiceArgs, lpServiceArgVectors);
1047
1048     if ((err = validate_service_handle(hService, SERVICE_START, &service)) != 0)
1049         return err;
1050
1051     if (service->service_entry->config.dwStartType == SERVICE_DISABLED)
1052         return ERROR_SERVICE_DISABLED;
1053
1054     err = service_start(service->service_entry, dwNumServiceArgs, lpServiceArgVectors);
1055
1056     return err;
1057 }
1058
1059 DWORD __cdecl svcctl_ControlService(
1060     SC_RPC_HANDLE hService,
1061     DWORD dwControl,
1062     SERVICE_STATUS *lpServiceStatus)
1063 {
1064     DWORD access_required;
1065     struct sc_service_handle *service;
1066     DWORD result;
1067     BOOL ret;
1068     HANDLE control_mutex;
1069
1070     WINE_TRACE("(%p, %d, %p)\n", hService, dwControl, lpServiceStatus);
1071
1072     switch (dwControl)
1073     {
1074     case SERVICE_CONTROL_CONTINUE:
1075     case SERVICE_CONTROL_NETBINDADD:
1076     case SERVICE_CONTROL_NETBINDDISABLE:
1077     case SERVICE_CONTROL_NETBINDENABLE:
1078     case SERVICE_CONTROL_NETBINDREMOVE:
1079     case SERVICE_CONTROL_PARAMCHANGE:
1080     case SERVICE_CONTROL_PAUSE:
1081         access_required = SERVICE_PAUSE_CONTINUE;
1082         break;
1083     case SERVICE_CONTROL_INTERROGATE:
1084         access_required = SERVICE_INTERROGATE;
1085         break;
1086     case SERVICE_CONTROL_STOP:
1087         access_required = SERVICE_STOP;
1088         break;
1089     default:
1090         if (dwControl >= 128 && dwControl <= 255)
1091             access_required = SERVICE_USER_DEFINED_CONTROL;
1092         else
1093             return ERROR_INVALID_PARAMETER;
1094     }
1095
1096     if ((result = validate_service_handle(hService, access_required, &service)) != 0)
1097         return result;
1098
1099     service_lock_exclusive(service->service_entry);
1100
1101     result = ERROR_SUCCESS;
1102     switch (service->service_entry->status.dwCurrentState)
1103     {
1104     case SERVICE_STOPPED:
1105         result = ERROR_SERVICE_NOT_ACTIVE;
1106         break;
1107     case SERVICE_START_PENDING:
1108         if (dwControl==SERVICE_CONTROL_STOP)
1109             break;
1110         /* fall through */
1111     case SERVICE_STOP_PENDING:
1112         result = ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
1113         break;
1114     }
1115
1116     if (result==ERROR_SUCCESS && !service->service_entry->control_mutex) {
1117         result = ERROR_SERVICE_CANNOT_ACCEPT_CTRL;
1118         service_terminate(service->service_entry);
1119     }
1120
1121     if (result != ERROR_SUCCESS)
1122     {
1123         if (lpServiceStatus)
1124         {
1125             lpServiceStatus->dwServiceType = service->service_entry->status.dwServiceType;
1126             lpServiceStatus->dwCurrentState = service->service_entry->status.dwCurrentState;
1127             lpServiceStatus->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
1128             lpServiceStatus->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
1129             lpServiceStatus->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
1130             lpServiceStatus->dwCheckPoint = service->service_entry->status.dwCheckPoint;
1131             lpServiceStatus->dwWaitHint = service->service_entry->status.dwWaitHint;
1132         }
1133         service_unlock(service->service_entry);
1134         return result;
1135     }
1136
1137     if (!service_accepts_control(service->service_entry, dwControl))
1138     {
1139         service_unlock(service->service_entry);
1140         return ERROR_INVALID_SERVICE_CONTROL;
1141     }
1142
1143     /* prevent races by caching control_mutex and clearing it on
1144      * stop instead of outside the services lock */
1145     control_mutex = service->service_entry->control_mutex;
1146     if (dwControl == SERVICE_CONTROL_STOP)
1147         service->service_entry->control_mutex = NULL;
1148
1149     service_unlock(service->service_entry);
1150
1151     ret = WaitForSingleObject(control_mutex, 30000);
1152     if (ret == WAIT_OBJECT_0)
1153     {
1154         service_send_control(service->service_entry, service->service_entry->control_pipe,
1155                 dwControl, &result);
1156
1157         if (lpServiceStatus)
1158         {
1159             service_lock_shared(service->service_entry);
1160             lpServiceStatus->dwServiceType = service->service_entry->status.dwServiceType;
1161             lpServiceStatus->dwCurrentState = service->service_entry->status.dwCurrentState;
1162             lpServiceStatus->dwControlsAccepted = service->service_entry->status.dwControlsAccepted;
1163             lpServiceStatus->dwWin32ExitCode = service->service_entry->status.dwWin32ExitCode;
1164             lpServiceStatus->dwServiceSpecificExitCode = service->service_entry->status.dwServiceSpecificExitCode;
1165             lpServiceStatus->dwCheckPoint = service->service_entry->status.dwCheckPoint;
1166             lpServiceStatus->dwWaitHint = service->service_entry->status.dwWaitHint;
1167             service_unlock(service->service_entry);
1168         }
1169
1170         if (dwControl == SERVICE_CONTROL_STOP)
1171             CloseHandle(control_mutex);
1172         else
1173             ReleaseMutex(control_mutex);
1174
1175         return result;
1176     }
1177     else
1178     {
1179         if (dwControl == SERVICE_CONTROL_STOP)
1180             CloseHandle(control_mutex);
1181         return ERROR_SERVICE_REQUEST_TIMEOUT;
1182     }
1183 }
1184
1185 DWORD __cdecl svcctl_CloseServiceHandle(
1186     SC_RPC_HANDLE *handle)
1187 {
1188     WINE_TRACE("(&%p)\n", *handle);
1189
1190     SC_RPC_HANDLE_destroy(*handle);
1191     *handle = NULL;
1192
1193     return ERROR_SUCCESS;
1194 }
1195
1196 static void SC_RPC_LOCK_destroy(SC_RPC_LOCK hLock)
1197 {
1198     struct sc_lock *lock = hLock;
1199     scmdatabase_unlock_startup(lock->db);
1200     HeapFree(GetProcessHeap(), 0, lock);
1201 }
1202
1203 void __RPC_USER SC_RPC_LOCK_rundown(SC_RPC_LOCK hLock)
1204 {
1205     SC_RPC_LOCK_destroy(hLock);
1206 }
1207
1208 DWORD __cdecl svcctl_LockServiceDatabase(
1209     SC_RPC_HANDLE hSCManager,
1210     SC_RPC_LOCK *phLock)
1211 {
1212     struct sc_manager_handle *manager;
1213     struct sc_lock *lock;
1214     DWORD err;
1215
1216     WINE_TRACE("(%p, %p)\n", hSCManager, phLock);
1217
1218     if ((err = validate_scm_handle(hSCManager, SC_MANAGER_LOCK, &manager)) != ERROR_SUCCESS)
1219         return err;
1220
1221     err = scmdatabase_lock_startup(manager->db);
1222     if (err != ERROR_SUCCESS)
1223         return err;
1224
1225     lock = HeapAlloc(GetProcessHeap(), 0, sizeof(struct sc_lock));
1226     if (!lock)
1227     {
1228         scmdatabase_unlock_startup(manager->db);
1229         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
1230     }
1231
1232     lock->db = manager->db;
1233     *phLock = lock;
1234
1235     return ERROR_SUCCESS;
1236 }
1237
1238 DWORD __cdecl svcctl_UnlockServiceDatabase(
1239     SC_RPC_LOCK *phLock)
1240 {
1241     WINE_TRACE("(&%p)\n", *phLock);
1242
1243     SC_RPC_LOCK_destroy(*phLock);
1244     *phLock = NULL;
1245
1246     return ERROR_SUCCESS;
1247 }
1248
1249 static BOOL map_state(DWORD state, DWORD mask)
1250 {
1251     switch (state)
1252     {
1253     case SERVICE_START_PENDING:
1254     case SERVICE_STOP_PENDING:
1255     case SERVICE_RUNNING:
1256     case SERVICE_CONTINUE_PENDING:
1257     case SERVICE_PAUSE_PENDING:
1258     case SERVICE_PAUSED:
1259         if (SERVICE_ACTIVE & mask) return TRUE;
1260         break;
1261     case SERVICE_STOPPED:
1262         if (SERVICE_INACTIVE & mask) return TRUE;
1263         break;
1264     default:
1265         WINE_ERR("unknown state %u\n", state);
1266         break;
1267     }
1268     return FALSE;
1269 }
1270
1271 DWORD __cdecl svcctl_EnumServicesStatusW(
1272     SC_RPC_HANDLE hmngr,
1273     DWORD type,
1274     DWORD state,
1275     BYTE *buffer,
1276     DWORD size,
1277     LPDWORD needed,
1278     LPDWORD returned)
1279 {
1280     DWORD err, sz, total_size, num_services;
1281     DWORD_PTR offset;
1282     struct sc_manager_handle *manager;
1283     struct service_entry *service;
1284     ENUM_SERVICE_STATUSW *s;
1285
1286     WINE_TRACE("(%p, 0x%x, 0x%x, %p, %u, %p, %p)\n", hmngr, type, state, buffer, size, needed, returned);
1287
1288     if (!type || !state)
1289         return ERROR_INVALID_PARAMETER;
1290
1291     if ((err = validate_scm_handle(hmngr, SC_MANAGER_ENUMERATE_SERVICE, &manager)) != ERROR_SUCCESS)
1292         return err;
1293
1294     scmdatabase_lock_exclusive(manager->db);
1295
1296     total_size = num_services = 0;
1297     LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry)
1298     {
1299         if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state))
1300         {
1301             total_size += sizeof(ENUM_SERVICE_STATUSW);
1302             total_size += (strlenW(service->name) + 1) * sizeof(WCHAR);
1303             if (service->config.lpDisplayName)
1304             {
1305                 total_size += (strlenW(service->config.lpDisplayName) + 1) * sizeof(WCHAR);
1306             }
1307             num_services++;
1308         }
1309     }
1310     *returned = 0;
1311     *needed = total_size;
1312     if (total_size > size)
1313     {
1314         scmdatabase_unlock(manager->db);
1315         return ERROR_MORE_DATA;
1316     }
1317     s = (ENUM_SERVICE_STATUSW *)buffer;
1318     offset = num_services * sizeof(ENUM_SERVICE_STATUSW);
1319     LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry)
1320     {
1321         if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state))
1322         {
1323             sz = (strlenW(service->name) + 1) * sizeof(WCHAR);
1324             memcpy(buffer + offset, service->name, sz);
1325             s->lpServiceName = (WCHAR *)offset; /* store a buffer offset instead of a pointer */
1326             offset += sz;
1327
1328             if (!service->config.lpDisplayName) s->lpDisplayName = NULL;
1329             else
1330             {
1331                 sz = (strlenW(service->config.lpDisplayName) + 1) * sizeof(WCHAR);
1332                 memcpy(buffer + offset, service->config.lpDisplayName, sz);
1333                 s->lpDisplayName = (WCHAR *)offset;
1334                 offset += sz;
1335             }
1336             memcpy(&s->ServiceStatus, &service->status, sizeof(SERVICE_STATUS));
1337             s++;
1338         }
1339     }
1340     *returned = num_services;
1341     *needed = 0;
1342     scmdatabase_unlock(manager->db);
1343     return ERROR_SUCCESS;
1344 }
1345
1346 static struct service_entry *find_service_by_group(struct scmdatabase *db, const WCHAR *group)
1347 {
1348     struct service_entry *service;
1349     LIST_FOR_EACH_ENTRY(service, &db->services, struct service_entry, entry)
1350     {
1351         if (service->config.lpLoadOrderGroup && !strcmpiW(group, service->config.lpLoadOrderGroup))
1352             return service;
1353     }
1354     return NULL;
1355 }
1356
1357 static BOOL match_group(const WCHAR *g1, const WCHAR *g2)
1358 {
1359     if (!g2) return TRUE;
1360     if (!g2[0] && (!g1 || !g1[0])) return TRUE;
1361     if (g1 && !strcmpW(g1, g2)) return TRUE;
1362     return FALSE;
1363 }
1364
1365 DWORD __cdecl svcctl_EnumServicesStatusExW(
1366     SC_RPC_HANDLE hmngr,
1367     DWORD type,
1368     DWORD state,
1369     BYTE *buffer,
1370     DWORD size,
1371     LPDWORD needed,
1372     LPDWORD returned,
1373     LPCWSTR group)
1374 {
1375     DWORD err, sz, total_size, num_services;
1376     DWORD_PTR offset;
1377     struct sc_manager_handle *manager;
1378     struct service_entry *service;
1379     ENUM_SERVICE_STATUS_PROCESSW *s;
1380
1381     WINE_TRACE("(%p, 0x%x, 0x%x, %p, %u, %p, %p, %s)\n", hmngr, type, state, buffer, size,
1382                needed, returned, wine_dbgstr_w(group));
1383
1384     if (!type || !state)
1385         return ERROR_INVALID_PARAMETER;
1386
1387     if ((err = validate_scm_handle(hmngr, SC_MANAGER_ENUMERATE_SERVICE, &manager)) != ERROR_SUCCESS)
1388         return err;
1389
1390     scmdatabase_lock_exclusive(manager->db);
1391
1392     if (group && !find_service_by_group(manager->db, group))
1393     {
1394         scmdatabase_unlock(manager->db);
1395         return ERROR_SERVICE_DOES_NOT_EXIST;
1396     }
1397
1398     total_size = num_services = 0;
1399     LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry)
1400     {
1401         if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state)
1402             && match_group(service->config.lpLoadOrderGroup, group))
1403         {
1404             total_size += sizeof(ENUM_SERVICE_STATUS_PROCESSW);
1405             total_size += (strlenW(service->name) + 1) * sizeof(WCHAR);
1406             if (service->config.lpDisplayName)
1407             {
1408                 total_size += (strlenW(service->config.lpDisplayName) + 1) * sizeof(WCHAR);
1409             }
1410             num_services++;
1411         }
1412     }
1413     *returned = 0;
1414     *needed = total_size;
1415     if (total_size > size)
1416     {
1417         scmdatabase_unlock(manager->db);
1418         return ERROR_MORE_DATA;
1419     }
1420     s = (ENUM_SERVICE_STATUS_PROCESSW *)buffer;
1421     offset = num_services * sizeof(ENUM_SERVICE_STATUS_PROCESSW);
1422     LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry)
1423     {
1424         if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state)
1425             && match_group(service->config.lpLoadOrderGroup, group))
1426         {
1427             sz = (strlenW(service->name) + 1) * sizeof(WCHAR);
1428             memcpy(buffer + offset, service->name, sz);
1429             s->lpServiceName = (WCHAR *)offset; /* store a buffer offset instead of a pointer */
1430             offset += sz;
1431
1432             if (!service->config.lpDisplayName) s->lpDisplayName = NULL;
1433             else
1434             {
1435                 sz = (strlenW(service->config.lpDisplayName) + 1) * sizeof(WCHAR);
1436                 memcpy(buffer + offset, service->config.lpDisplayName, sz);
1437                 s->lpDisplayName = (WCHAR *)offset;
1438                 offset += sz;
1439             }
1440             s->ServiceStatusProcess = service->status;
1441             s++;
1442         }
1443     }
1444     *returned = num_services;
1445     *needed = 0;
1446     scmdatabase_unlock(manager->db);
1447     return ERROR_SUCCESS;
1448 }
1449
1450 DWORD __cdecl svcctl_QueryServiceObjectSecurity(void)
1451 {
1452     WINE_FIXME("\n");
1453     return ERROR_CALL_NOT_IMPLEMENTED;
1454 }
1455
1456 DWORD __cdecl svcctl_SetServiceObjectSecurity(void)
1457 {
1458     WINE_FIXME("\n");
1459     return ERROR_CALL_NOT_IMPLEMENTED;
1460 }
1461
1462 DWORD __cdecl svcctl_QueryServiceStatus(void)
1463 {
1464     WINE_FIXME("\n");
1465     return ERROR_CALL_NOT_IMPLEMENTED;
1466 }
1467
1468
1469 DWORD __cdecl svcctl_NotifyBootConfigStatus(void)
1470 {
1471     WINE_FIXME("\n");
1472     return ERROR_CALL_NOT_IMPLEMENTED;
1473 }
1474
1475 DWORD __cdecl svcctl_SCSetServiceBitsW(void)
1476 {
1477     WINE_FIXME("\n");
1478     return ERROR_CALL_NOT_IMPLEMENTED;
1479 }
1480
1481
1482 DWORD __cdecl svcctl_EnumDependentServicesW(void)
1483 {
1484     WINE_FIXME("\n");
1485     return ERROR_CALL_NOT_IMPLEMENTED;
1486 }
1487
1488 DWORD __cdecl svcctl_QueryServiceLockStatusW(void)
1489 {
1490     WINE_FIXME("\n");
1491     return ERROR_CALL_NOT_IMPLEMENTED;
1492 }
1493
1494 DWORD __cdecl svcctl_SCSetServiceBitsA(void)
1495 {
1496     WINE_FIXME("\n");
1497     return ERROR_CALL_NOT_IMPLEMENTED;
1498 }
1499
1500 DWORD __cdecl svcctl_ChangeServiceConfigA(void)
1501 {
1502     WINE_FIXME("\n");
1503     return ERROR_CALL_NOT_IMPLEMENTED;
1504 }
1505
1506 DWORD __cdecl svcctl_CreateServiceA(void)
1507 {
1508     WINE_FIXME("\n");
1509     return ERROR_CALL_NOT_IMPLEMENTED;
1510 }
1511
1512 DWORD __cdecl svcctl_EnumDependentServicesA(void)
1513 {
1514     WINE_FIXME("\n");
1515     return ERROR_CALL_NOT_IMPLEMENTED;
1516 }
1517
1518 DWORD __cdecl svcctl_EnumServicesStatusA(void)
1519 {
1520     WINE_FIXME("\n");
1521     return ERROR_CALL_NOT_IMPLEMENTED;
1522 }
1523
1524 DWORD __cdecl svcctl_OpenSCManagerA(void)
1525 {
1526     WINE_FIXME("\n");
1527     return ERROR_CALL_NOT_IMPLEMENTED;
1528 }
1529
1530 DWORD __cdecl svcctl_OpenServiceA(void)
1531 {
1532     WINE_FIXME("\n");
1533     return ERROR_CALL_NOT_IMPLEMENTED;
1534 }
1535
1536 DWORD __cdecl svcctl_QueryServiceConfigA(void)
1537 {
1538     WINE_FIXME("\n");
1539     return ERROR_CALL_NOT_IMPLEMENTED;
1540 }
1541
1542 DWORD __cdecl svcctl_QueryServiceLockStatusA(void)
1543 {
1544     WINE_FIXME("\n");
1545     return ERROR_CALL_NOT_IMPLEMENTED;
1546 }
1547
1548 DWORD __cdecl svcctl_StartServiceA(void)
1549 {
1550     WINE_FIXME("\n");
1551     return ERROR_CALL_NOT_IMPLEMENTED;
1552 }
1553
1554 DWORD __cdecl svcctl_GetServiceDisplayNameA(void)
1555 {
1556     WINE_FIXME("\n");
1557     return ERROR_CALL_NOT_IMPLEMENTED;
1558 }
1559
1560 DWORD __cdecl svcctl_GetServiceKeyNameA(void)
1561 {
1562     WINE_FIXME("\n");
1563     return ERROR_CALL_NOT_IMPLEMENTED;
1564 }
1565
1566 DWORD __cdecl svcctl_GetCurrentGroupStateW(void)
1567 {
1568     WINE_FIXME("\n");
1569     return ERROR_CALL_NOT_IMPLEMENTED;
1570 }
1571
1572 DWORD __cdecl svcctl_EnumServiceGroupW(void)
1573 {
1574     WINE_FIXME("\n");
1575     return ERROR_CALL_NOT_IMPLEMENTED;
1576 }
1577
1578 DWORD __cdecl svcctl_ChangeServiceConfig2A(void)
1579 {
1580     WINE_FIXME("\n");
1581     return ERROR_CALL_NOT_IMPLEMENTED;
1582 }
1583
1584 DWORD __cdecl svcctl_QueryServiceConfig2A(void)
1585 {
1586     WINE_FIXME("\n");
1587     return ERROR_CALL_NOT_IMPLEMENTED;
1588 }
1589
1590
1591 DWORD RPC_Init(void)
1592 {
1593     WCHAR transport[] = SVCCTL_TRANSPORT;
1594     WCHAR endpoint[] = SVCCTL_ENDPOINT;
1595     DWORD err;
1596
1597     if ((err = RpcServerUseProtseqEpW(transport, 0, endpoint, NULL)) != ERROR_SUCCESS)
1598     {
1599         WINE_ERR("RpcServerUseProtseq failed with error %u\n", err);
1600         return err;
1601     }
1602
1603     if ((err = RpcServerRegisterIf(svcctl_v2_0_s_ifspec, 0, 0)) != ERROR_SUCCESS)
1604     {
1605         WINE_ERR("RpcServerRegisterIf failed with error %u\n", err);
1606         return err;
1607     }
1608
1609     if ((err = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE)) != ERROR_SUCCESS)
1610     {
1611         WINE_ERR("RpcServerListen failed with error %u\n", err);
1612         return err;
1613     }
1614     return ERROR_SUCCESS;
1615 }
1616
1617 DWORD events_loop(void)
1618 {
1619     struct timeout_queue_elem *iter, *iter_safe;
1620     DWORD err;
1621     HANDLE wait_handles[2];
1622     DWORD timeout = INFINITE;
1623
1624     wait_handles[0] = __wine_make_process_system();
1625     wait_handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL);
1626     timeout_queue_event = wait_handles[1];
1627
1628     SetEvent(g_hStartedEvent);
1629
1630     WINE_TRACE("Entered main loop\n");
1631
1632     do
1633     {
1634         err = WaitForMultipleObjects(2, wait_handles, FALSE, timeout);
1635         WINE_TRACE("Wait returned %d\n", err);
1636
1637         if(err==WAIT_OBJECT_0+1 || err==WAIT_TIMEOUT)
1638         {
1639             FILETIME cur_time;
1640             ULARGE_INTEGER time;
1641
1642             GetSystemTimeAsFileTime(&cur_time);
1643             time.u.LowPart = cur_time.dwLowDateTime;
1644             time.u.HighPart = cur_time.dwHighDateTime;
1645
1646             EnterCriticalSection(&timeout_queue_cs);
1647             timeout = INFINITE;
1648             LIST_FOR_EACH_ENTRY_SAFE(iter, iter_safe, &timeout_queue, struct timeout_queue_elem, entry)
1649             {
1650                 if(CompareFileTime(&cur_time, &iter->time) >= 0)
1651                 {
1652                     LeaveCriticalSection(&timeout_queue_cs);
1653                     iter->func(iter->service_entry);
1654                     EnterCriticalSection(&timeout_queue_cs);
1655
1656                     release_service(iter->service_entry);
1657                     list_remove(&iter->entry);
1658                     HeapFree(GetProcessHeap(), 0, iter);
1659                 }
1660                 else
1661                 {
1662                     ULARGE_INTEGER time_diff;
1663
1664                     time_diff.u.LowPart = iter->time.dwLowDateTime;
1665                     time_diff.u.HighPart = iter->time.dwHighDateTime;
1666                     time_diff.QuadPart = (time_diff.QuadPart-time.QuadPart)/10000;
1667
1668                     if(time_diff.QuadPart < timeout)
1669                         timeout = time_diff.QuadPart;
1670                 }
1671             }
1672             LeaveCriticalSection(&timeout_queue_cs);
1673
1674             if(timeout != INFINITE)
1675                 timeout += 1000;
1676         }
1677     } while (err != WAIT_OBJECT_0);
1678
1679     WINE_TRACE("Object signaled - wine shutdown\n");
1680     EnterCriticalSection(&timeout_queue_cs);
1681     LIST_FOR_EACH_ENTRY_SAFE(iter, iter_safe, &timeout_queue, struct timeout_queue_elem, entry)
1682     {
1683         LeaveCriticalSection(&timeout_queue_cs);
1684         iter->func(iter->service_entry);
1685         EnterCriticalSection(&timeout_queue_cs);
1686
1687         release_service(iter->service_entry);
1688         list_remove(&iter->entry);
1689         HeapFree(GetProcessHeap(), 0, iter);
1690     }
1691     LeaveCriticalSection(&timeout_queue_cs);
1692
1693     CloseHandle(wait_handles[0]);
1694     CloseHandle(wait_handles[1]);
1695     return ERROR_SUCCESS;
1696 }
1697
1698 void __RPC_USER SC_RPC_HANDLE_rundown(SC_RPC_HANDLE handle)
1699 {
1700     SC_RPC_HANDLE_destroy(handle);
1701 }
1702
1703 void  __RPC_FAR * __RPC_USER MIDL_user_allocate(SIZE_T len)
1704 {
1705     return HeapAlloc(GetProcessHeap(), 0, len);
1706 }
1707
1708 void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr)
1709 {
1710     HeapFree(GetProcessHeap(), 0, ptr);
1711 }