MacOS – Running a script as root and presenting a GUI to logging in users

macospythonscript

I am having a programming decision problem and hope, that you can show me a solution or tell me, if my plan on how to solve the problem would be okay or not.

I am managing some macs and want to run a script every time a user logs in. The script (which I haven't written yet) needs to make some changes (or not). The changes need administrator privileges but the script also needs information from the currently logged in (or logging in) user.

Normaly, this would be a job for a LaunchAgent / Deamon, but here comes the problem: Scripts in /LaunchDeamons run as system administrator but start together with the system. They are not aware of users loging in or out and they may not show a GUI. ~/LaunchAgents run as the logged in user and get started when the user logs in. Also they may show a GUI. However they do not run as system administrator.

So this is my problem: running a script as root, but starting it whenever a user logs in and present a GUI to him: My solution would be to write two scripts:

Script 1 runs as system administrator and lies in /LaunchDeamons. This would have a Socket open for incomming connections (AKA "Server Socket").

Script 2 runs as the user beeing logged in and lies in ~/LaunchAgents. It presents a GUI to the user and sends the information to script 1 via a socket connection.

It doesn't seem to me as this would be the most reliable / straight forward solution, but I can't think of anything else. I also tried to find out how other programs solve this problem (munki for example, which presents a GUI to the user but installs programs as system administrator) but couldn't figure out how they did it.

I'd be happy for some comments on this!
Regards, Christian

PS: I plan to use python.

Best Answer

Sockets - Involved but Tried and Tested

Your solution of two scripts talking through a socket is the tried and tested approach. We did this for Power Manager and it has worked reliably from OS X 10.3 – 10.11.

You can use a tcp or unix domain socket for this purpose.

In our situation, the daemon pmd runs as a computer wide process and the user session daemon pmuser runs once per user. They talk to share information that a computer wide daemon can not safely obtain and to perform actions within specific user sessions.

The computer wide daemon can detect when a user logs in. This is done through the SystemConfiguration API SCDynamicStoreKeyCreateConsoleUser.

The graphical script must be launched within the graphical user session; a launchd job in /Library/LaunchDaemons with the key value pair LimitLoadToSessionType: Aqua set will ensure this.

Depending on the information you need to gather from the user, I expect what you want to achieve is possible with Power Manager; feel free to get in touch.

launchctl's bsexec

Recent versions of OS X include a launchctl that promises what you need: the ability to launch a process within a user session. See How to launch a GUI application in another user's graphical session? for the details and likely problems you will encounter.

If you are deploying on OS X 10.10 and later, investigate launchd's new domain capabilities. A complete rewrite in OS X 10.10 opened up many as yet not completely documented options. The launchd mailing list is a treasure trove of information.