~ubuntu-branches/ubuntu/trusty/cloud-utils/trusty-proposed

« back to all changes in this revision

Viewing changes to bin/mount-image-callback

  • Committer: Package Import Robot
  • Author(s): Scott Moser
  • Date: 2013-08-27 16:23:58 UTC
  • mfrom: (99.1.7 saucy)
  • Revision ID: package-import@ubuntu.com-20130827162358-x2bz93z1kt0xcl0f
Tags: 0.27-0ubuntu3
* sync to trunk at revno 246
  * cloud-localds: add man page [Thomas Bechtold]
  * cloud-localds: only use qemu-img convert if output format is not 'raw'
  * cloud-localds: add '--hostname' flag to specify local-hostname in
    meta-data.
  * cloud-publish-image: add '--architecture' when using 'register'
  * cloud-publish-image: improvements to '-v' (debugging)
  * cloud-publish-image: pass through --root-device-name
  * cloud-run-instances: dropped (obsolete, not recommended)
  * remove ubuntu-cloud-keyring (replaced in ubuntu by
    ubuntu-cloudimage-keyring)
  * mount-image-callback: add utility
* split package into cloud-guest-utils and cloud-image-utils.
* remove deprecated 'uec-*' commands: uec-publish-tarball, uec-publish-image,
  uec-run-instances, uec-resize-image.
* fix lintian issue with debian/copyright

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/bin/bash
 
2
 
 
3
VERBOSITY=0
 
4
TEMP_D=""
 
5
UMOUNT=""
 
6
QEMU_DISCONNECT=""
 
7
 
 
8
error() { echo "$@" 1>&2; }
 
9
 
 
10
Usage() {
 
11
        cat <<EOF
 
12
Usage: ${0##*/} [ options ] file cmd [ args ]
 
13
 
 
14
   mount a file to a temporary mount point and then
 
15
   invoke the provided cmd with args
 
16
 
 
17
   the temporary mountpoint will be put in an a environment variable
 
18
   named MOUNTPOINT.
 
19
 
 
20
   if any of the arguments are the literal string '_MOUNTPOINT_', then
 
21
   they will be replaced with the mount point. Example:
 
22
      ${0##*/} my.img chroot _MOUNTPOINT_ /bin/sh
 
23
 
 
24
   options:
 
25
    -v | --verbose             increase verbosity
 
26
         --read-only           use read-only mount.
 
27
    -p | --proc                bind mount /proc
 
28
    -s | --sys                 bind mount /sys
 
29
    -d | --dev                 bind mount /dev
 
30
         --system-mounts       bind mount /sys, /proc, /dev
 
31
         --system-resolvconf   copy host's resolvconf into /etc/resolvconf
 
32
EOF
 
33
}
 
34
 
 
35
# umount_r(mp) : unmount any filesystems under r
 
36
#  this is useful to unmount a chroot that had sys, proc ... mounted
 
37
umount_r() {
 
38
        local p
 
39
        for p in "$@"; do
 
40
                [ -n "$p" ] || continue
 
41
                tac /proc/mounts | sh -c '
 
42
                        p=$1
 
43
                        while read s mp t opt a b ; do
 
44
                                [ "${mp}" = "${p}" -o "${mp#${p}/}" != "${mp}" ] ||
 
45
                                        continue
 
46
                                umount "$mp" || exit 1
 
47
                        done
 
48
                        exit 0' umount_r "${p%/}"
 
49
                [ $? -eq 0 ] || return
 
50
        done
 
51
}
 
52
 
 
53
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; exit 1; }
 
54
cleanup() {
 
55
        if [ -n "$UMOUNT" ]; then
 
56
                umount_r "$UMOUNT" ||
 
57
                        error "WARNING: unmounting filesystems failed!"
 
58
        fi
 
59
        if [ -n "$QEMU_DISCONNECT" ]; then
 
60
                local out=""
 
61
                out=$(qemu-nbd --disconnect "$QEMU_DISCONNECT" 2>&1) || {
 
62
                        error "warning: failed: qemu-nbd --disconnect $QEMU_DISCONNECT"
 
63
                        error "$out"
 
64
                }
 
65
        fi
 
66
        [ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] ||
 
67
                rm --one-file-system -Rf "${TEMP_D}" ||
 
68
                error "removal of temp dir failed!"
 
69
}
 
70
 
 
71
debug() {
 
72
        local level="$1"; shift;
 
73
        [ "${level}" -gt "${VERBOSITY}" ] && return
 
74
        error "${@}"
 
75
}
 
76
 
 
77
mount_callback_umount() {
 
78
        local img_in="$1" dev="" out="" mp="" ret="" img="" ro=""
 
79
        local opts="" bmounts="" system_resolvconf=false
 
80
 
 
81
        short_opts="dhpsv"
 
82
        long_opts="dev,help,proc,read-only,sys,system-mounts,system-resolvconf,verbose"
 
83
        getopt_out=$(getopt --name "${0##*/}" \
 
84
                --options "${short_opts}" --long "${long_opts}" -- "$@") &&
 
85
                eval set -- "${getopt_out}" ||
 
86
                { bad_Usage; return 1; }
 
87
 
 
88
        while [ $# -ne 0 ]; do
 
89
                cur=${1}; next=${2};
 
90
                case "$cur" in
 
91
                        -d|--dev) bmounts="${bmounts:+${bmounts} /dev}";;
 
92
                        -h|--help) Usage ; exit 0;;
 
93
                        -p|--proc) bmounts="${bmounts:+${bmounts} /proc}";;
 
