summaryrefslogtreecommitdiff
path: root/dotfiles/system/.local/bin/zfssnapshot
blob: b715722df4b14501203e3b3e98f2df7669aaee8c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/bin/env bash
# Craig Jennings <c@cjennings.net>
# Create a ZFS snapshot across all datasets with a dated, descriptive name.

set -euo pipefail

# Usage info
show_help() {
    cat << EOF
Usage: ${0##*/} [-h] [DESCRIPTION]
Create a ZFS snapshot across all datasets.

    -h              display this help and exit
    DESCRIPTION     short description for the snapshot (optional, will prompt if omitted)

Snapshot names are formatted as: YYYY-MM-DD-description
Only alphanumeric characters, hyphens, and underscores are allowed in descriptions.
Spaces are converted to hyphens automatically.

Examples:
    ${0##*/} before-upgrade
    ${0##*/} "pre system update"
    ${0##*/}                        # prompts for description
EOF
}

# Check for ZFS
if ! command -v zfs &> /dev/null; then
    echo "Error: zfs command not found. Is ZFS installed?"
    exit 1
fi

# Check for root/sudo
if [ "$EUID" -ne 0 ]; then
    echo "Error: This script must be run as root (use sudo)"
    exit 1
fi

# Parse arguments
while getopts ":h" opt; do
    case ${opt} in
        h)
            show_help
            exit 0
            ;;
        \?)
            echo "Invalid option: -$OPTARG" >&2
            show_help
            exit 1
            ;;
    esac
done
shift $((OPTIND - 1))

# Get description from argument or prompt
if [ $# -ge 1 ]; then
    description="$*"
else
    read -r -p "Enter snapshot description: " description
    if [ -z "$description" ]; then
        echo "Error: Description cannot be empty"
        exit 1
    fi
fi

# Sanitize description: convert spaces to hyphens, lowercase
description=$(echo "$description" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')

# Validate description: only allow alphanumeric, hyphens, underscores
if [[ ! "$description" =~ ^[a-z0-9_-]+$ ]]; then
    echo "Error: Description contains invalid characters"
    echo "Only letters, numbers, hyphens, and underscores are allowed"
    echo "Sanitized input was: $description"
    exit 1
fi

# Create snapshot name with date prefix
date_prefix=$(date +%Y-%m-%d)
snapshot_name="${date_prefix}-${description}"

# Get all pools
pools=$(zpool list -H -o name)

if [ -z "$pools" ]; then
    echo "Error: No ZFS pools found"
    exit 1
fi

echo "Creating snapshots with name: @${snapshot_name}"
echo ""

# Create recursive snapshots on each pool
for pool in $pools; do
    echo "Snapshotting pool: $pool"
    if zfs snapshot -r "${pool}@${snapshot_name}"; then
        echo "  ✓ Created ${pool}@${snapshot_name} (recursive)"
    else
        echo "  ✗ Failed to snapshot $pool"
    fi
done

echo ""
echo "Snapshot complete. Verify with: zfs list -t snapshot | grep $snapshot_name"