git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

src/server/run_server.rs (9.4K)

  1 // SPDX-FileCopyrightText: © 2020 The River Developers
  2 // SPDX-License-Identifier: GPL-3.0-only
  3 
  4 use clap::Parser;
  5 use std::ffi::{CStr, CString};
  6 use std::os::raw::c_char;
  7 
  8 use crate::ffi;
  9 use crate::server;
 10 use crate::process;
 11 
 12 const USAGE: &str = "\
 13 usage: river [options]
 14 
 15   -h, --help           Print this help message and exit.
 16   --version            Print the version number and exit.
 17   -c <command>         Run `sh -c <command>` on startup instead of the default init executable.
 18   --log-level <level>  Set the log level to error, warning, info, or debug.
 19   --no-xwayland        Disable xwayland even if built with support.
 20 ";
 21 
 22 #[derive(Parser, Debug)]
 23 #[command(disable_help_flag = true)]
 24 struct Args {
 25     #[arg(short = 'h', long = "help")]
 26     help: bool,
 27 
 28     #[arg(long = "version")]
 29     version: bool,
 30 
 31     #[arg(short = 'c')]
 32     command: Option<String>,
 33 
 34     #[arg(long = "log-level", default_value = "info")]
 35     log_level: String,
 36 
 37     #[arg(long = "no-xwayland")]
 38     no_xwayland: bool,
 39 }
 40 
 41 #[no_mangle]
 42 pub unsafe extern "C" fn river_wlroots_log_callback(
 43     importance: ffi::wlr_log_importance,
 44     ptr: *const c_char,
 45     len: usize,
 46 ) {
 47     let message_slice = std::slice::from_raw_parts(ptr as *const u8, len);
 48     let message = String::from_utf8_lossy(message_slice);
 49     let message_trimmed = message.trim();
 50 
 51     match importance {
 52         ffi::wlr_log_importance_WLR_ERROR => log::error!(target: "wlroots", "{}", message_trimmed),
 53         ffi::wlr_log_importance_WLR_INFO => log::info!(target: "wlroots", "{}", message_trimmed),
 54         ffi::wlr_log_importance_WLR_DEBUG => log::debug!(target: "wlroots", "{}", message_trimmed),
 55         _ => {}
 56     }
 57 }
 58 
 59 fn default_init_path() -> Option<String> {
 60     let path = if let Ok(xdg_config_home) = std::env::var("XDG_CONFIG_HOME") {
 61         format!("{}/cce/init", xdg_config_home)
 62     } else if let Ok(home) = std::env::var("HOME") {
 63         format!("{}/.config/cce/init", home)
 64     } else {
 65         return None;
 66     };
 67 
 68     if std::path::Path::new(&path).exists() {
 69         Some(path)
 70     } else {
 71         None
 72     }
 73 }
 74 
 75 fn detect_classic(path: &str) {
 76     if let Ok(content) = std::fs::read_to_string(path) {
 77         if content.contains("riverctl") {
 78             log::error!(
 79                 "The init file {} contains the string \"riverctl\".\n\
 80                  This version of river does not support riverctl.\n\
 81                  See https://isaacfreund.com/software/river for more information.",
 82                 path
 83             );
 84             std::process::exit(1);
 85         }
 86     }
 87 }
 88 
 89 pub fn run_server() {
 90     let args = match Args::try_parse() {
 91         Ok(a) => a,
 92         Err(_) => {
 93             eprintln!("{}", USAGE);
 94             std::process::exit(1);
 95         }
 96     };
 97 
 98     if args.help {
 99         println!("{}", USAGE);
100         std::process::exit(0);
101     }
102 
103     if args.version {
104         println!("0.5.0-dev -xwayland (Rust rewrite starter)");
105         std::process::exit(0);
106     }
107 
108     let log_level = match args.log_level.as_str() {
109         "error" => log::LevelFilter::Error,
110         "warning" => log::LevelFilter::Warn,
111         "info" => log::LevelFilter::Info,
112         "debug" => log::LevelFilter::Debug,
113         _ => log::LevelFilter::Info,
114     };
115 
116     env_logger::Builder::new()
117         .filter(None, log_level)
118         .init();
119 
120     log::info!("initializing river (Rust rewrite)");
121 
122     let importance = match log_level {
123         log::LevelFilter::Debug => ffi::wlr_log_importance_WLR_DEBUG,
124         log::LevelFilter::Info => ffi::wlr_log_importance_WLR_INFO,
125         _ => ffi::wlr_log_importance_WLR_ERROR,
126     };
127 
128     unsafe {
129         ffi::river_init_wlroots_log(importance);
130     }
131 
132     let startup_command = if let Some(cmd) = args.command {
133         Some(cmd)
134     } else {
135         default_init_path()
136     };
137 
138     if let Some(ref cmd) = startup_command {
139         detect_classic(cmd);
140     }
141 
142     let mut server = Box::new(server::Server::default());
143     if let Err(e) = server.init(!args.no_xwayland) {
144         log::error!("failed to initialize server: {}", e);
145         std::process::exit(1);
146     }
147 
148     if let Some(path) = crate::config::default_config_path() {
149         log::info!("loading config from {}", path);
150         if let Err(e) = crate::config::parse_config(&path, &mut server.wm) {
151             log::error!("failed to parse config at {}: {}", path, e);
152         }
153     } else {
154         log::warn!("no config file found, using defaults");
155     }
156 
157     if let Some(state_path) = crate::config::default_state_path() {
158         unsafe {
159             server.wm.load_state(&state_path);
160         }
161     }
162 
163     process::setup();
164 
165     let socket_ptr = unsafe {
166         ffi::wl_display_add_socket_auto(server.wl_server)
167     };
168     if socket_ptr.is_null() {
169         log::error!("failed to add wayland socket");
170         server.deinit();
171         std::process::exit(1);
172     }
173     let socket_str = unsafe { CStr::from_ptr(socket_ptr).to_string_lossy().into_owned() };
174     log::info!("running server on display socket: {}", socket_str);
175 
176     std::env::set_var("WAYLAND_DISPLAY", &socket_str);
177 
178     unsafe { server.wm.start_ipc(Some(socket_str.clone())) };
179 
180     let status_sender = crate::status_server::spawn_status_server(Some(socket_str.clone()));
181     server.wm.status_sender = Some(status_sender);
182 
183     let stream_hub = crate::stream_server::spawn_stream_server(Some(socket_str.clone()));
184     unsafe { server.wm.start_stream(stream_hub) };
185 
186 
187     let started = unsafe { ffi::wlr_backend_start(server.backend) };
188     if !started {
189         log::error!("failed to start wlr_backend");
190         server.deinit();
191         std::process::exit(1);
192     }
193 
194     // Suppress the "requested activation" notification burst that session
195     // restore is about to trigger: every respawned window issues an
196     // xdg-activation request as it maps. The grace window covers the whole
197     // startup sequence (startup programs + restored windows).
198     server::begin_startup_activation_grace();
199 
200     // Spawn configured startup programs
201     let current_startup = server.wm.startup.clone();
202     for prog in current_startup {
203         unsafe {
204             server.wm.spawn_startup_program(prog);
205         }
206     }
207 
208     unsafe {
209         server.wm.spawn_restored_windows();
210     }
211 
212     struct ServerGuard {
213         init_pid: Option<nix::unistd::Pid>,
214         wm: *mut crate::window_manager::WindowManager,
215     }
216     impl Drop for ServerGuard {
217         fn drop(&mut self) {
218             if let Some(pid) = self.init_pid {
219                 log::info!("sending SIGTERM to child process group {}", pid);
220                 let _ = nix::sys::signal::kill(
221                     nix::unistd::Pid::from_raw(-pid.as_raw()),
222                     nix::sys::signal::Signal::SIGTERM,
223                 );
224             }
225             unsafe {
226                 if !self.wm.is_null() {
227                     for (_, pid) in &(*self.wm).startup_pids {
228                         log::info!("sending SIGTERM to startup program pid {}", pid);
229                         let _ = nix::sys::signal::kill(
230                             *pid,
231                             nix::sys::signal::Signal::SIGTERM,
232                         );
233                     }
234                 }
235             }
236         }
237     }
238 
239     let child_pgid = if let Some(ref cmd) = startup_command {
240         log::info!("running init executable '{}'", cmd);
241         unsafe {
242             match nix::unistd::fork() {
243                 Ok(nix::unistd::ForkResult::Child) => {
244                     process::cleanup_child();
245                     std::env::set_var("WAYLAND_DISPLAY", &socket_str);
246 
247                     if !args.no_xwayland && !server.xwayland.is_null() {
248                         let xwayland_cast = server.xwayland as *mut server::WlrXwayland;
249                         if !(*xwayland_cast).display_name.is_null() {
250                             let display_name = CStr::from_ptr((*xwayland_cast).display_name)
251                                 .to_string_lossy()
252                                 .into_owned();
253                             std::env::set_var("DISPLAY", display_name);
254                         }
255                     }
256 
257                     let cmd_c = CString::new(cmd.clone()).unwrap();
258                     let sh_c = CString::new("/bin/sh").unwrap();
259                     let c_c = CString::new("-c").unwrap();
260                     let args = [sh_c.as_c_str(), c_c.as_c_str(), cmd_c.as_c_str()];
261                     
262                     let env: Vec<CString> = std::env::vars()
263                         .map(|(k, v)| CString::new(format!("{}={}", k, v)).unwrap())
264                         .collect();
265                     let env_ptrs: Vec<&CStr> = env.iter().map(|s| s.as_c_str()).collect();
266 
267                     eprintln!("[execve] target cmd: {}, env WAYLAND_DISPLAY: {:?}", cmd, std::env::var("WAYLAND_DISPLAY"));
268                     let _ = nix::unistd::execve(&sh_c, &args, &env_ptrs);
269                     std::process::exit(1);
270                 }
271                 Ok(nix::unistd::ForkResult::Parent { child }) => Some(child),
272                 Err(_) => {
273                     log::error!("failed to fork child process");
274                     None
275                 }
276             }
277         }
278     } else {
279         None
280     };
281 
282     let _guard = ServerGuard {
283         init_pid: child_pgid,
284         wm: &mut server.wm as *mut crate::window_manager::WindowManager,
285     };
286 
287     log::info!("running server");
288     unsafe {
289         ffi::wl_display_run(server.wl_server);
290     }
291 
292     log::info!("shutting down server");
293     unsafe {
294         server.wm.save_state();
295     }
296     server.wm.shutting_down = true;
297     std::mem::drop(_guard);
298     server.deinit();
299 }