tor_guardmgr/
err.rs

1//! Declare error types for the `tor-guardmgr` crate.
2
3use futures::task::SpawnError;
4use std::sync::Arc;
5use std::time::Instant;
6use tor_basic_utils::iter::FilterCount;
7use tor_error::{Bug, ErrorKind, HasKind};
8
9/// A error caused by a failure to pick a guard.
10#[derive(Clone, Debug, thiserror::Error)]
11#[non_exhaustive]
12pub enum PickGuardError {
13    /// All members of the current sample were down or unusable.
14    #[error(
15        "No usable guards. Rejected {} as down, then {} as pending, then \
16         {} as unsuitable to purpose, then {} with filter.",
17        running.display_frac_rejected(),
18        pending.display_frac_rejected(),
19        suitable.display_frac_rejected(),
20        filtered.display_frac_rejected(),
21    )]
22    AllGuardsDown {
23        /// The next time at which any guard will be retriable.
24        retry_at: Option<Instant>,
25        /// How many guards we rejected because they had failed too recently.
26        running: FilterCount,
27        /// How many guards we rejected because we are already probing them.
28        pending: FilterCount,
29        /// How many guards we rejected as unsuitable for the intended application.
30        suitable: FilterCount,
31        /// How many guards we rejected because of the provided filter.
32        filtered: FilterCount,
33    },
34
35    /// We have no usable fallback directories.
36    #[error(
37        "No usable fallbacks. Rejected {} as not running, then {} as filtered.", 
38         running.display_frac_rejected(),
39        filtered.display_frac_rejected()
40    )]
41    AllFallbacksDown {
42        /// The next time at which any fallback directory will back available.
43        retry_at: Option<Instant>,
44        /// The number of fallbacks that were believed to be running or down, after applying
45        /// the filter.
46        running: FilterCount,
47        /// The number of fallbacks that satisfied our filter, or did not.
48        filtered: FilterCount,
49    },
50
51    /// Tried to select guards or fallbacks from an empty list.
52    #[error("Tried to pick from an empty list")]
53    NoCandidatesAvailable,
54
55    /// An internal programming error occurred.
56    #[error("Internal error")]
57    Internal(#[from] Bug),
58}
59
60impl tor_error::HasKind for PickGuardError {
61    fn kind(&self) -> tor_error::ErrorKind {
62        use tor_error::ErrorKind as EK;
63        use PickGuardError as E;
64        match self {
65            E::AllFallbacksDown { .. } | E::AllGuardsDown { .. } => EK::TorAccessFailed,
66            E::NoCandidatesAvailable => EK::NoPath,
67            E::Internal(_) => EK::Internal,
68        }
69    }
70}
71
72impl tor_error::HasRetryTime for PickGuardError {
73    fn retry_time(&self) -> tor_error::RetryTime {
74        use tor_error::RetryTime as RT;
75        use PickGuardError as E;
76        match self {
77            // Some errors contain times that we can just use.
78            E::AllGuardsDown {
79                retry_at: Some(when),
80                ..
81            } => RT::At(*when),
82            E::AllFallbacksDown {
83                retry_at: Some(when),
84                ..
85            } => RT::At(*when),
86
87            // If we don't know when the guards/fallbacks will be back up,
88            // though, then we should suggest a random delay.
89            E::AllGuardsDown { .. } | E::AllFallbacksDown { .. } => RT::AfterWaiting,
90
91            // We were asked to choose some kind of guard that doesn't exist in
92            // our current universe; that's not going to be come viable down the
93            // line.
94            E::NoCandidatesAvailable => RT::Never,
95
96            // Don't try to recover from internal errors.
97            E::Internal(_) => RT::Never,
98        }
99    }
100}
101/// An error caused while creating or updating a guard manager.
102#[derive(Clone, Debug, thiserror::Error)]
103#[non_exhaustive]
104pub enum GuardMgrError {
105    /// An error manipulating persistent state
106    #[error("Problem accessing persistent guard state")]
107    State(#[from] tor_persist::Error),
108
109    /// Configuration is not valid or available
110    #[error("Invalid configuration")]
111    InvalidConfig(#[from] GuardMgrConfigError),
112
113    /// An error that occurred while trying to spawn a daemon task.
114    #[error("Unable to spawn {spawning}")]
115    Spawn {
116        /// What we were trying to spawn.
117        spawning: &'static str,
118        /// What happened when we tried to spawn it.
119        #[source]
120        cause: Arc<SpawnError>,
121    },
122}
123
124impl HasKind for GuardMgrError {
125    #[rustfmt::skip] // to preserve table in match
126    fn kind(&self) -> ErrorKind {
127        use GuardMgrError as G;
128        match self {
129            G::State(e)               => e.kind(),
130            G::InvalidConfig(e)       => e.kind(),
131            G::Spawn{ cause, .. }     => cause.kind(),
132        }
133    }
134}
135
136impl GuardMgrError {
137    /// Construct a new `GuardMgrError` from a `SpawnError`.
138    pub(crate) fn from_spawn(spawning: &'static str, err: SpawnError) -> GuardMgrError {
139        GuardMgrError::Spawn {
140            spawning,
141            cause: Arc::new(err),
142        }
143    }
144}
145
146/// An error encountered while configuring or reconfiguring a guard manager
147///
148/// When this occurs during initial configuration, it will be returned wrapped
149/// up in `GuardMgrError`.
150///
151/// When it occurs during reconfiguration, it is not exposed to caller:
152/// instead, it is converted into a `tor_config::ReconfigureError`.
153#[derive(Clone, Debug, thiserror::Error)]
154#[non_exhaustive]
155pub enum GuardMgrConfigError {
156    /// Specified configuration requires exclusive access to stored state, which we don't have
157    #[error("Configuration requires exclusive access to shared state, but another instance of Arti has the lock: {0}")]
158    NoLock(String),
159}
160
161impl From<GuardMgrConfigError> for tor_config::ReconfigureError {
162    fn from(g: GuardMgrConfigError) -> tor_config::ReconfigureError {
163        use tor_config::ReconfigureError as R;
164        use GuardMgrConfigError as G;
165        match g {
166            e @ G::NoLock(_) => R::UnsupportedSituation(e.to_string()),
167        }
168    }
169}
170
171impl HasKind for GuardMgrConfigError {
172    fn kind(&self) -> ErrorKind {
173        use ErrorKind as EK;
174        use GuardMgrConfigError as G;
175        match self {
176            // `InvalidConfig` and `FeatureDisabled` aren't right, because
177            // those should be detected at config build time and reported as `ConfigBuildError`.
178            // `InvalidConfigTransition` isn't right, because restarting arti won't help.
179            // Possibly at some point we will allow concurrent artis to work this way.
180            G::NoLock(_) => EK::NotImplemented,
181        }
182    }
183}