summaryrefslogtreecommitdiff
path: root/parabola-mirror
blob: de9893b79b748bb596de380227e0425015c51891 (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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#!/bin/bash

# alfplayer
# 2014-06-12
#
# Mirror the whole Parabola repository using rsync and keep snapshots

set -e

script_filename="$(basename "$0")"

# Create temporary log file
log_tmp="$(mktemp -p /var/tmp)"

# Default configuration values.
# They can be overridden by setting the variables in the calling environment.
# 1 to set, 0 to unset
: ${repo:=rsync://repo.parabola.nu:875/repos}
: ${base_dir:=/srv/http}
: ${parabola_dir:=${base_dir}/parabola}
: ${oldest_version:=10}    # snapshots older than this one will be deleted (see also no_delete above)
: ${log_file:=${script_filename}.log}
#: ${mail_to:=your@mail.com}     # comment out to disable
#: ${date_exclude:=2014.06.19}    # disable running this script this date (see next line)
: ${forcerun:=0}    # force running on excluded date
: ${terminal:=1}    # outputs to stdout using rsync --progress (and logs to a file)
: ${no_delete:=0}    # disable deletion of oldest snapshots
#: ${TZ:=UTC}      # set time zone

export TZ

# Lock with flock (provided by util-linux), save to log file and send email on exit
LOCKFILE="/var/lock/${script_filename}"
LOCKFD=99

_lock()             { flock -$1 $LOCKFD; }

# Wait until there is no process writing to ${log_tmp}
# Waiting can be necessary if command groups are not executed until the end
_wait_log_tmp() {
    if [[ -e "${log_tmp}" ]] ; then
        while fuser "${log_tmp}" > /dev/null ; do
            sleep 0.2
        done      
    fi
}

_no_more_locking()  {
    set +e

    # Save exit status
    es=$?

    _wait_log_tmp
    cat "${log_tmp}" >> "${log_file}"

    if [[ $? == 0 ]] ; then
        log_written=1
    else
	echo "=> ERROR: Failed to write to log file: ${log_file}" >&2
    fi

    if [[ -e ${parabola_dir}.tmp ]] ; then
	echo "=> WARNING: Directory with partial transfer ${LOCAL}.tmp remains in file system"
    fi

    if [[ ${mail_to} ]] ; then
	echo "=> Sending output to ${mail_to}"
    	mail -s "[$(hostname)] ${script_filename}" "${mail_to}" < "${log_tmp}"
    fi

    if [[ ${log_written} == 1 ]] ; then
    	rm -f "${log_tmp}"
    fi

    if [[ $es != 0 ]] ; then
        echo "=> WARNING: Unsuccessful script termination. Exit status: $es"
        if [[ ${mail_to} ]] ; then
	    echo "=> Sending error notification to ${mail_to}"
    	    mail -s "[$(hostname)] ${script_filename} failed. See logged output." "${mail_to}" < /dev/null
        fi
    fi

    _lock u
    _lock xn && \
	rm -f $LOCKFILE
}

_prepare_locking()  { eval "exec $LOCKFD>\"$LOCKFILE\""; trap _no_more_locking EXIT; }

_prepare_locking

# Lock now. The lock is disabled automatically when the script exits (with any error code).
if ! _lock xn ; then
    echo "=> ERROR: Could not obtain lock. Exiting." >&2
    exit 1
fi

date="$(date +%Y.%m.%d)"
current="${parabola_dir}-${date}"
local_useful=0

{

if [[ ${date_exclude} && ${forcerun} != 1 ]] ; then
    if [[ $DATE == ${date_exclude} ]] ; then
	echo "Manually disabled: ${date}. Exiting."
	exit 0
    fi
fi

# Test if ${parabola_dir} is an existing symlink pointing to an existing directory
if [[ -h ${parabola_dir} ]] ; then
  last_path="$(readlink -f "${parabola_dir}")"
  if [[ -d ${last_path} ]] ; then
    last="${last_path##*/}"
    local_useful=1
  else
    echo "=> ERROR: ${parabola_dir} is a symlink which does not point to an existing directory." >&2
    exit 1
  fi
else
  echo "=> ERROR: ${parabola_dir} does not exist or is not a symlink." >&2
  exit 1
fi


echo
echo "=> Creating snapshot for date ${date}"

if [[ -e "${current}" ]] ; then
  echo "${current} already exists. Exiting." >&2
  exit 1
fi

if [[ -e "${parabola_dir}".tmp ]] ; then
  echo "${parabola_dir}.tmp already exists. Resuming."
else
  cp -al "${last_path}" "${parabola_dir}".tmp
fi

} &> >(tee -a "${log_tmp}")

_wait_log_tmp

rsync "${repo}"/ -rtvlH ${terminal:+--progress} --log-file="${log_tmp}" --safe-links --delete --link-dest="$base_dir"/archlinux/ --link-dest="$parabola_dir" "$parabola_dir".tmp

{

cd "$base_dir"

echo "=> Delete versions older than the ${oldest_version} oldest version"
delete_list=( $(for dir in parabola-* ; do echo "$(find "$dir" -type f -printf '%T@\n' | sort -n | tail -1) $dir" ; done | awk '{print $2}' | head -n -"${oldest_version}") )

for dir in ${delete_list[@]} ; do
    if [[ ${no_delete} != 1 ]] ; then
        echo "$dir"
        rm -rf "$dir"
    else
	echo "DRY-RUN: rm -rf $dir"
    fi
done

echo "=> Start serving the new repository version"
mv "${parabola_dir}.tmp" "${current}"

if [[ ${local_useful} == 1 ]] ; then
    echo "  => Deleting ${parabola_dir} symlink"
    rm -rf "${parabola_dir}"
fi

# Create symlink
echo "  => Creating symlink ${current} to ${parabola_dir}"
ln -s ${current##*/} ${parabola_dir}

echo "=> Disk space report"
df -h /

echo "=> ${script_filename} finished successfully. Finish time: $(date --rfc-3339=seconds)"

} &> >(tee -a "${log_tmp}")

_wait_log_tmp