summaryrefslogtreecommitdiff
path: root/rsyncshot
blob: d12827efb2571c31cf5b62d548a1c8edab6c80fd (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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/bin/bash

# uncomment next 4 lines for debugging output
# exec 5> >(logger -t $0)
# BASH_XTRACEFD="5"
# PS4='$LINENO: '
# set -x

SCRIPTLOC=/usr/local/bin/rsyncshot;
DESTINATION=/media/backup;

INSTALLHOME=/etc/rsyncshot
LOGHOME=/var/log/rsyncshot.log;

INCLUDES="$INSTALLHOME/include.txt";
EXCLUDES="$INSTALLHOME/exclude.txt";

CRON_H="0 1-23 * * * $SCRIPTLOC hourly 22";
CRON_D="0 0 * * 1-6 $SCRIPTLOC daily 6";
CRON_W="0 0 * * 7 $SCRIPTLOC weekly 51";

help()
{
    echo ""
    echo "rsyncshot - compact snapshots on Linux using rsync and hard links."
    echo ""
    echo "rsyncshot must be run as root"
    echo ""
    echo "Usage: "
    echo "rsyncshot <name> <number of backups to retain>"
    echo "          setup (installs rsyncshot and cron jobs)"
    echo "          help  (prints this info)"
    echo "Notes:"
    echo "- install and log locations defined in script."

error()
{
    echo "ERROR: $0: $@" 1>&2;
    echo "See \"rsyncshot help\" for usage."
    exit 1;
}

setup()
{
    # copy this file to directory on path
    cp -f $0 /usr/local/bin
    echo "$0 copied to /usr/local/bin"
    
    # make install home if it doesn't exist;
    if [ ! -d $INSTALLHOME ]; then
	mkdir -p $INSTALLHOME;
	"Created install home at $INSTALLHOME.";
    fi

    # create includes file and add default entries
    if [ -f $INCLUDES ]; then rm $INCLUDES; fi
    printf "/home /etc /usr/local/bin" >> $INCLUDES;
    echo "modify include file at $INCLUDES";

    # create excludes file and add default entries
    if [ -f $EXCLUDES ]; then rm $EXCLUDES; fi
    printf "*.pyc\n*.pyo\n*.class\n*.elc\n*.o\n*.tmp\n.cache*" >>  $EXCLUDES;
    echo "modify exclude file at $EXCLUDES";

    # write out current crontab, append default entries, and install
    crontab -l > crontemp;
    echo "$CRON_H >> $LOGHOME 2>&1" >> crontemp;
    echo "$CRON_D >> $LOGHOME 2>&1" >> crontemp;
    echo "$CRON_W >> $LOGHOME 2>&1">> crontemp;
    crontab crontemp;
    rm crontemp;
    echo "hourly, daily, and weekly cron jobs installed.";
}

if [ "$TYPE" = "HELP" ]; then help; exit; fi

if [ "$EUID" -ne 0 ]; then error "This script must be run as root."; fi

echo "rsyncshot invoked on `date -u` with: $0 $1 $2";

if ! [[ $1 =~ [a-zA-Z] ]]; then error "snapshot type not recognized."; fi
TYPE=$(tr '[a-z]' '[A-Z]' <<< $1);
if [ "$TYPE" = "SETUP" ]; then setup; exit; fi

if ! [[ $2 =~ [0-9] ]]; then error "max snapshots not a number."; fi
MAX=$(($2-1));

if [ ! -f "$INCLUDES" ]; then error "include file $INCLUDES not found."; fi
SOURCES=$(<$INCLUDES);
for SOURCE in $SOURCES
do
    if [ ! -d "$SOURCE" ]; then error "source $SOURCE not found"; fi
done

if [ ! -f "$EXCLUDES" ]; then error "Exclude file $EXCLUDES not found."; fi

for SOURCE in $SOURCES
do
    rsync -avh -i --times \
    	  --delete  --delete-excluded \
    	  --exclude-from=$EXCLUDES \
    	  --update $SOURCE $DESTINATION/latest ;
done

if [ -d $DESTINATION/$TYPE.$MAX ]; then
    rm -rf $DESTINATION/$TYPE.$MAX;
fi

for (( start=$(($MAX)); start>=0; start--)); do
    end=$(($start+1));
	if [ -d $DESTINATION/$TYPE.$start ]; then
	    mv $DESTINATION/$TYPE.$start $DESTINATION/$TYPE.$end;
	fi
done

touch $DESTINATION/latest

cp -al $DESTINATION/latest $DESTINATION/$TYPE.0;

chmod -w $DESTINATION/$TYPE.0

echo "rsyncshot completed `date -u` ";
exit 0;