if(($ACT == 'edit' || $ACT == 'preview') && $INFO['editable']){ ?> } else { ?> } ?>
For a large software product, we use separate user-accounts for automatic builds. All build accounts are separate and non-administrator accounts.
The last step of the build-process packages the application and arranges application icons. This requires a Finder Process for the user running the build-process, which in turn requires the user to be logged-in.
Also for running select unit-tests, application windows are needed, which also depends on a local session.
The process are launched by a cron-job which launches the build/test processes inside a screen-session. This allows to log the output (via screen) and also to attach to a running process if needed.
All build-users are password-less (empty passphrase) which facilitates fast-user switching and have ssh-key for remote access (by default ssh does not allow login with an empty password, make sure to keep this config)
OSX only allows to auto-login a single user. There is no standard way to automatically log-in multiple users at startup.
Every time the machine boots, all build-users need to be logged in. With a password-less setup, it's easy enough to simply VNC into the machine (using a dedicated user-account/passphrase) and then fast-user-switch cycle through all users, but it's not always convenient to do so.
A combination of various mechanisms is needed.
The general way to switch users from the commanline is easy enough:
/System/Library/CoreServices/Menu\ Extras/User.menu/Contents/Resources/CGSession -switchToUserID <UID>
The crux with this command is, that it requires a session in the first place. Calling this command directly from launchd ~/Library/LaunchAgents/
does not work. CGSession
sends a signal to the currently active session. In order to chain multiple automatic logins, the command needs be executes in the same Mach bootstrap namespace as the loginwindow which spawned the current session.
To put this all together, the solution is as follows:
which leaves us with some small further complications:
The first is easily remedied. Using the `Script Editor` allows one to compile an AppleScript into an .app bundle. This applescript is intentionally left simple as to re-use it for various accounts:
autologin.app
- AppleScript
do shell script "$HOME/bin/autologin.sh"
To work around the admin requirement, all local users are allowed to run a script via sudo:
sudoers
– edit via `sudo visudo`
%localaccounts ALL=(root) NOPASSWD: /usr/local/bin/switchuser.sh
The script that is used for switching users, it takes 2 arguments: current-username and the UID to switch to. Since this script is invoked via sudo, it is not trivial to find the calling user's name so this needs to be passed as argument.
/usr/local/bin/switchuser.sh
#!/bin/bash LOGINPID=$(ps auxwwww \ | grep -v grep \ | fgrep /System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow \ | egrep "^$1" \ | awk '//{print $2;}') launchctl bsexec $LOGINPID \ "/System/Library/CoreServices/Menu Extras/User.menu/Contents/Resources/CGSession" \ -switchToUserID $2
and finally a small script to tie it all together:
$HOME/bin/autologin.sh
#!/bin/sh sudo /usr/local/bin/switchuser.sh `whoami` `id -u nextbuilduser`
This last script is the only one that differs for every user. nextbuilduser
needs to be adjusted to chain logins. All user-accounts can share autostart.app
as Login Item.
.
The actual /usr/local/bin/switchuser.sh
is a bit more elaborate to gracefully handle errors and prevent abuse with bogus parameters. There is a if test -z “$LOGINPID”; then
case to catch potential PID lookup errors, and also contains a if test -z “$1” -o -z “$2”; then
to verify that all parameters are given, as well as two `id`
checks to see if the script is running as root
and the target user exists. These are not essential and have been left out for brevity.
To sum up, the complete flow is:
$HOME/bin/autologin.app
after logon.$HOME/bin/autologin.sh
shell script is called from the .app`whoami`
and the UID of the next user to log in `id -u name`
./usr/local/bin/switchuser.sh
with admin privileges. Due to sudoers setup, no passphrase is needed for local-users to run this command.switchuser.sh
triggers the actual user-switch.While this is all pretty straight forward, it's a complex setup for a simple task.
Note: if using empty-passwords is not an option, there are means to inject plaintext passwords, the switchuser.sh can in turn call an applescript (the password would become a 3rd parameter). e.g.
osascript << EOF tell application "System Events" keystroke "yourpasswordhere" & return end tell EOF