如何从Linux上运行的.NET Core应用程序关闭计算机

时间:2018-05-02 18:28:07

标签: c# linux .net-core ubuntu-16.04 daemon

我在Linux上运行.net核心2.0程序(Ubuntu Server 16.04 LTS)。

我试图通过使用以下命令调用进程来关闭计算机:sudo shutdown -h now,但是当程序在后台作为守护程序服务运行时,关闭进程不起作用。

以下是代码:

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");
}

我的假设是该服务作为一个单独的会话运行,因此它没有任何控制权。如何在应用程序作为Linux守护程序运行时让应用程序关闭计算机?

1 个答案:

答案 0 :(得分:1)

我建议您更改代码以使用P / Invoke直接调用Linux的reboot函数,如果失败则会提供更多详细信息。

虽然调用其他可执行文件来执行任务是Unix / Linux上的惯例(特别是来自shell脚本),.NET程序实际上并不适合,并且所需的代码非常脆弱(例如,当您看到{时{1}}),特别是在.NET世界中处理来自其他进程的标准IO(sudostdinstdout)非常困难。

stderr

用法:

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         = 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       = 0xCDEF0123;
    public const Int32 LINUX_REBOOT_CMD_CAD_ON     = 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   = 0xA1B2C3D4;
    public const Int32 LINUX_REBOOT_CMD_SW_SUSPEND = 0xD000FCE2;
    public const Int32 LINUX_REBOOT_CMD_KEXEC      = 0x45584543;

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

请注意,成功拨打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():" + err.ToString() ); } } 将永远不会返回。