diff options
Diffstat (limited to 'src/charon-svc/charon-svc.c')
-rw-r--r-- | src/charon-svc/charon-svc.c | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/src/charon-svc/charon-svc.c b/src/charon-svc/charon-svc.c new file mode 100644 index 000000000..03cbdb871 --- /dev/null +++ b/src/charon-svc/charon-svc.c @@ -0,0 +1,333 @@ +/* + * 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 <hydra.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; +} + +/** + * 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 (libhydra_init()) + { + update_status(SERVICE_START_PENDING); + if (libcharon_init()) + { + charon->load_loggers(charon, levels, TRUE); + 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); + libhydra_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; +} |