Revision 83d0c566

b/snf-image-helper/Makefile.am
24 24
		   $(edit) $${srcdir}$@.in >$@.tmp
25 25
	mv $@.tmp $@
26 26

  
27
CLEANFILES = snf-image-helper
27
CLEANFILES = snf-image-helper common.sh
/dev/null
1
# Copyright (C) 2011, 2012, 2013 GRNET S.A.
2
# Copyright (C) 2007, 2008, 2009 Google Inc.
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful, but
10
# WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
# General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
# 02110-1301, USA.
18

  
19
PROGNAME=$(basename $0)
20

  
21
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
22

  
23
# Programs
24
XMLSTARLET=xmlstarlet
25
TUNE2FS=tune2fs
26
RESIZE2FS=resize2fs
27
PARTED=parted
28
SFDISK=sfdisk
29
MKSWAP=mkswap
30
BLKID=blkid
31
BLOCKDEV=blockdev
32
REGLOOKUP=reglookup
33
CHNTPW=chntpw
34
SGDISK=sgdisk
35
GROWFS_UFS=growfs.ufs
36
DUMPFS_UFS=dumpfs.ufs
37
GROWFS_OPENBSD=growfs.openbsd
38
DUMPFS_OPENBSD=dumpfs.openbsd
39
DATE="date -u" # Time in UTC
40
EATMYDATA=eatmydata
41
MOUNT="mount -n"
42

  
43
CLEANUP=( )
44
ERRORS=( )
45
WARNINGS=( )
46

  
47
MSG_TYPE_TASK_START="TASK_START"
48
MSG_TYPE_TASK_END="TASK_END"
49

  
50
STDERR_LINE_SIZE=10
51

  
52
add_cleanup() {
53
    local cmd=""
54
    for arg; do cmd+=$(printf "%q " "$arg"); done
55
    CLEANUP+=("$cmd")
56
}
57

  
58
close_fd() {
59
    local fd=$1
60

  
61
    exec {fd}>&-
62
}
63

  
64
send_result_kvm() {
65
    echo "$@" > /dev/ttyS1
66
}
67

  
68
send_monitor_message_kvm() {
69
    echo "$@" > /dev/ttyS2
70
}
71

  
72
send_result_xen() {
73
    xenstore-write /local/domain/0/snf-image-helper/$DOMID "$*"
74
}
75

  
76
send_monitor_message_xen() {
77
    #Broadcast the message
78
    echo "$@" | socat "STDIO" "UDP-DATAGRAM:${BROADCAST}:${MONITOR_PORT},broadcast"
79
}
80

  
81
prepare_helper() {
82
	local cmdline item key val hypervisor domid
83

  
84
	read -a cmdline	 < /proc/cmdline
85
	for item in "${cmdline[@]}"; do
86
            key=$(cut -d= -f1 <<< "$item")
87
            val=$(cut -d= -f2 <<< "$item")
88
            if [ "$key" = "hypervisor" ]; then
89
                hypervisor="$val"
90
            fi
91
            if [ "$key" = "rules_dev" ]; then
92
                export RULES_DEV="$val"
93
            fi
94
            if [ "$key" = "helper_ip" ]; then
95
                export IP="$val"
96
                export NETWORK="$IP/24"
97
                export BROADCAST="${IP%.*}.255"
98
            fi
99
            if [ "$key" = "monitor_port" ]; then
100
                export MONITOR_PORT="$val"
101
            fi
102
	done
103

  
104
    case "$hypervisor" in
105
    kvm)
106
        HYPERVISOR=kvm
107
        ;;
108
    xen-hvm|xen-pvm)
109
        if [ -z "$IP" ]; then
110
            echo "ERROR: \`helper_ip' not defined or empty" >&2
111
            exit 1
112
        fi
113
        if [ -z "$MONITOR_PORT" ]; then
114
            echo "ERROR: \`monitor_port' not defined or empty" >&2
115
            exit 1
116
        fi
117
        $MOUNT -t xenfs xenfs /proc/xen
118
        ip addr add "$NETWORK" dev eth0
119
        ip link set eth0 up
120
        ip route add default dev eth0
121
        export DOMID=$(xenstore-read domid)
122
        HYPERVISOR=xen
123
        ;;
124
    *)
125
        echo "ERROR: Unknown hypervisor: \`$hypervisor'" >&2
126
        exit 1
127
        ;;
128
    esac
129

  
130
    export HYPERVISOR
