summaryrefslogtreecommitdiff
path: root/yarns/obnam.sh
blob: 3c855f5e56416ebc4806be0f4d40f0b382d12608 (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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# Copyright 2013-2016  Lars Wirzenius
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# =*= License: GPL-3+ =*=


# A helper to print an error message and terminate.

die()
{
    printf '%s\n' -- "$*" 1>&2
    exit 1
}


# Run Obnam in a safe way that ignore's any configuration files
# outside the test. The first argument MUST be the client name. The
# configuration file $DATADIR/$1.conf is used, if it exists. In addition,
# the environment variables specified in $DATADIR/$1.env are added for
# the duration of running Obnam.

run_obnam()
{
    local name="$1"
    shift

    # Create the config file, if it doesn't already exist.
    local conf="$DATADIR/$name.conf"
    if [ ! -e "$conf" ]
    then
        add_to_config "$name" client-name "$name"
    fi

    # Always turn off weak-random, or else anything that uses
    # encryption will take a long time. We don't need strong random
    # numbers for tests.
    add_to_config "$name" weak-random yes

    # Make lock timeout be very short, to make test suite not take
    # long. Since the test suite only does one thing at a time, and
    # does not run things in parallel, there's no way a lock will go
    # away, so a zero timeout is OK.
    add_to_config "$name" lock-timeout 0

    # If a repository format has been specified, use it.
    if [ -n "$REPOSITORY_FORMAT" ]
    then
        add_to_config "$name" repository-format "$REPOSITORY_FORMAT"
    fi

    (
        if [ -e "$DATADIR/$name.env" ]
        then
            . "$DATADIR/$name.env"
        fi
        "$SRCDIR/obnam" --no-default-config --config "$conf" \
            --quiet --log-level debug --log "$DATADIR/obnam.log" \
            --trace obnamlib --trace larch "$@"
    )
}


# Add an environment variable to the Obnam run.

add_to_env()
{
    local user="$1"
    local var="$2"
    local value="$3"
    printf 'export %s=%s\n' "$var" "$value" >> "$DATADIR/$user.env"
}


# Add a setting to the configuration file for a given client.

add_to_config()
{
    local client="$1"
    local filename="$DATADIR/$client.conf"
    local key="$2"
    local value="$3"

    if [ ! -e "$filename" ]
    then
        printf '[config]\n' > "$filename"
        printf 'client-name = %s\n' "$client" >> "$filename"
    fi
    printf '%s = %s\n' "$key" "$value" >> "$filename"
}


# Attempt to run a command, which may fail. Capture its stdout,
# stderr, and exit code.

attempt()
{
    if "$@" \
        > "$DATADIR/attempt.stdout" \
        2> "$DATADIR/attempt.stderr"
    then
        exit=0
    else
        exit=$?
    fi
    echo "$exit" > "$DATADIR/attempt.exit"
}


# Match captured output from attempt against a regular expression.

attempt_matches()
{
    grep "$2" "$DATADIR/attempt.$1"
}


# Check exit code of latest attempt.

attempt_exit_was()
{
    grep -Fx "$1" "$DATADIR/attempt.exit"
}


# Normalise time fields in a manifest that vary uncontrollably on
# some filesystems.

normalise_manifest_times()
{
    sed '/^Mtime:/s/\.[0-9]* / /' "$@"
}


# Remove "Nlink" lines for directories. It is rarely a useful thing
# compare exactly, unlike for non-directories, since it is merely
# extra checking that the right number of subdirectories exist. That
# extra checking is both unnecessary (if the subdirs are in the
# manifest, they already get checked), and harmful (if a subdirectory
# is excluded from a backup, it won't be in the restored data
# manifest, but the link count will be wrong).

remove_nlink_for_directories()
{
    # This assumes Mode comes before Nlink, which is does in
    # summain output.
    awk '
        $1 == "Mode:" && $2 ~ /^40/ { isdir = 1 }
        $1 == "Nlink:" && isdir { next }
        NF > 0 { paragraph = paragraph $0 "\n" }
        NF == 0 && paragraph {
            printf "%s\n", paragraph
            paragraph = ""
            isdir = 0
        }
        END { if (paragraph) printf "%s", paragraph }
    ' "$@"
}


# Create a manifest with summain of a file or a directory.

manifest()
{
    summain -r "$1" --exclude Ino --exclude Dev -c SHA1 |
    normalise_manifest_times |
    remove_nlink_for_directories
}


# Get a GPG fingerprint given a username.

get_fingerprint()
{
    gpg --fingerprint --with-colons "$1" |
        awk -F: '/^fpr:/ { print $10; exit }'
}


# Get a GPG keyid given a username.

get_keyid()
{
    get_fingerprint "$1" |
    awk '{ print substr($0, length-8) }'
}