Skip to content

Commit 2bdf3ed

Browse files
authored
Add reboot syscall (#839)
* 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
1 parent 707c51e commit 2bdf3ed

6 files changed

Lines changed: 125 additions & 1 deletion

File tree

src/backend/libc/system/syscalls.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use super::types::RawUname;
44
use crate::backend::c;
55
#[cfg(not(target_os = "wasi"))]
66
use crate::backend::conv::ret_infallible;
7+
#[cfg(target_os = "linux")]
8+
use crate::system::RebootCommand;
79
#[cfg(linux_kernel)]
810
use crate::system::Sysinfo;
911
use core::mem::MaybeUninit;
@@ -56,3 +58,8 @@ pub(crate) fn sethostname(name: &[u8]) -> io::Result<()> {
5658
))
5759
}
5860
}
61+
62+
#[cfg(target_os = "linux")]
63+
pub(crate) fn reboot(cmd: RebootCommand) -> io::Result<()> {
64+
unsafe { ret(c::reboot(cmd as i32)) }
65+
}

src/backend/linux_raw/c.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,3 +270,26 @@ pub(crate) const CLOCK_THREAD_CPUTIME_ID: c_int =
270270
linux_raw_sys::general::CLOCK_THREAD_CPUTIME_ID as _;
271271
pub(crate) const CLOCK_PROCESS_CPUTIME_ID: c_int =
272272
linux_raw_sys::general::CLOCK_PROCESS_CPUTIME_ID as _;
273+
274+
#[allow(overflowing_literals)]
275+
#[allow(dead_code)]
276+
mod reboot_symbols {
277+
use linux_raw_sys::ctypes::*;
278+
279+
pub(crate) const LINUX_REBOOT_MAGIC1: c_int = 0xfee1dead;
280+
pub(crate) const LINUX_REBOOT_MAGIC2: c_int = 672274793;
281+
pub(crate) const LINUX_REBOOT_MAGIC2A: c_int = 85072278;
282+
pub(crate) const LINUX_REBOOT_MAGIC2B: c_int = 369367448;
283+
pub(crate) const LINUX_REBOOT_MAGIC2C: c_int = 537993216;
284+
285+
pub(crate) const LINUX_REBOOT_CMD_RESTART: c_int = 0x01234567;
286+
pub(crate) const LINUX_REBOOT_CMD_HALT: c_int = 0xCDEF0123;
287+
pub(crate) const LINUX_REBOOT_CMD_CAD_ON: c_int = 0x89ABCDEF;
288+
pub(crate) const LINUX_REBOOT_CMD_CAD_OFF: c_int = 0x00000000;
289+
pub(crate) const LINUX_REBOOT_CMD_POWER_OFF: c_int = 0x4321FEDC;
290+
pub(crate) const LINUX_REBOOT_CMD_RESTART2: c_int = 0xA1B2C3D4;
291+
pub(crate) const LINUX_REBOOT_CMD_SW_SUSPEND: c_int = 0xD000FCE2;
292+
pub(crate) const LINUX_REBOOT_CMD_KEXEC: c_int = 0x45584543;
293+
}
294+
295+
pub(crate) use reboot_symbols::*;

src/backend/linux_raw/system/syscalls.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66
#![allow(unsafe_code, clippy::undocumented_unsafe_blocks)]
77

88
use super::types::RawUname;
9-
use crate::backend::conv::{ret, ret_infallible, slice};
9+
use crate::backend::{
10+
c,
11+
conv::{c_int, ret, ret_infallible, slice},
12+
};
1013
use crate::io;
14+
use crate::system::RebootCommand;
1115
use crate::system::Sysinfo;
1216
use core::mem::MaybeUninit;
1317

@@ -34,3 +38,15 @@ pub(crate) fn sethostname(name: &[u8]) -> io::Result<()> {
3438
let (ptr, len) = slice(name);
3539
unsafe { ret(syscall_readonly!(__NR_sethostname, ptr, len)) }
3640
}
41+
42+
#[inline]
43+
pub(crate) fn reboot(cmd: RebootCommand) -> io::Result<()> {
44+
unsafe {
45+
ret(syscall_readonly!(
46+
__NR_reboot,
47+
c_int(c::LINUX_REBOOT_MAGIC1),
48+
c_int(c::LINUX_REBOOT_MAGIC2),
49+
c_int(cmd as i32)
50+
))
51+
}
52+
}

src/system.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#![allow(unsafe_code)]
88

