VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxService/VBoxService.cpp@ 16684

Last change on this file since 16684 was 13837, checked in by vboxsync, 16 years ago

s/%Vr\([acfs]\)/%Rr\1/g - since I'm upsetting everyone anyway, better make the most of it...

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.1 KB
Line 
1/* $Id: VBoxService.cpp 13837 2008-11-05 02:54:02Z vboxsync $ */
2/** @file
3 * VBoxService - The Guest Additions Helper Service.
4 */
5
6/*
7 * Copyright (C) 2008 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#include "VBoxService.h"
23#ifdef VBOX_WITH_GUEST_PROPS
24 #include "VBoxVMInfo.h"
25#endif
26#include "resource.h"
27
28#define VBOXSERVICE_NAME _T("VBoxService")
29#define VBOXSERVICE_FRIENDLY_NAME _T("VBoxService")
30
31/* Global variables. */
32HANDLE gvboxDriver;
33HANDLE gStopSem;
34
35SERVICE_STATUS gvboxServiceStatus;
36DWORD gvboxServiceStatusCode;
37SERVICE_STATUS_HANDLE gvboxServiceStatusHandle;
38
39VBOXSERVICEENV gServiceEnv;
40
41/* Prototypes. */
42void writeLog (char* a_pszText, ...);
43
44/* The service table. */
45static VBOXSERVICEINFO vboxServiceTable[] =
46{
47#ifdef VBOX_WITH_GUEST_PROPS
48 {
49 "VMInfo",
50 vboxVMInfoInit,
51 vboxVMInfoThread,
52 vboxVMInfoDestroy,
53 },
54#endif
55 {
56 NULL
57 }
58};
59
60/**
61 * @todo Format code style.
62 * @todo Add full unicode support.
63 * @todo Add event log capabilities / check return values.
64 */
65
66DWORD AddAceToObjectsSecurityDescriptor (LPTSTR pszObjName,
67 SE_OBJECT_TYPE ObjectType,
68 LPTSTR pszTrustee,
69 TRUSTEE_FORM TrusteeForm,
70 DWORD dwAccessRights,
71 ACCESS_MODE AccessMode,
72 DWORD dwInheritance)
73{
74 DWORD dwRes = 0;
75 PACL pOldDACL = NULL, pNewDACL = NULL;
76 PSECURITY_DESCRIPTOR pSD = NULL;
77 EXPLICIT_ACCESS ea;
78
79 if (NULL == pszObjName)
80 return ERROR_INVALID_PARAMETER;
81
82 /* Get a pointer to the existing DACL. */
83 dwRes = GetNamedSecurityInfo(pszObjName, ObjectType,
84 DACL_SECURITY_INFORMATION,
85 NULL, NULL, &pOldDACL, NULL, &pSD);
86 if (ERROR_SUCCESS != dwRes) {
87 writeLog("VBoxService: GetNamedSecurityInfo: Error %u\n", dwRes);
88 goto Cleanup;
89 }
90
91 /* Initialize an EXPLICIT_ACCESS structure for the new ACE. */
92 ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
93 ea.grfAccessPermissions = dwAccessRights;
94 ea.grfAccessMode = AccessMode;
95 ea.grfInheritance= dwInheritance;
96 ea.Trustee.TrusteeForm = TrusteeForm;
97 ea.Trustee.ptstrName = pszTrustee;
98
99 /* Create a new ACL that merges the new ACE into the existing DACL. */
100 dwRes = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL);
101 if (ERROR_SUCCESS != dwRes) {
102 writeLog("VBoxService: SetEntriesInAcl: Error %u\n", dwRes);
103 goto Cleanup;
104 }
105
106 /* Attach the new ACL as the object's DACL. */
107 dwRes = SetNamedSecurityInfo(pszObjName, ObjectType,
108 DACL_SECURITY_INFORMATION,
109 NULL, NULL, pNewDACL, NULL);
110 if (ERROR_SUCCESS != dwRes) {
111 writeLog("VBoxService: SetNamedSecurityInfo: Error %u\n", dwRes);
112 goto Cleanup;
113 }
114
115Cleanup:
116
117 if(pSD != NULL)
118 LocalFree((HLOCAL) pSD);
119 if(pNewDACL != NULL)
120 LocalFree((HLOCAL) pNewDACL);
121
122 return dwRes;
123}
124
125static void SetStatus (DWORD a_dwStatus)
126{
127 if (NULL == gvboxServiceStatusHandle) /* Program could be in testing mode, so no service environment available. */
128 return;
129
130 gvboxServiceStatusCode = a_dwStatus;
131
132 SERVICE_STATUS ss;
133 ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
134 ss.dwCurrentState = gvboxServiceStatusCode;
135 ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
136 ss.dwWin32ExitCode = NOERROR;
137 ss.dwServiceSpecificExitCode = NOERROR;
138 ss.dwCheckPoint = 0;
139 ss.dwWaitHint = 3000;
140
141 SetServiceStatus (gvboxServiceStatusHandle, &ss);
142}
143
144static int vboxStartServices (VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable)
145{
146 Log(("VBoxService: Starting services ...\n"));
147
148 Assert(pEnv);
149 pEnv->hStopEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
150
151 if (!pEnv->hStopEvent)
152 {
153 /* Could not create event. */
154 return VERR_NOT_SUPPORTED;
155 }
156
157 while (pTable->pszName)
158 {
159 Log(("VBoxService: Starting %s ...\n", pTable->pszName));
160
161 int rc = VINF_SUCCESS;
162
163 bool fStartThread = false;
164
165 pTable->hThread = (HANDLE)0;
166 pTable->pInstance = NULL;
167 pTable->fStarted = false;
168
169 if (pTable->pfnInit)
170 {
171 rc = pTable->pfnInit (pEnv, &pTable->pInstance, &fStartThread);
172 }
173
174 if (RT_FAILURE (rc))
175 {
176 writeLog("VBoxService: Failed to initialize! Error = %Rrc.\n", rc);
177 }
178 else
179 {
180 if (pTable->pfnThread && fStartThread)
181 {
182 unsigned threadid;
183
184 pTable->hThread = (HANDLE)_beginthreadex (NULL, /* security */
185 0, /* stacksize */
186 pTable->pfnThread,
187 pTable->pInstance,
188 0, /* initflag */
189 &threadid);
190
191 if (pTable->hThread == (HANDLE)(0))
192 {
193 rc = VERR_NOT_SUPPORTED;
194 }
195 }
196
197 if (RT_FAILURE (rc))
198 {
199 Log(("VBoxService: Failed to start the thread: %s\n", pTable->pszName));
200
201 if (pTable->pfnDestroy)
202 {
203 pTable->pfnDestroy (pEnv, pTable->pInstance);
204 }
205 }
206 else
207 {
208 pTable->fStarted = true;
209 }
210 }
211
212 /* Advance to next table element. */
213 pTable++;
214 }
215
216 Log(("VBoxService: All threads started.\n"));
217 return VINF_SUCCESS;
218}
219
220static void vboxStopServices (BOOL bAlert, VBOXSERVICEENV *pEnv, VBOXSERVICEINFO *pTable)
221{
222 /* Signal to all threads. */
223 Assert(pEnv);
224 Assert(pTable);
225 if (bAlert && (NULL != pEnv->hStopEvent))
226 {
227 Log(("VBoxService: Setting stop event ...\n"));
228 SetEvent(pEnv->hStopEvent);
229 }
230
231 while (pTable->pszName)
232 {
233 if (pTable->fStarted)
234 {
235 if (pTable->pfnThread)
236 {
237 Log(("VBoxService: Waiting for thread %s to close ...\n", pTable->pszName));
238
239 /* There is a thread, wait for termination. */
240 WaitForSingleObject(pTable->hThread, INFINITE);
241
242 CloseHandle (pTable->hThread);
243 pTable->hThread = 0;
244 }
245
246 if (pTable->pfnDestroy)
247 {
248 pTable->pfnDestroy (pEnv, pTable->pInstance);
249 }
250
251 pTable->fStarted = false;
252 }
253
254 /* Advance to next table element. */
255 pTable++;
256 }
257
258 CloseHandle (pEnv->hStopEvent);
259 SetStatus (SERVICE_STOPPED);
260}
261
262void vboxServiceStart()
263{
264 gStopSem = CreateEvent(NULL, TRUE, FALSE, NULL);
265 if (gStopSem == NULL)
266 {
267 writeLog("VBoxService: CreateEvent for stopping failed: rc = %d\n", GetLastError());
268 return;
269 }
270
271 /* Create a well-known SID for the "Builtin Users" group. */
272 PSID pBuiltinUsersSID = NULL;
273 SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_LOCAL_SID_AUTHORITY;
274
275 if(!AllocateAndInitializeSid(&SIDAuthWorld, 1,
276 SECURITY_LOCAL_RID,
277 0, 0, 0, 0, 0, 0, 0,
278 &pBuiltinUsersSID))
279 {
280 writeLog("VBoxService: AllocateAndInitializeSid: Error %u\n", GetLastError());
281 }
282
283 AddAceToObjectsSecurityDescriptor (TEXT("\\\\.\\VBoxMiniRdrDN"),
284 SE_FILE_OBJECT,
285 (LPTSTR)pBuiltinUsersSID,
286 TRUSTEE_IS_SID,
287 FILE_GENERIC_READ | FILE_GENERIC_WRITE,
288 SET_ACCESS,
289 NO_INHERITANCE);
290
291 /* Start service threads. */
292 int rc = vboxStartServices(&gServiceEnv, vboxServiceTable);
293
294 /** @todo Add error handling. */
295
296 SetStatus (SERVICE_RUNNING);
297}
298
299DWORD WINAPI ServiceCtrlHandler (DWORD dwControl,
300 DWORD dwEventType,
301 LPVOID lpEventData,
302 LPVOID lpContext)
303{
304 DWORD rc = NO_ERROR;
305
306 switch (dwControl)
307 {
308
309 case SERVICE_CONTROL_INTERROGATE:
310 SetStatus(gvboxServiceStatusCode);
311 break;
312
313 case SERVICE_CONTROL_STOP:
314 case SERVICE_CONTROL_SHUTDOWN:
315 {
316 SetStatus(SERVICE_STOP_PENDING);
317
318 vboxStopServices(TRUE, &gServiceEnv, vboxServiceTable);
319
320 /* Don't close VBox driver here - the service might be
321 * started again. */
322 CloseHandle(gStopSem);
323
324 SetStatus(SERVICE_STOPPED);
325 }
326 break;
327
328 case SERVICE_CONTROL_SESSIONCHANGE: /* Only Win XP and up. */
329
330 switch (dwEventType)
331 {
332 case WTS_SESSION_LOGON:
333 Log(("VBoxService: A user has logged on to the session.\n"));
334 break;
335 case WTS_SESSION_LOGOFF:
336 Log(("VBoxService: A user has logged off from the session.\n"));
337 break;
338 default:
339 break;
340 }
341 break;
342
343 default:
344
345 Log(("VBoxService: Service control function not implemented: %ld\n", dwControl));
346 rc = ERROR_CALL_NOT_IMPLEMENTED;
347 break;
348 }
349 return rc;
350}
351
352void WINAPI ServiceMain (DWORD argc, LPTSTR *argv)
353{
354 Log(("VBoxService: Registering service control handler ...\n"));
355 gvboxServiceStatusHandle = RegisterServiceCtrlHandlerEx (VBOXSERVICE_NAME, ServiceCtrlHandler, NULL);
356
357 if (NULL == gvboxServiceStatusHandle)
358 {
359 DWORD dwErr = GetLastError();
360
361 switch (dwErr)
362 {
363 case ERROR_INVALID_NAME:
364 writeLog("VBoxService: Invalid service name!\n");
365 break;
366 case ERROR_SERVICE_DOES_NOT_EXIST:
367 writeLog("VBoxService: Service does not exist!\n");
368 break;
369 default:
370 writeLog("VBoxService: Could not register service control handle! Error: %ld\n", dwErr);
371 break;
372 }
373 }
374 else
375 {
376 vboxServiceStart();
377
378 while (1) {
379 Sleep (100); /** @todo */
380 }
381 }
382}
383
384int Install ()
385{
386 SC_HANDLE hService, hSCManager;
387 TCHAR imagePath[MAX_PATH] = { 0 };
388
389 GetModuleFileName(NULL,imagePath,MAX_PATH);
390 writeLog("VBoxService: Installing service ...\n");
391
392 hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
393
394 if (NULL == hSCManager) {
395 writeLog("VBoxService: Could not open SCM! Error: %d\n", GetLastError());
396 return 1;
397 }
398
399 hService = ::CreateService (hSCManager,
400 VBOXSERVICE_NAME, VBOXSERVICE_FRIENDLY_NAME,
401 SERVICE_ALL_ACCESS,
402 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
403 SERVICE_DEMAND_START,SERVICE_ERROR_NORMAL,
404 imagePath, NULL, NULL, NULL, NULL, NULL);
405
406 if (NULL == hService) {
407 writeLog("VBoxService: Could not create service! Error: %d\n", GetLastError());
408 CloseServiceHandle(hSCManager);
409 return 1;
410 }
411 else
412 {
413 writeLog("VBoxService: Service successfully installed!\n");
414 }
415
416 CloseServiceHandle(hService);
417 CloseServiceHandle(hSCManager);
418
419 return 0;
420}
421
422int Uninstall ()
423{
424 SC_HANDLE hService, hSCManager;
425 hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
426
427 writeLog("VBoxService: Uninstalling service ...\n");
428
429 if (NULL == hSCManager) {
430 writeLog("VBoxService: Could not open SCM! Error: %d\n", GetLastError());
431 return 1;
432 }
433
434 hService = OpenService (hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS );
435 if (NULL == hService) {
436 writeLog("VBoxService: Could not open service! Error: %d\n", GetLastError());
437 CloseServiceHandle (hSCManager);
438 return 1;
439 }
440
441 if (FALSE == DeleteService (hService))
442 {
443 writeLog("VBoxService: Could not remove service! Error: %d\n", GetLastError());
444 CloseServiceHandle (hService);
445 CloseServiceHandle (hSCManager);
446 return 1;
447 }
448 else
449 {
450 HKEY hKey = NULL;
451 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\System"), 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) {
452 RegDeleteKey(hKey, VBOXSERVICE_NAME);
453 RegCloseKey(hKey);
454 }
455
456 writeLog("VBoxService: Service successfully uninstalled!\n");
457 }
458
459 CloseServiceHandle(hService);
460 CloseServiceHandle(hSCManager);
461
462 return 0;
463}
464
465void writeLog (char* a_pszText, ...)
466{
467 char szBuffer[1024] = { 0 };
468
469 Assert(a_pszText);
470
471 va_list va;
472 va_start(va, a_pszText);
473
474 RTStrPrintfV(szBuffer, sizeof(szBuffer), a_pszText, va);
475 printf(szBuffer);
476 LogRel((szBuffer));
477
478 va_end(va);
479}
480
481void printHelp (_TCHAR* a_pszName)
482{
483 Assert(a_pszName);
484 _tprintf(_T("VBoxService - Guest Additions Helper Service for Windows XP/2K/Vista\n"));
485 _tprintf(_T("Version: %d.%d.%d.%d\n\n"), VBOX_VERSION_MAJOR, VBOX_VERSION_MINOR, VBOX_VERSION_BUILD, VBOX_SVN_REV);
486 _tprintf(_T("Syntax:\n"));
487 _tprintf(_T("\tTo install: %ws /i\n"), a_pszName);
488 _tprintf(_T("\tTo uninstall: %ws /u\n"), a_pszName);
489 _tprintf(_T("\tTo execute from command line: %ws /t\n"), a_pszName);
490 _tprintf(_T("\tThis help text: %ws /h\n"), a_pszName);
491}
492
493int _tmain(int argc, _TCHAR* argv[])
494{
495 /* Do not use a global namespace ("Global\\") for mutex name here, will blow up NT4 compatibility! */
496 HANDLE hMutexAppRunning = CreateMutex (NULL, FALSE, VBOXSERVICE_NAME);
497 if ( (hMutexAppRunning != NULL)
498 && (GetLastError() == ERROR_ALREADY_EXISTS))
499 {
500 /* Close the mutex for this application instance. */
501 CloseHandle(hMutexAppRunning);
502 hMutexAppRunning = NULL;
503 }
504
505 int rc = RTR3Init();
506 if (RT_FAILURE(rc))
507 {
508 writeLog("VBoxService: Failed to initialise the VirtualBox runtime! Error: %d\n", rc);
509 return rc;
510 }
511
512 rc = VbglR3Init();
513 if (RT_FAILURE(rc))
514 {
515 writeLog("VBoxService: Failed to contact the VirtualBox host! Program maybe not running in a VM? Error: %d\n", rc);
516 return rc;
517 }
518
519 LogRel(("VBoxService: Started.\n"));
520
521 static SERVICE_TABLE_ENTRY const s_serviceTable[]=
522 {
523 {VBOXSERVICE_NAME, ServiceMain},
524 {NULL,NULL}
525 };
526
527 if (argc > 1)
528 {
529 if (0 == _tcsicmp(argv[1], _T("/i")))
530 Install();
531 else if (0 == _tcsicmp(argv[1], _T("/u")))
532 Uninstall();
533 else if (0 == _tcsicmp(argv[1], _T("/t")))
534 {
535 vboxServiceStart();
536
537 while (1) {
538 Sleep( 100 ); /** @todo */
539 }
540 }
541 else if (0 == _tcsicmp(argv[1], _T("/h")))
542 printHelp(argv[0]);
543
544 else {
545 _tprintf(_T("Invalid command line argument: %ws\n"), argv[1]);
546 _tprintf(_T("Type %s /h to display help.\n"), argv[0]);
547 }
548 }
549 else /* Normal service. */
550 {
551 if (FALSE == StartServiceCtrlDispatcher (s_serviceTable))
552 printHelp(argv[0]); /* Application called from command line, print some help. */
553 }
554
555 /* Release instance mutex. */
556 if (hMutexAppRunning != NULL) {
557 CloseHandle (hMutexAppRunning);
558 hMutexAppRunning = NULL;
559 }
560
561 writeLog("VBoxService: Ended.\n");
562
563 VbglR3Term();
564 return 0;
565}
566
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