My software installs a privileged daemon using the SMAppService api. After removing the executables and recompiling the software I sometimes find that it needs to be registered again. After doing this, i.e. ensuring the application is properly registered and enabled in Login Items & Extensions the helper is not run when initiated from XPC.
SMAppService.status has returned .enabled, and there is a valid job dictionary for the helper. I check the job dictionary with a function called updatePenaltyBoxStatus() that was given to me by a friend but I think originated from Apple.
If I logoff (or reboot), login again, manually open Login Items & Extensions to check registration, then retry the application, it works. I don't mind doing this but it is probably a bit much for a lot of my users.
Is there a reliable way to do this programatically?
Here is my Swift translation of updatePenaltyBoxStatus. I fetch the job dictionary with SMJobCopyDictionary() prior to calling isInPenaltyBox(). I also had to write C wrapper functions for the WIFEXITED and WIFEXITSTATUS macros.
func isInPenaltyBox(_ dict: Dictionary<String, Any>?) -> Bool
{
guard let jobDict = dict else {
// If the helper was in the penalty box, unregistering it doesn't change that. So don't override a previous helperInPenaltyBox value
return m_penalty_box
}
if let lastExitStatusObj = jobDict["LastExitStatus"] as? NSNumber {
let lastExitStatus = lastExitStatusObj.intValue
if wifexited(Int32(lastExitStatus)) == 0 {
// It might've stopped or exited due to a signal or whatever.
// Regardless, it didn't meet our criteria for winding up in the penalty box.
m_penalty_box = false
}
// Now get the exit status and check for `EX_CONFIG`.
let status = wexitstatus(Int32(lastExitStatus))
let newInPenaltyBox = status == EX_CONFIG
if m_penalty_box != newInPenaltyBox {
Logger.instance.log(
"Penalty box change: "
+ m_ident
+ " old: " + String(m_penalty_box)
+ " new: " + String(newInPenaltyBox))
}
m_penalty_box = newInPenaltyBox
}
return m_penalty_box
}
After removing the executables and recompiling the software I sometimes find that it needs to be registered again.
Before you start taking drastic measures here, it’s important to check whether this problem affects your users in practice. Because your users are unlikely to be doing the above (-: So, if you run through realistic user scenarios — that is, install your app on a ‘clean’ Mac and then upgrade to a new version — do you see this problem?
Note I tend to do this sort of testing on a VM, because that way I can get back to a fresh configuration by restoring from a snapshot. Try to avoid running tests like this on main work Mac, because the constant thrash of building and rebuilding tends to confuse various bits of the system.
I think originated from Apple.
If it did, it’s not doing supported stuff. Documentation for SMJobCopyDictionary is very thin on the ground. The official docs aren’t exactly helpful, other than to indicate that the API is deprecated. The doc comments in <ServiceManagement/ServiceManagement.h> are more useful, in that at least imply that the resulting dictionary reflects some part of the launchd job, as described in the launchd.plist man page. And that man page does not document LastExitStatus or any significance to EX_CONFIG.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"