8000 feat: rely on command environment instead of PID file by augustoccesar · Pull Request #222 · mentimeter/linkup · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: rely on command environment instead of PID file #222

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 10 additions & 5 deletions linkup-cli/src/commands/health.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ use clap::crate_version;
use colored::Colorize;
use serde::Serialize;

use crate::{linkup_dir_path, local_config::LocalState, services, Result};
use crate::{
linkup_dir_path,
local_config::LocalState,
services::{self, find_service_pid, BackgroundService},
Result,
};

#[cfg(target_os = "macos")]
use super::local_dns;
Expand Down Expand Up @@ -98,7 +103,7 @@ impl BackgroudServices {
fn load(state: &LocalState) -> Self {
let mut managed_pids: Vec<services::Pid> = Vec::with_capacity(4);

let linkup_server = match services::LocalServer::new().running_pid() {
let linkup_server = match find_service_pid(services::LocalServer::ID) {
Some(pid) => {
managed_pids.push(pid);

Expand All @@ -108,7 +113,7 @@ impl BackgroudServices {
};

let cloudflared = if services::is_cloudflared_installed() {
match services::CloudflareTunnel::new().running_pid() {
match find_service_pid(services::CloudflareTunnel::ID) {
Some(pid) => {
managed_pids.push(pid);

Expand All @@ -123,7 +128,7 @@ impl BackgroudServices {
#[cfg(target_os = "macos")]
let dns_server =
if local_dns::is_installed(&crate::local_config::managed_domains(Some(state), &None)) {
match services::LocalDnsServer::new().running_pid() {
match find_service_pid(services::LocalDnsServer::ID) {
Some(pid) => {
managed_pids.push(pid);

Expand All @@ -146,7 +151,7 @@ impl BackgroudServices {
}

fn find_potential_orphan_processes(managed_pids: Vec<services::Pid>) -> Vec<OrphanProcess> {
let current_pid = services::get_current_process_pid();
let current_pid = sysinfo::get_current_pid().unwrap();
let mut orphans = Vec::new();

for (pid, process) in services::system().processes() {
Expand Down
7 changes: 0 additions & 7 deletions linkup-cli/src/commands/server.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
use crate::Result;
use linkup::MemoryStringStore;
use std::fs;
use tokio::select;

#[derive(clap::Args)]
pub struct Args {
#[arg(long)]
pidfile: String,

#[command(subcommand)]
server_kind: ServerKind,
}
Expand All @@ -28,9 +24,6 @@ pub enum ServerKind {
}

pub async fn server(args: &Args) -> Result<()> {
let pid = std::process::id();
fs::write(&args.pidfile, pid.to_string())?;

match &args.server_kind {
#[cfg_attr(not(target_os = "macos"), allow(unused_variables))]
ServerKind::LocalWorker { certs_dir } => {
Expand Down
7 changes: 4 additions & 3 deletions linkup-cli/src/commands/stop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use anyhow::Context;

use crate::env_files::clear_env_file;
use crate::local_config::LocalState;
use crate::services::{stop_service, BackgroundService};
use crate::{services, Result};

#[derive(clap::Args)]
Expand All @@ -31,10 +32,10 @@ pub fn stop(_args: &Args, clear_env: bool) -> Result<()> {
}
}

services::LocalServer::new().stop();
services::CloudflareTunnel::new().stop();
stop_service(services::LocalServer::ID);
stop_service(services::CloudflareTunnel::ID);
#[cfg(target_os = "macos")]
services::LocalDnsServer::new().stop();
stop_service(services::LocalDnsServer::ID);

println!("Stopped linkup");

Expand Down
45 changes: 33 additions & 12 deletions linkup-cli/src/services/cloudflare_tunnel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use url::Url;

use crate::{linkup_file_path, local_config::LocalState, worker_client::WorkerClient, Result};

use super::{get_running_pid, stop_pid_file, BackgroundService, Pid, PidError, Signal};
use super::{find_service_pid, BackgroundService, PidError};

#[derive(thiserror::Error, Debug)]
#[allow(dead_code)]
Expand Down Expand Up @@ -91,6 +91,7 @@ impl CloudflareTunnel {
.stdout(stdout_file)
.stderr(stderr_file)
.stdin(Stdio::null())
.env("LINKUP_SERVICE_ID", Self::ID)
.args([
"tunnel",
"--pidfile",
Expand All @@ -104,16 +105,6 @@ impl CloudflareTunnel {
Ok(tunnel_url)
}

pub fn stop(&self) {
log::debug!("Stopping {}", Self::NAME);

stop_pid_file(&self.pidfile_path, Signal::Interrupt);
}

pub fn running_pid(&self) -> Option<Pid> {
get_running_pid(&self.pidfile_path)
}

async fn dns_propagated(&self, tunnel_url: &Url) -> bool {
let mut opts = ResolverOpts::default();
opts.cache_size = 0; // Disable caching
Expand Down Expand Up @@ -152,6 +143,7 @@ impl CloudflareTunnel {
}

impl BackgroundService for CloudflareTunnel {
const ID: &str = "cloudflare-tunnel";
const NAME: &str = "Cloudflare Tunnel";

async fn run_with_progress(
Expand Down Expand Up @@ -179,7 +171,7 @@ impl BackgroundService for CloudflareTunnel {
return Err(Error::InvalidSessionName(state.linkup.session_name.clone()).into());
}

if self.running_pid().is_some() {
if find_service_pid(Self::ID).is_some() {
self.notify_update_with_details(
&status_sender,
super::RunStatus::Started,
Expand Down Expand Up @@ -359,3 +351,32 @@ fn create_config_yml(tunnel_id: &str) -> Result<(), Error> {

Ok(())
}

// Get the pid from a pidfile, but only return Some in case the pidfile is valid and the written pid on the file
// is running.
fn get_running_pid(file_path: &Path) -> Option<super::Pid> {
let pid = match get_pid(file_path) {
Ok(pid) => pid,
Err(_) => return None,
};

super::system().process(pid).map(|_| pid)
}

fn get_pid(file_path: &Path) -> Result<super::Pid, PidError> {
if let Err(e) = File::open(file_path) {
return Err(PidError::NoPidFile(e.to_string()));
}

match fs::read_to_string(file_path) {
Ok(content) => {
let pid_u32 = content
.trim()
.parse::<u32>()
.map_err(|e| PidError::BadPidFile(e.to_string()))?;

Ok(super::Pid::from_u32(pid_u32))
}
Err(e) => Err(PidError::BadPidFile(e.to_string())),
}
}
18 changes: 3 additions & 15 deletions linkup-cli/src/services/local_dns_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,18 @@ use anyhow::Context;

use crate::{linkup_file_path, local_config::LocalState, Result};

use super::{get_running_pid, stop_pid_file, BackgroundService, Pid, Signal};
use super::BackgroundService;

pub struct LocalDnsServer {
stdout_file_path: PathBuf,
stderr_file_path: PathBuf,
pidfile_path: PathBuf,
}

impl LocalDnsServer {
pub fn new() -> Self {
Self {
stdout_file_path: linkup_file_path("localdns-stdout"),
stderr_file_path: linkup_file_path("localdns-stderr"),
pidfile_path: linkup_file_path("localdns-pid"),
}
}

Expand All @@ -37,10 +35,9 @@ impl LocalDnsServer {
env::current_exe().context("Failed to get the current executable")?,
);
command.env("RUST_LOG", "debug");
command.env("LINKUP_SERVICE_ID", Self::ID);
command.args([
"server",
"--pidfile",
self.pidfile_path.to_str().unwrap(),
"dns",
"--session-name",
session_name,
Expand All @@ -57,19 +54,10 @@ impl LocalDnsServer {

Ok(())
}

pub fn stop(&self) {
log::debug!("Stopping {}", Self::NAME);

stop_pid_file(&self.pidfile_path, Signal::Interrupt);
}

pub fn running_pid(&self) -> Option<Pid> {
get_running_pid(&self.pidfile_path)
}
}

impl BackgroundService for LocalDnsServer {
const ID: &str = "linkup-local-dns-server";
const NAME: &str = "Local DNS server";

async fn run_with_progress(
Expand Down
18 changes: 3 additions & 15 deletions linkup-cli/src/services/local_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
worker_client, Result,
};

use super::{get_running_pid, stop_pid_file, BackgroundService, Pid, PidError, Signal};
use super::{BackgroundService, PidError};

#[derive(thiserror::Error, Debug)]
pub enum Error {
Expand All @@ -35,15 +35,13 @@ pub enum Error {
pub struct LocalServer {
stdout_file_path: PathBuf,
stderr_file_path: PathBuf,
pidfile_path: PathBuf,
}

impl LocalServer {
pub fn new() -> Self {
Self {
stdout_file_path: linkup_file_path("localserver-stdout"),
stderr_file_path: linkup_file_path("localserver-stderr"),
pidfile_path: linkup_file_path("localserver-pid"),
}
}

Expand All @@ -62,10 +60,9 @@ impl LocalServer {
env::current_exe().context("Failed to get the current executable")?,
);
command.env("RUST_LOG", "debug");
command.env("LINKUP_SERVICE_ID", Self::ID);
command.args([
"server",
"--pidfile",
self.pidfile_path.to_str().unwrap(),
"local-worker",
"--certs-dir",
linkup_certs_dir_path().to_str().unwrap(),
Expand All @@ -81,16 +78,6 @@ impl LocalServer {
Ok(())
}

pub fn stop(&self) {
log::debug!("Stopping {}", Self::NAME);

stop_pid_file(&self.pidfile_path, Signal::Interrupt);
}

pub fn running_pid(&self) -> Option<Pid> {
get_running_pid(&self.pidfile_path)
}

async fn reachable(&self) -> bool {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(1))
Expand All @@ -116,6 +103,7 @@ impl LocalServer {
}

impl BackgroundService for LocalServer {
const ID: &str = "linkup-local-server";
const NAME: &str = "Linkup local server";

async fn run_with_progress(
Expand Down
48 changes: 14 additions & 34 deletions linkup-cli/src/services/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use std::fs::{self, File};
use std::path::Path;
use std::{fmt::Display, sync};

use sysinfo::{get_current_pid, ProcessRefreshKind, RefreshKind, System};
use sysinfo::{ProcessRefreshKind, RefreshKind, System};
use thiserror::Error;

mod cloudflare_tunnel;
Expand Down Expand Up @@ -50,6 +48,7 @@ pub struct RunUpdate {
}

pub trait BackgroundService {
const ID: &str;
const NAME: &str;

async fn run_with_progress(
Expand Down Expand Up @@ -92,40 +91,25 @@ pub enum PidError {
BadPidFile(String),
}

fn get_pid(file_path: &Path) -> Result<Pid, PidError> {
if let Err(e) = File::open(file_path) {
return Err(PidError::NoPidFile(e.to_string()));
}

match fs::read_to_string(file_path) {
Ok(content) => {
let pid_u32 = content
.trim()
.parse::<u32>()
.map_err(|e| PidError::BadPidFile(e.to_string()))?;

Ok(Pid::from_u32(pid_u32))
pub fn find_service_pid(service_id: &str) -> Option<Pid> {
for (pid, process) in system().processes() {
if process
.environ()
.iter()
.any(|item| item.to_string_lossy() == format!("LINKUP_SERVICE_ID={service_id}"))
{
return Some(*pid);
}
Err(e) => Err(PidError::BadPidFile(e.to_string())),
}
}

// Get the pid from a pidfile, but only return Some in case the pidfile is valid and the written pid on the file
// is running.
pub fn get_running_pid(file_path: &Path) -> Option<Pid> {
let pid = match get_pid(file_path) {
Ok(pid) => pid,
Err(_) => return None,
};

system().process(pid).map(|_| pid)
None
}

pub fn stop_pid_file(pid_file: &Path, signal: Signal) {
if let Some(pid) = get_running_pid(pid_file) {
pub fn stop_service(service_id: &str) {
if let Some(pid) = find_service_pid(service_id) {
system()
.process(pid)
.map(|process| process.kill_with(signal));
.map(|process| process.kill_with(Signal::Interrupt));
}
}

Expand All @@ -134,7 +118,3 @@ pub fn system() -> System {
RefreshKind::nothing().with_processes(ProcessRefreshKind::everything()),
)
}

pub fn get_current_process_pid() -> Pid {
get_current_pid().unwrap()
}
0