From 0cd1cbb1320cf26ef3801297b4fb8daa439f6217 Mon Sep 17 00:00:00 2001 From: Pipo Date: Wed, 24 Sep 2025 17:18:02 +0200 Subject: [PATCH] Initial commit --- .DS_Store | Bin 0 -> 6148 bytes LICENSE | 21 ++++++++++++ README.md | 46 +++++++++++++++++++++++++++ launchd/com.snapclean.listener.plist | 15 +++++++++ scripts/auto-snapclean-listener.sh | 43 +++++++++++++++++++++++++ scripts/install.sh | 17 ++++++++++ scripts/uninstall.sh | 6 ++++ 7 files changed, 148 insertions(+) create mode 100644 .DS_Store create mode 100644 LICENSE create mode 100644 README.md create mode 100644 launchd/com.snapclean.listener.plist create mode 100644 scripts/auto-snapclean-listener.sh create mode 100644 scripts/install.sh create mode 100644 scripts/uninstall.sh diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a970548a0242c742fe5402e6a1cba76893c96ed3 GIT binary patch literal 6148 zcmeHKO-sW-5Pe$k zm3*r_N&N2G`~CCia`-x+W8gLahF7r&W;?UYEXj`WIw4o-QC2_hy0T#HDg(-ZGVlWi zShGdirw+AM29yD1;J|=kbn;)K(c#2I>r)xM5f7|LF7ke?3S? z%78NPuNW}RWSk7SC0|>E%}K3|s1H;T@hcr3LfEmTn7&eq{ukEBD4J(dn>q3Dl* Mr9m5I;8z)V2fzz;NdN!< literal 0 HcmV?d00001 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9457651 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Pipo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9fa9966 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# auto-snapclean-listener + +A small macOS utility script that automatically deletes all local Time Machine snapshots once they are created. +It is useful for users who do not want macOS to keep local snapshots and prefer to rely only on external Time Machine backups. + +⚠️ Tested on **macOS Tahoe**. Other versions may work, but are not guaranteed. + +--- + +## Features + +- Listens for local Time Machine snapshot creation events. +- Waits until the backup process is finished (avoids conflicts). +- Automatically deletes all local snapshots afterwards. +- Lightweight: no constant polling, event-driven. + +--- + +## Installation + +Clone the repository: + +```bash +git clone https://github.com/YOURUSERNAME/auto-snapclean-listener.git +cd auto-snapclean-listener +``` + +Make the script executable: + +```bash +chmod +x auto-snapclean-listener.sh +``` + +Install the LaunchDaemon: + +```bash +sudo cp auto-snapclean-listener.plist /Library/LaunchDaemons/ +sudo launchctl load /Library/LaunchDaemons/auto-snapclean-listener.plist +``` + +Uninstall (if needed): + +```bash +sudo launchctl unload /Library/LaunchDaemons/auto-snapclean-listener.plist +sudo rm /Library/LaunchDaemons/auto-snapclean-listener.plist +``` diff --git a/launchd/com.snapclean.listener.plist b/launchd/com.snapclean.listener.plist new file mode 100644 index 0000000..4ffb64a --- /dev/null +++ b/launchd/com.snapclean.listener.plist @@ -0,0 +1,15 @@ + + + + + Labelcom.snapclean.listener + ProgramArguments + + /usr/local/bin/auto-snapclean-listener.sh + + RunAtLoad + KeepAlive + UserNameroot + + \ No newline at end of file diff --git a/scripts/auto-snapclean-listener.sh b/scripts/auto-snapclean-listener.sh new file mode 100644 index 0000000..935f82f --- /dev/null +++ b/scripts/auto-snapclean-listener.sh @@ -0,0 +1,43 @@ +#!/bin/zsh +set -euo pipefail + +LOGF="/var/log/snapclean.log" +log() { print -r -- "$(date '+%F %T') $*" >> "$LOGF"; } + +is_running() { + /usr/bin/tmutil status 2>/dev/null | /usr/bin/grep -Eq '"Running" = 1|Running = 1' +} + +wait_until_tm_idle() { + local i=0 + while is_running; do + sleep 2 + (( i++ )) + [[ $i -gt 150 ]] && log "Timeout: TM still running. Waiting until it's finished." && break + done +} + +delete_all_snapshots() { + log "Deletes all local Snapshots at / …" + /usr/bin/tmutil deletelocalsnapshots / || true + log "Finished deleting." +} + +# Make sure logfile exists +[[ -f "$LOGF" ]] || { sudo touch "$LOGF"; sudo chmod 644 "$LOGF"; } + +while IFS= read -r line; do + if print -r -- "$line" | /usr/bin/grep -q \ + "com.apple.TimeMachine:LocalSnapshotManagement] Created Time Machine local snapshot"; then + log "Event erkannt: $line" + # Short break until the snapshot is in index + sleep 2 + # Waiting until TM is finished + wait_until_tm_idle + # delete all local snapshots + delete_all_snapshots + fi +done < <( + /usr/bin/log stream --style syslog --level info \ + --predicate 'process == "backupd" AND subsystem == "com.apple.TimeMachine"' +) \ No newline at end of file diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100644 index 0000000..57e81d0 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,17 @@ +#!/bin/zsh +set -euo pipefail + +PLIST_SRC="$(cd -- "$(dirname "$0")/.." && pwd)/launchd/com.snapclean.listener.plist" +SCRIPT_SRC="$(cd -- "$(dirname "$0")/.." && pwd)/scripts/auto-snapclean-listener.sh" + +sudo install -m 0755 "$SCRIPT_SRC" /usr/local/bin/auto-snapclean-listener.sh +sudo install -m 0644 "$PLIST_SRC" /Library/LaunchDaemons/com.snapclean.listener.plist +sudo touch /var/log/snapclean.log /var/log/snapclean.out /var/log/snapclean.err +sudo chmod 644 /var/log/snapclean.* + +# (re)load +sudo launchctl unload /Library/LaunchDaemons/com.snapclean.listener.plist 2>/dev/null || true +sudo launchctl load -w /Library/LaunchDaemons/com.snapclean.listener.plist +sudo launchctl list | grep snapclean || true + +echo "✅ snapcleaner installed." \ No newline at end of file diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh new file mode 100644 index 0000000..78a3487 --- /dev/null +++ b/scripts/uninstall.sh @@ -0,0 +1,6 @@ +#!/bin/zsh +set -euo pipefail +sudo launchctl unload /Library/LaunchDaemons/com.snapclean.listener.plist 2>/dev/null || true +sudo rm -f /Library/LaunchDaemons/com.snapclean.listener.plist +sudo rm -f /usr/local/bin/auto-snapclean-listener.sh +echo "✅ snapcleaner deleted. Logs stay in /var/log/snapclean.*" \ No newline at end of file