Fix passing arg and env arguments to the native helper in .NET6

This commit is contained in:
Greg Munn 2022-01-12 15:12:46 -05:00
Родитель 861e649149
Коммит 83186d0ad9
1 изменённых файлов: 65 добавлений и 2 удалений

Просмотреть файл

@ -1,6 +1,8 @@
using System;
using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics;
using System.Text;
namespace XtermSharp {
[StructLayout(LayoutKind.Sequential)]
@ -19,7 +21,7 @@ namespace XtermSharp {
extern static int execve (string process, string [] args, string [] env);
[DllImport ("libpty.dylib", EntryPoint="fork_and_exec")]
extern static int HeavyFork (string process, string [] args, string [] env, out int master, UnixWindowSize winSize);
extern static unsafe int HeavyFork (string process, byte** args, byte** env, out int master, UnixWindowSize winSize);
static bool HeavyDuty = true;
/// <summary>
@ -34,7 +36,7 @@ namespace XtermSharp {
public static int ForkAndExec (string programName, string [] args, string [] env, out int master, UnixWindowSize winSize)
{
if (HeavyDuty) {
return HeavyFork (programName, args, env, out master, winSize);
return DoHeavyFork (programName, args, env, out master, winSize);
} else {
var pid = forkpty (out master, IntPtr.Zero, IntPtr.Zero, ref winSize);
if (pid < 0)
@ -47,6 +49,67 @@ namespace XtermSharp {
}
}
static unsafe int DoHeavyFork (string programName, string [] args, string [] env, out int master, UnixWindowSize winSize)
{
byte** argvPtr = null, envpPtr = null;
int result = -1;
try {
AllocNullTerminatedArray (args, ref argvPtr);
AllocNullTerminatedArray (env, ref envpPtr);
result = HeavyFork (programName, argvPtr, envpPtr, out master, winSize);
return result == 0 ? 0 : Marshal.GetLastWin32Error ();
} finally {
FreeArray (argvPtr, args.Length);
FreeArray (envpPtr, env.Length);
}
}
private static unsafe void AllocNullTerminatedArray (string [] arr, ref byte** arrPtr)
{
int arrLength = arr.Length + 1; // +1 is for null termination
// Allocate the unmanaged array to hold each string pointer.
// It needs to have an extra element to null terminate the array.
arrPtr = (byte**)Marshal.AllocHGlobal (sizeof (IntPtr) * arrLength);
Debug.Assert (arrPtr != null);
// Zero the memory so that if any of the individual string allocations fails,
// we can loop through the array to free any that succeeded.
// The last element will remain null.
for (int i = 0; i < arrLength; i++) {
arrPtr [i] = null;
}
// Now copy each string to unmanaged memory referenced from the array.
// We need the data to be an unmanaged, null-terminated array of UTF8-encoded bytes.
for (int i = 0; i < arr.Length; i++) {
byte [] byteArr = Encoding.UTF8.GetBytes (arr [i]);
arrPtr [i] = (byte*)Marshal.AllocHGlobal (byteArr.Length + 1); //+1 for null termination
Debug.Assert (arrPtr [i] != null);
Marshal.Copy (byteArr, 0, (IntPtr)arrPtr [i], byteArr.Length); // copy over the data from the managed byte array
arrPtr [i] [byteArr.Length] = (byte)'\0'; // null terminate
}
}
private static unsafe void FreeArray (byte** arr, int length)
{
if (arr != null) {
// Free each element of the array
for (int i = 0; i < length; i++) {
if (arr [i] != null) {
Marshal.FreeHGlobal ((IntPtr)arr [i]);
arr [i] = null;
}
}
// And then the array itself
Marshal.FreeHGlobal ((IntPtr)arr);
}
}
[DllImport ("libc", SetLastError = true)]
extern static int ioctl (int fd, long cmd, ref UnixWindowSize WinSz);