VirtualBox

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

Last change on this file since 69496 was 69496, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.9 KB
Line 
1/* $Id: VBoxService-win.cpp 69496 2017-10-28 14:55:58Z vboxsync $ */
2/** @file
3 * VBoxService - Guest Additions Service Skeleton, Windows Specific Parts.
4 */
5
6/*
7 * Copyright (C) 2009-2017 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 <iprt/win/windows.h>
29#include <process.h>
30#include <aclapi.h>
31
32
33/*********************************************************************************************************************************
34* Internal Functions *
35*********************************************************************************************************************************/
36static void WINAPI vgsvcWinMain(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, vgsvcWinMain },
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 vgsvcWinAddAceToObjectsSecurityDescriptor(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 VGSvcError("AddAceToObjectsSecurityDescriptor: Object not found/installed: %s\n", pszObjName);
83 else
84 VGSvcError("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 VGSvcError("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 VGSvcError("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 vgsvcWinSetStatus(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 VGSvcVerbose(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 VGSvcError("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 VGSvcError("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 VGSvcWinSetStopPendingStatus(uint32_t uCheckPoint)
179{
180 vgsvcWinSetStatus(SERVICE_STOP_PENDING, uCheckPoint);
181}
182
183
184static RTEXITCODE vgsvcWinSetDesc(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 VGSvcError("Cannot set the service description! Error: %ld\n", GetLastError());
197 return RTEXITCODE_FAILURE;
198 }
199#else
200 RT_NOREF(hService);
201#endif
202 return RTEXITCODE_SUCCESS;
203}
204
205
206/**
207 * Installs the service.
208 */
209RTEXITCODE VGSvcWinInstall(void)
210{
211 VGSvcVerbose(1, "Installing service ...\n");
212
213 TCHAR imagePath[MAX_PATH] = { 0 };
214 GetModuleFileName(NULL, imagePath, sizeof(imagePath));
215
216 SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
217 if (hSCManager == NULL)
218 {
219 VGSvcError("Could not open SCM! Error: %ld\n", GetLastError());
220 return RTEXITCODE_FAILURE;
221 }
222
223 RTEXITCODE rc = RTEXITCODE_SUCCESS;
224 SC_HANDLE hService = CreateService(hSCManager,
225 VBOXSERVICE_NAME, VBOXSERVICE_FRIENDLY_NAME,
226 SERVICE_ALL_ACCESS,
227 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
228 SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
229 imagePath, NULL, NULL, NULL, NULL, NULL);
230 if (hService != NULL)
231 VGSvcVerbose(0, "Service successfully installed!\n");
232 else
233 {
234 DWORD dwErr = GetLastError();
235 switch (dwErr)
236 {
237 case ERROR_SERVICE_EXISTS:
238 VGSvcVerbose(1, "Service already exists, just updating the service config.\n");
239 hService = OpenService(hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS);
240 if (hService)
241 {
242 if (ChangeServiceConfig (hService,
243 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
244 SERVICE_DEMAND_START,
245 SERVICE_ERROR_NORMAL,
246 imagePath,
247 NULL,
248 NULL,
249 NULL,
250 NULL,
251 NULL,
252 VBOXSERVICE_FRIENDLY_NAME))
253 VGSvcVerbose(1, "The service config has been successfully updated.\n");
254 else
255 rc = VGSvcError("Could not change service config! Error: %ld\n", GetLastError());
256 }
257 else
258 rc = VGSvcError("Could not open service! Error: %ld\n", GetLastError());
259 break;
260
261 default:
262 rc = VGSvcError("Could not create service! Error: %ld\n", dwErr);
263 break;
264 }
265 }
266
267 if (rc == RTEXITCODE_SUCCESS)
268 rc = vgsvcWinSetDesc(hService);
269
270 CloseServiceHandle(hService);
271 CloseServiceHandle(hSCManager);
272 return rc;
273}
274
275/**
276 * Uninstalls the service.
277 */
278RTEXITCODE VGSvcWinUninstall(void)
279{
280 VGSvcVerbose(1, "Uninstalling service ...\n");
281
282 SC_HANDLE hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
283 if (hSCManager == NULL)
284 {
285 VGSvcError("Could not open SCM! Error: %d\n", GetLastError());
286 return RTEXITCODE_FAILURE;
287 }
288
289 RTEXITCODE rcExit;
290 SC_HANDLE hService = OpenService(hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS );
291 if (hService != NULL)
292 {
293 if (DeleteService(hService))
294 {
295 /*
296 * ???
297 */
298 HKEY hKey = NULL;
299 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
300 "SYSTEM\\CurrentControlSet\\Services\\EventLog\\System",
301 0,
302 KEY_ALL_ACCESS,
303 &hKey)
304 == ERROR_SUCCESS)
305 {
306 RegDeleteKey(hKey, VBOXSERVICE_NAME);
307 RegCloseKey(hKey);
308 }
309
310 VGSvcVerbose(0, "Service successfully uninstalled!\n");
311 rcExit = RTEXITCODE_SUCCESS;
312 }
313 else
314 rcExit = VGSvcError("Could not remove service! Error: %d\n", GetLastError());
315 CloseServiceHandle(hService);
316 }
317 else
318 rcExit = VGSvcError("Could not open service! Error: %d\n", GetLastError());
319 CloseServiceHandle(hSCManager);
320
321 return rcExit;
322}
323
324
325static int vgsvcWinStart(void)
326{
327 int rc = VINF_SUCCESS;
328
329#ifndef TARGET_NT4
330 /* Create a well-known SID for the "Builtin Users" group. */
331 PSID pBuiltinUsersSID = NULL;
332 SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_LOCAL_SID_AUTHORITY;
333
334 if (!AllocateAndInitializeSid(&SIDAuthWorld, 1,
335 SECURITY_LOCAL_RID,
336 0, 0, 0, 0, 0, 0, 0,
337 &pBuiltinUsersSID))
338 {
339 rc = RTErrConvertFromWin32(GetLastError());
340 }
341 else
342 {
343 DWORD dwRes = vgsvcWinAddAceToObjectsSecurityDescriptor(TEXT("\\\\.\\VBoxMiniRdrDN"),
344 SE_FILE_OBJECT,
345 (LPTSTR)pBuiltinUsersSID,
346 TRUSTEE_IS_SID,
347 FILE_GENERIC_READ | FILE_GENERIC_WRITE,
348 SET_ACCESS,
349 NO_INHERITANCE);
350 if (dwRes != ERROR_SUCCESS)
351 {
352 if (dwRes == ERROR_FILE_NOT_FOUND)
353 {
354 /* If we don't find our "VBoxMiniRdrDN" (for Shared Folders) object above,
355 don't report an error; it just might be not installed. Otherwise this
356 would cause the SCM to hang on starting up the service. */
357 rc = VINF_SUCCESS;
358 }
359 else
360 rc = RTErrConvertFromWin32(dwRes);
361 }
362 }
363#endif
364
365 if (RT_SUCCESS(rc))
366 {
367 vgsvcWinSetStatus(SERVICE_START_PENDING, 0);
368
369 rc = VGSvcStartServices();
370 if (RT_SUCCESS(rc))
371 {
372 vgsvcWinSetStatus(SERVICE_RUNNING, 0);
373 VGSvcMainWait();
374 }
375 else
376 {
377 vgsvcWinSetStatus(SERVICE_STOPPED, 0);
378#if 0 /** @todo r=bird: Enable this if SERVICE_CONTROL_STOP isn't triggered automatically */
379 VGSvcStopServices();
380#endif
381 }
382 }
383 else
384 vgsvcWinSetStatus(SERVICE_STOPPED, 0);
385
386 if (RT_FAILURE(rc))
387 VGSvcError("Service failed to start with rc=%Rrc!\n", rc);
388
389 return rc;
390}
391
392
393/**
394 * Call StartServiceCtrlDispatcher.
395 *
396 * The main() thread invokes this when not started in foreground mode. It
397 * won't return till the service is being shutdown (unless start up fails).
398 *
399 * @returns RTEXITCODE_SUCCESS on normal return after service shutdown.
400 * Something else on failure, error will have been reported.
401 */
402RTEXITCODE VGSvcWinEnterCtrlDispatcher(void)
403{
404 if (!StartServiceCtrlDispatcher(&g_aServiceTable[0]))
405 return VGSvcError("StartServiceCtrlDispatcher: %u. Please start %s with option -f (foreground)!\n",
406 GetLastError(), g_pszProgName);
407 return RTEXITCODE_SUCCESS;
408}
409
410
411#ifndef TARGET_NT4
412static const char* vgsvcWTSStateToString(DWORD dwEvent)
413{
414 switch (dwEvent)
415 {
416 case WTS_CONSOLE_CONNECT:
417 return "A session was connected to the console terminal";
418
419 case WTS_CONSOLE_DISCONNECT:
420 return "A session was disconnected from the console terminal";
421
422 case WTS_REMOTE_CONNECT:
423 return "A session connected to the remote terminal";
424
425 case WTS_REMOTE_DISCONNECT:
426 return "A session was disconnected from the remote terminal";
427
428 case WTS_SESSION_LOGON:
429 return "A user has logged on to a session";
430
431 case WTS_SESSION_LOGOFF:
432 return "A user has logged off the session";
433
434 case WTS_SESSION_LOCK:
435 return "A session has been locked";
436
437 case WTS_SESSION_UNLOCK:
438 return "A session has been unlocked";
439
440 case WTS_SESSION_REMOTE_CONTROL:
441 return "A session has changed its remote controlled status";
442#if 0
443 case WTS_SESSION_CREATE:
444 return "A session has been created";
445
446 case WTS_SESSION_TERMINATE:
447 return "The session has been terminated";
448#endif
449 default:
450 break;
451 }
452
453 return "Uknonwn state";
454}
455#endif /* !TARGET_NT4 */
456
457
458#ifdef TARGET_NT4
459static VOID WINAPI vgsvcWinCtrlHandler(DWORD dwControl)
460#else
461static DWORD WINAPI vgsvcWinCtrlHandler(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
462#endif
463{
464#ifdef TARGET_NT4
465 VGSvcVerbose(2, "Control handler: Control=%#x\n", dwControl);
466#else
467 RT_NOREF1(lpContext);
468 VGSvcVerbose(2, "Control handler: Control=%#x, EventType=%#x\n", dwControl, dwEventType);
469#endif
470
471 DWORD rcRet = NO_ERROR;
472 switch (dwControl)
473 {
474 case SERVICE_CONTROL_INTERROGATE:
475 vgsvcWinSetStatus(g_dwWinServiceLastStatus, 0);
476 break;
477
478 case SERVICE_CONTROL_STOP:
479 case SERVICE_CONTROL_SHUTDOWN:
480 {
481 vgsvcWinSetStatus(SERVICE_STOP_PENDING, 0);
482
483 int rc2 = VGSvcStopServices();
484 if (RT_FAILURE(rc2))
485 rcRet = ERROR_GEN_FAILURE;
486 else
487 {
488 rc2 = VGSvcReportStatus(VBoxGuestFacilityStatus_Terminated);
489 AssertRC(rc2);
490 }
491
492 vgsvcWinSetStatus(SERVICE_STOPPED, 0);
493 break;
494 }
495
496# ifndef TARGET_NT4
497 case SERVICE_CONTROL_SESSIONCHANGE: /* Only Windows 2000 and up. */
498 {
499 AssertPtr(lpEventData);
500 PWTSSESSION_NOTIFICATION pNotify = (PWTSSESSION_NOTIFICATION)lpEventData;
501 Assert(pNotify->cbSize == sizeof(WTSSESSION_NOTIFICATION));
502
503 VGSvcVerbose(1, "Control handler: %s (Session=%ld, Event=%#x)\n",
504 vgsvcWTSStateToString(dwEventType),
505 pNotify->dwSessionId, dwEventType);
506
507 /* Handle all events, regardless of dwEventType. */
508 int rc2 = VGSvcVMInfoSignal();
509 AssertRC(rc2);
510 break;
511 }
512# endif /* !TARGET_NT4 */
513
514 default:
515 VGSvcVerbose(1, "Control handler: Function not implemented: %#x\n", dwControl);
516 rcRet = ERROR_CALL_NOT_IMPLEMENTED;
517 break;
518 }
519
520#ifndef TARGET_NT4
521 return rcRet;
522#endif
523}
524
525
526static void WINAPI vgsvcWinMain(DWORD argc, LPTSTR *argv)
527{
528 RT_NOREF2(argc, argv);
529 VGSvcVerbose(2, "Registering service control handler ...\n");
530#ifdef TARGET_NT4
531 g_hWinServiceStatus = RegisterServiceCtrlHandler(VBOXSERVICE_NAME, vgsvcWinCtrlHandler);
532#else
533 g_hWinServiceStatus = RegisterServiceCtrlHandlerEx(VBOXSERVICE_NAME, vgsvcWinCtrlHandler, NULL);
534#endif
535 if (g_hWinServiceStatus != NULL)
536 {
537 VGSvcVerbose(2, "Service control handler registered.\n");
538 vgsvcWinStart();
539 }
540 else
541 {
542 DWORD dwErr = GetLastError();
543 switch (dwErr)
544 {
545 case ERROR_INVALID_NAME:
546 VGSvcError("Invalid service name!\n");
547 break;
548 case ERROR_SERVICE_DOES_NOT_EXIST:
549 VGSvcError("Service does not exist!\n");
550 break;
551 default:
552 VGSvcError("Could not register service control handle! Error: %ld\n", dwErr);
553 break;
554 }
555 }
556}
557
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