How to shutdown computer from a .NET Core application running on Linux

杀马特。学长 韩版系。学妹 提交于 2021-02-08 04:01:34

问题


I have a .net core 2.0 program running on Linux (Ubuntu Server 16.04 LTS).

I'm trying to shutdown the computer by invoking a process with the following command: sudo shutdown -h now, though when the program is running in the background as daemon service, the shutdown process does not work.

Here is the code:

var process = new Process
{
    StartInfo =
    {
        CreateNoWindow = true,
        RedirectStandardError = true,
        RedirectStandardInput = true,
        RedirectStandardOutput = true,
        UseShellExecute = false,
        FileName = Environment.GetEnvironmentVariable("SHELL"),
        Arguments = "-s"
    },
    EnableRaisingEvents = true
};

if (process.Start())
{
    process.BeginErrorReadLine();
    process.BeginOutputReadLine();
    process.StandardInput.WriteLine("sudo shutdown -h now");
}

My assumption is that the service runs as a separate session so it doesn't have any control. How can I get the application to shutdown the computer when it is running as a Linux daemon?


回答1:


I recommend changing your code to use P/Invoke to call Linux's reboot function directly, this will also give you more details if it fails.

While invoking other executables to perform tasks is the convention on Unix/Linux (especially from shell scripts), .NET programs really don't fit in well and the code required is very brittle (e.g. as you're seeing with sudo), especially as in the .NET world processing Standard IO (stdin, stdout, stderr) from other processes is very difficult.

internal static class NativeMethods
{
    [DllImport( "libc.so", SetLastError = true)] // You may need to change this to "libc.so.6" or "libc.so.7" depending on your platform)
    public static extern Int32 reboot(Int32 magic, Int32 magic2, Int32 cmd, IntPtr arg);

    public const Int32 LINUX_REBOOT_MAGIC1 = unchecked((int)0xfee1dead);
    public const Int32 LINUX_REBOOT_MAGIC2 = 672274793;
    public const Int32 LINUX_REBOOT_MAGIC2A = 85072278;
    public const Int32 LINUX_REBOOT_MAGIC2B = 369367448;
    public const Int32 LINUX_REBOOT_MAGIC2C = 537993216;


    public const Int32 LINUX_REBOOT_CMD_RESTART = 0x01234567;
    public const Int32 LINUX_REBOOT_CMD_HALT = unchecked((int)0xCDEF0123);
    public const Int32 LINUX_REBOOT_CMD_CAD_ON = unchecked((int)0x89ABCDEF);
    public const Int32 LINUX_REBOOT_CMD_CAD_OFF = 0x00000000;
    public const Int32 LINUX_REBOOT_CMD_POWER_OFF = 0x4321FEDC;
    public const Int32 LINUX_REBOOT_CMD_RESTART2 = unchecked((int)0xA1B2C3D4);
    public const Int32 LINUX_REBOOT_CMD_SW_SUSPEND = unchecked((int)0xD000FCE2);
    public const Int32 LINUX_REBOOT_CMD_KEXEC = 0x45584543;

    public const Int32 EPERM  =  1;
    public const Int32 EFAULT = 14;
    public const Int32 EINVAL = 22;
}

Usage:

using static NativeMethods;

public static void Shutdown()
{
    Int32 ret = reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, IntPtr.Zero );

    // `reboot(LINUX_REBOOT_CMD_POWER_OFF)` never returns if it's successful, so if it returns 0 then that's weird, we should treat it as an error condition instead of success:
    if( ret == 0 ) throw new InvalidOperationException( "reboot(LINUX_REBOOT_CMD_POWER_OFF) returned 0.");

    // ..otherwise we expect it to return -1 in the event of failure, so any other value is exceptional:
    if( ret != -1 ) throw new InvalidOperationException( "Unexpected reboot() return value: " + ret );

    // At this point, ret == -1, which means check `errno`!
    // `errno` is accessed via Marshal.GetLastWin32Error(), even on non-Win32 platforms and especially even on Linux

    Int32 errno = Marshal.GetLastWin32Error();
    switch( errno )
    {
    case EPERM:
        throw new UnauthorizedAccessException( "You do not have permission to call reboot()" );

    case EINVAL:
        throw new ArgumentException( "Bad magic numbers (stray cosmic-ray?)" );

    case EFAULT:
    default:
        throw new InvalidOperationException( "Could not call reboot():" + errno.ToString() );
    }
}

Note that a successful call to reboot() will never return.



来源:https://stackoverflow.com/questions/50141260/how-to-shutdown-computer-from-a-net-core-application-running-on-linux

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!