Skip to content

Commit

Permalink
Add reboot syscall (#839)
Browse files Browse the repository at this point in the history
* Add reboot syscall

* Make reboot symbols work

* Implement reboot for linux_raw backend

* Move reboot syscall to system module

* Document reboot syscall

* Feature gate reboot on non linux systems

* Remove unnecessary cfg directive

* Properly feature gate reboot

* Mark RebootCommand as non_exhaustive

* Add test for reboot syscall

* Remove Restart2 reboot command
  • Loading branch information
morr0ne authored Sep 30, 2023
1 parent 707c51e commit 2bdf3ed
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 1 deletion.
7 changes: 7 additions & 0 deletions src/backend/libc/system/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use super::types::RawUname;
use crate::backend::c;
#[cfg(not(target_os = "wasi"))]
use crate::backend::conv::ret_infallible;
#[cfg(target_os = "linux")]
use crate::system::RebootCommand;
#[cfg(linux_kernel)]
use crate::system::Sysinfo;
use core::mem::MaybeUninit;
Expand Down Expand Up @@ -56,3 +58,8 @@ pub(crate) fn sethostname(name: &[u8]) -> io::Result<()> {
))
}
}

#[cfg(target_os = "linux")]
pub(crate) fn reboot(cmd: RebootCommand) -> io::Result<()> {
unsafe { ret(c::reboot(cmd as i32)) }
}
23 changes: 23 additions & 0 deletions src/backend/linux_raw/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,26 @@ pub(crate) const CLOCK_THREAD_CPUTIME_ID: c_int =
linux_raw_sys::general::CLOCK_THREAD_CPUTIME_ID as _;
pub(crate) const CLOCK_PROCESS_CPUTIME_ID: c_int =
linux_raw_sys::general::CLOCK_PROCESS_CPUTIME_ID as _;

#[allow(overflowing_literals)]
#[allow(dead_code)]
mod reboot_symbols {
use linux_raw_sys::ctypes::*;

pub(crate) const LINUX_REBOOT_MAGIC1: c_int = 0xfee1dead;
pub(crate) const LINUX_REBOOT_MAGIC2: c_int = 672274793;
pub(crate) const LINUX_REBOOT_MAGIC2A: c_int = 85072278;
pub(crate) const LINUX_REBOOT_MAGIC2B: c_int = 369367448;
pub(crate) const LINUX_REBOOT_MAGIC2C: c_int = 537993216;

pub(crate) const LINUX_REBOOT_CMD_RESTART: c_int = 0x01234567;
pub(crate) const LINUX_REBOOT_CMD_HALT: c_int = 0xCDEF0123;
pub(crate) const LINUX_REBOOT_CMD_CAD_ON: c_int = 0x89ABCDEF;
pub(crate) const LINUX_REBOOT_CMD_CAD_OFF: c_int = 0x00000000;
pub(crate) const LINUX_REBOOT_CMD_POWER_OFF: c_int = 0x4321FEDC;
pub(crate) const LINUX_REBOOT_CMD_RESTART2: c_int = 0xA1B2C3D4;
pub(crate) const LINUX_REBOOT_CMD_SW_SUSPEND: c_int = 0xD000FCE2;
pub(crate) const LINUX_REBOOT_CMD_KEXEC: c_int = 0x45584543;
}

pub(crate) use reboot_symbols::*;
18 changes: 17 additions & 1 deletion src/backend/linux_raw/system/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)]

use super::types::RawUname;
use crate::backend::conv::{ret, ret_infallible, slice};
use crate::backend::{
c,
conv::{c_int, ret, ret_infallible, slice},
};
use crate::io;
use crate::system::RebootCommand;
use crate::system::Sysinfo;
use core::mem::MaybeUninit;

Expand All @@ -34,3 +38,15 @@ pub(crate) fn sethostname(name: &[u8]) -> io::Result<()> {
let (ptr, len) = slice(name);
unsafe { ret(syscall_readonly!(__NR_sethostname, ptr, len)) }
}

