VirtualBox

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

Last change on this file since 89451 was 89359, checked in by vboxsync, 4 years ago

VBoxAutostart: bugref:9341, bugref:8161: Advertise that we accept
SERVICE_CONTROL_SHUTDOWN and stop the service when we get it. This
releases the references that keep VBoxSVC alive - and/or stuck around,
depending on how you view it.

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