VirtualBox

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

Last change on this file since 95171 was 95139, checked in by vboxsync, 3 years ago

FE/VBoxAutostart: Lots of bigger and smaller cleanups, especially regarding mixed up return types, better and more (optionally verbose) logging for the Windows event log, added missing syntax help.

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

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