🚧 Move process management to launch-pad

This commit is contained in:
Lucy 2022-09-19 13:47:44 -04:00
parent 9c57475bb6
commit a4a791ed33
No known key found for this signature in database
GPG key ID: EBC517FAD666BBF1
6 changed files with 147 additions and 381 deletions

View file

@ -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")?;

View file

@ -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),
);
}

View file

@ -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()?

View file

@ -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();