Как выключить компьютер из приложения .NET Core, работающего в Linux - PullRequest
0 голосов
/ 02 мая 2018

У меня есть программа .net core 2.0, работающая в Linux (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 голосов
/ 03 мая 2018

Я рекомендую изменить код для использования P / Invoke для прямого вызова функции Linux reboot, это также даст вам больше подробностей в случае сбоя.

В то время как вызов других исполняемых файлов для выполнения задач является соглашением для Unix / Linux (особенно из сценариев оболочки), программы .NET на самом деле плохо вписываются, и требуемый код очень хрупок (например, как вы видите с помощью *). 1004 *), тем более что в мире .NET обработка стандартных операций ввода-вывода (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() никогда не вернется.

...