cli: show service status in tunnel log

Fixes #183714
This commit is contained in:
Connor Peet
2023-06-15 14:11:03 -07:00
parent 979ae3940a
commit 8a74ad8ff5
10 changed files with 80 additions and 29 deletions

View File

@@ -6,7 +6,7 @@
use std::{fmt, path::PathBuf};
use crate::{constants, log, options, tunnels::code_server::CodeServerArgs};
use clap::{ValueEnum, Args, Parser, Subcommand};
use clap::{Args, Parser, Subcommand, ValueEnum};
use const_format::concatcp;
const CLI_NAME: &str = concatcp!(constants::PRODUCT_NAME_LONG, " CLI");

View File

@@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/
use async_trait::async_trait;
use base64::{Engine as _, engine::general_purpose as b64};
use base64::{engine::general_purpose as b64, Engine as _};
use serde::Serialize;
use sha2::{Digest, Sha256};
use std::{str::FromStr, time::Duration};
use sysinfo::Pid;
@@ -247,8 +248,14 @@ pub async fn kill(ctx: CommandContext) -> Result<i32, AnyError> {
.map_err(|e| e.into())
}
#[derive(Serialize)]
pub struct StatusOutput {
pub tunnel: Option<protocol::singleton::TunnelState>,
pub service_installed: bool,
}
pub async fn status(ctx: CommandContext) -> Result<i32, AnyError> {
let status = do_single_rpc_call::<_, protocol::singleton::Status>(
let tunnel_status = do_single_rpc_call::<_, protocol::singleton::Status>(
&ctx.paths.tunnel_lockfile(),
ctx.log.clone(),
protocol::singleton::METHOD_STATUS,
@@ -256,17 +263,24 @@ pub async fn status(ctx: CommandContext) -> Result<i32, AnyError> {
)
.await;
match status {
Err(CodeError::NoRunningTunnel) => {
ctx.log.result(CodeError::NoRunningTunnel.to_string());
Ok(1)
}
Err(e) => Err(e.into()),
Ok(s) => {
ctx.log.result(serde_json::to_string(&s).unwrap());
Ok(0)
}
}
let service_installed = create_service_manager(ctx.log.clone(), &ctx.paths)
.is_installed()
.await
.unwrap_or(false);
ctx.log.result(
serde_json::to_string(&StatusOutput {
service_installed,
tunnel: match tunnel_status {
Ok(s) => Some(s.tunnel),
Err(CodeError::NoRunningTunnel) => None,
Err(e) => return Err(e.into()),
},
})
.unwrap(),
);
Ok(0)
}
/// Removes unused servers.

View File

@@ -18,8 +18,8 @@ pub mod tunnels;
pub mod update_service;
pub mod util;
mod download_cache;
mod async_pipe;
mod download_cache;
mod json_rpc;
mod msgpack_rpc;
mod rpc;

View File

@@ -41,6 +41,9 @@ pub trait ServiceManager {
/// Show logs from the running service to standard out.
async fn show_logs(&self) -> Result<(), AnyError>;
/// Gets whether the tunnel service is installed.
async fn is_installed(&self) -> Result<bool, AnyError>;
/// Unregisters the current executable as a service.
async fn unregister(&self) -> Result<(), AnyError>;
}

View File

@@ -40,7 +40,7 @@ impl SystemdService {
async fn connect() -> Result<Connection, AnyError> {
let connection = Connection::session()
.await
.map_err(|e| wrap(e, "error creating dbus session"))?;
.map_err(|e| wrap(e, "Error creating dbus session. This command uses systemd for managing services, you should check that systemd is installed and running as a user. If it's already installed, you may need to:\n\n- Install the `dbus-user-session` package and reboot\n- Start the user dbus session with `systemctl --user enable dbus --now`. \n\nThe error encountered was"))?;
Ok(connection)
}
@@ -113,6 +113,20 @@ impl ServiceManager for SystemdService {
Ok(())
}
async fn is_installed(&self) -> Result<bool, AnyError> {
let connection = SystemdService::connect().await?;
let proxy = SystemdService::proxy(&connection).await?;
let state = proxy
.get_unit_file_state(SystemdService::service_name_string())
.await;
if let Ok(s) = state {
Ok(s == "enabled")
} else {
Ok(false)
}
}
async fn run(
self,
launcher_paths: crate::state::LauncherPaths,
@@ -219,6 +233,8 @@ trait SystemdManagerDbus {
force: bool,
) -> zbus::Result<(bool, Vec<(String, String, String)>)>;
fn get_unit_file_state(&self, file: String) -> zbus::Result<String>;
fn link_unit_files(
&self,
files: Vec<String>,

View File

@@ -75,6 +75,11 @@ impl ServiceManager for LaunchdService {
handle.run_service(self.log, launcher_paths).await
}
async fn is_installed(&self) -> Result<bool, AnyError> {
let cmd = capture_command_and_check_status("launchctl", &["list"]).await?;
Ok(String::from_utf8_lossy(&cmd.stdout).contains(&get_service_label()))
}
async fn unregister(&self) -> Result<(), crate::util::errors::AnyError> {
let service_file = get_service_file_path()?;

View File

@@ -114,6 +114,11 @@ impl CliServiceManager for WindowsService {
Ok(())
}
async fn is_installed(&self) -> Result<bool, AnyError> {
let key = WindowsService::open_key()?;
Ok(key.get_raw_value(TUNNEL_ACTIVITY_NAME).is_ok())
}
async fn unregister(&self) -> Result<(), AnyError> {
let key = WindowsService::open_key()?;
key.delete_value(TUNNEL_ACTIVITY_NAME)

View File

@@ -6,7 +6,7 @@ use crate::util::errors::{wrap, WrappedError};
use flate2::read::GzDecoder;
use std::fs;
use std::io::{Seek, SeekFrom};
use std::io::Seek;
use std::path::{Path, PathBuf};
use tar::Archive;
@@ -65,7 +65,7 @@ where
// reset since skip logic read the tar already:
tar_gz
.seek(SeekFrom::Start(0))
.rewind()
.map_err(|e| wrap(e, "error resetting seek position"))?;
let tar = GzDecoder::new(tar_gz);

View File

@@ -88,8 +88,7 @@ where
use std::io::Read;
use std::os::unix::ffi::OsStringExt;
if matches!(file.unix_mode(), Some(mode) if mode & (S_IFLNK as u32) == (S_IFLNK as u32))
{
if matches!(file.unix_mode(), Some(mode) if mode & S_IFLNK == S_IFLNK) {
let mut link_to = Vec::new();
file.read_to_end(&mut link_to).map_err(|e| {
wrap(