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