mirror of
https://github.com/bytedream/docker4ssh.git
synced 2025-06-27 01:40:32 +02:00
Initial commit
This commit is contained in:
412
container/Cargo.lock
generated
Normal file
412
container/Cargo.lock
generated
Normal file
@ -0,0 +1,412 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docker4ssh"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"failure",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"structopt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"failure_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "failure_derive"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.27.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.130"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "structopt"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"lazy_static",
|
||||
"structopt-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "structopt-derive"
|
||||
version = "0.4.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
25
container/Cargo.toml
Normal file
25
container/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "docker4ssh"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["ByteDream"]
|
||||
repository = "https://github.com/ByteDream/docker4ssh"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[[bin]]
|
||||
name = "configure"
|
||||
path = "src/configure/main.rs"
|
||||
|
||||
[dependencies]
|
||||
failure = "0.1"
|
||||
log = "0.4"
|
||||
structopt = "0.3"
|
||||
serde = { version = "1.0", features = ["derive"]}
|
||||
serde_json = "1.0"
|
||||
serde_repr = "0.1"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
opt-level = "z"
|
||||
panic = "abort"
|
296
container/src/configure/cli/cli.rs
Normal file
296
container/src/configure/cli/cli.rs
Normal file
@ -0,0 +1,296 @@
|
||||
use std::fmt::{Debug, format};
|
||||
use std::net::TcpStream;
|
||||
use std::os::unix::process::ExitStatusExt;
|
||||
use std::process::{Command, ExitStatus};
|
||||
use std::time::SystemTime;
|
||||
use log::{info, warn};
|
||||
use structopt::StructOpt;
|
||||
use structopt::clap::AppSettings;
|
||||
use crate::configure::cli::parser;
|
||||
use crate::shared::api::api::API;
|
||||
use crate::shared::api::request;
|
||||
use crate::shared::api::request::{ConfigGetResponse, ConfigNetworkMode, ConfigPostRequest, ConfigRunLevel};
|
||||
|
||||
type Result<T> = std::result::Result<T, failure::Error>;
|
||||
|
||||
trait Execute {
|
||||
fn execute(self, api: &mut API) -> Result<()>;
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "configure",
|
||||
about = "A command line wrapper to control docker4ssh containers from within them",
|
||||
settings = &[AppSettings::ArgRequiredElseHelp]
|
||||
)]
|
||||
struct Opts {
|
||||
#[structopt(short, long, global = true, help = "Verbose output")]
|
||||
verbose: bool,
|
||||
|
||||
#[structopt(subcommand)]
|
||||
commands: Option<Root>
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "ping",
|
||||
about = "Ping the control socket"
|
||||
)]
|
||||
struct Ping {}
|
||||
|
||||
impl Execute for Ping {
|
||||
fn execute(self, api: &mut API) -> Result<()> {
|
||||
let start = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_nanos();
|
||||
let result = request::PingRequest::new().request(api)?;
|
||||
info!("Pong! Ping is {:.4}ms", ((result.received - start) as f64) / 1000.0 / 1000.0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "error",
|
||||
about = "Example error message sent from socket",
|
||||
)]
|
||||
struct Error {}
|
||||
|
||||
impl Execute for Error {
|
||||
fn execute(self, api: &mut API) -> Result<()> {
|
||||
request::ErrorRequest::new().request(api)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "info",
|
||||
about = "Shows information about the current container",
|
||||
)]
|
||||
struct Info {}
|
||||
|
||||
impl Execute for Info {
|
||||
fn execute(self, api: &mut API) -> Result<()> {
|
||||
let result = request::InfoRequest:: new().request(api)?;
|
||||
info!(concat!(
|
||||
"\tContainer ID: {}"
|
||||
), result.container_id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "config",
|
||||
about = "Get or set the behavior of the current container",
|
||||
settings = &[AppSettings::ArgRequiredElseHelp]
|
||||
)]
|
||||
struct Config {
|
||||
#[structopt(subcommand)]
|
||||
commands: Option<ConfigCommands>
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
enum ConfigCommands {
|
||||
Get(ConfigGet),
|
||||
Set(ConfigSet)
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "get",
|
||||
about = "Show the current container behavior"
|
||||
)]
|
||||
struct ConfigGet {}
|
||||
|
||||
impl Execute for ConfigGet {
|
||||
fn execute(self, api: &mut API) -> Result<()> {
|
||||
let response: ConfigGetResponse = request::ConfigGetRequest::new().request(api)?;
|
||||
|
||||
info!(concat!(
|
||||
"\tNetwork Mode: {}\n",
|
||||
"\tConfigurable: {}\n",
|
||||
"\tRun Level: {}\n",
|
||||
"\tStartup Information: {}\n",
|
||||
"\tExit After: {}\n",
|
||||
"\tKeep On Exit: {}"
|
||||
), response.network_mode, response.configurable, response.run_level, response.startup_information, response.exit_after, response.keep_on_exit);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "set",
|
||||
about = "Set the current container behavior",
|
||||
settings = &[AppSettings::ArgRequiredElseHelp]
|
||||
)]
|
||||
struct ConfigSet {
|
||||
#[structopt(long, help = "If the container should keep running even after the user exits", parse(try_from_str = parser::parse_network_mode))]
|
||||
network_mode: Option<ConfigNetworkMode>,
|
||||
|
||||
#[structopt(long, help = "If the container should be configurable from within")]
|
||||
configurable: Option<bool>,
|
||||
|
||||
#[structopt(long, help = "Set the container stop behavior", parse(try_from_str = parser::parse_config_run_level))]
|
||||
run_level: Option<ConfigRunLevel>,
|
||||
|
||||
#[structopt(long, help = "If information about the container should be shown when a user connects")]
|
||||
startup_information: Option<bool>,
|
||||
|
||||
#[structopt(long, help = "Process name after which the container should exit")]
|
||||
exit_after: Option<String>,
|
||||
|
||||
#[structopt(long, help = "If the container should be not deleted after exit")]
|
||||
keep_on_exit: Option<bool>
|
||||
}
|
||||
|
||||
impl Execute for ConfigSet {
|
||||
fn execute(self, api: &mut API) -> Result<()> {
|
||||
let mut request = request::ConfigPostRequest::new();
|
||||
|
||||
if let Some(exit_after) = self.exit_after.as_ref() {
|
||||
let program_runs = Command::new("pidof")
|
||||
.arg("-s")
|
||||
.arg(exit_after).status().unwrap().success();
|
||||
if !program_runs {
|
||||
warn!("NOTE: There is currently no process running with the name '{}'", exit_after);
|
||||
}
|
||||
}
|
||||
|
||||
request.body.network_mode = self.network_mode;
|
||||
request.body.configurable = self.configurable;
|
||||
request.body.run_level = self.run_level;
|
||||
request.body.startup_information = self.startup_information;
|
||||
request.body.exit_after = self.exit_after;
|
||||
request.body.keep_on_exit = self.keep_on_exit;
|
||||
|
||||
request.request(api)?;
|
||||
|
||||
if let Some(keep_on_exit) = self.keep_on_exit {
|
||||
if keep_on_exit {
|
||||
if let Ok(auth) = request::AuthGetRequest::new().request(api) {
|
||||
info!("To reconnect to this container, use the user '{}' for the ssh connection", &auth.user)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "auth",
|
||||
about = "Get or set the container authentication",
|
||||
settings = &[AppSettings::ArgRequiredElseHelp]
|
||||
)]
|
||||
struct Auth {
|
||||
#[structopt(subcommand)]
|
||||
commands: Option<AuthCommands>
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
enum AuthCommands {
|
||||
Get(AuthGet),
|
||||
Set(AuthSet)
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "get",
|
||||
about = "Show the current username used for ssh authentication and if a password is set"
|
||||
)]
|
||||
struct AuthGet {}
|
||||
|
||||
impl Execute for AuthGet {
|
||||
fn execute(self, api: &mut API) -> Result<()> {
|
||||
let response = request::AuthGetRequest::new().request(api)?;
|
||||
|
||||
info!(concat!(
|
||||
"\tUser: {}\n",
|
||||
"\tHas Password: {}\n"
|
||||
), response.user, response.has_password);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(
|
||||
name = "set",
|
||||
about = "Set the authentication settings",
|
||||
settings = &[AppSettings::ArgRequiredElseHelp]
|
||||
)]
|
||||
struct AuthSet {
|
||||
#[structopt(long, help = "The container username")]
|
||||
user: Option<String>,
|
||||
#[structopt(long, help = "The container password. If empty, the authentication gets removed")]
|
||||
password: Option<String>
|
||||
}
|
||||
|
||||
impl Execute for AuthSet {
|
||||
fn execute(self, api: &mut API) -> Result<()> {
|
||||
let mut request = request::AuthPostRequest::new();
|
||||
request.body.user = self.user;
|
||||
request.body.password = self.password.clone();
|
||||
|
||||
request.request(api)?;
|
||||
|
||||
if let Some(password) = self.password {
|
||||
if password == "" {
|
||||
warn!("No password was specified so the authentication got deleted")
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
enum Root {
|
||||
Auth(Auth),
|
||||
Error(Error),
|
||||
Info(Info),
|
||||
Ping(Ping),
|
||||
Config(Config)
|
||||
}
|
||||
|
||||
pub fn cli(route: String) {
|
||||
if let Some(subcommand) = Opts::from_args().commands {
|
||||
let mut result: Result<()> = Ok(());
|
||||
let mut api = API::new(route, String::new());
|
||||
match subcommand {
|
||||
Root::Auth(auth) => {
|
||||
if let Some(subsubcommand) = auth.commands {
|
||||
match subsubcommand {
|
||||
AuthCommands::Get(auth_get) => {
|
||||
result = auth_get.execute(&mut api)
|
||||
}
|
||||
AuthCommands::Set(auth_set) => {
|
||||
result = auth_set.execute(&mut api)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Root::Error(error) => result = error.execute(&mut api),
|
||||
Root::Info(info) => result = info.execute(&mut api),
|
||||
Root::Ping(ping) => result = ping.execute(&mut api),
|
||||
Root::Config(config) => {
|
||||
if let Some(subsubcommand) = config.commands {
|
||||
match subsubcommand {
|
||||
ConfigCommands::Get(config_get) => {
|
||||
result = config_get.execute(&mut api)
|
||||
}
|
||||
ConfigCommands::Set(config_set) => {
|
||||
result = config_set.execute(&mut api)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if result.is_err() {
|
||||
log::error!("{}", result.err().unwrap().to_string())
|
||||
}
|
||||
}
|
||||
}
|
4
container/src/configure/cli/mod.rs
Normal file
4
container/src/configure/cli/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
mod cli;
|
||||
pub mod parser;
|
||||
|
||||
pub use cli::cli;
|
23
container/src/configure/cli/parser.rs
Normal file
23
container/src/configure/cli/parser.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use std::f32::consts::E;
|
||||
use std::fmt::format;
|
||||
use crate::shared::api::request::{ConfigNetworkMode, ConfigRunLevel};
|
||||
|
||||
pub fn parse_network_mode(src: &str) -> Result<ConfigNetworkMode, String> {
|
||||
match String::from(src).to_lowercase().as_str() {
|
||||
"off" | "1" => Ok(ConfigNetworkMode::Off),
|
||||
"full" | "2" => Ok(ConfigNetworkMode::Full),
|
||||
"host" | "3" => Ok(ConfigNetworkMode::Host),
|
||||
"docker" | "4" => Ok(ConfigNetworkMode::Docker),
|
||||
"none" | "5" => Ok(ConfigNetworkMode::None),
|
||||
_ => Err(format!("'{} is not a valid network mode. Choose from 'off', 'full', 'host', 'docker', 'none'", src))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_config_run_level(src: &str) -> Result<ConfigRunLevel, String> {
|
||||
match String::from(src).to_lowercase().as_str() {
|
||||
"user" | "1" => Ok(ConfigRunLevel::User),
|
||||
"container" | "2" => Ok(ConfigRunLevel::Container),
|
||||
"forever" | "3" => Ok(ConfigRunLevel::Forever),
|
||||
_ => Err(format!("'{}' is not a valid run level. Choose from: 'user', 'container', 'forever'", src))
|
||||
}
|
||||
}
|
19
container/src/configure/main.rs
Normal file
19
container/src/configure/main.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use std::fs;
|
||||
use std::net::TcpStream;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::process::exit;
|
||||
use log::{LevelFilter, trace, warn, info, error};
|
||||
use docker4ssh::configure::cli;
|
||||
use docker4ssh::shared::logging::init_logger;
|
||||
|
||||
fn main() {
|
||||
init_logger(LevelFilter::Debug);
|
||||
|
||||
match fs::read_to_string("/etc/docker4ssh") {
|
||||
Ok(route) => cli(route),
|
||||
Err(e) => {
|
||||
error!("Failed to read /etc/docker4ssh: {}", e.to_string());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
3
container/src/configure/mod.rs
Normal file
3
container/src/configure/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod cli;
|
||||
|
||||
pub use cli::cli;
|
2
container/src/lib.rs
Normal file
2
container/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod shared;
|
||||
pub mod configure;
|
157
container/src/shared/api/api.rs
Normal file
157
container/src/shared/api/api.rs
Normal file
@ -0,0 +1,157 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use log::Level::Error;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, failure::Error>;
|
||||
|
||||
pub struct API {
|
||||
route: String,
|
||||
host: String,
|
||||
}
|
||||
|
||||
impl API {
|
||||
pub const fn new(route: String, host: String) -> Self {
|
||||
API {
|
||||
route,
|
||||
host,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_connection(&mut self) -> Result<TcpStream> {
|
||||
match TcpStream::connect(&self.route) {
|
||||
Ok(stream) => Ok(stream),
|
||||
Err(e) => Err(failure::format_err!("Failed to connect to {}: {}", self.route, e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&mut self, request: &Request) -> Result<APIResult> {
|
||||
let mut connection = self.new_connection()?;
|
||||
|
||||
connection.write_all(request.as_string().as_bytes())?;
|
||||
let mut buf: String = String::new();
|
||||
connection.read_to_string(&mut buf).map_err(|e| failure::err_msg(e.to_string()))?;
|
||||
Ok(APIResult::new(request, buf))
|
||||
}
|
||||
|
||||
pub fn request_with_err(&mut self, request: &Request) -> Result<APIResult> {
|
||||
let result = self.request(request)?;
|
||||
if result.result_code >= 400 {
|
||||
let err: APIError = result.body()?;
|
||||
Err(failure::err_msg(format!("Error {}: {}", result.result_code, err.message)))
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct APIError {
|
||||
message: String
|
||||
}
|
||||
|
||||
pub struct APIResult {
|
||||
// TODO: Store the whole request instead of only the path
|
||||
request_path: String,
|
||||
|
||||
result_code: i32,
|
||||
result_body: String
|
||||
}
|
||||
|
||||
impl APIResult {
|
||||
fn new(request: &Request, raw_response: String) -> Self {
|
||||
APIResult {
|
||||
request_path: request.path.clone(),
|
||||
|
||||
// TODO: Parse http body better
|
||||
result_code: raw_response[9..12].parse().unwrap(),
|
||||
result_body: raw_response.split_once("\r\n\r\n").unwrap().1.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(self) -> String {
|
||||
self.request_path
|
||||
}
|
||||
|
||||
pub fn code(&self) -> i32 {
|
||||
return self.result_code
|
||||
}
|
||||
|
||||
pub fn has_body(&self) -> bool {
|
||||
self.result_body.len() > 0
|
||||
}
|
||||
|
||||
pub fn body<'a, T: Deserialize<'a>>(&'a self) -> Result<T> {
|
||||
let result: T = serde_json::from_str(&self.result_body).map_err(|e| {
|
||||
// checks if the error has a body and if so, return it
|
||||
if self.has_body() {
|
||||
let error: APIError = serde_json::from_str(&self.result_body).unwrap_or_else(|ee| {
|
||||
APIError{message: format!("could not deserialize response: {}", e.to_string())}
|
||||
});
|
||||
failure::format_err!("Failed to call '{}': {}", self.request_path, error.message)
|
||||
} else {
|
||||
failure::format_err!("Failed to call '{}': {}", self.request_path, e.to_string())
|
||||
}
|
||||
})?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Method {
|
||||
GET,
|
||||
POST
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
method: Method,
|
||||
path: String,
|
||||
headers: HashMap<String, String>,
|
||||
body: String,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub fn new(path: String) -> Self {
|
||||
Request{
|
||||
method: Method::GET,
|
||||
path,
|
||||
headers: Default::default(),
|
||||
body: "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_method(&mut self, method: Method) -> &Self {
|
||||
self.method = method;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_path(&mut self, path: String) -> &Self {
|
||||
self.path = path;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_header(&mut self, field: &str, value: String) -> &Self {
|
||||
self.headers.insert(String::from(field), value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_body(&mut self, body: String) -> &Self {
|
||||
self.body = body;
|
||||
self.headers.insert("Content-Length".to_string(), self.body.len().to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> String {
|
||||
let method;
|
||||
match self.method {
|
||||
Method::GET => method = "GET",
|
||||
Method::POST => method = "POST"
|
||||
}
|
||||
|
||||
let headers_as_string = self.headers.iter().map(|f| format!("{}: {}", f.0, f.1)).collect::<String>();
|
||||
|
||||
return format!("{} {} HTTP/1.0\r\n\
|
||||
{}\r\n\r\n\
|
||||
{}\r\n", method, self.path, headers_as_string, self.body)
|
||||
}
|
||||
}
|
2
container/src/shared/api/mod.rs
Normal file
2
container/src/shared/api/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod request;
|
||||
pub mod api;
|
220
container/src/shared/api/request.rs
Normal file
220
container/src/shared/api/request.rs
Normal file
@ -0,0 +1,220 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::de::Unexpected::Str;
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
use crate::shared::api::api::{API, Method, Request, Result};
|
||||
use crate::shared::api::api::Method::POST;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PingResponse {
|
||||
pub received: u128
|
||||
}
|
||||
|
||||
pub struct PingRequest {
|
||||
request: Request
|
||||
}
|
||||
|
||||
impl PingRequest {
|
||||
pub fn new() -> Self {
|
||||
PingRequest {
|
||||
request: Request::new(String::from("/ping"))
|
||||
}
|
||||
}
|
||||
pub fn request(&self, api: &mut API) -> Result<PingResponse> {
|
||||
let result: PingResponse = api.request_with_err(&self.request)?.body()?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ErrorRequest {
|
||||
request: Request
|
||||
}
|
||||
|
||||
impl ErrorRequest {
|
||||
pub fn new() -> Self {
|
||||
ErrorRequest {
|
||||
request: Request::new(String::from("/error"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&self, api: &mut API) -> Result<()> {
|
||||
api.request_with_err(&self.request)?.body()?;
|
||||
// should never call Ok
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct InfoResponse {
|
||||
pub container_id: String
|
||||
}
|
||||
|
||||
pub struct InfoRequest {
|
||||
request: Request
|
||||
}
|
||||
|
||||
impl InfoRequest {
|
||||
pub fn new() -> Self {
|
||||
InfoRequest{
|
||||
request: Request::new(String::from("/info"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&self, api: &mut API) -> Result<InfoResponse> {
|
||||
let result: InfoResponse = api.request_with_err(&self.request)?.body()?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum ConfigRunLevel {
|
||||
User = 1,
|
||||
Container = 2,
|
||||
Forever = 3
|
||||
}
|
||||
|
||||
impl Display for ConfigRunLevel {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum ConfigNetworkMode {
|
||||
Off = 1,
|
||||
Full = 2,
|
||||
Host = 3,
|
||||
Docker = 4,
|
||||
None = 5
|
||||
}
|
||||
|
||||
impl Display for ConfigNetworkMode {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ConfigGetResponse {
|
||||
pub network_mode: ConfigNetworkMode,
|
||||
pub configurable: bool,
|
||||
pub run_level: ConfigRunLevel,
|
||||
pub startup_information: bool,
|
||||
pub exit_after: String,
|
||||
pub keep_on_exit: bool
|
||||
}
|
||||
|
||||
pub struct ConfigGetRequest {
|
||||
request: Request
|
||||
}
|
||||
|
||||
impl ConfigGetRequest {
|
||||
pub fn new() -> ConfigGetRequest {
|
||||
ConfigGetRequest{
|
||||
request: Request::new(String::from("/config"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&self, api: &mut API) -> Result<ConfigGetResponse> {
|
||||
let result: ConfigGetResponse = api.request_with_err(&self.request)?.body()?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ConfigPostBody {
|
||||
pub network_mode: Option<ConfigNetworkMode>,
|
||||
pub configurable: Option<bool>,
|
||||
pub run_level: Option<ConfigRunLevel>,
|
||||
pub startup_information: Option<bool>,
|
||||
pub exit_after: Option<String>,
|
||||
pub keep_on_exit: Option<bool>
|
||||
}
|
||||
|
||||
pub struct ConfigPostRequest {
|
||||
request: Request,
|
||||
pub body: ConfigPostBody
|
||||
}
|
||||
|
||||
impl ConfigPostRequest {
|
||||
pub fn new() -> ConfigPostRequest {
|
||||
let mut request = Request::new(String::from("/config"));
|
||||
request.set_method(Method::POST);
|
||||
|
||||
ConfigPostRequest {
|
||||
request,
|
||||
body: ConfigPostBody{
|
||||
network_mode: None,
|
||||
configurable: None,
|
||||
run_level: None,
|
||||
startup_information: None,
|
||||
exit_after: None,
|
||||
keep_on_exit: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&mut self, api: &mut API) -> Result<()> {
|
||||
self.request.set_body(serde_json::to_string(&self.body)?);
|
||||
api.request_with_err(&self.request)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct AuthGetResponse {
|
||||
pub user: String,
|
||||
pub has_password: bool
|
||||
}
|
||||
|
||||
pub struct AuthGetRequest {
|
||||
request: Request
|
||||
}
|
||||
|
||||
impl AuthGetRequest {
|
||||
pub fn new() -> AuthGetRequest {
|
||||
AuthGetRequest{
|
||||
request: Request::new(String::from("/auth"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&self, api: &mut API) -> Result<AuthGetResponse> {
|
||||
let result: AuthGetResponse = api.request_with_err(&self.request)?.body()?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct AuthPostBody {
|
||||
pub user: Option<String>,
|
||||
pub password: Option<String>
|
||||
}
|
||||
|
||||
pub struct AuthPostRequest {
|
||||
request: Request,
|
||||
pub body: AuthPostBody
|
||||
}
|
||||
|
||||
impl AuthPostRequest {
|
||||
pub fn new() -> AuthPostRequest {
|
||||
let mut request = Request::new(String::from("/auth"));
|
||||
request.set_method(POST);
|
||||
|
||||
AuthPostRequest {
|
||||
request,
|
||||
body: AuthPostBody{
|
||||
user: None,
|
||||
password: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&mut self, api: &mut API) -> Result<()> {
|
||||
self.request.set_body(serde_json::to_string(&self.body)?);
|
||||
api.request_with_err(&self.request)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
19
container/src/shared/logging/logger.rs
Normal file
19
container/src/shared/logging/logger.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use log::{info, Metadata, Record};
|
||||
|
||||
pub struct Logger;
|
||||
|
||||
impl log::Log for Logger {
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
println!("{}", record.args().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
11
container/src/shared/logging/mod.rs
Normal file
11
container/src/shared/logging/mod.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use log::{LevelFilter, SetLoggerError};
|
||||
|
||||
pub mod logger;
|
||||
|
||||
pub use logger::Logger;
|
||||
|
||||
static LOGGER: Logger = Logger;
|
||||
|
||||
pub fn init_logger(level: LevelFilter) -> Result<(), SetLoggerError> {
|
||||
log::set_logger(&Logger).map(|()| log::set_max_level(level))
|
||||
}
|
2
container/src/shared/mod.rs
Normal file
2
container/src/shared/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod api;
|
||||
pub mod logging;
|
Reference in New Issue
Block a user