Skip to content
This repository was archived by the owner on Apr 15, 2021. It is now read-only.

Commit 61dfe91

Browse files
committed
feat: Implement --stayopen, fix up argument names, fire Disconnected pb msg
- Implement --stayopen, allowing servers to be reused - Change CLI arguments for websocket and frontend pipe - Add Ctrl-C exiting for --stayopen and regular CLI runs - Fire ClientDisconnected pb msg on client disconnect or shutdown Fixes #2 Fixes #3 Fixes #5 Fixes #6
1 parent f3f2570 commit 61dfe91

File tree

2 files changed

+119
-62
lines changed

2 files changed

+119
-62
lines changed

IntifaceCLI/Options.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,19 @@ class Options
2020
[Option("userdeviceconfig", HelpText = "User Device Configuration file")]
2121
public string UserDeviceConfigFile { get; set; }
2222

23-
[Option("websocketserver", HelpText = "Run websocket server")]
24-
public bool UseWebsocketServer { get; set; }
25-
26-
[Option("websocketserverallinterfaces", Default = false, HelpText = "If passed, listen on all interfaces. Otherwise, only listen on 127.0.0.1.")]
23+
[Option("wsallinterfaces", Default = false, HelpText = "If passed, websocket server listens on all interfaces. Otherwise, only listen on 127.0.0.1.")]
2724
public bool WebsocketServerAllInterfaces { get; set; }
2825

29-
[Option("insecureport", Default = 0, HelpText = "Insecure port for websocket servers.")]
26+
[Option("wsinsecureport", Default = 0, HelpText = "Insecure port for websocket servers.")]
3027
public int WebsocketServerInsecurePort { get; set; }
3128

32-
[Option("secureport", Default = 0, HelpText = "Secure port for websocket servers. Requires certificate files also be passed.")]
29+
[Option("wssecureport", Default = 0, HelpText = "Secure port for websocket servers. Requires certificate files also be passed.")]
3330
public int WebsocketServerSecurePort { get; set; }
3431

35-
[Option("certfile", HelpText = "Certificate file (in PEM format) for secure Websocket Server")]
32+
[Option("wscertfile", HelpText = "Certificate file (in PEM format) for secure Websocket Server")]
3633
public string CertFile { get; set; }
3734

38-
[Option("privfile", HelpText = "Private Key file (in PEM format) for secure Websocket Server")]
35+
[Option("wsprivfile", HelpText = "Private Key file (in PEM format) for secure Websocket Server")]
3936
public string PrivFile { get; set; }
4037

4138
[Option("ipcserver", HelpText = "Run ipc server")]
@@ -44,8 +41,8 @@ class Options
4441
[Option("ipcpipe", Default = "ButtplugPipe", HelpText = "Pipe name for IPC Server")]
4542
public string IpcPipe { get; set; }
4643

47-
[Option("guipipe", Default = false, HelpText = "If passed, output protobufs for parent process, instead of strings.")]
48-
public bool GuiPipe { get; set; }
44+
[Option("frontendpipe", Default = false, HelpText = "If passed, output protobufs for parent process, instead of strings.")]
45+
public bool FrontendPipe { get; set; }
4946

5047
[Option("pingtime", Default = 0, HelpText = "Ping timeout maximum for server (in milliseconds")]
5148
public int PingTime { get; set; }

IntifaceCLI/ServerCLI.cs

Lines changed: 112 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,30 @@
1-
using System;
2-
using System.IO;
3-
using System.Threading;
4-
using System.Threading.Tasks;
5-
using Buttplug.Core.Logging;
1+
using Buttplug.Core.Logging;
62
using Buttplug.Devices.Configuration;
73
using Buttplug.Server;
8-
using Buttplug.Server.Connectors.WebsocketServer;
94
using Buttplug.Server.Connectors;
5+
using Buttplug.Server.Connectors.WebsocketServer;
106
using Google.Protobuf;
7+
using System;
8+
using System.IO;
9+
using System.Threading;
10+
using System.Threading.Tasks;
1111

