forked from xkjyeah/openvpnserv2
-
Notifications
You must be signed in to change notification settings - Fork 25
Moving to interactive service #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
dfd5e61
Split into multiple files
lstipakov 66b2bc6
GHA: enable for all branches
lstipakov 71703ce
Decouple OpenVPN service logic from Windows service infrastructure
lstipakov e21b7ed
Remove unused functions
lstipakov 5f91668
Use interactice service to start openvpn process
lstipakov 8f7be16
Remove priority class code
lstipakov eedeab1
Remove installer code
lstipakov 83c6d32
Generate assembly metadata during build
lstipakov db8cb6e
mono: switch to MSBuild
lstipakov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,6 @@ | ||
| obj | ||
| /bin | ||
| .vs | ||
| .vscode | ||
| AssemblyInfo.cs | ||
| git_hash.txt |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,189 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.IO; | ||
| using System.IO.Pipes; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using System.Timers; | ||
|
|
||
| namespace OpenVpn | ||
| { | ||
| /// <summary> | ||
| /// Represents single OpenVPN connection | ||
| /// </summary> | ||
| class OpenVpnChild | ||
| { | ||
| string logFile; | ||
| Process process; | ||
| System.Timers.Timer restartTimer; | ||
| OpenVpnServiceConfiguration config; | ||
| string configFile; | ||
| string exitEvent; | ||
| private CancellationTokenSource exitPollingToken = new CancellationTokenSource(); | ||
|
|
||
| /// <summary> | ||
| /// Constructs OpenVpnChild object | ||
| /// </summary> | ||
| /// <param name="config"></param> | ||
| /// <param name="configFile">path to ovpn profile</param> | ||
| public OpenVpnChild(OpenVpnServiceConfiguration config, string configFile) | ||
| { | ||
| this.config = config; | ||
| this.configFile = configFile; | ||
| this.exitEvent = Path.GetFileName(configFile) + "_" + Process.GetCurrentProcess().Id.ToString(); | ||
| var justFilename = Path.GetFileName(configFile); | ||
| logFile = Path.Combine(config.logDir, justFilename.Substring(0, justFilename.Length - config.configExt.Length) + ".log"); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Signal OpenVPN process exit event and cancel polling task. | ||
| /// </summary> | ||
| public void SignalProcess() | ||
| { | ||
| if (restartTimer != null) | ||
| { | ||
| restartTimer.Stop(); | ||
| } | ||
| try | ||
| { | ||
| if (process != null && !process.HasExited) | ||
| { | ||
| try | ||
| { | ||
| config.LogMessage($"Signalling PID {process.Id} for config {configFile} to exit"); | ||
|
|
||
| using (var waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset, exitEvent)) | ||
| { | ||
| waitHandle.Set(); // Signal OpenVPN to exit gracefully | ||
| } | ||
|
|
||
| exitPollingToken.Cancel(); // Stop monitoring | ||
| } | ||
| catch (IOException e) | ||
| { | ||
| config.LogMessage("IOException creating exit event named '" + exitEvent + "' " + e.Message + e.StackTrace, EventLogEntryType.Error); | ||
| } | ||
| catch (UnauthorizedAccessException e) | ||
| { | ||
| config.LogMessage("UnauthorizedAccessException creating exit event named '" + exitEvent + "' " + e.Message + e.StackTrace, EventLogEntryType.Error); | ||
| } | ||
| catch (WaitHandleCannotBeOpenedException e) | ||
| { | ||
| config.LogMessage("WaitHandleCannotBeOpenedException creating exit event named '" + exitEvent + "' " + e.Message + e.StackTrace, EventLogEntryType.Error); | ||
| } | ||
| catch (ArgumentException e) | ||
| { | ||
| config.LogMessage("ArgumentException creating exit event named '" + exitEvent + "' " + e.Message + e.StackTrace, EventLogEntryType.Error); | ||
| } | ||
| } | ||
| } | ||
| catch (InvalidOperationException) { } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Polling task to detect process exit and restart it. | ||
| /// </summary> | ||
| private async void MonitorProcessExit() | ||
d12fk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| if (process == null) return; | ||
|
|
||
| config.LogMessage($"Started polling for OpenVPN process, PID {process.Id}"); | ||
|
|
||
| try | ||
| { | ||
| while (!process.HasExited) | ||
| { | ||
| await Task.Delay(1000, exitPollingToken.Token); | ||
| } | ||
|
|
||
| config.LogMessage($"Process {process.Id} has exited.", EventLogEntryType.Warning); | ||
| RestartAfterDelay(10000); | ||
| } | ||
| catch (TaskCanceledException) | ||
| { | ||
| config.LogMessage("Process monitoring cancelled."); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| config.LogMessage($"Error in MonitorProcessExit: {ex.Message}", EventLogEntryType.Error); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Restart OpenVPN process after delay | ||
| /// </summary> | ||
| /// <param name="delayMs"></param> | ||
| private void RestartAfterDelay(int delayMs) | ||
| { | ||
| config.LogMessage($"Restarting process for {configFile} in {delayMs / 1000} sec."); | ||
|
|
||
| restartTimer = new System.Timers.Timer(delayMs); | ||
| restartTimer.AutoReset = false; | ||
| restartTimer.Elapsed += (object source, ElapsedEventArgs ev) => | ||
| { | ||
| Start(); | ||
| }; | ||
| restartTimer.Start(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Name of the OpenVPN interactive service pipe | ||
| /// </summary> | ||
| private const string PipeName = @"openvpn\service"; | ||
|
|
||
| /// <summary> | ||
| /// Start OpenVPN child process. | ||
| /// Connect to interactive service via named pipe and pass a startup info. | ||
| /// Read OpenVPN process PID from the pipe and set up polling task | ||
| /// to detect process exit. | ||
| /// </summary> | ||
| public void Start() | ||
| { | ||
| using (var pipeClient = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut, PipeOptions.Asynchronous)) | ||
| { | ||
| config.LogMessage("Connecting to iservice pipe..."); | ||
| pipeClient.Connect(5000); | ||
|
|
||
| using (var writer = new BinaryWriter(pipeClient, Encoding.Unicode)) | ||
| using (var reader = new StreamReader(pipeClient, Encoding.Unicode)) | ||
| { | ||
| // send startup info | ||
| var logOption = config.logAppend ? "--log-append " : "--log"; | ||
| var cmdLine = $"{logOption} \"{logFile}\" --config \"{configFile}\" --service \"{exitEvent}\" 0 --pull-filter ignore route-method"; | ||
|
|
||
lstipakov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // config_dir + \0 + options + \0 + password + \0 | ||
| var startupInfo = $"{config.configDir}\0{cmdLine}\0\0"; | ||
|
|
||
| byte[] messageBytes = Encoding.Unicode.GetBytes(startupInfo); | ||
| writer.Write(messageBytes); | ||
| writer.Flush(); | ||
|
|
||
| config.LogMessage("Sent startupInfo to iservice"); | ||
|
|
||
| // read openvpn process pid from the pipe | ||
| string[] lines = { reader.ReadLine(), reader.ReadLine() }; | ||
|
|
||
| config.LogMessage($"Read from iservice: {string.Join(" ", lines)}"); | ||
| var errorCode = Convert.ToInt32(lines[0], 16); | ||
|
|
||
| if (errorCode == 0) | ||
| { | ||
| var pid = Convert.ToInt32(lines[1], 16); | ||
| process = Process.GetProcessById(pid); | ||
|
|
||
| exitPollingToken = new CancellationTokenSource(); | ||
| Task.Run(() => MonitorProcessExit(), exitPollingToken.Token); | ||
|
|
||
| config.LogMessage($"Started monitoring OpenVPN process, PID {pid}"); | ||
| } else | ||
| { | ||
| config.LogMessage("Error getting openvpn process PID", EventLogEntryType.Error); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| using System; | ||
| using System.Diagnostics; | ||
|
|
||
| namespace OpenVpn | ||
| { | ||
| class OpenVpnServiceConfiguration | ||
| { | ||
| public string exePath { get; set; } | ||
| public string configExt { get; set; } | ||
| public string configDir { get; set; } | ||
| public string logDir { get; set; } | ||
| public bool logAppend { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Delegate used to log messages with a specified severity level. | ||
| /// </summary> | ||
| public Action<string, EventLogEntryType> Log; | ||
|
|
||
| /// <summary> | ||
| /// Constructs OpenVpnServiceConfiguration object | ||
| /// </summary> | ||
| /// <param name="logAction">Log callback</param> | ||
| public OpenVpnServiceConfiguration(Action<string, EventLogEntryType> logAction) | ||
| { | ||
| Log = logAction; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Writes log message via log callback | ||
| /// </summary> | ||
| /// <param name="message"></param> | ||
| /// <param name="type"></param> | ||
| public void LogMessage(string message, EventLogEntryType type = EventLogEntryType.Information) | ||
| { | ||
| Log(message, type); | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.