/** * Bitch about too many arguments (after RTGetOpt stops) in the system log. * * @returns 1 * @param pszAction The action name. * @param argc The argument count. * @param argv The argument vector. * @param iArg The argument index. */ int supSvcLogTooManyArgsError(const char *pszAction, int argc, char **argv, int iArg) { Assert(iArg < argc); supSvcLogError("%s - Too many arguments: %s", pszAction, argv[iArg]); for ( ; iArg < argc; iArg++) LogRel(("arg#%i: %s\n", iArg, argv[iArg])); return 1; }
/** * Instantiates and starts the services. * * @returns VBox status code. Done bitching on failure. */ int supSvcCreateAndStartServices(void) { LogFlowFuncEnter(); /* * Validate that all services are in the NotCreated state. */ unsigned i; for (i = 0; i < RT_ELEMENTS(g_aServices); i++) if (g_aServices[i].enmState != kSupSvcServiceState_NotCreated) { supSvcLogError("service %s in state %d, expected state %d (NotCreated)", g_aServices[i].pszName, g_aServices[i].enmState, kSupSvcServiceState_NotCreated); return VERR_WRONG_ORDER; } /* * Create all the services, then start them. */ int rc = VINF_SUCCESS; for (i = 0; i < RT_ELEMENTS(g_aServices); i++) { void *pvInstance = NULL; int rc = g_aServices[i].pfnCreate(&pvInstance); if (RT_FAILURE(rc)) { Log(("supSvcCreateAndStartServices: %s -> %Rrc\n", g_aServices[i].pszName, rc)); break; } g_aServices[i].pvInstance = pvInstance; g_aServices[i].enmState = kSupSvcServiceState_Paused; } if (RT_SUCCESS(rc)) { for (i = 0; i < RT_ELEMENTS(g_aServices); i++) { g_aServices[i].pfnStart(g_aServices[i].pvInstance); g_aServices[i].enmState = kSupSvcServiceState_Running; } } else { /* * Destroy any services we managed to instantiate. */ while (i-- > 0) { g_aServices[i].pfnStopAndDestroy(g_aServices[i].pvInstance, false /* fRunning */); g_aServices[i].pvInstance = NULL; g_aServices[i].enmState = kSupSvcServiceState_NotCreated; } } LogFlow(("supSvcCreateAndStartServices: returns %Rrc\n", rc)); return rc; }
/** * 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; }
/** * Checks if it's possible to stop the services. * * @returns VBox status code, done bitching on failure. */ int supSvcTryStopServices(void) { LogFlowFuncEnter(); /* * Check that the services are all created and count the running ones. */ unsigned i; unsigned cRunning = 0; for (i = 0; i < RT_ELEMENTS(g_aServices); i++) if (g_aServices[i].enmState == kSupSvcServiceState_Running) cRunning++; else if (g_aServices[i].enmState == kSupSvcServiceState_NotCreated) { supSvcLogError("service %s in state %d (NotCreated), expected pause or running", g_aServices[i].pszName, g_aServices[i].enmState, kSupSvcServiceState_NotCreated); return VERR_WRONG_ORDER; } if (!cRunning) return VINF_SUCCESS; /* all stopped, nothing to do. */ Assert(cRunning == RT_ELEMENTS(g_aServices)); /* all or nothing */ /* * Try stop them in reverse of start order. */ int rc = VINF_SUCCESS; i = RT_ELEMENTS(g_aServices); while (i-- > 0) { rc = g_aServices[i].pfnTryStop(g_aServices[i].pvInstance); if (RT_FAILURE(rc)) { Log(("supSvcTryStopServices: %s -> %Rrc\n", g_aServices[i].pszName, rc)); break; } g_aServices[i].enmState = kSupSvcServiceState_Paused; } if (RT_FAILURE(rc)) { /* Failed, restart the ones we succeeded in stopping. */ while (++i < RT_ELEMENTS(g_aServices)) { g_aServices[i].pfnStart(g_aServices[i].pvInstance); g_aServices[i].enmState = kSupSvcServiceState_Running; } } LogFlow(("supSvcTryStopServices: returns %Rrc\n", rc)); return rc; }
/** * Deals with RTGetOpt failure, bitching in the system log. * * @returns 1 * @param pszAction The action name. * @param rc The RTGetOpt return value. * @param argc The argument count. * @param argv The argument vector. * @param iArg The argument index. * @param pValue The value returned by RTGetOpt. */ int supSvcLogGetOptError(const char *pszAction, int rc, int argc, char **argv, int iArg, PCRTOPTIONUNION pValue) { supSvcLogError("%s - RTGetOpt failure, %Rrc (%d): %s", pszAction, rc, rc, iArg < argc ? argv[iArg] : "<null>"); return 1; }
/** * 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 = RTR3InitExe(argc, &argv, 0); if (RT_FAILURE(rc)) { supSvcLogError("RTR3InitExe failed with rc=%Rrc", rc); return 1; } /* * Parse the initial arguments to determine 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; } }
/** * 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 independent 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(); }
/** * 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, specific 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; }