HANDLE create_event (const char *name, bool allow_all, bool initial_state, bool manual_reset) { if (allow_all) { struct security_attributes sa; if (!init_security_attributes_allow_all (&sa)) return NULL; return CreateEvent (&sa.sa, (BOOL)manual_reset, (BOOL)initial_state, name); } else return CreateEvent (NULL, (BOOL)manual_reset, (BOOL)initial_state, name); }
void semaphore_open (struct semaphore *s, const char *name) { struct security_attributes sa; s->locked = false; s->name = name; s->hand = NULL; if (init_security_attributes_allow_all (&sa)) s->hand = CreateSemaphore(&sa.sa, 1, 1, name); if (s->hand == NULL) msg (M_WARN|M_ERRNO, "WARNING: Cannot create Win32 semaphore '%s'", name); else dmsg (D_SEMAPHORE, "Created Win32 semaphore '%s'", s->name); }
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); }
void win32_signal_open (struct win32_signal *ws, int force, const char *exit_event_name, bool exit_event_initial_state) { CLEAR (*ws); ws->mode = WSO_MODE_UNDEF; ws->in.read = INVALID_HANDLE_VALUE; ws->in.write = INVALID_HANDLE_VALUE; ws->console_mode_save = 0; ws->console_mode_save_defined = false; if (force == WSO_NOFORCE || force == WSO_FORCE_CONSOLE) { /* * Try to open console. */ ws->in.read = GetStdHandle (STD_INPUT_HANDLE); if (ws->in.read != INVALID_HANDLE_VALUE) { if (GetConsoleMode (ws->in.read, &ws->console_mode_save)) { /* running on a console */ const DWORD new_console_mode = ws->console_mode_save & ~(ENABLE_WINDOW_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT); if (new_console_mode != ws->console_mode_save) { if (!SetConsoleMode (ws->in.read, new_console_mode)) msg (M_ERR, "Error: win32_signal_open: SetConsoleMode failed"); ws->console_mode_save_defined = true; } ws->mode = WSO_MODE_CONSOLE; } else ws->in.read = INVALID_HANDLE_VALUE; /* probably running as a service */ } } /* * If console open failed, assume we are running * as a service. */ if ((force == WSO_NOFORCE || force == WSO_FORCE_SERVICE) && !HANDLE_DEFINED (ws->in.read) && exit_event_name) { struct security_attributes sa; if (!init_security_attributes_allow_all (&sa)) msg (M_ERR, "Error: win32_signal_open: init SA failed"); ws->in.read = CreateEvent (&sa.sa, TRUE, exit_event_initial_state ? TRUE : FALSE, exit_event_name); if (ws->in.read == NULL) { msg (M_WARN|M_ERRNO, "NOTE: CreateEvent '%s' failed", exit_event_name); } else { if (WaitForSingleObject (ws->in.read, 0) != WAIT_TIMEOUT) msg (M_FATAL, "ERROR: Exit Event ('%s') is signaled", exit_event_name); else ws->mode = WSO_MODE_SERVICE; } } }
VOID ServiceStart (DWORD dwArgc, LPTSTR *lpszArgv) { char exe_path[MAX_PATH]; char config_dir[MAX_PATH]; char ext_string[16]; char log_dir[MAX_PATH]; char priority_string[64]; char append_string[2]; DWORD priority; bool append; ResetError (); if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000)) { MSG (M_ERR, "ReportStatusToSCMgr #1 failed"); goto finish; } /* * Create our exit event */ exit_event = create_event (EXIT_EVENT_NAME, false, false, true); if (!exit_event) { MSG (M_ERR, "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) { MSG (M_ERR, "Exit event is already signaled -- we were not shut down properly"); goto finish; } if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000)) { MSG (M_ERR, "ReportStatusToSCMgr #2 failed"); goto finish; } /* * Read info from registry in key HKLM\SOFTWARE\OpenVPN */ { HKEY openvpn_key; LONG status; DWORD len; DWORD type; char error_string[256]; static const char error_format_str[] = "Error querying registry key of type REG_SZ: HKLM\\" REG_KEY "\\%s"; static const char error_format_dword[] = "Error querying registry key of type REG_DWORD: HKLM\\" REG_KEY "\\%s"; status = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_KEY, 0, KEY_READ, &openvpn_key); if (status != ERROR_SUCCESS) { SetLastError (status); MSG (M_SYSERR, "Registry key HKLM\\" REG_KEY " not found"); goto finish; } /* get path to openvpn.exe */ QUERY_REG_STRING ("exe_path", exe_path); /* get path to configuration directory */ QUERY_REG_STRING ("config_dir", config_dir); /* get extension on configuration files */ QUERY_REG_STRING ("config_ext", ext_string); /* get path to log directory */ QUERY_REG_STRING ("log_dir", log_dir); /* get priority for spawned OpenVPN subprocesses */ QUERY_REG_STRING ("priority", priority_string); /* should we truncate or append to logfile? */ QUERY_REG_STRING ("log_append", append_string); RegCloseKey (openvpn_key); } /* set process priority */ priority = NORMAL_PRIORITY_CLASS; if (!strcasecmp (priority_string, "IDLE_PRIORITY_CLASS")) priority = IDLE_PRIORITY_CLASS; else if (!strcasecmp (priority_string, "BELOW_NORMAL_PRIORITY_CLASS")) priority = BELOW_NORMAL_PRIORITY_CLASS; else if (!strcasecmp (priority_string, "NORMAL_PRIORITY_CLASS")) priority = NORMAL_PRIORITY_CLASS; else if (!strcasecmp (priority_string, "ABOVE_NORMAL_PRIORITY_CLASS")) priority = ABOVE_NORMAL_PRIORITY_CLASS; else if (!strcasecmp (priority_string, "HIGH_PRIORITY_CLASS")) priority = HIGH_PRIORITY_CLASS; else { MSG (M_ERR, "Unknown priority name: %s", priority_string); goto finish; } /* set log file append/truncate flag */ append = false; if (append_string[0] == '0') append = false; else if (append_string[0] == '1') append = true; else { MSG (M_ERR, "Log file append flag (given as '%s') must be '0' or '1'", append_string); goto finish; } /* * Instantiate an OpenVPN process for each configuration * file found. */ { WIN32_FIND_DATA find_obj; HANDLE find_handle; BOOL more_files; char find_string[MAX_PATH]; mysnprintf (find_string, "%s\\*", config_dir); find_handle = FindFirstFile (find_string, &find_obj); if (find_handle == INVALID_HANDLE_VALUE) { MSG (M_ERR, "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; char log_file[MAX_PATH]; char log_path[MAX_PATH]; char command_line[256]; CLEAR (start_info); CLEAR (proc_info); CLEAR (sa); if (!ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000)) { MSG (M_ERR, "ReportStatusToSCMgr #3 failed"); FindClose (find_handle); goto finish; } /* does file have the correct type and extension? */ if (match (&find_obj, ext_string)) { /* get log file pathname */ if (!modext (log_file, sizeof (log_file), find_obj.cFileName, "log")) { MSG (M_ERR, "Cannot construct logfile name based on: %s", find_obj.cFileName); FindClose (find_handle); goto finish; } mysnprintf (log_path, "%s\\%s", log_dir, log_file); /* construct command line */ mysnprintf (command_line, 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)) { MSG (M_SYSERR, "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, append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (log_handle == INVALID_HANDLE_VALUE) { MSG (M_SYSERR, "Cannot open logfile: %s", log_path); FindClose (find_handle); goto finish; } /* append to logfile? */ if (append) { if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) { MSG (M_SYSERR, "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(exe_path, command_line, NULL, NULL, TRUE, priority | CREATE_NEW_CONSOLE, NULL, config_dir, &start_info, &proc_info)) { MSG (M_SYSERR, "CreateProcess failed, exe='%s' cmdline='%s' dir='%s'", exe_path, command_line, 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)) { MSG (M_SYSERR, "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 */ if (!ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0)) { MSG (M_ERR, "ReportStatusToSCMgr SERVICE_RUNNING failed"); goto finish; } /* wait for our shutdown signal */ if (WaitForSingleObject (exit_event, INFINITE) != WAIT_OBJECT_0) { MSG (M_ERR, "wait for shutdown signal failed"); } finish: ServiceStop (); if (exit_event) CloseHandle (exit_event); }
void redirect_stdout_stderr (const char *file, bool append) { #if defined(WIN32) if (!std_redir) { HANDLE log_handle; int log_fd; struct security_attributes sa; init_security_attributes_allow_all (&sa); log_handle = CreateFile (file, GENERIC_WRITE, FILE_SHARE_READ, &sa.sa, append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (log_handle == INVALID_HANDLE_VALUE) { msg (M_WARN|M_ERRNO, "Warning: cannot open --log file: %s", file); return; } /* append to logfile? */ if (append) { if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) msg (M_ERR, "Error: cannot seek to end of --log file: %s", file); } /* save original stderr for password prompts */ orig_stderr = GetStdHandle (STD_ERROR_HANDLE); /* set up for redirection */ if (!SetStdHandle (STD_OUTPUT_HANDLE, log_handle) || !SetStdHandle (STD_ERROR_HANDLE, log_handle)) msg (M_ERR, "Error: cannot redirect stdout/stderr to --log file: %s", file); /* direct stdout/stderr to point to log_handle */ log_fd = _open_osfhandle ((intptr_t)log_handle, _O_TEXT); if (log_fd == -1) msg (M_ERR, "Error: --log redirect failed due to _open_osfhandle failure"); /* open log_handle as FILE stream */ ASSERT (msgfp == NULL); msgfp = _fdopen (log_fd, "w"); if (msgfp == NULL) msg (M_ERR, "Error: --log redirect failed due to _fdopen"); std_redir = true; } #elif defined(HAVE_DUP2) if (!std_redir) { int out = open (file, O_CREAT | O_WRONLY | (append ? O_APPEND : O_TRUNC), S_IRUSR | S_IWUSR); if (out < 0) { msg (M_WARN|M_ERRNO, "Warning: Error redirecting stdout/stderr to --log file: %s", file); return; } if (dup2 (out, 1) == -1) msg (M_ERR, "--log file redirection error on stdout"); if (dup2 (out, 2) == -1) msg (M_ERR, "--log file redirection error on stderr"); if (out > 2) close (out); std_redir = true; } #else msg (M_WARN, "WARNING: The --log option is not supported on this OS because it lacks the dup2 function"); #endif }