Discussion:
[launchd-dev] Starting Agents in specific GUI sessions
NuSkooler
2013-08-15 23:07:27 UTC
Permalink
I am working on a large project that includes a kernel module (that talks
with the daemon), a centralized daemon (that runs as root/privileged) and
agents that interact with a user logged into a graphical session.

In the Windows world, when the daemon needs to show UI it simply creates a
new instance of a agent in the targeted session using CreateProcessAsUser()
and friends. On OS X however, this type of functionality does not seem to
be present.

I've found discussions around the "bsexec" option of launchctl but this
appears to no longer function in 10.7+. Are there new APIs/ways of doing
this? I know the session ID (derived from audit session information/etc.)
but do not see away to connect the dots.

I know about registering plist's in /Library/LaunchAgents in order to have
the agents start at login, but there are still times in which I need to
start/stop new instances of agents "on the fly" from the daemon space.

Thanks,

Bryan Ashby
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/launchd-dev/attachments/20130815/c619f0a0/attachment.html>
Jerry Krinock
2013-08-16 00:36:29 UTC
Permalink
Hello, Bryan. Processes launched by agents can spawn other processes, and these other processes may be GUI processes. Processes may also install and load additional launch agents. Non-GUI processes (processes without a connection to the window server) can show rudimentary alerts using the CFUserNotification API.

Maybe we need to know a little more about what you're trying to do.
NuSkooler
2013-08-16 15:33:41 UTC
Permalink
Thanks for the reply --

Our product is a Parental Controls product. The kernel module is
responsible for traffic capture that, if it's interesting is then proxied
to/from the user space (running as root on *nix, system on Windows, etc.)
daemon. The daemon in turn occasionally needs to display UI to users. A
user may be in any graphical session on the box (e.g. Windows or OS X Fast
User Switching, Screen Sharing, ...). The UI itself is more than just
notifications. We allow decisions to be made by the user, prompt to login
to our product in some situations, etc.

The "backup plan" on OS X is to use the /Library/LaunchAgents plist system
so start agents at user login. This works for most of the cases, but there
are times such as in our update process that we need to stop all agents ->
update them -> restart them.

Hope that helps clarify,

Bryan
Post by Jerry Krinock
Hello, Bryan. Processes launched by agents can spawn other processes, and
these other processes may be GUI processes. Processes may also install and
load additional launch agents. Non-GUI processes (processes without a
connection to the window server) can show rudimentary alerts using the
CFUserNotification API.
Maybe we need to know a little more about what you're trying to do.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/launchd-dev/attachments/20130816/46895502/attachment.html>
Jerry Krinock
2013-08-16 16:17:40 UTC
Permalink
? daemon ? occasionally needs to display UI to users ? more than just notifications
I would say that you should write a little app (.app) to do that, and your daemon should launch it as needed. If this is for Mac OS X 10.7 or later, you should consider XPC. Someone who knows more about XPC than I do please correct me and elaborate.
there are times such as in our update process that we need to stop all agents -> update them -> restart them
In other words, you need to do in a program what a sysadmin can do "manually" using the launchctl(1) command-line tool. I have some old code in a public repository at github.com which has methods which do this brute force, by invoking launchctl via NSTask. But before you do something like that, I vaguely recall reading somewhere that there may now be proper API for this. But I can't find any reference to it.

Does anyone know of such an API in Mac OS X, or was I just dreaming?

Jerry

P.S. You didn't mention sandboxing. I think the install/update/start/stop LaunchAgents stuff is unlikely to be allowed if you're sandboxed.
Quinn &quot;The Eskimo!&quot;
2013-08-16 16:24:06 UTC
Permalink
Post by Jerry Krinock
I would say that you should write a little app (.app) to do that, and your daemon should launch it as needed.
Please don't have your daemon launch an app; that's an exercise fraught with compatibility liability. The most straightforward solution to this problem is to install a tiny GUI launchd agent [1]. It can then communicate with the daemon, allowing the daemon to know about all the login sessions and send messages to the agent in one specific session or all sessions. If the agent needs to display GUI, it can either do so directly or launch an app to do so. It's safe for a GUI launchd agent to launch an app because it is, by definition, running in a GUI login context.

Share and Enjoy
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

[1] I'm using the terms from Technote 2083 "Daemons and Agents", which I recommend you read.

<https://developer.apple.com/library/mac/#technotes/tn2083/_index.html>
Jerry Krinock
2013-08-19 13:23:24 UTC
Permalink
Post by Quinn &quot;The Eskimo!&quot;
The most straightforward solution to this problem is to install a tiny GUI launchd agent [1].
Thank you, Quinn. Nuskooler's issue was that his/her daemon needed to *occasionally* display GUI.

Is the GUI launchd agent which you recommend a separate thing, in addition to the daemon, or do you mean that the daemon itself should be a GUI launchd agent?

If this GUI launchd agent is a separate thing, should it be installed at startup, or does the daemon install it when needed?

Is there any API by which a daemon, or any program for that matter, can install another agent? In other words, is there a launchctl(2) equivalent to launchctl(1)? If not, is that intentional?

