SUPSvc-win.cpp revision fb1975a6972d89de9e515bed0248db93f04ec9d8
/* $Id$ */
/** @file
* VirtualBox Support Service - Windows Specific Code.
*/
/*
* Copyright (C) 2008 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SUP
#include <Windows.h>
#include <VBox/log.h>
#include <VBox/version.h>
#include <iprt/string.h>
#include <iprt/mem.h>
#include <iprt/initterm.h>
#include <iprt/stream.h>
#include <iprt/getopt.h>
#include <iprt/semaphore.h>
#ifdef DEBUG_bird
# include <iprt/env.h>
#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. */
static SERVICE_STATUS_HANDLE g_hSupSvcWinCtrlHandler = NULL;
/** The service status. */
static uint32_t volatile g_u32SupSvcWinStatus = SERVICE_STOPPED;
/** The semaphore the main service thread is waiting on in supSvcWinServiceMain. */
static RTSEMEVENTMULTI g_hSupSvcWinEvent = NIL_RTSEMEVENTMULTI;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static SC_HANDLE supSvcWinOpenSCManager(const char *pszAction, DWORD dwAccess);
/**
* 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.
*/
static SC_HANDLE supSvcWinOpenSCManager(const char *pszAction, DWORD dwAccess)
{
SC_HANDLE hSCM = OpenSCManager(NULL /* lpMachineName*/, NULL /* lpDatabaseName */, dwAccess);
if (hSCM == NULL)
{
DWORD err = GetLastError();
switch (err)
{
case ERROR_ACCESS_DENIED:
supSvcDisplayError("%s - OpenSCManager failure: access denied\n", pszAction);
break;
default:
supSvcDisplayError("%s - OpenSCManager failure: %d\n", pszAction, err);
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.
*/
static SC_HANDLE supSvcWinOpenService(const char *pszAction, DWORD dwSCMAccess, DWORD dwSVCAccess,
unsigned cIgnoredErrors, ...)
{
SC_HANDLE hSCM = supSvcWinOpenSCManager(pszAction, dwSCMAccess);
if (!hSCM)
return NULL;
SC_HANDLE hSvc = OpenService(hSCM, SUPSVC_SERVICE_NAME, dwSVCAccess);
if (hSvc)
{
CloseServiceHandle(hSCM);
SetLastError(0);
}
else
{
DWORD err = GetLastError();
bool fIgnored = false;
va_list va;
va_start(va, cIgnoredErrors);
while (!fIgnored && cIgnoredErrors-- > 0)
fIgnored = va_arg(va, long) == err;
va_end(va);
if (!fIgnored)
{
switch (err)
{
case ERROR_ACCESS_DENIED:
supSvcDisplayError("%s - OpenService failure: access denied\n", pszAction);
break;
case ERROR_SERVICE_DOES_NOT_EXIST:
supSvcDisplayError("%s - OpenService failure: The service does not exist. Reinstall it.\n", pszAction);
break;
default:
supSvcDisplayError("%s - OpenService failure: %d\n", pszAction, err);
break;
}
}
CloseServiceHandle(hSCM);
SetLastError(err);
}
return hSvc;
}
void supSvcOsLogErrorStr(const char *pszMsg)
{
HANDLE hEventLog = RegisterEventSource(NULL /* local computer */, "VBoxSupSvc");
AssertReturnVoid(hEventLog != NULL);
const char *apsz[2];
apsz[0] = "VBoxSupSvc";
apsz[1] = pszMsg;
BOOL fRc = ReportEvent(hEventLog, /* hEventLog */
EVENTLOG_ERROR_TYPE, /* wType */
0, /* wCategory */
0 /** @todo mc */, /* dwEventID */
NULL, /* lpUserSid */
RT_ELEMENTS(apsz), /* wNumStrings */
0, /* dwDataSize */
apsz, /* lpStrings */
NULL); /* lpRawData */
AssertMsg(fRc, ("%d\n", GetLastError()));
DeregisterEventSource(hEventLog);
}
static int supSvcWinInterrogate(int argc, char **argv)
{
RTPrintf("VBoxSupSvc: The \"interrogate\" action is not implemented.\n");
return 1;
}
static int supSvcWinStop(int argc, char **argv)
{
RTPrintf("VBoxSupSvc: The \"stop\" action is not implemented.\n");
return 1;
}
static int supSvcWinContinue(int argc, char **argv)
{
RTPrintf("VBoxSupSvc: The \"continue\" action is not implemented.\n");
return 1;
}
static int supSvcWinPause(int argc, char **argv)
{
RTPrintf("VBoxSupSvc: The \"pause\" action is not implemented.\n");
return 1;
}
static int supSvcWinStart(int argc, char **argv)
{
RTPrintf("VBoxSupSvc: The \"start\" action is not implemented.\n");
return 1;
}
static int supSvcWinQueryDescription(int argc, char **argv)
{
RTPrintf("VBoxSupSvc: The \"qdescription\" action is not implemented.\n");
return 1;
}
static int supSvcWinQueryConfig(int argc, char **argv)
{
RTPrintf("VBoxSupSvc: The \"qconfig\" action is not implemented.\n");
return 1;
}
static int supSvcWinDisable(int argc, char **argv)
{
RTPrintf("VBoxSupSvc: The \"disable\" action is not implemented.\n");
return 1;
}
static int supSvcWinEnable(int argc, char **argv)
{
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.
*/
static int supSvcWinDelete(int argc, char **argv)
{
/*
* Parse the arguments.
*/
bool fVerbose = false;
static const RTGETOPTDEF s_aOptions[] =
{
{ "--verbose", 'v', RTGETOPT_REQ_NOTHING }
};
int ch;
RTGETOPTUNION Value;
RTGETOPTSTATE GetState;
RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
while ((ch = RTGetOpt(&GetState, &Value)))
switch (ch)
{
case 'v':
fVerbose = true;
break;
case VINF_GETOPT_NOT_OPTION:
return supSvcDisplayTooManyArgsError("delete", argc, argv, iArg);
default:
return supSvcDisplayGetOptError("delete", ch, argc, argv, iArg, &Value);
}
/*
* Create the service.
*/
int rc = 1;
SC_HANDLE hSvc = supSvcWinOpenService("delete", SERVICE_CHANGE_CONFIG, DELETE,
1, ERROR_SERVICE_DOES_NOT_EXIST);
if (hSvc)
{
if (DeleteService(hSvc))
{
RTPrintf("Successfully deleted the %s service.\n", SUPSVC_SERVICE_NAME);
rc = 0;
}
else
supSvcDisplayError("delete - DeleteService failed, err=%d.\n", GetLastError());
CloseServiceHandle(hSvc);
}
else if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
{
if (fVerbose)
RTPrintf("The service %s was not installed, nothing to be done.", SUPSVC_SERVICE_NAME);
else
RTPrintf("Successfully deleted the %s service.\n", SUPSVC_SERVICE_NAME);
rc = 0;
}
return rc;
}
/**
* Handle the 'create' action.
*
* @returns 0 or 1.
* @param argc The action argument count.
* @param argv The action argument vector.
*/
static int supSvcWinCreate(int argc, char **argv)
{
/*
* Parse the arguments.
*/
bool fVerbose = false;
static const RTOPTIONDEF s_aOptions[] =
{
{ "--verbose", 'v', RTGETOPT_REQ_NOTHING }
};
int iArg = 0;
int ch;
RTGETOPTUNION Value;
while ((ch = RTGetOpt(argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value)))
switch (ch)
{
case 'v': fVerbose = true; break;
default: return supSvcDisplayGetOptError("create", ch, argc, argv, iArg, &Value);
}
if (iArg != argc)
return supSvcDisplayTooManyArgsError("create", argc, argv, iArg);
/*
* 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 (GetModuleFileName(NULL /* the executable */, szExecPath, sizeof(szExecPath)))
{
if (fVerbose)
RTPrintf("Creating the %s service, binary \"%s\"...\n",
SUPSVC_SERVICE_NAME, szExecPath); /* yea, the binary name isn't UTF-8, but wtf. */
SC_HANDLE hSvc = CreateService(hSCM, /* hSCManager */
SUPSVC_SERVICE_NAME, /* lpServiceName */
SUPSVC_SERVICE_DISPLAY_NAME, /* lpDisplayName */
SERVICE_CHANGE_CONFIG | SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG, /* dwDesiredAccess */
SERVICE_WIN32_OWN_PROCESS, /* dwServiceType ( | SERVICE_INTERACTIVE_PROCESS? ) */
SERVICE_DEMAND_START/*_AUTO*/, /* dwStartType */
SERVICE_ERROR_NORMAL, /* dwErrorControl */
szExecPath, /* lpBinaryPathName */
NULL, /* lpLoadOrderGroup */
NULL, /* lpdwTagId */
NULL, /* lpDependencies */
NULL, /* lpServiceStartName (=> LocalSystem) */
NULL); /* lpPassword */
if (hSvc)
{
RTPrintf("Successfully created the %s service.\n", SUPSVC_SERVICE_NAME);
/** @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;
CloseServiceHandle(hSvc);
}
else
{
DWORD err = GetLastError();
switch (err)
{
case ERROR_SERVICE_EXISTS:
supSvcDisplayError("create - The service already exists.\n");
break;
default:
supSvcDisplayError("create - CreateService failed, err=%d.\n", GetLastError());
break;
}
}
CloseServiceHandle(hSvc);
}
else
supSvcDisplayError("create - Failed to obtain the executable path: %d\n", GetLastError());
}
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.
*/
static bool supSvcWinSetServiceStatus(DWORD dwStatus, int iWaitHint, DWORD dwExitCode)
{
SERVICE_STATUS SvcStatus;
SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
SvcStatus.dwWin32ExitCode = dwExitCode;
SvcStatus.dwServiceSpecificExitCode = 0;
SvcStatus.dwWaitHint = iWaitHint >= 0 ? iWaitHint : 3000;
SvcStatus.dwCurrentState = dwStatus;
LogFlow(("supSvcWinSetServiceStatus: %d -> %d\n", g_u32SupSvcWinStatus, dwStatus));
g_u32SupSvcWinStatus = dwStatus;
switch (dwStatus)
{
case SERVICE_START_PENDING:
SvcStatus.dwControlsAccepted = 0;
break;
default:
SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
break;
}
static DWORD dwCheckPoint = 0;
switch (dwStatus)
{
case SERVICE_RUNNING:
case SERVICE_STOPPED:
SvcStatus.dwCheckPoint = 0;
default:
SvcStatus.dwCheckPoint = ++dwCheckPoint;
break;
}
return SetServiceStatus(g_hSupSvcWinCtrlHandler, &SvcStatus) != FALSE;
}
/**
* 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",
dwControl, dwEventType, pvEventData));
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.
*/
case SERVICE_CONTROL_INTERROGATE:
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.
*/
supSvcWinSetServiceStatus(SERVICE_STOP_PENDING, 3000, NO_ERROR);
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.
*/
rc = RTSemEventMultiSignal(g_hSupSvcWinEvent);
if (RT_FAILURE(rc))
supSvcLogError("SERVICE_CONTROL_STOP: RTSemEventMultiSignal failed, %Rrc\n", rc);
}
return NO_ERROR;
}
case SERVICE_CONTROL_PAUSE:
case SERVICE_CONTROL_CONTINUE:
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_PARAMCHANGE:
case SERVICE_CONTROL_NETBINDADD:
case SERVICE_CONTROL_NETBINDREMOVE:
case SERVICE_CONTROL_NETBINDENABLE:
case SERVICE_CONTROL_NETBINDDISABLE:
case SERVICE_CONTROL_DEVICEEVENT:
case SERVICE_CONTROL_HARDWAREPROFILECHANGE:
case SERVICE_CONTROL_POWEREVENT:
case SERVICE_CONTROL_SESSIONCHANGE:
#ifdef SERVICE_CONTROL_PRESHUTDOWN /* vista */
case SERVICE_CONTROL_PRESHUTDOWN:
#endif
default:
return ERROR_CALL_NOT_IMPLEMENTED;
}
NOREF(dwEventType);
NOREF(pvEventData);
NOREF(pvContext);
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.
*/
static VOID WINAPI supSvcWinServiceMain(DWORD cArgs, LPSTR *papszArgs)
{
LogFlowFuncEnter();
/*
* Register the control handler function for the service and report to SCM.
*/
Assert(g_u32SupSvcWinStatus == SERVICE_STOPPED);
g_hSupSvcWinCtrlHandler = RegisterServiceCtrlHandlerEx(SUPSVC_SERVICE_NAME, supSvcWinServiceCtrlHandlerEx, NULL);
if (g_hSupSvcWinCtrlHandler)
{
DWORD err = ERROR_GEN_FAILURE;
if (supSvcWinSetServiceStatus(SERVICE_START_PENDING, 3000, NO_ERROR))
{
/*
* Parse arguments.
*/
static const RTOPTIONDEF s_aOptions[] =
{
{ "--dummy", 'd', RTGETOPT_REQ_NOTHING }
};
int iArg = 1; /* the first arg is the service name */
int ch;
int rc = 0;
RTGETOPTUNION Value;
while ( !rc
&& (ch = RTGetOpt(cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value)))
switch (ch)
{
default: rc = supSvcLogGetOptError("main", ch, cArgs, papszArgs, iArg, &Value); break;
}
if (iArg != cArgs)
rc = supSvcLogTooManyArgsError("main", cArgs, papszArgs, iArg);
if (!rc)
{
/*
* Create the event semaphore we'll be waiting on and
* then instantiate the actual services.
*/
int rc = RTSemEventMultiCreate(&g_hSupSvcWinEvent);
if (RT_SUCCESS(rc))
{
rc = supSvcCreateAndStartServices();
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"));
rc = RTSemEventMultiWait(g_hSupSvcWinEvent, RT_INDEFINITE_WAIT);
if (RT_SUCCESS(rc))
{
LogFlow(("supSvcWinServiceMain: woke up\n"));
err = NO_ERROR;
}
else
supSvcLogError("RTSemEventWait failed, rc=%Rrc", rc);
}
else
{
err = GetLastError();
supSvcLogError("SetServiceStatus failed, err=%d", err);
}
/*
* Destroy the service instances, stopping them if
* they're still running (weird failure cause).
*/
supSvcStopAndDestroyServices();
}
RTSemEventMultiDestroy(g_hSupSvcWinEvent);
g_hSupSvcWinEvent = NIL_RTSEMEVENTMULTI;
}
else
supSvcLogError("RTSemEventMultiCreate failed, rc=%Rrc", rc);
}
/* else: bad args */
}
else
{
err = GetLastError();
supSvcLogError("SetServiceStatus failed, err=%d", err);
}
supSvcWinSetServiceStatus(SERVICE_STOPPED, 0, err);
}
else
supSvcLogError("RegisterServiceCtrlHandlerEx failed, err=%d", GetLastError());
LogFlowFuncLeave();
}
/**
* Handle the 'create' action.
*
* @returns 0 or 1.
* @param argc The action argument count.
* @param argv The action argument vector.
*/
static int supSvcWinRunIt(int argc, char **argv)
{
LogFlowFuncEnter();
/*
* Initialize release logging.
*/
/** @todo release logging of the system-wide service. */
/*
* Parse the arguments.
*/
static const RTOPTIONDEF s_aOptions[] =
{
{ "--dummy", 'd', RTGETOPT_REQ_NOTHING }
};
int iArg = 0;
int ch;
RTGETOPTUNION Value;
while ((ch = RTGetOpt(argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value)))
switch (ch)
{
default: return supSvcDisplayGetOptError("runit", ch, argc, argv, iArg, &Value);
}
if (iArg != argc)
return supSvcDisplayTooManyArgsError("runit", argc, argv, iArg);
/*
* 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[] =
{
{ SUPSVC_SERVICE_NAME, supSvcWinServiceMain },
{ NULL, NULL}
};
if (StartServiceCtrlDispatcher(&s_aServiceStartTable[0]))
{
LogFlowFuncLeave();
return 0; /* told to quit, so quit. */
}
DWORD err = GetLastError();
switch (err)
{
case ERROR_FAILED_SERVICE_CONTROLLER_CONNECT:
supSvcDisplayError("Cannot run a service from the command line. Use the 'start' action to start it the right way.\n");
break;
default:
supSvcLogError("StartServiceCtrlDispatcher failed, err=%d", err);
break;
}
return 1;
}
/**
* Show the version info.
*
* @returns 0.
*/
static int supSvcWinShowVersion(int argc, char **argv)
{
/*
* Parse the arguments.
*/
bool fBrief = false;
static const RTOPTIONDEF s_aOptions[] =
{
{ "--brief", 'b', RTGETOPT_REQ_NOTHING }
};
int iArg = 0;
int ch;
RTGETOPTUNION Value;
while ((ch = RTGetOpt(argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), &iArg, &Value)))
switch (ch)
{
case 'b': fBrief = true; break;
default: return supSvcDisplayGetOptError("version", ch, argc, argv, iArg, &Value);
}
if (iArg != argc)
return supSvcDisplayTooManyArgsError("version", argc, argv, iArg);
/*
* Do the printing.
*/
if (fBrief)
RTPrintf("%s\n", VBOX_VERSION_STRING);
else
RTPrintf("VirtualBox System Service Version %s\n"
"(C) 2008 Sun Microsystems, Inc.\n"
"All rights reserved.\n",
VBOX_VERSION_STRING);
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",
VBOX_VERSION_STRING);
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.
*/
int main(int argc, char **argv)
{
/*
* Initialize the IPRT first of all.
*/
#ifdef DEBUG_bird
RTEnvSet("VBOX_LOG", "sup=~0");
RTEnvSet("VBOX_LOG_DEST", "file=E:\\temp\\VBoxSupSvc.log");
RTEnvSet("VBOX_LOG_FLAGS", "unbuffered thread msprog");
#endif
int rc = RTR3Init();
if (RT_FAILURE(rc))
{
supSvcLogError("RTR3Init failed with rc=%Rrc", rc);
return 1;
}
/*
* Parse the initial arguments to determin the desired action.
*/
enum
{
kSupSvcAction_RunIt,
kSupSvcAction_Create,
kSupSvcAction_Delete,
kSupSvcAction_Enable,
kSupSvcAction_Disable,
kSupSvcAction_QueryConfig,
kSupSvcAction_QueryDescription,
kSupSvcAction_Start,
kSupSvcAction_Pause,
kSupSvcAction_Continue,
kSupSvcAction_Stop,
kSupSvcAction_Interrogate,
kSupSvcAction_End
} enmAction = kSupSvcAction_RunIt;
int iArg = 1;
if (argc > 1)
{
if ( !stricmp(argv[iArg], "/RegServer")
|| !stricmp(argv[iArg], "install")
|| !stricmp(argv[iArg], "/i"))
enmAction = kSupSvcAction_Create;
else if ( !stricmp(argv[iArg], "/UnregServer")
|| !stricmp(argv[iArg], "/u")
|| !stricmp(argv[iArg], "uninstall")
|| !stricmp(argv[iArg], "delete"))
enmAction = kSupSvcAction_Delete;
else if (!stricmp(argv[iArg], "enable"))
enmAction = kSupSvcAction_Enable;
else if (!stricmp(argv[iArg], "disable"))
enmAction = kSupSvcAction_Disable;
else if (!stricmp(argv[iArg], "qconfig"))
enmAction = kSupSvcAction_QueryConfig;
else if (!stricmp(argv[iArg], "qdescription"))
enmAction = kSupSvcAction_QueryDescription;
else if ( !stricmp(argv[iArg], "start")
|| !stricmp(argv[iArg], "/t"))
enmAction = kSupSvcAction_Start;
else if (!stricmp(argv[iArg], "pause"))
enmAction = kSupSvcAction_Start;
else if (!stricmp(argv[iArg], "continue"))
enmAction = kSupSvcAction_Continue;
else if (!stricmp(argv[iArg], "stop"))
enmAction = kSupSvcAction_Stop;
else if (!stricmp(argv[iArg], "interrogate"))
enmAction = kSupSvcAction_Interrogate;
else if ( !stricmp(argv[iArg], "help")
|| !stricmp(argv[iArg], "?")
|| !stricmp(argv[iArg], "/?")
|| !stricmp(argv[iArg], "-?")
|| !stricmp(argv[iArg], "/h")
|| !stricmp(argv[iArg], "-h")
|| !stricmp(argv[iArg], "/help")
|| !stricmp(argv[iArg], "-help")
|| !stricmp(argv[iArg], "--help"))
return supSvcWinShowHelp();
else if ( !stricmp(argv[iArg], "version")
|| !stricmp(argv[iArg], "/v")
|| !stricmp(argv[iArg], "-v")
|| !stricmp(argv[iArg], "/version")
|| !stricmp(argv[iArg], "-version")
|| !stricmp(argv[iArg], "--version"))
return supSvcWinShowVersion(argc - iArg - 1, argv + iArg + 1);
else
iArg--;
iArg++;
}
/*
* Dispatch it.
*/
switch (enmAction)
{
case kSupSvcAction_RunIt:
return supSvcWinRunIt(argc - iArg, argv + iArg);
case kSupSvcAction_Create:
return supSvcWinCreate(argc - iArg, argv + iArg);
case kSupSvcAction_Delete:
return supSvcWinDelete(argc - iArg, argv + iArg);
case kSupSvcAction_Enable:
return supSvcWinEnable(argc - iArg, argv + iArg);
case kSupSvcAction_Disable:
return supSvcWinDisable(argc - iArg, argv + iArg);
case kSupSvcAction_QueryConfig:
return supSvcWinQueryConfig(argc - iArg, argv + iArg);
case kSupSvcAction_QueryDescription:
return supSvcWinQueryDescription(argc - iArg, argv + iArg);
case kSupSvcAction_Start:
return supSvcWinStart(argc - iArg, argv + iArg);
case kSupSvcAction_Pause:
return supSvcWinPause(argc - iArg, argv + iArg);
case kSupSvcAction_Continue:
return supSvcWinContinue(argc - iArg, argv + iArg);
case kSupSvcAction_Stop:
return supSvcWinStop(argc - iArg, argv + iArg);
case kSupSvcAction_Interrogate:
return supSvcWinInterrogate(argc - iArg, argv + iArg);
default:
AssertMsgFailed(("enmAction=%d\n", enmAction));
return 1;
}
}