Automated screensaver configuration in command line/shell script

rstansifer
New Contributor III

So I'm configuring some Default User Template LaunchAgents to configure a default screensaver for some of my company's most popular fan art. (It's actually been really well received by the team). They're designed as first-run images when they log into their forced-Local Active Directory users.

However, I'm getting very inconsistent results now...namely it's not working at all.

The .plist launches a Shell Script located in the /sbin/ folder. The odd thing is that it mostly works in an existing user.

The screensaver is meant to use "Flipup" with a specific folder of images, shuffle the pictures and set a start time of 5 minutes.

For example, I intentionally set my "default configuration user" screensaver first to the "Holiday Mobile," set the picture selection to "Cosmos" (one of the defaults) with a start time of 20 minutes and unchecked "shuffle images." Then I changed it to "Arabesque."

When I logged out and logged back in to kick in the script, the result was me getting "Holiday Mobile." However, when I went to "Desktop and Screensaver Settings," "Arabesque" was actually selected though the "Holiday Mobile" preview was showing with a grayed out "Screen Saver Options" below. (However, the start time did change to 5 minutes and when I did click on "Holiday Mobile," "Shuffle Slide Order" was checked properly.)

So in the test user, it's goes to Fan Art, activating Shuffle and changing the time.

But when I create a new user after adding the LaunchAgent .plist to the Default User Template, I get nothing but the standard "Computer Name" screensaver on a black background.

I am totally confused on why this is occurring. Any suggestions?

Here's the shell script:

#!/bin/sh

## Set Screensaver to Photo Slideshow
/usr/bin/defaults -currentHost write com.apple.screensaver 'CleanExit' -string "YES"
/usr/bin/defaults -currentHost write com.apple.screensaver 'PrefsVersion' -int "100"
/usr/bin/defaults -currentHost write com.apple.screensaver 'idleTime' -int "300"
/usr/bin/defaults -currentHost write com.apple.screensaver "moduleDict" -dict-add "path" -string "/System/Library/Frameworks/ScreenSaver.framework/Resources/iLifeSlideshows.saver"
/usr/bin/defaults -currentHost write com.apple.screensaver "moduleDict" -dict-add "type" -int "0" 
/usr/bin/defaults -currentHost write com.apple.screensaver 'ShowClock' -bool "false"
/usr/bin/defaults -currentHost write com.apple.screensaver 'tokenRemovalAction' -int "0"

## Set Type of Slideshow to "Flipup" (Results inconsistent)
/usr/bin/defaults -currentHost write com.apple.ScreenSaver.iLifeSlideshows 'styleKey' -string "Flipup" 

## Set location of photos to Fan Art 
/usr/bin/defaults -currentHost write com.apple.ScreenSaverPhotoChooser 'SelectedSource' -int "4"
/usr/bin/defaults -currentHost write com.apple.ScreenSaverPhotoChooser 'SelectedFolderPath' "/Manehattan Files/Manehattan Backgrounds/Fan Art"
/usr/bin/defaults -currentHost write com.apple.ScreenSaverPhotoChooser 'ShufflesPhotos' -bool "true"

## Removes the .plist LaunchAgent from inside the User Launch Agent Folder. 
rm -f ~/Library/LaunchAgents/set-screensaver.plist

exit
1 ACCEPTED SOLUTION

rstansifer
New Contributor III

I wanted to make sure I posted the solution @nessts provided on another page.

He showed me a very simple thing I needed to add to the end of my script:

killall cfprefsd

Solved the problem entirely. While if you go into System Preferences, it still shows the old screensaver being selected, but it works.

View solution in original post

17 REPLIES 17

gachowski
Valued Contributor II

I had the same issues in X.9 and kinda gave up... I did some more testing in the Beta of X.10 and I think Apple might have changed how the screen saver loads it preferences.

I am guessing that it's like Safai and to change those setting I had to use CoreFoundation framework.

https://managingosx.wordpress.com/2015/02/02/command-line-tools-via-python-and-cocoa/

That said I am guessing : )