Thanks,

Jerry


[1] https://developer.apple.com/library/mac/#technotes/tn2083/_index.html
Quinn &quot;The Eskimo!&quot;
2013-08-21 09:29:26 UTC
Permalink
Post by Jerry Krinock
Nuskooler's issue was that his/her daemon needed to *occasionally* display GUI.
That's fine. The GUI launchd agent doesn't need to be heavyweight; you can make a tiny tool that simply listens for messages from the daemon and then launches the main GUI when necessary.
Post by Jerry Krinock
Is the GUI launchd agent which you recommend a separate thing, in addition to the daemon, or do you mean that the daemon itself should be a GUI launchd agent?
It must be a separate process (because all the environmental context things are per-process). Whether you use the same binary as the daemon (having it do different things based on command line arguments) is purely a question of packaging. Personally I would have a separate binary (because it makes it easier to make the agent lightweight, per the previous point).
Post by Jerry Krinock
If this GUI launchd agent is a separate thing, should it be installed at startup, or does the daemon install it when needed?
It should be installed when you install your product. It's launched by the system when a new login session is created and terminated when that login session is destroyed. This means there can be multiple instances running, for example, in the fast user switching case.
Post by Jerry Krinock
Is there any API by which a daemon, or any program for that matter, can install another agent? In other words, is there a launchctl(2) equivalent to launchctl(1)?
That'd be launchctl(3), but I get what you mean (-:

There are limited APIs for job management in the Server Management framework, but not sufficient to replace launchctl entirely.

Also, I have to stress that not even launchctl is enough to satisfy all install, upgrade and uninstall scenarios. You can read about it in this old thread.

<https://lists.macosforge.org/pipermail/launchd-dev/2010-February/000741.html>

Over the years I've tried various workarounds for this (like the "bsexec" stuff in the afore-mentioned post) but none of them are satisfactory. Specifically, they:

o always seemed like a hack

o never worked with pre-login launchd agents

o have broken on recent systems [1]

I'll repeat the take-home message from that post:

[...] there's only one way to solve the "first install problem"
that's guaranteed to be compatible in the long term: force a restart.
Post by Jerry Krinock
If not, is that intentional?
It definitely a matter of priorities.

Share and Enjoy
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

[1] When 10.7 decoupled the security context from the Mach bootstrap namespace, switching instead to the audit session ID.
Thomas Clement
2013-08-21 10:25:57 UTC
Permalink
Post by Quinn &quot;The Eskimo!&quot;
Also, I have to stress that not even launchctl is enough to satisfy all install, upgrade and uninstall scenarios. You can read about it in this old thread.
<https://lists.macosforge.org/pipermail/launchd-dev/2010-February/000741.html>
o always seemed like a hack
o never worked with pre-login launchd agents
o have broken on recent systems [1]
[...] there's only one way to solve the "first install problem"
that's guaranteed to be compatible in the long term: force a restart.
What about the "launchctl asuser" command? Seems helpful here, although it still feels kind of like a hack.

Thomas

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/launchd-dev/attachments/20130821/11c034ab/attachment.html>
Quinn &quot;The Eskimo!&quot;
2013-08-21 10:59:03 UTC
Permalink
Post by Thomas Clement
What about the "launchctl asuser" command?
Last I checked (which was on 10.7.x), "asuser" does not set the security context correctly.

Share and Enjoy
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
NuSkooler
2013-08-16 16:44:39 UTC
Permalink
Thanks again for your replies --

Maybe I didn't describe this well enough:

- The daemon is non-graphical of course and runs with or without a login
session as root.

