[英]How to shutdown computer from a .NET Core application running on Linux
I have a .net core 2.0 program running on Linux (Ubuntu Server 16.04 LTS).我有一个运行在 Linux (Ubuntu Server 16.04 LTS) 上的 .net 核心 2.0 程序。
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.我试图通过使用以下命令调用进程来关闭计算机: sudo shutdown -h now
,但是当程序作为守护进程服务在后台运行时,关闭进程不起作用。
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.我的假设是该服务作为单独的 session 运行,因此它没有任何控制权。 How can I get the application to shutdown the computer when it is running as a Linux daemon?当应用程序作为 Linux 守护程序运行时,如何让应用程序关闭计算机?
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.我建议更改您的代码以使用 P/Invoke 直接调用 Linux 的reboot
功能,如果失败,这也会为您提供更多详细信息。
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 (eg as you're seeing with sudo
), especially as in the .NET world processing Standard IO ( stdin
, stdout
, stderr
) from other processes is very difficult.虽然调用其他可执行文件来执行任务是 Unix/Linux 上的约定(尤其是从 shell 脚本),但 .NET 程序确实不适合,并且所需的代码非常脆弱(例如,正如您在sudo
看到的那样),尤其是在 .NET 世界中,处理来自其他进程的标准 IO( stdin
、 stdout
、 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 = 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.请注意,成功调用reboot()
将永远不会返回。
Adding that, for .net core running on the Raspberry Pi, we need to use the other answer, but follow user Tom's comment and change the DllImport to:补充一点,对于在 Raspberry Pi 上运行的 .net 核心,我们需要使用其他答案,但按照用户 Tom 的评论并将 DllImport 更改为:
[DllImport( "libc.so.6", SetLastError = true)]
public static extern Int32 reboot(Int32 cmd, IntPtr arg);
And then to power off we can call:然后关闭电源,我们可以调用:
reboot(LINUX_REBOOT_CMD_POWER_OFF, IntPtr.Zero);
Or to reboot:或者重新启动:
reboot(LINUX_REBOOT_CMD_RESTART, IntPtr.Zero);
This works on AWS EC2 with Ubuntu Server, used in daemon.这适用于带有 Ubuntu 服务器的 AWS EC2,用于守护进程。 Tested only there, may work also elsewhere.只在那里测试过,也可能在其他地方工作。
Process process = new Process();
process.StartInfo.FileName = "/usr/bin/sudo";
process.StartInfo.Arguments = "/sbin/shutdown -h now";
process.Start();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.