blob: fdd2d121fcfe2e68fb981fefae4c5c19438af686 [file] [log] [blame]
Federico Ressi08c74e92018-06-12 14:19:21 +02001#!/bin/bash
2
3# IMPLEMENTATION NOTE: It was not possible to implement this script using
4# virt-customize because of below ubuntu bugs:
5# - https://bugs.launchpad.net/ubuntu/+source/libguestfs/+bug/1632405
6# - https://bugs.launchpad.net/ubuntu/+source/isc-dhcp/+bug/1650740
7#
8# It has therefore been adopted a more low level strategy performing below
9# steps:
10# - mount guest image to a temporary folder
11# - set up an environment suitable for executing chroot
12# - execute customize_image function inside chroot environment
13# - cleanup chroot environment
14
15# Array of packages to be installed of guest image
16INSTALL_GUEST_PACKAGES=(
17 socat # used to replace nc for testing advanced network features like
18 # multicast
Slawek Kaplonski7e5923a2021-10-08 16:05:21 +020019 iperf3
20 iputils-ping
21 ncat
22 psmisc # provides killall command
23 python3
24 tcpdump
25 vlan
Federico Ressi08c74e92018-06-12 14:19:21 +020026)
27
28# Function to be executed once after chroot on guest image
29# Add more customization steps here
30function customize_image {
31 # dhclient-script requires to read /etc/fstab for setting up network
32 touch /etc/fstab
33 chmod ugo+r /etc/fstab
34
35 # Ubuntu guest image _apt user could require access to below folders
36 local apt_user_folders=( /var/lib/apt/lists/partial )
37 mkdir -p "${apt_user_folders[@]}"
38 chown _apt.root -fR "${apt_user_folders[@]}"
39
40 # Install desired packages to Ubuntu guest image
Federico Ressi71bda862018-05-28 11:38:56 +020041 (
42 DEBIAN_FRONTEND=noninteractive
Slawek Kaplonski7e5923a2021-10-08 16:05:21 +020043 sudo apt-get update -y
44 sudo apt-get install -y "${INSTALL_GUEST_PACKAGES[@]}"
Federico Ressi71bda862018-05-28 11:38:56 +020045 )
Federico Ressi08c74e92018-06-12 14:19:21 +020046}
47
48function main {
49 set -eux
50 trap cleanup EXIT
51 "${ENTRY_POINT:-chroot_image}" "$@"
52}
53
54# Chroot to guest image then executes customize_image function inside it
55function chroot_image {
56 local image_file=$1
57 local temp_dir=${TEMP_DIR:-$(make_temp -d)}
58
59 # Mount guest image into a temporary directory
60 local mount_dir=${temp_dir}/mount
61 mkdir -p "${mount_dir}"
62 mount_image "${mount_dir}" "${temp_dir}/pid"
63
64 # Mount system directories
65 bind_dir "/dev" "${mount_dir}/dev"
66 bind_dir "/dev/pts" "${mount_dir}/dev/pts"
67 bind_dir "/proc" "${mount_dir}/proc"
68 bind_dir "/sys" "${mount_dir}/sys"
69
70 # Mount to keep temporary files out of guest image
71 mkdir -p "${temp_dir}/apt" "${temp_dir}/cache" "${temp_dir}/tmp"
72 bind_dir "${temp_dir}/cache" "${mount_dir}/var/cache"
73 bind_dir "${temp_dir}/tmp" "${mount_dir}/tmp"
74 bind_dir "${temp_dir}/tmp" "${mount_dir}/var/tmp"
75 bind_dir "${temp_dir}/apt" "${mount_dir}/var/lib/apt"
76
Federico Ressi71bda862018-05-28 11:38:56 +020077 # Temporarly replace /etc/resolv.conf symlink to use the same DNS as this
78 # host
79 local resolv_file=${mount_dir}/etc/resolv.conf
80 sudo mv -f "${resolv_file}" "${resolv_file}.orig"
81 sudo cp /etc/resolv.conf "${resolv_file}"
82 add_cleanup sudo mv -f "${resolv_file}.orig" "${resolv_file}"
Federico Ressi08c74e92018-06-12 14:19:21 +020083
84 # Makesure /etc/fstab exists and it is readable because it is required by
85 # /sbin/dhclient-script
86 sudo touch /etc/fstab
87 sudo chmod 644 /etc/fstab
88
89 # Copy this script to mount dir
90 local script_name=$(basename "$0")
91 local script_file=${mount_dir}/${script_name}
92 sudo cp "$0" "${script_file}"
93 sudo chmod 500 "${script_file}"
94 add_cleanup sudo rm -f "'${script_file}'"
95
96 # Execute customize_image inside chroot environment
97 local command_line=( ${CHROOT_COMMAND:-customize_image} )
98 local entry_point=${command_line[0]}
99 unset command_line[0]
100 sudo -E "ENTRY_POINT=${entry_point}" \
101 chroot "${mount_dir}" "/${script_name}" "${command_line[@]:-}"
102}
103
104# Mounts guest image to $1 directory writing pid to $1 pid file
105# Then registers umount of such directory for final cleanup
106function mount_image {
107 local mount_dir=$1
108 local pid_file=$2
109
110 # export libguest settings
111 export LIBGUESTFS_BACKEND=${LIBGUESTFS_BACKEND:-direct}
112 export LIBGUESTFS_BACKEND_SETTINGS=${LIBGUESTFS_BACKEND_SETTINGS:-force_tcg}
113
114 # Mount guest image
115 sudo -E guestmount -i \
116 --add "${image_file}" \
117 --pid-file "${pid_file}" \
118 "${mount_dir}"
119
120 add_cleanup \
121 'ENTRY_POINT=umount_image' \
122 "'$0'" "'${mount_dir}'" "'${pid_file}'"
123}
124
125# Unmounts guest image directory
126function umount_image {
127 local mount_dir=$1
128 local pid_file=$2
129 local timeout=10
130
131 # Take PID just before unmounting
132 local pid=$(cat ${pid_file} || true)
133 sudo -E guestunmount "${mount_dir}"
134
135 if [ "${pid:-}" != "" ]; then
136 # Make sure guestmount process is not running before using image
137 # file again
138 local count=${timeout}
139 while sudo kill -0 "${pid}" 2> /dev/null && (( count-- > 0 )); do
140 sleep 1
141 done
142 if [ ${count} == 0 ]; then
143 # It is not safe to use image file at this point
144 echo "Wait for guestmount to exit failed after ${timeout} seconds"
145 fi
146 fi
147}
148
149# Creates a temporary file or directory and register removal for final cleanup
150function make_temp {
151 local temporary=$(mktemp "$@")
152 add_cleanup sudo rm -fR "'${temporary}'"
153 echo "${temporary}"
154}
155
156# Bind directory $1 to directory $2 and register umount for final cleanup
157function bind_dir {
158 local source_dir=$1
159 local target_dir=$2
160 sudo mount --bind "${source_dir}" "${target_dir}"
161 add_cleanup sudo umount "'${target_dir}'"
162}
163
164# Registers a command line to be executed for final cleanup
165function add_cleanup {
166 CLEANUP_FILE=${CLEANUP_FILE:-$(mktemp)}
167
168 echo -e "$*" >> ${CLEANUP_FILE}
169}
170
171# Execute command lines for final cleanup in reversed order
172function cleanup {
173 error=$?
174
175 local cleanup_file=${CLEANUP_FILE:-}
176 if [ -r "${cleanup_file}" ]; then
177 tac "${cleanup_file}" | bash +e -x
178 CLEANUP_FILE=
179 rm -fR "${cleanup_file}"
180 fi
181
182 exit ${error}
183}
184
185main "$@"