DWORD MsgToEventLog (DWORD flags, LPCTSTR format, ...) { HANDLE hEventSource; TCHAR msg[2][256]; DWORD error = 0; LPCTSTR err_msg = TEXT(""); va_list arglist; if (flags & MSG_FLAGS_SYS_CODE) { error = GetLastError (); err_msg = GetLastErrorText (); } hEventSource = RegisterEventSource (NULL, APPNAME); if (hEventSource != NULL) { openvpn_sntprintf (msg[0], _countof (msg[0]), TEXT("%s%s: %s"), APPNAME, (flags & MSG_FLAGS_ERROR) ? TEXT(" error") : TEXT(""), err_msg); va_start (arglist, format); openvpn_vsntprintf (msg[1], _countof (msg[1]), format, arglist); va_end (arglist); const TCHAR *mesg[] = { msg[0], msg[1] }; ReportEvent (hEventSource, flags & MSG_FLAGS_ERROR ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 2, 0, mesg, NULL); DeregisterEventSource (hEventSource); } return error; }
LPCTSTR GetLastErrorText () { static TCHAR buf[256]; DWORD len; LPTSTR tmp = NULL; len = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&tmp, 0, NULL); if (len == 0 || (long) _countof (buf) < (long) len + 14) buf[0] = TEXT('\0'); else { tmp[_tcslen (tmp) - 2] = TEXT('\0'); /* remove CR/LF characters */ openvpn_sntprintf (buf, _countof (buf), TEXT("%s (0x%x)"), tmp, GetLastError()); } if (tmp) LocalFree (tmp); return buf; }
VOID WINAPI ServiceStartAutomatic(DWORD dwArgc, LPTSTR *lpszArgv) { DWORD error = NO_ERROR; settings_t settings; service = RegisterServiceCtrlHandlerEx(automatic_service.name, ServiceCtrlAutomatic, &status); if (!service) { return; } status.dwServiceType = SERVICE_WIN32_SHARE_PROCESS; status.dwCurrentState = SERVICE_START_PENDING; status.dwServiceSpecificExitCode = NO_ERROR; status.dwWin32ExitCode = NO_ERROR; status.dwWaitHint = 3000; if (!ReportStatusToSCMgr(service, &status)) { MsgToEventLog(M_ERR, TEXT("ReportStatusToSCMgr #1 failed")); goto finish; } /* * Create our exit event */ exit_event = create_event(EXIT_EVENT_NAME, false, false, true); if (!exit_event) { MsgToEventLog(M_ERR, TEXT("CreateEvent failed")); goto finish; } /* * If exit event is already signaled, it means we were not * shut down properly. */ if (WaitForSingleObject(exit_event, 0) != WAIT_TIMEOUT) { MsgToEventLog(M_ERR, TEXT("Exit event is already signaled -- we were not shut down properly")); goto finish; } if (!ReportStatusToSCMgr(service, &status)) { MsgToEventLog(M_ERR, TEXT("ReportStatusToSCMgr #2 failed")); goto finish; } /* * Read info from registry in key HKLM\SOFTWARE\OpenVPN */ error = GetOpenvpnSettings(&settings); if (error != ERROR_SUCCESS) { goto finish; } /* * Instantiate an OpenVPN process for each configuration * file found. */ { WIN32_FIND_DATA find_obj; HANDLE find_handle; BOOL more_files; TCHAR find_string[MAX_PATH]; openvpn_sntprintf(find_string, MAX_PATH, TEXT("%s\\*"), settings.config_dir); find_handle = FindFirstFile(find_string, &find_obj); if (find_handle == INVALID_HANDLE_VALUE) { MsgToEventLog(M_ERR, TEXT("Cannot get configuration file list using: %s"), find_string); goto finish; } /* * Loop over each config file */ do { HANDLE log_handle = NULL; STARTUPINFO start_info; PROCESS_INFORMATION proc_info; struct security_attributes sa; TCHAR log_file[MAX_PATH]; TCHAR log_path[MAX_PATH]; TCHAR command_line[256]; CLEAR(start_info); CLEAR(proc_info); CLEAR(sa); if (!ReportStatusToSCMgr(service, &status)) { MsgToEventLog(M_ERR, TEXT("ReportStatusToSCMgr #3 failed")); FindClose(find_handle); goto finish; } /* does file have the correct type and extension? */ if (match(&find_obj, settings.ext_string)) { /* get log file pathname */ if (!modext(log_file, _countof(log_file), find_obj.cFileName, TEXT("log"))) { MsgToEventLog(M_ERR, TEXT("Cannot construct logfile name based on: %s"), find_obj.cFileName); FindClose(find_handle); goto finish; } openvpn_sntprintf(log_path, _countof(log_path), TEXT("%s\\%s"), settings.log_dir, log_file); /* construct command line */ openvpn_sntprintf(command_line, _countof(command_line), TEXT(PACKAGE " --service %s 1 --config \"%s\""), EXIT_EVENT_NAME, find_obj.cFileName); /* Make security attributes struct for logfile handle so it can * be inherited. */ if (!init_security_attributes_allow_all(&sa)) { error = MsgToEventLog(M_SYSERR, TEXT("InitializeSecurityDescriptor start_" PACKAGE " failed")); goto finish; } /* open logfile as stdout/stderr for soon-to-be-spawned subprocess */ log_handle = CreateFile(log_path, GENERIC_WRITE, FILE_SHARE_READ, &sa.sa, settings.append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (log_handle == INVALID_HANDLE_VALUE) { error = MsgToEventLog(M_SYSERR, TEXT("Cannot open logfile: %s"), log_path); FindClose(find_handle); goto finish; } /* append to logfile? */ if (settings.append) { if (SetFilePointer(log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) { error = MsgToEventLog(M_SYSERR, TEXT("Cannot seek to end of logfile: %s"), log_path); FindClose(find_handle); goto finish; } } /* fill in STARTUPINFO struct */ GetStartupInfo(&start_info); start_info.cb = sizeof(start_info); start_info.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; start_info.wShowWindow = SW_HIDE; start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); start_info.hStdOutput = start_info.hStdError = log_handle; /* create an OpenVPN process for one config file */ if (!CreateProcess(settings.exe_path, command_line, NULL, NULL, TRUE, settings.priority | CREATE_NEW_CONSOLE, NULL, settings.config_dir, &start_info, &proc_info)) { error = MsgToEventLog(M_SYSERR, TEXT("CreateProcess failed, exe='%s' cmdline='%s' dir='%s'"), settings.exe_path, command_line, settings.config_dir); FindClose(find_handle); CloseHandle(log_handle); goto finish; } /* close unneeded handles */ Sleep(1000); /* try to prevent race if we close logfile * handle before child process DUPs it */ if (!CloseHandle(proc_info.hProcess) || !CloseHandle(proc_info.hThread) || !CloseHandle(log_handle)) { error = MsgToEventLog(M_SYSERR, TEXT("CloseHandle failed")); goto finish; } } /* more files to process? */ more_files = FindNextFile(find_handle, &find_obj); } while (more_files); FindClose(find_handle); } /* we are now fully started */ status.dwCurrentState = SERVICE_RUNNING; status.dwWaitHint = 0; if (!ReportStatusToSCMgr(service, &status)) { MsgToEventLog(M_ERR, TEXT("ReportStatusToSCMgr SERVICE_RUNNING failed")); goto finish; } /* wait for our shutdown signal */ if (WaitForSingleObject(exit_event, INFINITE) != WAIT_OBJECT_0) { MsgToEventLog(M_ERR, TEXT("wait for shutdown signal failed")); } finish: if (exit_event) { CloseHandle(exit_event); } status.dwCurrentState = SERVICE_STOPPED; status.dwWin32ExitCode = error; ReportStatusToSCMgr(service, &status); }
DWORD GetOpenvpnSettings (settings_t *s) { TCHAR priority[64]; TCHAR append[2]; DWORD error; HKEY key; LONG status = RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_KEY, 0, KEY_READ, &key); if (status != ERROR_SUCCESS) { SetLastError (status); return MsgToEventLog (M_SYSERR, TEXT("Could not open Registry key HKLM\\%s not found"), REG_KEY); } error = GetRegString (key, TEXT("exe_path"), s->exe_path, sizeof (s->exe_path)); if (error != ERROR_SUCCESS) goto out; error = GetRegString (key, TEXT("config_dir"), s->config_dir, sizeof (s->config_dir)); if (error != ERROR_SUCCESS) goto out; error = GetRegString (key, TEXT("config_ext"), s->ext_string, sizeof (s->ext_string)); if (error != ERROR_SUCCESS) goto out; error = GetRegString (key, TEXT("log_dir"), s->log_dir, sizeof (s->log_dir)); if (error != ERROR_SUCCESS) goto out; error = GetRegString (key, TEXT("priority"), priority, sizeof (priority)); if (error != ERROR_SUCCESS) goto out; error = GetRegString (key, TEXT("log_append"), append, sizeof (append)); if (error != ERROR_SUCCESS) goto out; /* read if present, else use default */ error = GetRegString (key, TEXT("ovpn_admin_group"), s->ovpn_admin_group, sizeof (s->ovpn_admin_group)); if (error != ERROR_SUCCESS) { openvpn_sntprintf(s->ovpn_admin_group, _countof(s->ovpn_admin_group), OVPN_ADMIN_GROUP); error = 0; /* this error is not fatal */ } /* set process priority */ if (!_tcsicmp (priority, TEXT("IDLE_PRIORITY_CLASS"))) s->priority = IDLE_PRIORITY_CLASS; else if (!_tcsicmp (priority, TEXT("BELOW_NORMAL_PRIORITY_CLASS"))) s->priority = BELOW_NORMAL_PRIORITY_CLASS; else if (!_tcsicmp (priority, TEXT("NORMAL_PRIORITY_CLASS"))) s->priority = NORMAL_PRIORITY_CLASS; else if (!_tcsicmp (priority, TEXT("ABOVE_NORMAL_PRIORITY_CLASS"))) s->priority = ABOVE_NORMAL_PRIORITY_CLASS; else if (!_tcsicmp (priority, TEXT("HIGH_PRIORITY_CLASS"))) s->priority = HIGH_PRIORITY_CLASS; else { SetLastError (ERROR_INVALID_DATA); error = MsgToEventLog (M_SYSERR, TEXT("Unknown priority name: %s"), priority); goto out; } /* set log file append/truncate flag */ if (append[0] == TEXT('0')) s->append = FALSE; else if (append[0] == TEXT('1')) s->append = TRUE; else { SetLastError (ERROR_INVALID_DATA); error = MsgToEventLog (M_ERR, TEXT("Log file append flag (given as '%s') must be '0' or '1'"), append); goto out; } out: RegCloseKey (key); return error; }