blob: 8efe25f9b813fc83a7c215650be7029a6da24bea [file] [log] [blame]
/*
* 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, TEXT("SetServiceStatus"));
}
return res;
}
static int
CmdInstallServices()
{
SC_HANDLE service;
SC_HANDLE svc_ctl_mgr;
TCHAR path[512];
int i, ret = _service_max;
if (GetModuleFileName(NULL, path + 1, 510) == 0)
{
_tprintf(TEXT("Unable to install service - %s\n"), GetLastErrorText());
return 1;
}
path[0] = TEXT('\"');
_tcscat(path, TEXT("\""));
svc_ctl_mgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
if (svc_ctl_mgr == NULL)
{
_tprintf(TEXT("OpenSCManager failed - %s\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)
{
_tprintf(TEXT("%s installed.\n"), openvpn_service[i].display_name);
CloseServiceHandle(service);
--ret;
}
else
{
_tprintf(TEXT("CreateService failed - %s\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)
{
_tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText());
return 1;
}
service = OpenService(svc_ctl_mgr, openvpn_service[type].name, SERVICE_ALL_ACCESS);
if (service)
{
if (StartService(service, 0, NULL))
{
_tprintf(TEXT("Service Started\n"));
ret = 0;
}
else
{
_tprintf(TEXT("StartService failed - %s\n"), GetLastErrorText());
}
CloseServiceHandle(service);
}
else
{
_tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText());
}
CloseServiceHandle(svc_ctl_mgr);
return ret;
}
static int
CmdRemoveServices()
{
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)
{
_tprintf(TEXT("OpenSCManager failed - %s\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)
{
_tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText());
goto out;
}
/* try to stop the service */
if (ControlService(service, SERVICE_CONTROL_STOP, &status))
{
_tprintf(TEXT("Stopping %s."), ovpn_svc->display_name);
Sleep(1000);
while (QueryServiceStatus(service, &status))
{
if (status.dwCurrentState == SERVICE_STOP_PENDING)
{
_tprintf(TEXT("."));
Sleep(1000);
}
else
{
break;
}
}
if (status.dwCurrentState == SERVICE_STOPPED)
{
_tprintf(TEXT("\n%s stopped.\n"), ovpn_svc->display_name);
}
else
{
_tprintf(TEXT("\n%s failed to stop.\n"), ovpn_svc->display_name);
}
}
/* now remove the service */
if (DeleteService(service))
{
_tprintf(TEXT("%s removed.\n"), ovpn_svc->display_name);
--ret;
}
else
{
_tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText());
}
CloseServiceHandle(service);
}
out:
CloseServiceHandle(svc_ctl_mgr);
return ret;
}
int
_tmain(int argc, TCHAR *argv[])
{
/*
* Automatic + Interactive service (as a SERVICE_WIN32_SHARE_PROCESS)
* This is the default.
*/
const SERVICE_TABLE_ENTRY dispatchTable_shared[] = {
{ automatic_service.name, ServiceStartAutomatic },
{ interactive_service.name, ServiceStartInteractive },
{ NULL, NULL }
};
/* Automatic service only (as a SERVICE_WIN32_OWN_PROCESS) */
const SERVICE_TABLE_ENTRY dispatchTable_automatic[] = {
{ TEXT(""), ServiceStartAutomaticOwn },
{ NULL, NULL }
};
/* Interactive service only (as a SERVICE_WIN32_OWN_PROCESS) */
const SERVICE_TABLE_ENTRY dispatchTable_interactive[] = {
{ TEXT(""), ServiceStartInteractiveOwn },
{ NULL, NULL }
};
const SERVICE_TABLE_ENTRY *dispatchTable = dispatchTable_shared;
openvpn_service[0] = automatic_service;
openvpn_service[1] = interactive_service;
for (int i = 1; i < argc; i++)
{
if (*argv[i] == TEXT('-') || *argv[i] == TEXT('/'))
{
if (_tcsicmp(TEXT("install"), argv[i] + 1) == 0)
{
return CmdInstallServices();
}
else if (_tcsicmp(TEXT("remove"), argv[i] + 1) == 0)
{
return CmdRemoveServices();
}
else if (_tcsicmp(TEXT("start"), argv[i] + 1) == 0)
{
BOOL is_auto = argc < i + 2 || _tcsicmp(TEXT("interactive"), argv[i + 1]) != 0;
return CmdStartService(is_auto ? automatic : interactive);
}
else if (argc > i + 2 && _tcsicmp(TEXT("instance"), argv[i] + 1) == 0)
{
dispatchTable = _tcsicmp(TEXT("interactive"), argv[i + 1]) != 0 ?
dispatchTable_automatic :
dispatchTable_interactive;
service_instance = argv[i + 2];
i += 2;
}
else
{
_tprintf(TEXT("%s -install to install the services\n"), APPNAME);
_tprintf(TEXT("%s -start <name> to start a service (\"automatic\" or \"interactive\")\n"), APPNAME);
_tprintf(TEXT("%s -remove to remove the services\n"), APPNAME);
_tprintf(TEXT("\nService run-time parameters:\n"));
_tprintf(TEXT("-instance <name> <id>\n")
TEXT(" Runs the service as an alternate instance. <name> can be \"automatic\" or\n")
TEXT(" \"interactive\". The service settings will be loaded from\n")
TEXT(" HKLM\\Software\\" PACKAGE_NAME "<id> registry key, and the interactive service will accept\n")
TEXT(" requests on \\\\.\\pipe\\" PACKAGE "<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
*/
_tprintf(TEXT("\nStartServiceCtrlDispatcher being called.\n"));
_tprintf(TEXT("This may take several seconds. Please wait.\n"));
if (!StartServiceCtrlDispatcher(dispatchTable))
{
MsgToEventLog(MSG_FLAGS_ERROR, TEXT("StartServiceCtrlDispatcher failed."));
}
return 0;
}