- I *do* have GUI applications (the "agents") that are to show UI. As
mentioned, the "backup plan" is to use the plist system where they are
registered to load at login (/Library/LaunchAgents/*.plist). This works for
*most* of the cases.

I know how to manually launch an application that's either registered or
not *in the current session* via launchctl. The issue is launching one "in
to" another specific session.
Post by Quinn &quot;The Eskimo!&quot;
Please don't have your daemon launch an app; that's an exercise fraught
with compatibility liability.
Can you elaborate here more? I know they are different worlds, but we do
this on Windows without any issue. I already know and track sessions, know
when a user logs in or out, etc.
Post by Quinn &quot;The Eskimo!&quot;
The most straightforward solution to this problem is to install a tiny GUI
launchd agent [1]. It can then communicate with the daemon, allowing the
daemon to know about all the login sessions and send messages to the agent
in one specific session or all sessions. If the agent needs to display
GUI, it can either do so directly or launch an app to do so. It's safe for
a GUI launchd agent to launch an app because it is, by definition, running
in a GUI login context.
Again this is what I'm calling my "back up plan". It does work most of the
time, but there are edge cases in which it would be extremely desirable to
programmatically (from the daemon) launch said GUI agents into a users
session.

In the end, I think I can make this work with the /Library/LaunchAgents
plists perhaps with some KeepAlive logic, but it will require quite a few
works arounds in our code base, and it seems odd that there are no clean
APIs to programmatically target a session and launch a GUI agent in it from
a privileged (e.g. root) service/daemon.

Bryan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/launchd-dev/attachments/20130816/10ce3007/attachment.html>
Shawn Erickson
2013-08-16 20:24:08 UTC
Permalink
The supported and correct way is to use a Launch Agent for what you are attempting.

What you do on Windows shouldn't be applied directly on the Mac for technical and security model reasons.

-Shawn
Post by NuSkooler
Thanks again for your replies --
- The daemon is non-graphical of course and runs with or without a login session as root.
- I *do* have GUI applications (the "agents") that are to show UI. As mentioned, the "backup plan" is to use the plist system where they are registered to load at login (/Library/LaunchAgents/*.plist). This works for *most* of the cases.
I know how to manually launch an application that's either registered or not *in the current session* via launchctl. The issue is launching one "in to" another specific session.
Post by Quinn &quot;The Eskimo!&quot;
Please don't have your daemon launch an app; that's an exercise fraught with compatibility liability.
Can you elaborate here more? I know they are different worlds, but we do this on Windows without any issue. I already know and track sessions, know when a user logs in or out, etc.
Post by Quinn &quot;The Eskimo!&quot;
The most straightforward solution to this problem is to install a tiny GUI launchd agent [1]. It can then communicate with the daemon, allowing the daemon to know about all the login sessions and send messages to the agent in one specific session or all sessions. If the agent needs to display GUI, it can either do so directly or launch an app to do so. It's safe for a GUI launchd agent to launch an app because it is, by definition, running in a GUI login context.
Again this is what I'm calling my "back up plan". It does work most of the time, but there are edge cases in which it would be extremely desirable to programmatically (from the daemon) launch said GUI agents into a users session.
In the end, I think I can make this work with the /Library/LaunchAgents plists perhaps with some KeepAlive logic, but it will require quite a few works arounds in our code base, and it seems odd that there are no clean APIs to programmatically target a session and launch a GUI agent in it from a privileged (e.g. root) service/daemon.
Bryan
_______________________________________________
launchd-dev mailing list
launchd-dev at lists.macosforge.org
https://lists.macosforge.org/mailman/listinfo/launchd-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/launchd-dev/attachments/20130816/e092dc36/attachment.html>
Jerry Krinock
2013-08-16 23:43:09 UTC
Permalink
Post by NuSkooler
I *do* have GUI applications (the "agents")
I'm sure that part of the trouble here is the ambiguity which has been baked in to the documentation through history. The word "agent" in particular is a frequent sower of confusion.
Kevin Meaney
2013-08-17 08:11:38 UTC
Permalink
Post by Jerry Krinock
Post by NuSkooler
I *do* have GUI applications (the "agents")
I'm sure that part of the trouble here is the ambiguity which has been baked in to the documentation through history. The word "agent" in particular is a frequent sower of confusion.
This relative up to date documentation has a decent description of what Apple means by the various terms, Agents, Daemons, Login Items etc.

https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/DesigningDaemons.html

Kevin
Quinn &quot;The Eskimo!&quot;
2013-08-19 10:46:28 UTC
Permalink
Post by NuSkooler
Can you elaborate here more?
Historically, folks who've attempted to do this break with every major release of the OS. Things that have broken them in the past include:

o introduction of multiple users in 10.0

o introduction of fast user switching in 10.3

o introduction of launchd in 10.5

o changes to launchd session management in 10.6

o introduction of multiple GUI login sessions (via Screen Sharing) in 10.7

o security session management changes in 10.8

In contrast, the approach I've described works on 10.5 and later, and the analogous approach--which uses a login item rather than a GUI launchd agent--works on all versions of OS X (actually, as far back as System 7).
Post by NuSkooler
I know they are different worlds, but we do this on Windows without any issue. I already know and track sessions, know when a user logs in or out, etc.
Alas, your Windows experience will only take you so far on the Mac, and this is one of the places that things just don't match up.

Share and Enjoy
--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
NuSkooler
2013-08-21 16:21:39 UTC
Permalink
Thank you again for all of your replies!

My current plan for OS X is to register my Agent with launchd to start at
user login (e.g. once per session) and use the clean exit code members of
the plist to keep it alive unless explicitly shut down cleanly (In other
words, keep it alive in case it crashes; else let it exit)

As I mentioned, on Windows we do spawn some "Agent" processes on demand in
user(s) session spaces. I may be able to modify this slightly, but if not,
from what I understand this is the appropriate approach:

daemon (launchd spawned as daemon) -> {IPC} -> agent_shim (launchd spawned
as login agent) -> execute real_agent (will be in users context)

This seems like a fair approach, if needed.

As far as questions about "asuser": In 10.8 at least, this will create a
process as the user, but in the context of the launchd that launchctl
talked to. The rules for this can be seen in the launchctl code, but
basically if EUID or UID are root, use the root/system launchd, else use
the current users's launchd. TL;DR, it doesn't allow you to change the
session the process is launched in, only the user it's run as.

Bryan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/launchd-dev/attachments/20130821/71f385ef/attachment.html>
Loading...