Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Create Detached Process on Linux

Tags:

c#

.net

linux

I am writing a software update process on Linux. Application is .NET 5 RC1 (Sept 15 2020 release). When a certain packet is received by my application, it downloads the software update to a sub-folder then spawns off the executable to perform the software update.

Unfortunately, using Process.Start and ProcessStartInfo seems to create a process that is attached to the main process. Since the software update must stop the process in order to update it, it also gets stopped because it is a child of the process, having been spawned via Process.Start.

How do I create a detached process on Linux? On Windows I am using PInvoke and the CreateProcess API with the DETACHED_PROCESS flag, see the following:

var processInformation = new ProcessUtility.PROCESS_INFORMATION();
var startupInfo = new ProcessUtility.STARTUPINFO();
var sa = new ProcessUtility.SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
CreateProcess(null, "\"" + fileName + "\" " + arguments, ref sa, ref sa, false, DETACHED_PROCESS, IntPtr.Zero, Path.GetDirectoryName(fileName), ref startupInfo, out processInformation);

Here is my code for Linux. I had read that appending & to a process on Linux creates it detached, but that does not appear to be the case.

ProcessStartInfo info = new ProcessStartInfo
{
    // Linux uses " &" to detach the process
    Arguments = arguments + " &",
    CreateNoWindow = true,
    FileName = fileName,
    UseShellExecute = false,
    WindowStyle = ProcessWindowStyle.Hidden,
    WorkingDirectory = Path.GetDirectoryName(fileName)
};

Process.Start(info);
like image 946
jjxtra Avatar asked Jun 17 '26 01:06

jjxtra


2 Answers

I was unable to get nohup or disown to work from C#. Killing the parent process always resulted in the child process being terminated as well.

I ended up using at, which can be installed via sudo apt install at. The atd service is installed and will stay running even when rebooted.

Here is the C# code that I used:

// the following assumes `sudo apt install at` has been run.
string fileName = "[your process to execute]";
string arguments = "[your command line arguments for fileName]";
string argumentsEscaped = arguments.Replace("\"", "\\\"");
string fullArgs = $"-c \"echo sudo \\\"{fileName}\\\" {argumentsEscaped} | at now\"";
ProcessStartInfo info = new()
{
    Arguments = fullArgs,
    CreateNoWindow = true,
    FileName = "/bin/bash",
    UseShellExecute = false,
    WindowStyle = ProcessWindowStyle.Hidden,
    WorkingDirectory = Path.GetDirectoryName(fileName)
};

using var process = Process.Start(info);
process.WaitForExit();
// make sure to check process.ExitCode == 0
like image 122
jjxtra Avatar answered Jun 18 '26 16:06

jjxtra


Slightly refactored version of @jjxtra solution, so it's easier to understand what's going on in the arguments. Btw, the echo is not an example, but the way of executing at command.

string command = $"actual command to run";
string atdCommand = $@"echo \""{command}\"" | at now";
string bashCommand = $@"-c ""{atdCommand}"" ";

ProcessStartInfo psi = new ProcessStartInfo
{
    FileName = "/bin/bash",
    Arguments = bashCommand,
    ...
};
like image 23
anth Avatar answered Jun 18 '26 15:06

anth



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!