C

PS it was easy for me to give up as it's not really a security issue and more of a nice to have : )

rstansifer
New Contributor III

That makes sense, gachowski. I actually use a Python script to change the desktop background for 10.9 and 10.10 (https://gist.github.com/gregneagle/6957826). I have found that the actual saved settings for the screensaver are in the ByHost preferences instead of the "standard" user Preferences. (I would love to know why on earth customization like that is in ByHost...)

Is there a way to directly change ByHost preferences without having the UUID (or whatever ByHost is using for identification these days)?

While I could get the UUID for all my systems, I'd like to future-proof this script so I don't have to manually add it or create 160 different versions of the file.

babaganouj
New Contributor II

We do something similar with a set of images for Security info. We use the /Library/LaunchAgents as the path which has been consistant in changing the UUID (com.apple.ScreenSaver.iLifeSlideShows.94396787-07A1-561E-816D-931A14B53A53.plist) for our accounts.

The flip command your using looks spot on to the command we use in setting to classic so you might need to look at: 1) Dubious permission error?
If you manually run the launch agent using launchctl unload and load maybe this error is coming up. LaunchAgents display that if the permissions on the file/folder have more than just the Admin having write access. Its caused me grief plenty of times.

2) Are you using ~/Library/LaunchAgents ? If not, moving it to /Library/LaunchAgents may help

I'll post a screen cap of our source folder and our script in case that helps out:
(There is a com.apple.screensaver.plist which changes the screensaver to be something different at login)

#!/bin/bash if [ -e $HOME/Library/.screensaverset ] then logger -t " ScreenSaver already set " "File Exists" exit else

Changes the defaults to be a slideshow picture

defaults -currentHost write com.apple.screensaver moduleDict -dict moduleName "iLifeSlideshow" path "/System/Library/Frameworks/ScreenSaver.framework/Resources/iLifeSlideshows.saver" type 0 defaults -currentHost write com.apple.ScreenSaverPhotoChooser LastViewedPhotoPath "" defaults -currentHost write com.apple.ScreenSaverPhotoChooser SelectedFolderPath /Library/Screen Savers/Default Collections/5-CBE defaults -currentHost write com.apple.ScreenSaverPhotoChooser SelectedSource -int 3 defaults -currentHost write com.apple.ScreenSaverPhotoChooser ShufflesPhotos -bool false defaults -currentHost write com.apple.ScreenSaver.iLifeSlideShows styleKey -string "Classic"

Check if the screensaver was already installed

touch $HOME/Library/.screensaverset logger -t " ScreenSaver has been set " "File Exists" fi exit 0

Here is what our current source folder is for the package

5f72e4200d024617bf9304a9b34a8ee5

rstansifer
New Contributor III

@rziemski

1) Dubious permission error?
If you manually run the launch agent using launchctl unload and load maybe this error is coming up. LaunchAgents display that if the permissions on the file/folder have more than just the Admin having write access. Its caused me grief plenty of times.

When I was first setting this up, I found that the only way I could get the .plist that runs it to work correctly was to have it as read-only. The shell script then removes the .plist with the rm command. (The shell is R/W for all, but I'll try switching it to Administrators only R/W while the rest to Read Only. Sadly, it didn't take.)

Here's the .plist that's actually in the ~/Library/LaunchAgents/ folder:

<?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>set-screensaver</string>
   <key>Program</key>
   <string>/sbin/set-screensaver</string>
   <key>RunAtLoad</key>
   <true/>
</dict>
</plist>

2) Are you using ~/Library/LaunchAgents ? If not, moving it to /Library/LaunchAgents may help

Yes and no. The .plist that tells the Mac to run the program is in the ~/Library/LaunchAgents (the .plist above). However, the bash script itself is in /sbin/. I actually need to have them in the user library as these Macs will be used by multiple users. That way, it's a first-launch for the first user, but will also run on first-launch for any other user.

