:%s/daemon/service/g

This commit is contained in:
Albert Armea 2025-12-29 12:52:03 -05:00
parent c3f3770ea6
commit f5d7d69578
25 changed files with 112 additions and 112 deletions

View file

@ -3,7 +3,7 @@
config_version = 1 config_version = 1
[daemon] [service]
# Uncomment to customize paths # Uncomment to customize paths
# socket_path = "/run/shepherdd/shepherdd.sock" # socket_path = "/run/shepherdd/shepherdd.sock"
# log_dir = "/var/log/shepherdd" # log_dir = "/var/log/shepherdd"
@ -15,24 +15,24 @@ default_max_run_seconds = 3600
# Global volume restrictions (optional) # Global volume restrictions (optional)
# These apply when no entry-specific restrictions are defined # These apply when no entry-specific restrictions are defined
[daemon.volume] [service.volume]
max_volume = 80 # Maximum volume percentage (0-100) max_volume = 80 # Maximum volume percentage (0-100)
# min_volume = 20 # Minimum volume percentage (0-100) # min_volume = 20 # Minimum volume percentage (0-100)
allow_mute = true # Whether mute toggle is allowed allow_mute = true # Whether mute toggle is allowed
allow_change = true # Whether volume changes are allowed at all allow_change = true # Whether volume changes are allowed at all
# Default warning thresholds # Default warning thresholds
[[daemon.default_warnings]] [[service.default_warnings]]
seconds_before = 300 seconds_before = 300
severity = "info" severity = "info"
message = "5 minutes remaining" message = "5 minutes remaining"
[[daemon.default_warnings]] [[service.default_warnings]]
seconds_before = 60 seconds_before = 60
severity = "warn" severity = "warn"
message = "1 minute remaining!" message = "1 minute remaining!"
[[daemon.default_warnings]] [[service.default_warnings]]
seconds_before = 10 seconds_before = 10
severity = "critical" severity = "critical"
message = "10 seconds remaining!" message = "10 seconds remaining!"
@ -232,7 +232,7 @@ days = "weekends"
start = "10:00" start = "10:00"
end = "20:00" end = "20:00"
# No [entries.limits] section - uses daemon defaults # No [entries.limits] section - uses service defaults
# Omitting limits entirely uses default_max_run_seconds # Omitting limits entirely uses default_max_run_seconds
## === Media === ## === Media ===

View file

