diff --git a/AmongUsCapture/AmongUsCapture.csproj b/AmongUsCapture/AmongUsCapture.csproj index 6141bc4a..3713f773 100644 --- a/AmongUsCapture/AmongUsCapture.csproj +++ b/AmongUsCapture/AmongUsCapture.csproj @@ -14,6 +14,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/AmongUsCapture/IPC/DBus/IPCadapterDBus.cs b/AmongUsCapture/IPC/DBus/IPCadapterDBus.cs index 43371aa3..e674db7e 100644 --- a/AmongUsCapture/IPC/DBus/IPCadapterDBus.cs +++ b/AmongUsCapture/IPC/DBus/IPCadapterDBus.cs @@ -2,10 +2,14 @@ using System.Diagnostics; using System.IO; using System.Net; +using System.Runtime.InteropServices; using System.Security.Policy; using System.Text; using System.Threading; using System.Threading.Tasks; +using Castle.Components.DictionaryAdapter; +using Gtk; +using Mono.Unix; using SharedMemory; using Tmds.DBus; using Tmds.DBus.Transports; @@ -28,6 +32,7 @@ public override URIStartResult HandleURIStart(string[] args) { var myProcessId = Process.GetCurrentProcess().Id; //Process[] processes = Process.GetProcessesByName("AmongUsCapture"); + //Process[] dotnetprocesses = Process.GetProcessesByName("dotnet"); //foreach (Process p in processes) //{ //if (p.Id != myProcessId) @@ -37,18 +42,81 @@ public override URIStartResult HandleURIStart(string[] args) // } Console.WriteLine(Program.GetExecutablePath()); - mutex = new Mutex(true, appName, out var createdNew); + //mutex = new Mutex(true, appName, out var createdNew); + bool createdNew = false; var wasURIStart = args.Length > 0 && args[0].StartsWith(UriScheme + "://"); var result = URIStartResult.CONTINUE; + if (!File.Exists(Path.Join(Settings.StorageLocation, ".amonguscapture.pid"))) + { + createdNew = true; + } + else + { + // Open our PID file. + using (var pidfile = File.OpenText(Path.Join(Settings.StorageLocation, ".amonguscapture.pid"))) + { + var pid = pidfile.ReadLine(); + if (pid != null) + { + var pidint = Int32.Parse(pid); + + try + { + var capproc = Process.GetProcessById(pidint); + var assmbname = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; + var runnername = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; + + if (!capproc.ProcessName.Contains("dotnet")) + { + // We're just going to assume that a dotnet process that matches this set of parameters + // is a running capture process. + throw new ArgumentException(); + + } + else if (!capproc.ProcessName.Contains(Path.GetFileName(runnername))) + { + throw new ArgumentException(); + } + + + if (capproc.HasExited) + { + throw new ArgumentException(); + } + } + catch (ArgumentException e) + { + // Process doesn't exist. Clear the file. + Console.WriteLine($"Found stale PID file containing {pid}."); + File.Delete(Path.Join(Settings.StorageLocation, ".amonguscapture.pid")); + createdNew = true; + } + } + } + + } + + if (createdNew) + { + using (var pidwriter = File.CreateText(Path.Join(Settings.StorageLocation, ".amonguscapture.pid"))) + { + pidwriter.Write(myProcessId); + } + } + + if (!createdNew) // send it to already existing instance if applicable, then close { - if (wasURIStart) SendToken(args[0]); + if (wasURIStart) SendToken(args[0]).Wait(); return URIStartResult.CLOSE; } else if (wasURIStart) // URI start on new instance, continue as normal but also handle current argument { + // if we are running, we create a file lock with our process in it. + // Also attach the pid delete handler from Program. + result = URIStartResult.PARSE; } @@ -56,24 +124,76 @@ public override URIStartResult HandleURIStart(string[] args) return result; } - + private static void RegisterProtocol() { - + // we really should query the user for this, but since Dialogs appear to be completely fucked, we're going + // to just install it right now. + var xdg_path = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "applications"); + var xdg_file = Path.Join(xdg_path, "aucapture-opener.desktop"); + + if (!File.Exists(xdg_file)) + { + var executingassmb = System.Reflection.Assembly.GetExecutingAssembly().Location; + if (Path.HasExtension("dll")) + { + executingassmb = "dotnet " + executingassmb; + } + + var xdg_file_write = new string[] + { + "[Desktop Entry]", + "Type=Application", + "Name=aucapture URI Handler", + $"Exec={executingassmb} %u", + "StartupNotify=false", + "MimeType=x-scheme-handler/aucapture;" + }; + + using (var file = File.CreateText(xdg_file)) + { + foreach (string str in xdg_file_write) + { + file.WriteLine(str); + } + } + + var xdg_posix = new UnixFileInfo(xdg_file); + + xdg_posix.FileAccessPermissions = FileAccessPermissions.UserReadWriteExecute + | FileAccessPermissions.GroupRead + | FileAccessPermissions.GroupExecute + | FileAccessPermissions.OtherRead + | FileAccessPermissions.OtherExecute; + + // Finally, register with xdg-mime. + + var xdgproc = new Process() + { + StartInfo = new ProcessStartInfo + { + FileName = "/usr/bin/xdg-mime", + Arguments = $"default aucapture-opener.desktop x-scheme-handler/aucapture", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + } + }; + + xdgproc.Start(); + string result = xdgproc.StandardOutput.ReadToEnd(); + xdgproc.WaitForExit(); + } } public override async Task SendToken(string jsonText) { - while (!_isListening) - { - Thread.Sleep(1000); - } - // Send the token via DBus. using (Connection conn = new Connection(Address.Session)) { await conn.ConnectAsync(); - + var obj = new IPCLink(); await conn.RegisterObjectAsync(obj); await conn.RegisterServiceAsync("org.AmongUsCapture.ipc", ServiceRegistrationOptions.None); @@ -92,7 +212,7 @@ public override void SendToken(string host, string connectCode) public async override Task RegisterMinion() { - Task.Factory.StartNew( async () => + Task.Factory.StartNew(async () => { using (_dbusconnection = new Connection(Address.Session)) @@ -113,7 +233,7 @@ public async override Task RegisterMinion() } } }); - + } public override void startWithToken(string uri) @@ -124,18 +244,18 @@ public override void startWithToken(string uri) public override bool Cancel() { if (!_cancellation.IsCancellationRequested) - { + { _cancellation.Cancel(); return true; } return false; } - + private void RespondToDbus(string signalresponse) { Settings.conInterface.WriteModuleTextColored("DBus", Color.Silver, - $"Recieved new message on DBus: {signalresponse}"); + $"Received new message on DBus: \"{signalresponse}\""); signalresponse = signalresponse.Trim('\r', '\n'); @@ -144,5 +264,4 @@ private void RespondToDbus(string signalresponse) OnTokenEvent(token); } } - } \ No newline at end of file diff --git a/AmongUsCapture/Memory/ProcessMemoryLinux.cs b/AmongUsCapture/Memory/ProcessMemoryLinux.cs index 5f67536c..0c8296b5 100644 --- a/AmongUsCapture/Memory/ProcessMemoryLinux.cs +++ b/AmongUsCapture/Memory/ProcessMemoryLinux.cs @@ -296,9 +296,12 @@ public static class LinuxAPI // // https://man7.org/linux/man-pages/man2/process_vm_readv.2.html - [DllImport("libc.so.6", SetLastError = true)] + [DllImport("libc", SetLastError = true)] public static extern int process_vm_readv(int pid, IntPtr local_iov, ulong liovcnt, IntPtr remote_iov, ulong riovcnt, ulong flags); + + [DllImport("libc", SetLastError = true)] + private static extern int chmod(string pathname, int mode); } diff --git a/AmongUsCapture/Program.cs b/AmongUsCapture/Program.cs index a58cdc15..e31c2338 100644 --- a/AmongUsCapture/Program.cs +++ b/AmongUsCapture/Program.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using System.IO; using System.IO.Pipes; using System.Net.Sockets; using System.Runtime.InteropServices; @@ -34,6 +35,11 @@ private static void Main(string[] args) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Settings.PersistentSettings.debugConsole) AllocConsole(); // needs to be the first call in the program to prevent weird bugs + if (!Directory.Exists(Settings.StorageLocation)) + { + // Create Settings directory if it doesn't exist, as we need to stick our pidfile there. + Directory.CreateDirectory(Settings.StorageLocation); + } URIStartResult uriRes = URIStartResult.CLOSE; uriRes = IPCadapter.getInstance().HandleURIStart(args); @@ -87,17 +93,17 @@ private static void OpenGUI() window.DeleteEvent += (object o, DeleteEventArgs e) => { + // Make sure the pid file is deleted if the application quits. + CleanPid(); Application.Quit(); }; - // Post a quick message to the console if we are using Linux, notifying the user that IPC links do not work. - - Settings.conInterface.WriteModuleTextColored("Notification", Color.Red, - $"You are running amonguscapture under Linux. Discord capture links are not currently supported. Use the manual details in your DM instead."); - window.ShowAll(); Application.Run(); + + CleanPid(); + Environment.Exit(0); } @@ -106,11 +112,37 @@ public static string GetExecutablePath() return Process.GetCurrentProcess().MainModule.FileName; } + private static void CleanPid() + { + // Make sure the pidfile is cleaned up if we have one. + var pidfile = Path.Join(Settings.StorageLocation, ".amonguscapture.pid"); + + if (File.Exists(pidfile)) + { + int pid; + bool fileread; + using (var pidread = File.OpenText(Path.Join(Settings.StorageLocation, ".amonguscapture.pid"))) + { + fileread = Int32.TryParse(pidread.ReadLine(), out pid); + } + + if (!fileread) + { + // Bad read, file must be corrupt. Clear pidfile. + File.Delete(pidfile); + } + + if (pid == Process.GetCurrentProcess().Id) + { + // This is our process. Delete file. + File.Delete(pidfile); + } + } + } [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool AllocConsole(); - } } \ No newline at end of file diff --git a/AmongUsCapture/Properties/launchSettings.json b/AmongUsCapture/Properties/launchSettings.json index 556eeb71..e8056cc8 100644 --- a/AmongUsCapture/Properties/launchSettings.json +++ b/AmongUsCapture/Properties/launchSettings.json @@ -5,7 +5,7 @@ }, "AmongUsCaptureIPC": { "commandName": "Project", - "commandLineArgs": "aucapture://localhost:8123/3A9CD4A1?insecure" + "commandLineArgs": "aucapture://localhost:8123/E68A0AD9?insecure" } } } \ No newline at end of file diff --git a/AmongUsCapture/Settings.cs b/AmongUsCapture/Settings.cs index f2d49ae9..593fbe6f 100644 --- a/AmongUsCapture/Settings.cs +++ b/AmongUsCapture/Settings.cs @@ -7,7 +7,7 @@ namespace AmongUsCapture { public static class Settings { - public static string StorageLocation = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "\\AmongUsCapture"); + public static string StorageLocation = Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AmongUsCapture"); public static ConsoleInterface conInterface; diff --git a/AmongUsCapture/UserForm.cs b/AmongUsCapture/UserForm.cs index 0c00214f..5ff26da7 100644 --- a/AmongUsCapture/UserForm.cs +++ b/AmongUsCapture/UserForm.cs @@ -66,9 +66,7 @@ public UserForm(ClientSocket sock) : base("Among Us Capture - GTK") GameMemReader.getInstance().ChatMessageAdded += OnChatMessageAdded; GameMemReader.getInstance().JoinedLobby += OnJoinedLobby; GameMemReader.getInstance().GameUnverified += _eventGameIsPirated; - - - + // Load URL _urlHostEntryField.Text = Settings.PersistentSettings.host;