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