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