-
Notifications
You must be signed in to change notification settings - Fork 1
/
AuxiliaryExecute.txt
161 lines (144 loc) · 5.14 KB
/
AuxiliaryExecute.txt
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//
// AuxiliaryExecute.swift
//
// Created by Speedyfriend67 on 22.07.24
//
//
// AuxiliaryExecute.swift
// MyYearWithGit
//
// Created by Lakr Aream on 2021/11/27.
//
import Foundation
/// Execute command or shell with posix, shared with AuxiliaryExecute.local
public class AuxiliaryExecute {
/// we do not recommend you to subclass this singleton
public static let local = AuxiliaryExecute()
// if binary not found when you call the shell api
// we will take some time to rebuild the bianry table each time
// -->>> this is a time-heavy-task
// so use binaryLocationFor(command:) to cache it if needed
// system path
internal var currentPath: [String] = []
// system binary table
internal var binaryTable: [String: String] = [:]
// for you to put your own search path
internal var extraSearchPath: [String] = []
// for you to set your own binary table and will be used firstly
// if you set nil here
// -> we will return nil even the binary found in system path
internal var overwriteTable: [String: String?] = [:]
// this value is used when providing 0 or negative timeout paramete
internal static let maxTimeoutValue: Double = 2_147_483_647
/// when reading from file pipe, must called from async queue
internal static let pipeControlQueue = DispatchQueue(
label: "wiki.qaq.AuxiliaryExecute.pipeRead",
attributes: .concurrent
)
/// when killing process or monitoring events from process, must called from async queue
/// we are making this queue serial queue so won't called at the same time when timeout
internal static let processControlQueue = DispatchQueue(
label: "wiki.qaq.AuxiliaryExecute.processControl",
attributes: []
)
/// used for setting binary table, avoid crash
internal let lock = NSLock()
/// nope!
private init() {
// no need to setup binary table
// we will make call to it when you call the shell api
// if you only use the spawn api
// we don't need to setup the hole table cause it‘s time-heavy-task
}
/// Execution Error, do the localization your self
public enum ExecuteError: Error, LocalizedError, Codable {
// not found in path
case commandNotFound
// invalid, may be missing, wrong permission or any other reason
case commandInvalid
// fcntl failed
case openFilePipeFailed
// posix failed
case posixSpawnFailed
// waitpid failed
case waitPidFailed
// timeout when execute
case timeout
}
public enum TerminationReason: Codable {
case exit(Int32)
case uncaughtSignal(Int32)
}
public struct PersonaOptions: Codable {
let uid: uid_t
let gid: gid_t
}
/// Execution Receipt
public struct ExecuteReceipt: Codable {
// exit code when process exit,
// or signal code when process terminated by signal
public let terminationReason: TerminationReason
// process pid that was when it is alive
// -1 means spawn failed in some situation
public let pid: Int
// wait result for final waitpid inside block at
// processSource - eventMask.exit, usually is pid
// -1 for other cases
public let wait: Int
// any error from us, not the command it self
// DOES NOT MEAN THAT THE COMMAND DONE WELL
public let error: ExecuteError?
// stdout
public let stdout: String
// stderr
public let stderr: String
/// General initialization of receipt object
/// - Parameters:
/// - terminationReason: termination reason
/// - pid: pid when process alive
/// - wait: wait result on waitpid
/// - error: error if any
/// - stdout: stdout
/// - stderr: stderr
internal init(
terminationReason: TerminationReason,
pid: Int,
wait: Int,
error: AuxiliaryExecute.ExecuteError?,
stdout: String,
stderr: String
) {
self.terminationReason = terminationReason
self.pid = pid
self.wait = wait
self.error = error
self.stdout = stdout
self.stderr = stderr
}
/// Template for making failure receipt
/// - Parameters:
/// - terminationReason: default uncaught signal 0
/// - pid: default -1
/// - wait: default -1
/// - error: error
/// - stdout: default empty
/// - stderr: default empty
internal static func failure(
terminationReason: TerminationReason = .uncaughtSignal(0),
pid: Int = -1,
wait: Int = -1,
error: AuxiliaryExecute.ExecuteError?,
stdout: String = "",
stderr: String = ""
) -> ExecuteReceipt {
.init(
terminationReason: terminationReason,
pid: pid,
wait: wait,
error: error,
stdout: stdout,
stderr: stderr
)
}
}
}