Reputation: 111
I'm trying to get a script to run every day at the same time to restart a Mac. I cannot use the built in schedule feature in the energy saving preference panel because I have apps running that prevent a normal restart. If I initiate the restart via the shutdown
command in terminal it forces the restart regardless of which apps might be preventing it.
I'm really new to shell scripting and launchd so I'm really as a novice level so please keep that in mind. This is my first post on here but I have periodically visited over the years and normally found the answer I was looking for.
Having tried various things I've kind of reached a bit of a road block. Where I'm at is that I have a .plist file in my /Library/LaunchDaemons folder that points to a script that should run at a certain time. I initially tried writing the .plist file in bbedit using code found on an online tutorial. It always failed to load into launchctl (either manually or via a restart). I then tried using the plist editor in Xcode and that seems to have worked better.
Having done that, the plist loads into launchctl. It also seems to try to run the script at the specified time. I've looked at the console and it seemsthat the Mac does try to run the script but it fails with this error:
com.apple.xpc.launchd[1] (com.apple.restart.sh[940]): Service exited with abnormal code: 1
I've tried running the script directly from the terminal eg sh scriptname
and the Mac restarts.
This is the script:
#!/bin/bash
shutdown -r now
I have the script saved in the /Library/Scripts folder.
This is the contents of the .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>com.apple.restart.sh</string>
<key>Program</key>
<string>/Library/Scripts/com.apple.restart.sh</string>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>9</integer>
<key>Minute</key>
<integer>50</integer>
</dict>
</dict>
</plist>
I'm now really at a loss as to what is going wrong because the .plist seems to successfully load into launchctl and the script tries to run at the specified time but fails. If the script is run manually it works.
My best guess is this is something to do with permissions or ownership but that's just my hunch. I've tried running chown root:wheel
on the files. That changed the error to:
abnormal code 78
Any help would be gratefully received.
Edit following requests from https://stackoverflow.com/users/2836621/mark-setchell
Latest plist code here:
<?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>com.erithacus.restart</string>
<key>Program</key>
<string>/Library/Scripts/com.erithacus.restart.sh</string>
<key>StandardOutPath</key>
<string>/tmp/com.erithacus.restart.stdout</string>
<key>StandardErrorPath</key>
<string>/tmp/com.erithacus.restart.stderr</string>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>11</integer>
<key>Minute</key>
<integer>10</integer>
</dict>
</dict>
</plist>
Latest script contents:
#!/bin/bash
/sbin/shutdown -r now >>/tmp/shutdown.log 2&>1
The contents of the .stdout file is empty.
The contents of the .stderr is as follows: /Library/Scripts/com.erithacus.restart.sh: line 3: 1: Read-only file system
Could this be to do with macOS Catalina having the OS on a read only partition?
EDIT 2. A solution.
Final plist file (com.erithacus.restart.sh) contents (had to be made using Xcode though):
<?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>com.erithacus.restart</string>
<key>Program</key>
<string>/Library/Scripts/com.erithacus.restart.sh</string>
<key>StandardOutPath</key>
<string>/tmp/com.erithacus.restart.stdout</string>
<key>StandardErrorPath</key>
<string>/tmp/com.erithacus.restart.stderr</string>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>12</integer>
<key>Minute</key>
<integer>10</integer>
</dict>
</dict>
</plist>
(obviously change the time under StartCalendarInterval
to suit)
This plist is saved in /Library/LaunchDaemons
Final script (com.erithacus.restart.sh):
#!/bin/bash
date +"%F %T Shutting down"
/sbin/shutdown -r now
Script is saved in /Library/Scripts
The key, and I think, fundamental thing that changed from the original is that I changed the ownership of the plist file to be root
using sudo chown root [plist file name]
.
Then I had to load it into launchctl
as root, ie sudo launchctl load [plist file name]
. The weird thing is that when loaded this way, it doesn't show up with the command launchctl list
but does if sudo launchctl list
.
This resulted in a successful timed restart of the machine by the system which overrides any apps that might otherwise prevent a restart. This is helpful for a server that I want to restart each night at 5am (so obviously the times in the plist file will be changed now to run at that time).
Particular thanks to @marksetchell who's error logging suggestions helped me to figure this out.
Upvotes: 11
Views: 16729
Reputation: 208003
A few things to check.
Firstly, make sure your script is executable with:
chmod +x /Library/Scripts/com.apple.restart.sh
Secondly, put the full path to shutdown
in your script to make sure it can be found:
#!/bin/bash
/sbin/shutdown -r now
I found that by running:
which shutdown
Output
/sbin/shutdown
Third, make sure your script is straight ASCII text and not an RTF or Pages or MS-Word document:
file /Library/Scripts/com.apple.restart.sh
Output
/Library/Scripts/com.apple.restart.sh: Bourne-Again shell script text executable, ASCII text
Fourth, test your script outside of launchctl
to make sure it runs stand-alone first:
/Library/Scripts/com.apple.restart.sh
Fifth, redirect the output like this in your plist
file:
<key>StandardOutPath</key>
<string>/tmp/com.apple.restart.stdout</string>
<key>StandardErrorPath</key>
<string>/tmp/com.apple.restart.stderr</string>
Upvotes: 7