94
                        -s|--sys) bmounts="${bmounts:+${bmounts} /sys}";;
 
95
                           --system-mounts) bmounts="/dev /proc /sys";;
 
96
                           --system-resolvconf) system_resolvconf=true;;
 
97
                        -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
 
98
                           --opts) opts="${opts} $next"; shift;;
 
99
                           --read-only) ro="ro";;
 
100
                        --) shift; break;;
 
101
                esac
 
102
                shift;
 
103
        done
 
104
 
 
105
        [ $# -ge 2 ] || { bad_Usage "must provide image and cmd"; return 1; }
 
106
 
 
107
        [ -n "$ro" ] && $system_resolvconf && {
 
108
                error "--read-only is incompatible with system-resolvconf";
 
109
                return 1;
 
110
        }
 
111
 
 
112
        img_in="$1"
 
113
        shift 1
 
114
 
 
115
        img=$(readlink -f "$img_in") ||
 
116
                { error "failed to get full path to $img_in"; return 1; }
 
117
 
 
118
        [ "$(id -u)" = "0" ] || 
 
119
                { error "sorry, must be root"; return 1; }
 
120
 
 
121
        TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") ||
 
122
                { error "failed to make tempdir"; return 1; }
 
123
        trap cleanup EXIT
 
124
 
 
125
        mp="${TEMP_D}/mp"
 
126
 
 
127
        mkdir "$mp" || return
 
128
 
 
129
        local cmd="" arg="" found=false
 
130
        cmd=( )
 
131
        for arg in "$@"; do
 
132
                if [ "${arg}" = "_MOUNTPOINT_" ]; then
 
133
                        debug 1 "replaced string _MOUNTPOINT_ in arguments arg ${#cmd[@]}"
 
134
                        arg=$mp
 
135
                fi
 
136
                cmd[${#cmd[@]}]="$arg"
 
137
        done
 
138
 
 
139
        if [ "${cmd[0]##*/}" = "bash" -o "${cmd[0]##*/}" = "sh" ] &&
 
140
           [ ${#cmd[@]} -eq 0 ]; then
 
141
                debug 1 "invoking shell ${cmd[0]}"
 
142
                error "MOUNTPOINT=$mp"
 
143
        fi
 
144
 
 
145
        local hasqemu=false
 
146
        command -v "qemu-nbd" >/dev/null 2>&1 && hasqemu=true
 
147
 
 
148
        if out=$(set -f; mount -o loop${ro:+,$ro} $opts \
 
149
                 "$img" "$mp" 2>&1); then
 
150
                debug 1 "mounted simple filesystem image '$img_in'"
 
151
                UMOUNT="$mp"
 
152
        else
 
153
                if ! $hasqemu; then
 
154
                        error "simple mount of '$img_in' failed."
 
155
                        error "if this not a raw image, or it is partitioned"
 
156
                        error "you must have qemu-nbd (apt-get install qemu-utils)"
 
157
                        error "mount failed with: $out"
 
158
                        return 1
 
159
                fi
 
160
        fi
 
161
 
 
162
        if [ -z "$UMOUNT" ]; then
 
163
                if [ ! -e /sys/block/nbd0 ] && ! grep -q nbd /proc/modules; then
 
164
                        debug 1 "trying to load nbd module"
 
165
                        modprobe nbd >/dev/null 2>&1
 
166
                        udevadm settle >/dev/null 2>&1
 
167
                fi
 
168
                [ -e /sys/block/nbd0 ] || {
 
169
                        error "no nbd kernel support, but simple mount failed"
 
170
                        return 1;
 
171
                }
 
172
 
 
173
                local f nbd=""
 
174
                for f in /sys/block/nbd*; do
 
175
                        [ -d "$f" -a ! -f "$f/pid" ] && nbd=${f##*/} && break
 
176
                done
 
177
                if [ -z "$nbd" ]; then
 
178
                        error "failed to find an nbd device"
 
179
                        return 1;
 
180
                fi
 
181
                nbd="/dev/$nbd"
 
182
 
 
183
                if ! qemu-nbd --connect "$nbd" "$img"; then
 
184
                        error "failed to qemu-nbd connect $img to $nbd"
 
185
                        return 1
 
186
                fi
 
187
                QEMU_DISCONNECT="$nbd"
 
188
 
 
189
                local pfile="/sys/block/${nbd#/dev/}/pid"
 
190
                if [ ! -f "$pfile" ]; then
 
191
                        debug 1 "waiting on pidfile for $nbd in $pfile"
 
192
                        local i=0
 
193
                        while [ ! -f "$pfile" ] && i=$(($i+1)); do
 
194
                                if [ $i -eq 200 ]; then
 
195
                                        error "giving up on pidfile $pfile for $nbd"
 
196
                                        return 1
 
197
                                fi
 
198
                                sleep .1
 
199
                                debug 2 "."
 
200
                        done
 
201
                fi
 
202
 
 
203
                debug 1 "connected $img_in to $nbd. now udev-settling"
 
204
                udevadm settle >/dev/null 2>&1
 
205
 
 
206
                local mdev="$nbd"
 
207
                if [ -b "${nbd}p1" ]; then
 
208
                        mdev="${nbd}p1"
 
209
                fi
 
210
                if ( set -f; mount ${ro:+-o ${ro}} $opts "$mdev" "$mp" ) &&
 
211
                        UMOUNT="$mp"; then
 
212
                        debug 1 "mounted $mdev via qemu-nbd $nbd"
 
213
                else
 
214
                        local pid="" pfile="/sys/block/${nbd#/dev/}/pid"
 
215
                        { read pid < "$pfile" ; } >/dev/null 2>&1
 
216
                        [ -n "$pid" -a ! -d "/proc/$pid" ] ||
 
217
                                error "qemu-nbd process seems to have died. was '$pid'"
 
218
 
 
219
                        qemu-nbd --disconnect "$nbd" && QEMU_DISCONNECT=""
 
220
                        error "failed to mount $mdev"
 
221
                        return 1
 
222
                fi
 
223
 
 
224
        fi
 
225
 
 
226
        local bindmp=""
 
227
        for bindmp in $bmounts; do
 
228
                [ -d "$mp${bindmp}" ] || mkdir "$mp${bindmp}" ||
 
229
                        { error "failed mkdir $bindmp in mount"; return 1; }
 
230
                mount --bind "$bindmp" "$mp/${bindmp}" ||
 
231
                        { error "failed bind mount '$bindmp'"; return 1; }
 
232
        done
 
233
 
 
234
        if ${system_resolvconf}; then
 
235
                local rcf="$mp/etc/resolv.conf"
 
236
                debug 1 "replacing /etc/resolvconf"
 
237
                if [ -e "$rcf" -o -L "$rcf" ]; then
 
238
                        local trcf="$rcf.${0##*/}.$$"
 
239
                        rm -f "$trcf" &&
 
240
                                mv "$rcf" "$trcf" && ORIG_RESOLVCONF="$trcf" ||
 
241
                                { error "failed mv $rcf"; return 1; }
 
242
                fi
 
243
                cp "/etc/resolv.conf" "$rcf" ||
 
244
                        { error "failed copy /etc/resolv.conf"; return 1; }
 
245
        fi
 
246
 
 
247
        debug 1 "invoking: MOUNTPOINT=$mp" "${cmd[@]}"
 
248
        MOUNTPOINT="$mp" "${cmd[@]}"
 
249
        ret=$?
 
250
 
 
251
        if ${system_resolvconf}; then
 
252
                local rcf="$mp/etc/resolv.conf"
 
253
                cmp --quiet "/etc/resolv.conf" "$rcf" >/dev/null ||
 
254
                        error "WARN: /etc/resolv.conf changed in image!"
 
255
                rm "$rcf" &&
 
256
                        { [ -z "$ORIG_RESOLVCONF" ] || mv "$ORIG_RESOLVCONF" "$rcf"; } ||
 
257
                        { error "failed to restore /etc/resolv.conf"; return 1; }
 
258
        fi
 
259
 
 
260
        debug 1 "cmd returned $ret. unmounting $mp"
 
261
        umount_r "$mp" || { error "failed umount $img"; return 1; }
 
262
        UMOUNT=""
 
263
        rmdir "$mp"
 
264
 
 
265
        if [ -n "$QEMU_DISCONNECT" ]; then
 
266
                local out=""
 
267
                out=$(qemu-nbd --disconnect "$QEMU_DISCONNECT" 2>&1) &&
 
268
                        QEMU_DISCONNECT="" || {
 
269
                                error "failed to disconnect $QEMU_DISCONNECT";
 
270
                                error "$out"
 
271
                                return 1;
 
272
                }
 
273
        fi
 
274
        return $ret
 
275
}
 
276
 
 
277
mount_callback_umount "$@"
 
278
 
 
279
# vi: ts=4 noexpandtab