#[inline]
pub(crate) fn reboot(cmd: RebootCommand) -> io::Result<()> {
unsafe {
ret(syscall_readonly!(
__NR_reboot,
c_int(c::LINUX_REBOOT_MAGIC1),
c_int(c::LINUX_REBOOT_MAGIC2),
c_int(cmd as i32)
))
}
}
58 changes: 58 additions & 0 deletions src/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#![allow(unsafe_code)]

use crate::backend;
#[cfg(target_os = "linux")]
use crate::backend::c;
use crate::ffi::CStr;
#[cfg(not(any(target_os = "espidf", target_os = "emscripten")))]
use crate::io;
Expand Down Expand Up @@ -154,3 +156,59 @@ pub fn sysinfo() -> Sysinfo {
pub fn sethostname(name: &[u8]) -> io::Result<()> {
backend::system::syscalls::sethostname(name)
}

/// Reboot command to be used with [`reboot`].
///
/// See [`reboot`] documentation for more info
///
/// [`reboot`]: crate::system::reboot
#[cfg(target_os = "linux")]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(i32)]
#[non_exhaustive]
pub enum RebootCommand {
/// CAD is disabled.
/// This means that the CAD keystroke will cause a SIGINT signal to be sent to init (process 1),
/// whereupon this process may decide upon a proper action (maybe: kill all processes, sync, reboot).
/// Disables the Ctrl-Alt-Del keystroke.
///
/// When disabled, the keystroke will send a SIGINT signal to pid 1
CadOff = c::LINUX_REBOOT_CMD_CAD_OFF,
/// Enables the Ctrl-Alt-Del keystroke.
///
/// When enabled, the keystroke will trigger LINUX_REBOOT_CMD_RESTART
CadOn = c::LINUX_REBOOT_CMD_CAD_ON,
/// Prints the message "System halted" and halts the system
Halt = c::LINUX_REBOOT_CMD_HALT,
/// Execute a kernel that has been loaded earlier with [`kexec_load`].
///
/// [`kexec_load`]: https://man7.org/linux/man-pages/man2/kexec_load.2.html
Kexec = c::LINUX_REBOOT_CMD_KEXEC,
/// Prints the message "Power down.", stops the system and tries to remove all power
PowerOff = c::LINUX_REBOOT_CMD_POWER_OFF,
/// Prints the message "Restarting system." and triggers a restart
Restart = c::LINUX_REBOOT_CMD_RESTART,
/// Hibernate the system by suspending to disk
SwSuspend = c::LINUX_REBOOT_CMD_SW_SUSPEND,
}

/// `reboot`—Reboot or enable/disable Ctrl-Alt-Del
///
/// The reboot syscall, despite the name, can actually do much more than reboot.
///
/// Among other things it can
/// - Restart, Halt, Power Off and Suspend the system
/// - Enable and disable the Ctrl-Alt-Del keystroke
/// - Execute other kernels
/// - Terminate init inside PID namespaces
///
/// It is highly reccomended to carefully read the kernel documentation before calling this function.
///
/// # References
/// - [Linux]
///
/// [Linux]: https://man7.org/linux/man-pages/man2/reboot.2.html
#[cfg(target_os = "linux")]
pub fn reboot(cmd: RebootCommand) -> io::Result<()> {
backend::system::syscalls::reboot(cmd)
}
2 changes: 2 additions & 0 deletions tests/system/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#![cfg(feature = "system")]
#![cfg(not(any(windows, target_os = "wasi")))]

#[cfg(target_os = "linux")]
mod reboot;
#[cfg(linux_kernel)]
mod sysinfo;
mod uname;
18 changes: 18 additions & 0 deletions tests/system/reboot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#[test]
#[cfg(feature = "thread")]
fn test_reboot() {
use rustix::{
io::Errno,
system::{self, RebootCommand},
thread::{self, CapabilityFlags},
};

let mut capabilities = thread::capabilities(None).expect("Failed to get capabilities");

capabilities.effective.set(CapabilityFlags::SYS_BOOT, false);

thread::set_capabilities(None, capabilities).expect("Failed to set capabilities");

// The reboot syscall requires the CAP_SYS_BOOT permission to be called, otherwise EPERM is returned
assert_eq!(system::reboot(RebootCommand::Restart), Err(Errno::PERM));
}

0 comments on commit 2bdf3ed

Please sign in to comment.