StdErrAdapter.cs 3.2 KB

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