diff options
Diffstat (limited to 'Ryujinx.Common/SystemInterop')
-rw-r--r-- | Ryujinx.Common/SystemInterop/StdErrAdapter.cs | 93 | ||||
-rw-r--r-- | Ryujinx.Common/SystemInterop/UnixStream.cs | 155 |
2 files changed, 248 insertions, 0 deletions
diff --git a/Ryujinx.Common/SystemInterop/StdErrAdapter.cs b/Ryujinx.Common/SystemInterop/StdErrAdapter.cs new file mode 100644 index 00000000..12e240ad --- /dev/null +++ b/Ryujinx.Common/SystemInterop/StdErrAdapter.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; +using System.Runtime.Versioning; +using System.Threading; +using Ryujinx.Common.Logging; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common.SystemInterop +{ + public partial class StdErrAdapter : IDisposable + { + private bool _disposable = false; + private UnixStream _pipeReader; + private UnixStream _pipeWriter; + private Thread _worker; + + public StdErrAdapter() + { + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + { + RegisterPosix(); + } + } + + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("macos")] + private void RegisterPosix() + { + const int stdErrFileno = 2; + + (int readFd, int writeFd) = MakePipe(); + dup2(writeFd, stdErrFileno); + + _pipeReader = new UnixStream(readFd); + _pipeWriter = new UnixStream(writeFd); + + _worker = new Thread(EventWorker); + _disposable = true; + _worker.Start(); + } + + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("macos")] + private void EventWorker() + { + TextReader reader = new StreamReader(_pipeReader); + string line; + while ((line = reader.ReadLine()) != null) + { + Logger.Error?.PrintRawMsg(line); + } + } + + private void Dispose(bool disposing) + { + if (_disposable) + { + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + { + _pipeReader?.Close(); + _pipeWriter?.Close(); + } + + _disposable = false; + } + } + + public void Dispose() + { + Dispose(true); + } + + [LibraryImport("libc", SetLastError = true)] + private static partial int dup2(int fd, int fd2); + + [LibraryImport("libc", SetLastError = true)] + private static unsafe partial int pipe(int* pipefd); + + private static unsafe (int, int) MakePipe() + { + int *pipefd = stackalloc int[2]; + + if (pipe(pipefd) == 0) + { + return (pipefd[0], pipefd[1]); + } + else + { + throw new(); + } + } + } +} diff --git a/Ryujinx.Common/SystemInterop/UnixStream.cs b/Ryujinx.Common/SystemInterop/UnixStream.cs new file mode 100644 index 00000000..1d644997 --- /dev/null +++ b/Ryujinx.Common/SystemInterop/UnixStream.cs @@ -0,0 +1,155 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +namespace Ryujinx.Common.SystemInterop +{ + [SupportedOSPlatform("linux")] + [SupportedOSPlatform("macos")] + public partial class UnixStream : Stream, IDisposable + { + private const int InvalidFd = -1; + + private int _fd; + + [LibraryImport("libc", SetLastError = true)] + private static partial long read(int fd, IntPtr buf, ulong count); + + [LibraryImport("libc", SetLastError = true)] + private static partial long write(int fd, IntPtr buf, ulong count); + + [LibraryImport("libc", SetLastError = true)] + private static partial int close(int fd); + + public UnixStream(int fd) + { + if (InvalidFd == fd) + { + throw new ArgumentException("Invalid file descriptor"); + } + + _fd = fd; + + CanRead = read(fd, IntPtr.Zero, 0) != -1; + CanWrite = write(fd, IntPtr.Zero, 0) != -1; + } + + ~UnixStream() + { + Close(); + } + + public override bool CanRead { get; } + public override bool CanWrite { get; } + public override bool CanSeek => false; + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override void Flush() + { + } + + public override unsafe int Read([In, Out] byte[] buffer, int offset, int count) + { + if (offset < 0 || offset > (buffer.Length - count) || count < 0) + { + throw new ArgumentOutOfRangeException(); + } + + if (buffer.Length == 0) + { + return 0; + } + + long r = 0; + fixed (byte* buf = &buffer[offset]) + { + do + { + r = read(_fd, (IntPtr)buf, (ulong)count); + } while (ShouldRetry(r)); + } + + return (int)r; + } + + public override unsafe void Write(byte[] buffer, int offset, int count) + { + if (offset < 0 || offset > (buffer.Length - count) || count < 0) + { + throw new ArgumentOutOfRangeException(); + } + + if (buffer.Length == 0) + { + return; + } + + fixed (byte* buf = &buffer[offset]) + { + long r = 0; + do { + r = write(_fd, (IntPtr)buf, (ulong)count); + } while (ShouldRetry(r)); + } + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Close() + { + if (_fd == InvalidFd) + { + return; + } + + Flush(); + + int r; + do { + r = close(_fd); + } while (ShouldRetry(r)); + + _fd = InvalidFd; + } + + void IDisposable.Dispose() + { + Close(); + } + + private bool ShouldRetry(long r) + { + if (r == -1) + { + const int eintr = 4; + + int errno = Marshal.GetLastPInvokeError(); + + if (errno == eintr) + { + return true; + } + + throw new SystemException($"Operation failed with error 0x{errno:X}"); + } + + return false; + } + } +} |