C# 如何从运行在Linux上的.NET核心应用程序关闭计算机
我有一个运行在Linux上的.NETCore2.0程序(Ubuntu服务器16.04LTS) 我试图通过以下命令调用进程来关闭计算机:C# 如何从运行在Linux上的.NET核心应用程序关闭计算机,c#,linux,.net-core,ubuntu-16.04,daemon,C#,Linux,.net Core,Ubuntu 16.04,Daemon,我有一个运行在Linux上的.NETCore2.0程序(Ubuntu服务器16.04LTS) 我试图通过以下命令调用进程来关闭计算机:sudo shutdown-h now,但当程序作为守护进程服务在后台运行时,关闭进程不起作用 代码如下: var process = new Process { StartInfo = { CreateNoWindow = true, RedirectStandardError = true, Re
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守护进程运行时,如何让它关闭计算机?我建议将您的代码更改为使用p/Invoke直接调用Linux的
重新启动
功能,如果失败,这也将为您提供更多详细信息
虽然调用其他可执行文件执行任务是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;
}
用法:
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() );
}
}
请注意,对
reboot()的成功调用
将永远不会返回。当作为执事运行时,您的进程可能没有调用sudo
-或sudo
的权限,但它会立即提示输入密码,因此它处于空闲状态。感谢您的回复。我尝试以这种方式实现它,但在重新启动()时得到了错误-1。你知道原因是什么吗?@IX92我很抱歉。我误读了文档。我已经更新了我的答案。如果它返回-1
,那么你可以通过errno
(Marshal.GetLastWin32Error()
-即使在Linux上也是如此)。错误号不是返回值。似乎有几个值可能需要标记为unchecked
,因为它们不能由C#中的有符号整数保存。例如,public const int LINUX_REBOOT\u CMD_CAD_ON=unchecked((int)0x89ABCDEF)
@Dai got alwaysEINVAL
(错误的幻数)作为错误代码。在ARM处理器上使用libc.so.6。我得到了与@MaxR相同的结果。使用strace,我可以看到一些奇怪的情况:reboot(LINUX\u reboot\u MAGIC1,LINUX\u reboot\u MAGIC2,0xfee1dead/*LINUX\u reboot\u CMD\uuu???*/)=-1 EINVAL(无效参数)
-此调用方法似乎不需要前两个幻数。将DllImport更改为公共静态外部Int32重新启动(Int32 cmd,IntPtr arg);
并通过重新启动进行调用(LINUX_reboot_cmd_POWER_OFF,IntPtr.Zero);
工作正常。