1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
//! Provides functions to handle or query the status of the Mullvad launch
//! daemon/system service on macOS.
//!
//! If the service exists but needs to be approved by the user, this status
//! must be checked so that the user can be directed to approve the launch
//! daemon in the system settings.
use objc2_foundation::{NSOperatingSystemVersion, NSProcessInfo, NSURL, ns_string};
use objc2_service_management::{SMAppService, SMAppServiceStatus};
/// Path to the plist that defines the Mullvad launch daemon.
/// It must be kept in sync with the path defined in
/// `dist-assets/pkg-scripts/postinstall`.
const DAEMON_PLIST_PATH: &str = "/Library/LaunchDaemons/net.mullvad.daemon.plist";
/// Authorization status of the Mullvad daemon.
#[repr(i32)]
pub enum LaunchDaemonStatus {
Ok = 0,
NotFound = 1,
NotAuthorized = 2,
Unknown = 3,
}
/// Return whether the daemon is running, not found, or is not authorized.
/// NOTE: On macos < 13, this function always returns `LaunchDaemonStatus::Ok`.
pub fn get_status() -> LaunchDaemonStatus {
// `SMAppService` does not exist if the major version is less than 13.
let os_version = get_os_version();
if os_version.majorVersion < 13 {
return LaunchDaemonStatus::Ok;
}
// SAFETY: daemon_plist_path is not an empty path & it is a valid system path.
// https://developer.apple.com/documentation/foundation/nsurl/fileurl(withpath:)#parameters
let daemon_plist_url = unsafe { NSURL::fileURLWithPath(ns_string!(DAEMON_PLIST_PATH)) };
get_status_for_url(&daemon_plist_url)
}
fn get_status_for_url(url: &NSURL) -> LaunchDaemonStatus {
// SAFETY: Apple does not state *anything* regarding safety requirements of this function:
// https://developer.apple.com/documentation/servicemanagement/smappservice/statusforlegacyplist(at:)
// But using a bit of reasoning & the [guidelines of objc2](https://github.com/madsmtm/objc2/blob/master/crates/header-translator/README.md#what-is-required-for-a-method-to-be-safe):
// """
// What is required for a method to be safe?
// 1. The method must not take a raw pointer; one could trivially pass ptr::invalid() and cause UB with that.
// 2. Any extra requirements that the method states in its documentation must be upheld.
// """
// we can conclude that:
// (1.) is upheld by the virtue of url being a reference, since references are always valid.
// (2.) is trivially upheld since Apple does not state safety requirements.
let status = unsafe { SMAppService::statusForLegacyURL(url) };
match status {
SMAppServiceStatus::NotRegistered | SMAppServiceStatus::NotFound => {
LaunchDaemonStatus::NotFound
}
SMAppServiceStatus::Enabled => LaunchDaemonStatus::Ok,
SMAppServiceStatus::RequiresApproval => LaunchDaemonStatus::NotAuthorized,
// Unknown status
_ => LaunchDaemonStatus::Unknown,
}
}
fn get_os_version() -> NSOperatingSystemVersion {
let process_info = NSProcessInfo::processInfo();
process_info.operatingSystemVersion()
}
|