@ -100,7 +100,7 @@ pub enum ErrorCode {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")] #[serde(tag = "type", rename_all = "snake_case")]
pub enum Command { pub enum Command {
/// Get current daemon state /// Get current service state
GetState, GetState,
/// List available entries /// List available entries
@ -160,7 +160,7 @@ pub enum Command {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")] #[serde(tag = "type", rename_all = "snake_case")]
pub enum ResponsePayload { pub enum ResponsePayload {
State(crate::DaemonStateSnapshot), State(crate::ServiceStateSnapshot),
Entries(Vec<crate::EntryView>), Entries(Vec<crate::EntryView>),
LaunchApproved { LaunchApproved {
session_id: shepherd_util::SessionId, session_id: shepherd_util::SessionId,
@ -234,7 +234,7 @@ mod tests {
fn response_serialization() { fn response_serialization() {
let resp = Response::success( let resp = Response::success(
1, 1,
ResponsePayload::State(crate::DaemonStateSnapshot { ResponsePayload::State(crate::ServiceStateSnapshot {
api_version: API_VERSION, api_version: API_VERSION,
policy_loaded: true, policy_loaded: true,
current_session: None, current_session: None,

View file

@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use shepherd_util::{EntryId, SessionId}; use shepherd_util::{EntryId, SessionId};
use std::time::Duration; use std::time::Duration;
use crate::{DaemonStateSnapshot, SessionEndReason, WarningSeverity, API_VERSION}; use crate::{ServiceStateSnapshot, SessionEndReason, WarningSeverity, API_VERSION};
/// Event envelope /// Event envelope
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -25,12 +25,12 @@ impl Event {
} }
} }
/// All possible events from daemon to clients /// All possible events from the service to clients
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")] #[serde(tag = "type", rename_all = "snake_case")]
pub enum EventPayload { pub enum EventPayload {
/// Full state snapshot (sent on subscribe and major changes) /// Full state snapshot (sent on subscribe and major changes)
StateChanged(DaemonStateSnapshot), StateChanged(ServiceStateSnapshot),
/// Session has started /// Session has started
SessionStarted { SessionStarted {
@ -80,7 +80,7 @@ pub enum EventPayload {
muted: bool, muted: bool,
}, },
/// Daemon is shutting down /// Service is shutting down
Shutdown, Shutdown,
/// Audit event (for admin clients) /// Audit event (for admin clients)

View file

@ -1,9 +1,9 @@
//! Protocol types for shepherdd IPC //! Protocol types for shepherdd IPC
//! //!
//! This crate defines the stable API between the daemon and clients: //! This crate defines the stable API between shepherdd and clients:
//! - Commands (requests from clients) //! - Commands (requests from clients)
//! - Responses //! - Responses
//! - Events (daemon -> clients) //! - Events (service -> clients)
//! - Versioning //! - Versioning
mod commands; mod commands;

View file

@ -155,8 +155,8 @@ pub enum SessionEndReason {
ProcessExited { exit_code: Option<i32> }, ProcessExited { exit_code: Option<i32> },
/// Policy change terminated session /// Policy change terminated session
PolicyStop, PolicyStop,
/// Daemon shutdown /// Service shutdown
DaemonShutdown, ServiceShutdown,
/// Launch failed /// Launch failed
LaunchFailed { error: String }, LaunchFailed { error: String },
} }
@ -187,9 +187,9 @@ pub struct SessionInfo {
pub warnings_issued: Vec<u64>, pub warnings_issued: Vec<u64>,
} }
/// Full daemon state snapshot /// Full service state snapshot
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DaemonStateSnapshot { pub struct ServiceStateSnapshot {
pub api_version: u32, pub api_version: u32,
pub policy_loaded: bool, pub policy_loaded: bool,
pub current_session: Option<SessionInfo>, pub current_session: Option<SessionInfo>,

View file

@ -1,6 +1,6 @@
//! Validated policy structures //! Validated policy structures
use crate::schema::{RawConfig, RawDays, RawEntry, RawEntryKind, RawVolumeConfig, RawWarningThreshold}; use crate::schema::{RawConfig, RawDays, RawEntry, RawEntryKind, RawVolumeConfig, RawServiceConfig, RawWarningThreshold};
use crate::validation::{parse_days, parse_time}; use crate::validation::{parse_days, parse_time};
use shepherd_api::{EntryKind, WarningSeverity, WarningThreshold}; use shepherd_api::{EntryKind, WarningSeverity, WarningThreshold};
use shepherd_util::{DaysOfWeek, EntryId, TimeWindow, WallClock}; use shepherd_util::{DaysOfWeek, EntryId, TimeWindow, WallClock};
@ -11,8 +11,8 @@ use std::time::Duration;
/// Validated policy ready for use by the core engine /// Validated policy ready for use by the core engine
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Policy { pub struct Policy {
/// Daemon configuration /// Service configuration
pub daemon: DaemonConfig, pub service: ServiceConfig,
/// Validated entries /// Validated entries
pub entries: Vec<Entry>, pub entries: Vec<Entry>,
@ -31,7 +31,7 @@ impl Policy {
/// Convert from raw config (after validation) /// Convert from raw config (after validation)
pub fn from_raw(raw: RawConfig) -> Self { pub fn from_raw(raw: RawConfig) -> Self {
let default_warnings = raw let default_warnings = raw
.daemon .service
.default_warnings .default_warnings
.clone() .clone()
.map(|w| w.into_iter().map(convert_warning).collect()) .map(|w| w.into_iter().map(convert_warning).collect())
@ -39,13 +39,13 @@ impl Policy {
// 0 means unlimited, None means use 1 hour default // 0 means unlimited, None means use 1 hour default
let default_max_run = raw let default_max_run = raw
.daemon .service
.default_max_run_seconds .default_max_run_seconds
.map(seconds_to_duration_or_unlimited) .map(seconds_to_duration_or_unlimited)
.unwrap_or(Some(Duration::from_secs(3600))); // 1 hour default .unwrap_or(Some(Duration::from_secs(3600))); // 1 hour default
let global_volume = raw let global_volume = raw
.daemon .service
.volume .volume
.as_ref() .as_ref()
.map(convert_volume_config) .map(convert_volume_config)
@ -58,7 +58,7 @@ impl Policy {
.collect(); .collect();
Self { Self {
daemon: DaemonConfig::from_raw(raw.daemon), service: ServiceConfig::from_raw(raw.service),
entries, entries,
default_warnings, default_warnings,
default_max_run, default_max_run,
@ -72,16 +72,16 @@ impl Policy {
} }
} }
/// Daemon configuration /// Service configuration
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DaemonConfig { pub struct ServiceConfig {
pub socket_path: PathBuf, pub socket_path: PathBuf,
pub log_dir: PathBuf, pub log_dir: PathBuf,
pub data_dir: PathBuf, pub data_dir: PathBuf,
} }
impl DaemonConfig { impl ServiceConfig {
fn from_raw(raw: crate::schema::RawDaemonConfig) -> Self { fn from_raw(raw: RawServiceConfig) -> Self {
Self { Self {
socket_path: raw socket_path: raw
.socket_path .socket_path
@ -96,7 +96,7 @@ impl DaemonConfig {
} }
} }
impl Default for DaemonConfig { impl Default for ServiceConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
socket_path: PathBuf::from("/run/shepherdd/shepherdd.sock"), socket_path: PathBuf::from("/run/shepherdd/shepherdd.sock"),
@ -208,9 +208,9 @@ pub struct LimitsPolicy {
/// Volume control policy /// Volume control policy
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct VolumePolicy { pub struct VolumePolicy {
/// Maximum volume percentage allowed (enforced by daemon) /// Maximum volume percentage allowed (enforced by the service)
pub max_volume: Option<u8>, pub max_volume: Option<u8>,
/// Minimum volume percentage allowed (enforced by daemon) /// Minimum volume percentage allowed (enforced by the service)
pub min_volume: Option<u8>, pub min_volume: Option<u8>,
/// Whether mute toggle is allowed /// Whether mute toggle is allowed
pub allow_mute: bool, pub allow_mute: bool,

View file

@ -10,18 +10,18 @@ pub struct RawConfig {
/// Config schema version /// Config schema version
pub config_version: u32, pub config_version: u32,
/// Global daemon settings /// Global service settings
#[serde(default)] #[serde(default, alias = "daemon")]
pub daemon: RawDaemonConfig, pub service: RawServiceConfig,
/// List of allowed entries /// List of allowed entries
#[serde(default)] #[serde(default)]
pub entries: Vec<RawEntry>, pub entries: Vec<RawEntry>,
} }
/// Daemon-level settings /// Service-level settings
#[derive(Debug, Clone, Default, Deserialize, Serialize)] #[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct RawDaemonConfig { pub struct RawServiceConfig {
/// IPC socket path (default: /run/shepherdd/shepherdd.sock) /// IPC socket path (default: /run/shepherdd/shepherdd.sock)
pub socket_path: Option<PathBuf>, pub socket_path: Option<PathBuf>,

View file

@ -110,7 +110,7 @@ fn validate_entry(entry: &RawEntry, config: &RawConfig) -> Vec<ValidationError>
.limits .limits
.as_ref() .as_ref()
.and_then(|l| l.max_run_seconds) .and_then(|l| l.max_run_seconds)
.or(config.daemon.default_max_run_seconds); .or(config.service.default_max_run_seconds);
// Only validate warnings if max_run is Some and not 0 (unlimited) // Only validate warnings if max_run is Some and not 0 (unlimited)
if let (Some(warnings), Some(max_run)) = (&entry.warnings, max_run) { if let (Some(warnings), Some(max_run)) = (&entry.warnings, max_run) {
@ -245,7 +245,7 @@ mod tests {
fn test_duplicate_id_detection() { fn test_duplicate_id_detection() {
let config = RawConfig { let config = RawConfig {
config_version: 1, config_version: 1,
daemon: Default::default(), service: Default::default(),
entries: vec![ entries: vec![
RawEntry { RawEntry {
id: "game".into(), id: "game".into(),

View file

@ -2,7 +2,7 @@
use chrono::{DateTime, Local}; use chrono::{DateTime, Local};
use shepherd_api::{ use shepherd_api::{
DaemonStateSnapshot, EntryKindTag, EntryView, ReasonCode, SessionEndReason, ServiceStateSnapshot, EntryKindTag, EntryView, ReasonCode, SessionEndReason,
WarningSeverity, API_VERSION, WarningSeverity, API_VERSION,
}; };
use shepherd_config::{Entry, Policy}; use shepherd_config::{Entry, Policy};
@ -472,8 +472,8 @@ impl CoreEngine {
}) })
} }
/// Get current daemon state snapshot /// Get current service state snapshot
pub fn get_state(&self) -> DaemonStateSnapshot { pub fn get_state(&self) -> ServiceStateSnapshot {
let current_session = self.current_session.as_ref().map(|s| { let current_session = self.current_session.as_ref().map(|s| {
s.to_session_info(MonotonicInstant::now()) s.to_session_info(MonotonicInstant::now())
}); });
@ -481,7 +481,7 @@ impl CoreEngine {
// Build entry views for the snapshot // Build entry views for the snapshot
let entries = self.list_entries(shepherd_util::now()); let entries = self.list_entries(shepherd_util::now());
DaemonStateSnapshot { ServiceStateSnapshot {
api_version: API_VERSION, api_version: API_VERSION,
policy_loaded: true, policy_loaded: true,
current_session, current_session,
@ -553,7 +553,7 @@ mod tests {
fn make_test_policy() -> Policy { fn make_test_policy() -> Policy {
Policy { Policy {
daemon: Default::default(), service: Default::default(),
entries: vec![Entry { entries: vec![Entry {
id: EntryId::new("test-game"), id: EntryId::new("test-game"),
label: "Test Game".into(), label: "Test Game".into(),
@ -661,7 +661,7 @@ mod tests {
disabled: false, disabled: false,
disabled_reason: None, disabled_reason: None,
}], }],
daemon: Default::default(), service: Default::default(),
default_warnings: vec![], default_warnings: vec![],
default_max_run: Some(Duration::from_secs(3600)), default_max_run: Some(Duration::from_secs(3600)),
volume: Default::default(), volume: Default::default(),
@ -722,7 +722,7 @@ mod tests {
disabled: false, disabled: false,
disabled_reason: None, disabled_reason: None,
}], }],
daemon: Default::default(), service: Default::default(),
default_warnings: vec![], default_warnings: vec![],
default_max_run: Some(Duration::from_secs(3600)), default_max_run: Some(Duration::from_secs(3600)),
volume: Default::default(), volume: Default::default(),

View file

@ -1,6 +1,6 @@
//! Host adapter trait interfaces for shepherdd //! Host adapter trait interfaces for shepherdd
//! //!
//! This crate defines the capability-based interface between the daemon core //! This crate defines the capability-based interface between the shepherdd service
//! and platform-specific implementations. It contains no platform code itself. //! and platform-specific implementations. It contains no platform code itself.
mod capabilities; mod capabilities;

View file

@ -1,7 +1,7 @@
//! Volume control trait interfaces //! Volume control trait interfaces
//! //!
//! Defines the capability-based interface for volume control between //! Defines the capability-based interface for volume control between
//! the daemon core and platform-specific implementations. //! the shepherdd service and platform-specific implementations.
use async_trait::async_trait; use async_trait::async_trait;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -67,9 +67,9 @@ pub struct VolumeCapabilities {
/// Volume restrictions that can be enforced by policy /// Volume restrictions that can be enforced by policy
#[derive(Debug, Clone, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct VolumeRestrictions { pub struct VolumeRestrictions {
/// Maximum volume percentage allowed (enforced by daemon) /// Maximum volume percentage allowed (enforced by the service)
pub max_volume: Option<u8>, pub max_volume: Option<u8>,
/// Minimum volume percentage allowed (enforced by daemon) /// Minimum volume percentage allowed (enforced by the service)
pub min_volume: Option<u8>, pub min_volume: Option<u8>,
/// Whether mute toggle is allowed /// Whether mute toggle is allowed
pub allow_mute: bool, pub allow_mute: bool,

View file

@ -82,7 +82,7 @@ impl LinuxHost {
info!(pid = pid, pgid = pgid, status = ?status, "Process exited - sending HostEvent::Exited"); info!(pid = pid, pgid = pgid, status = ?status, "Process exited - sending HostEvent::Exited");
// We don't have the session_id here, so we use a placeholder // We don't have the session_id here, so we use a placeholder
// The daemon should track the mapping // The service should track the mapping
let handle = HostSessionHandle::new( let handle = HostSessionHandle::new(
SessionId::new(), // This will be matched by PID SessionId::new(), // This will be matched by PID
HostHandlePayload::Linux { pid, pgid }, HostHandlePayload::Linux { pid, pgid },

View file

@ -225,8 +225,8 @@ impl ManagedProcess {
// Special handling for WAYLAND_DISPLAY: // Special handling for WAYLAND_DISPLAY:
// If SHEPHERD_WAYLAND_DISPLAY is set, use that instead of the inherited value. // If SHEPHERD_WAYLAND_DISPLAY is set, use that instead of the inherited value.
// This allows apps to be launched on a nested compositor while the daemon // This allows apps to be launched on a nested compositor while the service
// runs on the parent compositor. When the daemon runs inside the nested // runs on the parent compositor. When the service runs inside the nested
// compositor, this is not needed as WAYLAND_DISPLAY is already correct. // compositor, this is not needed as WAYLAND_DISPLAY is already correct.
if let Ok(shepherd_display) = std::env::var("SHEPHERD_WAYLAND_DISPLAY") { if let Ok(shepherd_display) = std::env::var("SHEPHERD_WAYLAND_DISPLAY") {
debug!(display = %shepherd_display, "Using SHEPHERD_WAYLAND_DISPLAY override for child process"); debug!(display = %shepherd_display, "Using SHEPHERD_WAYLAND_DISPLAY override for child process");

View file

@ -236,7 +236,7 @@ fn build_hud_content(state: SharedState) -> gtk4::Box {
volume_slider.set_increments(5.0, 10.0); volume_slider.set_increments(5.0, 10.0);
volume_slider.add_css_class("volume-slider"); volume_slider.add_css_class("volume-slider");
// Set initial value from daemon // Set initial value from shepherdd
if let Some(info) = crate::volume::get_volume_status() { if let Some(info) = crate::volume::get_volume_status() {
volume_slider.set_value(info.percent as f64); volume_slider.set_value(info.percent as f64);
} }
@ -333,7 +333,7 @@ fn build_hud_content(state: SharedState) -> gtk4::Box {
let session_state = state_for_close.session_state(); let session_state = state_for_close.session_state();
if let Some(session_id) = session_state.session_id() { if let Some(session_id) = session_state.session_id() {
tracing::info!("Requesting end session for {}", session_id); tracing::info!("Requesting end session for {}", session_id);
// Send StopCurrent command to daemon // Send StopCurrent command to shepherdd
let socket_path = std::env::var("SHEPHERD_SOCKET") let socket_path = std::env::var("SHEPHERD_SOCKET")
.unwrap_or_else(|_| "./dev-runtime/shepherd.sock".to_string()); .unwrap_or_else(|_| "./dev-runtime/shepherd.sock".to_string());
std::thread::spawn(move || { std::thread::spawn(move || {
@ -349,7 +349,7 @@ fn build_hud_content(state: SharedState) -> gtk4::Box {
} }
} }
Err(e) => { Err(e) => {
tracing::error!("Failed to connect to daemon: {}", e); tracing::error!("Failed to connect to shepherdd: {}", e);
} }
} }
}); });

View file

@ -1,7 +1,7 @@
//! Volume monitoring and control module //! Volume monitoring and control module
//! //!
//! Provides volume status and control via the shepherdd daemon. //! Provides volume status and control via shepherdd.
//! The daemon handles actual volume control and enforces restrictions. //! The service handles actual volume control and enforces restrictions.
use shepherd_api::{Command, ResponsePayload, VolumeInfo}; use shepherd_api::{Command, ResponsePayload, VolumeInfo};
use shepherd_ipc::IpcClient; use shepherd_ipc::IpcClient;
@ -15,7 +15,7 @@ fn get_socket_path() -> PathBuf {
.unwrap_or_else(|_| PathBuf::from("./dev-runtime/shepherd.sock")) .unwrap_or_else(|_| PathBuf::from("./dev-runtime/shepherd.sock"))
} }
/// Get current volume status from the daemon /// Get current volume status from shepherdd
pub fn get_volume_status() -> Option<VolumeInfo> { pub fn get_volume_status() -> Option<VolumeInfo> {
let socket_path = get_socket_path(); let socket_path = get_socket_path();
@ -45,14 +45,14 @@ pub fn get_volume_status() -> Option<VolumeInfo> {
} }
}, },
Err(e) => { Err(e) => {
tracing::debug!("Failed to connect to daemon for volume: {}", e); tracing::debug!("Failed to connect to shepherdd for volume: {}", e);
None None
} }
} }
}) })
} }
/// Toggle mute state via the daemon /// Toggle mute state via shepherdd
pub fn toggle_mute() -> anyhow::Result<()> { pub fn toggle_mute() -> anyhow::Result<()> {
let socket_path = get_socket_path(); let socket_path = get_socket_path();
@ -75,7 +75,7 @@ pub fn toggle_mute() -> anyhow::Result<()> {
}) })
} }
/// Increase volume by a step via the daemon /// Increase volume by a step via shepherdd
pub fn volume_up(step: u8) -> anyhow::Result<()> { pub fn volume_up(step: u8) -> anyhow::Result<()> {
let socket_path = get_socket_path(); let socket_path = get_socket_path();
@ -98,7 +98,7 @@ pub fn volume_up(step: u8) -> anyhow::Result<()> {
}) })
} }
/// Decrease volume by a step via the daemon /// Decrease volume by a step via shepherdd
pub fn volume_down(step: u8) -> anyhow::Result<()> { pub fn volume_down(step: u8) -> anyhow::Result<()> {
let socket_path = get_socket_path(); let socket_path = get_socket_path();
@ -121,7 +121,7 @@ pub fn volume_down(step: u8) -> anyhow::Result<()> {
}) })
} }
/// Set volume to a specific percentage via the daemon /// Set volume to a specific percentage via shepherdd
pub fn set_volume(percent: u8) -> anyhow::Result<()> { pub fn set_volume(percent: u8) -> anyhow::Result<()> {
let socket_path = get_socket_path(); let socket_path = get_socket_path();

View file

@ -15,7 +15,7 @@ pub struct IpcClient {
} }
impl IpcClient { impl IpcClient {
/// Connect to the daemon /// Connect to shepherdd
pub async fn connect(socket_path: impl AsRef<Path>) -> IpcResult<Self> { pub async fn connect(socket_path: impl AsRef<Path>) -> IpcResult<Self> {
let stream = UnixStream::connect(socket_path).await?; let stream = UnixStream::connect(socket_path).await?;
let (read_half, write_half) = stream.into_split(); let (read_half, write_half) = stream.into_split();
@ -67,7 +67,7 @@ impl IpcClient {
} }
} }
/// Stream of events from the daemon /// Stream of events from shepherdd
pub struct EventStream { pub struct EventStream {
reader: BufReader<tokio::net::unix::OwnedReadHalf>, reader: BufReader<tokio::net::unix::OwnedReadHalf>,
} }

View file

@ -10,7 +10,7 @@ use tokio::runtime::Runtime;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tracing::{debug, error, info}; use tracing::{debug, error, info};
use crate::client::{ClientCommand, CommandClient, DaemonClient}; use crate::client::{ClientCommand, CommandClient, ServiceClient};
use crate::grid::LauncherGrid; use crate::grid::LauncherGrid;
use crate::state::{LauncherState, SharedState}; use crate::state::{LauncherState, SharedState};
@ -188,7 +188,7 @@ impl LauncherApp {
match client.launch(&entry_id).await { match client.launch(&entry_id).await {
Ok(response) => { Ok(response) => {
debug!(response = ?response, "Launch response"); debug!(response = ?response, "Launch response");
// Handle error responses from daemon // Handle error responses from shepherdd
match response.result { match response.result {
shepherd_api::ResponseResult::Ok(payload) => { shepherd_api::ResponseResult::Ok(payload) => {
// Check what kind of success response we got // Check what kind of success response we got
@ -227,7 +227,7 @@ impl LauncherApp {
shepherd_api::ResponseResult::Err(err) => { shepherd_api::ResponseResult::Err(err) => {
// Launch failed on server side - refresh state to recover // Launch failed on server side - refresh state to recover
error!(error = %err.message, "Launch failed on server"); error!(error = %err.message, "Launch failed on server");
// Request fresh state from daemon to get back to correct state // Request fresh state from shepherdd to get back to correct state
match client.get_state().await { match client.get_state().await {
Ok(state_resp) => { Ok(state_resp) => {
if let shepherd_api::ResponseResult::Ok( if let shepherd_api::ResponseResult::Ok(
@ -293,14 +293,14 @@ impl LauncherApp {
}); });
}); });
// Start daemon client in background thread (separate from GTK main loop) // Start shepherdd client in background thread (separate from GTK main loop)
// This ensures the tokio runtime is properly driven for event reception // This ensures the tokio runtime is properly driven for event reception
let state_for_client = state.clone(); let state_for_client = state.clone();
let socket_for_client = socket_path.clone(); let socket_for_client = socket_path.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
let rt = tokio::runtime::Runtime::new().expect("Failed to create tokio runtime for event loop"); let rt = tokio::runtime::Runtime::new().expect("Failed to create tokio runtime for event loop");
rt.block_on(async move { rt.block_on(async move {
let client = DaemonClient::new(socket_for_client, state_for_client, command_rx); let client = ServiceClient::new(socket_for_client, state_for_client, command_rx);
client.run().await; client.run().await;
}); });
}); });

View file

@ -26,13 +26,13 @@ pub enum ClientCommand {
} }
/// Client connection manager /// Client connection manager
pub struct DaemonClient { pub struct ServiceClient {
socket_path: std::path::PathBuf, socket_path: std::path::PathBuf,
state: SharedState, state: SharedState,
command_rx: mpsc::UnboundedReceiver<ClientCommand>, command_rx: mpsc::UnboundedReceiver<ClientCommand>,
} }
impl DaemonClient { impl ServiceClient {
pub fn new( pub fn new(
socket_path: impl AsRef<Path>, socket_path: impl AsRef<Path>,
state: SharedState, state: SharedState,
@ -67,13 +67,13 @@ impl DaemonClient {
async fn connect_and_run(&mut self) -> Result<()> { async fn connect_and_run(&mut self) -> Result<()> {
self.state.set(LauncherState::Connecting); self.state.set(LauncherState::Connecting);
info!(path = %self.socket_path.display(), "Connecting to daemon"); info!(path = %self.socket_path.display(), "Connecting to shepherdd");
let mut client = IpcClient::connect(&self.socket_path) let mut client = IpcClient::connect(&self.socket_path)
.await .await
.context("Failed to connect to daemon")?; .context("Failed to connect to shepherdd")?;
info!("Connected to daemon"); info!("Connected to shepherdd");
// Get initial state (includes entries) // Get initial state (includes entries)
info!("Sending GetState command"); info!("Sending GetState command");
@ -116,11 +116,11 @@ impl DaemonClient {
} }
} }
// Handle events from daemon // Handle events from shepherdd
event_result = events.next() => { event_result = events.next() => {
match event_result { match event_result {
Ok(event) => { Ok(event) => {
info!(event = ?event, "Received event from daemon (client.rs)"); info!(event = ?event, "Received event from shepherdd (client.rs)");
self.state.handle_event(event); self.state.handle_event(event);
} }
Err(e) => { Err(e) => {

View file

@ -1,6 +1,6 @@
//! Launcher application state management //! Launcher application state management
use shepherd_api::{DaemonStateSnapshot, EntryView, Event, EventPayload}; use shepherd_api::{ServiceStateSnapshot, EntryView, Event, EventPayload};
use shepherd_util::SessionId; use shepherd_util::SessionId;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
@ -9,7 +9,7 @@ use tokio::sync::watch;
/// Current state of the launcher UI /// Current state of the launcher UI
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum LauncherState { pub enum LauncherState {
/// Not connected to daemon /// Not connected to shepherdd
Disconnected, Disconnected,
/// Connected, waiting for initial state /// Connected, waiting for initial state
Connecting, Connecting,
@ -58,9 +58,9 @@ impl SharedState {
self.receiver.clone() self.receiver.clone()
} }
/// Update state from daemon event /// Update state from shepherdd event
pub fn handle_event(&self, event: Event) { pub fn handle_event(&self, event: Event) {
tracing::info!(event = ?event.payload, "Received event from daemon"); tracing::info!(event = ?event.payload, "Received event from shepherdd");
match event.payload { match event.payload {
EventPayload::StateChanged(snapshot) => { EventPayload::StateChanged(snapshot) => {
tracing::info!(has_session = snapshot.current_session.is_some(), "Applying state snapshot"); tracing::info!(has_session = snapshot.current_session.is_some(), "Applying state snapshot");
@ -109,7 +109,7 @@ impl SharedState {
self.set(LauncherState::Connecting); self.set(LauncherState::Connecting);
} }
EventPayload::Shutdown => { EventPayload::Shutdown => {
// Daemon is shutting down // Service is shutting down
self.set(LauncherState::Disconnected); self.set(LauncherState::Disconnected);
} }
EventPayload::AuditEntry { .. } => { EventPayload::AuditEntry { .. } => {
@ -121,7 +121,7 @@ impl SharedState {
} }
} }
fn apply_snapshot(&self, snapshot: DaemonStateSnapshot) { fn apply_snapshot(&self, snapshot: ServiceStateSnapshot) {
if let Some(session) = snapshot.current_session { if let Some(session) = snapshot.current_session {
let now = shepherd_util::now(); let now = shepherd_util::now();
// For unlimited sessions (deadline=None), time_remaining is None // For unlimited sessions (deadline=None), time_remaining is None

View file

@ -10,11 +10,11 @@ use std::time::Duration;
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")] #[serde(tag = "type", rename_all = "snake_case")]
pub enum AuditEventType { pub enum AuditEventType {
/// Daemon started /// Service started
DaemonStarted, ServiceStarted,
/// Daemon stopped /// Service stopped
DaemonStopped, ServiceStopped,
/// Policy loaded/reloaded /// Policy loaded/reloaded
PolicyLoaded { entry_count: usize }, PolicyLoaded { entry_count: usize },

View file

@ -272,12 +272,12 @@ mod tests {
fn test_audit_log() { fn test_audit_log() {
let store = SqliteStore::in_memory().unwrap(); let store = SqliteStore::in_memory().unwrap();
let event = AuditEvent::new(AuditEventType::DaemonStarted); let event = AuditEvent::new(AuditEventType::ServiceStarted);
store.append_audit(event).unwrap(); store.append_audit(event).unwrap();
let events = store.get_recent_audits(10).unwrap(); let events = store.get_recent_audits(10).unwrap();
assert_eq!(events.len(), 1); assert_eq!(events.len(), 1);
assert!(matches!(events[0].event, AuditEventType::DaemonStarted)); assert!(matches!(events[0].event, AuditEventType::ServiceStarted));
} }
#[test] #[test]

View file

@ -3,7 +3,7 @@ name = "shepherdd"
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
description = "The shepherdd daemon: policy enforcement for child-focused computing" description = "The shepherdd background service: policy enforcement for child-focused computing"
[[bin]] [[bin]]
name = "shepherdd" name = "shepherdd"

View file

@ -1,4 +1,4 @@
//! shepherdd - The shepherd daemon //! shepherdd - The shepherd background service
//! //!
//! This is the main entry point for the shepherdd service. //! This is the main entry point for the shepherdd service.
//! It wires together all the components: //! It wires together all the components:
@ -12,7 +12,7 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use clap::Parser; use clap::Parser;
use shepherd_api::{ use shepherd_api::{
Command, DaemonStateSnapshot, ErrorCode, ErrorInfo, Event, EventPayload, HealthStatus, Command, ServiceStateSnapshot, ErrorCode, ErrorInfo, Event, EventPayload, HealthStatus,
Response, ResponsePayload, SessionEndReason, StopMode, VolumeInfo, VolumeRestrictions, Response, ResponsePayload, SessionEndReason, StopMode, VolumeInfo, VolumeRestrictions,
API_VERSION, API_VERSION,
}; };
@ -30,10 +30,10 @@ use tokio::sync::Mutex;
use tracing::{debug, error, info, warn, Level}; use tracing::{debug, error, info, warn, Level};
use tracing_subscriber::EnvFilter; use tracing_subscriber::EnvFilter;
/// shepherdd - Policy enforcement daemon for child-focused computing /// shepherdd - Policy enforcement service for child-focused computing
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(name = "shepherdd")] #[command(name = "shepherdd")]
#[command(about = "Policy enforcement daemon for child-focused computing", long_about = None)] #[command(about = "Policy enforcement service for child-focused computing", long_about = None)]
struct Args { struct Args {
/// Configuration file path /// Configuration file path
#[arg(short, long, default_value = "/etc/shepherdd/config.toml")] #[arg(short, long, default_value = "/etc/shepherdd/config.toml")]
@ -52,8 +52,8 @@ struct Args {
log_level: String, log_level: String,
} }
/// Main daemon state /// Main service state
struct Daemon { struct Service {
engine: CoreEngine, engine: CoreEngine,
host: Arc<LinuxHost>, host: Arc<LinuxHost>,
volume: Arc<LinuxVolumeController>, volume: Arc<LinuxVolumeController>,
@ -62,7 +62,7 @@ struct Daemon {
rate_limiter: RateLimiter, rate_limiter: RateLimiter,
} }
impl Daemon { impl Service {
async fn new(args: &Args) -> Result<Self> { async fn new(args: &Args) -> Result<Self> {
// Load configuration // Load configuration
let policy = load_config(&args.config) let policy = load_config(&args.config)
@ -78,12 +78,12 @@ impl Daemon {
let socket_path = args let socket_path = args
.socket .socket
.clone() .clone()
.unwrap_or_else(|| policy.daemon.socket_path.clone()); .unwrap_or_else(|| policy.service.socket_path.clone());
let data_dir = args let data_dir = args
.data_dir .data_dir
.clone() .clone()
.unwrap_or_else(|| policy.daemon.data_dir.clone()); .unwrap_or_else(|| policy.service.data_dir.clone());
// Create data directory // Create data directory
std::fs::create_dir_all(&data_dir) std::fs::create_dir_all(&data_dir)
@ -98,8 +98,8 @@ impl Daemon {
info!(db_path = %db_path.display(), "Store initialized"); info!(db_path = %db_path.display(), "Store initialized");
// Log daemon start // Log service start
store.append_audit(AuditEvent::new(AuditEventType::DaemonStarted))?; store.append_audit(AuditEvent::new(AuditEventType::ServiceStarted))?;
// Initialize host adapter // Initialize host adapter
let host = Arc::new(LinuxHost::new()); let host = Arc::new(LinuxHost::new());
@ -168,7 +168,7 @@ impl Daemon {
let tick_interval = Duration::from_millis(100); let tick_interval = Duration::from_millis(100);
let mut tick_timer = tokio::time::interval(tick_interval); let mut tick_timer = tokio::time::interval(tick_interval);
info!("Daemon running"); info!("Service running");
loop { loop {
tokio::select! { tokio::select! {
@ -940,7 +940,7 @@ async fn main() -> Result<()> {
"shepherdd starting" "shepherdd starting"
); );
// Create and run daemon // Create and run the service
let daemon = Daemon::new(&args).await?; let service = Service::new(&args).await?;
daemon.run().await service.run().await
} }

View file

@ -1,6 +1,6 @@
//! Integration tests for shepherdd //! Integration tests for shepherdd
//! //!
//! These tests verify the end-to-end behavior of the daemon. //! These tests verify the end-to-end behavior of shepherdd.
use shepherd_api::{EntryKind, WarningSeverity, WarningThreshold}; use shepherd_api::{EntryKind, WarningSeverity, WarningThreshold};
use shepherd_config::{AvailabilityPolicy, Entry, LimitsPolicy, Policy}; use shepherd_config::{AvailabilityPolicy, Entry, LimitsPolicy, Policy};
@ -14,7 +14,7 @@ use std::time::Duration;
fn make_test_policy() -> Policy { fn make_test_policy() -> Policy {
Policy { Policy {
daemon: Default::default(), service: Default::default(),
entries: vec![ entries: vec![
Entry { Entry {
id: EntryId::new("test-game"), id: EntryId::new("test-game"),

View file

@ -132,16 +132,16 @@ workspace 1 output *
### Application startup ### Application startup
# Start the daemon FIRST - it needs to create the socket before HUD/launcher connect # Start shepherdd FIRST - it needs to create the socket before HUD/launcher connect
# Running inside sway ensures all spawned processes use the nested compositor # Running inside sway ensures all spawned processes use the nested compositor
exec ./target/debug/shepherdd -c ./config.example.toml exec ./target/debug/shepherdd -c ./config.example.toml
# Give the daemon a moment to initialize, then start UI components # Give shepherdd a moment to initialize, then start UI components
# Start the shepherd-hud (time remaining overlay) # Start the shepherd-hud (time remaining overlay)
exec sleep 1 && $hud exec sleep 1 && $hud
# Start the shepherd-launcher on startup (the main "home" screen) # Start the shepherd-launcher on startup (the main "home" screen)
# Small delay to ensure daemon is ready # Small delay to ensure shepherdd is ready
exec_always sleep 1 && $launcher exec_always sleep 1 && $launcher
### Disable workspace switching ### Disable workspace switching