337 lines
6.9 KiB
C
337 lines
6.9 KiB
C
/*
|
|
* Copyright (C) 2013 Martin Willi
|
|
* Copyright (C) 2013 revosec AG
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*/
|
|
|
|
#include <library.h>
|
|
#include <daemon.h>
|
|
|
|
#include <utils/backtrace.h>
|
|
#include <threading/thread.h>
|
|
|
|
/**
|
|
* The name of our service, both internal and external
|
|
*/
|
|
#define SERVICE_NAME "charon-svc"
|
|
|
|
/**
|
|
* Current service status
|
|
*/
|
|
static SERVICE_STATUS status;
|
|
|
|
/**
|
|
* Handle for service status
|
|
*/
|
|
static SERVICE_STATUS_HANDLE handle;
|
|
|
|
/**
|
|
* Wait event for main thread
|
|
*/
|
|
static HANDLE event;
|
|
|
|
/**
|
|
* hook in library for debugging messages
|
|
*/
|
|
extern void (*dbg) (debug_t group, level_t level, char *fmt, ...);
|
|
|
|
/**
|
|
* Forward declaration
|
|
*/
|
|
static DWORD WINAPI service_handler(DWORD dwControl, DWORD dwEventType,
|
|
LPVOID lpEventData, LPVOID lpContext);
|
|
|
|
/**
|
|
* Logging hook for library logs, using stderr output
|
|
*/
|
|
static void dbg_stderr(debug_t group, level_t level, char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (level <= 1)
|
|
{
|
|
va_start(args, fmt);
|
|
fprintf(stderr, "00[%N] ", debug_names, group);
|
|
vfprintf(stderr, fmt, args);
|
|
fprintf(stderr, "\n");
|
|
va_end(args);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log strongSwan/Windows version during startup
|
|
*/
|
|
static void print_version()
|
|
{
|
|
OSVERSIONINFOEX osvie;
|
|
|
|
memset(&osvie, 0, sizeof(osvie));
|
|
osvie.dwOSVersionInfoSize = sizeof(osvie);
|
|
|
|
if (GetVersionEx((LPOSVERSIONINFO)&osvie))
|
|
{
|
|
DBG1(DBG_DMN, "Starting IKE service %s (strongSwan %s, "
|
|
"Windows %s %d.%d.%d (SP %d.%d)", SERVICE_NAME, VERSION,
|
|
osvie.wProductType == VER_NT_WORKSTATION ? "Client" : "Server",
|
|
osvie.dwMajorVersion, osvie.dwMinorVersion, osvie.dwBuildNumber,
|
|
osvie.wServicePackMajor, osvie.wServicePackMinor);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update service state to SCM, increase check point if state didn't change
|
|
*/
|
|
static void update_status(DWORD state)
|
|
{
|
|
if (state == status.dwCurrentState)
|
|
{
|
|
status.dwCheckPoint++;
|
|
}
|
|
else
|
|
{
|
|
status.dwCheckPoint = 0;
|
|
}
|
|
status.dwCurrentState = state;
|
|
if (handle)
|
|
{
|
|
SetServiceStatus(handle, &status);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Control handler for console
|
|
*/
|
|
static BOOL WINAPI console_handler(DWORD dwCtrlType)
|
|
{
|
|
switch (dwCtrlType)
|
|
{
|
|
case CTRL_C_EVENT:
|
|
case CTRL_BREAK_EVENT:
|
|
case CTRL_CLOSE_EVENT:
|
|
DBG1(DBG_DMN, "application is stopping, cleaning up");
|
|
if (status.dwCurrentState == SERVICE_RUNNING)
|
|
{
|
|
charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL,
|
|
dwCtrlType);
|
|
}
|
|
/* signal main thread to clean up */
|
|
SetEvent(event);
|
|
return TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Service handler function
|
|
*/
|
|
static DWORD WINAPI service_handler(DWORD dwControl, DWORD dwEventType,
|
|
LPVOID lpEventData, LPVOID lpContext)
|
|
{
|
|
switch (dwControl)
|
|
{
|
|
case SERVICE_CONTROL_STOP:
|
|
case SERVICE_CONTROL_SHUTDOWN:
|
|
DBG1(DBG_DMN, "service is stopping, cleaning up");
|
|
if (status.dwCurrentState == SERVICE_RUNNING)
|
|
{
|
|
charon->bus->alert(charon->bus, ALERT_SHUTDOWN_SIGNAL,
|
|
dwControl);
|
|
}
|
|
/* signal main thread to clean up */
|
|
SetEvent(event);
|
|
return NO_ERROR;
|
|
case SERVICE_CONTROL_INTERROGATE:
|
|
return NO_ERROR;
|
|
default:
|
|
return ERROR_CALL_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wait for console program shutdown
|
|
*/
|
|
static int console_wait()
|
|
{
|
|
update_status(SERVICE_RUNNING);
|
|
|
|
if (WaitForSingleObjectEx(event, INFINITE, TRUE) != WAIT_OBJECT_0)
|
|
{
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Wait for service shutdown
|
|
*/
|
|
static int service_wait()
|
|
{
|
|
/* service is initialized, we now accept control requests */
|
|
status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
|
update_status(SERVICE_RUNNING);
|
|
status.dwControlsAccepted = 0;
|
|
|
|
if (WaitForSingleObjectEx(event, INFINITE, TRUE) != WAIT_OBJECT_0)
|
|
{
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Add namespace alias
|
|
*/
|
|
static void __attribute__ ((constructor))register_namespace()
|
|
{
|
|
/* inherit settings from charon */
|
|
library_add_namespace("charon");
|
|
}
|
|
|
|
/**
|
|
* Initialize and run charon using a wait function
|
|
*/
|
|
static void init_and_run(DWORD dwArgc, LPTSTR *lpszArgv, int (*wait)())
|
|
{
|
|
level_t levels[DBG_MAX];
|
|
int i;
|
|
|
|
for (i = 0; i < DBG_MAX; i++)
|
|
{
|
|
levels[i] = LEVEL_CTRL;
|
|
}
|
|
|
|
update_status(SERVICE_START_PENDING);
|
|
event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (event)
|
|
{
|
|
update_status(SERVICE_START_PENDING);
|
|
if (library_init(NULL, SERVICE_NAME))
|
|
{
|
|
update_status(SERVICE_START_PENDING);
|
|
if (libcharon_init())
|
|
{
|
|
charon->set_default_loggers(charon, levels, TRUE);
|
|
charon->load_loggers(charon);
|
|
print_version();
|
|
update_status(SERVICE_START_PENDING);
|
|
if (charon->initialize(charon, PLUGINS))
|
|
{
|
|
update_status(SERVICE_START_PENDING);
|
|
lib->plugins->status(lib->plugins, LEVEL_CTRL);
|
|
|
|
charon->start(charon);
|
|
|
|
status.dwWin32ExitCode = wait();
|
|
}
|
|
update_status(SERVICE_STOP_PENDING);
|
|
libcharon_deinit();
|
|
}
|
|
update_status(SERVICE_STOP_PENDING);
|
|
library_deinit();
|
|
}
|
|
update_status(SERVICE_STOP_PENDING);
|
|
CloseHandle(event);
|
|
}
|
|
update_status(SERVICE_STOPPED);
|
|
}
|
|
|
|
/**
|
|
* Main routine when running from console
|
|
*/
|
|
static void console_main(DWORD dwArgc, LPTSTR *lpszArgv)
|
|
{
|
|
status.dwWin32ExitCode = 1;
|
|
|
|
if (SetConsoleCtrlHandler(console_handler, TRUE))
|
|
{
|
|
init_and_run(dwArgc, lpszArgv, console_wait);
|
|
SetConsoleCtrlHandler(console_handler, FALSE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Switch the working directory to the executable directory
|
|
*/
|
|
static bool switch_workingdir()
|
|
{
|
|
CHAR path[MAX_PATH], *pos;
|
|
HMODULE module;
|
|
|
|
module = GetModuleHandle(NULL);
|
|
if (!module)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (!GetModuleFileName(module, path, sizeof(path)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
pos = strrchr(path, '\\');
|
|
if (!pos)
|
|
{
|
|
return FALSE;
|
|
}
|
|
*pos = 0;
|
|
return SetCurrentDirectory(path);
|
|
}
|
|
|
|
/**
|
|
* Service main routine when running as service
|
|
*/
|
|
static void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv)
|
|
{
|
|
memset(&status, 0, sizeof(status));
|
|
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
|
status.dwWin32ExitCode = 1;
|
|
|
|
handle = RegisterServiceCtrlHandlerEx(SERVICE_NAME, service_handler, NULL);
|
|
if (handle)
|
|
{
|
|
if (switch_workingdir())
|
|
{
|
|
init_and_run(dwArgc, lpszArgv, service_wait);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Main function, starts the service
|
|
*/
|
|
int main(int argc, char *argv[])
|
|
{
|
|
SERVICE_TABLE_ENTRY services[] = {
|
|
{
|
|
.lpServiceName = SERVICE_NAME,
|
|
.lpServiceProc = service_main,
|
|
},
|
|
{ NULL, NULL },
|
|
};
|
|
DWORD err;
|
|
|
|
dbg = dbg_stderr;
|
|
|
|
if (!StartServiceCtrlDispatcher(services))
|
|
{
|
|
err = GetLastError();
|
|
if (err == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
|
|
{
|
|
console_main(argc, argv);
|
|
}
|
|
else
|
|
{
|
|
return 2;
|
|
}
|
|
}
|
|
return status.dwWin32ExitCode;
|
|
}
|