/* * 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 . * * 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 #include #include #include /** * 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->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); 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; }