forked from intitni/CopilotForXcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLaunchAgentManager.swift
More file actions
116 lines (104 loc) · 3.74 KB
/
LaunchAgentManager.swift
File metadata and controls
116 lines (104 loc) · 3.74 KB
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
107
108
109
110
111
112
113
114
115
116
import Foundation
public struct LaunchAgentManager {
let serviceIdentifier: String
let executablePath: String
var launchAgentDirURL: URL {
FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent("Library/LaunchAgents")
}
var launchAgentPath: String {
launchAgentDirURL.appendingPathComponent("\(serviceIdentifier).plist").path
}
public init(serviceIdentifier: String, executablePath: String) {
self.serviceIdentifier = serviceIdentifier
self.executablePath = executablePath
}
public func setupLaunchAgent() async throws {
let content = """
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>RunAtLoad</key>
<true/>
<key>Label</key>
<string>\(serviceIdentifier)</string>
<key>Program</key>
<string>\(executablePath)</string>
<key>MachServices</key>
<dict>
<key>\(serviceIdentifier)</key>
<true/>
</dict>
</dict>
</plist>
"""
if !FileManager.default.fileExists(atPath: launchAgentDirURL.path) {
try FileManager.default.createDirectory(
at: launchAgentDirURL,
withIntermediateDirectories: false
)
}
FileManager.default.createFile(
atPath: launchAgentPath,
contents: content.data(using: .utf8)
)
try await launchctl("load", launchAgentPath)
}
public func removeLaunchAgent() async throws {
try await launchctl("unload", launchAgentPath)
try FileManager.default.removeItem(atPath: launchAgentPath)
}
public func restartLaunchAgent() async throws {
try await helper("reload-launch-agent", "--service-identifier", serviceIdentifier)
}
}
private func process(_ launchPath: String, _ args: [String]) async throws {
let task = Process()
task.launchPath = launchPath
task.arguments = args
task.environment = [
"PATH": "/usr/bin",
]
let outpipe = Pipe()
task.standardOutput = outpipe
return try await withUnsafeThrowingContinuation { continuation in
do {
task.terminationHandler = { process in
do {
if process.terminationStatus == 0 {
continuation.resume(returning: ())
} else {
if let data = try? outpipe.fileHandleForReading.readToEnd(),
let content = String(data: data, encoding: .utf8)
{
continuation.resume(throwing: E(errorDescription: content))
} else {
continuation.resume(
throwing: E(
errorDescription: "Unknown error."
)
)
}
}
}
}
try task.run()
} catch {
continuation.resume(throwing: error)
}
}
}
private func helper(_ args: String...) async throws {
guard let url = Bundle.main.executableURL?
.deletingLastPathComponent()
.appendingPathComponent("Helper")
else { throw E(errorDescription: "Unable to locate Helper.") }
return try await process(url.path, args)
}
private func launchctl(_ args: String...) async throws {
return try await process("/bin/launchctl", args)
}
struct E: Error, LocalizedError {
var errorDescription: String?
}