svchost: Add LOAD_WITH_ALTERED_SEARCH_PATH flag when loading the dll.
[wine/multimedia.git] / programs / svchost / svchost.c
blob704510407ca4c9043cad64fc719a033d6601f665
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 strings used throughout svchost */
38 static const WCHAR kd[] = {'-','k',0};
40 static const WCHAR ks[] = {'/','k',0};
42 static const WCHAR reg_separator[] = {'\\',0};
44 static const WCHAR service_reg_path[] = {
45 'S','y','s','t','e','m',
46 '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
47 '\\','S','e','r','v','i','c','e','s',0};
49 static const WCHAR parameters[] = {
50 'P','a','r','a','m','e','t','e','r','s',0};
52 static const WCHAR service_dll[] = {
53 'S','e','r','v','i','c','e','D','l','l',0};
55 static const WCHAR svchost_path[] = {
56 'S','o','f','t','w','a','r','e',
57 '\\','M','i','c','r','o','s','o','f','t',
58 '\\','W','i','n','d','o','w','s',' ','N','T',
59 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
60 '\\','S','v','c','h','o','s','t',0};
62 static const CHAR service_main[] = "ServiceMain";
64 /* Allocate and initialize a WSTR containing the queried value */
65 static LPWSTR GetRegValue(HKEY service_key, const WCHAR *value_name)
67 DWORD type;
68 DWORD reg_size;
69 DWORD size;
70 LONG ret;
71 LPWSTR value;
73 WINE_TRACE("\n");
75 ret = RegQueryValueExW(service_key, value_name, NULL, &type, NULL, &reg_size);
76 if (ret != ERROR_SUCCESS)
78 return NULL;
81 /* Add space for potentially missing NULL terminators in initial alloc.
82 * The worst case REG_MULTI_SZ requires two NULL terminators. */
83 size = reg_size + (2 * sizeof(WCHAR));
84 value = HeapAlloc(GetProcessHeap(), 0, size);
86 ret = RegQueryValueExW(service_key, value_name, NULL, &type,
87 (LPBYTE)value, &reg_size);
88 if (ret != ERROR_SUCCESS)
90 HeapFree(GetProcessHeap(), 0, value);
91 return NULL;
94 /* Explicitly NULL terminate the result */
95 value[size / sizeof(WCHAR) - 1] = '\0';
96 value[size / sizeof(WCHAR) - 2] = '\0';
98 return value;
101 /* Allocate and initialize a WSTR containing the expanded string */
102 static LPWSTR ExpandEnv(LPWSTR string)
104 DWORD size;
105 LPWSTR expanded_string;
107 WINE_TRACE("\n");
109 size = 0;
110 size = ExpandEnvironmentStringsW(string, NULL, size);
111 if (size == 0)
113 WINE_ERR("cannot expand env vars in %s: %u\n",
114 wine_dbgstr_w(string), GetLastError());
115 return NULL;
117 expanded_string = HeapAlloc(GetProcessHeap(), 0,
118 (size + 1) * sizeof(WCHAR));
119 if (ExpandEnvironmentStringsW(string, expanded_string, size) == 0)
121 WINE_ERR("cannot expand env vars in %s: %u\n",
122 wine_dbgstr_w(string), GetLastError());
123 HeapFree(GetProcessHeap(), 0, expanded_string);
124 return NULL;
126 return expanded_string;
129 /* Fill in service table entry for a specified service */
130 static BOOL AddServiceElem(LPWSTR service_name,
131 SERVICE_TABLE_ENTRYW *service_table_entry)
133 LONG ret;
134 HKEY service_hkey = NULL;
135 LPWSTR service_param_key = NULL;
136 LPWSTR dll_name_short = NULL;
137 LPWSTR dll_name_long = NULL;
138 LPSTR dll_service_main = NULL;
139 HMODULE library = NULL;
140 LPSERVICE_MAIN_FUNCTIONW service_main_func = NULL;
141 BOOL success = FALSE;
142 DWORD reg_size;
143 DWORD size;
145 WINE_TRACE("Adding element for %s\n", wine_dbgstr_w(service_name));
147 /* Construct registry path to the service's parameters key */
148 size = (lstrlenW(service_reg_path) + lstrlenW(reg_separator) +
149 lstrlenW(service_name) + lstrlenW(reg_separator) +
150 lstrlenW(parameters) + 1);
151 service_param_key = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
152 lstrcpyW(service_param_key, service_reg_path);
153 lstrcatW(service_param_key, reg_separator);
154 lstrcatW(service_param_key, service_name);
155 lstrcatW(service_param_key, reg_separator);
156 lstrcatW(service_param_key, parameters);
157 service_param_key[size - 1] = '\0';
158 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, service_param_key, 0,
159 KEY_READ, &service_hkey);
160 if (ret != ERROR_SUCCESS)
162 WINE_ERR("cannot open key %s, err=%d\n",
163 wine_dbgstr_w(service_param_key), ret);
164 goto cleanup;
167 /* Find DLL associate with service from key */
168 dll_name_short = GetRegValue(service_hkey, service_dll);
169 if (!dll_name_short)
171 WINE_ERR("cannot find registry value %s for service %s\n",
172 wine_dbgstr_w(service_dll), wine_dbgstr_w(service_name));
173 RegCloseKey(service_hkey);
174 goto cleanup;
177 /* Expand environment variables in ServiceDll name*/
178 dll_name_long = ExpandEnv(dll_name_short);
179 if (!dll_name_long)
181 WINE_ERR("failed to expand string %s\n",
182 wine_dbgstr_w(dll_name_short));
183 RegCloseKey(service_hkey);
184 goto cleanup;
187 /* Look for alternate to default ServiceMain entry point */
188 ret = RegQueryValueExA(service_hkey, service_main, NULL, NULL, NULL, &reg_size);
189 if (ret == ERROR_SUCCESS)
191 /* Add space for potentially missing NULL terminator, allocate, and
192 * fill with the registry value */
193 size = reg_size + 1;
194 dll_service_main = HeapAlloc(GetProcessHeap(), 0, size);
195 ret = RegQueryValueExA(service_hkey, service_main, NULL, NULL,
196 (LPBYTE)dll_service_main, &reg_size);
197 if (ret != ERROR_SUCCESS)
199 RegCloseKey(service_hkey);
200 goto cleanup;
202 dll_service_main[size - 1] = '\0';
204 RegCloseKey(service_hkey);
206 /* Load the DLL and obtain a pointer to ServiceMain entry point */
207 library = LoadLibraryExW(dll_name_long, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
208 if (!library)
210 WINE_ERR("failed to load library %s, err=%u\n",
211 wine_dbgstr_w(dll_name_long), GetLastError());
212 goto cleanup;
214 if (dll_service_main)
216 service_main_func =
217 (LPSERVICE_MAIN_FUNCTIONW) GetProcAddress(library, dll_service_main);
219 else
221 service_main_func =
222 (LPSERVICE_MAIN_FUNCTIONW) GetProcAddress(library, service_main);
224 if (!service_main_func)
226 WINE_ERR("cannot locate ServiceMain procedure in DLL for %s\n",
227 wine_dbgstr_w(service_name));
228 FreeLibrary(library);
229 goto cleanup;
232 if (GetProcAddress(library, "SvchostPushServiceGlobals"))
234 WINE_FIXME("library %s expects undocumented SvchostPushServiceGlobals function to be called\n",
235 wine_dbgstr_w(dll_name_long));
238 /* Fill in the service table entry */
239 service_table_entry->lpServiceName = service_name;
240 service_table_entry->lpServiceProc = service_main_func;
241 success = TRUE;
243 cleanup:
244 HeapFree(GetProcessHeap(), 0, service_param_key);
245 HeapFree(GetProcessHeap(), 0, dll_name_short);
246 HeapFree(GetProcessHeap(), 0, dll_name_long);
247 HeapFree(GetProcessHeap(), 0, dll_service_main);
248 return success;
251 /* Initialize the service table for a list (REG_MULTI_SZ) of services */
252 static BOOL StartGroupServices(LPWSTR services)
254 LPWSTR service_name = NULL;
255 SERVICE_TABLE_ENTRYW *service_table = NULL;
256 DWORD service_count;
257 BOOL ret;
259 /* Count the services to load */
260 service_count = 0;
261 service_name = services;
262 while (*service_name != '\0')
264 ++service_count;
265 service_name = service_name + lstrlenW(service_name);
266 ++service_name;
268 WINE_TRACE("Service group contains %d services\n", service_count);
270 /* Populate the service table */
271 service_table = HeapAlloc(GetProcessHeap(), 0,
272 (service_count + 1) * sizeof(SERVICE_TABLE_ENTRYW));
273 service_count = 0;
274 service_name = services;
275 while (*service_name != '\0')
277 if (!AddServiceElem(service_name, &service_table[service_count]))
279 HeapFree(GetProcessHeap(), 0, service_table);
280 return FALSE;
282 ++service_count;
283 service_name = service_name + lstrlenW(service_name);
284 ++service_name;
286 service_table[service_count].lpServiceName = NULL;
287 service_table[service_count].lpServiceProc = NULL;
289 /* Start the services */
290 if (!(ret = StartServiceCtrlDispatcherW(service_table)))
291 WINE_ERR("StartServiceCtrlDispatcherW failed to start %s: %u\n",
292 wine_dbgstr_w(services), GetLastError());
294 HeapFree(GetProcessHeap(), 0, service_table);
295 return ret;
298 /* Find the list of services associated with a group name and start those
299 * services */
300 static BOOL LoadGroup(PWCHAR group_name)
302 HKEY group_hkey = NULL;
303 LPWSTR services = NULL;
304 LONG ret;
306 WINE_TRACE("Loading service group for %s\n", wine_dbgstr_w(group_name));
308 /* Lookup group_name value of svchost registry entry */
309 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, svchost_path, 0,
310 KEY_READ, &group_hkey);
311 if (ret != ERROR_SUCCESS)
313 WINE_ERR("cannot open key %s, err=%d\n",
314 wine_dbgstr_w(svchost_path), ret);
315 return FALSE;
317 services = GetRegValue(group_hkey, group_name);
318 RegCloseKey(group_hkey);
319 if (!services)
321 WINE_ERR("cannot find registry value %s in %s\n",
322 wine_dbgstr_w(group_name), wine_dbgstr_w(svchost_path));
323 return FALSE;
326 /* Start services */
327 if (!(ret = StartGroupServices(services)))
328 WINE_TRACE("Failed to start service group\n");
330 HeapFree(GetProcessHeap(), 0, services);
331 return ret;
334 /* Load svchost group specified on the command line via the /k option */
335 int wmain(int argc, WCHAR *argv[])
337 int option_index;
339 WINE_TRACE("\n");
341 for (option_index = 1; option_index < argc; option_index++)
343 if (lstrcmpiW(argv[option_index], ks) == 0 ||
344 lstrcmpiW(argv[option_index], kd) == 0)
346 ++option_index;
347 if (option_index >= argc)
349 WINE_ERR("Must specify group to initialize\n");
350 return 0;
352 if (!LoadGroup(argv[option_index]))
354 WINE_ERR("Failed to load requested group: %s\n",
355 wine_dbgstr_w(argv[option_index]));
356 return 0;
359 else
361 WINE_FIXME("Unrecognized option: %s\n",
362 wine_dbgstr_w(argv[option_index]));
363 return 0;
367 return 0;