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: