wineoss: Fix missing break statement.
[wine.git] / programs / svchost / svchost.c
blob618147a55c466e436a2b5aa78e303861202da927
1 /*
2 * Implementation of svchost.exe
4 * Copyright 2007 Google (Roy Shea)
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.
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.
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
21 /* Usage:
22 * Starting a service group:
24 * svchost /k service_group_name
27 #include <stdarg.h>
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winreg.h"
32 #include "winsvc.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(svchost);
37 static const WCHAR service_reg_path[] = L"System\\CurrentControlSet\\Services";
38 static const WCHAR svchost_path[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Svchost";
40 /* Allocate and initialize a WSTR containing the queried value */
41 static LPWSTR GetRegValue(HKEY service_key, const WCHAR *value_name)
43 DWORD type;
44 DWORD reg_size;
45 DWORD size;
46 LONG ret;
47 LPWSTR value;
49 WINE_TRACE("\n");
51 ret = RegQueryValueExW(service_key, value_name, NULL, &type, NULL, &reg_size);
52 if (ret != ERROR_SUCCESS)
54 return NULL;
57 /* Add space for potentially missing NULL terminators in initial alloc.
58 * The worst case REG_MULTI_SZ requires two NULL terminators. */
59 size = reg_size + (2 * sizeof(WCHAR));
60 value = HeapAlloc(GetProcessHeap(), 0, size);
62 ret = RegQueryValueExW(service_key, value_name, NULL, &type,
63 (LPBYTE)value, &reg_size);
64 if (ret != ERROR_SUCCESS)
66 HeapFree(GetProcessHeap(), 0, value);
67 return NULL;
70 /* Explicitly NULL terminate the result */
71 value[size / sizeof(WCHAR) - 1] = '\0';
72 value[size / sizeof(WCHAR) - 2] = '\0';
74 return value;
77 /* Allocate and initialize a WSTR containing the expanded string */
78 static LPWSTR ExpandEnv(LPWSTR string)
80 DWORD size;
81 LPWSTR expanded_string;
83 WINE_TRACE("\n");
85 size = 0;
86 size = ExpandEnvironmentStringsW(string, NULL, size);
87 if (size == 0)
89 WINE_ERR("cannot expand env vars in %s: %lu\n",
90 wine_dbgstr_w(string), GetLastError());
91 return NULL;
93 expanded_string = HeapAlloc(GetProcessHeap(), 0,
94 (size + 1) * sizeof(WCHAR));
95 if (ExpandEnvironmentStringsW(string, expanded_string, size) == 0)
97 WINE_ERR("cannot expand env vars in %s: %lu\n",
98 wine_dbgstr_w(string), GetLastError());
99 HeapFree(GetProcessHeap(), 0, expanded_string);
100 return NULL;
102 return expanded_string;
105 /* Fill in service table entry for a specified service */
106 static BOOL AddServiceElem(LPWSTR service_name,
107 SERVICE_TABLE_ENTRYW *service_table_entry)
109 LONG ret;
110 HKEY service_hkey = NULL;
111 LPWSTR service_param_key = NULL;
112 LPWSTR dll_name_short = NULL;
113 LPWSTR dll_name_long = NULL;
114 LPSTR dll_service_main = NULL;
115 HMODULE library = NULL;
116 LPSERVICE_MAIN_FUNCTIONW service_main_func = NULL;
117 BOOL success = FALSE;
118 DWORD reg_size;
119 DWORD size;
121 WINE_TRACE("Adding element for %s\n", wine_dbgstr_w(service_name));
123 /* Construct registry path to the service's parameters key */
124 size = lstrlenW(service_reg_path) + lstrlenW(L"\\") + lstrlenW(service_name) + lstrlenW(L"\\") +
125 lstrlenW(L"Parameters") + 1;
126 service_param_key = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
127 lstrcpyW(service_param_key, service_reg_path);
128 lstrcatW(service_param_key, L"\\");
129 lstrcatW(service_param_key, service_name);
130 lstrcatW(service_param_key, L"\\");
131 lstrcatW(service_param_key, L"Parameters");
132 service_param_key[size - 1] = '\0';
133 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, service_param_key, 0,
134 KEY_READ, &service_hkey);
135 if (ret != ERROR_SUCCESS)
137 WINE_ERR("cannot open key %s, err=%ld\n",
138 wine_dbgstr_w(service_param_key), ret);
139 goto cleanup;
142 /* Find DLL associate with service from key */
143 dll_name_short = GetRegValue(service_hkey, L"ServiceDll");
144 if (!dll_name_short)
146 WINE_ERR("cannot find registry value ServiceDll for service %s\n",
147 wine_dbgstr_w(service_name));
148 RegCloseKey(service_hkey);
149 goto cleanup;
152 /* Expand environment variables in ServiceDll name*/
153 dll_name_long = ExpandEnv(dll_name_short);
154 if (!dll_name_long)
156 WINE_ERR("failed to expand string %s\n",
157 wine_dbgstr_w(dll_name_short));
158 RegCloseKey(service_hkey);
159 goto cleanup;
162 /* Look for alternate to default ServiceMain entry point */
163 ret = RegQueryValueExA(service_hkey, "ServiceMain", NULL, NULL, NULL, &reg_size);
164 if (ret == ERROR_SUCCESS)
166 /* Add space for potentially missing NULL terminator, allocate, and
167 * fill with the registry value */
168 size = reg_size + 1;
169 dll_service_main = HeapAlloc(GetProcessHeap(), 0, size);
170 ret = RegQueryValueExA(service_hkey, "ServiceMain", NULL, NULL,
171 (LPBYTE)dll_service_main, &reg_size);
172 if (ret != ERROR_SUCCESS)
174 RegCloseKey(service_hkey);
175 goto cleanup;
177 dll_service_main[size - 1] = '\0';
179 RegCloseKey(service_hkey);
181 /* Load the DLL and obtain a pointer to ServiceMain entry point */
182 library = LoadLibraryExW(dll_name_long, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
183 if (!library)
185 WINE_ERR("failed to load library %s, err=%lu\n",
186 wine_dbgstr_w(dll_name_long), GetLastError());
187 goto cleanup;
189 if (dll_service_main)
191 service_main_func =
192 (LPSERVICE_MAIN_FUNCTIONW) GetProcAddress(library, dll_service_main);
194 else
196 service_main_func =
197 (LPSERVICE_MAIN_FUNCTIONW) GetProcAddress(library, "ServiceMain");
199 if (!service_main_func)
201 WINE_ERR("cannot locate ServiceMain procedure in DLL for %s\n",
202 wine_dbgstr_w(service_name));
203 FreeLibrary(library);
204 goto cleanup;
207 if (GetProcAddress(library, "SvchostPushServiceGlobals"))
209 WINE_FIXME("library %s expects undocumented SvchostPushServiceGlobals function to be called\n",
210 wine_dbgstr_w(dll_name_long));
213 /* Fill in the service table entry */
214 service_table_entry->lpServiceName = service_name;
215 service_table_entry->lpServiceProc = service_main_func;
216 success = TRUE;
218 cleanup:
219 HeapFree(GetProcessHeap(), 0, service_param_key);
220 HeapFree(GetProcessHeap(), 0, dll_name_short);
221 HeapFree(GetProcessHeap(), 0, dll_name_long);
222 HeapFree(GetProcessHeap(), 0, dll_service_main);
223 return success;
226 /* Initialize the service table for a list (REG_MULTI_SZ) of services */
227 static BOOL StartGroupServices(LPWSTR services)
229 LPWSTR service_name = NULL;
230 SERVICE_TABLE_ENTRYW *service_table = NULL;
231 DWORD service_count;
232 BOOL ret;
234 /* Count the services to load */
235 service_count = 0;
236 service_name = services;
237 while (*service_name != '\0')
239 ++service_count;
240 service_name = service_name + lstrlenW(service_name);
241 ++service_name;
243 WINE_TRACE("Service group contains %ld services\n", service_count);
245 /* Populate the service table */
246 service_table = HeapAlloc(GetProcessHeap(), 0,
247 (service_count + 1) * sizeof(SERVICE_TABLE_ENTRYW));
248 service_count = 0;
249 service_name = services;
250 while (*service_name != '\0')
252 if (!AddServiceElem(service_name, &service_table[service_count]))
254 HeapFree(GetProcessHeap(), 0, service_table);
255 return FALSE;
257 ++service_count;
258 service_name = service_name + lstrlenW(service_name);
259 ++service_name;
261 service_table[service_count].lpServiceName = NULL;
262 service_table[service_count].lpServiceProc = NULL;
264 /* Start the services */
265 if (!(ret = StartServiceCtrlDispatcherW(service_table)))
266 WINE_ERR("StartServiceCtrlDispatcherW failed to start %s: %lu\n",
267 wine_dbgstr_w(services), GetLastError());
269 HeapFree(GetProcessHeap(), 0, service_table);
270 return ret;
273 /* Find the list of services associated with a group name and start those
274 * services */
275 static BOOL LoadGroup(PWCHAR group_name)
277 HKEY group_hkey = NULL;
278 LPWSTR services = NULL;
279 LONG ret;
281 WINE_TRACE("Loading service group for %s\n", wine_dbgstr_w(group_name));
283 /* Lookup group_name value of svchost registry entry */
284 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, svchost_path, 0,
285 KEY_READ, &group_hkey);
286 if (ret != ERROR_SUCCESS)
288 WINE_ERR("cannot open key %s, err=%ld\n",
289 wine_dbgstr_w(svchost_path), ret);
290 return FALSE;
292 services = GetRegValue(group_hkey, group_name);
293 RegCloseKey(group_hkey);
294 if (!services)
296 WINE_ERR("cannot find registry value %s in %s\n",
297 wine_dbgstr_w(group_name), wine_dbgstr_w(svchost_path));
298 return FALSE;
301 /* Start services */
302 if (!(ret = StartGroupServices(services)))
303 WINE_TRACE("Failed to start service group\n");
305 HeapFree(GetProcessHeap(), 0, services);
306 return ret;
309 /* Load svchost group specified on the command line via the /k option */
310 int __cdecl wmain(int argc, WCHAR *argv[])
312 int option_index;
314 WINE_TRACE("\n");
316 for (option_index = 1; option_index < argc; option_index++)
318 if (lstrcmpiW(argv[option_index], L"/k") == 0 || lstrcmpiW(argv[option_index], L"-k") == 0)
320 ++option_index;
321 if (option_index >= argc)
323 WINE_ERR("Must specify group to initialize\n");
324 return 0;
326 if (!LoadGroup(argv[option_index]))
328 WINE_ERR("Failed to load requested group: %s\n",
329 wine_dbgstr_w(argv[option_index]));
330 return 0;
333 else
335 WINE_FIXME("Unrecognized option: %s\n",
336 wine_dbgstr_w(argv[option_index]));
337 return 0;
341 return 0;