Skip to content

Instantly share code, notes, and snippets.

@pchalamet
Last active December 23, 2024 19:38
Show Gist options
  • Save pchalamet/98b2eddece97f1f4d5799afbd0e0da2a to your computer and use it in GitHub Desktop.
Save pchalamet/98b2eddece97f1f4d5799afbd0e0da2a to your computer and use it in GitHub Desktop.
Attempt to start a child process in parent group so it's automatically killed if parent die
using System.Runtime.InteropServices;
namespace ProcessGroup;
//
// Attempt to start a child process and enroll it in the same process group as the parent.
// Goal is to be able to kill the child process if the parent process is killed.
//
public static class Process
{
[DllImport("libc")]
private static extern int getpid();
[DllImport("libc")]
private static extern int getsid(int pid);
[DllImport("libc")]
private static extern int setpgid(int pid, int pgid);
[DllImport("libc")]
private static extern int getpgid(int pid);
[DllImport("libc")]
private static extern int posix_spawn(
out int pid,
string path,
IntPtr fileActions,
ref IntPtr attrp,
string?[] argv,
string?[] envp);
// P/Invoke declaration for posix_spawnattr_init
[DllImport("libc")]
private static extern int posix_spawnattr_init(ref IntPtr attrp);
[DllImport("libc")]
private static extern int posix_spawnattr_destroy(ref IntPtr attrp);
[DllImport("libc")]
private static extern int posix_spawnattr_setpgroup(ref IntPtr attrp, int pgroup);
[DllImport("libc")]
private static extern IntPtr strerror(int errnum);
private static void CheckError(string fun, int error)
{
if (error != 0)
{
IntPtr messagePtr = strerror(error);
var errstring = Marshal.PtrToStringAnsi(messagePtr) ?? "Unknown error";
throw new Exception($"{fun} failed with error {error} ({errstring})");
}
}
public static int Start(string process, params string[] args)
{
Console.WriteLine($"Current pid is {getpid()}");
Console.WriteLine($"Current session is {getsid(0)}");
Console.WriteLine($"Current progress group is {getpgid(0)}");
int err;
IntPtr attrp = IntPtr.Zero;
try
{
err = posix_spawnattr_init(ref attrp);
CheckError("posix_spawnattr_init", err);
err = posix_spawnattr_setpgroup(ref attrp, 0);
CheckError("posix_spawnattr_setpgroup", err);
// Define arguments for the child process
string path = process;
string?[] argv = [..args, null];
string?[] envp = [null]; // Use parent's environment
// Spawn the child process
err = posix_spawn(out int childPid, path, IntPtr.Zero, ref attrp, argv, envp);
CheckError("posix_spawn", err);
Console.WriteLine($"Child process started with PID: {childPid}");
return childPid;
}
catch {
posix_spawnattr_destroy(ref attrp);
throw;
}
// NOTE: also does not work (error 13: Permission denied)
// // Set the child process to the same process group as the parent
// err = setpgid(childProcess.Id, 0)
// if (err != 0)
// {
// Console.WriteLine($"Failed to set process group: {err}");
// childProcess.Kill();
// return null;
// }
// return childProcess;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment