VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp@ 44248

Last change on this file since 44248 was 44114, checked in by vboxsync, 12 years ago

VBoxService-win: Fixed SCM hangs on Windows 2000 guests.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.8 KB
Line 
1/* $Id: VBoxService-win.cpp 44114 2012-12-13 09:21:40Z vboxsync $ */
2/** @file
3 * VBoxService - Guest Additions Service Skeleton, Windows Specific Parts.
4 */
5
6/*
7 * Copyright (C) 2009-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <iprt/assert.h>
23#include <iprt/err.h>
24#include <iprt/system.h> /* For querying OS version. */
25#include <VBox/VBoxGuestLib.h>
26#include "VBoxServiceInternal.h"
27
28#include <Windows.h>
29#include <process.h>
30#include <aclapi.h>
31
32
33/*******************************************************************************
34* Internal Functions *
35*******************************************************************************/
36static void WINAPI vboxServiceWinMain(DWORD argc, LPTSTR *argv);
37
38
39/*******************************************************************************
40* Global Variables *
41*******************************************************************************/
42static DWORD g_dwWinServiceLastStatus = 0;
43SERVICE_STATUS_HANDLE g_hWinServiceStatus = NULL;
44/** The semaphore for the dummy Windows service. */
45static RTSEMEVENT g_WindowsEvent = NIL_RTSEMEVENT;
46
47static SERVICE_TABLE_ENTRY const g_aServiceTable[] =
48{
49 { VBOXSERVICE_NAME, vboxServiceWinMain },
50 { NULL, NULL}
51};
52
53
54/**
55 * @todo Format code style.
56 * @todo Add full unicode support.
57 * @todo Add event log capabilities / check return values.
58 */
59static DWORD vboxServiceWinAddAceToObjectsSecurityDescriptor(LPTSTR pszObjName,
60 SE_OBJECT_TYPE ObjectType,
61 LPTSTR pszTrustee,
62 TRUSTEE_FORM TrusteeForm,
63 DWORD dwAccessRights,
64 ACCESS_MODE AccessMode,
65 DWORD dwInheritance)
66{
67 DWORD dwRes = 0;
68 PACL pOldDACL = NULL, pNewDACL = NULL;
69 PSECURITY_DESCRIPTOR pSD = NULL;
70 EXPLICIT_ACCESS ea;
71
72 if (NULL == pszObjName)
73 return ERROR_INVALID_PARAMETER;
74
75 /* Get a pointer to the existing DACL. */
76 dwRes = GetNamedSecurityInfo(pszObjName, ObjectType,
77 DACL_SECURITY_INFORMATION,
78 NULL, NULL, &pOldDACL, NULL, &pSD);
79 if (ERROR_SUCCESS != dwRes)
80 {
81 if (dwRes == ERROR_FILE_NOT_FOUND)
82 VBoxServiceError("AddAceToObjectsSecurityDescriptor: Object not found/installed: %s\n", pszObjName);
83 else
84 VBoxServiceError("AddAceToObjectsSecurityDescriptor: GetNamedSecurityInfo: Error %u\n", dwRes);
85 goto l_Cleanup;
86 }
87
88 /* Initialize an EXPLICIT_ACCESS structure for the new ACE. */
89 ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
90 ea.grfAccessPermissions = dwAccessRights;
91 ea.grfAccessMode = AccessMode;
92 ea.grfInheritance= dwInheritance;
93 ea.Trustee.TrusteeForm = TrusteeForm;
94 ea.Trustee.ptstrName = pszTrustee;
95
96 /* Create a new ACL that merges the new ACE into the existing DACL. */
97 dwRes = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL);
98 if (ERROR_SUCCESS != dwRes)
99 {
100 VBoxServiceError("AddAceToObjectsSecurityDescriptor: SetEntriesInAcl: Error %u\n", dwRes);
101 goto l_Cleanup;
102 }
103
104 /* Attach the new ACL as the object's DACL. */
105 dwRes = SetNamedSecurityInfo(pszObjName, ObjectType,
106 DACL_SECURITY_INFORMATION,
107 NULL, NULL, pNewDACL, NULL);
108 if (ERROR_SUCCESS != dwRes)
109 {
110 VBoxServiceError("AddAceToObjectsSecurityDescriptor: SetNamedSecurityInfo: Error %u\n", dwRes);
111 goto l_Cleanup;
112 }
113
114 /** @todo get rid of that spaghetti jump ... */
115l_Cleanup:
116
117 if(pSD != NULL)
118 LocalFree((HLOCAL) pSD);
119 if(pNewDACL != NULL)
120 LocalFree((HLOCAL) pNewDACL);
121
122 return dwRes;
123}
124
125
126/** Reports our current status to the SCM. */
127static BOOL vboxServiceWinSetStatus(DWORD dwStatus, DWORD dwCheckPoint)
128{
129 if (g_hWinServiceStatus == NULL) /* Program could be in testing mode, so no service environment available. */
130 return FALSE;
131
132 VBoxServiceVerbose(2, "Setting service status to: %ld\n", dwStatus);
133 g_dwWinServiceLastStatus = dwStatus;
134
135 SERVICE_STATUS ss;
136 RT_ZERO(ss);
137
138 ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
139 ss.dwCurrentState = dwStatus;
140 /* Don't accept controls when in start pending state. */
141 if (ss.dwCurrentState != SERVICE_START_PENDING)
142 {
143 ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
144#ifndef TARGET_NT4
145 /* Don't use SERVICE_ACCEPT_SESSIONCHANGE on Windows 2000.
146 * This makes SCM angry. */
147 char szOSVersion[32];
148 int rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE,
149 szOSVersion, sizeof(szOSVersion));
150 if (RT_SUCCESS(rc))
151 {
152 if (RTStrVersionCompare(szOSVersion, "5.1") >= 0)
153 ss.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE;
154 }
155 else
156 VBoxServiceError("Error determining OS version, rc=%Rrc\n", rc);
157#endif
158 }
159
160 ss.dwWin32ExitCode = NO_ERROR;
161 ss.dwServiceSpecificExitCode = 0; /* Not used */
162 ss.dwCheckPoint = dwCheckPoint;
163 ss.dwWaitHint = 3000;
164
165 BOOL fStatusSet = SetServiceStatus(g_hWinServiceStatus, &ss);
166 if (!fStatusSet)
167 VBoxServiceError("Error reporting service status=%ld (controls=%x, checkpoint=%ld) to SCM: %ld\n",
168 dwStatus, ss.dwControlsAccepted, dwCheckPoint, GetLastError());
169 return fStatusSet;
170}
171
172
173/**
174 * Reports SERVICE_STOP_PENDING to SCM.
175 *
176 * @param uCheckPoint Some number.
177 */
178void VBoxServiceWinSetStopPendingStatus(uint32_t uCheckPoint)
179{
180 vboxServiceWinSetStatus(SERVICE_STOP_PENDING, uCheckPoint);
181}
182
183
184static RTEXITCODE vboxServiceWinSetDesc(SC_HANDLE hService)
185{
186#ifndef TARGET_NT4
187 /* On W2K+ there's ChangeServiceConfig2() which lets us set some fields
188 like a longer service description. */
189 /** @todo On Vista+ SERVICE_DESCRIPTION also supports localized strings! */
190 SERVICE_DESCRIPTION desc;
191 desc.lpDescription = VBOXSERVICE_DESCRIPTION;
192 if (!ChangeServiceConfig2(hService,
193 SERVICE_CONFIG_DESCRIPTION, /* Service info level */
194 &desc))
195 {
196 VBoxServiceError("Cannot set the service description! Error: %ld\n", GetLastError());
197 return RTEXITCODE_FAILURE;
198 }
199#endif
200 return RTEXITCODE_SUCCESS;
201}
202
203
204/**
205 * Installs the service.
206 */
207RTEXITCODE VBoxServiceWinInstall(void)
208{
209 VBoxServiceVerbose(1, "Installing service ...\n");
210
211 TCHAR imagePath[MAX_PATH] = { 0 };
212 GetModuleFileName(NULL, imagePath, sizeof(imagePath));
213
214 SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
215 if (hSCManager == NULL)
216 {
217 VBoxServiceError("Could not open SCM! Error: %ld\n", GetLastError());
218 return RTEXITCODE_FAILURE;
219 }
220
221 RTEXITCODE rc = RTEXITCODE_SUCCESS;
222 SC_HANDLE hService = CreateService(hSCManager,
223 VBOXSERVICE_NAME, VBOXSERVICE_FRIENDLY_NAME,
224 SERVICE_ALL_ACCESS,
225 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
226 SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
227 imagePath, NULL, NULL, NULL, NULL, NULL);
228 if (hService != NULL)
229 VBoxServiceVerbose(0, "Service successfully installed!\n");
230 else
231 {
232 DWORD dwErr = GetLastError();
233 switch (dwErr)
234 {
235 case ERROR_SERVICE_EXISTS:
236 VBoxServiceVerbose(1, "Service already exists, just updating the service config.\n");
237 hService = OpenService(hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS);
238 if (hService)
239 {
240 if (ChangeServiceConfig (hService,
241 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
242 SERVICE_DEMAND_START,
243 SERVICE_ERROR_NORMAL,
244 imagePath,
245 NULL,
246 NULL,
247 NULL,
248 NULL,
249 NULL,
250 VBOXSERVICE_FRIENDLY_NAME))
251 VBoxServiceVerbose(1, "The service config has been successfully updated.\n");
252 else
253 rc = VBoxServiceError("Could not change service config! Error: %ld\n", GetLastError());
254 }
255 else
256 rc = VBoxServiceError("Could not open service! Error: %ld\n", GetLastError());
257 break;
258
259 default:
260 rc = VBoxServiceError("Could not create service! Error: %ld\n", dwErr);
261 break;
262 }
263 }
264
265 if (rc == RTEXITCODE_SUCCESS)
266 rc = vboxServiceWinSetDesc(hService);
267
268 CloseServiceHandle(hService);
269 CloseServiceHandle(hSCManager);
270 return rc;
271}
272
273/**
274 * Uninstalls the service.
275 */
276RTEXITCODE VBoxServiceWinUninstall(void)
277{
278 VBoxServiceVerbose(1, "Uninstalling service ...\n");
279
280 SC_HANDLE hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
281 if (hSCManager == NULL)
282 {
283 VBoxServiceError("Could not open SCM! Error: %d\n", GetLastError());
284 return RTEXITCODE_FAILURE;
285 }
286
287 RTEXITCODE rcExit;
288 SC_HANDLE hService = OpenService(hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS );
289 if (hService != NULL)
290 {
291 if (DeleteService(hService))
292 {
293 /*
294 * ???
295 */
296 HKEY hKey = NULL;
297 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
298 "SYSTEM\\CurrentControlSet\\Services\\EventLog\\System",
299 0,
300 KEY_ALL_ACCESS,
301 &hKey)
302 == ERROR_SUCCESS)
303 {
304 RegDeleteKey(hKey, VBOXSERVICE_NAME);
305 RegCloseKey(hKey);
306 }
307
308 VBoxServiceVerbose(0, "Service successfully uninstalled!\n");
309 rcExit = RTEXITCODE_SUCCESS;
310 }
311 else
312 rcExit = VBoxServiceError("Could not remove service! Error: %d\n", GetLastError());
313 CloseServiceHandle(hService);
314 }
315 else
316 rcExit = VBoxServiceError("Could not open service! Error: %d\n", GetLastError());
317 CloseServiceHandle(hSCManager);
318
319 return rcExit;
320}
321
322
323static int vboxServiceWinStart(void)
324{
325 int rc = VINF_SUCCESS;
326
327#ifndef TARGET_NT4
328 /* Create a well-known SID for the "Builtin Users" group. */
329 PSID pBuiltinUsersSID = NULL;
330 SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_LOCAL_SID_AUTHORITY;
331
332 if (!AllocateAndInitializeSid(&SIDAuthWorld, 1,
333 SECURITY_LOCAL_RID,
334 0, 0, 0, 0, 0, 0, 0,
335 &pBuiltinUsersSID))
336 {
337 rc = RTErrConvertFromWin32(GetLastError());
338 }
339 else
340 {
341 DWORD dwRes = vboxServiceWinAddAceToObjectsSecurityDescriptor(TEXT("\\\\.\\VBoxMiniRdrDN"),
342 SE_FILE_OBJECT,
343 (LPTSTR)pBuiltinUsersSID,
344 TRUSTEE_IS_SID,
345 FILE_GENERIC_READ | FILE_GENERIC_WRITE,
346 SET_ACCESS,
347 NO_INHERITANCE);
348 if (dwRes != ERROR_SUCCESS)
349 {
350 if (dwRes == ERROR_FILE_NOT_FOUND)
351 {
352 /* If we don't find our "VBoxMiniRdrDN" (for Shared Folders) object above,
353 don't report an error; it just might be not installed. Otherwise this
354 would cause the SCM to hang on starting up the service. */
355 rc = VINF_SUCCESS;
356 }
357 else
358 rc = RTErrConvertFromWin32(dwRes);
359 }
360 }
361#endif
362
363 if (RT_SUCCESS(rc))
364 {
365 vboxServiceWinSetStatus(SERVICE_START_PENDING, 0);
366
367 rc = VBoxServiceStartServices();
368 if (RT_SUCCESS(rc))
369 {
370 vboxServiceWinSetStatus(SERVICE_RUNNING, 0);
371 VBoxServiceMainWait();
372 }
373 else
374 {
375 vboxServiceWinSetStatus(SERVICE_STOPPED, 0);
376#if 0 /** @todo r=bird: Enable this if SERVICE_CONTROL_STOP isn't triggered automatically */
377 VBoxServiceStopServices();
378#endif
379 }
380 }
381 else
382 vboxServiceWinSetStatus(SERVICE_STOPPED, 0);
383
384 if (RT_FAILURE(rc))
385 VBoxServiceError("Service failed to start with rc=%Rrc!\n", rc);
386
387 return rc;
388}
389
390
391/**
392 * Call StartServiceCtrlDispatcher.
393 *
394 * The main() thread invokes this when not started in foreground mode. It
395 * won't return till the service is being shutdown (unless start up fails).
396 *
397 * @returns RTEXITCODE_SUCCESS on normal return after service shutdown.
398 * Something else on failure, error will have been reported.
399 */
400RTEXITCODE VBoxServiceWinEnterCtrlDispatcher(void)
401{
402 if (!StartServiceCtrlDispatcher(&g_aServiceTable[0]))
403 return VBoxServiceError("StartServiceCtrlDispatcher: %u. Please start %s with option -f (foreground)!\n",
404 GetLastError(), g_pszProgName);
405 return RTEXITCODE_SUCCESS;
406}
407
408
409#ifndef TARGET_NT4
410static const char* vboxServiceWTSStateToString(DWORD dwEvent)
411{
412 switch (dwEvent)
413 {
414 case WTS_CONSOLE_CONNECT:
415 return "A session was connected to the console terminal";
416
417 case WTS_CONSOLE_DISCONNECT:
418 return "A session was disconnected from the console terminal";
419
420 case WTS_REMOTE_CONNECT:
421 return "A session connected to the remote terminal";
422
423 case WTS_REMOTE_DISCONNECT:
424 return "A session was disconnected from the remote terminal";
425
426 case WTS_SESSION_LOGON:
427 return "A user has logged on to a session";
428
429 case WTS_SESSION_LOGOFF:
430 return "A user has logged off the session";
431
432 case WTS_SESSION_LOCK:
433 return "A session has been locked";
434
435 case WTS_SESSION_UNLOCK:
436 return "A session has been unlocked";
437
438 case WTS_SESSION_REMOTE_CONTROL:
439 return "A session has changed its remote controlled status";
440#if 0
441 case WTS_SESSION_CREATE:
442 return "A session has been created";
443
444 case WTS_SESSION_TERMINATE:
445 return "The session has been terminated";
446#endif
447 default:
448 break;
449 }
450
451 return "Uknonwn state";
452}
453#endif /* !TARGET_NT4 */
454
455
456#ifdef TARGET_NT4
457static VOID WINAPI vboxServiceWinCtrlHandler(DWORD dwControl)
458#else
459static DWORD WINAPI vboxServiceWinCtrlHandler(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
460#endif
461{
462 DWORD rcRet = NO_ERROR;
463
464#ifdef TARGET_NT4
465 VBoxServiceVerbose(2, "Control handler: Control=%#x\n", dwControl);
466#else
467 VBoxServiceVerbose(2, "Control handler: Control=%#x, EventType=%#x\n", dwControl, dwEventType);
468#endif
469
470 switch (dwControl)
471 {
472 case SERVICE_CONTROL_INTERROGATE:
473 vboxServiceWinSetStatus(g_dwWinServiceLastStatus, 0);
474 break;
475
476 case SERVICE_CONTROL_STOP:
477 case SERVICE_CONTROL_SHUTDOWN:
478 {
479 vboxServiceWinSetStatus(SERVICE_STOP_PENDING, 0);
480
481 int rc2 = VBoxServiceStopServices();
482 if (RT_FAILURE(rc2))
483 rcRet = ERROR_GEN_FAILURE;
484 else
485 {
486 rc2 = VBoxServiceReportStatus(VBoxGuestFacilityStatus_Terminated);
487 AssertRC(rc2);
488 }
489
490 vboxServiceWinSetStatus(SERVICE_STOPPED, 0);
491 break;
492 }
493
494# ifndef TARGET_NT4
495 case SERVICE_CONTROL_SESSIONCHANGE: /* Only Windows 2000 and up. */
496 {
497 AssertPtr(lpEventData);
498 PWTSSESSION_NOTIFICATION pNotify = (PWTSSESSION_NOTIFICATION)lpEventData;
499 Assert(pNotify->cbSize == sizeof(WTSSESSION_NOTIFICATION));
500
501 VBoxServiceVerbose(1, "Control handler: %s (Session=%ld, Event=%#x)\n",
502 vboxServiceWTSStateToString(dwEventType),
503 pNotify->dwSessionId, dwEventType);
504
505 /* Handle all events, regardless of dwEventType. */
506 int rc2 = VBoxServiceVMInfoSignal();
507 AssertRC(rc2);
508 break;
509 }
510# endif /* !TARGET_NT4 */
511
512 default:
513 VBoxServiceVerbose(1, "Control handler: Function not implemented: %#x\n", dwControl);
514 rcRet = ERROR_CALL_NOT_IMPLEMENTED;
515 break;
516 }
517
518#ifndef TARGET_NT4
519 return rcRet;
520#endif
521}
522
523
524static void WINAPI vboxServiceWinMain(DWORD argc, LPTSTR *argv)
525{
526 VBoxServiceVerbose(2, "Registering service control handler ...\n");
527#ifdef TARGET_NT4
528 g_hWinServiceStatus = RegisterServiceCtrlHandler(VBOXSERVICE_NAME, vboxServiceWinCtrlHandler);
529#else
530 g_hWinServiceStatus = RegisterServiceCtrlHandlerEx(VBOXSERVICE_NAME, vboxServiceWinCtrlHandler, NULL);
531#endif
532 if (g_hWinServiceStatus != NULL)
533 {
534 VBoxServiceVerbose(2, "Service control handler registered.\n");
535 vboxServiceWinStart();
536 }
537 else
538 {
539 DWORD dwErr = GetLastError();
540 switch (dwErr)
541 {
542 case ERROR_INVALID_NAME:
543 VBoxServiceError("Invalid service name!\n");
544 break;
545 case ERROR_SERVICE_DOES_NOT_EXIST:
546 VBoxServiceError("Service does not exist!\n");
547 break;
548 default:
549 VBoxServiceError("Could not register service control handle! Error: %ld\n", dwErr);
550 break;
551 }
552 }
553}
554
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette