12

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.

9
  • Which account does the script run under? Can you add debugging? (Like shutdown -r now >>/tmp/shutdown.log and make sure the user who runs the script has write access to that file.) Commented Jul 30, 2020 at 9:36
  • Also, probably your own scripts should not look like they belong to com.apple Commented Jul 30, 2020 at 9:36
  • Thanks, I'll give that a go. I've tried various names and somewhere I read someone saying to try that. Agree, it is weird. Will try changing it to something else again as well. My understanding of launchd is that they run by the system as root so I'm not sure the user is relevant? At least, I'm trying to arrive at an arrangement where the user isn't important and it just runs regardless. Commented Jul 30, 2020 at 9:41
  • I would imagine that it runs as root but then there should not be an error so that's my line of speculation at this point. I think there's a way with launchd to specify which user to run as. Commented Jul 30, 2020 at 10:32
  • 3
    This is a warning to all future Googlers: redirecting STDOUT or STDERR from inside a shell script launched from launchd is a route to madness. Do not do it unless you already understand what 3> does. Hopefully don't do it at all. As mentioned, launchd can redirect them for you. It's much happier if you go that way. Commented Mar 30, 2022 at 19:37

1 Answer 1

7

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>
Sign up to request clarification or add additional context in comments.

18 Comments

Thank you for such a thorough reply. I have been able to successfully run the script file from the terminal. Would that exclude some of your suggestions? Anyway, I checked the ASCII and the output is correct.
The steps are in order, so if you get to Step 4 you don't need to worry about anything in Steps 1..3. I presume it is still not working, so can you click edit under your question and add in your latest, greatest plist file, and also the contents of /tmp/com.apple.restart.stderr and corresponding stdout after you reboot your computer and load the launchctl job please?
You could also maybe a line to your shutdown script, just before you run /sbin/shutdown that does date +"%F %T Shutting down"
Edit: your last two comments appeared after I posted this. Will do as you've said. I've followed everything you've noted and this is the error from the .stderr log: /Library/Scripts/com.erithacus.restart.sh: line 3: 1: Read-only file system
One question, what does: date +"%F %T Shutting down" do?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.