diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3c9ce9..40f592c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
+### Added
+- macos LaunchAgent support. Install with `make install-launchagent` and activate with `make activate-launchagent`. See [README.md](README.md) for details.
## [5.0.0] - 2022-02-08
### Added
diff --git a/Library/LaunchAgents/com.github.erikw.restic-automatic-backup.plist b/Library/LaunchAgents/com.github.erikw.restic-automatic-backup.plist
new file mode 100644
index 0000000..ca468c0
--- /dev/null
+++ b/Library/LaunchAgents/com.github.erikw.restic-automatic-backup.plist
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+ Label
+ com.github.erikw.restic-automatic-backup
+ ProgramArguments
+
+
+ /bin/bash
+ -c
+ source /usr/local/etc/restic/erikw.env.sh && /usr/local/bin/restic_backup.sh >>$HOME/$LOG_OUT 2>>$HOME/$LOG_ERR
+
+ EnvironmentVariables
+
+ PATH
+ /usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
+ LOG_OUT
+ /Library/Logs/restic/restic_stdout.log
+ LOG_ERR
+ /Library/Logs/restic/restic_stderr.log
+
+ RunAtLoad
+
+
+ StartCalendarInterval
+
+
+ Hour
+ 19
+ Minute
+ 0
+
+
+
+
diff --git a/Makefile b/Makefile
index ecc420d..a4c1b28 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,4 @@
+# TODO add install for launchagent completely, and unisntall target using bootstrap?
#### Notes ####################################################################
# This build process is done in three stages (out-of-source build):
# 1. copy source files to the local build directory.
@@ -17,7 +18,8 @@
.PHONY: help clean uninstall \
install-systemd install-cron \
install-targets-script install-targets-conf install-targets-systemd \
- install-targets-cron
+ install-targets-cron \
+ activate-launchagent deactivate-launchagent
#### Macros ###################################################################
NOW := $(shell date +%Y-%m-%d_%H:%M:%S)
@@ -38,46 +40,59 @@ MKDIR_PARENTS=sh -c '\
test -d $$dir || mkdir -p $$dir \
' MKDIR_PARENTS
+# LaunchAgent names.
+UID := $(shell id -u)
+LAUNCHAGENT = com.github.erikw.restic-automatic-backup
+LAUNCHAGENT_TARGET = gui/$(UID)/$(LAUNCHAGENT)
+
# Source directories.
-DIR_SCRIPT = bin
-DIR_CONF = etc/restic
-DIR_SYSTEMD = usr/lib/systemd/system
-DIR_CRON = etc/cron.d
+DIR_SCRIPT = bin
+DIR_CONF = etc/restic
+DIR_SYSTEMD = usr/lib/systemd/system
+DIR_CRON = etc/cron.d
+DIR_LAUNCHAGENT = Library/LaunchAgents
# Source files.
SRCS_SCRIPT = $(filter-out %cron_mail, $(wildcard $(DIR_SCRIPT)/*))
SRCS_CONF = $(wildcard $(DIR_CONF)/*)
SRCS_SYSTEMD = $(wildcard $(DIR_SYSTEMD)/*)
SRCS_CRON = $(wildcard $(DIR_CRON)/*)
+SRCS_LAUNCHAGENT= $(wildcard $(DIR_LAUNCHAGENT)/*)
# Local build directory. Sources will be copied here,
# modified and then installed from this directory.
-BUILD_DIR := build
-BUILD_DIR_SCRIPT = $(BUILD_DIR)/$(DIR_SCRIPT)
-BUILD_DIR_CONF = $(BUILD_DIR)/$(DIR_CONF)
-BUILD_DIR_SYSTEMD = $(BUILD_DIR)/$(DIR_SYSTEMD)
-BUILD_DIR_CRON = $(BUILD_DIR)/$(DIR_CRON)
+BUILD_DIR := build
+BUILD_DIR_SCRIPT = $(BUILD_DIR)/$(DIR_SCRIPT)
+BUILD_DIR_CONF = $(BUILD_DIR)/$(DIR_CONF)
+BUILD_DIR_SYSTEMD = $(BUILD_DIR)/$(DIR_SYSTEMD)
+BUILD_DIR_CRON = $(BUILD_DIR)/$(DIR_CRON)
+BUILD_DIR_LAUNCHAGENT = $(BUILD_DIR)/$(DIR_LAUNCHAGENT)
# Sources copied to build directory.
-BUILD_SRCS_SCRIPT = $(addprefix $(BUILD_DIR)/, $(SRCS_SCRIPT))
-BUILD_SRCS_CONF = $(addprefix $(BUILD_DIR)/, $(SRCS_CONF))
-BUILD_SRCS_SYSTEMD = $(addprefix $(BUILD_DIR)/, $(SRCS_SYSTEMD))
-BUILD_SRCS_CRON = $(addprefix $(BUILD_DIR)/, $(SRCS_CRON))
+BUILD_SRCS_SCRIPT = $(addprefix $(BUILD_DIR)/, $(SRCS_SCRIPT))
+BUILD_SRCS_CONF = $(addprefix $(BUILD_DIR)/, $(SRCS_CONF))
+BUILD_SRCS_SYSTEMD = $(addprefix $(BUILD_DIR)/, $(SRCS_SYSTEMD))
+BUILD_SRCS_CRON = $(addprefix $(BUILD_DIR)/, $(SRCS_CRON))
+BUILD_SRCS_LAUNCHAGENT = $(addprefix $(BUILD_DIR)/, $(SRCS_LAUNCHAGENT))
# Destination directories
DEST_DIR_SCRIPT = $(PREFIX)/$(DIR_SCRIPT)
DEST_DIR_CONF = $(PREFIX)/$(DIR_CONF)
DEST_DIR_SYSTEMD = $(PREFIX)/$(DIR_SYSTEMD)
DEST_DIR_CRON = $(PREFIX)/$(DIR_CRON)
+DEST_DIR_LAUNCHAGENT= $(HOME)/$(DIR_LAUNCHAGENT)
+DEST_DIR_MAC_LOG = $(HOME)/Library/Logs/restic
# Destination file targets.
-DEST_TARGS_SCRIPT = $(addprefix $(PREFIX)/, $(SRCS_SCRIPT))
-DEST_TARGS_CONF = $(addprefix $(PREFIX)/, $(SRCS_CONF))
-DEST_TARGS_SYSTEMD = $(addprefix $(PREFIX)/, $(SRCS_SYSTEMD))
-DEST_TARGS_CRON = $(addprefix $(PREFIX)/, $(SRCS_CRON))
+DEST_TARGS_SCRIPT = $(addprefix $(PREFIX)/, $(SRCS_SCRIPT))
+DEST_TARGS_CONF = $(addprefix $(PREFIX)/, $(SRCS_CONF))
+DEST_TARGS_SYSTEMD = $(addprefix $(PREFIX)/, $(SRCS_SYSTEMD))
+DEST_TARGS_CRON = $(addprefix $(PREFIX)/, $(SRCS_CRON))
+DEST_TARGS_LAUNCHAGENT = $(addprefix $(HOME)/, $(SRCS_LAUNCHAGENT))
INSTALLED_FILES = $(DEST_TARGS_SCRIPT) $(DEST_TARGS_CONF) \
- $(DEST_TARGS_SYSTEMD) $(DEST_TARGS_CRON)
+ $(DEST_TARGS_SYSTEMD) $(DEST_TARGS_CRON) \
+ $(DEST_TARGS_LAUNCHAGENT)
#### Targets ##################################################################
@@ -89,7 +104,7 @@ help:
clean:
$(RM) -r $(BUILD_DIR)
-# target: uninstall - Uninstall ALL files from all install targets.
+# target: uninstall - Uninstall ALL installed (including config) files.
uninstall:
@for file in $(INSTALLED_FILES); do \
echo $(RM) $$file; \
@@ -101,17 +116,24 @@ uninstall:
# $ PREFIX=/usr/local make install-systemd
# $ PREFIX=/tmp/test make install-systemd
# target: install-systemd - Install systemd setup.
-install-systemd: install-targets-script install-targets-conf install-targets-systemd
+install-systemd: install-targets-script install-targets-conf \
+ install-targets-systemd
# target: install-cron - Install cron setup.
install-cron: install-targets-script install-targets-conf install-targets-cron
+# target: install-launchagent - Install LaunchAgent setup.
+install-launchagent: install-targets-script install-targets-conf \
+ install-targets-launchagent
+
# Install targets. Prereq build sources as well,
# so that build dir is re-created if deleted.
install-targets-script: $(DEST_TARGS_SCRIPT) $(BUILD_SRCS_SCRIPT)
install-targets-conf: $(DEST_TARGS_CONF) $(BUILD_SRCS_CONF)
-install-targets-systemd: $(DEST_TARGS_SYSTEMD) $(BUILD_SRCS_SYSTEMD)
-install-targets-cron: $(DEST_TARGS_CRON) $(BUILD_SRCS_CRON)
+install-targets-systemd: $(DEST_TARGS_SYSTEMD) $(BUILD_SRCS_SYSTEMD)
+install-targets-cron: $(DEST_TARGS_CRON) $(BUILD_SRCS_CRON)
+install-targets-launchagent: $(DEST_TARGS_LAUNCHAGENT) \
+ $(BUILD_SRCS_LAUNCHAGENT) $(DEST_DIR_MAC_LOG)
# Copies sources to build directory & replace "$INSTALL_PREFIX".
$(BUILD_DIR)/% : %
@@ -138,3 +160,22 @@ $(DEST_DIR_SYSTEMD)/%: $(BUILD_DIR_SYSTEMD)/%
$(DEST_DIR_CRON)/%: $(BUILD_DIR_CRON)/%
@${MKDIR_PARENTS} $@
install -m 0644 $< $@
+
+# Install destination launchagent files.
+$(DEST_DIR_LAUNCHAGENT)/%: $(BUILD_DIR_LAUNCHAGENT)/%
+ @${MKDIR_PARENTS} $@
+ install -m 0444 $< $@
+
+# Install destination mac log dir.
+$(DEST_DIR_MAC_LOG):
+ mkdir -p $@
+
+# target: activate-launchagent - Activate the LaunchAgent.
+activate-launchagent:
+ launchctl bootstrap gui/$(UID) $(DEST_TARGS_LAUNCHAGENT)
+ launchctl enable $(LAUNCHAGENT_TARGET)
+ launchctl kickstart -p $(LAUNCHAGENT_TARGET)
+
+# target: deactivate-launchagent - Deactivate and remove the LaunchAgent.
+deactivate-launchagent:
+ launchctl bootout $(LAUNCHAGENT_TARGET)
diff --git a/README.md b/README.md
index e9113fa..4583c5d 100644
--- a/README.md
+++ b/README.md
@@ -44,11 +44,11 @@ Tip: use the Section icon in the top left of this document to navigate the secti
* Arch: part of the `base-devel` meta package, Debian/Ubuntu: part of the `build-essential` meta package, macOS: use the preinstalled or a more recent with Homebrew)
-
# Setup
Depending on your system, the setup will look different. Choose one of
-* [Linux + Systemd](#setup-linux-systemd)
-* [Cron](#setup-cron) - for any system having a cron daemon. Tested on FreeBSD and macOS.
+*
[Linux + Systemd](#setup-linux-systemd)
+*
[macOS + LaunchAgent](#setup-macos-launchagent)
+*
[Cron](#setup-cron) - for any system having a cron daemon. Tested on FreeBSD and macOS.
## Setup Linux Systemd
### TL;DR Setup
@@ -260,6 +260,36 @@ straightforward (it needs to run with sudo to read environment). Just run:
| `resticw stats` / `resticw stats snapshot-id ...` | Show the statistics for the whole repo or the specified snapshots |
| `resticw mount /mnt/restic` | Mount your remote repository |
+## Setup macOS LaunchAgent
+LaunchAgent is the modern service scheduler in in macOS that uses [Launchd](https://www.launchd.info/).
+[Launchd](https://www.launchd.info/) is the modern built-in service scheduler in macOS. It has support for running services as root (Daemon) or as a normal user (Agent). Here we we set up an LauchAgent to be run as your normal user for starting regular backups.
+
+1. In general, follow the same setup as in (#setup-linux-systemd) except for:
+ * use `make install-launchagent` instead of `make install-systemd`
+ * install everything to `/usr/local` and run restic as your own use, not root
+ * Thus, install with
+ ```console
+ $ PREFIX=/usr/local make install-launchagent
+ ```
+1. After installation with `make` , edit the installed LaunchAgent if you want to change the default schedule or profile used:
+ ```console
+ $ vim ~/Library/LaunchAgents/com.github.erikw.restic-automatic-backup.plist
+ ```
+1. Now install, enable and start the first run!
+ ```console
+ $ launchctl bootstrap gui/$UID ~/Library/LaunchAgents/com.github.erikw.restic-automatic-backup.plist
+ $ launchctl enable gui/$UID/com.github.erikw.restic-automatic-backup
+ $ launchctl kickstart -p gui/$UID/com.github.erikw.restic-automatic-backup
+ ```
+ As a convenience, a shortcut for the above commands are `$ make activate-launchagent`.
+
+Use the `disable` command to temporarily pause the agent, or `bootout` to uninstall it.
+```
+$ launchctl disable gui/$UID/com.github.erikw.restic-automatic-backup
+$ launchctl bootout gui/$UID/com.github.erikw.restic-automatic-backup
+```
+
+If you updated the `.plist` file, you need to issue the `bootout` followed by `bootrstrap` and `enable` sub-commands of `launchctl`. This will guarantee that the file is properly reloaded.
## Setup Cron
If you want to run an all-classic cron job instead, do like this: