I was looking for time-based project similar to Outset (which runs boot and login scripts stored in various directories), and apparently there’s one already baked into macOS that will run daily, weekly, and monthly scripts.
Shoutout to @elios on the MacAdmins Slack for letting me know about
If you run
sudo launchctl list | grep periodic-, you’ll see that these launch daemons are running:
And, though I don’t love SIP in general, it’s great for this, because you can’t actually disable the launch daemons:
sudo launchctl unload /System/Library/LaunchDaemons/com.apple.periodic-daily.plist
/System/Library/LaunchDaemons/com.apple.periodic-daily.plist: Operation not permitted while System Integrity Protection is engaged
So that means as long as you can enforce your daily, weekly, and monthly scripts being in the right place, with the right permissions, and with the right hash, they’ll be run regularly-ish.
Locations of Scripts
You can find scripts in
/etc/periodic/monthly. You can also put your own scripts in there (root-owned, 755 permissions), and they’ll run alongside the ones that come with macOS.
/etc/defaults/periodic.conf, though, there’s another recommended place to put scripts:
# periodic script dirs
So that would be
/usr/local/etc/periodic/monthly. Having your scripts separated from the built-in scripts may be a good idea, even though they’ll run fine alongside the built-in scripts.
If your script has any
echo commands, the output will go to the appropriate log file (by default, those logs would be
/var/log/monthly.out), but there won’t necessarily (again, with the default settings) be any other indicators in the daily, weekly, and monthly logs that your scripts ran.
The format of the log seems to be a date/time, all the
echo statements from the scripts run, and then a closer like
-- End of daily/weekly/monthly output --.
If you don’t want to wait until the next day, week, or month, you can do some manual testing by running a command like this, for example:
sudo periodic daily
No TCC/PPPC support
Allowing full disk access to a script relies on giving that access to the actual parent process. As far as I can tell, the parent process is the
/usr/sbin/periodic binary, but that binary (although shipped with macOS) isn’t code-signed.