SMAppService - helper is not started

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
    }
Answered by DTS Engineer in 886723022
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"

Accepted Answer
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"

Thank you Quinn. I always appreciate your help. It's a pity that sfltool doesn't allow removing all traces of a given job to get around these issues, but I take your point about testing on a clean VM. Thanks again

SMAppService - helper is not started
 
 
Q