StdErrAdapter.cs
1 using Microsoft.Win32.SafeHandles; 2 using Ryujinx.Common.Logging; 3 using System; 4 using System.IO; 5 using System.Runtime.InteropServices; 6 using System.Runtime.Versioning; 7 using System.Threading; 8 using System.Threading.Tasks; 9 10 namespace Ryujinx.Common.SystemInterop 11 { 12 public partial class StdErrAdapter : IDisposable 13 { 14 private bool _disposable; 15 private Stream _pipeReader; 16 private Stream _pipeWriter; 17 private CancellationTokenSource _cancellationTokenSource; 18 private Task _worker; 19 20 public StdErrAdapter() 21 { 22 if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) 23 { 24 RegisterPosix(); 25 } 26 } 27 28 [SupportedOSPlatform("linux")] 29 [SupportedOSPlatform("macos")] 30 private void RegisterPosix() 31 { 32 const int StdErrFileno = 2; 33 34 (int readFd, int writeFd) = MakePipe(); 35 dup2(writeFd, StdErrFileno); 36 37 _pipeReader = CreateFileDescriptorStream(readFd); 38 _pipeWriter = CreateFileDescriptorStream(writeFd); 39 40 _cancellationTokenSource = new CancellationTokenSource(); 41 _worker = Task.Run(async () => await EventWorkerAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token); 42 _disposable = true; 43 } 44 45 [SupportedOSPlatform("linux")] 46 [SupportedOSPlatform("macos")] 47 private async Task EventWorkerAsync(CancellationToken cancellationToken) 48 { 49 using TextReader reader = new StreamReader(_pipeReader, leaveOpen: true); 50 string line; 51 while (cancellationToken.IsCancellationRequested == false && (line = await reader.ReadLineAsync(cancellationToken)) != null) 52 { 53 Logger.Error?.PrintRawMsg(line); 54 } 55 } 56 57 public void Dispose() 58 { 59 GC.SuppressFinalize(this); 60 61 if (_disposable) 62 { 63 _disposable = false; 64 65 if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) 66 { 67 _cancellationTokenSource.Cancel(); 68 _worker.Wait(0); 69 _pipeReader?.Close(); 70 _pipeWriter?.Close(); 71 } 72 } 73 } 74 75 [LibraryImport("libc", SetLastError = true)] 76 private static partial int dup2(int fd, int fd2); 77 78 [LibraryImport("libc", SetLastError = true)] 79 private static partial int pipe(Span<int> pipefd); 80 81 private static (int, int) MakePipe() 82 { 83 Span<int> pipefd = stackalloc int[2]; 84 85 if (pipe(pipefd) == 0) 86 { 87 return (pipefd[0], pipefd[1]); 88 } 89 90 throw new(); 91 } 92 93 [SupportedOSPlatform("linux")] 94 [SupportedOSPlatform("macos")] 95 private static Stream CreateFileDescriptorStream(int fd) 96 { 97 return new FileStream( 98 new SafeFileHandle(fd, ownsHandle: true), 99 FileAccess.ReadWrite 100 ); 101 } 102 103 } 104 }