99
use crate::backend;
10+
#[cfg(target_os = "linux")]
11+
use crate::backend::c;
1012
use crate::ffi::CStr;
1113
#[cfg(not(any(target_os = "espidf", target_os = "emscripten")))]
1214
use crate::io;
@@ -154,3 +156,59 @@ pub fn sysinfo() -> Sysinfo {
154156
pub fn sethostname(name: &[u8]) -> io::Result<()> {
155157
backend::system::syscalls::sethostname(name)
156158
}
159+
160+
/// Reboot command to be used with [`reboot`].
161+
///
162+
/// See [`reboot`] documentation for more info
163+
///
164+
/// [`reboot`]: crate::system::reboot
165+
#[cfg(target_os = "linux")]
166+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
167+
#[repr(i32)]
168+
#[non_exhaustive]
169+
pub enum RebootCommand {
170+
/// CAD is disabled.
171+
/// This means that the CAD keystroke will cause a SIGINT signal to be sent to init (process 1),
172+
/// whereupon this process may decide upon a proper action (maybe: kill all processes, sync, reboot).
173+
/// Disables the Ctrl-Alt-Del keystroke.
174+
///
175+
/// When disabled, the keystroke will send a SIGINT signal to pid 1
176+
CadOff = c::LINUX_REBOOT_CMD_CAD_OFF,
177+
/// Enables the Ctrl-Alt-Del keystroke.
178+
///
179+
/// When enabled, the keystroke will trigger LINUX_REBOOT_CMD_RESTART
180+
CadOn = c::LINUX_REBOOT_CMD_CAD_ON,
181+
/// Prints the message "System halted" and halts the system
182+
Halt = c::LINUX_REBOOT_CMD_HALT,
183+
/// Execute a kernel that has been loaded earlier with [`kexec_load`].
184+
///
185+
/// [`kexec_load`]: https://man7.org/linux/man-pages/man2/kexec_load.2.html
186+
Kexec = c::LINUX_REBOOT_CMD_KEXEC,
187+
/// Prints the message "Power down.", stops the system and tries to remove all power
188+
PowerOff = c::LINUX_REBOOT_CMD_POWER_OFF,
189+
/// Prints the message "Restarting system." and triggers a restart
190+
Restart = c::LINUX_REBOOT_CMD_RESTART,
191+
/// Hibernate the system by suspending to disk
192+
SwSuspend = c::LINUX_REBOOT_CMD_SW_SUSPEND,
193+
}
194+
195+
/// `reboot`—Reboot or enable/disable Ctrl-Alt-Del
196+
///
197+
/// The reboot syscall, despite the name, can actually do much more than reboot.
198+
///
199+
/// Among other things it can
200+
/// - Restart, Halt, Power Off and Suspend the system
201+
/// - Enable and disable the Ctrl-Alt-Del keystroke
202+
/// - Execute other kernels
203+
/// - Terminate init inside PID namespaces
204+
///
205+
/// It is highly reccomended to carefully read the kernel documentation before calling this function.
206+
///
207+
/// # References
208+
/// - [Linux]
209+
///
210+
/// [Linux]: https://man7.org/linux/man-pages/man2/reboot.2.html
211+
#[cfg(target_os = "linux")]
212+
pub fn reboot(cmd: RebootCommand) -> io::Result<()> {
213+
backend::system::syscalls::reboot(cmd)
214+
}

tests/system/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#![cfg(feature = "system")]
44
#![cfg(not(any(windows, target_os = "wasi")))]
55

6+
#[cfg(target_os = "linux")]
7+
mod reboot;
68
#[cfg(linux_kernel)]
79
mod sysinfo;
810
mod uname;

tests/system/reboot.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#[test]
2+
#[cfg(feature = "thread")]
3+
fn test_reboot() {
4+
use rustix::{
5+
io::Errno,
6+
system::{self, RebootCommand},
7+
thread::{self, CapabilityFlags},
8+
};
9+
10+
let mut capabilities = thread::capabilities(None).expect("Failed to get capabilities");
11+
12+
capabilities.effective.set(CapabilityFlags::SYS_BOOT, false);
13+
14+
thread::set_capabilities(None, capabilities).expect("Failed to set capabilities");
15+
16+
// The reboot syscall requires the CAP_SYS_BOOT permission to be called, otherwise EPERM is returned
17+
assert_eq!(system::reboot(RebootCommand::Restart), Err(Errno::PERM));
18+
}

0 commit comments

Comments
 (0)