Skip to content
Open
Changes from 4 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ed6d619
Use ssh2 instead of std::process::Command
Feb 12, 2025
c5811d8
Append ssh port if not already there
Feb 12, 2025
61d992c
Add missing G_SLICE env variable
Feb 12, 2025
a566f08
Use process::Command to construct shell command
Feb 12, 2025
ee2828c
Set G_SLICE in acap-ssh-utils instead
Feb 13, 2025
327dc82
Handle the exit_status of remote program
Feb 13, 2025
7bc38a0
Channels can't be reused so create it only when needed
Feb 13, 2025
c236986
Use bail! macro
Feb 13, 2025
38a275d
Return Vec<u8> from exec_capture_output
Feb 13, 2025
537e846
Make session blocking
Feb 13, 2025
1cbac37
Change println! to debug!
Feb 13, 2025
1d8fd32
Handle root/acap user differently
Feb 14, 2025
ede5781
Change unwrap to expect for clarity
Feb 14, 2025
193268f
Remove unnecessary assignment
Feb 14, 2025
6cb7f19
Always append port 22 to host
Feb 14, 2025
f832a11
Handle directories with sftp
Feb 17, 2025
5580324
Handle arguments to remote programs when using 'su'
Feb 17, 2025
6df6689
Add contexts to fallible sftp calls
Feb 17, 2025
67ad2e5
Fix lint
Feb 17, 2025
ac11ef6
Merge remote-tracking branch 'upstream/main' into rust-ssh
Mar 27, 2025
d1f8363
Update docstrings
Mar 27, 2025
1f2dd63
Remove sshpass from system requirements
Mar 27, 2025
5e8ff7a
Use is_ok_and instead of expect
Mar 27, 2025
feecdbb
Merge remote-tracking branch 'upstream/main' into rust-ssh
Apr 16, 2025
ecd277e
Change `as_root` to `as_package_user`
Apr 16, 2025
29aa98b
Handle symlinks
Apr 16, 2025
3020e38
Change CWD when running ACAP
Apr 16, 2025
7afa257
Use `with_context` instead of `context`
Apr 16, 2025
1757a27
Avoid unnecessary `unwrap`s
Apr 16, 2025
020cfb9
Pass arguments correctly + test
Apr 16, 2025
94ee183
Reformat
Apr 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 33 additions & 12 deletions crates/acap-ssh-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ use std::{
path::{Path, PathBuf},
};

use anyhow::bail;
use anyhow::{bail, Context};
use flate2::read::GzDecoder;
use log::debug;
use tar::Archive;

use ssh2::{FileStat, Session};
Expand Down Expand Up @@ -44,6 +43,7 @@ impl RemoteCommand {

cmd.arg(executable);
if let Some(args) = args {
cmd.arg("--"); // They should be passed to the process, not to 'su'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested this?

I don't have a camera at hand, but su --help on Debian does not mention that -- can be used and experimenting seems to confirm this:

$ su --help 
Usage:
 su [options] [-] [<user> [<argument>...]]
...

$ env --help
env --help
Usage: env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]
...

$ su root -c env -- --help
SHELL=/bin/bash
...

I understand the camera might differ, so this is just to double check.

I will make sure to get a camera set up in the next week so that I can check things like this myself and not have to bother you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. When I was running the tests it was complaining since we pass --test-threads=1 to the test. Adding the '--' suppressed this but that's not the correct way to do it. We need to pass the executable with its arguments as a whole, i.e. as a string:

su root -c "env --help"

I've fixed that now and added a test in inspect_env to make sure this stays working.

cmd.args(args);
}

Expand Down Expand Up @@ -126,17 +126,23 @@ pub fn run_other<S: AsRef<str>>(
{
let path = Path::new(&path);

let sftp = session.sftp()?;
sftp.create(path)?.write_all(&std::fs::read(prog)?)?;
let mut stat = sftp.stat(path)?;
let sftp = session.sftp().context("Creating sftp session")?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I'm usually too lazy to add context, but it really does help when troubleshooting.

sftp.create(path)
.context(format!("Creating {:?}", path))?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using with_context to avoid creating these strings eagerly.

.write_all(&std::fs::read(prog)?)
.context(format!("Writing {:?}", prog))?;
let mut stat = sftp
.stat(path)
.context(format!("Running `stat` on {:?}", path))?;
// `sftp.create` creates a new file with write-only permissions,
// but since we expect to run this program we need to mark it executable
// for the user
stat.perm = Some(0o100744);
sftp.setstat(path, stat)?;
sftp.setstat(path, stat)
.context(format!("Updating stat on {:?}", path))?;
}

RemoteCommand::new(None::<&str>, Some(env), &path, Some(args)).exec(session)
RemoteCommand::new(None::<&str>, Some(env), path, Some(args)).exec(session)
}

// TODO: Consider abstracting away the difference between devices that support developer mode, and
Expand Down Expand Up @@ -201,7 +207,7 @@ pub fn patch_package(package: &Path, session: &Session) -> anyhow::Result<()> {
};

let package_dir = PathBuf::from("/usr/local/packages").join(app_name);
let sftp = session.sftp()?;
let sftp = session.sftp().context("Creating sftp session")?;
if sftp.stat(&package_dir).is_err() {
bail!("Package doesn't exist!");
}
Expand All @@ -223,11 +229,26 @@ pub fn patch_package(package: &Path, session: &Session) -> anyhow::Result<()> {
mtime: Some(header.mtime()?),
size: None,
};

if header.entry_type().is_dir() {
// If the directory can't be opened, then it doesn't exist so we need to create it.
// TODO: What if permissions has changed?
if sftp.opendir(entry.path()?).is_err() {
sftp.mkdir(&header.path()?, header.mode()? as i32)
.context(format!("Creating directory {:?}", entry.path()?))?;
}

continue;
}

entry.read_to_end(&mut buf)?;
debug!("Writing file: {:?}", entry.path()?);
let mut file = sftp.create(&package_dir.join(&entry.path()?))?;
file.write_all(&buf)?;
file.setstat(stat)?;
let mut file = sftp
.create(&package_dir.join(&entry.path()?))
.context(format!("Creating {:?}", entry.path()?))?;
file.write_all(&buf)
.context(format!("Writing to {:?}", entry.path()?))?;
file.setstat(stat)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work if the app contains files that are symlinks?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inspect_env has a couple of symlinks, so that could be useful for testing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not. I'll fix that.

.context(format!("Updating stat on {:?}", entry.path()?))?;
}
}

Expand Down