And question: why do you have the Computer Name.saver in your package? Why does that need to be replaced?

mm2270
Legendary Contributor III

You can get the Mac's UUID and store it as a variable in the script and then use it as part of the path to the plist file.

#!/bin/sh

## Get UUID
UUID=$(ioreg -rd1 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformUUID/{print $4}')

## Get Logged In User
loggedInUser=$(ls -l /dev/console | awk '{print $3}')

## Set path to the current user's ByHost plist file
PLIST="/Users/${loggedInUser}/Library/Preferences/ByHost/com.apple.screensaver.${UUID}.plist"

## Now do your commands using the above "$PLIST" variable
/usr/bin/defaults write "$PLIST" CleanExit -string "YES"
# etc, etc.

nessts
Valued Contributor II

man defaults people.
or just type defaults at the command prompt and get help

defaults Command line interface to a user's defaults. Syntax: 'defaults' [-currentHost | -host <hostname>] followed by one of the following: read shows all defaults read <domain> shows defaults for given domain read <domain> <key> shows defaults for given domain, key read-type <domain> <key> shows the type for the given domain, key write <domain> <domain_rep> writes domain (overwrites existing) write <domain> <key> <value> writes key for domain rename <domain> <old_key> <new_key> renames old_key to new_key delete <domain> deletes domain delete <domain> <key> deletes key in domain import <domain> <path to plist> writes the plist at path to domain import <domain> - writes a plist from stdin to domain export <domain> <path to plist> saves domain as a binary plist to path export <domain> - writes domain as an xml plist to stdout domains lists all domains find <word> lists all entries containing word help print this help <domain> is ( <domain_name> | -app <application_name> | -globalDomain ) or a path to a file omitting the '.plist' extension <value> is one of: <value_rep> -string <string_value> -data <hex_digits> -int[eger] <integer_value> -float <floating-point_value> -bool[ean] (true | false | yes | no) -date <date_rep> -array <value1> <value2> ... -array-add <value1> <value2> ... -dict <key1> <value1> <key2> <value2> ... -dict-add <key1> <value1> ... Exit 255

as stated in the original post. not sure why you would want to go through all that extra coding.
so that means you run

defaults -currentHost write com.apple.screensaver CleanExit -string "YES"

```

rstansifer
New Contributor III

I wanted to make sure I posted the solution @nessts provided on another page.

He showed me a very simple thing I needed to add to the end of my script:

killall cfprefsd

Solved the problem entirely. While if you go into System Preferences, it still shows the old screensaver being selected, but it works.

znilsson
Contributor II

So here's mine, updated for my environment. I put our single image into a folder called 5-Experian, and put it into Library/Screen Savers/Default Collections. Everything in this script works except for choosing that folder. The screensaver does switch to the Classic module, but it stays on the National Geographic folder.

Any ideas on why it's not selecting my 5-Experian folder?

#!/bin/bash


## Changes the defaults to be a slideshow picture

defaults -currentHost write com.apple.screensaver moduleDict -dict moduleName "iLifeSlideshow" path "/System/Library/Frameworks/ScreenSaver.framework/Resources/iLifeSlideshows.saver" type 0
defaults -currentHost write com.apple.ScreenSaverPhotoChooser LastViewedPhotoPath ""
defaults -currentHost write com.apple.ScreenSaverPhotoChooser SelectedFolderPath /Library/Screen Savers/Default Collections/5-Experian
defaults -currentHost write com.apple.ScreenSaverPhotoChooser SelectedSource -int 3
defaults -currentHost write com.apple.ScreenSaverPhotoChooser ShufflesPhotos -bool false
defaults -currentHost write com.apple.ScreenSaver.iLifeSlideShows styleKey -string "Classic"
killall cfprefsd



exit 0

donmontalvo
Esteemed Contributor III

@znilsson just curious, does this work?

/usr/bin/defaults -currentHost write com.apple.ScreenSaverPhotoChooser SelectedFolderPath "/Library/Screen Savers/Default Collections/5-Experian"

I'm working on a similar project. Using your example, and the above syntax, here's what I get:

$ defaults read /Users/username/Library/Preferences/ByHost/com.apple.screensaver.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.plist 
{
    CleanExit = YES;
    PrefsVersion = 100;
    moduleDict =     {
        moduleName = iLifeSlideshow;
        path = "/System/Library/Frameworks/ScreenSaver.framework/Resources/iLifeSlideshows.saver";
        type = 0;
    };
}
$ defaults read /Users/username/Library/Preferences/ByHost/com.apple.ScreenSaverPhotoChooser.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.plist 
{
    LastViewedPhotoPath = "";
    SelectedFolderPath = "/Library/Screen Savers/Default Collections/5-Experian";
    SelectedSource = 3;
    ShufflesPhotos = 0;
}
$ defaults read /Users/username/Library/Preferences/ByHost/com.apple.ScreenSaver.iLifeSlideShows.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.plist 
{
    styleKey = Classic;
}
$

Broken out by individual settings:

$ defaults read /Users/username/Library/Preferences/ByHost/com.apple.screensaver.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.plist moduleDict
{
    moduleName = iLifeSlideshow;
    path = "/System/Library/Frameworks/ScreenSaver.framework/Resources/iLifeSlideshows.saver";
    type = 0;
}
$ defaults read /Users/username/Library/Preferences/ByHost/com.apple.ScreenSaverPhotoChooser.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.plist LastViewedPhotoPath

$ defaults read /Users/username/Library/Preferences/ByHost/com.apple.ScreenSaverPhotoChooser.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.plist SelectedFolderPath
/Library/Screen Savers/Default Collections/5-Experian
$ defaults read /Users/username/Library/Preferences/ByHost/com.apple.ScreenSaverPhotoChooser.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.plist SelectedSource
3
$ defaults read /Users/username/Library/Preferences/ByHost/com.apple.ScreenSaverPhotoChooser.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.plist ShufflesPhotos
0
$ defaults read /Users/username/Library/Preferences/ByHost/com.apple.ScreenSaver.iLifeSlideShows.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.plist styleKey
Classic
$

Or using -currentHost, running as user:

$ defaults -currentHost read com.apple.screensaver moduleDict
{
    moduleName = iLifeSlideshow;
    path = "/System/Library/Frameworks/ScreenSaver.framework/Resources/iLifeSlideshows.saver";
    type = 0;
}
$ defaults read -currentHost com.apple.ScreenSaverPhotoChooser LastViewedPhotoPath

$ defaults read -currentHost com.apple.ScreenSaverPhotoChooser SelectedFolderPath
/Library/Screen Savers/Default Collections/5-Experian
$ defaults read -currentHost com.apple.ScreenSaverPhotoChooser SelectedSource
3
$ defaults read -currentHost com.apple.ScreenSaverPhotoChooser ShufflesPhotos
0
$ defaults read -currentHost com.apple.ScreenSaver.iLifeSlideShows styleKey
Classic
$

Not sure though, if all these settings need to go into the ByHost plist. Will test when I get to the office.

Don

--
https://donmontalvo.com

znilsson
Contributor II

@donmontalvo Hey, thanks Don. Running that line did correctly choose the 5-Experian folder. Added into the rest of my script, it does seem to be working correctly now. Thanks so much.

Now I just need to get it to work through Casper, haha. This is where a "run as user" option would be super handy. What would be the best way to make this run as the logged in user? It does work when run locally on the target machine, so at this point it's just an issue of making the script run from Casper as the user rather than as the casper management account.

donmontalvo
Esteemed Contributor III

@znilsson wrote:

Now I just need to get it to work through Casper, haha. This is where a "run as user" option would be super handy.

Here ya go...

#!/bin/sh

CURRENTUSER=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0];  username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "
");'`

/usr/bin/defaults -currentHost write /Users/"${CURRENTUSER}"/Library/Preferences/ByHost/com.apple.screensaver moduleDict -dict moduleName "iLifeSlideshow" path "/System/Library/Frameworks/ScreenSaver.framework/Resources/iLifeSlideshows.saver" type 0
/usr/bin/defaults -currentHost write /Users/"${CURRENTUSER}"/Library/Preferences/ByHost/com.apple.ScreenSaverPhotoChooser LastViewedPhotoPath ""
/usr/bin/defaults -currentHost write /Users/"${CURRENTUSER}"/Library/Preferences/ByHost/com.apple.ScreenSaverPhotoChooser SelectedFolderPath "/Library/Screen Savers/Default Collections/5-Experian"
/usr/bin/defaults -currentHost write /Users/"${CURRENTUSER}"/Library/Preferences/ByHost/com.apple.ScreenSaverPhotoChooser SelectedSource -int 3
/usr/bin/defaults -currentHost write /Users/"${CURRENTUSER}"/Library/Preferences/ByHost/com.apple.ScreenSaverPhotoChooser ShufflesPhotos -bool false
/usr/bin/defaults -currentHost write /Users/"${CURRENTUSER}"/Library/Preferences/ByHost/com.apple.ScreenSaver.iLifeSlideShows styleKey -string "Classic"
/usr/sbin/chown "${CURRENTUSER}" /Users/"${CURRENTUSER}"/Library/Preferences/ByHost/com.apple.[Ss]creen[Ss]aver*.plist
/usr/bin/killall cfprefsd

exit 0
--
https://donmontalvo.com

znilsson
Contributor II

@donmontalvo Holy awesome, thanks Don. Works perfectly.

donmontalvo
Esteemed Contributor III

Thank @mm2270, @bentoms, @davidacland, et al., we just steal their ideas and techniques

--
https://donmontalvo.com

adamlalicker
New Contributor III

It looks like macOS 10.12 broke this script - Has anyone made this script work on 10.12

bmagistro
New Contributor II

This seems to be working.
https://www.jamf.com/jamf-nation/discussions/22713/configuration-profile-screen-saver-sierra

Edit: Adding the script I am using

#!/bin/sh
# Get user logged into console and put into variable "user"
user=`ls -l /dev/console | cut -d " " -f 4`


sudo -u $user defaults -currentHost write com.apple.screensaver CleanExit -string "YES"
sudo -u $user defaults -currentHost write com.apple.screensaver PrefsVersion -int 100
sudo -u $user defaults -currentHost write com.apple.screensaver showClock -string "YES"
sudo -u $user defaults -currentHost write com.apple.screensaver idleTime -int 120
sudo -u $user defaults -currentHost write com.apple.screensaver moduleDict -dict moduleName -string "iLifeSlideshows" path -string "/System/Library/Frameworks/ScreenSaver.framework/Resources/iLifeSlideshows.saver" type -int 0
sudo -u $user defaults -currentHost write com.apple.screensaver tokenRemovalAction -int 0

sudo -u $user defaults -currentHost write com.apple.ScreenSaverPhotoChooser LastViewedPhotoPath -string ""
sudo -u $user defaults -currentHost write com.apple.ScreenSaverPhotoChooser SelectedFolderPath -string "/Path/To/Pictures/To/Show"
sudo -u $user defaults -currentHost write com.apple.ScreenSaverPhotoChooser SelectedSource -int 3

sudo -u $user defaults -currentHost write com.apple.ScreenSaver.iLifeSlideShows styleKey -string "VintagePrints"

sudo killall -hup cfprefsd

cbooker
New Contributor III

The above script works great on Mac OS 10.11 and 10.12. Is there a version of this script that would work on Mac OS 10.7. We still have some older machines that I need to setup with a screen saver.

Thanks.

user-CrLmEPZVbD
New Contributor

I cannot get the above script to work for Mac OS 10.14. Can anyone test to see if the recommended scripts work for 10.14?