Skip to content

Commit 5ac1f89

Browse files
Merge branch 'daemon-doesnt-work-properly-after-disabling-all-access-des-529'
2 parents ef18ba2 + 2fda59b commit 5ac1f89

File tree

3 files changed

+89
-29
lines changed

3 files changed

+89
-29
lines changed

mullvad-daemon/src/access_method.rs

+25-10
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ where
8181
) -> Result<(), Error> {
8282
// Make sure that we are not trying to remove a built-in API access
8383
// method
84-
let command = match self.settings.api_access_methods.find(&access_method) {
84+
let command = match self.settings.api_access_methods.find_by_id(&access_method) {
8585
Some(api_access_method) => {
8686
if api_access_method.is_builtin() {
8787
Err(Error::RemoveBuiltIn)
@@ -131,7 +131,7 @@ where
131131
) -> Result<AccessMethodSetting, Error> {
132132
self.settings
133133
.api_access_methods
134-
.find(&access_method)
134+
.find_by_id(&access_method)
135135
.ok_or(Error::NoSuchMethod(access_method))
136136
.cloned()
137137
}
@@ -147,22 +147,37 @@ where
147147
&mut self,
148148
access_method_update: AccessMethodSetting,
149149
) -> Result<(), Error> {
150-
// We have to be a bit careful. If we are about to disable the last
151-
// remaining enabled access method, we would cause an inconsistent state
152-
// in the daemon's settings. Therefore, we have to safeguard against
153-
// this by explicitly checking for & disallow any update which would
154-
// cause the last enabled access method to become disabled.
150+
// If the currently active access method is updated, we need to re-set
151+
// it after updating the settings.
155152
let current = self.get_current_access_method().await?;
156153
let mut command = Command::Nothing;
157154
let settings_update = |settings: &mut Settings| {
158-
if let Some(access_method) = settings
159-
.api_access_methods
160-
.find_mut(&access_method_update.get_id())
155+
let access_methods = &mut settings.api_access_methods;
156+
if let Some(access_method) =
157+
access_methods.find_by_id_mut(&access_method_update.get_id())
161158
{
162159
*access_method = access_method_update;
163160
if access_method.get_id() == current.get_id() {
164161
command = Command::Set(access_method.get_id())
165162
}
163+
// We have to be a bit careful. If we are about to disable the last
164+
// remaining enabled access method, we would cause an inconsistent state
165+
// in the daemon's settings. Therefore, we have to safeguard against
166+
// this by explicitly checking for any update which would cause the last
167+
// enabled access method to become disabled. In that case, we should
168+
// re-enable the `Direct` access method.
169+
if access_methods.collect_enabled().is_empty() {
170+
if let Some(direct) = access_methods.get_direct() {
171+
direct.enabled = true;
172+
} else {
173+
// If the `Direct` access method does not exist within the
174+
// settings for some reason, the settings are in an
175+
// inconsistent state. We don't have much choice but to
176+
// reset these settings to their default value.
177+
log::warn!("The built-in access methods can not be found. This might be due to a corrupt settings file");
178+
*access_methods = access_method::Settings::default();
179+
}
180+
}
166181
}
167182
};
168183

mullvad-daemon/src/api.rs

+35-9
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,31 @@ pub enum Error {
4545
NotRunning(#[error(source)] oneshot::Canceled),
4646
}
4747

48+
impl std::fmt::Display for Message {
49+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50+
match self {
51+
Message::Get(_) => f.write_str("Get"),
52+
Message::Set(_, _) => f.write_str("Set"),
53+
Message::Next(_) => f.write_str("Next"),
54+
Message::Update(_, _) => f.write_str("Update"),
55+
}
56+
}
57+
}
58+
59+
impl Error {
60+
/// Check if this error implies that the currenly running
61+
/// [`AccessModeSelector`] can not continue to operate properly.
62+
///
63+
/// To recover from this kind of error, the daemon will probably have to
64+
/// intervene.
65+
fn is_critical_error(&self) -> bool {
66+
matches!(
67+
self,
68+
Error::SendFailed(..) | Error::OneshotSendFailed | Error::NotRunning(..)
69+
)
70+
}
71+
}
72+
4873
type ResponseTx<T> = oneshot::Sender<Result<T>>;
4974
type Result<T> = std::result::Result<T, Error>;
5075

@@ -66,7 +91,7 @@ impl AccessModeSelectorHandle {
6691

6792
pub async fn get_access_method(&self) -> Result<AccessMethodSetting> {
6893
self.send_command(Message::Get).await.map_err(|err| {
69-
log::error!("Failed to get current access method!");
94+
log::debug!("Failed to get current access method!");
7095
err
7196
})
7297
}
@@ -75,7 +100,7 @@ impl AccessModeSelectorHandle {
75100
self.send_command(|tx| Message::Set(tx, value))
76101
.await
77102
.map_err(|err| {
78-
log::error!("Failed to set new access method!");
103+
log::debug!("Failed to set new access method!");
79104
err
80105
})
81106
}
@@ -84,14 +109,14 @@ impl AccessModeSelectorHandle {
84109
self.send_command(|tx| Message::Update(tx, values))
85110
.await
86111
.map_err(|err| {
87-
log::error!("Failed to update new access methods!");
112+
log::debug!("Failed to switch to a new set of access methods");
88113
err
89114
})
90115
}
91116

92117
pub async fn next(&self) -> Result<ApiConnectionMode> {
93118
self.send_command(Message::Next).await.map_err(|err| {
94-
log::error!("Failed to update new access methods!");
119+
log::debug!("Failed while getting the next access method");
95120
err
96121
})
97122
}
@@ -163,6 +188,7 @@ impl AccessModeSelector {
163188

164189
async fn into_future(mut self) {
165190
while let Some(cmd) = self.cmd_rx.next().await {
191+
log::trace!("Processing {cmd} command");
166192
let execution = match cmd {
167193
Message::Get(tx) => self.on_get_access_method(tx),
168194
Message::Set(tx, value) => self.on_set_access_method(tx, value),
@@ -171,13 +197,13 @@ impl AccessModeSelector {
171197
};
172198
match execution {
173199
Ok(_) => (),
174-
Err(err) => {
175-
log::trace!(
176-
"AccessModeSelector is going down due to {error}",
177-
error = err
178-
);
200+
Err(error) if error.is_critical_error() => {
201+
log::error!("AccessModeSelector failed due to an internal error and won't be able to recover without a restart. {error}");
179202
break;
180203
}
204+
Err(error) => {
205+
log::debug!("AccessModeSelector failed processing command due to {error}");
206+
}
181207
}
182208
}
183209
}

mullvad-types/src/access_method.rs

+29-10
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,34 @@ impl Settings {
2121
self.retain(|method| method.get_id() != *api_access_method)
2222
}
2323

24-
/// Search for a particular [`AccessMethod`] in `api_access_methods`.
25-
pub fn find(&self, element: &Id) -> Option<&AccessMethodSetting> {
24+
/// Search for any [`AccessMethod`] in `api_access_methods` which matches `predicate`.
25+
pub fn find<P>(&self, predicate: P) -> Option<&AccessMethodSetting>
26+
where
27+
P: Fn(&AccessMethodSetting) -> bool,
28+
{
2629
self.access_method_settings
2730
.iter()
28-
.find(|api_access_method| *element == api_access_method.get_id())
31+
.find(|api_access_method| predicate(api_access_method))
2932
}
3033

31-
/// Search for a particular [`AccessMethod`] in `api_access_methods`.
32-
///
33-
/// If the [`AccessMethod`] is found to be part of `api_access_methods`, a
34-
/// mutable reference to that inner element is returned. Otherwise, `None`
35-
/// is returned.
36-
pub fn find_mut(&mut self, element: &Id) -> Option<&mut AccessMethodSetting> {
34+
/// Search for any [`AccessMethod`] in `api_access_methods`.
35+
pub fn find_mut<P>(&mut self, predicate: P) -> Option<&mut AccessMethodSetting>
36+
where
37+
P: Fn(&AccessMethodSetting) -> bool,
38+
{
3739
self.access_method_settings
3840
.iter_mut()
39-
.find(|api_access_method| *element == api_access_method.get_id())
41+
.find(|api_access_method| predicate(api_access_method))
42+
}
43+
44+
/// Search for a particular [`AccessMethod`] in `api_access_methods`.
45+
pub fn find_by_id(&self, element: &Id) -> Option<&AccessMethodSetting> {
46+
self.find(|api_access_method| *element == api_access_method.get_id())
47+
}
48+
49+
/// Search for a particular [`AccessMethod`] in `api_access_methods`.
50+
pub fn find_by_id_mut(&mut self, element: &Id) -> Option<&mut AccessMethodSetting> {
51+
self.find_mut(|api_access_method| *element == api_access_method.get_id())
4052
}
4153

4254
/// Equivalent to [`Vec::retain`].
@@ -52,6 +64,13 @@ impl Settings {
5264
self.access_method_settings.clone()
5365
}
5466

67+
/// Get a reference to the `Direct` access method instance of this [`Settings`].
68+
pub fn get_direct(&mut self) -> Option<&mut AccessMethodSetting> {
69+
self.find_mut(|access_method| {
70+
access_method.access_method == BuiltInAccessMethod::Direct.into()
71+
})
72+
}
73+
5574
pub fn direct() -> AccessMethodSetting {
5675
let method = BuiltInAccessMethod::Direct;
5776
AccessMethodSetting::new(method.canonical_name(), true, AccessMethod::from(method))

0 commit comments

Comments
 (0)