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

src/server/run_server.rs (9.5K)

  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             let _ = std::fs::remove_file("/tmp/cce-status-interface-adjust-mode");
219             if let Some(pid) = self.init_pid {
220                 log::info!("sending SIGTERM to child process group {}", pid);
221                 let _ = nix::sys::signal::kill(
222                     nix::unistd::Pid::from_raw(-pid.as_raw()),
223                     nix::sys::signal::Signal::SIGTERM,
224                 );
225             }
226             unsafe {
227                 if !self.wm.is_null() {
228                     for (_, pid) in &(*self.wm).startup_pids {
229                         log::info!("sending SIGTERM to startup program pid {}", pid);
230                         let _ = nix::sys::signal::kill(
231                             *pid,
232                             nix::sys::signal::Signal::SIGTERM,
233                         );
234                     }
235                 }
236             }
237         }
238     }
239 
240     let child_pgid = if let Some(ref cmd) = startup_command {
241         log::info!("running init executable '{}'", cmd);
242         unsafe {
243             match nix::unistd::fork() {
244                 Ok(nix::unistd::ForkResult::Child) => {
245                     process::cleanup_child();
246                     std::env::set_var("WAYLAND_DISPLAY", &socket_str);
247 
248                     if !args.no_xwayland && !server.xwayland.is_null() {
249                         let xwayland_cast = server.xwayland as *mut server::WlrXwayland;
250                         if !(*xwayland_cast).display_name.is_null() {
251                             let display_name = CStr::from_ptr((*xwayland_cast).display_name)
252                                 .to_string_lossy()
253                                 .into_owned();
254                             std::env::set_var("DISPLAY", display_name);
255                         }
256                     }
257 
258                     let cmd_c = CString::new(cmd.clone()).unwrap();
259                     let sh_c = CString::new("/bin/sh").unwrap();
260                     let c_c = CString::new("-c").unwrap();
261                     let args = [sh_c.as_c_str(), c_c.as_c_str(), cmd_c.as_c_str()];
262                     
263                     let env: Vec<CString> = std::env::vars()
264                         .map(|(k, v)| CString::new(format!("{}={}", k, v)).unwrap())
265                         .collect();
266                     let env_ptrs: Vec<&CStr> = env.iter().map(|s| s.as_c_str()).collect();
267 
268                     eprintln!("[execve] target cmd: {}, env WAYLAND_DISPLAY: {:?}", cmd, std::env::var("WAYLAND_DISPLAY"));
269                     let _ = nix::unistd::execve(&sh_c, &args, &env_ptrs);
270                     std::process::exit(1);
271                 }
272                 Ok(nix::unistd::ForkResult::Parent { child }) => Some(child),
273                 Err(_) => {
274                     log::error!("failed to fork child process");
275                     None
276                 }
277             }
278         }
279     } else {
280         None
281     };
282 
283     let _guard = ServerGuard {
284         init_pid: child_pgid,
285         wm: &mut server.wm as *mut crate::window_manager::WindowManager,
286     };
287 
288     log::info!("running server");
289     unsafe {
290         ffi::wl_display_run(server.wl_server);
291     }
292 
293     log::info!("shutting down server");
294     unsafe {
295         server.wm.save_state();
296     }
297     server.wm.shutting_down = true;
298     std::mem::drop(_guard);
299     server.deinit();
300 }