1212
namespace IntifaceCLI
1313
{
14-
class ServerCLI
14+
internal class ServerCLI
1515
{
1616
private bool _useProtobufOutput;
17-
public bool ServerReady { get; }
1817
private DeviceManager _deviceManager;
1918
private readonly Stream _stdout = Console.OpenStandardOutput();
20-
private TaskCompletionSource<bool> _exitWait = new TaskCompletionSource<bool>();
21-
private CancellationTokenSource _stdinTokenSource = new CancellationTokenSource();
19+
private TaskCompletionSource<bool> _disconnectWait = new TaskCompletionSource<bool>();
20+
private readonly CancellationTokenSource _stdinTokenSource = new CancellationTokenSource();
2221
private Task _stdioTask;
23-
22+
private bool _shouldExit;
2423

2524
// Simple server that exposes device manager, since we'll need to chain device managers
2625
// through it for this. This is required because Windows 10 has problems disconnecting from
2726
// BLE devices without completely stopping and restarting processes. :(
28-
class CLIServer : ButtplugServer
27+
private class CLIServer : ButtplugServer
2928
{
3029
public DeviceManager DeviceManager => _deviceManager;
3130
public IButtplugLogManager LogManager => BpLogManager;
@@ -36,32 +35,37 @@ public CLIServer(string aServerName, uint aMaxPingTime, DeviceManager aDevMgr)
3635
}
3736
}
3837

39-
4038
public ServerCLI()
4139
{
42-
40+
Console.CancelKeyPress += (aObj, aEvent) =>
41+
{
42+
PrintProcessLog("Console exit called.");
43+
_shouldExit = true;
44+
_disconnectWait.SetResult(true);
45+
};
4346
}
4447

4548
private void ReadStdio()
4649
{
47-
using (Stream stdin = Console.OpenStandardInput())
50+
using (var stdin = Console.OpenStandardInput())
4851
{
4952
// Largest message we can receive is 1mb, so just allocate that now.
5053
var buf = new byte[1024768];
5154

52-
while (!_exitWait.Task.IsCompleted)
55+
while (!_disconnectWait.Task.IsCompleted && !_shouldExit)
5356
{
5457
try
5558
{
5659
var msg = ServerControlMessage.Parser.ParseDelimitedFrom(stdin);
5760

58-
if (msg == null || msg.Stop == null)
61+
if (msg?.Stop == null && !_shouldExit)
5962
{
6063
continue;
6164
}
6265

6366
_stdinTokenSource.Cancel();
64-
_exitWait?.SetResult(true);
67+
_disconnectWait?.SetResult(true);
68+
_shouldExit = true;
6569
break;
6670
}
6771
catch (InvalidProtocolBufferException)
@@ -97,15 +101,13 @@ private void PrintProcessLog(string aLogMsg)
97101

98102
public void RunServer(Options aOptions)
99103
{
100-
Console.CancelKeyPress += (obj, ev) => { _exitWait.SetResult(true); };
101-
102104
if (aOptions.Version)
103105
{
104106
Console.WriteLine("1");
105107
return;
106108
}
107109

108-
_useProtobufOutput = aOptions.GuiPipe;
110+
_useProtobufOutput = aOptions.FrontendPipe;
109111
if (_useProtobufOutput)
110112
{
111113
_stdioTask = new Task(ReadStdio);
@@ -133,13 +135,7 @@ public void RunServer(Options aOptions)
133135
DeviceConfigurationManager.Manager.LoadUserConfigurationFile(aOptions.UserDeviceConfigFile);
134136
}
135137

136-
if (aOptions.UseWebsocketServer && aOptions.UseIpcServer)
137-
{
138-
PrintProcessLog("ERROR: Can't run websocket server and IPC server at the same time!");
139-
return;
140-
}
141-
142-
if (!aOptions.UseWebsocketServer && !aOptions.UseIpcServer)
138+
if (aOptions.WebsocketServerInsecurePort == 0 && aOptions.WebsocketServerSecurePort == 0 && !aOptions.UseIpcServer)
143139
{
144140
PrintProcessLog("ERROR: Must specify either IPC server or Websocket server!");
145141
return;
@@ -158,6 +154,8 @@ public void RunServer(Options aOptions)
158154
ButtplugServer ServerFactory()
159155
{
160156
var server = new CLIServer(aOptions.ServerName, (uint)aOptions.PingTime, _deviceManager);
157+
158+
// Pull out the device manager for reuse later.
161159
if (_deviceManager == null)
162160
{
163161
_deviceManager = server.DeviceManager;
@@ -171,44 +169,103 @@ ButtplugServer ServerFactory()
171169
});
172170
}
173171

172+
server.ClientConnected += (aObj, aEvent) =>
173+
{
174+
if (_useProtobufOutput)
175+
{
176+
SendProcessMessage(new ServerProcessMessage
177+
{
178+
ClientConnected = new ServerProcessMessage.Types.ClientConnected
179+
{
180+
ClientName = server.ClientName
181+
}
182+
});
183+
}
184+
else
185+
{
186+
Console.WriteLine($"Client connected: {server.ClientName}");
187+
}
188+
};
189+
174190
return server;
175191
}
176192

177-
var ipcServer = new ButtplugIPCServer();
178-
var insecureWebsocketServer = new ButtplugWebsocketServer();
179-
var secureWebsocketServer = new ButtplugWebsocketServer();
193+
ButtplugIPCServer ipcServer = null;
194+
ButtplugWebsocketServer insecureWebsocketServer = null;
195+
ButtplugWebsocketServer secureWebsocketServer = null;
180196

181-
if (aOptions.UseWebsocketServer)
197+
if (aOptions.WebsocketServerInsecurePort != 0)
182198
{
183-
if (aOptions.WebsocketServerInsecurePort != 0)
184-
{
185-
insecureWebsocketServer.StartServerAsync(ServerFactory, 1, aOptions.WebsocketServerInsecurePort, !aOptions.WebsocketServerAllInterfaces).Wait();
186-
insecureWebsocketServer.ConnectionClosed += (aSender, aArgs) => { _exitWait.SetResult(true); };
187-
PrintProcessLog("Insecure websocket Server now running...");
188-
}
189-
if (aOptions.WebsocketServerSecurePort != 0 && aOptions.CertFile != null && aOptions.PrivFile != null)
190-
{
191-
secureWebsocketServer.StartServerAsync(ServerFactory, 1, aOptions.WebsocketServerSecurePort, !aOptions.WebsocketServerAllInterfaces, aOptions.CertFile, aOptions.PrivFile).Wait();
192-
secureWebsocketServer.ConnectionClosed += (aSender, aArgs) => { _exitWait.SetResult(true); };
193-
PrintProcessLog("Secure websocket Server now running...");
194-
}
199+
insecureWebsocketServer = new ButtplugWebsocketServer();
200+
insecureWebsocketServer.StartServerAsync(ServerFactory, 1, aOptions.WebsocketServerInsecurePort,
201+
!aOptions.WebsocketServerAllInterfaces).Wait();
202+
insecureWebsocketServer.ConnectionClosed += (aSender, aArgs) => { _disconnectWait.SetResult(true); };
203+
PrintProcessLog("Insecure websocket Server now running...");
195204
}
196-
else if (aOptions.UseIpcServer)
205+
206+
if (aOptions.WebsocketServerSecurePort != 0 && aOptions.CertFile != null &&
207+
aOptions.PrivFile != null)
197208
{
209+
secureWebsocketServer = new ButtplugWebsocketServer();
210+
secureWebsocketServer.StartServerAsync(ServerFactory, 1, aOptions.WebsocketServerSecurePort,
211+
!aOptions.WebsocketServerAllInterfaces, aOptions.CertFile, aOptions.PrivFile).Wait();
212+
secureWebsocketServer.ConnectionClosed += (aSender, aArgs) => { _disconnectWait.SetResult(true); };
213+
PrintProcessLog("Secure websocket Server now running...");
214+
}
215+
216+
if (aOptions.UseIpcServer)
217+
{
218+
ipcServer = new ButtplugIPCServer();
198219
ipcServer.StartServer(ServerFactory, aOptions.IpcPipe);
199-
ipcServer.ConnectionClosed += (aSender, aArgs) => { _exitWait.SetResult(true); };
220+
ipcServer.ConnectionClosed += (aSender, aArgs) => { _disconnectWait.SetResult(true); };
200221
PrintProcessLog("IPC Server now running...");
201222
}
202223

203-
// Now that all server possibilities are up and running, if we have
204-
// a pipe, let the parent program know we've started.
224+
// Now that all server possibilities are up and running, if we have a pipe, let the
225+
// parent program know we've started.
205226
if (_useProtobufOutput)
206227
{
207-
var msg = new ServerProcessMessage { ProcessStarted = new ServerProcessMessage.Types.ProcessStarted() };
228+
var msg = new ServerProcessMessage
229+
{ ProcessStarted = new ServerProcessMessage.Types.ProcessStarted() };
208230
SendProcessMessage(msg);
209231
}
232+
else
233+
{
234+
Console.WriteLine("Server started, waiting for client connection.");
235+
}
236+
237+
do
238+
{
239+
_disconnectWait.Task.Wait();
240+
241+
if (ipcServer != null && ipcServer.Connected)
242+
{
243+
ipcServer?.Disconnect();
244+
}
245+
246+
if (insecureWebsocketServer != null && insecureWebsocketServer.Connected)
247+
{
248+
insecureWebsocketServer?.DisconnectAsync().Wait();
249+
}
250+
251+
if (secureWebsocketServer != null && secureWebsocketServer.Connected)
252+
{
253+
secureWebsocketServer?.DisconnectAsync().Wait();
254+
}
255+
256+
if (_useProtobufOutput)
257+
{
258+
var msg = new ServerProcessMessage
259+
{ ClientDisconnected = new ServerProcessMessage.Types.ClientDisconnected() };
260+
SendProcessMessage(msg);
261+
}
262+
else
263+
{
264+
Console.WriteLine("Client disconnected.");
265+
}
266+
_disconnectWait = new TaskCompletionSource<bool>();
267+
} while (aOptions.StayOpen && !_shouldExit);
210268

211-
_exitWait.Task.Wait();
212269
if (!_useProtobufOutput)
213270
{
214271
return;
@@ -218,10 +275,13 @@ ButtplugServer ServerFactory()
218275

219276
if (_useProtobufOutput)
220277
{
221-
var exitMsg = new ServerProcessMessage();
222-
exitMsg.ProcessEnded = new ServerProcessMessage.Types.ProcessEnded();
278+
var exitMsg = new ServerProcessMessage
279+
{
280+
ProcessEnded = new ServerProcessMessage.Types.ProcessEnded()
281+
};
223282
SendProcessMessage(exitMsg);
224283
}
284+
225285
_stdinTokenSource.Cancel();
226286
}
227287
}

0 commit comments

Comments
 (0)