SUPSvc-win.cpp revision e64031e20c39650a7bc902a3e1aba613b9415dee
/* $Id$ */
/** @file
* VirtualBox Support Service - Windows Specific Code.
*/
/*
* Copyright (C) 2008 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SUP
#include <Windows.h>
#include <iprt/initterm.h>
#include <iprt/semaphore.h>
#ifdef DEBUG_bird
#endif
#include "../SUPSvcInternal.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The service name. */
#define SUPSVC_SERVICE_NAME "VBoxSupSvc"
/** The service display name. */
#define SUPSVC_SERVICE_DISPLAY_NAME "VirtualBox Support Service"
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The service control handler handle. */
/** The service status. */
/** The semaphore the main service thread is waiting on in supSvcWinServiceMain. */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Opens the service control manager.
*
* When this fails, an error message will be displayed.
*
* @returns Valid handle on success.
* NULL on failure, will display an error message.
*
* @param pszAction The action which is requesting access to SCM.
* @param dwAccess The desired access.
*/
{
{
switch (err)
{
case ERROR_ACCESS_DENIED:
break;
default:
break;
}
}
return hSCM;
}
/**
* Opens the service.
*
* Last error is preserved on failure and set to 0 on success.
*
* @returns Valid service handle on success.
* NULL on failure, will display an error message unless it's ignored.
*
* @param pszAction The action which is requestion access to the service.
* @param dwSCMAccess The service control manager access.
* @param dwSVCAccess The desired service access.
* @param cIgnoredErrors The number of ignored errors.
* @param ... Errors codes that should not cause a message to be displayed.
*/
unsigned cIgnoredErrors, ...)
{
if (!hSCM)
return NULL;
if (hSvc)
{
SetLastError(0);
}
else
{
bool fIgnored = false;
while (!fIgnored && cIgnoredErrors-- > 0)
if (!fIgnored)
{
switch (err)
{
case ERROR_ACCESS_DENIED:
break;
supSvcDisplayError("%s - OpenService failure: The service does not exist. Reinstall it.\n", pszAction);
break;
default:
break;
}
}
}
return hSvc;
}
void supSvcOsLogErrorStr(const char *pszMsg)
{
const char *apsz[2];
apsz[0] = "VBoxSupSvc";
EVENTLOG_ERROR_TYPE, /* wType */
0, /* wCategory */
0 /** @todo mc */, /* dwEventID */
NULL, /* lpUserSid */
0, /* dwDataSize */
apsz, /* lpStrings */
NULL); /* lpRawData */
}
{
RTPrintf("VBoxSupSvc: The \"interrogate\" action is not implemented.\n");
return 1;
}
{
RTPrintf("VBoxSupSvc: The \"stop\" action is not implemented.\n");
return 1;
}
{
RTPrintf("VBoxSupSvc: The \"continue\" action is not implemented.\n");
return 1;
}
{
RTPrintf("VBoxSupSvc: The \"pause\" action is not implemented.\n");
return 1;
}
{
RTPrintf("VBoxSupSvc: The \"start\" action is not implemented.\n");
return 1;
}
{
RTPrintf("VBoxSupSvc: The \"qdescription\" action is not implemented.\n");
return 1;
}
{
RTPrintf("VBoxSupSvc: The \"qconfig\" action is not implemented.\n");
return 1;
}
{
RTPrintf("VBoxSupSvc: The \"disable\" action is not implemented.\n");
return 1;
}
{
RTPrintf("VBoxSupSvc: The \"enable\" action is not implemented.\n");
return 1;
}
/**
* Handle the 'delete' action.
*
* @returns 0 or 1.
* @param argc The action argument count.
* @param argv The action argument vector.
*/
{
/*
* Parse the arguments.
*/
bool fVerbose = false;
static const RTGETOPTDEF s_aOptions[] =
{
};
int ch;
RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
switch (ch)
{
case 'v':
fVerbose = true;
break;
case VINF_GETOPT_NOT_OPTION:
default:
}
/*
* Create the service.
*/
int rc = 1;
if (hSvc)
{
if (DeleteService(hSvc))
{
rc = 0;
}
else
}
else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
{
if (fVerbose)
else
rc = 0;
}
return rc;
}
/**
* Handle the 'create' action.
*
* @returns 0 or 1.
* @param argc The action argument count.
* @param argv The action argument vector.
*/
{
/*
* Parse the arguments.
*/
bool fVerbose = false;
static const RTOPTIONDEF s_aOptions[] =
{
};
int iArg = 0;
int ch;
switch (ch)
{
case 'v': fVerbose = true; break;
}
/*
* Create the service.
*/
int rc = 1;
SC_HANDLE hSCM = supSvcWinOpenSCManager("create", SC_MANAGER_CREATE_SERVICE); /*SC_MANAGER_ALL_ACCESS*/
if (hSCM)
{
char szExecPath[MAX_PATH];
{
if (fVerbose)
RTPrintf("Creating the %s service, binary \"%s\"...\n",
SUPSVC_SERVICE_NAME, /* lpServiceName */
SUPSVC_SERVICE_DISPLAY_NAME, /* lpDisplayName */
SERVICE_WIN32_OWN_PROCESS, /* dwServiceType ( | SERVICE_INTERACTIVE_PROCESS? ) */
SERVICE_ERROR_NORMAL, /* dwErrorControl */
szExecPath, /* lpBinaryPathName */
NULL, /* lpLoadOrderGroup */
NULL, /* lpdwTagId */
NULL, /* lpDependencies */
NULL, /* lpServiceStartName (=> LocalSystem) */
NULL); /* lpPassword */
if (hSvc)
{
/** @todo Set the service description or it'll look weird in the vista service manager.
* Anything else that should be configured? Start access or something? */
rc = 0;
}
else
{
switch (err)
{
case ERROR_SERVICE_EXISTS:
supSvcDisplayError("create - The service already exists.\n");
break;
default:
break;
}
}
}
else
}
return rc;
}
/**
* Sets the service status, just a SetServiceStatus Wrapper.
*
* @returns See SetServiceStatus.
* @param dwStatus The current status.
* @param iWaitHint The wait hint, if < 0 then supply a default.
* @param dwExitCode The service exit code.
*/
{
switch (dwStatus)
{
case SERVICE_START_PENDING:
break;
default:
break;
}
static DWORD dwCheckPoint = 0;
switch (dwStatus)
{
case SERVICE_RUNNING:
case SERVICE_STOPPED:
SvcStatus.dwCheckPoint = 0;
default:
break;
}
}
/**
* Service control handler (extended).
*
* @returns Windows status (see HandlerEx).
* @retval NO_ERROR if handled.
* @retval ERROR_CALL_NOT_IMPLEMENTED if not handled.
*
* @param dwControl The control code.
* @param dwEventType Event type. (specific to the control?)
* @param pvEventData Event data, specfic to the event.
* @param pvContext The context pointer registered with the handler.
* Currently not used.
*/
static DWORD WINAPI supSvcWinServiceCtrlHandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID pvEventData, LPVOID pvContext)
{
LogFlow(("supSvcWinServiceCtrlHandlerEx: dwControl=%#x dwEventType=%#x pvEventData=%p\n",
switch (dwControl)
{
/*
* Interrogate the service about it's current status.
* MSDN says that this should just return NO_ERROR and does
* not need to set the status again.
*/
return NO_ERROR;
/*
* Request to stop the service.
*/
case SERVICE_CONTROL_STOP:
{
/*
* Check if the real services can be stopped and then tell them to stop.
*/
int rc = supSvcTryStopServices();
if (RT_SUCCESS(rc))
{
/*
* Notify the main thread that we're done, it will wait for the
* real services to stop, destroy them, and finally set the windows
* service status to SERVICE_STOPPED and return.
*/
if (RT_FAILURE(rc))
}
return NO_ERROR;
}
case SERVICE_CONTROL_PAUSE:
case SERVICE_CONTROL_CONTINUE:
case SERVICE_CONTROL_SHUTDOWN:
#ifdef SERVICE_CONTROL_PRESHUTDOWN /* vista */
#endif
default:
return ERROR_CALL_NOT_IMPLEMENTED;
}
return NO_ERROR;
}
/**
* Windows Service Main.
*
* This is invoked when the service is started and should not return until
* the service has been stopped.
*
* @param cArgs Argument count.
* @param papszArgs Argument vector.
*/
{
/*
* Register the control handler function for the service and report to SCM.
*/
g_hSupSvcWinCtrlHandler = RegisterServiceCtrlHandlerEx(SUPSVC_SERVICE_NAME, supSvcWinServiceCtrlHandlerEx, NULL);
{
{
/*
* Parse arguments.
*/
static const RTOPTIONDEF s_aOptions[] =
{
};
int ch;
int rc = 0;
while ( !rc
switch (ch)
{
}
if (!rc)
{
/*
* Create the event semaphore we'll be waiting on and
* then instantiate the actual services.
*/
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/*
* Update the status and enter the work loop.
*
* The work loop is just a dummy wait here as the services run
* in independant threads.
*/
if (supSvcWinSetServiceStatus(SERVICE_RUNNING, 0, 0))
{
LogFlow(("supSvcWinServiceMain: calling RTSemEventMultiWait\n"));
if (RT_SUCCESS(rc))
{
LogFlow(("supSvcWinServiceMain: woke up\n"));
}
else
}
else
{
err = GetLastError();
}
/*
* Destroy the service instances, stopping them if
* they're still running (weird failure cause).
*/
}
}
else
}
/* else: bad args */
}
else
{
err = GetLastError();
}
}
else
}
/**
* Handle the 'create' action.
*
* @returns 0 or 1.
* @param argc The action argument count.
* @param argv The action argument vector.
*/
{
/*
* Initialize release logging.
*/
/** @todo release logging of the system-wide service. */
/*
* Parse the arguments.
*/
static const RTOPTIONDEF s_aOptions[] =
{
};
int iArg = 0;
int ch;
switch (ch)
{
}
/*
* Register the service with the service control manager
* and start dispatching requests from it (all done by the API).
*/
static SERVICE_TABLE_ENTRY const s_aServiceStartTable[] =
{
};
{
return 0; /* told to quit, so quit. */
}
switch (err)
{
supSvcDisplayError("Cannot run a service from the command line. Use the 'start' action to start it the right way.\n");
break;
default:
break;
}
return 1;
}
/**
* Show the version info.
*
* @returns 0.
*/
{
/*
* Parse the arguments.
*/
bool fBrief = false;
static const RTOPTIONDEF s_aOptions[] =
{
};
int iArg = 0;
int ch;
switch (ch)
{
case 'b': fBrief = true; break;
}
/*
* Do the printing.
*/
if (fBrief)
else
RTPrintf("VirtualBox System Service Version %s\n"
"(C) 2008 Sun Microsystems, Inc.\n"
"All rights reserved.\n",
return 0;
}
/**
* Show the usage help screen.
*
* @returns 0.
*/
static int supSvcWinShowHelp(void)
{
RTPrintf("VirtualBox System Service Version %s\n"
"(C) 2008 Sun Microsystems, Inc.\n"
"All rights reserved.\n"
"\n",
RTPrintf("Usage:\n"
"\n"
"VBoxSupSvc\n"
" Runs the service.\n"
"VBoxSupSvc <version|-v|--version> [-brief]\n"
" Displays the version.\n"
"VBoxSupSvc <help|-?|-h|--help> [...]\n"
" Displays this help screen.\n"
"\n"
"VBoxSupSvc <install|/RegServer|/i>\n"
" Installs the service.\n"
"VBoxSupSvc <install|delete|/UnregServer|/u>\n"
" Uninstalls the service.\n"
);
return 0;
}
/**
* VBoxSUPSvc main(), Windows edition.
*
*
* @returns 0 on success.
*
* @param argc Number of arguments in argv.
* @param argv Argument vector.
*/
{
/*
* Initialize the IPRT first of all.
*/
#ifdef DEBUG_bird
#endif
if (RT_FAILURE(rc))
{
return 1;
}
/*
* Parse the initial arguments to determin the desired action.
*/
enum
{
int iArg = 1;
if (argc > 1)
{
return supSvcWinShowHelp();
else
iArg--;
iArg++;
}
/*
* Dispatch it.
*/
switch (enmAction)
{
case kSupSvcAction_RunIt:
case kSupSvcAction_Create:
case kSupSvcAction_Delete:
case kSupSvcAction_Enable:
case kSupSvcAction_Disable:
case kSupSvcAction_Start:
case kSupSvcAction_Pause:
case kSupSvcAction_Continue:
case kSupSvcAction_Stop:
default:
return 1;
}
}