- Sync with trunk r58248 to bring the latest changes from Amine (headers) and others...
[reactos.git] / base / services / svchost / svchost.c
1 /*
2 * PROJECT: ReactOS SvcHost
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: /base/services/svchost/svchost.c
5 * PURPOSE: Provide dll service loader
6 * PROGRAMMERS: Gregor Brunmar (gregor.brunmar@home.se)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "svchost.h"
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* DEFINES *******************************************************************/
17
18 static LPCTSTR SVCHOST_REG_KEY = _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\SvcHost");
19 static LPCTSTR SERVICE_KEY = _T("SYSTEM\\CurrentControlSet\\Services\\");
20 static LPCTSTR PARAMETERS_KEY = _T("\\Parameters");
21
22 #define SERVICE_KEY_LENGTH _tcslen(SERVICE_KEY);
23 #define REG_MAX_DATA_SIZE 2048
24
25 static PSERVICE FirstService = NULL;
26
27 /* FUNCTIONS *****************************************************************/
28
29 BOOL PrepareService(LPCTSTR ServiceName)
30 {
31 HKEY hServiceKey;
32 TCHAR ServiceKeyBuffer[MAX_PATH + 1];
33 DWORD LeftOfBuffer = sizeof(ServiceKeyBuffer) / sizeof(ServiceKeyBuffer[0]);
34 DWORD KeyType;
35 PTSTR Buffer = NULL;
36 DWORD BufferSize = MAX_PATH + 1;
37 LONG RetVal;
38 HINSTANCE hServiceDll;
39 TCHAR DllPath[MAX_PATH + 2]; /* See MSDN on ExpandEnvironmentStrings() for ANSI strings for more details on + 2 */
40 LPSERVICE_MAIN_FUNCTION ServiceMainFunc;
41 PSERVICE Service;
42
43 /* Compose the registry path to the service's "Parameter" key */
44 _tcsncpy(ServiceKeyBuffer, SERVICE_KEY, LeftOfBuffer);
45 LeftOfBuffer -= _tcslen(SERVICE_KEY);
46 _tcsncat(ServiceKeyBuffer, ServiceName, LeftOfBuffer);
47 LeftOfBuffer -= _tcslen(ServiceName);
48 _tcsncat(ServiceKeyBuffer, PARAMETERS_KEY, LeftOfBuffer);
49 LeftOfBuffer -= _tcslen(PARAMETERS_KEY);
50
51 if (LeftOfBuffer < 0)
52 {
53 DPRINT1("Buffer overflow for service name: '%s'\n", ServiceName);
54 return FALSE;
55 }
56
57 /* Open the service registry key to find the dll name */
58 if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, ServiceKeyBuffer, 0, KEY_READ, &hServiceKey))
59 {
60 DPRINT1("Could not open service key (%s)\n", ServiceKeyBuffer);
61 return FALSE;
62 }
63
64 do
65 {
66 if (Buffer)
67 HeapFree(GetProcessHeap(), 0, Buffer);
68
69 Buffer = HeapAlloc(GetProcessHeap(), 0, BufferSize);
70 if (NULL == Buffer)
71 {
72 DPRINT1("Not enough memory for service: %s\n", ServiceName);
73 return FALSE;
74 }
75
76 RetVal = RegQueryValueEx(hServiceKey, _T("ServiceDll"), NULL, &KeyType, (LPBYTE)Buffer, &BufferSize);
77
78 } while (ERROR_MORE_DATA == RetVal);
79
80
81 RegCloseKey(hServiceKey);
82
83 if (ERROR_SUCCESS != RetVal || 0 == BufferSize)
84 {
85 DPRINT1("Could not read 'ServiceDll' value from service: %s, ErrorCode: 0x%x\n", ServiceName, RetVal);
86 HeapFree(GetProcessHeap(), 0, Buffer);
87 return FALSE;
88 }
89
90 /* Convert possible %SystemRoot% to a real path */
91 BufferSize = ExpandEnvironmentStrings(Buffer, DllPath, _countof(DllPath));
92 if (0 == BufferSize)
93 {
94 DPRINT1("Invalid ServiceDll path: %s\n", Buffer);
95 HeapFree(GetProcessHeap(), 0, Buffer);
96 return FALSE;
97 }
98
99 HeapFree(GetProcessHeap(), 0, Buffer);
100
101 /* Load the service dll */
102 DPRINT("Trying to load dll\n");
103 hServiceDll = LoadLibrary(DllPath);
104
105 if (NULL == hServiceDll)
106 {
107 DPRINT1("Unable to load ServiceDll: %s, ErrorCode: %u\n", DllPath, GetLastError());
108 return FALSE;
109 }
110
111 ServiceMainFunc = (LPSERVICE_MAIN_FUNCTION)GetProcAddress(hServiceDll, "ServiceMain");
112
113 /* Allocate a service node in the linked list */
114 Service = HeapAlloc(GetProcessHeap(), 0, sizeof(SERVICE));
115 if (NULL == Service)
116 {
117 DPRINT1("Not enough memory for service: %s\n", ServiceName);
118 return FALSE;
119 }
120
121 memset(Service, 0, sizeof(SERVICE));
122 Service->Name = HeapAlloc(GetProcessHeap(), 0, (_tcslen(ServiceName)+1) * sizeof(TCHAR));
123 if (NULL == Service->Name)
124 {
125 DPRINT1("Not enough memory for service: %s\n", ServiceName);
126 HeapFree(GetProcessHeap(), 0, Service);
127 return FALSE;
128 }
129 _tcscpy(Service->Name, ServiceName);
130
131 Service->hServiceDll = hServiceDll;
132 Service->ServiceMainFunc = ServiceMainFunc;
133
134 Service->Next = FirstService;
135 FirstService = Service;
136
137 return TRUE;
138 }
139
140 VOID FreeServices(VOID)
141 {
142 while (FirstService)
143 {
144 PSERVICE Service = FirstService;
145 FirstService = Service->Next;
146
147 FreeLibrary(Service->hServiceDll);
148
149 HeapFree(GetProcessHeap(), 0, Service->Name);
150 HeapFree(GetProcessHeap(), 0, Service);
151 }
152 }
153
154 /*
155 * Returns the number of services successfully loaded from the category
156 */
157 DWORD LoadServiceCategory(LPCTSTR ServiceCategory)
158 {
159 HKEY hServicesKey;
160 DWORD KeyType;
161 DWORD BufferSize = REG_MAX_DATA_SIZE;
162 TCHAR Buffer[REG_MAX_DATA_SIZE];
163 LPCTSTR ServiceName;
164 DWORD BufferIndex = 0;
165 DWORD NrOfServices = 0;
166
167 /* Get all the services in this category */
168 if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, SVCHOST_REG_KEY, 0, KEY_READ, &hServicesKey))
169 {
170 DPRINT1("Could not open service category: %s\n", ServiceCategory);
171 return 0;
172 }
173
174 if (ERROR_SUCCESS != RegQueryValueEx(hServicesKey, ServiceCategory, NULL, &KeyType, (LPBYTE)Buffer, &BufferSize))
175 {
176 DPRINT1("Could not open service category (2): %s\n", ServiceCategory);
177 RegCloseKey(hServicesKey);
178 return 0;
179 }
180
181 /* Clean up */
182 RegCloseKey(hServicesKey);
183
184 /* Load services in the category */
185 ServiceName = Buffer;
186 while (_T('\0') != ServiceName[0])
187 {
188 size_t Length;
189
190 Length = _tcslen(ServiceName);
191 if (0 == Length)
192 break;
193
194 if (TRUE == PrepareService(ServiceName))
195 ++NrOfServices;
196
197 BufferIndex += Length + 1;
198
199 ServiceName = &Buffer[BufferIndex];
200 }
201
202 return NrOfServices;
203 }
204
205 int _tmain (int argc, LPTSTR argv [])
206 {
207 DWORD NrOfServices;
208 LPSERVICE_TABLE_ENTRY ServiceTable;
209
210 if (argc < 3)
211 {
212 /* MS svchost.exe doesn't seem to print help, should we? */
213 return 0;
214 }
215
216 if (_tcscmp(argv[1], _T("-k")) != 0)
217 {
218 /* For now, we only handle "-k" */
219 return 0;
220 }
221
222 NrOfServices = LoadServiceCategory(argv[2]);
223
224 DPRINT("NrOfServices: %lu\n", NrOfServices);
225 if (0 == NrOfServices)
226 return 0;
227
228 ServiceTable = HeapAlloc(GetProcessHeap(), 0, sizeof(SERVICE_TABLE_ENTRY) * (NrOfServices + 1));
229
230 if (NULL != ServiceTable)
231 {
232 DWORD i;
233 PSERVICE Service = FirstService;
234
235 /* Fill the service table */
236 for (i = 0; i < NrOfServices; i++)
237 {
238 DPRINT("Loading service: %s\n", Service->Name);
239 ServiceTable[i].lpServiceName = Service->Name;
240 ServiceTable[i].lpServiceProc = Service->ServiceMainFunc;
241 Service = Service->Next;
242 }
243
244 /* Set a NULL entry to end the service table */
245 ServiceTable[i].lpServiceName = NULL;
246 ServiceTable[i].lpServiceProc = NULL;
247
248 if (FALSE == StartServiceCtrlDispatcher(ServiceTable))
249 DPRINT1("Failed to start service control dispatcher, ErrorCode: %lu\n", GetLastError());
250
251 HeapFree(GetProcessHeap(), 0, ServiceTable);
252 }
253 else
254 {
255 DPRINT1("Not enough memory for the service table, trying to allocate %u bytes\n", sizeof(SERVICE_TABLE_ENTRY) * (NrOfServices + 1));
256 }
257
258 DPRINT("Freeing services...\n");
259 FreeServices();
260
261 return 0;
262 }
263
264 /* EOF */