131
}
132

  
133
report_error() {
134
    msg=""
135
    if [ ${#ERRORS[*]} -eq 0 ]; then
136
        # No error message. Print stderr
137
        local lines stderr
138
        stderr="$(tail --lines=${STDERR_LINE_SIZE} "$STDERR_FILE")"
139
        lines=$(wc -l <<< "$stderr")
140
        msg="STDERR:${lines}:$stderr"
141
    else
142
        for line in "${ERRORS[@]}"; do
143
            msg+="ERROR:$line"$'\n'
144
        done
145
    fi
146

  
147
    send_monitor_message_${HYPERVISOR} "$msg"
148
}
149

  
150
log_error() {
151
    ERRORS+=("$*")
152

  
153
    send_result_${HYPERVISOR} "ERROR: $@"
154

  
155
    # Use return instead of exit. The set -x options will terminate the script
156
    # but will also trigger ERR traps if defined.
157
    return 1
158
}
159

  
160
warn() {
161
    echo "Warning: $@" >&2
162
    send_monitor_message_${HYPERVISOR} "WARNING: $@"
163
}
164

  
165
report_task_start() {
166
    send_monitor_message_${HYPERVISOR} "$MSG_TYPE_TASK_START:${PROGNAME:2}"
167
}
168

  
169
report_task_end() {
170
    send_monitor_message_${HYPERVISOR} "$MSG_TYPE_TASK_END:${PROGNAME:2}"
171
}
172

  
173
system_poweroff() {
174
    while [ 1 ]; do
175
        # Credits to psomas@grnet.gr for this ...
176
        echo o > /proc/sysrq-trigger
177
        sleep 1
178
    done
179
}
180

  
181
get_base_distro() {
182
    local root_dir=$1
183

  
184
    if [ -e "$root_dir/etc/debian_version" ]; then
185
        echo "debian"
186
    elif [ -e "$root_dir/etc/redhat-release" ]; then
187
        echo "redhat"
188
    elif [ -e "$root_dir/etc/slackware-version" ]; then
189
        echo "slackware"
190
    elif [ -e "$root_dir/etc/SuSE-release" ]; then
191
        echo "suse"
192
    elif [ -e "$root_dir/etc/gentoo-release" ]; then
193
        echo "gentoo"
194
    elif [ -e "$root_dir/etc/arch-release" ]; then
195
        echo "arch"
196
    elif [ -e "$root_dir/etc/freebsd-update.conf" ]; then
197
        echo "freebsd"
198
    elif [ -e "$root_dir/etc/release" ]; then
199
        if grep -i netbsd "$root_dir/etc/release" &> /dev/null; then
200
            echo "netbsd"
201
        else
202
            warn "Unknown Unix flavor."
203
        fi
204
    elif [ -e "$root_dir/etc/motd" ]; then
205
        if grep -i ^openbsd <(head -1 "$root_dir/etc/motd") &> /dev/null; then
206
            echo "openbsd"
207
        else
208
            warn "Unknown Unix flavor"
209
        fi
210
    else
211
        warn "Unknown base distro."
212
    fi
213
}
214

  
215
get_distro() {
216
    local root_dir distro
217
    root_dir=$1
218

  
219
    if [ -e "$root_dir/etc/debian_version" ]; then
220
        distro="debian"
221
        if [ -e ${root_dir}/etc/lsb-release ]; then
222
            ID=$(grep ^DISTRIB_ID= ${root_dir}/etc/lsb-release | cut -d= -f2)
223
            if [ "x$ID" = "xUbuntu" ]; then
224
                distro="ubuntu"
225
            fi
226
        fi
227
        echo "$distro"
228
    elif [ -e "$root_dir/etc/fedora-release" ]; then
229
        echo "fedora"
230
    elif [ -e "$root_dir/etc/centos-release" ]; then
231
        echo "centos"
232
    elif [ -e "$root_dir/etc/redhat-release" ]; then
233
        echo "redhat"
234
    elif [ -e "$root_dir/etc/slackware-version" ]; then
235
        echo "slackware"
236
    elif [ -e "$root_dir/etc/SuSE-release" ]; then
237
        echo "suse"
238
    elif [ -e "$root_dir/etc/gentoo-release" ]; then
239
        echo "gentoo"
240
    elif [ -e "$root_dir/etc/arch-release" ]; then
241
        echo "arch"
242
    elif [ -e "$root_dir/etc/freebsd-update.conf" ]; then
243
        echo "freebsd"
244
    elif [ -e "$root_dir/etc/release" ]; then
245
        if grep -in netbsd "$root_dir/etc/release" &> /dev/null; then
246
            echo "netbsd"
247
        else
248
            warn "Unknown Unix flavor"
249
        fi
250
    elif [ -e "$root_dir/etc/motd" ]; then
251
        if grep -i ^openbsd <(head -1 "$root_dir/etc/motd") &> /dev/null; then
252
            echo "openbsd"
253
        else
254
            warn "Unknown Unix flavor"
255
        fi
256
    else
257
        warn "Unknown distro."
258
    fi
259
}
260

  
261
get_partition_table() {
262
    local dev output
263
    dev="$1"
264
    # If the partition table is gpt then parted will raise an error if the
265
    # secondary gpt is not it the end of the disk, and a warning that has to
266
    # do with the "Last Usable LBA" entry in gpt.
267
    if ! output="$("$PARTED" -s -m "$dev" unit s print | grep -E -v "^(Warning|Error): ")"; then
268
        log_error "Unable to read partition table for device \`${dev}'. The image seems corrupted."
269
    fi
270

  
271
    echo "$output"
272
}
273

  
274
get_partition_table_type() {
275
    local ptable dev field
276
    ptable="$1"
277

  
278
    dev="$(sed -n 2p <<< "$ptable")"
279
    IFS=':' read -ra field <<< "$dev"
280

  
281
    echo "${field[5]}"
282
}
283

  
284
get_partition_count() {
285
    local ptable="$1"
286

  
287
    expr $(echo "$ptable" | wc -l) - 2
288
}
289

  
290
get_partition_by_num() {
291
    local ptable="$1"
292
    local id="$2"
293

  
294
    grep "^$id:" <<< "$ptable"
295
}
296

  
297
get_last_partition() {
298
    local ptable="$1"
299

  
300
    echo "$ptable" | tail -1
301
}
302

  
303
is_extended_partition() {
304
    local dev="$1"
305
    local part_num="$2"
306

  
307
    id=$($SFDISK --force --print-id "$dev" "$part_num")
308
    if [ "$id" = "5" -o "$id" = "f" ]; then
309
        echo "yes"
310
    else
311
        echo "no"
312
    fi
313
}
314

  
315
get_extended_partition() {
316
    local ptable dev
317
    ptable="$1"
318
    dev="$(echo "$ptable" | sed -n 2p | cut -d':' -f1)"
319

  
320
    tail -n +3 <<< "$ptable" | while read line; do
321
        part_num=$(cut -d':' -f1 <<< "$line")
322
        if [ $(is_extended_partition "$dev" "$part_num") == "yes" ]; then
323
            echo "$line"
324
            return 0
325
        fi
326
    done
327
    echo ""
328
}
329

  
330
get_logical_partitions() {
331
    local ptable part_num
332
    ptable="$1"
333

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

  
341
    return 0
342
}
343

  
344
get_last_primary_partition() {
345
    local ptable dev output
346
    ptable="$1"
347
    dev=$(echo "ptable" | sed -n 2p | cut -d':' -f1)
348

  
349
    for i in 4 3 2 1; do
350
        if output=$(grep "^$i:" <<< "$ptable"); then
351
            echo "$output"
352
            return 0
353
        fi
354
    done
355
    echo ""
356
}
357

  
358
get_partition_to_resize() {
359
    local dev table table_type last_part last_part_num extended last_primary \
360
        ext_num prim_num
361
    dev="$1"
362

  
363
    table=$(get_partition_table "$dev")
364
    if [ -z "$table" ]; then
365
        return 0
366
    fi
367

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

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

  
376
    if [ "$table_type" == "msdos" -a $last_part_num -gt 4 ]; then
377
        extended=$(get_extended_partition "$table")
378
        last_primary=$(get_last_primary_partition "$table")
379
        ext_num=$(cut -d: -f1 <<< "$extended")
380
        last_prim_num=$(cut -d: -f1 <<< "$last_primary")
381

  
382
        if [ "$ext_num" != "$last_prim_num" ]; then
383
            echo "$last_prim_num"
384
        else
385
            echo "$last_part_num"
386
        fi
387
    else
388
        echo "$last_part_num"
389
    fi
390
}
391

  
392
create_partition() {
393
    local device="$1"
394
    local part="$2"
395
    local ptype="$3"
396

  
397
    local fields=()
398
    IFS=":;" read -ra fields <<< "$part"
399
    local id="${fields[0]}"
400
    local start="${fields[1]}"
401
    local end="${fields[2]}"
402
    local size="${fields[3]}"
403
    local fs="${fields[4]}"
404
    local name="${fields[5]}"
405
    local flags="${fields[6]//,/ }"
406

  
407
    if [ "$ptype" = "primary" -o "$ptype" = "logical" -o "$ptype" = "extended" ]; then
408
        $PARTED -s -m -- $device mkpart "$ptype" $fs "$start" "$end"
409
        for flag in $flags; do
410
            $PARTED -s -m $device set "$id" "$flag" on
411
        done
412
    else
413
        # For gpt
414
        start=${start:0:${#start}-1} # remove the s at the end
415
        end=${end:0:${#end}-1} # remove the s at the end
416
        $SGDISK -n "$id":"$start":"$end" -t "$id":"$ptype" "$device"
417
    fi
418
}
419

  
420
enlarge_partition() {
421
    local device part ptype new_end fields new_part table logical id
422
    device="$1"
423
    part="$2"
424
    ptype="$3"
425
    new_end="$4"
426

  
427
    if [ -z "$new_end" ]; then
428
        new_end=$(cut -d: -f 3 <<< "$(get_last_free_sector "$device")")
429
    fi
430

  
431
    fields=()
432
    IFS=":;" read -ra fields <<< "$part"
433
    fields[2]="$new_end"
434

  
435
    new_part=""
436
    for ((i = 0; i < ${#fields[*]}; i = i + 1)); do
437
        new_part="$new_part":"${fields[$i]}"
438
    done
439
    new_part=${new_part:1}
440

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

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

  
452
    if [ "$ptype" = "extended" ]; then
453
        # Recreate logical partitions
454
        echo "$logical" | while read logical_part; do
455
            create_partition "$device" "$logical_part" "logical"
456
        done
457
    fi
458
}
459

  
460
get_last_free_sector() {
461
    local dev unit last_line ptype
462
    dev="$1"
463
    unit="$2"
464

  
465
    if [ -n "$unit" ]; then
466
        unit="unit $unit"
467
    fi
468

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

  
472
    if [ "$ptype" = "free;" ]; then
473
        echo "$last_line"
474
    fi
475
}
476

  
477
get_unattend() {
478
    local target exists
479
    target="$1"
480

  
481
    # Workaround to search for $target/Unattend.xml in an case insensitive way.
482
    exists=$(find "$target"/ -maxdepth 1 -iname unattend.xml)
483
    if [ $(wc -l <<< "$exists") -gt 1 ]; then
484
        log_error "Found multiple Unattend.xml files in the image:" $exists
485
    fi
486

  
487
    echo "$exists"
488
}
489

  
490
umount_all() {
491
    local target mpoints
492
    target="$1"
493

  
494
    # Unmount file systems mounted under directory `target'
495
    mpoints="$({ awk "{ if (match(\$2, \"^$target\")) { print \$2 } }" < /proc/mounts; } | sort -rbd | uniq)"
496

  
497
    for mpoint in $mpoints; do
498
        umount $mpoint
499
    done
500
}
501

  
502
get_ufstype() {
503
    local device ufs
504

  
505
    device="$1"
506
    ufs="$($DUMPFS_UFS "$device" | head -1 | awk -F "[()]" '{ for (i=2; i<NF; i+=2) print $i }')"
507

  
508
    case "$ufs" in
509
        UFS1)
510
            echo 44bsd
511
            ;;
512
        UFS2)
513
            echo ufs2
514
            ;;
515
        *)
516
            log_error "Unsupported UFS type: \`$ufs' in device $device"
517
            echo ""
518
            ;;
519
    esac
520
}
521

  
522
cleanup() {
523
    # if something fails here, it shouldn't call cleanup again...
524
    trap - EXIT
525

  
526
    if [ ${#CLEANUP[*]} -gt 0 ]; then
527
        LAST_ELEMENT=$((${#CLEANUP[*]}-1))
528
        REVERSE_INDEXES=$(seq ${LAST_ELEMENT} -1 0)
529
        for i in $REVERSE_INDEXES; do
530
            # If something fails here, it's better to retry it for a few times
531
            # before we give up with an error. This is needed for kpartx when
532
            # dealing with ntfs partitions mounted through fuse. umount is not
533
            # synchronous and may return while the partition is still busy. A
534
            # premature attempt to delete partition mappings through kpartx on
535
            # a device that hosts previously mounted ntfs partition may fail
536
            # with a `device-mapper: remove ioctl failed: Device or resource
537
            # busy' error. A sensible workaround for this is to wait for a
538
            # while and then try again.
539
            local cmd=${CLEANUP[$i]}
540
            $cmd || for interval in 0.25 0.5 1 2 4; do
541
            echo "Command $cmd failed!"
542
            echo "I'll wait for $interval secs and will retry..."
543
            sleep $interval
544
            $cmd && break
545
        done
546
	if [ "$?" != "0" ]; then
547
            echo "Giving Up..."
548
            exit 1;
549
        fi
550
    done
551
  fi
552
}
553

  
554
task_cleanup() {
555
    local rc=$?
556

  
557
    if [ $rc -eq 0 ]; then
558
       report_task_end
559
    else
560
       report_error
561
    fi
562

  
563
    cleanup
564
}
565

  
566
check_if_excluded() {
567
    local name exclude
568
    name="$(tr [a-z] [A-Z] <<< ${PROGNAME:2})"
569
    exclude="SNF_IMAGE_PROPERTY_EXCLUDE_TASK_${name}"
570
    if [ -n "${!exclude}" ]; then
571
        warn "Task ${PROGNAME:2} was excluded and will not run."
572
        exit 0
573
    fi
574

  
575
    return 0
576
}
577

  
578
return_success() {
579
    send_result_${HYPERVISOR} "SUCCESS"
580
}
581

  
582
trap cleanup EXIT
583
set -o pipefail
584

  
585
STDERR_FILE=$(mktemp)
586
add_cleanup rm -f "$STDERR_FILE"
587
exec 2> >(tee -a "$STDERR_FILE" >&2)
588

  
589
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
b/snf-image-helper/common.sh.in
1
# Copyright (C) 2011, 2012, 2013 GRNET S.A.
2
# Copyright (C) 2007, 2008, 2009 Google Inc.
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful, but
10
# WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
# General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
# 02110-1301, USA.
18

  
19
PROGNAME=$(basename $0)
20

  
21
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
22

  
23
# Programs
24
XMLSTARLET=xmlstarlet
25
TUNE2FS=tune2fs
26
RESIZE2FS=resize2fs
27
PARTED=parted
28
SFDISK=sfdisk
29
MKSWAP=mkswap
30
BLKID=blkid
31
BLOCKDEV=blockdev
32
REGLOOKUP=reglookup
33
CHNTPW=chntpw
34
SGDISK=sgdisk
35
GROWFS_UFS=growfs.ufs
36
DUMPFS_UFS=dumpfs.ufs
37
GROWFS_OPENBSD=growfs.openbsd
38
DUMPFS_OPENBSD=dumpfs.openbsd
39
DATE="date -u" # Time in UTC
40
EATMYDATA=eatmydata
41
MOUNT="mount -n"
42

  
43
CLEANUP=( )
44
ERRORS=( )
45
WARNINGS=( )
46

  
47
MSG_TYPE_TASK_START="TASK_START"
48
MSG_TYPE_TASK_END="TASK_END"
49

  
50
STDERR_LINE_SIZE=10
51

  
52
add_cleanup() {
53
    local cmd=""
54
    for arg; do cmd+=$(printf "%q " "$arg"); done
55
    CLEANUP+=("$cmd")
56
}
57

  
58
close_fd() {
59
    local fd=$1
60

  
61
    exec {fd}>&-
62
}
63

  
64
send_result_kvm() {
65
    echo "$@" > /dev/ttyS1
66
}
67

  
68
send_monitor_message_kvm() {
69
    echo "$@" > /dev/ttyS2
70
}
71

  
72
send_result_xen() {
73
    xenstore-write /local/domain/0/snf-image-helper/$DOMID "$*"
74
}
75

  
76
send_monitor_message_xen() {
77
    #Broadcast the message
78
    echo "$@" | socat "STDIO" "UDP-DATAGRAM:${BROADCAST}:${MONITOR_PORT},broadcast"
79
}
80

  
81
prepare_helper() {
82
	local cmdline item key val hypervisor domid
83

  
84
	read -a cmdline	 < /proc/cmdline
85
	for item in "${cmdline[@]}"; do
86
            key=$(cut -d= -f1 <<< "$item")
87
            val=$(cut -d= -f2 <<< "$item")
88
            if [ "$key" = "hypervisor" ]; then
89
                hypervisor="$val"
90
            fi
91
            if [ "$key" = "rules_dev" ]; then
92
                export RULES_DEV="$val"
93
            fi
94
            if [ "$key" = "helper_ip" ]; then
95
                export IP="$val"
96
                export NETWORK="$IP/24"
97
                export BROADCAST="${IP%.*}.255"
98
            fi
99
            if [ "$key" = "monitor_port" ]; then
100
                export MONITOR_PORT="$val"
101
            fi
102
	done
103

  
104
    case "$hypervisor" in
105
    kvm)
106
        HYPERVISOR=kvm
107
        ;;
108
    xen-hvm|xen-pvm)
109
        if [ -z "$IP" ]; then
110
            echo "ERROR: \`helper_ip' not defined or empty" >&2
111
            exit 1
112
        fi
113
        if [ -z "$MONITOR_PORT" ]; then
114
            echo "ERROR: \`monitor_port' not defined or empty" >&2
115
            exit 1
116
        fi
117
        $MOUNT -t xenfs xenfs /proc/xen
118
        ip addr add "$NETWORK" dev eth0
119
        ip link set eth0 up
120
        ip route add default dev eth0
121
        export DOMID=$(xenstore-read domid)
122
        HYPERVISOR=xen
123
        ;;
124
    *)
125
        echo "ERROR: Unknown hypervisor: \`$hypervisor'" >&2
126
        exit 1
127
        ;;
128
    esac
129

  
130
    export HYPERVISOR
131
}
132

  
133
report_error() {
134
    msg=""
135
    if [ ${#ERRORS[*]} -eq 0 ]; then
136
        # No error message. Print stderr
137
        local lines stderr
138
        stderr="$(tail --lines=${STDERR_LINE_SIZE} "$STDERR_FILE")"
139
        lines=$(wc -l <<< "$stderr")
140
        msg="STDERR:${lines}:$stderr"
141
    else
142
        for line in "${ERRORS[@]}"; do
143
            msg+="ERROR:$line"$'\n'
144
        done
145
    fi
146

  
147
    send_monitor_message_${HYPERVISOR} "$msg"
148
}
149

  
150
log_error() {
151
    ERRORS+=("$*")
152

  
153
    send_result_${HYPERVISOR} "ERROR: $@"
154

  
155
    # Use return instead of exit. The set -x options will terminate the script
156
    # but will also trigger ERR traps if defined.
157
    return 1
158
}
159

  
160
warn() {
161
    echo "Warning: $@" >&2
162
    send_monitor_message_${HYPERVISOR} "WARNING: $@"
163
}
164

  
165
report_task_start() {
166
    send_monitor_message_${HYPERVISOR} "$MSG_TYPE_TASK_START:${PROGNAME:2}"
167
}
168

  
169
report_task_end() {
170
    send_monitor_message_${HYPERVISOR} "$MSG_TYPE_TASK_END:${PROGNAME:2}"
171
}
172

  
173
system_poweroff() {
174
    while [ 1 ]; do
175
        # Credits to psomas@grnet.gr for this ...
176
        echo o > /proc/sysrq-trigger
177
        sleep 1
178
    done
179
}
180

  
181
get_base_distro() {
182
    local root_dir=$1
183

  
184
    if [ -e "$root_dir/etc/debian_version" ]; then
185
        echo "debian"
186
    elif [ -e "$root_dir/etc/redhat-release" ]; then
187
        echo "redhat"
188
    elif [ -e "$root_dir/etc/slackware-version" ]; then
189
        echo "slackware"
190
    elif [ -e "$root_dir/etc/SuSE-release" ]; then
191
        echo "suse"
192
    elif [ -e "$root_dir/etc/gentoo-release" ]; then
193
        echo "gentoo"
194
    elif [ -e "$root_dir/etc/arch-release" ]; then
195
        echo "arch"
196
    elif [ -e "$root_dir/etc/freebsd-update.conf" ]; then
197
        echo "freebsd"
198
    elif [ -e "$root_dir/etc/release" ]; then
199
        if grep -i netbsd "$root_dir/etc/release" &> /dev/null; then
200
            echo "netbsd"
201
        else
202
            warn "Unknown Unix flavor."
203
        fi
204
    elif [ -e "$root_dir/etc/motd" ]; then
205
        if grep -i ^openbsd <(head -1 "$root_dir/etc/motd") &> /dev/null; then
206
            echo "openbsd"
207
        else
208
            warn "Unknown Unix flavor"
209
        fi
210
    else
211
        warn "Unknown base distro."
212
    fi
213
}
214

  
215
get_distro() {
216
    local root_dir distro
217
    root_dir=$1
218

  
219
    if [ -e "$root_dir/etc/debian_version" ]; then
220
        distro="debian"
221
        if [ -e ${root_dir}/etc/lsb-release ]; then
222
            ID=$(grep ^DISTRIB_ID= ${root_dir}/etc/lsb-release | cut -d= -f2)
223
            if [ "x$ID" = "xUbuntu" ]; then
224
                distro="ubuntu"
225
            fi
226
        fi
227
        echo "$distro"
228
    elif [ -e "$root_dir/etc/fedora-release" ]; then
229
        echo "fedora"
230
    elif [ -e "$root_dir/etc/centos-release" ]; then
231
        echo "centos"
232
    elif [ -e "$root_dir/etc/redhat-release" ]; then
233
        echo "redhat"
234
    elif [ -e "$root_dir/etc/slackware-version" ]; then
235
        echo "slackware"
236
    elif [ -e "$root_dir/etc/SuSE-release" ]; then
237
        echo "suse"
238
    elif [ -e "$root_dir/etc/gentoo-release" ]; then
239
        echo "gentoo"
240
    elif [ -e "$root_dir/etc/arch-release" ]; then
241
        echo "arch"
242
    elif [ -e "$root_dir/etc/freebsd-update.conf" ]; then
243
        echo "freebsd"
244
    elif [ -e "$root_dir/etc/release" ]; then
245
        if grep -in netbsd "$root_dir/etc/release" &> /dev/null; then
246
            echo "netbsd"
247
        else
248
            warn "Unknown Unix flavor"
249
        fi
250
    elif [ -e "$root_dir/etc/motd" ]; then
251
        if grep -i ^openbsd <(head -1 "$root_dir/etc/motd") &> /dev/null; then
252
            echo "openbsd"
253
        else
254
            warn "Unknown Unix flavor"
255
        fi
256
    else
257
        warn "Unknown distro."
258
    fi
259
}
260

  
261
get_partition_table() {
262
    local dev output
263
    dev="$1"
264
    # If the partition table is gpt then parted will raise an error if the
265
    # secondary gpt is not it the end of the disk, and a warning that has to
266
    # do with the "Last Usable LBA" entry in gpt.
267
    if ! output="$("$PARTED" -s -m "$dev" unit s print | grep -E -v "^(Warning|Error): ")"; then
268
        log_error "Unable to read partition table for device \`${dev}'. The image seems corrupted."
269
    fi
270

  
271
    echo "$output"
272
}
273

  
274
get_partition_table_type() {
275
    local ptable dev field
276
    ptable="$1"
277

  
278
    dev="$(sed -n 2p <<< "$ptable")"
279
    IFS=':' read -ra field <<< "$dev"
280

  
281
    echo "${field[5]}"
282
}
283

  
284
get_partition_count() {
285
    local ptable="$1"
286

  
287
    expr $(echo "$ptable" | wc -l) - 2
288
}
289

  
290
get_partition_by_num() {
291
    local ptable="$1"
292
    local id="$2"
293

  
294
    grep "^$id:" <<< "$ptable"
295
}
296

  
297
get_last_partition() {
298
    local ptable="$1"
299

  
300
    echo "$ptable" | tail -1
301
}
302

  
303
is_extended_partition() {
304
    local dev="$1"
305
    local part_num="$2"
306

  
307
    id=$($SFDISK --force --print-id "$dev" "$part_num")
308
    if [ "$id" = "5" -o "$id" = "f" ]; then
309
        echo "yes"
310
    else
311
        echo "no"
312
    fi
313
}
314

  
315
get_extended_partition() {
316
    local ptable dev
317
    ptable="$1"
318
    dev="$(echo "$ptable" | sed -n 2p | cut -d':' -f1)"
319

  
320
    tail -n +3 <<< "$ptable" | while read line; do
321
        part_num=$(cut -d':' -f1 <<< "$line")
322
        if [ $(is_extended_partition "$dev" "$part_num") == "yes" ]; then
323
            echo "$line"
324
            return 0
325
        fi
326
    done
327
    echo ""
328
}
329

  
330
get_logical_partitions() {
331
    local ptable part_num
332
    ptable="$1"
333

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

  
341
    return 0
342
}
343

  
344
get_last_primary_partition() {
345
    local ptable dev output
346
    ptable="$1"
347
    dev=$(echo "ptable" | sed -n 2p | cut -d':' -f1)
348

  
349
    for i in 4 3 2 1; do
350
        if output=$(grep "^$i:" <<< "$ptable"); then
351
            echo "$output"
352
            return 0
353
        fi
354
    done
355
    echo ""
356
}
357

  
358
get_partition_to_resize() {
359
    local dev table table_type last_part last_part_num extended last_primary \
360
        ext_num prim_num
361
    dev="$1"
362

  
363
    table=$(get_partition_table "$dev")
364
    if [ -z "$table" ]; then
365
        return 0
366
    fi
367

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

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

  
376
    if [ "$table_type" == "msdos" -a $last_part_num -gt 4 ]; then
377
        extended=$(get_extended_partition "$table")
378
        last_primary=$(get_last_primary_partition "$table")
379
        ext_num=$(cut -d: -f1 <<< "$extended")
380
        last_prim_num=$(cut -d: -f1 <<< "$last_primary")
381

  
382
        if [ "$ext_num" != "$last_prim_num" ]; then
383
            echo "$last_prim_num"
384
        else
385
            echo "$last_part_num"
386
        fi
387
    else
388
        echo "$last_part_num"
389
    fi
390
}
391

  
392
create_partition() {
393
    local device="$1"
394
    local part="$2"
395
    local ptype="$3"
396

  
397
    local fields=()
398
    IFS=":;" read -ra fields <<< "$part"
399
    local id="${fields[0]}"
400
    local start="${fields[1]}"
401
    local end="${fields[2]}"
402
    local size="${fields[3]}"
403
    local fs="${fields[4]}"
404
    local name="${fields[5]}"
405
    local flags="${fields[6]//,/ }"
406

  
407
    if [ "$ptype" = "primary" -o "$ptype" = "logical" -o "$ptype" = "extended" ]; then
408
        $PARTED -s -m -- $device mkpart "$ptype" $fs "$start" "$end"
409
        for flag in $flags; do
410
            $PARTED -s -m $device set "$id" "$flag" on
411
        done
412
    else
413
        # For gpt
414
        start=${start:0:${#start}-1} # remove the s at the end
415
        end=${end:0:${#end}-1} # remove the s at the end
416
        $SGDISK -n "$id":"$start":"$end" -t "$id":"$ptype" "$device"
417
    fi
418
}
419

  
420
enlarge_partition() {
421
    local device part ptype new_end fields new_part table logical id
422
    device="$1"
423
    part="$2"
424
    ptype="$3"
425
    new_end="$4"
426

  
427
    if [ -z "$new_end" ]; then
428
        new_end=$(cut -d: -f 3 <<< "$(get_last_free_sector "$device")")
429
    fi
430

  
431
    fields=()
432
    IFS=":;" read -ra fields <<< "$part"
433
    fields[2]="$new_end"
434

  
435
    new_part=""
436
    for ((i = 0; i < ${#fields[*]}; i = i + 1)); do
437
        new_part="$new_part":"${fields[$i]}"
438
    done
439
    new_part=${new_part:1}
440

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

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

  
452
    if [ "$ptype" = "extended" ]; then
453
        # Recreate logical partitions
454
        echo "$logical" | while read logical_part; do
455
            create_partition "$device" "$logical_part" "logical"
456
        done
457
    fi
458
}
459

  
460
get_last_free_sector() {
461
    local dev unit last_line ptype
462
    dev="$1"
463
    unit="$2"
464

  
465
    if [ -n "$unit" ]; then
466
        unit="unit $unit"
467
    fi
468

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

  
472
    if [ "$ptype" = "free;" ]; then
473
        echo "$last_line"
474
    fi
475
}
476

  
477
get_unattend() {
478
    local target exists
479
    target="$1"
480

  
481
    # Workaround to search for $target/Unattend.xml in an case insensitive way.
482
    exists=$(find "$target"/ -maxdepth 1 -iname unattend.xml)
483
    if [ $(wc -l <<< "$exists") -gt 1 ]; then
484
        log_error "Found multiple Unattend.xml files in the image:" $exists
485
    fi
486

  
487
    echo "$exists"
488
}
489

  
490
disklabel2linux() {
491
    local partition i p
492
    partition="$1"
493

  
494
    i=4
495
    # Partition 'c' traditionally used to describe the entire disk is not
496
    # mapped to /dev/sda7 by the kernel
497
    for p in a b {d..p}; do
498
        let i++
499
        if [ "$p" = "$partition" ]; then
500
           echo "$i"
501
           return 0
502
        fi
503
    done
504

  
505
    log_error "Invalid BSD partition label: \`$partition'"
506
}
507

  
508
mount_all() {
509
    local osfamily target fs device fstab entry duid opts types num
510
    osfamily="$1"
511
    device="$2"
512
    target="$3"
513

  
514
    case "$osfamily" in
515
    linux)
516
        fs="ext[234]|msdos|vfat|ntfs"
517
        ;;
518
    freebsd)
519
        fs="ufs|msdosfs|ntfs"
520
        ;;
521
    openbsd)
522
        fs="ffs|msdos|ntfs|ext2fs"
523
        ;;
524
    netbsd)
525
        fs="ffs|ufs|msdos|ext2fs|ntfs"
526
        ;;
527
    *)
528
        log_error "Unsupported osfamily: \`$osfamily'"
529
        ;;
530
    esac
531

  
532
    fstab="$(grep -v ^\# "${target}/etc/fstab" | awk "{ if (match(\$3, \"$fs\")) { print \$2,\$1,\$3 } }" | sort -bd)"
533
    # <mpoint> <device> <fs>
534
    while read -ra entry; do
535
        # Skip root. It is already mounted
536
        if [ "${entry[0]}" = "/" ]; then
537
            continue
538
        fi
539

  
540
        opts="rw"
541
        types="auto"
542

  
543
        if [ "$osfamily" = linux ]; then
544
            # Linux persistent block device naming
545
            if [[ ${entry[1]} =~ ^(LABEL|UUID)= ]]; then
546
                entry[1]=$(findfs "${entry[1]}")
547
            else
548
                log_error "fstab contains non-persistent block device names"
549
            fi
550
        else
551
            if [[ "$osfamily" =~ ^(open|net)bsd$ ]]; then
552
                # OpenBSD DUIDs device naming
553
                if [[ "${entry[1]}" =~ ^[a-f0-9]{16}\.[a-p]$ ]]; then
554
                    duid="$(@scriptsdir@/disklabel.py --print-duid "$device")"
555

  
556
                    if [[ ! "${entry[1]}" =~ ^$duid ]]; then
557
                        warn "fstab refers to unknown DUID: \`$duid'"
558
                        continue
559
                    fi
560
                fi
561
                num="$(disklabel2linux "${entry[1]: -1}")"
562
                if [ "${entry[2]}" = ffs -o "$entry[2]" = ufs ]; then
563
                    types="ufs"
564
                    opts="ufstype=44bsd,rw"
565
                fi
566
            else # FreeBSD
567
                # We do not support FreeBSD labels for now
568
                if [[ "${entry[1]}" =~ ^/dev/(ufs|label)/ ]]; then
569
                    log_error "fstab contains FreeBSD labels. We currently don't support them"
570
                fi
571
                num="${entry[1]: -1}"
572
                if [ "${entry[2]}" = ufs ]; then
573
                    types="ufs"
574
                    opts="ufstype=ufs2,rw"
575
                fi
576
            fi
577
            entry[1]="${device}${num}"
578
        fi
579

  
580
        $MOUNT -t "$types" -o "$opts" "${entry[1]}" "${target}${entry[0]}"
581
        # In many cases when you try to mount a UFS file system read-write, the
582
        # mount command returns SUCCESS and a message like this gets printed:
583
        #
584
        #   mount: warning: <target> seems to be mounted read-only.
585
        #
586
        # remounting does the trick
587
        if [ "$types" = ufs ]; then
588
            $MOUNT -o remount,rw "${entry[1]}"
589
        fi
590

  
591
    done <<< "$fstab"
592
}
593

  
594
umount_all() {
595
    local target mpoints
596
    target="$1"
597

  
598
    # Unmount file systems mounted under directory `target'
599
    mpoints="$({ awk "{ if (match(\$2, \"^$target\")) { print \$2 } }" < /proc/mounts; } | sort -rbd | uniq)"
600

  
601
    for mpoint in $mpoints; do
602
        umount $mpoint
603
    done
604
}
605

  
606
get_ufstype() {
607
    local device ufs
608

  
609
    device="$1"
610
    ufs="$($DUMPFS_UFS "$device" | head -1 | awk -F "[()]" '{ for (i=2; i<NF; i+=2) print $i }')"
611

  
612
    case "$ufs" in
613
        UFS1)
614
            echo 44bsd
615
            ;;
616
        UFS2)
617
            echo ufs2
618
            ;;
619
        *)
620
            log_error "Unsupported UFS type: \`$ufs' in device $device"
621
            echo ""
622
            ;;
623
    esac
624
}
625

  
626
cleanup() {
627
    # if something fails here, it shouldn't call cleanup again...
628
    trap - EXIT
629

  
630
    if [ ${#CLEANUP[*]} -gt 0 ]; then
631
        LAST_ELEMENT=$((${#CLEANUP[*]}-1))
632
        REVERSE_INDEXES=$(seq ${LAST_ELEMENT} -1 0)
633
        for i in $REVERSE_INDEXES; do
634
            # If something fails here, it's better to retry it for a few times
635
            # before we give up with an error. This is needed for kpartx when
636
            # dealing with ntfs partitions mounted through fuse. umount is not
637
            # synchronous and may return while the partition is still busy. A
638
            # premature attempt to delete partition mappings through kpartx on
639
            # a device that hosts previously mounted ntfs partition may fail
640
            # with a `device-mapper: remove ioctl failed: Device or resource
641
            # busy' error. A sensible workaround for this is to wait for a
642
            # while and then try again.
643
            local cmd=${CLEANUP[$i]}
644
            $cmd || for interval in 0.25 0.5 1 2 4; do
645
            echo "Command $cmd failed!"
646
            echo "I'll wait for $interval secs and will retry..."
647
            sleep $interval
648
            $cmd && break
649
        done
650
	if [ "$?" != "0" ]; then
651
            echo "Giving Up..."
652
            exit 1;
653
        fi
654
    done
655
  fi
656
}
657

  
658
task_cleanup() {
659
    local rc=$?
660

  
661
    if [ $rc -eq 0 ]; then
662
       report_task_end
663
    else
664
       report_error
665
    fi
666

  
667
    cleanup
668
}
669

  
670
check_if_excluded() {
671
    local name exclude
672
    name="$(tr [a-z] [A-Z] <<< ${PROGNAME:2})"
673
    exclude="SNF_IMAGE_PROPERTY_EXCLUDE_TASK_${name}"
674
    if [ -n "${!exclude}" ]; then
675
        warn "Task ${PROGNAME:2} was excluded and will not run."
676
        exit 0
677
    fi
678

  
679
    return 0
680
}
681

  
682
return_success() {
683
    send_result_${HYPERVISOR} "SUCCESS"
684
}
685

  
686
trap cleanup EXIT
687
set -o pipefail
688

  
689
STDERR_FILE=$(mktemp)
690
add_cleanup rm -f "$STDERR_FILE"
691
exec 2> >(tee -a "$STDERR_FILE" >&2)
692

  
693
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
b/snf-image-helper/tasks/30MountImage.in
50 50
        os=${SNF_IMAGE_PROPERTY_OSFAMILY^^[bsd]}
51 51
        log_error "For ${os^?} images only UFS root partitions are supported."
52 52
    fi
53

  
53 54
    ufstype="$(get_ufstype "$rootdev")"
54 55
    if [ "x$ufstype" = "x" ]; then
55
        exit 1
56
        log_error "Unable to recognize the UFS type in the root partition."
56 57
    fi
58

  
57 59
    $MOUNT -t ufs -o ufstype="$ufstype,rw" "$rootdev" "$SNF_IMAGE_TARGET"
58 60

  
59
    # In many cases when you try to mount a UFS file system read-write,
60
    # the mount command returns SUCCESS and a message like this gets printed:
61
    #
62
    #   mount: warning: /mnt seems to be mounted read-only.
63
    #
64
    # remounting does the trick
61
    # See mount_all() for a reason why we do this
65 62
    $MOUNT -o remount,rw "$SNF_IMAGE_TARGET"
66 63
else
67 64
    $MOUNT -o rw "$rootdev" "$SNF_IMAGE_TARGET"
68 65
fi
69 66

  
70
if [ "$SNF_IMAGE_PROPERTY_OSFAMILY" != "linux" ]; then
67
if [ "$SNF_IMAGE_PROPERTY_OSFAMILY" = "windows" ]; then
71 68
    exit 0
72 69
fi
73 70

  
74
if [ ! -f "${SNF_IMAGE_TARGET}/etc/fstab" ]; then
75
    log_error "/etc/fstab is missing from the root file system"
76
fi
77

  
78
# Read the local partitions from fstab and get a sorted list:
79
#<mount_point> <device> <options>
80
fstab="$(grep -v ^\# "${SNF_IMAGE_TARGET}/etc/fstab" | awk '{ if (match($3, "ext[234]")) { print $2,$1,$4 } }' | sort -bd)"
81

  
82
# Mount non-root filesystems
83
while read line; do
84
    read -ra entry <<< "$line"
85
    # Skip root. It is already mounted
86
    if [ "${entry[0]}" = "/" ]; then
87
        continue
88
    fi
89

  
90
    if [[ ${entry[1]} =~ ^(LABEL|UUID)= ]]; then
91
        entry[1]=$(findfs "${entry[1]}")
92
    fi
93

  
94
    # I'm in doupt. Sould I mount the filesystems with the mount options
95
    # found in the image's /etc/fstab or not?
96
    $MOUNT "${entry[1]}" "${SNF_IMAGE_TARGET}${entry[0]}" # -o "${entry[2]}"
97

  
98
done <<< "$fstab"
71
mount_all "$SNF_IMAGE_PROPERTY_OSFAMILY" "$SNF_IMAGE_DEV" "$SNF_IMAGE_TARGET"
99 72

  
100 73
exit 0
101 74

  

Also available in: Unified diff