svchost.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /*
  2. * Implementation of svchost.exe
  3. *
  4. * Copyright 2007 Google (Roy Shea)
  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. /* Usage:
  21. * Starting a service group:
  22. *
  23. * svchost /k service_group_name
  24. */
  25. #include <stdarg.h>
  26. #include "windef.h"
  27. #include "winbase.h"
  28. #include "winreg.h"
  29. #include "winsvc.h"
  30. #include "wine/debug.h"
  31. WINE_DEFAULT_DEBUG_CHANNEL(svchost);
  32. static const WCHAR service_reg_path[] = L"System\\CurrentControlSet\\Services";
  33. static const WCHAR svchost_path[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Svchost";
  34. /* Allocate and initialize a WSTR containing the queried value */
  35. static LPWSTR GetRegValue(HKEY service_key, const WCHAR *value_name)
  36. {
  37. DWORD type;
  38. DWORD reg_size;
  39. DWORD size;
  40. LONG ret;
  41. LPWSTR value;
  42. WINE_TRACE("\n");
  43. ret = RegQueryValueExW(service_key, value_name, NULL, &type, NULL, &reg_size);
  44. if (ret != ERROR_SUCCESS)
  45. {
  46. return NULL;
  47. }
  48. /* Add space for potentially missing NULL terminators in initial alloc.
  49. * The worst case REG_MULTI_SZ requires two NULL terminators. */
  50. size = reg_size + (2 * sizeof(WCHAR));
  51. value = HeapAlloc(GetProcessHeap(), 0, size);
  52. ret = RegQueryValueExW(service_key, value_name, NULL, &type,
  53. (LPBYTE)value, &reg_size);
  54. if (ret != ERROR_SUCCESS)
  55. {
  56. HeapFree(GetProcessHeap(), 0, value);
  57. return NULL;
  58. }
  59. /* Explicitly NULL terminate the result */
  60. value[size / sizeof(WCHAR) - 1] = '\0';
  61. value[size / sizeof(WCHAR) - 2] = '\0';
  62. return value;
  63. }
  64. /* Allocate and initialize a WSTR containing the expanded string */
  65. static LPWSTR ExpandEnv(LPWSTR string)
  66. {
  67. DWORD size;
  68. LPWSTR expanded_string;
  69. WINE_TRACE("\n");
  70. size = 0;
  71. size = ExpandEnvironmentStringsW(string, NULL, size);
  72. if (size == 0)
  73. {
  74. WINE_ERR("cannot expand env vars in %s: %u\n",
  75. wine_dbgstr_w(string), GetLastError());
  76. return NULL;
  77. }
  78. expanded_string = HeapAlloc(GetProcessHeap(), 0,
  79. (size + 1) * sizeof(WCHAR));
  80. if (ExpandEnvironmentStringsW(string, expanded_string, size) == 0)
  81. {
  82. WINE_ERR("cannot expand env vars in %s: %u\n",
  83. wine_dbgstr_w(string), GetLastError());
  84. HeapFree(GetProcessHeap(), 0, expanded_string);
  85. return NULL;
  86. }
  87. return expanded_string;
  88. }
  89. /* Fill in service table entry for a specified service */
  90. static BOOL AddServiceElem(LPWSTR service_name,
  91. SERVICE_TABLE_ENTRYW *service_table_entry)
  92. {
  93. LONG ret;
  94. HKEY service_hkey = NULL;
  95. LPWSTR service_param_key = NULL;
  96. LPWSTR dll_name_short = NULL;
  97. LPWSTR dll_name_long = NULL;
  98. LPSTR dll_service_main = NULL;
  99. HMODULE library = NULL;
  100. LPSERVICE_MAIN_FUNCTIONW service_main_func = NULL;
  101. BOOL success = FALSE;
  102. DWORD reg_size;
  103. DWORD size;
  104. WINE_TRACE("Adding element for %s\n", wine_dbgstr_w(service_name));
  105. /* Construct registry path to the service's parameters key */
  106. size = lstrlenW(service_reg_path) + lstrlenW(L"\\") + lstrlenW(service_name) + lstrlenW(L"\\") +
  107. lstrlenW(L"Parameters") + 1;
  108. service_param_key = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
  109. lstrcpyW(service_param_key, service_reg_path);
  110. lstrcatW(service_param_key, L"\\");
  111. lstrcatW(service_param_key, service_name);
  112. lstrcatW(service_param_key, L"\\");
  113. lstrcatW(service_param_key, L"Parameters");
  114. service_param_key[size - 1] = '\0';
  115. ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, service_param_key, 0,
  116. KEY_READ, &service_hkey);
  117. if (ret != ERROR_SUCCESS)
  118. {
  119. WINE_ERR("cannot open key %s, err=%d\n",
  120. wine_dbgstr_w(service_param_key), ret);
  121. goto cleanup;
  122. }
  123. /* Find DLL associate with service from key */
  124. dll_name_short = GetRegValue(service_hkey, L"ServiceDll");
  125. if (!dll_name_short)
  126. {
  127. WINE_ERR("cannot find registry value ServiceDll for service %s\n",
  128. wine_dbgstr_w(service_name));
  129. RegCloseKey(service_hkey);
  130. goto cleanup;
  131. }
  132. /* Expand environment variables in ServiceDll name*/
  133. dll_name_long = ExpandEnv(dll_name_short);
  134. if (!dll_name_long)
  135. {
  136. WINE_ERR("failed to expand string %s\n",
  137. wine_dbgstr_w(dll_name_short));
  138. RegCloseKey(service_hkey);
  139. goto cleanup;
  140. }
  141. /* Look for alternate to default ServiceMain entry point */
  142. ret = RegQueryValueExA(service_hkey, "ServiceMain", NULL, NULL, NULL, &reg_size);
  143. if (ret == ERROR_SUCCESS)
  144. {
  145. /* Add space for potentially missing NULL terminator, allocate, and
  146. * fill with the registry value */
  147. size = reg_size + 1;
  148. dll_service_main = HeapAlloc(GetProcessHeap(), 0, size);
  149. ret = RegQueryValueExA(service_hkey, "ServiceMain", NULL, NULL,
  150. (LPBYTE)dll_service_main, &reg_size);
  151. if (ret != ERROR_SUCCESS)
  152. {
  153. RegCloseKey(service_hkey);
  154. goto cleanup;
  155. }
  156. dll_service_main[size - 1] = '\0';
  157. }
  158. RegCloseKey(service_hkey);
  159. /* Load the DLL and obtain a pointer to ServiceMain entry point */
  160. library = LoadLibraryExW(dll_name_long, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
  161. if (!library)
  162. {
  163. WINE_ERR("failed to load library %s, err=%u\n",
  164. wine_dbgstr_w(dll_name_long), GetLastError());
  165. goto cleanup;
  166. }
  167. if (dll_service_main)
  168. {
  169. service_main_func =
  170. (LPSERVICE_MAIN_FUNCTIONW) GetProcAddress(library, dll_service_main);
  171. }
  172. else
  173. {
  174. service_main_func =
  175. (LPSERVICE_MAIN_FUNCTIONW) GetProcAddress(library, "ServiceMain");
  176. }
  177. if (!service_main_func)
  178. {
  179. WINE_ERR("cannot locate ServiceMain procedure in DLL for %s\n",
  180. wine_dbgstr_w(service_name));
  181. FreeLibrary(library);
  182. goto cleanup;
  183. }
  184. if (GetProcAddress(library, "SvchostPushServiceGlobals"))
  185. {
  186. WINE_FIXME("library %s expects undocumented SvchostPushServiceGlobals function to be called\n",
  187. wine_dbgstr_w(dll_name_long));
  188. }
  189. /* Fill in the service table entry */
  190. service_table_entry->lpServiceName = service_name;
  191. service_table_entry->lpServiceProc = service_main_func;
  192. success = TRUE;
  193. cleanup:
  194. HeapFree(GetProcessHeap(), 0, service_param_key);
  195. HeapFree(GetProcessHeap(), 0, dll_name_short);
  196. HeapFree(GetProcessHeap(), 0, dll_name_long);
  197. HeapFree(GetProcessHeap(), 0, dll_service_main);
  198. return success;
  199. }
  200. /* Initialize the service table for a list (REG_MULTI_SZ) of services */
  201. static BOOL StartGroupServices(LPWSTR services)
  202. {
  203. LPWSTR service_name = NULL;
  204. SERVICE_TABLE_ENTRYW *service_table = NULL;
  205. DWORD service_count;
  206. BOOL ret;
  207. /* Count the services to load */
  208. service_count = 0;
  209. service_name = services;
  210. while (*service_name != '\0')
  211. {
  212. ++service_count;
  213. service_name = service_name + lstrlenW(service_name);
  214. ++service_name;
  215. }
  216. WINE_TRACE("Service group contains %d services\n", service_count);
  217. /* Populate the service table */
  218. service_table = HeapAlloc(GetProcessHeap(), 0,
  219. (service_count + 1) * sizeof(SERVICE_TABLE_ENTRYW));
  220. service_count = 0;
  221. service_name = services;
  222. while (*service_name != '\0')
  223. {
  224. if (!AddServiceElem(service_name, &service_table[service_count]))
  225. {
  226. HeapFree(GetProcessHeap(), 0, service_table);
  227. return FALSE;
  228. }
  229. ++service_count;
  230. service_name = service_name + lstrlenW(service_name);
  231. ++service_name;
  232. }
  233. service_table[service_count].lpServiceName = NULL;
  234. service_table[service_count].lpServiceProc = NULL;
  235. /* Start the services */
  236. if (!(ret = StartServiceCtrlDispatcherW(service_table)))
  237. WINE_ERR("StartServiceCtrlDispatcherW failed to start %s: %u\n",
  238. wine_dbgstr_w(services), GetLastError());
  239. HeapFree(GetProcessHeap(), 0, service_table);
  240. return ret;
  241. }
  242. /* Find the list of services associated with a group name and start those
  243. * services */
  244. static BOOL LoadGroup(PWCHAR group_name)
  245. {
  246. HKEY group_hkey = NULL;
  247. LPWSTR services = NULL;
  248. LONG ret;
  249. WINE_TRACE("Loading service group for %s\n", wine_dbgstr_w(group_name));
  250. /* Lookup group_name value of svchost registry entry */
  251. ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, svchost_path, 0,
  252. KEY_READ, &group_hkey);
  253. if (ret != ERROR_SUCCESS)
  254. {
  255. WINE_ERR("cannot open key %s, err=%d\n",
  256. wine_dbgstr_w(svchost_path), ret);
  257. return FALSE;
  258. }
  259. services = GetRegValue(group_hkey, group_name);
  260. RegCloseKey(group_hkey);
  261. if (!services)
  262. {
  263. WINE_ERR("cannot find registry value %s in %s\n",
  264. wine_dbgstr_w(group_name), wine_dbgstr_w(svchost_path));
  265. return FALSE;
  266. }
  267. /* Start services */
  268. if (!(ret = StartGroupServices(services)))
  269. WINE_TRACE("Failed to start service group\n");
  270. HeapFree(GetProcessHeap(), 0, services);
  271. return ret;
  272. }
  273. /* Load svchost group specified on the command line via the /k option */
  274. int __cdecl wmain(int argc, WCHAR *argv[])
  275. {
  276. int option_index;
  277. WINE_TRACE("\n");
  278. for (option_index = 1; option_index < argc; option_index++)
  279. {
  280. if (lstrcmpiW(argv[option_index], L"/k") == 0 || lstrcmpiW(argv[option_index], L"-k") == 0)
  281. {
  282. ++option_index;
  283. if (option_index >= argc)
  284. {
  285. WINE_ERR("Must specify group to initialize\n");
  286. return 0;
  287. }
  288. if (!LoadGroup(argv[option_index]))
  289. {
  290. WINE_ERR("Failed to load requested group: %s\n",
  291. wine_dbgstr_w(argv[option_index]));
  292. return 0;
  293. }
  294. }
  295. else
  296. {
  297. WINE_FIXME("Unrecognized option: %s\n",
  298. wine_dbgstr_w(argv[option_index]));
  299. return 0;
  300. }
  301. }
  302. return 0;
  303. }