mirror of
https://github.com/munki/munki.git
synced 2026-04-28 00:13:45 -05:00
Basic implementation of supervisor binary
This commit is contained in:
@@ -216,6 +216,9 @@
|
||||
C0684DFA2DBFE8130091E774 /* bootstrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21332C8793720023E9D9 /* bootstrapping.swift */; };
|
||||
C0684DFB2DBFE8130091E774 /* bootstrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21332C8793720023E9D9 /* bootstrapping.swift */; };
|
||||
C0684DFC2DBFE8130091E774 /* bootstrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21332C8793720023E9D9 /* bootstrapping.swift */; };
|
||||
C0684E212DBFF81B0091E774 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = C0684E202DBFF81B0091E774 /* ArgumentParser */; };
|
||||
C0684E232DC0029B0091E774 /* stderrout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C008A0552D25B20E0073ADBA /* stderrout.swift */; };
|
||||
C0684E252DC008A30091E774 /* UNIXProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21392C88CA1F0023E9D9 /* UNIXProcessInfo.swift */; };
|
||||
C06C21342C8793720023E9D9 /* bootstrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21332C8793720023E9D9 /* bootstrapping.swift */; };
|
||||
C06C21352C8793720023E9D9 /* bootstrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21332C8793720023E9D9 /* bootstrapping.swift */; };
|
||||
C06C213A2C88CA1F0023E9D9 /* UNIXProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21392C88CA1F0023E9D9 /* UNIXProcessInfo.swift */; };
|
||||
@@ -484,6 +487,15 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
C0684DFF2DBFF33E0091E774 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
C07A6FA32C2A82B400090743 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -620,6 +632,7 @@
|
||||
C05DB2022DAC53150081FACD /* manifestutil */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = manifestutil; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C05DB2122DAC67760081FACD /* manifestutil-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "manifestutil-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
C0684DD12DBFDBD20091E774 /* precache_agent */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = precache_agent; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C0684E012DBFF33E0091E774 /* supervisor */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = supervisor; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C06C21332C8793720023E9D9 /* bootstrapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = bootstrapping.swift; sourceTree = "<group>"; };
|
||||
C06C21392C88CA1F0023E9D9 /* UNIXProcessInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNIXProcessInfo.swift; sourceTree = "<group>"; };
|
||||
C06C213C2C88CACE0023E9D9 /* SignalHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalHandler.swift; sourceTree = "<group>"; };
|
||||
@@ -687,6 +700,7 @@
|
||||
C00519A22D2A5B850060DDB6 /* authrestartd */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = authrestartd; sourceTree = "<group>"; };
|
||||
C05DB2032DAC53150081FACD /* manifestutil */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = manifestutil; sourceTree = "<group>"; };
|
||||
C0684DD22DBFDBD20091E774 /* precache_agent */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = precache_agent; sourceTree = "<group>"; };
|
||||
C0684E022DBFF33E0091E774 /* supervisor */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = supervisor; sourceTree = "<group>"; };
|
||||
C0D66AC02D2CA1DE009EF807 /* logouthelper */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = logouthelper; sourceTree = "<group>"; };
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
@@ -739,6 +753,14 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C0684DFE2DBFF33E0091E774 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C0684E212DBFF81B0091E774 /* ArgumentParser in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C07A6FA22C2A82B400090743 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -913,6 +935,7 @@
|
||||
C0D66AC02D2CA1DE009EF807 /* logouthelper */,
|
||||
C05DB2032DAC53150081FACD /* manifestutil */,
|
||||
C0684DD22DBFDBD20091E774 /* precache_agent */,
|
||||
C0684E022DBFF33E0091E774 /* supervisor */,
|
||||
C07A6FA62C2A82B400090743 /* Products */,
|
||||
C01364502C311DFA008DB215 /* Frameworks */,
|
||||
);
|
||||
@@ -936,6 +959,7 @@
|
||||
C0D66ABF2D2CA1DE009EF807 /* logouthelper */,
|
||||
C05DB2022DAC53150081FACD /* manifestutil */,
|
||||
C0684DD12DBFDBD20091E774 /* precache_agent */,
|
||||
C0684E012DBFF33E0091E774 /* supervisor */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -1240,6 +1264,29 @@
|
||||
productReference = C0684DD12DBFDBD20091E774 /* precache_agent */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
C0684E002DBFF33E0091E774 /* supervisor */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = C0684E052DBFF33E0091E774 /* Build configuration list for PBXNativeTarget "supervisor" */;
|
||||
buildPhases = (
|
||||
C0684DFD2DBFF33E0091E774 /* Sources */,
|
||||
C0684DFE2DBFF33E0091E774 /* Frameworks */,
|
||||
C0684DFF2DBFF33E0091E774 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
C0684E022DBFF33E0091E774 /* supervisor */,
|
||||
);
|
||||
name = supervisor;
|
||||
packageProductDependencies = (
|
||||
C0684E202DBFF81B0091E774 /* ArgumentParser */,
|
||||
);
|
||||
productName = supervisor;
|
||||
productReference = C0684E012DBFF33E0091E774 /* supervisor */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
C07A6FA42C2A82B400090743 /* managedsoftwareupdate */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = C07A6FAC2C2A82B400090743 /* Build configuration list for PBXNativeTarget "managedsoftwareupdate" */;
|
||||
@@ -1447,6 +1494,9 @@
|
||||
C0684DD02DBFDBD10091E774 = {
|
||||
CreatedOnToolsVersion = 16.3;
|
||||
};
|
||||
C0684E002DBFF33E0091E774 = {
|
||||
CreatedOnToolsVersion = 16.3;
|
||||
};
|
||||
C07A6FA42C2A82B400090743 = {
|
||||
CreatedOnToolsVersion = 15.4;
|
||||
LastSwiftMigration = 1540;
|
||||
@@ -1510,6 +1560,7 @@
|
||||
C0D66ABE2D2CA1DE009EF807 /* logouthelper */,
|
||||
C05DB2012DAC53150081FACD /* manifestutil */,
|
||||
C0684DD02DBFDBD10091E774 /* precache_agent */,
|
||||
C0684E002DBFF33E0091E774 /* supervisor */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -1693,6 +1744,15 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C0684DFD2DBFF33E0091E774 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C0684E252DC008A30091E774 /* UNIXProcessInfo.swift in Sources */,
|
||||
C0684E232DC0029B0091E774 /* stderrout.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C07A6FA12C2A82B400090743 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -2224,6 +2284,34 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
C0684E062DBFF33E0091E774 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.googlecode.munki.supervisor;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
C0684E072DBFF33E0091E774 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.googlecode.munki.supervisor;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
C07A6FAA2C2A82B400090743 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@@ -2685,6 +2773,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
C0684E052DBFF33E0091E774 /* Build configuration list for PBXNativeTarget "supervisor" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
C0684E062DBFF33E0091E774 /* Debug */,
|
||||
C0684E072DBFF33E0091E774 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
C07A6FA02C2A82B400090743 /* Build configuration list for PBXProject "munki" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
@@ -2812,6 +2909,11 @@
|
||||
package = C0B715BC2DA6F43E00F255FB /* XCRemoteSwiftPackageReference "swift-certificates" */;
|
||||
productName = X509;
|
||||
};
|
||||
C0684E202DBFF81B0091E774 /* ArgumentParser */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = C0E994182C5C10E1006FDF44 /* XCRemoteSwiftPackageReference "swift-argument-parser" */;
|
||||
productName = ArgumentParser;
|
||||
};
|
||||
C0848D1E2C94D4610008B463 /* ArgumentParser */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = C0E994182C5C10E1006FDF44 /* XCRemoteSwiftPackageReference "swift-argument-parser" */;
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
//
|
||||
// supervisor.swift
|
||||
// supervisor
|
||||
//
|
||||
// Created by Greg Neagle on 4/28/25.
|
||||
//
|
||||
// Copyright 2024-2025 Greg Neagle.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import ArgumentParser
|
||||
import Foundation
|
||||
|
||||
private let PROCESS_DID_NOT_START: Int32 = 127
|
||||
private let PROCESS_TIMED_OUT: Int32 = 126
|
||||
private let signalName = [
|
||||
SIGINT: "SIGINT",
|
||||
SIGTERM: "SIGTERM",
|
||||
]
|
||||
|
||||
func signalHandler(_ sig: Int32) -> DispatchSourceSignal {
|
||||
// the intent here is to kill our child process(es) when we get a SIGINT or SIGTERM
|
||||
// (sadly we can't do it for SIGKILL) so they don't keep running if we're stopped
|
||||
// by the user (or killed by another process)
|
||||
signal(sig, SIG_IGN) // // Make sure the signal does not terminate the application.
|
||||
|
||||
let sigSrc = DispatchSource.makeSignalSource(signal: sig, queue: .main)
|
||||
sigSrc.setEventHandler {
|
||||
printStderr("Got signal \(signalName[sig] ?? String(sig))")
|
||||
// kill all our child processes
|
||||
let ourPid = ProcessInfo.processInfo.processIdentifier
|
||||
for task in processesWithPPID(ourPid) {
|
||||
printStderr("Sending signal \(signalName[sig] ?? String(sig)) to \(task.command), pid \(task.pid)...")
|
||||
let osErr = kill(task.pid, sig)
|
||||
if osErr != noErr {
|
||||
printStderr("Got err \(osErr) when sending \(signalName[sig] ?? String(sig)) to \(task.command), pid \(task.pid)")
|
||||
}
|
||||
}
|
||||
// reset the signal handler to default
|
||||
signal(sig, SIG_DFL)
|
||||
kill(ourPid, sig)
|
||||
}
|
||||
return sigSrc
|
||||
}
|
||||
|
||||
class SupervisorProcessRunner {
|
||||
let task = Process()
|
||||
var timeout: Int = 0
|
||||
|
||||
init(_ command: String, arguments: [String] = [], timeout: Int = 0) {
|
||||
task.executableURL = URL(fileURLWithPath: command)
|
||||
task.arguments = arguments
|
||||
self.timeout = timeout
|
||||
}
|
||||
|
||||
deinit {
|
||||
// make sure the task gets terminated
|
||||
killTask()
|
||||
}
|
||||
|
||||
func killTask() {
|
||||
let KILL_WAIT_TIME_USEC = useconds_t(1_000_000)
|
||||
task.terminate() // sends SIGTERM
|
||||
usleep(KILL_WAIT_TIME_USEC)
|
||||
if !task.isRunning {
|
||||
return
|
||||
}
|
||||
let pid = task.processIdentifier
|
||||
_signal.kill(pid, SIGKILL)
|
||||
usleep(KILL_WAIT_TIME_USEC)
|
||||
if task.isRunning {
|
||||
// log("pid \(pid) won't die")
|
||||
}
|
||||
}
|
||||
|
||||
func run() async -> Int32 {
|
||||
var deadline: Date?
|
||||
if !task.isRunning {
|
||||
do {
|
||||
if timeout > 0 {
|
||||
deadline = Date().addingTimeInterval(TimeInterval(timeout))
|
||||
}
|
||||
try task.run()
|
||||
} catch {
|
||||
// task didn't start
|
||||
printStderr("ERROR running \(task.executableURL?.path ?? "")")
|
||||
printStderr(error.localizedDescription)
|
||||
return PROCESS_DID_NOT_START
|
||||
}
|
||||
}
|
||||
while task.isRunning {
|
||||
// loop until process exits
|
||||
if let deadline {
|
||||
if Date() >= deadline {
|
||||
printStderr("ERROR: \(task.executableURL?.path ?? "") timed out after \(timeout) seconds")
|
||||
killTask()
|
||||
return PROCESS_TIMED_OUT
|
||||
}
|
||||
}
|
||||
await Task.yield()
|
||||
}
|
||||
return task.terminationStatus
|
||||
}
|
||||
}
|
||||
|
||||
class Supervisor {
|
||||
var timeout: Int
|
||||
var delayRandom: Int
|
||||
var command: String
|
||||
var arguments: [String]
|
||||
|
||||
init(timeout: Int, delayRandom: Int, command: String, arguments: [String]) {
|
||||
self.timeout = timeout
|
||||
self.delayRandom = delayRandom
|
||||
self.command = command
|
||||
self.arguments = arguments
|
||||
}
|
||||
|
||||
func execute() async -> Int32 {
|
||||
// log("Executing \(command) with arguments: \(arguments)")
|
||||
if delayRandom > 0 {
|
||||
let randomDelay = Int.random(in: 0 ... delayRandom)
|
||||
usleep(useconds_t(randomDelay * 1_000_000))
|
||||
}
|
||||
return await SupervisorProcessRunner(command, arguments: arguments, timeout: timeout).run()
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
struct SupervisorCommand: AsyncParsableCommand {
|
||||
static var configuration = CommandConfiguration(
|
||||
usage: "supervisor [options] -- <path_to_executable> [arguments]"
|
||||
)
|
||||
|
||||
@Option(help: ArgumentHelp("after n seconds, terminate the executable",
|
||||
discussion: "0 seconds means never timeout",
|
||||
valueName: "n")
|
||||
)
|
||||
var timeout: Int = 0
|
||||
|
||||
@Option(name: [.customLong("delayrandom")],
|
||||
help: ArgumentHelp("delay the execution of executable by random seconds up to n", valueName: "n"))
|
||||
var delayRandom: Int = 0
|
||||
|
||||
@Argument(parsing: .postTerminator,
|
||||
help: ArgumentHelp(valueName: "path_to_executable [arguments]"))
|
||||
var commandAndArgs: [String]
|
||||
|
||||
mutating func run() async throws {
|
||||
// install handlers for SIGINT and SIGTERM
|
||||
let sigintSrc = signalHandler(SIGINT)
|
||||
sigintSrc.activate()
|
||||
let sigtermSrc = signalHandler(SIGTERM)
|
||||
sigtermSrc.activate()
|
||||
|
||||
let command = commandAndArgs[0]
|
||||
let arguments = Array(commandAndArgs[1...])
|
||||
throw await ExitCode(
|
||||
Supervisor(
|
||||
timeout: timeout,
|
||||
delayRandom: delayRandom,
|
||||
command: command,
|
||||
arguments: arguments
|
||||
).execute()
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user