Question

I could not find a way of executing a script for the Guest account during login which executes every minute. They say using cron daemon is deprecated so it looks like I will be using launchd with .plist files.

Scenario: I have a public iMac I want to allow the general public to use the guest account and have it force log out every half-hour. I wrote a ruby script to check the login time and figure out time remaining. I can have it display a banner notification every 10 minutes using osascript and then make it log off my account. Problem is when I try to implement it for the Guest account it doesn't work.

The problem is when I am placing the .plist file inside /Library/LaunchDaemons since it runs after login and also runs as root. Running as root is important since I can have the privilege to shut down processes when time runs out. I need it to execute once every minute. This it the current plist file that does work when I login as my own username "owner" but not guest. Using org.user.plist

My original .plist file looked something like this

<?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>Label</key> 
        <string>org.user</string> 
    <key>Program</key> 
        <string>/usr/local/bin/notify-custom</string> 
    <key>RunAtLoad</key> 
        <true/> 
</dict> 
</plist>

Update 1 (Not a solution yet) .plist file which runs every 10 seconds for both Guest and my username

<?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>Label</key>
        <string>org.user</string>
    <key>ProgramArguments</key>
        <string>/usr/local/bin/notify-custom</string>
    <key>WatchPaths</key>
        <array>        
            <string>/Users/Guest/Library</string>
            <string>/Users/owner/Library</string>
        <array>
    </integer>
</dict>
</plist>

As a test to make sure the osascript notification banner pops up I have this code inside /usr/local/bin/notify-custom

#/bin/bash

#Using whoami would have shown me logged in as root under LaunchDaemon .plist
loggedinUser=`finger | awk 'NR==3{print $1}'`
#I need to manually run terminal and type sudo as guest for nextline to work
sudo -u $loggedinUser /usr/bin/osascript -e 'display notification "Test" with title "Banner Notification"'

Solution is below.

Was it helpful?

Solution 2

Solved. I have been working on this for a while. My solution finally does as I need and that is it starts up during login for the user Guest (and as an option I also have it starting up for myself user iMac1 just to display logged in time). I didn't see a simple way of putting the org.user.plist file in /Users/Guest/Library/LaunchAgents which would have theoretically launched it when the Guest logged in and the reason I gave up on that situation is since that folder isn't created until login.

What I did was put my .plist file it inside /Library/LaunchAgents/ which gets run for every user. That's fine since my code will distinguish the Guest user and take action (in this case log them out after set time.)

The final .plist file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
        <string>org.user</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/notify-custom</string>
    </array>
    <key>RunAtLoad</key>
        <true/>
    <key>StartInterval</key>
        <integer>60</integer>
</dict>
</plist>

Notice I added a RunAtLoad key since without it the script did run but waited a minute to fire it's first event. If instead I used WatchPaths key as @klanomath noted in his comment, then the script would run every 10 seconds since their must be activity happening in that folder regularly. I just wanted it to run every 60 seconds for now. I might change that timer later when I clean up the whole procedure with some more colorful warning dialogs written in Python.

Here is the ruby code inside /usr/local/bin/notify-custom that gets executed for every user login:

#!/usr/bin/ruby -w

require 'time'
require 'FileUtils'

loggedinUser=`finger|awk 'END{print $1}'`.strip
getloginTime=`finger|awk 'END{print}'|cut -c49-53`
getnowTime=`date|awk 'NR==1{print $4}'`[0..4]
loginTime=(Time.parse(getloginTime).to_i)
nowTime=(Time.parse(getnowTime).to_i)
diffSec=(nowTime-loginTime)
diffMin=(diffSec/60)
timeRemain=30-diffMin

#To see some console output while debugging
puts "getloginTime      =#{getloginTime}"
puts "getnowTime        =#{getnowTime}"
puts "loginTime=#{loginTime}"
puts "nowTime  =#{nowTime}"
puts "timeRemain=#{timeRemain}"

if loggedinUser == "Guest"
        open("/Users/#{loggedinUser}/Desktop/30 Minutes Max Use Per Day",'a'){|f| f.puts "With this new iMac, you are limited to a maximum of 1/2 hour use per day"}
        if timeRemain < 0
            `/usr/bin/osascript -e 'tell application "Finder" to set desktop picture to POSIX file "/Library/Desktop Pictures/Earth Horizon.jpg"'`
            `/usr/bin/osascript -e 'display notification "SHUTTING DOWN! Now= #{getnowTime}   LoggedInAt=#{getloginTime}   TimeRemain=#{timeRemain}" with title "Guest SHUTTING DOWN" sound name "Glass"'`
            `/usr/bin/osascript -e 'tell app "Terminal" to do script "sudo shutdown -h now"'`
        else
            `/usr/bin/osascript -e 'display notification "Now= #{getnowTime}     TimeRemain=#{timeRemain}" with title "#{loggedinUser} TIME LOGGED IN= #{getloginTime}" subtitle "User= #{loggedinUser}"'`
        end
else
        `/usr/bin/osascript -e 'display notification "Now= #{getnowTime}     TimeRemain=#{timeRemain}" with title "#{loggedinUser} TIME LOGGED IN= #{getloginTime}" subtitle "User= #{loggedinUser}"'`
end

Again note that if you use the LaunchDaemons instead, they run under the system root account while the Agents run on the logged in users account. Using the 2nd option I had to give the Guest user permission to run sudo shutdown as @klanomath mentioned below. This was done with running the command: $sudo visudo and adding the following to the end of the file:

Guest ALL=NOPASSWD: /sbin/shutdown

Also I wanted to only show the Guest account on the log on page so I hid my account from that screen with this command:

sudo dscl . create /Users/hiddenuser IsHidden 1

and if you change your mind you can bring it back with :

sudo dscl . create /Users/hiddenuser IsHidden 0

Thanks @klanomath and @user3439894

OTHER TIPS

In my opinion the following should work - it does in my VM! - launched as /Library/LaunchDaemons/org.user.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>Label</key>
    <string>org.user</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/touch</string>
        <string>-f</string>
        <string>/Users/Guest/Desktop/test.txt</string>
    </array>
    <key>UserName</key>
    <string>Guest</string>
    <key>GroupName</key>
    <string>_guest</string>
    <key>InitGroups</key>
    <true/>
    <key>WatchPaths</key>
    <array>
        <string>/Users/Guest/Library</string>
    </array>
</dict>
</plist>

As example task I use /usr/bin/touch -f /Users/Guest/Desktop/test.txt.

The trick here is that the complete content of the Guest folder is deleted after a log out. After a new Guest logs in the whole content is recreated from scratch. As soon as the folder /Users/Guest/Library is created, the example task (touch ...) is started due to the WatchPaths key.

Since the task/script/app has to be executed as guest you can't use launch agents because the path /Users/Guest/Library/LaunchAgents/ simply doesn't exist.

Use a launch daemon instead and run it as Guest/_guest. Your ruby script /usr/local/bin/notify-custom has to be world readable/executable? of course.


I also tried to run the task every 60 seconds - which works properly but throws some errors after the guest's log out. Probably it's better to implement the whole thing in the ruby script. Depending on your script your mileage may vary though.

If you have two different tasks to execute (e.g show a banner every 10 minutes with Ruby and a timer to force log out after 30 minutes) it's probably better to create two different launch daemons.

Licensed under: CC-BY-SA with attribution
Not affiliated with apple.stackexchange
scroll top