common.sh 10 KB
Newer Older
1
2
# Copyright (C) 2011 GRNET S.A. 
# Copyright (C) 2007, 2008, 2009 Google Inc.
Nikos Skalkotos's avatar
Nikos Skalkotos committed
3
#
4
5
6
7
# 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 2 of the License, or
# (at your option) any later version.
Nikos Skalkotos's avatar
Nikos Skalkotos committed
8
#
9
10
11
12
# 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.
Nikos Skalkotos's avatar
Nikos Skalkotos committed
13
#
14
15
16
17
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
Nikos Skalkotos's avatar
Nikos Skalkotos committed
18

19
RESULT=/dev/ttyS1
20
21
MONITOR=/dev/ttyS2

Nikos Skalkotos's avatar
Nikos Skalkotos committed
22
FLOPPY_DEV=/dev/fd0
23
PROGNAME=$(basename $0)
Nikos Skalkotos's avatar
Nikos Skalkotos committed
24

Nikos Skalkotos's avatar
Nikos Skalkotos committed
25
26
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin

Nikos Skalkotos's avatar
Nikos Skalkotos committed
27
28
# Programs
XMLSTARLET=xmlstarlet
29
TUNE2FS=tune2fs
Nikos Skalkotos's avatar
Nikos Skalkotos committed
30
RESIZE2FS=resize2fs
31
PARTED=parted
32
SFDISK=sfdisk
Nikos Skalkotos's avatar
Nikos Skalkotos committed
33
34
MKSWAP=mkswap
BLKID=blkid
35
BLOCKDEV=blockdev
36
37
REGLOOKUP=reglookup
CHNTPW=chntpw
38
DATE="date -u" # Time in UTC
Nikos Skalkotos's avatar
Nikos Skalkotos committed
39
40

CLEANUP=( )
41
42
ERRORS=( )
WARNINGS=( )
Nikos Skalkotos's avatar
Nikos Skalkotos committed
43

44
45
46
47
MSG_TYPE_TASK_START="TASK_START"
MSG_TYPE_TASK_END="TASK_END"

STDERR_LINE_SIZE=10
48

49
50
51
52
53
54
add_cleanup() {
    local cmd=""
    for arg; do cmd+=$(printf "%q " "$arg"); done
    CLEANUP+=("$cmd")
}

Nikos Skalkotos's avatar
Nikos Skalkotos committed
55
log_error() {
56
    ERRORS+=("$@")
57
    echo "ERROR: $@" | tee $RESULT >&2
Nikos Skalkotos's avatar
Nikos Skalkotos committed
58
59
60
    exit 1
}

61
62
warn() {
    echo "Warning: $@" >&2
63
    echo "WARNING:$@" > "$MONITOR"
64
65
}

66
report_task_start() {
67
    echo "$MSG_TYPE_TASK_START:${PROGNAME:2}" > "$MONITOR"
68
69
}

70
report_task_end() {
71
    echo "$MSG_TYPE_TASK_END:${PROGNAME:2}" > "$MONITOR"
72
73
74
}

