winemine: Increased buffer size for player names.
[wine] / programs / services / services.c
1 /*
2  * Services - controls services keeps track of their state
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 <winsvc.h>
26 #include <rpc.h>
27
28 #include "wine/unicode.h"
29 #include "wine/debug.h"
30 #include "svcctl.h"
31
32 #include "services.h"
33
34 #define MAX_SERVICE_NAME 260
35
36 WINE_DEFAULT_DEBUG_CHANNEL(service);
37
38 HANDLE g_hStartedEvent;
39 struct scmdatabase *active_database;
40
41 static const WCHAR SZ_LOCAL_SYSTEM[] = {'L','o','c','a','l','S','y','s','t','e','m',0};
42
43 /* Registry constants */
44 static const WCHAR SZ_SERVICES_KEY[] = { 'S','y','s','t','e','m','\\',
45       'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
46       'S','e','r','v','i','c','e','s',0 };
47
48 /* Service key values names */
49 static const WCHAR SZ_DISPLAY_NAME[]      = {'D','i','s','p','l','a','y','N','a','m','e',0 };
50 static const WCHAR SZ_TYPE[]              = {'T','y','p','e',0 };
51 static const WCHAR SZ_START[]             = {'S','t','a','r','t',0 };
52 static const WCHAR SZ_ERROR[]             = {'E','r','r','o','r','C','o','n','t','r','o','l',0 };
53 static const WCHAR SZ_IMAGE_PATH[]        = {'I','m','a','g','e','P','a','t','h',0};
54 static const WCHAR SZ_GROUP[]             = {'G','r','o','u','p',0};
55 static const WCHAR SZ_DEPEND_ON_SERVICE[] = {'D','e','p','e','n','d','O','n','S','e','r','v','i','c','e',0};
56 static const WCHAR SZ_DEPEND_ON_GROUP[]   = {'D','e','p','e','n','d','O','n','G','r','o','u','p',0};
57 static const WCHAR SZ_OBJECT_NAME[]       = {'O','b','j','e','c','t','N','a','m','e',0};
58 static const WCHAR SZ_TAG[]               = {'T','a','g',0};
59 static const WCHAR SZ_DESCRIPTION[]       = {'D','e','s','c','r','i','p','t','i','o','n',0};
60
61
62 DWORD service_create(LPCWSTR name, struct service_entry **entry)
63 {
64     *entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(**entry));
65     if (!*entry)
66         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
67     (*entry)->name = strdupW(name);
68     if (!(*entry)->name)
69     {
70         HeapFree(GetProcessHeap(), 0, *entry);
71         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
72     }
73     (*entry)->control_pipe = INVALID_HANDLE_VALUE;
74     return ERROR_SUCCESS;
75 }
76
77 void free_service_entry(struct service_entry *entry)
78 {
79     HeapFree(GetProcessHeap(), 0, entry->name);
80     HeapFree(GetProcessHeap(), 0, entry->config.lpBinaryPathName);
81     HeapFree(GetProcessHeap(), 0, entry->config.lpDependencies);
82     HeapFree(GetProcessHeap(), 0, entry->config.lpLoadOrderGroup);
83     HeapFree(GetProcessHeap(), 0, entry->config.lpServiceStartName);
84     HeapFree(GetProcessHeap(), 0, entry->config.lpDisplayName);
85     HeapFree(GetProcessHeap(), 0, entry->description);
86     HeapFree(GetProcessHeap(), 0, entry->dependOnServices);
87     HeapFree(GetProcessHeap(), 0, entry->dependOnGroups);
88     CloseHandle(entry->control_mutex);
89     CloseHandle(entry->control_pipe);
90     CloseHandle(entry->status_changed_event);
91     HeapFree(GetProcessHeap(), 0, entry);
92 }
93
94 static DWORD load_service_config(HKEY hKey, struct service_entry *entry)
95 {
96     DWORD err;
97     WCHAR *wptr;
98
99     if ((err = load_reg_string(hKey, SZ_IMAGE_PATH,   TRUE, &entry->config.lpBinaryPathName)) != 0)
100         return err;
101     if ((err = load_reg_string(hKey, SZ_GROUP,        0,    &entry->config.lpLoadOrderGroup)) != 0)
102         return err;
103     if ((err = load_reg_string(hKey, SZ_OBJECT_NAME,  TRUE, &entry->config.lpServiceStartName)) != 0)
104         return err;
105     if ((err = load_reg_string(hKey, SZ_DISPLAY_NAME, 0,    &entry->config.lpDisplayName)) != 0)
106         return err;
107     if ((err = load_reg_string(hKey, SZ_DESCRIPTION,  0,    &entry->description)) != 0)
108         return err;
109     if ((err = load_reg_multisz(hKey, SZ_DEPEND_ON_SERVICE, &entry->dependOnServices)) != 0)
110         return err;
111     if ((err = load_reg_multisz(hKey, SZ_DEPEND_ON_GROUP,   &entry->dependOnGroups)) != 0)
112         return err;
113
114     if ((err = load_reg_dword(hKey, SZ_TYPE,  &entry->config.dwServiceType)) != 0)
115         return err;
116     if ((err = load_reg_dword(hKey, SZ_START, &entry->config.dwStartType)) != 0)
117         return err;
118     if ((err = load_reg_dword(hKey, SZ_ERROR, &entry->config.dwErrorControl)) != 0)
119         return err;
120     if ((err = load_reg_dword(hKey, SZ_TAG,   &entry->config.dwTagId)) != 0)
121         return err;
122
123     WINE_TRACE("Image path           = %s\n", wine_dbgstr_w(entry->config.lpBinaryPathName) );
124     WINE_TRACE("Group                = %s\n", wine_dbgstr_w(entry->config.lpLoadOrderGroup) );
125     WINE_TRACE("Service account name = %s\n", wine_dbgstr_w(entry->config.lpServiceStartName) );
126     WINE_TRACE("Display name         = %s\n", wine_dbgstr_w(entry->config.lpDisplayName) );
127     WINE_TRACE("Service dependencies : %s\n", entry->dependOnServices[0] ? "" : "(none)");
128     for (wptr = entry->dependOnServices; *wptr; wptr += strlenW(wptr) + 1)
129         WINE_TRACE("    * %s\n", wine_dbgstr_w(wptr));
130     WINE_TRACE("Group dependencies   : %s\n", entry->dependOnGroups[0] ? "" : "(none)");
131     for (wptr = entry->dependOnGroups; *wptr; wptr += strlenW(wptr) + 1)
132         WINE_TRACE("    * %s\n", wine_dbgstr_w(wptr));
133
134     return ERROR_SUCCESS;
135 }
136
137 static DWORD reg_set_string_value(HKEY hKey, LPCWSTR value_name, LPCWSTR string)
138 {
139     if (!string)
140     {
141         DWORD err;
142         err = RegDeleteValueW(hKey, value_name);
143         if (err != ERROR_FILE_NOT_FOUND)
144             return err;
145
146         return ERROR_SUCCESS;
147     }
148
149     return RegSetValueExW(hKey, value_name, 0, REG_SZ, (LPBYTE)string, sizeof(WCHAR)*(strlenW(string) + 1));
150 }
151
152 DWORD save_service_config(struct service_entry *entry)
153 {
154     DWORD err;
155     HKEY hKey = NULL;
156
157     err = RegCreateKeyW(entry->db->root_key, entry->name, &hKey);
158     if (err != ERROR_SUCCESS)
159         goto cleanup;
160
161     if ((err = reg_set_string_value(hKey, SZ_DISPLAY_NAME, entry->config.lpDisplayName)) != 0)
162         goto cleanup;
163     if ((err = reg_set_string_value(hKey, SZ_IMAGE_PATH, entry->config.lpBinaryPathName)) != 0)
164         goto cleanup;
165     if ((err = reg_set_string_value(hKey, SZ_GROUP, entry->config.lpLoadOrderGroup)) != 0)
166         goto cleanup;
167     if ((err = reg_set_string_value(hKey, SZ_OBJECT_NAME, entry->config.lpServiceStartName)) != 0)
168         goto cleanup;
169     if ((err = reg_set_string_value(hKey, SZ_DESCRIPTION, entry->description)) != 0)
170         goto cleanup;
171     if ((err = RegSetValueExW(hKey, SZ_START, 0, REG_DWORD, (LPBYTE)&entry->config.dwStartType, sizeof(DWORD))) != 0)
172         goto cleanup;
173     if ((err = RegSetValueExW(hKey, SZ_ERROR, 0, REG_DWORD, (LPBYTE)&entry->config.dwErrorControl, sizeof(DWORD))) != 0)
174         goto cleanup;
175
176     if ((err = RegSetValueExW(hKey, SZ_TYPE, 0, REG_DWORD, (LPBYTE)&entry->config.dwServiceType, sizeof(DWORD))) != 0)
177         goto cleanup;
178
179     if (entry->config.dwTagId)
180         err = RegSetValueExW(hKey, SZ_TAG, 0, REG_DWORD, (LPBYTE)&entry->config.dwTagId, sizeof(DWORD));
181     else
182         err = RegDeleteValueW(hKey, SZ_TAG);
183
184     if (err != 0 && err != ERROR_FILE_NOT_FOUND)
185         goto cleanup;
186
187     err = ERROR_SUCCESS;
188 cleanup:
189     RegCloseKey(hKey);
190     return err;
191 }
192
193 DWORD scmdatabase_add_service(struct scmdatabase *db, struct service_entry *service)
194 {
195     int err;
196     service->db = db;
197     if ((err = save_service_config(service)) != ERROR_SUCCESS)
198     {
199         WINE_ERR("Couldn't store service configuration: error %u\n", err);
200         return ERROR_GEN_FAILURE;
201     }
202
203     list_add_tail(&db->services, &service->entry);
204     return ERROR_SUCCESS;
205 }
206
207 DWORD scmdatabase_remove_service(struct scmdatabase *db, struct service_entry *service)
208 {
209     int err;
210
211     err = RegDeleteTreeW(db->root_key, service->name);
212
213     if (err != 0)
214         return err;
215
216     list_remove(&service->entry);
217     service->entry.next = service->entry.prev = NULL;
218     return ERROR_SUCCESS;
219 }
220
221 static void scmdatabase_autostart_services(struct scmdatabase *db)
222 {
223     struct service_entry **services_list;
224     unsigned int i = 0;
225     unsigned int size = 32;
226     struct service_entry *service;
227
228     services_list = HeapAlloc(GetProcessHeap(), 0, size * sizeof(services_list[0]));
229     if (!services_list)
230         return;
231
232     scmdatabase_lock_shared(db);
233
234     LIST_FOR_EACH_ENTRY(service, &db->services, struct service_entry, entry)
235     {
236         if (service->config.dwStartType == SERVICE_BOOT_START ||
237             service->config.dwStartType == SERVICE_SYSTEM_START ||
238             service->config.dwStartType == SERVICE_AUTO_START)
239         {
240             if (i+1 >= size)
241             {
242                 size *= 2;
243                 services_list = HeapReAlloc(GetProcessHeap(), 0, services_list, size * sizeof(services_list[0]));
244                 if (!services_list)
245                     break;
246             }
247             services_list[i] = service;
248             service->ref_count++;
249             i++;
250         }
251     }
252
253     scmdatabase_unlock(db);
254
255     size = i;
256     for (i = 0; i < size; i++)
257     {
258         DWORD err;
259         service = services_list[i];
260         err = service_start(service, 0, NULL);
261         /* FIXME: do something if the service failed to start */
262         release_service(service);
263     }
264
265     HeapFree(GetProcessHeap(), 0, services_list);
266 }
267
268 BOOL validate_service_name(LPCWSTR name)
269 {
270     return (name && name[0] && !strchrW(name, '/') && !strchrW(name, '\\'));
271 }
272
273 BOOL validate_service_config(struct service_entry *entry)
274 {
275     if (entry->config.dwServiceType & SERVICE_WIN32 && (entry->config.lpBinaryPathName == NULL || !entry->config.lpBinaryPathName[0]))
276     {
277         WINE_ERR("Service %s is Win32 but has no image path set\n", wine_dbgstr_w(entry->name));
278         return FALSE;
279     }
280
281     switch (entry->config.dwServiceType)
282     {
283     case SERVICE_KERNEL_DRIVER:
284     case SERVICE_FILE_SYSTEM_DRIVER:
285     case SERVICE_WIN32_OWN_PROCESS:
286     case SERVICE_WIN32_SHARE_PROCESS:
287         /* No problem */
288         break;
289     case SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS:
290     case SERVICE_WIN32_SHARE_PROCESS | SERVICE_INTERACTIVE_PROCESS:
291         /* These can be only run as LocalSystem */
292         if (entry->config.lpServiceStartName && strcmpiW(entry->config.lpServiceStartName, SZ_LOCAL_SYSTEM) != 0)
293         {
294             WINE_ERR("Service %s is interactive but has a start name\n", wine_dbgstr_w(entry->name));
295             return FALSE;
296         }
297         break;
298     default:
299         WINE_ERR("Service %s has an unknown service type\n", wine_dbgstr_w(entry->name));
300         return FALSE;
301     }
302
303     /* StartType can only be a single value (if several values are mixed the result is probably not what was intended) */
304     if (entry->config.dwStartType > SERVICE_DISABLED)
305     {
306         WINE_ERR("Service %s has an unknown start type\n", wine_dbgstr_w(entry->name));
307         return FALSE;
308     }
309
310     /* SERVICE_BOOT_START and SERVICE_SYSTEM_START are only allowed for driver services */
311     if (((entry->config.dwStartType == SERVICE_BOOT_START) || (entry->config.dwStartType == SERVICE_SYSTEM_START)) &&
312         ((entry->config.dwServiceType & SERVICE_WIN32_OWN_PROCESS) || (entry->config.dwServiceType & SERVICE_WIN32_SHARE_PROCESS)))
313     {
314         WINE_ERR("Service %s - SERVICE_BOOT_START and SERVICE_SYSTEM_START are only allowed for driver services\n", wine_dbgstr_w(entry->name));
315         return FALSE;
316     }
317
318     if (entry->config.lpServiceStartName == NULL)
319         entry->config.lpServiceStartName = strdupW(SZ_LOCAL_SYSTEM);
320
321     return TRUE;
322 }
323
324
325 struct service_entry *scmdatabase_find_service(struct scmdatabase *db, LPCWSTR name)
326 {
327     struct service_entry *service;
328
329     LIST_FOR_EACH_ENTRY(service, &db->services, struct service_entry, entry)
330     {
331         if (strcmpiW(name, service->name) == 0)
332             return service;
333     }
334
335     return NULL;
336 }
337
338 struct service_entry *scmdatabase_find_service_by_displayname(struct scmdatabase *db, LPCWSTR name)
339 {
340     struct service_entry *service;
341
342     LIST_FOR_EACH_ENTRY(service, &db->services, struct service_entry, entry)
343     {
344         if (strcmpiW(name, service->config.lpDisplayName) == 0)
345             return service;
346     }
347
348     return NULL;
349 }
350
351 void release_service(struct service_entry *service)
352 {
353     if (InterlockedDecrement(&service->ref_count) == 0 && is_marked_for_delete(service))
354         free_service_entry(service);
355 }
356
357 static DWORD scmdatabase_create(struct scmdatabase **db)
358 {
359     DWORD err;
360
361     *db = HeapAlloc(GetProcessHeap(), 0, sizeof(**db));
362     if (!*db)
363         return ERROR_NOT_ENOUGH_SERVER_MEMORY;
364
365     (*db)->service_start_lock = FALSE;
366     list_init(&(*db)->services);
367
368     InitializeCriticalSection(&(*db)->cs);
369
370     err = RegCreateKeyExW(HKEY_LOCAL_MACHINE, SZ_SERVICES_KEY, 0, NULL,
371                           REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
372                           &(*db)->root_key, NULL);
373     if (err != ERROR_SUCCESS)
374         HeapFree(GetProcessHeap(), 0, *db);
375
376     return err;
377 }
378
379 static void scmdatabase_destroy(struct scmdatabase *db)
380 {
381     RegCloseKey(db->root_key);
382     DeleteCriticalSection(&db->cs);
383     HeapFree(GetProcessHeap(), 0, db);
384 }
385
386 static DWORD scmdatabase_load_services(struct scmdatabase *db)
387 {
388     DWORD err;
389     int i;
390
391     for (i = 0; TRUE; i++)
392     {
393         WCHAR szName[MAX_SERVICE_NAME];
394         struct service_entry *entry;
395         HKEY hServiceKey;
396
397         err = RegEnumKeyW(db->root_key, i, szName, MAX_SERVICE_NAME);
398         if (err == ERROR_NO_MORE_ITEMS)
399             break;
400
401         if (err != 0)
402         {
403             WINE_ERR("Error %d reading key %d name - skipping\n", err, i);
404             continue;
405         }
406
407         err = service_create(szName, &entry);
408         if (err != ERROR_SUCCESS)
409             break;
410
411         WINE_TRACE("Loading service %s\n", wine_dbgstr_w(szName));
412         err = RegOpenKeyExW(db->root_key, szName, 0, KEY_READ | KEY_WRITE, &hServiceKey);
413         if (err == ERROR_SUCCESS)
414         {
415             err = load_service_config(hServiceKey, entry);
416             RegCloseKey(hServiceKey);
417         }
418
419         if (err != ERROR_SUCCESS)
420         {
421             WINE_ERR("Error %d reading registry key for service %s - skipping\n", err, wine_dbgstr_w(szName));
422             free_service_entry(entry);
423             continue;
424         }
425
426         if (entry->config.dwServiceType == 0)
427         {
428             /* Maybe an application only wrote some configuration in the service key. Continue silently */
429             WINE_TRACE("Even the service type not set for service %s - skipping\n", wine_dbgstr_w(szName));
430             free_service_entry(entry);
431             continue;
432         }
433
434         if (!validate_service_config(entry))
435         {
436             WINE_ERR("Invalid configuration of service %s - skipping\n", wine_dbgstr_w(szName));
437             free_service_entry(entry);
438             continue;
439         }
440
441         entry->status.dwServiceType = entry->config.dwServiceType;
442         entry->status.dwCurrentState = SERVICE_STOPPED;
443         entry->status.dwWin32ExitCode = ERROR_SERVICE_NEVER_STARTED;
444         entry->db = db;
445         /* all other fields are zero */
446
447         list_add_tail(&db->services, &entry->entry);
448     }
449     return ERROR_SUCCESS;
450 }
451
452 DWORD scmdatabase_lock_startup(struct scmdatabase *db)
453 {
454     if (InterlockedCompareExchange(&db->service_start_lock, TRUE, FALSE))
455         return ERROR_SERVICE_DATABASE_LOCKED;
456     return ERROR_SUCCESS;
457 }
458
459 void scmdatabase_unlock_startup(struct scmdatabase *db)
460 {
461     InterlockedCompareExchange(&db->service_start_lock, FALSE, TRUE);
462 }
463
464 void scmdatabase_lock_shared(struct scmdatabase *db)
465 {
466     EnterCriticalSection(&db->cs);
467 }
468
469 void scmdatabase_lock_exclusive(struct scmdatabase *db)
470 {
471     EnterCriticalSection(&db->cs);
472 }
473
474 void scmdatabase_unlock(struct scmdatabase *db)
475 {
476     LeaveCriticalSection(&db->cs);
477 }
478
479 void service_lock_shared(struct service_entry *service)
480 {
481     EnterCriticalSection(&service->db->cs);
482 }
483
484 void service_lock_exclusive(struct service_entry *service)
485 {
486     EnterCriticalSection(&service->db->cs);
487 }
488
489 void service_unlock(struct service_entry *service)
490 {
491     LeaveCriticalSection(&service->db->cs);
492 }
493
494 /* only one service started at a time, so there is no race on the registry
495  * value here */
496 static LPWSTR service_get_pipe_name(void)
497 {
498     static const WCHAR format[] = { '\\','\\','.','\\','p','i','p','e','\\',
499         'n','e','t','\\','N','t','C','o','n','t','r','o','l','P','i','p','e','%','u',0};
500     static const WCHAR service_current_key_str[] = { 'S','Y','S','T','E','M','\\',
501         'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
502         'C','o','n','t','r','o','l','\\',
503         'S','e','r','v','i','c','e','C','u','r','r','e','n','t',0};
504     LPWSTR name;
505     DWORD len;
506     HKEY service_current_key;
507     DWORD service_current = -1;
508     LONG ret;
509     DWORD type;
510
511     ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, service_current_key_str, 0,
512         NULL, REG_OPTION_VOLATILE, KEY_SET_VALUE | KEY_QUERY_VALUE, NULL,
513         &service_current_key, NULL);
514     if (ret != ERROR_SUCCESS)
515         return NULL;
516     len = sizeof(service_current);
517     ret = RegQueryValueExW(service_current_key, NULL, NULL, &type,
518         (BYTE *)&service_current, &len);
519     if ((ret == ERROR_SUCCESS && type == REG_DWORD) || ret == ERROR_FILE_NOT_FOUND)
520     {
521         service_current++;
522         RegSetValueExW(service_current_key, NULL, 0, REG_DWORD,
523             (BYTE *)&service_current, sizeof(service_current));
524     }
525     RegCloseKey(service_current_key);
526     if ((ret != ERROR_SUCCESS || type != REG_DWORD) && (ret != ERROR_FILE_NOT_FOUND))
527         return NULL;
528     len = sizeof(format)/sizeof(WCHAR) + 10 /* strlenW("4294967295") */;
529     name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
530     if (!name)
531         return NULL;
532     snprintfW(name, len, format, service_current);
533     return name;
534 }
535
536 static DWORD service_start_process(struct service_entry *service_entry, HANDLE *process)
537 {
538     PROCESS_INFORMATION pi;
539     STARTUPINFOW si;
540     LPWSTR path = NULL;
541     DWORD size;
542     BOOL r;
543
544     service_lock_exclusive(service_entry);
545
546     if (service_entry->config.dwServiceType == SERVICE_KERNEL_DRIVER)
547     {
548         static const WCHAR winedeviceW[] = {'\\','w','i','n','e','d','e','v','i','c','e','.','e','x','e',' ',0};
549         DWORD len = GetSystemDirectoryW( NULL, 0 ) + sizeof(winedeviceW)/sizeof(WCHAR) + strlenW(service_entry->name);
550
551         if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
552             return ERROR_NOT_ENOUGH_SERVER_MEMORY;
553         GetSystemDirectoryW( path, len );
554         lstrcatW( path, winedeviceW );
555         lstrcatW( path, service_entry->name );
556     }
557     else
558     {
559         size = ExpandEnvironmentStringsW(service_entry->config.lpBinaryPathName,NULL,0);
560         path = HeapAlloc(GetProcessHeap(),0,size*sizeof(WCHAR));
561         if (!path)
562             return ERROR_NOT_ENOUGH_SERVER_MEMORY;
563         ExpandEnvironmentStringsW(service_entry->config.lpBinaryPathName,path,size);
564     }
565
566     ZeroMemory(&si, sizeof(STARTUPINFOW));
567     si.cb = sizeof(STARTUPINFOW);
568     if (!(service_entry->config.dwServiceType & SERVICE_INTERACTIVE_PROCESS))
569     {
570         static WCHAR desktopW[] = {'_','_','w','i','n','e','s','e','r','v','i','c','e','_','w','i','n','s','t','a','t','i','o','n','\\','D','e','f','a','u','l','t',0};
571         si.lpDesktop = desktopW;
572     }
573
574     service_entry->status.dwCurrentState = SERVICE_START_PENDING;
575     service_entry->status.dwProcessId = pi.dwProcessId;
576
577     service_unlock(service_entry);
578
579     r = CreateProcessW(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
580     HeapFree(GetProcessHeap(),0,path);
581     if (!r)
582     {
583         service_lock_exclusive(service_entry);
584         service_entry->status.dwCurrentState = SERVICE_STOPPED;
585         service_entry->status.dwProcessId = 0;
586         service_unlock(service_entry);
587         return GetLastError();
588     }
589
590     *process = pi.hProcess;
591     CloseHandle( pi.hThread );
592
593     return ERROR_SUCCESS;
594 }
595
596 static DWORD service_wait_for_startup(struct service_entry *service_entry, HANDLE process_handle)
597 {
598     WINE_TRACE("%p\n", service_entry);
599
600     for (;;)
601     {
602         DWORD dwCurrentStatus;
603         HANDLE handles[2] = { service_entry->status_changed_event, process_handle };
604         DWORD ret;
605         ret = WaitForMultipleObjects(sizeof(handles)/sizeof(handles[0]), handles, FALSE, 20000);
606         if (ret != WAIT_OBJECT_0)
607             return ERROR_SERVICE_REQUEST_TIMEOUT;
608         service_lock_shared(service_entry);
609         dwCurrentStatus = service_entry->status.dwCurrentState;
610         service_unlock(service_entry);
611         if (dwCurrentStatus == SERVICE_RUNNING)
612         {
613             WINE_TRACE("Service started successfully\n");
614             return ERROR_SUCCESS;
615         }
616         if (dwCurrentStatus != SERVICE_START_PENDING)
617             return ERROR_SERVICE_REQUEST_TIMEOUT;
618     }
619 }
620
621 /******************************************************************************
622  * service_send_start_message
623  */
624 static BOOL service_send_start_message(struct service_entry *service, LPCWSTR *argv, DWORD argc)
625 {
626     DWORD i, len, count, result;
627     service_start_info *ssi;
628     LPWSTR p;
629     BOOL r;
630
631     WINE_TRACE("%s %p %d\n", wine_dbgstr_w(service->name), argv, argc);
632
633     /* FIXME: this can block so should be done in another thread */
634     r = ConnectNamedPipe(service->control_pipe, NULL);
635     if (!r && GetLastError() != ERROR_PIPE_CONNECTED)
636     {
637         WINE_ERR("pipe connect failed\n");
638         return FALSE;
639     }
640
641     /* calculate how much space do we need to send the startup info */
642     len = strlenW(service->name) + 1;
643     for (i=0; i<argc; i++)
644         len += strlenW(argv[i])+1;
645     len++;
646
647     ssi = HeapAlloc(GetProcessHeap(),0,FIELD_OFFSET(service_start_info, data[len]));
648     ssi->cmd = WINESERV_STARTINFO;
649     ssi->control = 0;
650     ssi->total_size = FIELD_OFFSET(service_start_info, data[len]);
651     ssi->name_size = strlenW(service->name) + 1;
652     strcpyW( ssi->data, service->name );
653
654     /* copy service args into a single buffer*/
655     p = &ssi->data[ssi->name_size];
656     for (i=0; i<argc; i++)
657     {
658         strcpyW(p, argv[i]);
659         p += strlenW(p) + 1;
660     }
661     *p=0;
662
663     r = WriteFile(service->control_pipe, ssi, ssi->total_size, &count, NULL);
664     if (r)
665     {
666         r = ReadFile(service->control_pipe, &result, sizeof result, &count, NULL);
667         if (r && result)
668         {
669             SetLastError(result);
670             r = FALSE;
671         }
672     }
673
674     HeapFree(GetProcessHeap(),0,ssi);
675
676     return r;
677 }
678
679 DWORD service_start(struct service_entry *service, DWORD service_argc, LPCWSTR *service_argv)
680 {
681     DWORD err;
682     LPWSTR name;
683     HANDLE process_handle = NULL;
684
685     err = scmdatabase_lock_startup(service->db);
686     if (err != ERROR_SUCCESS)
687         return err;
688
689     if (service->control_pipe != INVALID_HANDLE_VALUE)
690     {
691         scmdatabase_unlock_startup(service->db);
692         return ERROR_SERVICE_ALREADY_RUNNING;
693     }
694
695     service->control_mutex = CreateMutexW(NULL, TRUE, NULL);
696
697     if (!service->status_changed_event)
698         service->status_changed_event = CreateEventW(NULL, FALSE, FALSE, NULL);
699
700     name = service_get_pipe_name();
701     service->control_pipe = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX,
702                   PIPE_TYPE_BYTE|PIPE_WAIT, 1, 256, 256, 10000, NULL );
703     HeapFree(GetProcessHeap(), 0, name);
704     if (service->control_pipe==INVALID_HANDLE_VALUE)
705     {
706         WINE_ERR("failed to create pipe for %s, error = %d\n",
707             wine_dbgstr_w(service->name), GetLastError());
708         scmdatabase_unlock_startup(service->db);
709         return GetLastError();
710     }
711
712     err = service_start_process(service, &process_handle);
713
714     if (err == ERROR_SUCCESS)
715     {
716         if (!service_send_start_message(service, service_argv, service_argc))
717             err = ERROR_SERVICE_REQUEST_TIMEOUT;
718     }
719
720     if (err == ERROR_SUCCESS)
721         err = service_wait_for_startup(service, process_handle);
722
723     if (process_handle)
724         CloseHandle(process_handle);
725
726     ReleaseMutex(service->control_mutex);
727     scmdatabase_unlock_startup(service->db);
728
729     WINE_TRACE("returning %d\n", err);
730
731     return err;
732 }
733
734
735 int main(int argc, char *argv[])
736 {
737     static const WCHAR svcctl_started_event[] = SVCCTL_STARTED_EVENT;
738     DWORD err;
739     g_hStartedEvent = CreateEventW(NULL, TRUE, FALSE, svcctl_started_event);
740     err = scmdatabase_create(&active_database);
741     if (err != ERROR_SUCCESS)
742         return err;
743     if ((err = scmdatabase_load_services(active_database)) != ERROR_SUCCESS)
744         return err;
745     if ((err = RPC_Init()) == ERROR_SUCCESS)
746     {
747         scmdatabase_autostart_services(active_database);
748         RPC_MainLoop();
749     }
750     scmdatabase_destroy(active_database);
751     return err;
752 }