Thank you for your response and apologies for taking so long to answer.
The only solution here is to structure your product so that:
The core functionality lives in the daemon, which the user can’t kill [1].
The user doesn’t gain anything by killing your agent.
BlockQuote
So essentially, my LaunchDaemon (in charge of the networking) and my LaunchAgent (in charge or capture SytemEvents) will always be running.
Post
Replies
Boosts
Views
Activity
I was unable to catch any signals but I ended up using the “PowerManagement” library, which allow to get notifications when system is going sleep, waking up.
Thank you for the links.
*otherwise it wouldn’t work when the user is logged in. I think there needs to be an option to edit your post 😆
*remote control application
Hence the frame not changing.
I have confirmed that my avcapture is no longer capturing any frames the moment i log out
And one more update, my NSXPC connection is now working. But I'm sill facing the same issue with the screen capturing. Everything works great until I log out, I get most of the image but I cannot see the textview to enter my password but everything else works (remote keyboard, mouse, etc) and the screen frame I get remains the same from then on (the cool red background image for Ventura)
Hello!
I crated the support incident and today as I was trying again and I realized my app was sandbox. After dealing with that this is what I have:
let service = daemonConnection.remoteObjectProxyWithErrorHandler { err in
print("Received error: \(err.localizedDescription)")
} as? MyServiceProtocol
which prints:
Received error: Couldn’t communicate with a helper application.
or
Received error: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service named com.serviceName" UserInfo={NSDebugDescription=connection to service named com.serviceName }
That sounds like a TCC issue. What does CGPreflightScreenCaptureAccess return?
that returns true and everything works as expected when capturing the screen until I log out. That's when the my frames stay in the background image of the OS and I'm unable to see the password textField but everything else continues to work (mouse and keyboard remote control).
I'm now trying to connect to my Daemon but I'm unable to do it. Here is how I'm testing.
Here is my daemon plist.
<?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>KeepAlive</key>
<true/>
<key>Label</key>
<string>com.myCompany.myCompanyServer</string>
<key>MachServices</key>
<dict>
<key>com.myCompany.myCompanyServer.xpc</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>path to my exec </string>
<string>service</string>
</array>
<key>QueueDirectories</key>
<array/>
<key>Item 0</key>
<string>path to queue directory</string>
</dict>
</plist>
At this point I'm certain my daemon is up a running. Here is what it'll be my agent running as root. For some reason my interruptionHandler always gets called right away.
final class DaemonConnection {
private var machServiceName = "com.myCompany.myCompanyServer.xpc"
private var daemonConnection: NSXPCConnection
init() {
daemonConnection = NSXPCConnection(machServiceName: machServiceName, options: [.privileged])
daemonConnection.interruptionHandler = interruptionHandler
daemonConnection.invalidationHandler = invalidationHandler
daemonConnection.resume()
}
private func interruptionHandler() {
print("daemon connection was interrrupted")
}
private func invalidationHandler() {
print("daemon conenction was invalidated")
}
}
and here is my daemon code:
private var machServiceName = "com.myCompany.myCompanyServer.xpc."
xpcListener = NSXPCListener(machServiceName: machServiceName)
xpcListener.delegate = self
xpcListener.resume()
then my listener delegate
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
Logger.shared.logDebug(logtag, " ******* received new xpc connection request from \(String(describing: newConnection.serviceName)) *****")
return true
}
when I run launchctl print system/myDaemon.plist I see
environment = {
XPC_SERVICE_NAME => com.myCompany.myCompanyServer
}
and I would expect that to be com.myCompany.myCompanyServer.xpc? so it seems I'm missing something? Let me know what you think
Hello Eskimo!
So I was able to run my program as a daemon and it is keeping the connection with my client but now I'm getting some strange behavior with my screen capturing. When going into the LoginWindow the frame I get is just the background image of Ventura OS but I'm able to remote control the mouse and keyboard as expected so it kind of makes me feel like it has something to do with my AVCapture session and it running in a global context? Still trying to find out more on this but I figured I would update my status.
Thank you for clarifying that, it makes sense now.
As far as the launch daemon, I need it to run the code that connects to my client so I’m assuming this would be by compiling my code into an executable? If so, how would I go about embedding the libraries I need into the executable? This is probably a whole different topic but any pointers would be greatly appreciated.
Hello Eskimo! I have a few more questions.
I was looking for some steps or more info on how everything would work, mainly the LaunchDaemon. This is how I understand things as of right now.
LaunchDaemon - daemon for managing the network and centralised command and control. It is a remote control application so this means that the daemon will not get shut down and I can keep the connection to my client alive when moving from login to user context?
LaunchDaemon - launches service in and stays alive when going from login context to user context?
Once in user context, daemon can launch the app to provide ui to configure the service.
I was trying to start my service executable through my plist saved in LaunchDaemons (same plist as when I was starting the app from LanchAgents) but that does not work.
Is there any specific requirements for the service executable?
My end goal is to provide a smooth transition when going from login to user context, which means not shutting down my service in order to maintain the connection with my client.
Running on latest Monterey version
Thank you for the prompt response! I have some more questions if you don't mind.
The agent is instantiated for each relevant session — remember that you can have multiple GUI login sessions running simultaneously — and each instance connects to the daemon via an IPC mechanism of your choice (although I generally recommend XPC) and do work on behalf of the daemon in each GUI context.
Does this mean that in general my application should be done in two parts, and xpc service that deals with the networking, that can run without interruption in any session (login and logged in user), and then my NSApplication to deal with the very little UI I need and to capture user screen and other input?
The standard pattern is have a launchd daemon that manages the network connection and then have a launchd agent that’s set up to both pre-login (LoginWindow) and GUI login (Aqua) sessions.
As I mentioned before, it is a remote control application I'm working on and this is how I understand the standard patter would work.
my daemon:
It would be launched and kept alive through any session (LoginWindow and when user is logged in)
it would be in charge of my networking code, connecting to my client and receiving input from the client
may agent
one for LoginWindow and one when the user is launched.
takes care of the NSMenu my application represent
it captures all the screen, user input and sends it to my daemon which will then send it to the client.
Right now my application works and it is all contained within the same project, but it sounds like it would be better to split it and have a xpc service and a agent? this way The XPC Service can run and maintain connection with the client "non stop"?