1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
/* -------------------------------------------------------------------------- *\
* Apache 2.0 License Copyright © 2022-2023 The Aurae Authors *
* *
* +--------------------------------------------+ *
* | █████╗ ██╗ ██╗██████╗ █████╗ ███████╗ | *
* | ██╔══██╗██║ ██║██╔══██╗██╔══██╗██╔════╝ | *
* | ███████║██║ ██║██████╔╝███████║█████╗ | *
* | ██╔══██║██║ ██║██╔══██╗██╔══██║██╔══╝ | *
* | ██║ ██║╚██████╔╝██║ ██║██║ ██║███████╗ | *
* | ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ | *
* +--------------------------------------------+ *
* *
* Distributed Systems Runtime *
* *
* -------------------------------------------------------------------------- *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
* *
\* -------------------------------------------------------------------------- */
use anyhow::anyhow;
use std::{fs::OpenOptions, io::Read, mem, path::Path, slice};
use tracing::{info, trace};
use ::libc;
pub(crate) fn syscall_reboot(action: i32) {
unsafe {
if libc::reboot(action) != 0 {
// TODO: handle this better
panic!("failed to reboot");
}
}
}
pub(crate) fn power_off() {
syscall_reboot(libc::LINUX_REBOOT_CMD_POWER_OFF);
}
pub(crate) fn reboot() {
syscall_reboot(libc::LINUX_REBOOT_CMD_RESTART);
}
#[derive(Debug, Default, Copy, Clone)]
#[repr(C, packed)]
pub(crate) struct InputEvent {
tv_sec: u64,
tv_usec: u64,
evtype: u16,
code: u16,
value: u32,
}
// see https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/input-event-codes.h#L191
const KEY_POWER: u16 = 116;
const KEY_RESTART: u16 = 0x198;
fn to_u8_ptr<T>(p: *mut T) -> *mut u8 {
p as _
}
pub(crate) fn spawn_thread_power_button_listener(
power_btn_device_path: impl AsRef<Path>,
) -> anyhow::Result<()> {
let mut event_file = match OpenOptions::new()
.read(true)
.write(false)
.open(&power_btn_device_path)
{
Ok(file) => file,
Err(e) => {
return Err(anyhow!(
"Could not open power button device {}. {:?}",
power_btn_device_path.as_ref().display(),
e
));
}
};
let mut event: InputEvent = unsafe { mem::zeroed() };
let event_size = mem::size_of::<InputEvent>();
let power_btn_device = power_btn_device_path.as_ref().to_owned();
let _ = std::thread::spawn(move || {
loop {
let event_slice = unsafe {
slice::from_raw_parts_mut(to_u8_ptr(&mut event), event_size)
};
match event_file.read(event_slice) {
Ok(result) => {
trace!("Event0: {} {:?}", result, event);
if event.code == KEY_POWER {
// TODO: shutdown runtime
// - need to send signal via a channel to runtime
// - await for runtime
info!("Power Button pressed - shutting down now");
power_off();
} else if event.code == KEY_RESTART {
info!("Restart Button pressed - rebooting now");
reboot();
}
}
Err(e) => {
return Err::<(), anyhow::Error>(anyhow!(
"Could not parse event from {}: {:?}",
power_btn_device.display(),
e
));
}
}
}
});
Ok(())
}