VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxAutostart/VBoxAutostart-win.cpp@ 95969

最後變更 在這個檔案從95969是 95183,由 vboxsync 提交於 3 年 前

VBoxAutostart/win: Added "logon as service" privilege to user during autostart service installation [nit]. bugref:9431

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 51.1 KB
 
1/* $Id: VBoxAutostart-win.cpp 95183 2022-06-03 12:06:54Z vboxsync $ */
2/** @file
3 * VirtualBox Autostart Service - Windows Specific Code.
4 */
5
6/*
7 * Copyright (C) 2012-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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/dir.h>
23#include <iprt/env.h>
24#include <iprt/err.h>
25#include <iprt/getopt.h>
26#include <iprt/initterm.h>
27#include <iprt/mem.h>
28#include <iprt/message.h>
29#include <iprt/process.h>
30#include <iprt/path.h>
31#include <iprt/semaphore.h>
32#include <iprt/stream.h>
33#include <iprt/string.h>
34#include <iprt/thread.h>
35
36#include <iprt/win/windows.h>
37#include <ntsecapi.h>
38
39#define SECURITY_WIN32
40#include <Security.h>
41
42#include <VBox/com/array.h>
43#include <VBox/com/com.h>
44#include <VBox/com/ErrorInfo.h>
45#include <VBox/com/errorprint.h>
46#include <VBox/com/Guid.h>
47#include <VBox/com/listeners.h>
48#include <VBox/com/NativeEventQueue.h>
49#include <VBox/com/string.h>
50#include <VBox/com/VirtualBox.h>
51
52#include <VBox/log.h>
53
54#include "VBoxAutostart.h"
55#include "PasswordInput.h"
56
57
58/*********************************************************************************************************************************
59* Defined Constants And Macros *
60*********************************************************************************************************************************/
61/** The service name. */
62#define AUTOSTART_SERVICE_NAME "VBoxAutostartSvc"
63/** The service display name. */
64#define AUTOSTART_SERVICE_DISPLAY_NAME "VirtualBox Autostart Service"
65
66/* just define it here instead of including
67 * a bunch of nt headers */
68#ifndef STATUS_SUCCESS
69#define STATUS_SUCCESS ((NTSTATUS)0)
70#endif
71
72
73ComPtr<IVirtualBoxClient> g_pVirtualBoxClient = NULL;
74bool g_fVerbose = false;
75ComPtr<IVirtualBox> g_pVirtualBox = NULL;
76ComPtr<ISession> g_pSession = NULL;
77
78
79/*********************************************************************************************************************************
80* Global Variables *
81*********************************************************************************************************************************/
82/** The service control handler handle. */
83static SERVICE_STATUS_HANDLE g_hSupSvcWinCtrlHandler = NULL;
84/** The service status. */
85static uint32_t volatile g_u32SupSvcWinStatus = SERVICE_STOPPED;
86/** The semaphore the main service thread is waiting on in autostartSvcWinServiceMain. */
87static RTSEMEVENTMULTI g_hSupSvcWinEvent = NIL_RTSEMEVENTMULTI;
88/** The service name is used for send to service main. */
89static com::Bstr g_bstrServiceName;
90
91/** Verbosity level. */
92unsigned g_cVerbosity = 0;
93
94/** Logging parameters. */
95static uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
96static uint32_t g_uHistoryFileTime = 0; /* No time limit, it's very low volume. */
97static uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
98
99
100/*********************************************************************************************************************************
101* Internal Functions *
102*********************************************************************************************************************************/
103static SC_HANDLE autostartSvcWinOpenSCManager(const char *pszAction, DWORD dwAccess);
104
105static int autostartGetProcessDomainUser(com::Utf8Str &aUser)
106{
107 int rc = VERR_NOT_SUPPORTED;
108
109 RTUTF16 wszUsername[1024] = { 0 };
110 ULONG cwcUsername = RT_ELEMENTS(wszUsername);
111 char *pszUser = NULL;
112 if (!GetUserNameExW(NameSamCompatible, &wszUsername[0], &cwcUsername))
113 return RTErrConvertFromWin32(GetLastError());
114 rc = RTUtf16ToUtf8(wszUsername, &pszUser);
115 aUser = pszUser;
116 aUser.toLower();
117 RTStrFree(pszUser);
118 return rc;
119}
120
121static int autostartGetLocalDomain(com::Utf8Str &aDomain)
122{
123 RTUTF16 pwszDomain[MAX_COMPUTERNAME_LENGTH + 1] = { 0 };
124 uint32_t cwcDomainSize = MAX_COMPUTERNAME_LENGTH + 1;
125 if (!GetComputerNameW(pwszDomain, (LPDWORD)&cwcDomainSize))
126 return RTErrConvertFromWin32(GetLastError());
127 char *pszDomain = NULL;
128 int rc = RTUtf16ToUtf8(pwszDomain, &pszDomain);
129 aDomain = pszDomain;
130 aDomain.toLower();
131 RTStrFree(pszDomain);
132 return rc;
133}
134
135static int autostartGetDomainAndUser(const com::Utf8Str &aDomainAndUser, com::Utf8Str &aDomain, com::Utf8Str &aUser)
136{
137 size_t offDelim = aDomainAndUser.find("\\");
138 if (offDelim != aDomainAndUser.npos)
139 {
140 // if only domain is specified
141 if (aDomainAndUser.length() - offDelim == 1)
142 return VERR_INVALID_PARAMETER;
143
144 if (offDelim == 1 && aDomainAndUser[0] == '.')
145 {
146 int rc = autostartGetLocalDomain(aDomain);
147 aUser = aDomainAndUser.substr(offDelim + 1);
148 return rc;
149 }
150 aDomain = aDomainAndUser.substr(0, offDelim);
151 aUser = aDomainAndUser.substr(offDelim + 1);
152 aDomain.toLower();
153 aUser.toLower();
154 return VINF_SUCCESS;
155 }
156
157 offDelim = aDomainAndUser.find("@");
158 if (offDelim != aDomainAndUser.npos)
159 {
160 // if only domain is specified
161 if (offDelim == 0)
162 return VERR_INVALID_PARAMETER;
163
164 // with '@' but without domain
165 if (aDomainAndUser.length() - offDelim == 1)
166 {
167 int rc = autostartGetLocalDomain(aDomain);
168 aUser = aDomainAndUser.substr(0, offDelim);
169 return rc;
170 }
171 aDomain = aDomainAndUser.substr(offDelim + 1);
172 aUser = aDomainAndUser.substr(0, offDelim);
173 aDomain.toLower();
174 aUser.toLower();
175 return VINF_SUCCESS;
176 }
177
178 // only user is specified
179 int rc = autostartGetLocalDomain(aDomain);
180 aUser = aDomainAndUser;
181 aDomain.toLower();
182 aUser.toLower();
183 return rc;
184}
185
186/** Common helper for formatting the service name. */
187static void autostartFormatServiceName(const com::Utf8Str &aDomain, const com::Utf8Str &aUser, com::Utf8Str &aServiceName)
188{
189 aServiceName.printf("%s%s%s", AUTOSTART_SERVICE_NAME, aDomain.c_str(), aUser.c_str());
190}
191
192/** Used by the delete service operation. */
193static int autostartGetServiceName(const com::Utf8Str &aDomainAndUser, com::Utf8Str &aServiceName)
194{
195 com::Utf8Str sDomain;
196 com::Utf8Str sUser;
197 int rc = autostartGetDomainAndUser(aDomainAndUser, sDomain, sUser);
198 if (RT_FAILURE(rc))
199 return rc;
200 autostartFormatServiceName(sDomain, sUser, aServiceName);
201 return VINF_SUCCESS;
202}
203
204/**
205 * Print out progress on the console.
206 *
207 * This runs the main event queue every now and then to prevent piling up
208 * unhandled things (which doesn't cause real problems, just makes things
209 * react a little slower than in the ideal case).
210 */
211DECLHIDDEN(HRESULT) showProgress(ComPtr<IProgress> progress)
212{
213 using namespace com;
214
215 BOOL fCompleted = FALSE;
216 ULONG uCurrentPercent = 0;
217 Bstr bstrOperationDescription;
218
219 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
220
221 ULONG cOperations = 1;
222 HRESULT hrc = progress->COMGETTER(OperationCount)(&cOperations);
223 if (FAILED(hrc))
224 return hrc;
225
226 /* setup signal handling if cancelable */
227 bool fCanceledAlready = false;
228 BOOL fCancelable;
229 hrc = progress->COMGETTER(Cancelable)(&fCancelable);
230 if (FAILED(hrc))
231 fCancelable = FALSE;
232
233 hrc = progress->COMGETTER(Completed(&fCompleted));
234 while (SUCCEEDED(hrc))
235 {
236 progress->COMGETTER(Percent(&uCurrentPercent));
237
238 if (fCompleted)
239 break;
240
241 /* process async cancelation */
242 if (!fCanceledAlready)
243 {
244 hrc = progress->Cancel();
245 if (SUCCEEDED(hrc))
246 fCanceledAlready = true;
247 }
248
249 /* make sure the loop is not too tight */
250 progress->WaitForCompletion(100);
251
252 NativeEventQueue::getMainEventQueue()->processEventQueue(0);
253 hrc = progress->COMGETTER(Completed(&fCompleted));
254 }
255
256 /* complete the line. */
257 LONG iRc = E_FAIL;
258 hrc = progress->COMGETTER(ResultCode)(&iRc);
259 if (SUCCEEDED(hrc))
260 {
261 hrc = iRc;
262 }
263
264 return hrc;
265}
266
267DECLHIDDEN(void) autostartSvcOsLogStr(const char *pszMsg, AUTOSTARTLOGTYPE enmLogType)
268{
269 /* write it to the console + release log too (if configured). */
270 LogRel(("%s", pszMsg));
271
272 /** @todo r=andy Only (un)register source once? */
273 HANDLE hEventLog = RegisterEventSourceA(NULL /* local computer */, "VBoxAutostartSvc");
274 AssertReturnVoid(hEventLog != NULL);
275 WORD wType = 0;
276 const char *apsz[2];
277 apsz[0] = "VBoxAutostartSvc";
278 apsz[1] = pszMsg;
279
280 switch (enmLogType)
281 {
282 case AUTOSTARTLOGTYPE_INFO:
283 RTStrmPrintf(g_pStdOut, "%s", pszMsg);
284 wType = 0;
285 break;
286 case AUTOSTARTLOGTYPE_ERROR:
287 RTStrmPrintf(g_pStdErr, "Error: %s", pszMsg);
288 wType = EVENTLOG_ERROR_TYPE;
289 break;
290 case AUTOSTARTLOGTYPE_WARNING:
291 RTStrmPrintf(g_pStdOut, "Warning: %s", pszMsg);
292 wType = EVENTLOG_WARNING_TYPE;
293 break;
294 case AUTOSTARTLOGTYPE_VERBOSE:
295 RTStrmPrintf(g_pStdOut, "%s", pszMsg);
296 wType = EVENTLOG_INFORMATION_TYPE;
297 break;
298 default:
299 AssertMsgFailed(("Invalid log type %#x\n", enmLogType));
300 break;
301 }
302
303 /** @todo r=andy Why ANSI and not Unicode (xxxW)? */
304 BOOL fRc = ReportEventA(hEventLog, /* hEventLog */
305 wType, /* wType */
306 0, /* wCategory */
307 0 /** @todo mc */, /* dwEventID */
308 NULL, /* lpUserSid */
309 RT_ELEMENTS(apsz), /* wNumStrings */
310 0, /* dwDataSize */
311 apsz, /* lpStrings */
312 NULL); /* lpRawData */
313 AssertMsg(fRc, ("ReportEventA failed with %ld\n", GetLastError())); RT_NOREF(fRc);
314 DeregisterEventSource(hEventLog);
315}
316
317
318/**
319 * Adds "logon as service" policy to user rights
320 *
321 * When this fails, an error message will be displayed.
322 *
323 * @returns VBox status code.
324 *
325 * @param sUser The name of user whom the policy should be added.
326 */
327static int autostartUpdatePolicy(const com::Utf8Str &sUser)
328{
329 LSA_OBJECT_ATTRIBUTES objectAttributes;
330 /* Object attributes are reserved, so initialize to zeros. */
331 RT_ZERO(objectAttributes);
332
333 int vrc;
334
335 /* Get a handle to the Policy object. */
336 LSA_HANDLE hPolicy;
337 NTSTATUS ntRc = LsaOpenPolicy( NULL, &objectAttributes, POLICY_ALL_ACCESS, &hPolicy);
338 if (ntRc != STATUS_SUCCESS)
339 {
340 DWORD dwErr = LsaNtStatusToWinError(ntRc);
341 vrc = RTErrConvertFromWin32(dwErr);
342 autostartSvcDisplayError("LsaOpenPolicy failed rc=%Rrc (%#x)\n", vrc, dwErr);
343 return vrc;
344 }
345 /* Get user SID */
346 DWORD cbDomain = 0;
347 SID_NAME_USE enmSidUse = SidTypeUser;
348 RTUTF16 *pwszUser = NULL;
349 size_t cwUser = 0;
350 vrc = RTStrToUtf16Ex(sUser.c_str(), sUser.length(), &pwszUser, 0, &cwUser);
351 if (RT_SUCCESS(vrc))
352 {
353 PSID pSid = NULL;
354 DWORD cbSid = 0;
355 if (!LookupAccountNameW( NULL, pwszUser, pSid, &cbSid, NULL, &cbDomain, &enmSidUse))
356 {
357 DWORD dwErr = GetLastError();
358 if (dwErr == ERROR_INSUFFICIENT_BUFFER)
359 {
360 pSid = (PSID)RTMemAllocZ(cbSid);
361 if (pSid != NULL)
362 {
363 PRTUTF16 pwszDomain = (PRTUTF16)RTMemAllocZ(cbDomain * sizeof(RTUTF16));
364 if (pwszDomain != NULL)
365 {
366 if (LookupAccountNameW( NULL, pwszUser, pSid, &cbSid, pwszDomain, &cbDomain, &enmSidUse))
367 {
368 if (enmSidUse != SidTypeUser)
369 {
370 vrc = VERR_INVALID_PARAMETER;
371 autostartSvcDisplayError("The name %s is not the user\n", sUser.c_str());
372 }
373 else
374 {
375 /* Add privilege */
376 LSA_UNICODE_STRING lwsPrivilege;
377 // Create an LSA_UNICODE_STRING for the privilege names.
378 lwsPrivilege.Buffer = L"SeServiceLogonRight";
379 size_t cwPrivilege = wcslen(lwsPrivilege.Buffer);
380 lwsPrivilege.Length = (USHORT)cwPrivilege * sizeof(WCHAR);
381 lwsPrivilege.MaximumLength = (USHORT)(cwPrivilege + 1) * sizeof(WCHAR);
382 ntRc = LsaAddAccountRights(hPolicy, pSid, &lwsPrivilege, 1);
383 if (ntRc != STATUS_SUCCESS)
384 {
385 dwErr = LsaNtStatusToWinError(ntRc);
386 vrc = RTErrConvertFromWin32(dwErr);
387 autostartSvcDisplayError("LsaAddAccountRights failed rc=%Rrc (%#x)\n", vrc, dwErr);
388 }
389 }
390 }
391 else
392 {
393 dwErr = GetLastError();
394 vrc = RTErrConvertFromWin32(dwErr);
395 autostartSvcDisplayError("LookupAccountName failed rc=%Rrc (%#x)\n", vrc, dwErr);
396 }
397 RTMemFree(pwszDomain);
398 }
399 else
400 {
401 vrc = VERR_NO_MEMORY;
402 autostartSvcDisplayError("autostartUpdatePolicy failed rc=%Rrc\n", vrc);
403 }
404
405 RTMemFree(pSid);
406 }
407 else
408 {
409 vrc = VERR_NO_MEMORY;
410 autostartSvcDisplayError("autostartUpdatePolicy failed rc=%Rrc\n", vrc);
411 }
412 }
413 else
414 {
415 vrc = RTErrConvertFromWin32(dwErr);
416 autostartSvcDisplayError("LookupAccountName failed rc=%Rrc (%#x)\n", vrc, dwErr);
417 }
418 }
419 }
420 else
421 autostartSvcDisplayError("Failed to convert user name rc=%Rrc\n", vrc);
422
423 if (pwszUser != NULL)
424 RTUtf16Free(pwszUser);
425
426 LsaClose(hPolicy);
427 return vrc;
428}
429
430
431/**
432 * Opens the service control manager.
433 *
434 * When this fails, an error message will be displayed.
435 *
436 * @returns Valid handle on success.
437 * NULL on failure, will display an error message.
438 *
439 * @param pszAction The action which is requesting access to SCM.
440 * @param dwAccess The desired access.
441 */
442static SC_HANDLE autostartSvcWinOpenSCManager(const char *pszAction, DWORD dwAccess)
443{
444 SC_HANDLE hSCM = OpenSCManager(NULL /* lpMachineName*/, NULL /* lpDatabaseName */, dwAccess);
445 if (hSCM == NULL)
446 {
447 DWORD err = GetLastError();
448 switch (err)
449 {
450 case ERROR_ACCESS_DENIED:
451 autostartSvcDisplayError("%s - OpenSCManager failure: access denied\n", pszAction);
452 break;
453 default:
454 autostartSvcDisplayError("%s - OpenSCManager failure: %d\n", pszAction, err);
455 break;
456 }
457 }
458 return hSCM;
459}
460
461
462/**
463 * Opens the service.
464 *
465 * Last error is preserved on failure and set to 0 on success.
466 *
467 * @returns Valid service handle on success.
468 * NULL on failure, will display an error message unless it's ignored.
469 * Use GetLastError() to find out what the last Windows error was.
470 *
471 * @param pszAction The action which is requesting access to the service.
472 * @param dwSCMAccess The service control manager access.
473 * @param dwSVCAccess The desired service access.
474 * @param cIgnoredErrors The number of ignored errors.
475 * @param ... Errors codes that should not cause a message to be displayed.
476 */
477static SC_HANDLE autostartSvcWinOpenService(const PRTUTF16 pwszServiceName, const char *pszAction, DWORD dwSCMAccess, DWORD dwSVCAccess,
478 unsigned cIgnoredErrors, ...)
479{
480 SC_HANDLE hSCM = autostartSvcWinOpenSCManager(pszAction, dwSCMAccess);
481 if (!hSCM)
482 return NULL;
483
484 SC_HANDLE hSvc = OpenServiceW(hSCM, pwszServiceName, dwSVCAccess);
485 if (hSvc)
486 {
487 CloseServiceHandle(hSCM);
488 SetLastError(0);
489 }
490 else
491 {
492 DWORD const dwErr = GetLastError();
493 bool fIgnored = false;
494 va_list va;
495 va_start(va, cIgnoredErrors);
496 while (!fIgnored && cIgnoredErrors-- > 0)
497 fIgnored = (DWORD)va_arg(va, int) == dwErr;
498 va_end(va);
499 if (!fIgnored)
500 {
501 switch (dwErr)
502 {
503 case ERROR_ACCESS_DENIED:
504 autostartSvcDisplayError("%s - OpenService failure: access denied\n", pszAction);
505 break;
506 case ERROR_SERVICE_DOES_NOT_EXIST:
507 autostartSvcDisplayError("%s - OpenService failure: The service %ls does not exist. Reinstall it.\n",
508 pszAction, pwszServiceName);
509 break;
510 default:
511 autostartSvcDisplayError("%s - OpenService failure, rc=%Rrc (%#x)\n", RTErrConvertFromWin32(dwErr), dwErr);
512 break;
513 }
514 }
515
516 CloseServiceHandle(hSCM);
517 SetLastError(dwErr);
518 }
519 return hSvc;
520}
521
522static RTEXITCODE autostartSvcWinInterrogate(int argc, char **argv)
523{
524 RT_NOREF(argc, argv);
525 RTPrintf("VBoxAutostartSvc: The \"interrogate\" action is not implemented.\n");
526 return RTEXITCODE_FAILURE;
527}
528
529
530static RTEXITCODE autostartSvcWinStop(int argc, char **argv)
531{
532 RT_NOREF(argc, argv);
533 RTPrintf("VBoxAutostartSvc: The \"stop\" action is not implemented.\n");
534 return RTEXITCODE_FAILURE;
535}
536
537
538static RTEXITCODE autostartSvcWinContinue(int argc, char **argv)
539{
540 RT_NOREF(argc, argv);
541 RTPrintf("VBoxAutostartSvc: The \"continue\" action is not implemented.\n");
542 return RTEXITCODE_FAILURE;
543}
544
545
546static RTEXITCODE autostartSvcWinPause(int argc, char **argv)
547{
548 RT_NOREF(argc, argv);
549 RTPrintf("VBoxAutostartSvc: The \"pause\" action is not implemented.\n");
550 return RTEXITCODE_FAILURE;
551}
552
553
554static RTEXITCODE autostartSvcWinStart(int argc, char **argv)
555{
556 RT_NOREF(argc, argv);
557 RTPrintf("VBoxAutostartSvc: The \"start\" action is not implemented.\n");
558 return RTEXITCODE_SUCCESS;
559}
560
561
562static RTEXITCODE autostartSvcWinQueryDescription(int argc, char **argv)
563{
564 RT_NOREF(argc, argv);
565 RTPrintf("VBoxAutostartSvc: The \"qdescription\" action is not implemented.\n");
566 return RTEXITCODE_FAILURE;
567}
568
569
570static RTEXITCODE autostartSvcWinQueryConfig(int argc, char **argv)
571{
572 RT_NOREF(argc, argv);
573 RTPrintf("VBoxAutostartSvc: The \"qconfig\" action is not implemented.\n");
574 return RTEXITCODE_FAILURE;
575}
576
577
578static RTEXITCODE autostartSvcWinDisable(int argc, char **argv)
579{
580 RT_NOREF(argc, argv);
581 RTPrintf("VBoxAutostartSvc: The \"disable\" action is not implemented.\n");
582 return RTEXITCODE_FAILURE;
583}
584
585static RTEXITCODE autostartSvcWinEnable(int argc, char **argv)
586{
587 RT_NOREF(argc, argv);
588 RTPrintf("VBoxAutostartSvc: The \"enable\" action is not implemented.\n");
589 return RTEXITCODE_FAILURE;
590}
591
592
593/**
594 * Handle the 'delete' action.
595 *
596 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE.
597 * @param argc The action argument count.
598 * @param argv The action argument vector.
599 */
600static RTEXITCODE autostartSvcWinDelete(int argc, char **argv)
601{
602 /*
603 * Parse the arguments.
604 */
605 const char *pszUser = NULL;
606 static const RTGETOPTDEF s_aOptions[] =
607 {
608 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
609 { "--user", 'u', RTGETOPT_REQ_STRING },
610 };
611 int ch;
612 RTGETOPTUNION Value;
613 RTGETOPTSTATE GetState;
614 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
615 while ((ch = RTGetOpt(&GetState, &Value)))
616 {
617 switch (ch)
618 {
619 case 'v':
620 g_cVerbosity++;
621 break;
622 case 'u':
623 pszUser = Value.psz;
624 break;
625 default:
626 return autostartSvcDisplayGetOptError("delete", ch, &Value);
627 }
628 }
629
630 if (!pszUser)
631 return autostartSvcDisplayError("delete - DeleteService failed, user name required.\n");
632
633 com::Utf8Str sServiceName;
634 int vrc = autostartGetServiceName(pszUser, sServiceName);
635 if (RT_FAILURE(vrc))
636 return autostartSvcDisplayError("delete - DeleteService failed, service name for user %s cannot be constructed.\n",
637 pszUser);
638 /*
639 * Delete the service.
640 */
641 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
642 SC_HANDLE hSvc = autostartSvcWinOpenService(com::Bstr(sServiceName).raw(), "delete", SERVICE_CHANGE_CONFIG, DELETE, 0);
643 if (hSvc)
644 {
645 if (DeleteService(hSvc))
646 {
647 if (g_cVerbosity)
648 RTPrintf("Successfully deleted the %s service.\n", sServiceName.c_str());
649 rcExit = RTEXITCODE_SUCCESS;
650 }
651 else
652 {
653 DWORD const dwErr = GetLastError();
654 autostartSvcDisplayError("delete - DeleteService failed, rc=%Rrc (%#x)\n", RTErrConvertFromWin32(dwErr), dwErr);
655 }
656 CloseServiceHandle(hSvc);
657 }
658 return rcExit;
659}
660
661
662/**
663 * Handle the 'create' action.
664 *
665 * @returns 0 or 1.
666 * @param argc The action argument count.
667 * @param argv The action argument vector.
668 */
669static RTEXITCODE autostartSvcWinCreate(int argc, char **argv)
670{
671 /*
672 * Parse the arguments.
673 */
674 const char *pszUser = NULL;
675 com::Utf8Str strPwd;
676 const char *pszPwdFile = NULL;
677 static const RTGETOPTDEF s_aOptions[] =
678 {
679 /* Common options first. */
680 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
681 { "--user", 'u', RTGETOPT_REQ_STRING },
682 { "--username", 'u', RTGETOPT_REQ_STRING },
683 { "--password-file", 'p', RTGETOPT_REQ_STRING }
684 };
685 int ch;
686 RTGETOPTUNION Value;
687 RTGETOPTSTATE GetState;
688 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
689 while ((ch = RTGetOpt(&GetState, &Value)))
690 {
691 switch (ch)
692 {
693 /* Common options first. */
694 case 'v':
695 g_cVerbosity++;
696 break;
697 case 'u':
698 pszUser = Value.psz;
699 break;
700 case 'p':
701 pszPwdFile = Value.psz;
702 break;
703 default:
704 return autostartSvcDisplayGetOptError("create", ch, &Value);
705 }
706 }
707
708 if (!pszUser)
709 return autostartSvcDisplayError("Username is missing");
710
711 if (pszPwdFile)
712 {
713 /* Get password from file. */
714 RTEXITCODE rcExit = readPasswordFile(pszPwdFile, &strPwd);
715 if (rcExit == RTEXITCODE_FAILURE)
716 return rcExit;
717 }
718 else
719 {
720 /* Get password from console. */
721 RTEXITCODE rcExit = readPasswordFromConsole(&strPwd, "Enter password:");
722 if (rcExit == RTEXITCODE_FAILURE)
723 return rcExit;
724 }
725
726 if (strPwd.isEmpty())
727 return autostartSvcDisplayError("Password is missing");
728
729 com::Utf8Str sDomain;
730 com::Utf8Str sUserTmp;
731 int vrc = autostartGetDomainAndUser(pszUser, sDomain, sUserTmp);
732 if (RT_FAILURE(vrc))
733 return autostartSvcDisplayError("create - Failed to get domain and user from string '%s' (%Rrc)\n",
734 pszUser, vrc);
735 com::Utf8StrFmt sUserFullName("%s\\%s", sDomain.c_str(), sUserTmp.c_str());
736 com::Utf8StrFmt sDisplayName("%s %s@%s", AUTOSTART_SERVICE_DISPLAY_NAME, sUserTmp.c_str(), sDomain.c_str());
737 com::Utf8Str sServiceName;
738 autostartFormatServiceName(sDomain, sUserTmp, sServiceName);
739
740 vrc = autostartUpdatePolicy(sUserFullName);
741 if (RT_FAILURE(vrc))
742 return autostartSvcDisplayError("Failed to get/update \"logon as service\" policy for user %s (%Rrc)\n",
743 sUserFullName.c_str(), vrc);
744 /*
745 * Create the service.
746 */
747 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
748 SC_HANDLE hSCM = autostartSvcWinOpenSCManager("create", SC_MANAGER_CREATE_SERVICE); /*SC_MANAGER_ALL_ACCESS*/
749 if (hSCM)
750 {
751 char szExecPath[RTPATH_MAX];
752 if (RTProcGetExecutablePath(szExecPath, sizeof(szExecPath)))
753 {
754 if (g_cVerbosity)
755 RTPrintf("Creating the %s service, binary \"%s\"...\n",
756 sServiceName.c_str(), szExecPath); /* yea, the binary name isn't UTF-8, but wtf. */
757
758 /*
759 * Add service name as command line parameter for the service
760 */
761 com::Utf8StrFmt sCmdLine("\"%s\" --service=%s", szExecPath, sServiceName.c_str());
762 com::Bstr bstrServiceName(sServiceName);
763 com::Bstr bstrDisplayName(sDisplayName);
764 com::Bstr bstrCmdLine(sCmdLine);
765 com::Bstr bstrUserFullName(sUserFullName);
766 com::Bstr bstrPwd(strPwd);
767 com::Bstr bstrDependencies("Winmgmt\0RpcSs\0\0");
768
769 SC_HANDLE hSvc = CreateServiceW(hSCM, /* hSCManager */
770 bstrServiceName.raw(), /* lpServiceName */
771 bstrDisplayName.raw(), /* lpDisplayName */
772 SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG, /* dwDesiredAccess */
773 SERVICE_WIN32_OWN_PROCESS, /* dwServiceType ( | SERVICE_INTERACTIVE_PROCESS? ) */
774 SERVICE_AUTO_START, /* dwStartType */
775 SERVICE_ERROR_NORMAL, /* dwErrorControl */
776 bstrCmdLine.raw(), /* lpBinaryPathName */
777 NULL, /* lpLoadOrderGroup */
778 NULL, /* lpdwTagId */
779 bstrDependencies.raw(), /* lpDependencies */
780 bstrUserFullName.raw(), /* lpServiceStartName (NULL => LocalSystem) */
781 bstrPwd.raw()); /* lpPassword */
782 if (hSvc)
783 {
784 RTPrintf("Successfully created the %s service.\n", sServiceName.c_str());
785 /** @todo Set the service description or it'll look weird in the vista service manager.
786 * Anything else that should be configured? Start access or something? */
787 rcExit = RTEXITCODE_SUCCESS;
788 CloseServiceHandle(hSvc);
789 }
790 else
791 {
792 DWORD const dwErr = GetLastError();
793 switch (dwErr)
794 {
795 case ERROR_SERVICE_EXISTS:
796 autostartSvcDisplayError("create - The service already exists!\n");
797 break;
798 default:
799 autostartSvcDisplayError("create - CreateService failed, rc=%Rrc (%#x)\n",
800 RTErrConvertFromWin32(dwErr), dwErr);
801 break;
802 }
803 }
804 CloseServiceHandle(hSvc);
805 }
806 else
807 autostartSvcDisplayError("create - Failed to obtain the executable path\n");
808 }
809 return rcExit;
810}
811
812
813/**
814 * Sets the service status, just a SetServiceStatus Wrapper.
815 *
816 * @returns See SetServiceStatus.
817 * @param dwStatus The current status.
818 * @param iWaitHint The wait hint, if < 0 then supply a default.
819 * @param dwExitCode The service exit code.
820 */
821static bool autostartSvcWinSetServiceStatus(DWORD dwStatus, int iWaitHint, DWORD dwExitCode)
822{
823 SERVICE_STATUS SvcStatus;
824 SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
825 SvcStatus.dwWin32ExitCode = dwExitCode;
826 SvcStatus.dwServiceSpecificExitCode = 0;
827 SvcStatus.dwWaitHint = iWaitHint >= 0 ? iWaitHint : 3000;
828 SvcStatus.dwCurrentState = dwStatus;
829 LogFlow(("autostartSvcWinSetServiceStatus: %d -> %d\n", g_u32SupSvcWinStatus, dwStatus));
830 g_u32SupSvcWinStatus = dwStatus;
831 switch (dwStatus)
832 {
833 case SERVICE_START_PENDING:
834 SvcStatus.dwControlsAccepted = 0;
835 break;
836 default:
837 SvcStatus.dwControlsAccepted
838 = SERVICE_ACCEPT_STOP
839 | SERVICE_ACCEPT_SHUTDOWN;
840 break;
841 }
842
843 static DWORD dwCheckPoint = 0;
844 switch (dwStatus)
845 {
846 case SERVICE_RUNNING:
847 case SERVICE_STOPPED:
848 SvcStatus.dwCheckPoint = 0;
849 default:
850 SvcStatus.dwCheckPoint = ++dwCheckPoint;
851 break;
852 }
853 return SetServiceStatus(g_hSupSvcWinCtrlHandler, &SvcStatus) != FALSE;
854}
855
856
857/**
858 * Service control handler (extended).
859 *
860 * @returns Windows status (see HandlerEx).
861 * @retval NO_ERROR if handled.
862 * @retval ERROR_CALL_NOT_IMPLEMENTED if not handled.
863 *
864 * @param dwControl The control code.
865 * @param dwEventType Event type. (specific to the control?)
866 * @param pvEventData Event data, specific to the event.
867 * @param pvContext The context pointer registered with the handler.
868 * Currently not used.
869 */
870static DWORD WINAPI
871autostartSvcWinServiceCtrlHandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID pvEventData, LPVOID pvContext) RT_NOTHROW_DEF
872{
873 RT_NOREF(dwEventType);
874 RT_NOREF(pvEventData);
875 RT_NOREF(pvContext);
876
877 LogFlow(("autostartSvcWinServiceCtrlHandlerEx: dwControl=%#x dwEventType=%#x pvEventData=%p\n",
878 dwControl, dwEventType, pvEventData));
879
880 switch (dwControl)
881 {
882 /*
883 * Interrogate the service about it's current status.
884 * MSDN says that this should just return NO_ERROR and does
885 * not need to set the status again.
886 */
887 case SERVICE_CONTROL_INTERROGATE:
888 return NO_ERROR;
889
890 /*
891 * Request to stop the service.
892 */
893 case SERVICE_CONTROL_SHUTDOWN:
894 case SERVICE_CONTROL_STOP:
895 {
896 if (dwControl == SERVICE_CONTROL_SHUTDOWN)
897 autostartSvcLogVerbose(1, "SERVICE_CONTROL_SHUTDOWN\n");
898 else
899 autostartSvcLogVerbose(1, "SERVICE_CONTROL_STOP\n");
900
901 /*
902 * Check if the real services can be stopped and then tell them to stop.
903 */
904 autostartSvcWinSetServiceStatus(SERVICE_STOP_PENDING, 3000, NO_ERROR);
905
906 /*
907 * Notify the main thread that we're done, it will wait for the
908 * VMs to stop, and set the windows service status to SERVICE_STOPPED
909 * and return.
910 */
911 int rc = RTSemEventMultiSignal(g_hSupSvcWinEvent);
912 if (RT_FAILURE(rc)) /** @todo r=andy Don't we want to report back an error here to SCM? */
913 autostartSvcLogErrorRc(rc, "SERVICE_CONTROL_STOP: RTSemEventMultiSignal failed, %Rrc\n", rc);
914
915 return NO_ERROR;
916 }
917
918 default:
919 /*
920 * We only expect to receive controls we explicitly listed
921 * in SERVICE_STATUS::dwControlsAccepted. Logged in hex
922 * b/c WinSvc.h defines them in hex
923 */
924 autostartSvcLogWarning("Unexpected service control message 0x%RX64\n", (uint64_t)dwControl);
925 break;
926 }
927
928 return ERROR_CALL_NOT_IMPLEMENTED;
929}
930
931static int autostartStartVMs(void)
932{
933 int rc = autostartSetup();
934 if (RT_FAILURE(rc))
935 return rc;
936
937 const char *pszConfigFile = RTEnvGet("VBOXAUTOSTART_CONFIG");
938 if (!pszConfigFile)
939 return autostartSvcLogErrorRc(VERR_ENV_VAR_NOT_FOUND,
940 "Starting VMs failed. VBOXAUTOSTART_CONFIG environment variable is not defined.\n");
941 bool fAllow = false;
942
943 PCFGAST pCfgAst = NULL;
944 rc = autostartParseConfig(pszConfigFile, &pCfgAst);
945 if (RT_FAILURE(rc))
946 return autostartSvcLogErrorRc(rc, "Starting VMs failed. Failed to parse the config file. Check the access permissions and file structure.\n");
947 PCFGAST pCfgAstPolicy = autostartConfigAstGetByName(pCfgAst, "default_policy");
948 /* Check default policy. */
949 if (pCfgAstPolicy)
950 {
951 if ( pCfgAstPolicy->enmType == CFGASTNODETYPE_KEYVALUE
952 && ( !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "allow")
953 || !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "deny")))
954 {
955 if (!RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "allow"))
956 fAllow = true;
957 }
958 else
959 {
960 autostartConfigAstDestroy(pCfgAst);
961 return autostartSvcLogErrorRc(VERR_INVALID_PARAMETER, "'default_policy' must be either 'allow' or 'deny'.\n");
962 }
963 }
964
965 com::Utf8Str sUser;
966 rc = autostartGetProcessDomainUser(sUser);
967 if (RT_FAILURE(rc))
968 {
969 autostartConfigAstDestroy(pCfgAst);
970 return autostartSvcLogErrorRc(rc, "Failed to query username of the process (%Rrc).\n", rc);
971 }
972
973 PCFGAST pCfgAstUser = NULL;
974 for (unsigned i = 0; i < pCfgAst->u.Compound.cAstNodes; i++)
975 {
976 PCFGAST pNode = pCfgAst->u.Compound.apAstNodes[i];
977 com::Utf8Str sDomain;
978 com::Utf8Str sUserTmp;
979 rc = autostartGetDomainAndUser(pNode->pszKey, sDomain, sUserTmp);
980 if (RT_FAILURE(rc))
981 continue;
982 com::Utf8StrFmt sDomainUser("%s\\%s", sDomain.c_str(), sUserTmp.c_str());
983 if (sDomainUser == sUser)
984 {
985 pCfgAstUser = pNode;
986 break;
987 }
988 }
989
990 if ( pCfgAstUser
991 && pCfgAstUser->enmType == CFGASTNODETYPE_COMPOUND)
992 {
993 pCfgAstPolicy = autostartConfigAstGetByName(pCfgAstUser, "allow");
994 if (pCfgAstPolicy)
995 {
996 if ( pCfgAstPolicy->enmType == CFGASTNODETYPE_KEYVALUE
997 && ( !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "true")
998 || !RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "false")))
999 fAllow = RTStrCmp(pCfgAstPolicy->u.KeyValue.aszValue, "true") == 0;
1000 else
1001 {
1002 autostartConfigAstDestroy(pCfgAst);
1003 return autostartSvcLogErrorRc(VERR_INVALID_PARAMETER, "'allow' must be either 'true' or 'false'.\n");
1004 }
1005 }
1006 }
1007 else if (pCfgAstUser)
1008 {
1009 autostartConfigAstDestroy(pCfgAst);
1010 return autostartSvcLogErrorRc(VERR_INVALID_PARAMETER, "Invalid config, user is not a compound node.\n");
1011 }
1012
1013 if (!fAllow)
1014 {
1015 autostartConfigAstDestroy(pCfgAst);
1016 return autostartSvcLogErrorRc(VERR_INVALID_PARAMETER, "User is not allowed to autostart VMs.\n");
1017 }
1018
1019 if (RT_SUCCESS(rc))
1020 rc = autostartStartMain(pCfgAstUser);
1021
1022 autostartConfigAstDestroy(pCfgAst);
1023
1024 return rc;
1025}
1026
1027/**
1028 * Windows Service Main.
1029 *
1030 * This is invoked when the service is started and should not return until
1031 * the service has been stopped.
1032 *
1033 * @param cArgs Argument count.
1034 * @param papwszArgs Argument vector.
1035 */
1036static VOID WINAPI autostartSvcWinServiceMain(DWORD cArgs, LPWSTR *papwszArgs)
1037{
1038 RT_NOREF(cArgs, papwszArgs);
1039 LogFlowFuncEnter();
1040
1041 /* Give this thread a name in the logs. */
1042 RTThreadAdopt(RTTHREADTYPE_DEFAULT, 0, "service", NULL);
1043
1044#if 0
1045 for (size_t i = 0; i < cArgs; ++i)
1046 LogRel(("arg[%zu] = %ls\n", i, papwszArgs[i]));
1047#endif
1048
1049 DWORD dwErr = ERROR_GEN_FAILURE;
1050
1051 /*
1052 * Register the control handler function for the service and report to SCM.
1053 */
1054 Assert(g_u32SupSvcWinStatus == SERVICE_STOPPED);
1055 g_hSupSvcWinCtrlHandler = RegisterServiceCtrlHandlerExW(g_bstrServiceName.raw(), autostartSvcWinServiceCtrlHandlerEx, NULL);
1056 if (g_hSupSvcWinCtrlHandler)
1057 {
1058 if (autostartSvcWinSetServiceStatus(SERVICE_START_PENDING, 3000, NO_ERROR))
1059 {
1060 /*
1061 * Create the event semaphore we'll be waiting on and
1062 * then instantiate the actual services.
1063 */
1064 int rc = RTSemEventMultiCreate(&g_hSupSvcWinEvent);
1065 if (RT_SUCCESS(rc))
1066 {
1067 /*
1068 * Update the status and enter the work loop.
1069 */
1070 if (autostartSvcWinSetServiceStatus(SERVICE_RUNNING, 0, 0))
1071 {
1072 LogFlow(("autostartSvcWinServiceMain: calling autostartStartVMs\n"));
1073
1074 /* check if we should stopped already, e.g. windows shutdown */
1075 rc = RTSemEventMultiWait(g_hSupSvcWinEvent, 1);
1076 if (RT_FAILURE(rc))
1077 {
1078 /* No one signaled us to stop */
1079 rc = autostartStartVMs();
1080 }
1081 autostartShutdown();
1082 }
1083 else
1084 {
1085 dwErr = GetLastError();
1086 autostartSvcLogError("SetServiceStatus failed, rc=%Rrc (%#x)\n", RTErrConvertFromWin32(dwErr), dwErr);
1087 }
1088
1089 RTSemEventMultiDestroy(g_hSupSvcWinEvent);
1090 g_hSupSvcWinEvent = NIL_RTSEMEVENTMULTI;
1091 }
1092 else
1093 autostartSvcLogError("RTSemEventMultiCreate failed, rc=%Rrc", rc);
1094 }
1095 else
1096 {
1097 dwErr = GetLastError();
1098 autostartSvcLogError("SetServiceStatus failed, rc=%Rrc (%#x)\n", RTErrConvertFromWin32(dwErr), dwErr);
1099 }
1100 autostartSvcWinSetServiceStatus(SERVICE_STOPPED, 0, dwErr);
1101 }
1102 /* else error will be handled by the caller. */
1103}
1104
1105
1106/**
1107 * Handle the 'runit' action.
1108 *
1109 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE.
1110 * @param argc The action argument count.
1111 * @param argv The action argument vector.
1112 */
1113static RTEXITCODE autostartSvcWinRunIt(int argc, char **argv)
1114{
1115 int vrc;
1116
1117 LogFlowFuncEnter();
1118
1119 /*
1120 * Init com here for first main thread initialization.
1121 * Service main function called in another thread
1122 * created by service manager.
1123 */
1124 HRESULT hrc = com::Initialize();
1125# ifdef VBOX_WITH_XPCOM
1126 if (hrc == NS_ERROR_FILE_ACCESS_DENIED)
1127 {
1128 char szHome[RTPATH_MAX] = "";
1129 com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
1130 return RTMsgErrorExit(RTEXITCODE_FAILURE,
1131 "Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
1132 }
1133# endif
1134 if (FAILED(hrc))
1135 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize COM (%Rhrc)!", hrc);
1136 /*
1137 * Initialize release logging, do this early. This means command
1138 * line options (like --logfile &c) can't be introduced to affect
1139 * the log file parameters, but the user can't change them easily
1140 * anyway and is better off using environment variables.
1141 */
1142 do
1143 {
1144 char szLogFile[RTPATH_MAX];
1145 vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile),
1146 /* :fCreateDir */ false);
1147 if (RT_FAILURE(vrc))
1148 {
1149 autostartSvcLogError("Failed to get VirtualBox user home directory: %Rrc\n", vrc);
1150 break;
1151 }
1152
1153 if (!RTDirExists(szLogFile)) /* vbox user home dir */
1154 {
1155 autostartSvcLogError("%s doesn't exist\n", szLogFile);
1156 break;
1157 }
1158
1159 vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxAutostart.log");
1160 if (RT_FAILURE(vrc))
1161 {
1162 autostartSvcLogError( "Failed to construct release log file name: %Rrc\n", vrc);
1163 break;
1164 }
1165
1166 vrc = com::VBoxLogRelCreate(AUTOSTART_SERVICE_NAME,
1167 szLogFile,
1168 RTLOGFLAGS_PREFIX_THREAD
1169 | RTLOGFLAGS_PREFIX_TIME_PROG,
1170 "all",
1171 "VBOXAUTOSTART_RELEASE_LOG",
1172 RTLOGDEST_FILE,
1173 UINT32_MAX /* cMaxEntriesPerGroup */,
1174 g_cHistory,
1175 g_uHistoryFileTime,
1176 g_uHistoryFileSize,
1177 NULL);
1178 if (RT_FAILURE(vrc))
1179 autostartSvcLogError("Failed to create release log file: %Rrc\n", vrc);
1180 } while (0);
1181
1182 /*
1183 * Parse the arguments.
1184 */
1185 static const RTGETOPTDEF s_aOptions[] =
1186 {
1187 /* Common options first. */
1188 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1189 { "--service", 's', RTGETOPT_REQ_STRING },
1190 };
1191
1192 const char *pszServiceName = NULL;
1193 int ch;
1194 RTGETOPTUNION ValueUnion;
1195 RTGETOPTSTATE GetState;
1196 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1197 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1198 {
1199 switch (ch)
1200 {
1201 /* Common options first. */
1202 case 'v':
1203 g_cVerbosity++;
1204 break;
1205 case 's':
1206 pszServiceName = ValueUnion.psz;
1207 try
1208 {
1209 g_bstrServiceName = com::Bstr(ValueUnion.psz);
1210 }
1211 catch (...)
1212 {
1213 autostartSvcLogError("runit failed, service name is not valid UTF-8 string or out of memory");
1214 return RTEXITCODE_FAILURE;
1215 }
1216 break;
1217
1218 default:
1219 return autostartSvcDisplayGetOptError("runit", ch, &ValueUnion);
1220 }
1221 }
1222
1223 if (!pszServiceName)
1224 {
1225 autostartSvcLogError("runit failed, service name is missing");
1226 return RTEXITCODE_SYNTAX;
1227 }
1228
1229 autostartSvcLogInfo("Starting service %ls\n", g_bstrServiceName.raw());
1230
1231 /*
1232 * Register the service with the service control manager
1233 * and start dispatching requests from it (all done by the API).
1234 */
1235 SERVICE_TABLE_ENTRYW const s_aServiceStartTable[] =
1236 {
1237 { g_bstrServiceName.raw(), autostartSvcWinServiceMain },
1238 { NULL, NULL}
1239 };
1240
1241 if (StartServiceCtrlDispatcherW(&s_aServiceStartTable[0]))
1242 {
1243 LogFlowFuncLeave();
1244 return RTEXITCODE_SUCCESS; /* told to quit, so quit. */
1245 }
1246
1247 DWORD const dwErr = GetLastError();
1248 switch (dwErr)
1249 {
1250 case ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
1251 autostartSvcLogWarning("Cannot run a service from the command line. Use the 'start' action to start it the right way.\n");
1252 autostartSvcWinServiceMain(0 /* cArgs */, NULL /* papwszArgs */);
1253 break;
1254 default:
1255 autostartSvcLogError("StartServiceCtrlDispatcher failed, rc=%Rrc (%#x)\n", RTErrConvertFromWin32(dwErr), dwErr);
1256 break;
1257 }
1258
1259 com::Shutdown();
1260
1261 return RTEXITCODE_FAILURE;
1262}
1263
1264
1265/**
1266 * Show the version info.
1267 *
1268 * @returns RTEXITCODE_SUCCESS.
1269 */
1270static RTEXITCODE autostartSvcWinShowVersion(int argc, char **argv)
1271{
1272 /*
1273 * Parse the arguments.
1274 */
1275 bool fBrief = false;
1276 static const RTGETOPTDEF s_aOptions[] =
1277 {
1278 { "--brief", 'b', RTGETOPT_REQ_NOTHING }
1279 };
1280 int ch;
1281 RTGETOPTUNION Value;
1282 RTGETOPTSTATE GetState;
1283 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1284 while ((ch = RTGetOpt(&GetState, &Value)))
1285 switch (ch)
1286 {
1287 case 'b': fBrief = true; break;
1288 default: return autostartSvcDisplayGetOptError("version", ch, &Value);
1289 }
1290
1291 /*
1292 * Do the printing.
1293 */
1294 autostartSvcShowVersion(fBrief);
1295
1296 return RTEXITCODE_SUCCESS;
1297}
1298
1299
1300/**
1301 * Show the usage help screen.
1302 *
1303 * @returns RTEXITCODE_SUCCESS.
1304 */
1305static RTEXITCODE autostartSvcWinShowHelp(void)
1306{
1307 autostartSvcShowHeader();
1308
1309 const char *pszExe = RTPathFilename(RTProcExecutablePath());
1310
1311 RTPrintf("Usage:\n"
1312 "\n"
1313 "%s [global-options] [command] [command-options]\n"
1314 "\n"
1315 "Global options:\n"
1316 " -v\n"
1317 " Increases the verbosity. Can be specified multiple times."
1318 "\n\n"
1319 "No command given:\n"
1320 " Runs the service.\n"
1321 "Options:\n"
1322 " --service <name>\n"
1323 " Specifies the service name to run.\n"
1324 "\n"
1325 "Command </help|help|-?|-h|--help> [...]\n"
1326 " Displays this help screen.\n"
1327 "\n"
1328 "Command </version|version|-V|--version> [-brief]\n"
1329 " Displays the version.\n"
1330 "\n"
1331 "Command </i|install|/RegServer> --user <username> --password-file <...>\n"
1332 " Installs the service.\n"
1333 "Options:\n"
1334 " --user <username>\n"
1335 " Specifies the user name the service should be installed for.\n"
1336 " --password-file <path/to/file>\n"
1337 " Specifies the file for user password to use for installation.\n"
1338 "\n"
1339 "Command </u|uninstall|delete|/UnregServer>\n"
1340 " Uninstalls the service.\n"
1341 " --user <username>\n"
1342 " Specifies the user name the service should will be deleted for.\n",
1343 pszExe);
1344 return RTEXITCODE_SUCCESS;
1345}
1346
1347
1348/**
1349 * VBoxAutostart main(), Windows edition.
1350 *
1351 *
1352 * @returns 0 on success.
1353 *
1354 * @param argc Number of arguments in argv.
1355 * @param argv Argument vector.
1356 */
1357int main(int argc, char **argv)
1358{
1359 /*
1360 * Initialize the IPRT first of all.
1361 */
1362 int rc = RTR3InitExe(argc, &argv, 0);
1363 if (RT_FAILURE(rc))
1364 {
1365 autostartSvcLogError("RTR3InitExe failed with rc=%Rrc", rc);
1366 return RTEXITCODE_FAILURE;
1367 }
1368
1369 /*
1370 * Parse the initial arguments to determine the desired action.
1371 */
1372 enum
1373 {
1374 kAutoSvcAction_RunIt,
1375
1376 kAutoSvcAction_Create,
1377 kAutoSvcAction_Delete,
1378
1379 kAutoSvcAction_Enable,
1380 kAutoSvcAction_Disable,
1381 kAutoSvcAction_QueryConfig,
1382 kAutoSvcAction_QueryDescription,
1383
1384 kAutoSvcAction_Start,
1385 kAutoSvcAction_Pause,
1386 kAutoSvcAction_Continue,
1387 kAutoSvcAction_Stop,
1388 kAutoSvcAction_Interrogate,
1389
1390 kAutoSvcAction_End
1391 } enmAction = kAutoSvcAction_RunIt;
1392 int iArg = 1;
1393 if (argc > 1)
1394 {
1395 if ( !stricmp(argv[iArg], "/RegServer")
1396 || !stricmp(argv[iArg], "install")
1397 || !stricmp(argv[iArg], "/i"))
1398 enmAction = kAutoSvcAction_Create;
1399 else if ( !stricmp(argv[iArg], "/UnregServer")
1400 || !stricmp(argv[iArg], "/u")
1401 || !stricmp(argv[iArg], "uninstall")
1402 || !stricmp(argv[iArg], "delete"))
1403 enmAction = kAutoSvcAction_Delete;
1404
1405 else if (!stricmp(argv[iArg], "enable"))
1406 enmAction = kAutoSvcAction_Enable;
1407 else if (!stricmp(argv[iArg], "disable"))
1408 enmAction = kAutoSvcAction_Disable;
1409 else if (!stricmp(argv[iArg], "qconfig"))
1410 enmAction = kAutoSvcAction_QueryConfig;
1411 else if (!stricmp(argv[iArg], "qdescription"))
1412 enmAction = kAutoSvcAction_QueryDescription;
1413
1414 else if ( !stricmp(argv[iArg], "start")
1415 || !stricmp(argv[iArg], "/t"))
1416 enmAction = kAutoSvcAction_Start;
1417 else if (!stricmp(argv[iArg], "pause"))
1418 enmAction = kAutoSvcAction_Start;
1419 else if (!stricmp(argv[iArg], "continue"))
1420 enmAction = kAutoSvcAction_Continue;
1421 else if (!stricmp(argv[iArg], "stop"))
1422 enmAction = kAutoSvcAction_Stop;
1423 else if (!stricmp(argv[iArg], "interrogate"))
1424 enmAction = kAutoSvcAction_Interrogate;
1425 else if ( !stricmp(argv[iArg], "help")
1426 || !stricmp(argv[iArg], "?")
1427 || !stricmp(argv[iArg], "/?")
1428 || !stricmp(argv[iArg], "-?")
1429 || !stricmp(argv[iArg], "/h")
1430 || !stricmp(argv[iArg], "-h")
1431 || !stricmp(argv[iArg], "/help")
1432 || !stricmp(argv[iArg], "-help")
1433 || !stricmp(argv[iArg], "--help"))
1434 return autostartSvcWinShowHelp();
1435 else if ( !stricmp(argv[iArg], "version")
1436 || !stricmp(argv[iArg], "/ver")
1437 || !stricmp(argv[iArg], "-V") /* Note: "-v" is used for specifying the verbosity. */
1438 || !stricmp(argv[iArg], "/version")
1439 || !stricmp(argv[iArg], "-version")
1440 || !stricmp(argv[iArg], "--version"))
1441 return autostartSvcWinShowVersion(argc - iArg - 1, argv + iArg + 1);
1442 else
1443 iArg--;
1444 iArg++;
1445 }
1446
1447 /*
1448 * Dispatch it.
1449 */
1450 switch (enmAction)
1451 {
1452 case kAutoSvcAction_RunIt:
1453 return autostartSvcWinRunIt(argc - iArg, argv + iArg);
1454
1455 case kAutoSvcAction_Create:
1456 return autostartSvcWinCreate(argc - iArg, argv + iArg);
1457 case kAutoSvcAction_Delete:
1458 return autostartSvcWinDelete(argc - iArg, argv + iArg);
1459
1460 case kAutoSvcAction_Enable:
1461 return autostartSvcWinEnable(argc - iArg, argv + iArg);
1462 case kAutoSvcAction_Disable:
1463 return autostartSvcWinDisable(argc - iArg, argv + iArg);
1464 case kAutoSvcAction_QueryConfig:
1465 return autostartSvcWinQueryConfig(argc - iArg, argv + iArg);
1466 case kAutoSvcAction_QueryDescription:
1467 return autostartSvcWinQueryDescription(argc - iArg, argv + iArg);
1468
1469 case kAutoSvcAction_Start:
1470 return autostartSvcWinStart(argc - iArg, argv + iArg);
1471 case kAutoSvcAction_Pause:
1472 return autostartSvcWinPause(argc - iArg, argv + iArg);
1473 case kAutoSvcAction_Continue:
1474 return autostartSvcWinContinue(argc - iArg, argv + iArg);
1475 case kAutoSvcAction_Stop:
1476 return autostartSvcWinStop(argc - iArg, argv + iArg);
1477 case kAutoSvcAction_Interrogate:
1478 return autostartSvcWinInterrogate(argc - iArg, argv + iArg);
1479
1480 default:
1481 AssertMsgFailed(("enmAction=%d\n", enmAction));
1482 return RTEXITCODE_FAILURE;
1483 }
1484}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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