git.delta.rocks / remowt / refs/commits / 5f64fb1ffdf0

difftreelog

feat socket polkit helper

vpnuwsmwYaroslav Bolyukin2026-01-25parent: #4516008.patch.diff
in: trunk

5 files changed

modifiedcmds/remowt-agent/src/helper/dbus.rsdiffbeforeafterboth
2use std::marker::PhantomData;2use std::marker::PhantomData;
33
4use polkit_shared::{BackendRequest, Identity};4use polkit_shared::{BackendRequest, Identity};
5use tokio::runtime::Handle;
6use ui_prompt::dbus::DbusPrompterInterface;5use ui_prompt::dbus::DbusPrompterInterface;
7use ui_prompt::Prompter;6use ui_prompt::Prompter;
8use zbus::Connection;7use zbus::Connection;
36}34}
37impl<P: Prompter + Send + Sync + 'static> Drop for TemporaryPrompterInterface<P> {35impl<P: Prompter + Send + Sync + 'static> Drop for TemporaryPrompterInterface<P> {
38 fn drop(&mut self) {36 fn drop(&mut self) {
39 // FIXME: block_in_place prevents to moving to current_thread runtime37 // Removal is async because of async RwLock used inside...
40 // There should be a blocking way to remove ObjectServer listener.38 // We should not care about its reuse
41 // As far as I can see, it is only async because of async RwLock, shouldn't it be39 let connection = self.connection.clone();
42 // just a sync lock?40 let path = std::mem::take(&mut self.path);
43 tokio::task::block_in_place(move || {41 tokio::spawn(async move {
44 Handle::current().block_on(async {
45 let _ = self42 let _ = connection
46 .connection
47 .object_server()43 .object_server()
48 .remove::<DbusPrompterInterface<P>, String>(self.path.clone())44 .remove::<DbusPrompterInterface<P>, String>(path)
49 .await;45 .await;
50 });
51 });46 });
52 }47 }
53}48}
5449
50#[derive(Clone)]
55pub struct DbusHelper {51pub struct DbusHelper {
56 connection: Connection,52 connection: Connection,
57 helper: PolkitHelperProxy<'static>,53 helper: PolkitHelperProxy<'static>,
58}54}
55impl DbusHelper {
56 pub async fn new(connection: Connection) -> zbus::Result<Self> {
57 let helper = PolkitHelperProxy::new(&connection).await?;
58 Ok(Self { connection, helper })
59 }
60}
59impl Helper for DbusHelper {61impl Helper for DbusHelper {
60 async fn help_me<P: Prompter + Send + Sync + 'static>(62 async fn help_me<P: Prompter + Send + Sync + 'static>(
61 &self,63 &self,
modifiedcmds/remowt-agent/src/helper/mod.rsdiffbeforeafterboth
2use polkit_shared::Identity;2use polkit_shared::Identity;
3use ui_prompt::Prompter;3use ui_prompt::Prompter;
44
5mod dbus;
6mod protocol;
7mod socket;
5mod suid;8mod suid;
9
6mod dbus;10pub use dbus::DbusHelper;
7
8pub use suid::SuidHelper;11pub use socket::SocketHelper;
9pub use dbus::DbusHelper;12pub use suid::SuidHelper;
1013
11pub trait Helper {14pub trait Helper {
12 fn help_me<P: Prompter + Send + Sync + 'static>(15 fn help_me<P: Prompter + Send + Sync + 'static>(
addedcmds/remowt-agent/src/helper/protocol.rsdiffbeforeafterboth

no changes

addedcmds/remowt-agent/src/helper/socket.rsdiffbeforeafterboth

no changes

modifiedcmds/remowt-agent/src/helper/suid.rsdiffbeforeafterboth
1use std::pin::pin;
2use std::process::Stdio;1use std::process::Stdio;
32
4use anyhow::{bail, anyhow};3use anyhow::{anyhow, bail};
5use futures::stream::Peekable;
6use futures::StreamExt as _;
7use nix::unistd::User;4use nix::unistd::User;
8use polkit_shared::Identity;5use polkit_shared::Identity;
9use tokio::io::AsyncWriteExt as _;6use tokio::io::AsyncWriteExt as _;
10use tokio::process::Command;7use tokio::process::Command;
11use tokio::select;8use ui_prompt::Prompter;
9
12use tokio_util::codec::{FramedRead, LinesCodec};10use super::protocol::run_conversation;
13use ui_prompt::Prompter;
14
15use super::Helper;11use super::Helper;
1612
23 prompt: P,19 prompt: P,
24 identity: Identity,20 identity: Identity,
25 ) -> anyhow::Result<()> {21 ) -> anyhow::Result<()> {
26 let Some(uid) = dbg!(identity.uid()) else {22 let Some(uid) = identity.uid() else {
27 bail!("can't process identity");23 bail!("can't process identity");
28 };24 };
29 let user = User::from_uid(dbg!(uid))25 let user = User::from_uid(uid)
30 .map_err(|e| anyhow!("error querying user: {e}"))?26 .map_err(|e| anyhow!("error querying user: {e}"))?
31 .ok_or_else(|| anyhow!("user not found"))?;27 .ok_or_else(|| anyhow!("user not found"))?;
3228
37 cmd.kill_on_drop(true);33 cmd.kill_on_drop(true);
38 let mut child = cmd.spawn()?;34 let mut child = cmd.spawn()?;
39 let mut stdin = child.stdin.take().expect("piped");35 let mut stdin = child.stdin.take().expect("piped");
40 let mut stdout =36 let stdout = child.stdout.take().expect("piped");
41 pin!(
42 FramedRead::new(child.stdout.take().expect("piped"), LinesCodec::new()).peekable()
43 );
4437
45 assert!(!cookie.contains("\n"));38 assert!(!cookie.contains('\n'));
46 stdin.write_all(cookie.as_bytes()).await?;39 stdin.write_all(cookie.as_bytes()).await?;
47 stdin.write_all(b"\n").await?;40 stdin.write_all(b"\n").await?;
4841
49 while let Some(line) = stdout.next().await {42 let res = run_conversation(stdout, stdin, prompt).await;
50 let line = dbg!(line?);
51 // TODO: Dedicated codec?
52 let res = if let Some(prompt_text) = line.strip_prefix("PAM_PROMPT_ECHO_OFF ") {
53 prompt.prompt_text(false, prompt_text, "", &[]).await?
54 } else if let Some(prompt_text) = line.strip_prefix("PAM_PROMPT_ECHO_ON ") {
55 prompt.prompt_text(true, prompt_text, "", &[]).await?
56 } else if let Some(msg_text) = line.strip_prefix("PAM_ERROR_MSG ") {
57 prompt.display_text(true, msg_text, &[]).await?;
58 String::new()
59 } else if let Some(msg_text) = line.strip_prefix("PAM_TEXT_INFO ") {
60 select! {
61 _ = Peekable::peek(stdout.as_mut()) => {},
62 r = prompt.display_text(false, msg_text, &[]) => {r?}
63 }
64 String::new()
65 } else if line == "SUCCESS" {
66 return Ok(());
67 } else if line == "FAILURE" {
68 bail!("helper binary reported failure")
69 } else {
70 // TODO: Success/failure handling
71 bail!("unknown agent request");
72 };
73
74 if res.contains("\n") {
75 bail!("response should not include newline")
76 }
77
78 stdin.write_all(res.as_bytes()).await?;
79 stdin.write_all(b"\n").await?;
80 }
81 bail!("agent finished unexpectedly")43 drop(child);
44 res
82 }45 }
83}46}
8447