🚧 Move process management to launch-pad
This commit is contained in:
parent
9c57475bb6
commit
a4a791ed33
6 changed files with 147 additions and 381 deletions
24
src/comp.rs
24
src/comp.rs
|
|
@ -1,5 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
use crate::process::{ProcessEvent, ProcessHandler};
|
||||
use color_eyre::eyre::{Result, WrapErr};
|
||||
use sendfd::SendWithFd;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -51,28 +50,6 @@ pub fn create_privileged_socket(
|
|||
Ok((env_vars, client_fd))
|
||||
}
|
||||
|
||||
async fn receive_event(rx: &mut mpsc::UnboundedReceiver<ProcessEvent>) -> Option<()> {
|
||||
match rx.recv().await? {
|
||||
ProcessEvent::Started => {
|
||||
info!("started");
|
||||
Some(())
|
||||
}
|
||||
// cosmic-comp outputs everything to stderr because slog
|
||||
ProcessEvent::Stdout(line) | ProcessEvent::Stderr(line) => {
|
||||
info!("{}", line);
|
||||
Some(())
|
||||
}
|
||||
ProcessEvent::Ended(Some(status)) => {
|
||||
error!("exited with status {}", status);
|
||||
None
|
||||
}
|
||||
ProcessEvent::Ended(None) => {
|
||||
error!("exited");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cancellation safe!
|
||||
#[derive(Default)]
|
||||
struct IpcState {
|
||||
|
|
@ -197,7 +174,6 @@ pub fn run_compositor(
|
|||
mut socket_rx: mpsc::UnboundedReceiver<Vec<UnixStream>>,
|
||||
env_tx: oneshot::Sender<HashMap<String, String>>,
|
||||
) -> Result<JoinHandle<Result<()>>> {
|
||||
let (tx, mut rx) = unbounded_channel::<ProcessEvent>();
|
||||
// Create a pair of unix sockets - one for us (session),
|
||||
// one for the compositor (comp)
|
||||
let (session, comp) = UnixStream::pair().wrap_err("failed to create pair of unix sockets")?;
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
use crate::process::{ProcessEvent, ProcessHandler};
|
||||
use std::os::unix::io::OwnedFd;
|
||||
use tokio::sync::mpsc::unbounded_channel;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{Instrument, Span};
|
||||
|
||||
pub fn run_executable(
|
||||
token: CancellationToken,
|
||||
span: Span,
|
||||
executable: &'static str,
|
||||
args: Vec<String>,
|
||||
env_vars: Vec<(String, String)>,
|
||||
fds: Vec<OwnedFd>,
|
||||
) {
|
||||
let span_2 = span.clone();
|
||||
let (tx, mut rx) = unbounded_channel::<ProcessEvent>();
|
||||
tokio::spawn(
|
||||
async move {
|
||||
ProcessHandler::new(tx, &token).run(executable, args, env_vars, fds, &span);
|
||||
while let Some(event) = rx.recv().await {
|
||||
match event {
|
||||
ProcessEvent::Started => {
|
||||
info!("started");
|
||||
}
|
||||
ProcessEvent::Stdout(line) => {
|
||||
info!("{}", line);
|
||||
}
|
||||
ProcessEvent::Stderr(line) => {
|
||||
error!("{}", line);
|
||||
}
|
||||
ProcessEvent::Ended(Some(status)) => {
|
||||
error!("exited with status {}", status);
|
||||
return;
|
||||
}
|
||||
ProcessEvent::Ended(None) => {
|
||||
error!("exited");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.instrument(span_2),
|
||||
);
|
||||
}
|
||||
74
src/main.rs
74
src/main.rs
|
|
@ -3,7 +3,6 @@
|
|||
extern crate tracing;
|
||||
|
||||
mod comp;
|
||||
mod generic;
|
||||
mod process;
|
||||
mod service;
|
||||
mod systemd;
|
||||
|
|
@ -11,6 +10,7 @@ mod systemd;
|
|||
use async_signals::Signals;
|
||||
use color_eyre::{eyre::WrapErr, Result};
|
||||
use futures_util::StreamExt;
|
||||
use launch_pad::{process::Process, ProcessManager};
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::metadata::LevelFilter;
|
||||
|
|
@ -55,70 +55,32 @@ async fn main() -> Result<()> {
|
|||
.collect::<Vec<_>>();
|
||||
info!("got environmental variables: {:?}", env_vars);
|
||||
|
||||
let process_manager = ProcessManager::new().await;
|
||||
let mut sockets = Vec::with_capacity(2);
|
||||
|
||||
let (env, fd) = comp::create_privileged_socket(&mut sockets, &env_vars)
|
||||
.wrap_err("failed to create panel socket")?;
|
||||
generic::run_executable(
|
||||
token.child_token(),
|
||||
info_span!(parent: None, "cosmic-panel"),
|
||||
"cosmic-panel",
|
||||
vec![],
|
||||
env,
|
||||
vec![fd],
|
||||
process_manager.start(Process::new().with_executable("cosmic-panel").with_env(env));
|
||||
let (env, fd) = comp::create_privileged_socket(&mut sockets, &env_vars)
|
||||
.wrap_err("failed to create applet host")?;
|
||||
process_manager.start(
|
||||
Process::new()
|
||||
.with_executable("cosmic-applet-host")
|
||||
.with_env(env),
|
||||
);
|
||||
let (env, fd) = comp::create_privileged_socket(&mut sockets, &env_vars)
|
||||
.wrap_err("failed to create applet host")?;
|
||||
generic::run_executable(
|
||||
token.child_token(),
|
||||
info_span!(parent: None, "cosmic-applet-host"),
|
||||
"cosmic-applet-host",
|
||||
vec![],
|
||||
env,
|
||||
vec![fd],
|
||||
);
|
||||
let (env, fd) = comp::create_privileged_socket(&mut sockets, &env_vars)
|
||||
.wrap_err("failed to create applet host")?;
|
||||
generic::run_executable(
|
||||
token.child_token(),
|
||||
info_span!(parent: None, "swaybg"),
|
||||
"swaybg",
|
||||
vec![
|
||||
"-i".into(),
|
||||
"/usr/share/backgrounds/pop/kate-hazen-COSMIC-desktop-wallpaper.png".into(),
|
||||
],
|
||||
env,
|
||||
vec![fd],
|
||||
);
|
||||
generic::run_executable(
|
||||
token.child_token(),
|
||||
info_span!(parent: None, "cosmic-settings-daemon"),
|
||||
"cosmic-settings-daemon",
|
||||
vec![],
|
||||
vec![],
|
||||
vec![],
|
||||
);
|
||||
let (env, fd) = comp::create_privileged_socket(&mut sockets, &env_vars)
|
||||
.wrap_err("failed to create applet host")?;
|
||||
generic::run_executable(
|
||||
token.child_token(),
|
||||
info_span!(parent: None, "cosmic-osd"),
|
||||
"cosmic-osd",
|
||||
vec![],
|
||||
env,
|
||||
vec![fd],
|
||||
);
|
||||
let (env, fd) = comp::create_privileged_socket(&mut sockets, &env_vars)
|
||||
.wrap_err("failed to create applet host")?;
|
||||
generic::run_executable(
|
||||
token.child_token(),
|
||||
info_span!(parent: None, "xdg-desktop-portal-cosmic"),
|
||||
"/usr/libexec/xdg-desktop-portal-cosmic",
|
||||
vec![],
|
||||
env,
|
||||
vec![fd],
|
||||
process_manager.start(
|
||||
Process::new()
|
||||
.with_executable("swaybg")
|
||||
.with_args(&[
|
||||
"-i",
|
||||
"/usr/share/backgrounds/pop/kate-hazen-COSMIC-desktop-wallpaper.png",
|
||||
])
|
||||
.with_env(env),
|
||||
);
|
||||
socket_tx.send(sockets).unwrap();
|
||||
process_manager.start(Process::new().with_executable("cosmic-settings-daemon"));
|
||||
|
||||
let (exit_tx, exit_rx) = oneshot::channel();
|
||||
let _ = ConnectionBuilder::session()?
|
||||
|
|
|
|||
130
src/process.rs
130
src/process.rs
|
|
@ -1,135 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
use color_eyre::eyre::{ContextCompat, Result, WrapErr};
|
||||
use nix::fcntl;
|
||||
use std::{
|
||||
os::unix::prelude::*,
|
||||
process::{ExitStatus, Stdio},
|
||||
};
|
||||
use tokio::{
|
||||
io::{AsyncBufReadExt, BufReader},
|
||||
process::Command,
|
||||
sync::mpsc::UnboundedSender,
|
||||
};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{Instrument, Span};
|
||||
|
||||
pub enum ProcessEvent {
|
||||
Started,
|
||||
Stdout(String),
|
||||
Stderr(String),
|
||||
Ended(Option<ExitStatus>),
|
||||
}
|
||||
|
||||
pub struct ProcessHandler {
|
||||
tx: UnboundedSender<ProcessEvent>,
|
||||
cancellation_token: CancellationToken,
|
||||
}
|
||||
|
||||
impl ProcessHandler {
|
||||
pub fn new(tx: UnboundedSender<ProcessEvent>, cancellation_token: &CancellationToken) -> Self {
|
||||
Self {
|
||||
tx,
|
||||
cancellation_token: cancellation_token.child_token(),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use `OwnedFd` when stable
|
||||
pub fn run(
|
||||
self,
|
||||
executable: impl ToString,
|
||||
args: Vec<String>,
|
||||
vars: Vec<(String, String)>,
|
||||
fds: Vec<OwnedFd>,
|
||||
span: &Span,
|
||||
) {
|
||||
let executable = executable.to_string();
|
||||
tokio::spawn(
|
||||
async move {
|
||||
for fd in &fds {
|
||||
if let Err(err) = mark_as_not_cloexec(fd) {
|
||||
error!("failed to launch '{}': {}", executable, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let mut child = match Command::new(&executable)
|
||||
.args(&args)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.envs(vars)
|
||||
.kill_on_drop(true)
|
||||
.spawn()
|
||||
{
|
||||
Ok(child) => child,
|
||||
Err(error) => {
|
||||
error!(
|
||||
"failed to launch '{} {}': {}",
|
||||
executable,
|
||||
args.join(" "),
|
||||
error
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
drop(fds);
|
||||
let mut stdout = BufReader::new(child.stdout.take().unwrap()).lines();
|
||||
let mut stderr = BufReader::new(child.stderr.take().unwrap()).lines();
|
||||
std::mem::drop(self.tx.send(ProcessEvent::Started));
|
||||
loop {
|
||||
tokio::select! {
|
||||
status = child.wait() => match status {
|
||||
Ok(status) => {
|
||||
info!("'{}' exited with status {}", executable, status);
|
||||
std::mem::drop(self.tx.send(ProcessEvent::Ended(Some(status))));
|
||||
return;
|
||||
}
|
||||
Err(error) => {
|
||||
error!(
|
||||
"failed to wait for '{}' to end: {}",
|
||||
executable,
|
||||
error
|
||||
);
|
||||
return;
|
||||
}
|
||||
},
|
||||
line = stdout.next_line() => match line {
|
||||
Ok(Some(line)) => {
|
||||
std::mem::drop(self.tx.send(ProcessEvent::Stdout(line)));
|
||||
},
|
||||
Ok(None) => (),
|
||||
Err(error) => {
|
||||
warn!(
|
||||
"failed to get stdout line from '{}': {}",
|
||||
executable,
|
||||
error
|
||||
);
|
||||
}
|
||||
},
|
||||
line = stderr.next_line() => match line {
|
||||
Ok(Some(line)) => {
|
||||
std::mem::drop(self.tx.send(ProcessEvent::Stderr(line)));
|
||||
},
|
||||
Ok(None) => (),
|
||||
Err(error) => {
|
||||
warn!(
|
||||
"failed to get stderr line from '{}': {}",
|
||||
executable,
|
||||
error
|
||||
);
|
||||
}
|
||||
},
|
||||
_ = self.cancellation_token.cancelled() => {
|
||||
warn!("exiting '{}': cancelled", executable);
|
||||
std::mem::drop(self.tx.send(ProcessEvent::Ended(None)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.instrument(span.clone()),
|
||||
);
|
||||
}
|
||||
}
|
||||
use std::os::unix::prelude::*;
|
||||
|
||||
fn mark_as_not_cloexec(file: &impl AsFd) -> Result<()> {
|
||||
let raw_fd = file.as_fd().as_raw_fd();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue