]>
Commit | Line | Data |
---|---|---|
7801b96c TL |
1 | use std::ffi::OsString; |
2 | use std::os::fd::RawFd; | |
3 | ||
4 | use anyhow::{bail, Result}; | |
5 | ||
6 | const CMD_HELP: &str = "\ | |
7 | Usage: proxmox-termproxy [OPTIONS] --path <path> <listen-port> -- <terminal-cmd>... | |
8 | ||
9 | Arguments: | |
10 | <listen-port> Port or file descriptor to listen for TCP connections | |
11 | <terminal-cmd>... The command to run connected via a proxied PTY | |
12 | ||
13 | Options: | |
14 | --authport <authport> Port to relay auth-request, default 85 | |
15 | --port-as-fd Use <listen-port> as file descriptor. | |
16 | --path <path> ACL object path to test <perm> on. | |
17 | --perm <perm> Permission to test. | |
18 | -h, --help Print help | |
19 | "; | |
20 | ||
21 | #[derive(Debug)] | |
22 | pub enum PortOrFd { | |
23 | Port(u16), | |
24 | Fd(RawFd), | |
25 | } | |
26 | ||
27 | impl PortOrFd { | |
28 | fn from_cli(value: u64, use_as_fd: bool) -> Result<PortOrFd> { | |
29 | if use_as_fd { | |
30 | if value > RawFd::MAX as u64 { | |
31 | bail!("FD value too big"); | |
32 | } | |
33 | Ok(Self::Fd(value as RawFd)) | |
34 | } else { | |
35 | if value > u16::MAX as u64 { | |
36 | bail!("invalid port number"); | |
37 | } | |
38 | Ok(Self::Port(value as u16)) | |
39 | } | |
40 | } | |
41 | } | |
42 | ||
43 | #[derive(Debug)] | |
44 | pub struct Options { | |
45 | /// The actual command to run proxied in a pseudo terminal. | |
46 | pub terminal_command: Vec<OsString>, | |
47 | /// The port or FD that termproxy will listen on for an incoming conection | |
48 | pub listen_port: PortOrFd, | |
49 | /// The port of the local privileged daemon that authentication is relayed to. Defaults to `85` | |
50 | pub api_daemon_port: u16, | |
51 | /// The ACL object path the 'acl_permission' is checked on | |
52 | pub acl_path: String, | |
53 | /// The ACL permission that the ticket, read from the stream, is required to have on 'acl_path' | |
54 | pub acl_permission: Option<String>, | |
55 | } | |
56 | ||
57 | impl Options { | |
58 | pub fn from_env() -> Result<Self> { | |
59 | let mut args: Vec<_> = std::env::args_os().collect(); | |
60 | args.remove(0); // remove the executable path. | |
61 | ||
62 | // handle finding command after `--` first so that we only parse our options later | |
63 | let terminal_command = if let Some(dash_dash) = args.iter().position(|arg| arg == "--") { | |
64 | let later_args = args.drain(dash_dash + 1..).collect(); | |
65 | args.pop(); // .. then remove the `--` | |
66 | Some(later_args) | |
67 | } else { | |
68 | None | |
69 | }; | |
70 | ||
71 | // Now pass the remaining arguments through to `pico_args`. | |
72 | let mut args = pico_args::Arguments::from_vec(args); | |
73 | ||
74 | if args.contains(["-h", "--help"]) { | |
75 | print!("{CMD_HELP}"); | |
76 | std::process::exit(0); | |
77 | } else if terminal_command.is_none() { | |
78 | bail!("missing terminal command or -- option-end marker, see '-h' for usage"); | |
79 | } | |
80 | ||
81 | let options = Self { | |
82 | terminal_command: terminal_command.unwrap(), // checked above | |
83 | listen_port: PortOrFd::from_cli(args.free_from_str()?, args.contains("--port-as-fd"))?, | |
84 | api_daemon_port: args.opt_value_from_str("--authport")?.unwrap_or(85), | |
85 | acl_path: args.value_from_str("--path")?, | |
86 | acl_permission: args.opt_value_from_str("--perm")?, | |
87 | }; | |
88 | ||
89 | if !args.finish().is_empty() { | |
90 | bail!("unexpected extra arguments, use '-h' for usage"); | |
91 | } | |
92 | ||
93 | Ok(options) | |
94 | } | |
95 | ||
96 | pub fn use_listen_port_as_fd(&self) -> bool { | |
97 | match self.listen_port { | |
98 | PortOrFd::Fd(_) => true, | |
99 | _ => false, | |
100 | } | |
101 | } | |
102 | } |