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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
//! 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 objc::{class, msg_send, sel, sel_impl};
use std::ffi::CStr;
type Id = *mut objc::runtime::Object;
// Framework that contains `SMAppService`.
#[link(name = "ServiceManagement", kind = "framework")]
extern "C" {}
/// Returned by `[NSProcessInfo operatingSystemVersion]`. Contains the current
#[repr(C)]
#[derive(Debug)]
struct NSOperatingSystemVersion {
major_version: libc::c_longlong,
minor_version: libc::c_longlong,
patch_version: libc::c_longlong,
}
/// Implement Objective-C type encoding for the struct. Allows the `objc` crate
/// to perform function signature matching before performing calls into the Objective-C
/// runtime. This is needed to be able to enable the `verify_message` feature on `objc`.
unsafe impl objc::Encode for NSOperatingSystemVersion {
fn encode() -> objc::Encoding {
unsafe { objc::Encoding::from_str("{?=qqq}") }
}
}
/// 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.
if get_os_version().major_version < 13 {
return LaunchDaemonStatus::Ok;
}
get_status_for_url(&daemon_plist_url())
}
fn get_status_for_url(url: &Object) -> LaunchDaemonStatus {
let status: libc::c_long =
unsafe { msg_send![class!(SMAppService), statusForLegacyURL: url.0] };
match status {
// SMAppServiceStatusNotRegistered | SMAppServiceStatusNotFound
0 | 3 => LaunchDaemonStatus::NotFound,
// SMAppServiceStatusEnabled
1 => LaunchDaemonStatus::Ok,
// SMAppServiceStatusRequiresApproval
2 => LaunchDaemonStatus::NotAuthorized,
// Unknown status
_ => LaunchDaemonStatus::Unknown,
}
}
fn get_os_version() -> NSOperatingSystemVersion {
// the object is lazily instantiated, so we don't release it
let proc_info: Id = unsafe { msg_send![class!(NSProcessInfo), processInfo] };
unsafe { msg_send![proc_info, operatingSystemVersion] }
}
/// Returns an `NSURL` instance for `DAEMON_PLIST_PATH`.
fn daemon_plist_url() -> Object {
/// 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: &CStr = unsafe {
CStr::from_bytes_with_nul_unchecked(b"/Library/LaunchDaemons/net.mullvad.daemon.plist\0")
};
let nsstr_inst: Id = unsafe { msg_send![class!(NSString), alloc] };
let nsstr_inst: Id =
unsafe { msg_send![nsstr_inst, initWithUTF8String: DAEMON_PLIST_PATH.as_ptr()] };
let nsurl_inst: Id = unsafe { msg_send![class!(NSURL), alloc] };
let nsurl_inst: Id = unsafe { msg_send![nsurl_inst, initWithString: nsstr_inst] };
let _: () = unsafe { msg_send![nsstr_inst, release] };
assert!(!nsurl_inst.is_null());
Object(nsurl_inst)
}
/// Calls `[self.0 release]` when the wrapped instance is dropped.
struct Object(Id);
impl Drop for Object {
fn drop(&mut self) {
let _: () = unsafe { msg_send![self.0, release] };
}
}
|