blob: c22f722419dff054aea8f699503ff2cb4abf7bbd [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::{fmt::Display, path::PathBuf};
#[derive(Debug, Serialize, Deserialize)]
enum SocketStatus {
NotPresent,
Present,
}
impl Display for SocketStatus {
fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
use SocketStatus::*;
write!(
out,
"{}",
match self {
NotPresent => "Not Present",
Present => "Present",
}
)
}
}
#[derive(Debug, Serialize, Deserialize)]
enum PidStatus {
NotPresent,
NotRunning { pid: Option<u32> },
Running { pid: u32 },
}
impl Display for PidStatus {
fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
use PidStatus::*;
write!(
out,
"{}",
match self {
NotPresent => "Not Present".to_owned(),
NotRunning { pid: Some(pid) } =>
format!("Present, but not running (last pid: {pid})"),
NotRunning { pid: None } => format!("Present, but not valid"),
Running { pid } => format!("Present and running (pid: {pid})"),
}
)
}
}
#[derive(Debug, Serialize, Deserialize)]
struct FileInfo<T> {
path: PathBuf,
status: T,
}
impl<T> FileInfo<T> {
fn new(path: PathBuf, status: T) -> Self {
Self { path, status }
}
}
impl<T: Display> Display for FileInfo<T> {
fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
writeln!(out, " Path: {}", self.path.display())?;
writeln!(out, " Status: {}", self.status)?;
Ok(())
}
}
/// Loads details about a daemon socket file and its associated pid file.
#[derive(Debug, Serialize, Deserialize)]
pub struct SocketDetails {
socket: FileInfo<SocketStatus>,
pid: FileInfo<PidStatus>,
}
impl Display for SocketDetails {
fn fmt(&self, out: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
writeln!(out, "Socket:")?;
write!(out, "{}", self.socket)?;
writeln!(out, "")?;
writeln!(out, "Pid:")?;
write!(out, "{}", self.pid)?;
Ok(())
}
}
impl SocketDetails {
/// Loads information about the socket based on the given path
pub fn new(socket_path: PathBuf) -> Self {
let pid_path = socket_path.with_extension("pid");
// ignore errors trying to read this and just treat them as "no pid"
let pid = std::fs::File::open(&pid_path)
.ok()
.and_then(|pid_file| serde_json::from_reader(pid_file).ok());
let pid_running = pid.and_then(check_if_running);
let socket_status = match socket_path.exists() {
true => SocketStatus::Present,
false => SocketStatus::NotPresent,
};
let pid_status = match (pid_path.exists(), pid_running) {
(true, Some(pid)) => PidStatus::Running { pid },
(true, None) => PidStatus::NotRunning { pid },
(false, _) => PidStatus::NotPresent,
};
let socket = FileInfo::new(socket_path, socket_status);
let pid = FileInfo::new(pid_path, pid_status);
SocketDetails { socket, pid }
}
pub fn get_running_pid(&self) -> Option<u32> {
if let PidStatus::Running { pid } = self.pid.status {
Some(pid)
} else {
None
}
}
}
fn check_if_running(pid: u32) -> Option<u32> {
let nix_pid = nix::unistd::Pid::from_raw(pid.try_into().ok()?);
nix::sys::signal::kill(nix_pid, None).ok()?;
Some(pid)
}