glycin_utils/
instruction_handler.rs1use std::ffi::{c_int, c_void};
4use std::os::fd::FromRawFd;
5use std::os::unix::net::UnixStream;
6use std::sync::Mutex;
7
8use nix::libc::{c_uint, siginfo_t};
9
10use crate::dbus_editor_api::{Editor, EditorImplementation, VoidEditorImplementation};
11use crate::dbus_loader_api::{Loader, LoaderImplementation};
12
13pub struct DbusServer {
14 _dbus_connection: zbus::Connection,
15}
16
17impl DbusServer {
18 pub fn spawn_loader<L: LoaderImplementation>(description: String) {
19 futures_lite::future::block_on(async move {
20 let _connection = Self::connect::<L, VoidEditorImplementation>(description).await;
21 std::future::pending::<()>().await;
22 })
23 }
24
25 pub fn spawn_loader_editor<L: LoaderImplementation, E: EditorImplementation>(
26 description: String,
27 ) {
28 futures_lite::future::block_on(async move {
29 let _connection = Self::connect::<L, E>(description).await;
30 std::future::pending::<()>().await;
31 })
32 }
33
34 async fn connect<L: LoaderImplementation, E: EditorImplementation>(
35 description: String,
36 ) -> Self {
37 env_logger::builder().format_timestamp_millis().init();
38
39 log::info!("Loader {description} startup");
40
41 let mut dbus_fd_str = None;
42 let mut args = std::env::args().skip(1);
43 while let Some(arg) = args.next() {
44 match arg.as_str() {
45 "--dbus-fd" => {
46 dbus_fd_str = args.next();
47 }
48
49 _ => {
50 log::warn!("Stopping command line parsing at unknown argument: {arg:?}");
51 break;
52 }
53 }
54 }
55
56 let Some(dbus_fd_str) = dbus_fd_str else {
57 log::error!("FD that facilitates the D-Bus connection not specified via --dbus-fd");
58 std::process::exit(2);
59 };
60
61 let Ok(dbus_fd) = dbus_fd_str.parse::<i32>() else {
62 log::error!("FD specified via --dbus-fd is not a valid number: {dbus_fd_str:?}",);
63 std::process::exit(2);
64 };
65
66 log::debug!("Creating zbus connection to glycin");
67
68 let unix_stream: UnixStream = unsafe { UnixStream::from_raw_fd(dbus_fd) };
69
70 #[cfg(feature = "tokio")]
71 let unix_stream =
72 tokio::net::UnixStream::from_std(unix_stream).expect("wrapping unix stream works");
73
74 let mut dbus_connection_builder = zbus::connection::Builder::unix_stream(unix_stream)
75 .p2p()
76 .auth_mechanism(zbus::AuthMechanism::Anonymous);
77
78 let loader_instruction_handler = Loader::<L> {
79 image_id: Mutex::new(1),
80 loader: Default::default(),
81 };
82
83 dbus_connection_builder = dbus_connection_builder
84 .serve_at("/org/gnome/glycin", loader_instruction_handler)
85 .expect("Failed to setup loader handler");
86
87 if E::USEABLE {
88 let editor_instruction_handler = Editor::<E> {
89 image_id: Mutex::new(1),
90 editor: Default::default(),
91 };
92 dbus_connection_builder = dbus_connection_builder
93 .serve_at("/org/gnome/glycin", editor_instruction_handler)
94 .expect("Failed to setup editor handler");
95 }
96
97 let dbus_connection = dbus_connection_builder
98 .build()
99 .await
100 .expect("Failed to create private DBus connection");
101
102 log::debug!("D-Bus connection to glycin created");
103 DbusServer {
104 _dbus_connection: dbus_connection,
105 }
106 }
107}
108
109#[allow(non_camel_case_types)]
110extern "C" fn sigsys_handler(_: c_int, info: *mut siginfo_t, _: *mut c_void) {
111 #[repr(C)]
114 struct siginfo_t {
115 si_signo: c_int,
116 si_errno: c_int,
117 si_code: c_int,
118 _sifields: _sigsys,
119 }
120
121 #[repr(C)]
122 struct _sigsys {
123 _call_addr: *const c_void,
124 _syscall: c_int,
125 _arch: c_uint,
126 }
127
128 let info: *mut siginfo_t = info.cast();
129 let syscall = unsafe { info.as_ref().unwrap()._sifields._syscall };
130
131 let name = libseccomp::ScmpSyscall::from(syscall).get_name().ok();
132
133 libc_eprint("glycin sandbox: Blocked syscall used: ");
134 libc_eprint(&name.unwrap_or_else(|| String::from("Unknown Syscall")));
135 libc_eprint(" (");
136 libc_eprint(&syscall.to_string());
137 libc_eprint(")\n");
138
139 unsafe {
140 libc::exit(128 + libc::SIGSYS);
141 }
142}
143
144fn setup_sigsys_handler() {
145 let mut mask = nix::sys::signal::SigSet::empty();
146 mask.add(nix::sys::signal::Signal::SIGSYS);
147
148 let sigaction = nix::sys::signal::SigAction::new(
149 nix::sys::signal::SigHandler::SigAction(sigsys_handler),
150 nix::sys::signal::SaFlags::SA_SIGINFO,
151 mask,
152 );
153
154 unsafe {
155 if nix::sys::signal::sigaction(nix::sys::signal::Signal::SIGSYS, &sigaction).is_err() {
156 libc_eprint("glycin sandbox: Failed to init syscall failure signal handler");
157 }
158 };
159}
160
161#[allow(dead_code)]
162pub extern "C" fn pre_main() {
163 setup_sigsys_handler();
164}
165
166#[macro_export]
167macro_rules! init_main_loader {
168 ($loader:path) => {
169 #[cfg_attr(target_os = "linux", unsafe(link_section = ".ctors"))]
171 static __CTOR: extern "C" fn() = pre_main;
172
173 fn main() {
174 $crate::DbusServer::spawn_loader::<$loader>(format!(
175 "{} v{}",
176 env!("CARGO_PKG_NAME"),
177 env!("CARGO_PKG_VERSION")
178 ));
179 }
180 };
181}
182
183#[macro_export]
184macro_rules! init_main_loader_editor {
185 ($loader:path, $editor:path) => {
186 #[cfg_attr(target_os = "linux", unsafe(link_section = ".ctors"))]
188 static __CTOR: extern "C" fn() = pre_main;
189
190 fn main() {
191 $crate::DbusServer::spawn_loader_editor::<$loader, $editor>(format!(
192 "{} v{}",
193 env!("CARGO_PKG_NAME"),
194 env!("CARGO_PKG_VERSION")
195 ));
196 }
197 };
198}
199
200fn libc_eprint(s: &str) {
201 unsafe {
202 libc::write(
203 libc::STDERR_FILENO,
204 s.as_ptr() as *const libc::c_void,
205 s.len(),
206 );
207 }
208}