report_error() {
75
76
77
78
79
80
81
82
83
84
85
    if [ ${#ERRORS[*]} -eq 0 ]; then
        # No error message. Print stderr
	local lines=$(tail --lines=${STDERR_LINE_SIZE} "$STDERR_FILE" | wc -l)
        echo -n "STDERR:${lines}:" > "$MONITOR"
        tail --lines=$lines  "$STDERR_FILE" > "$MONITOR"
    else
        echo -n "ERROR:" > "$MONITOR"
        for line in "${ERRORS[@]}"; do
            echo "$line" > "$MONITOR"
        done
    fi
86
87
}

88
89
90
91
system_poweroff() {
    echo o > /proc/sysrq-trigger
}

Nikos Skalkotos's avatar
Nikos Skalkotos committed
92
93
94
95
96
97
98
99
100
get_base_distro() {
    local root_dir=$1

    if [ -e "$root_dir/etc/debian_version" ]; then
        echo "debian"
    elif [ -e "$root_dir/etc/redhat-release" ]; then
        echo "redhat"
    elif [ -e "$root_dir/etc/slackware-version" ]; then
        echo "slackware"
101
    elif [ -e "$root_dir/etc/SuSE-release" ]; then
Nikos Skalkotos's avatar
Nikos Skalkotos committed
102
        echo "suse"
103
    elif [ -e "$root_dir/etc/gentoo-release" ]; then
Nikos Skalkotos's avatar
Nikos Skalkotos committed
104
        echo "gentoo"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
105
106
    elif [ -e "$root_dir/etc/arch-release" ]; then
        echo "arch"
107
108
    else
        warn "Unknown base distro."
Nikos Skalkotos's avatar
Nikos Skalkotos committed
109
110
111
112
113
114
115
116
117
    fi
}

get_distro() {
    local root_dir=$1

    if [ -e "$root_dir/etc/debian_version" ]; then
        distro="debian"
        if [ -e ${root_dir}/etc/lsb-release ]; then
118
            ID=$(grep ^DISTRIB_ID= ${root_dir}/etc/lsb-release | cut -d= -f2)
Nikos Skalkotos's avatar
Nikos Skalkotos committed
119
120
121
122
123
124
125
            if [ "x$ID" = "xUbuntu" ]; then
                distro="ubuntu"
            fi
        fi
        echo "$distro"
    elif [ -e "$root_dir/etc/fedora-release" ]; then
        echo "fedora"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
126
    elif [ -e "$root_dir/etc/centos-release" ]; then
Nikos Skalkotos's avatar
Nikos Skalkotos committed
127
128
129
130
131
        echo "centos"
    elif [ -e "$root_dir/etc/redhat-release" ]; then
        echo "redhat"
    elif [ -e "$root_dir/etc/slackware-version" ]; then
        echo "slackware"
132
    elif [ -e "$root_dir/etc/SuSE-release" ]; then
Nikos Skalkotos's avatar
Nikos Skalkotos committed
133
        echo "suse"
134
    elif [ -e "$root_dir/etc/gentoo-release" ]; then
Nikos Skalkotos's avatar
Nikos Skalkotos committed
135
        echo "gentoo"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
136
137
    elif [ -e "$root_dir/etc/arch-release" ]; then
        echo "arch"
138
139
    else
        warn "Unknown distro."
Nikos Skalkotos's avatar
Nikos Skalkotos committed
140
141
142
    fi
}

143
144
145

get_partition_table() {
    local dev="$1"
146
147
148
149
    # If the partition table is gpt then parted will raise an error if the
    # secondary gpt is not it the end of the disk, and a warning that has to
    # do with the "Last Usable LBA" entry in gpt.
    if ! output="$("$PARTED" -s -m "$dev" unit s print | grep -E -v "^(Warning|Error): ")"; then
150
151
152
        log_error "Unable to read partition table for device \`${dev}'"
    fi

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
    echo "$output"
}

get_partition_table_type() {
    local ptable="$1"

    local dev="$(sed -n 2p <<< "$ptable")"
    declare -a field
    IFS=':' read -ra field <<< "$dev"

    echo "${field[5]}"
}

get_partition_count() {
    local ptable="$1"

    expr $(echo "$ptable" | wc -l) - 2
170
171
}

172
get_partition_by_num() {
173
174
175
176
177
178
    local ptable="$1"
    local id="$2"

    grep "^$id:" <<< "$ptable"
}

179
get_last_partition() {
180
    local ptable="$1"
181

182
    echo "$ptable" | tail -1
183
184
}

185
is_extended_partition() {
186
    local dev="$1"
187
    local part_num="$2"
188

189
190
191
192
193
194
    id=$($SFDISK --print-id "$dev" "$part_num")
    if [ "$id" = "5" ]; then
        echo "yes"
    else
        echo "no"
    fi
195
196
}

197
198
199
get_extended_partition() {
    local ptable="$1"
    local dev="$(echo "$ptable" | sed -n 2p | cut -d':' -f1)"
200

201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
    tail -n +3 <<< "$ptable" | while read line; do
        part_num=$(cut -d':' -f1 <<< "$line")
        if [ $(is_extended_partition "$dev" "$part_num") == "yes" ]; then
            echo "$line"
            return 0
        fi
    done
    echo ""
}

get_logical_partitions() {
    local ptable="$1"

    tail -n +3 <<< "$ptable" | while read line; do
        part_num=$(cut -d':' -f1 <<< "$line")
        if [ $part_num -ge 5 ]; then
            echo "$line"
        fi
    done

    return 0
}

get_last_primary_partition() {
    local ptable="$1"
    local dev=$(echo "ptable" | sed -n 2p | cut -d':' -f1)

    for i in 4 3 2 1; do
        if output=$(grep "^$i:" <<< "$ptable"); then
            echo "$output"
            return 0
        fi
    done
    echo ""
}

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
get_partition_to_resize() {
    local dev="$1"

    table=$(get_partition_table "$dev")

    if [ $(get_partition_count "$table") -eq 0 ]; then
        return 0
    fi

    table_type=$(get_partition_table_type "$table")
    last_part=$(get_last_partition "$table")
    last_part_num=$(cut -d: -f1 <<< "$last_part")

    if [ "$table_type" == "msdos" -a $last_part_num -gt 4 ]; then
        extended=$(get_extended_partition "$table")
        last_primary=$(get_last_primary_partition "$table")
        ext_num=$(cut -d: -f1 <<< "$extended")
        prim_num=$(cut -d: -f1 <<< "$last_primary")

        if [ "$ext_num" != "$last_prim_num" ]; then
            echo "$last_prim_num"
        else
            echo "$last_part_num"
        fi
    else
        echo "$last_part_num"
    fi
}

266
create_partition() {
267
268
269
    local device="$1"
    local part="$2"
    local ptype="$3"
270
271
272

    declare -a fields
    IFS=":;" read -ra fields <<< "$part"
273
274
275
276
277
278
279
280
281
    local id="${fields[0]}"
    local start="${fields[1]}"
    local end="${fields[2]}"
    local size="${fields[3]}"
    local fs="${fields[4]}"
    local name="${fields[5]}"
    local flags="${fields[6]//,/ }"

    $PARTED -s -m -- $device mkpart "$ptype" $fs "$start" "$end"
282
283
284
285
286
287
    for flag in $flags; do
        $PARTED -s -m $device set "$id" "$flag" on
    done
}

enlarge_partition() {
288
289
290
291
292
293
294
295
    local device="$1"
    local part="$2"
    local ptype="$3"
    local new_end="$4"

    if [ -z "$new_end" ]; then
        new_end=$(cut -d: -f 3 <<< "$(get_last_free_sector "$device")")
    fi
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323

    declare -a fields
    IFS=":;" read -ra fields <<< "$part"
    fields[2]="$new_end"

    local new_part=""
    for ((i = 0; i < ${#fields[*]}; i = i + 1)); do
        new_part="$new_part":"${fields[$i]}"
    done
    new_part=${new_part:1}

    # If this is an extended partition, removing it will also remove the
    # logical partitions it contains. We need to save them for later.
    if [ "$ptype" = "extended" ]; then
        local table="$(get_partition_table "$device")"
        local logical="$(get_logical_partitions "$table")"
    fi

    id=${fields[0]}
    $PARTED -s -m "$device" rm "$id"
    create_partition "$device" "$new_part" "$ptype"

    if [ "$ptype" = "extended" ]; then
        # Recreate logical partitions
        echo "$logical" | while read logical_part; do
            create_partition "$device" "$logical_part" "logical"
        done
    fi
324
325
326
327
}

get_last_free_sector() {
    local dev="$1"
328
329
330
331
332
333
334
335
    local unit="$2"

    if [ -n "$unit" ]; then
        unit="unit $unit"
    fi

    local last_line="$("$PARTED" -s -m "$dev" "$unit" print free | tail -1)"
    local ptype="$(cut -d: -f 5 <<< "$last_line")"
336

337
    if [ "$ptype" = "free;" ]; then
338
        echo "$last_line"
339
340
341
    fi
}

Nikos Skalkotos's avatar
Nikos Skalkotos committed
342
cleanup() {
343
    # if something fails here, it shouldn't call cleanup again...
Nikos Skalkotos's avatar
Nikos Skalkotos committed
344
345
346
347
348
349
350
351
352
353
    trap - EXIT

    if [ ${#CLEANUP[*]} -gt 0 ]; then
        LAST_ELEMENT=$((${#CLEANUP[*]}-1))
        REVERSE_INDEXES=$(seq ${LAST_ELEMENT} -1 0)
        for i in $REVERSE_INDEXES; do
            # If something fails here, it's better to retry it for a few times
            # before we give up with an error. This is needed for kpartx when
            # dealing with ntfs partitions mounted through fuse. umount is not
            # synchronous and may return while the partition is still busy. A
354
355
356
357
358
            # premature attempt to delete partition mappings through kpartx on
            # a device that hosts previously mounted ntfs partition may fail
            # with a `device-mapper: remove ioctl failed: Device or resource
            # busy' error. A sensible workaround for this is to wait for a
            # while and then try again.
Nikos Skalkotos's avatar
Nikos Skalkotos committed
359
360
361
362
363
364
365
            local cmd=${CLEANUP[$i]}
            $cmd || for interval in 0.25 0.5 1 2 4; do
            echo "Command $cmd failed!"
            echo "I'll wait for $interval secs and will retry..."
            sleep $interval
            $cmd && break
        done
366
367
368
369
	if [ "$?" != "0" ]; then
            echo "Giving Up..."
            exit 1;
        fi
Nikos Skalkotos's avatar
Nikos Skalkotos committed
370
371
372
373
    done
  fi
}

374
375
376
377
task_cleanup() {
    rc=$?

    if [ $rc -eq 0 ]; then
378
       report_task_end
379
380
381
382
383
384
385
    else
       report_error
    fi

    cleanup
}

386
check_if_excluded() {
387
388
    local name="$(tr [a-z] [A-Z] <<< ${PROGNAME:2})"
    local exclude="SNF_IMAGE_PROPERTY_EXCLUDE_TASK_${name}"
389
    if [ -n "${!exclude}" ]; then
390
        warn "Task ${PROGNAME:2} was excluded and will not run."
391
392
393
394
395
396
        exit 0
    fi

    return 0
}

397
trap cleanup EXIT
398
set -o pipefail
399

400
401
402
403
STDERR_FILE=$(mktemp)
add_cleanup rm -f "$STDERR_FILE"
exec 2> >(tee -a "$STDERR_FILE" >&2)

Nikos Skalkotos's avatar
Nikos Skalkotos committed
404
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :