/*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
* Copyright (C) 1993 - 2000. Microsoft Corporation. All rights reserved.
* 2013 Heiko Hund <heiko.hund@sophos.com>
*/
#include "service.h"
#include <windows.h>
#include <stdio.h>
#include <process.h>
openvpn_service_t openvpn_service[_service_max];
BOOL
ReportStatusToSCMgr(SERVICE_STATUS_HANDLE service, SERVICE_STATUS *status)
{
static DWORD dwCheckPoint = 1;
BOOL res = TRUE;
if (status->dwCurrentState == SERVICE_START_PENDING)
{
status->dwControlsAccepted = 0;
}
else
{
status->dwControlsAccepted = SERVICE_ACCEPT_STOP;
}
if (status->dwCurrentState == SERVICE_RUNNING
|| status->dwCurrentState == SERVICE_STOPPED)
{
status->dwCheckPoint = 0;
}
else
{
status->dwCheckPoint = dwCheckPoint++;
}
/* Report the status of the service to the service control manager. */
res = SetServiceStatus(service, status);
if (!res)
{
MsgToEventLog(MSG_FLAGS_ERROR, L"SetServiceStatus");
}
return res;
}
static int
CmdInstallServices(void)
{
SC_HANDLE service;
SC_HANDLE svc_ctl_mgr;
WCHAR path[512];
int i, ret = _service_max;
if (GetModuleFileName(NULL, path + 1, _countof(path) - 2) == 0)
{
wprintf(L"Unable to install service - %ls\n", GetLastErrorText());
return 1;
}
path[0] = L'\"';
wcscat_s(path, _countof(path), L"\"");
svc_ctl_mgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
if (svc_ctl_mgr == NULL)
{
wprintf(L"OpenSCManager failed - %ls\n", GetLastErrorText());
return 1;
}
for (i = 0; i < _service_max; i++)
{
service = CreateService(svc_ctl_mgr,
openvpn_service[i].name,
openvpn_service[i].display_name,
SERVICE_QUERY_STATUS,
SERVICE_WIN32_SHARE_PROCESS,
openvpn_service[i].start_type,
SERVICE_ERROR_NORMAL,
path, NULL, NULL,
openvpn_service[i].dependencies,
NULL, NULL);
if (service)
{
wprintf(L"%ls installed.\n", openvpn_service[i].display_name);
CloseServiceHandle(service);
--ret;
}
else
{
wprintf(L"CreateService failed - %ls\n", GetLastErrorText());
}
}
CloseServiceHandle(svc_ctl_mgr);
return ret;
}
static int
CmdStartService(openvpn_service_type type)
{
int ret = 1;
SC_HANDLE svc_ctl_mgr;
SC_HANDLE service;
svc_ctl_mgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (svc_ctl_mgr == NULL)
{
wprintf(L"OpenSCManager failed - %ls\n", GetLastErrorText());
return 1;
}
service = OpenService(svc_ctl_mgr, openvpn_service[type].name, SERVICE_ALL_ACCESS);
if (service)
{
if (StartService(service, 0, NULL))
{
wprintf(L"Service Started\n");
ret = 0;
}
else
{
wprintf(L"StartService failed - %ls\n", GetLastErrorText());
}
CloseServiceHandle(service);
}
else
{
wprintf(L"OpenService failed - %ls\n", GetLastErrorText());
}
CloseServiceHandle(svc_ctl_mgr);
return ret;
}
static int
CmdRemoveServices(void)
{
SC_HANDLE service;
SC_HANDLE svc_ctl_mgr;
SERVICE_STATUS status;
int i, ret = _service_max;
svc_ctl_mgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (svc_ctl_mgr == NULL)
{
wprintf(L"OpenSCManager failed - %ls\n", GetLastErrorText());
return 1;
}
for (i = 0; i < _service_max; i++)
{
openvpn_service_t *ovpn_svc = &openvpn_service[i];
service = OpenService(svc_ctl_mgr, ovpn_svc->name,
DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
if (service == NULL)
{
wprintf(L"OpenService failed - %ls\n", GetLastErrorText());
goto out;
}
/* try to stop the service */
if (ControlService(service, SERVICE_CONTROL_STOP, &status))
{
wprintf(L"Stopping %ls.", ovpn_svc->display_name);
Sleep(1000);
while (QueryServiceStatus(service, &status))
{
if (status.dwCurrentState == SERVICE_STOP_PENDING)
{
wprintf(L".");
Sleep(1000);
}
else
{
break;
}
}
if (status.dwCurrentState == SERVICE_STOPPED)
{
wprintf(L"\n%ls stopped.\n", ovpn_svc->display_name);
}
else
{
wprintf(L"\n%ls failed to stop.\n", ovpn_svc->display_name);
}
}
/* now remove the service */
if (DeleteService(service))
{
wprintf(L"%ls removed.\n", ovpn_svc->display_name);
--ret;
}
else
{
wprintf(L"DeleteService failed - %ls\n", GetLastErrorText());
}
CloseServiceHandle(service);
}
out:
CloseServiceHandle(svc_ctl_mgr);
return ret;
}
int
wmain(int argc, WCHAR *argv[])
{
/*
* Interactive service (as a SERVICE_WIN32_SHARE_PROCESS)
* This is the default.
*/
const SERVICE_TABLE_ENTRY dispatchTable_shared[] = {
{ interactive_service.name, ServiceStartInteractive },
{ NULL, NULL }
};
/* Interactive service only (as a SERVICE_WIN32_OWN_PROCESS) */
const SERVICE_TABLE_ENTRY dispatchTable_interactive[] = {
{ L"", ServiceStartInteractiveOwn },
{ NULL, NULL }
};
const SERVICE_TABLE_ENTRY *dispatchTable = dispatchTable_shared;
openvpn_service[interactive] = interactive_service;
for (int i = 1; i < argc; i++)
{
if (*argv[i] == L'-' || *argv[i] == L'/')
{
if (_wcsicmp(L"install", argv[i] + 1) == 0)
{
return CmdInstallServices();
}
else if (_wcsicmp(L"remove", argv[i] + 1) == 0)
{
return CmdRemoveServices();
}
else if (_wcsicmp(L"start", argv[i] + 1) == 0)
{
return CmdStartService(interactive);
}
else if (argc > i + 2 && _wcsicmp(L"instance", argv[i] + 1) == 0)
{
if (_wcsicmp(L"interactive", argv[i+1]) == 0)
{
dispatchTable = dispatchTable_interactive;
service_instance = argv[i + 2];
i += 2;
}
else
{
MsgToEventLog(M_ERR, L"Invalid argument to -instance <%s>. Service not started.", argv[i+1]);
return 1;
}
}
else
{
wprintf(L"%ls -install to install the interactive service\n", APPNAME);
wprintf(L"%ls -start [name] to start the service (name = \"interactive\") is optional\n", APPNAME);
wprintf(L"%ls -remove to remove the service\n", APPNAME);
wprintf(L"\nService run-time parameters:\n");
wprintf(L"-instance interactive <id>\n"
L" Runs the service as an alternate instance.\n"
L" The service settings will be loaded from\n"
L" HKLM\\Software\\" _L(PACKAGE_NAME) L"<id> registry key, and the service will accept\n"
L" requests on \\\\.\\pipe\\" _L(PACKAGE) L"<id>\\service named pipe.\n");
return 0;
}
}
}
/* If it doesn't match any of the above parameters
* the service control manager may be starting the service
* so we must call StartServiceCtrlDispatcher
*/
wprintf(L"\nStartServiceCtrlDispatcher being called.\n");
wprintf(L"This may take several seconds. Please wait.\n");
if (!StartServiceCtrlDispatcher(dispatchTable))
{
MsgToEventLog(MSG_FLAGS_ERROR, L"StartServiceCtrlDispatcher failed.");
}
return 0;
}