]> git.ipfire.org Git - people/jschlag/ipfire-3.x-image.git/blob - generate_image.sh
Refactor to script to work in /tmp
[people/jschlag/ipfire-3.x-image.git] / generate_image.sh
1 #!/bin/bash
2 ###############################################################################
3 # IPFire.org - An Open Source Firewall Solution #
4 # Copyright (C) - IPFire Development Team <info@ipfire.org> #
5 ###############################################################################
6
7 # Constants
8
9 # Proper error codes
10 EXIT_OK=0
11 EXIT_ERROR=1
12 EXIT_CONF_ERROR=2
13 EXIT_NOT_SUPPORTED=3
14 EXIT_NOT_HANDLED=4
15 EXIT_COMMAND_NOT_FOUND=127
16 EXIT_ERROR_ASSERT=128
17
18 EXIT_TRUE=0
19 EXIT_FALSE=1
20 EXIT_UNKNOWN=2
21
22 # Functions
23
24 log() {
25 local level=${1}
26 local message=${2}
27 echo "[${level}] ${message}"
28 }
29
30 cleanup() {
31 # Unmount image
32 umount ${IMAGE_MOUNT_DIR}
33
34 # Drop image mount dir
35 if [ -d ${IMAGE_MOUNT_DIR} ]; then
36 rm -df ${IMAGE_MOUNT_DIR}
37 fi
38
39 # Remove partition from the kernel table.
40 partx -d ${outlo}p1
41
42 # Remove loopback device
43 losetup -d ${outlo}
44
45 # Drop local repository.
46 if [ -d "${LOCAL_REPO_DIR}" ]; then
47 rm -dfR "${LOCAL_REPO_DIR}"
48 fi
49
50 # Drop local repository file.
51 if [ -f "${LOCAL_REPO_FILE}" ]; then
52 rm -dfR "${LOCAL_REPO_FILE}"
53 fi
54 }
55
56 cleanup_after_failure () {
57 cleanup
58
59 if [ -f ${IMAGE_BASE_FILE} ]; then
60 rm -f ${IMAGE_BASE_FILE}
61 fi
62 }
63
64 generate_image_filename() {
65 local distro=${1}
66 local version=${2}
67 local arch=${3}
68
69 echo "${distro}-${version}-${arch}"
70 }
71
72 check_for_pakfire() {
73 local return_value=0
74
75 # TODO
76 # Check that pakfire-server binary is available
77 # Check that pakfire binary is available
78
79 # Check that repo files are installed. (pakfire need to know which repos exist)
80 local repo_dir="/etc/pakfire/repos"
81
82 if [ ! -d "${repo_dir}" ]; then
83 log ERROR "Could not find respository directory ${repo_dir}"
84 return_value=1
85 fi
86
87 return ${return_value}
88 }
89
90 compress_image() {
91 local compression=${1}
92 local image_file=${2}
93 local level=${3}
94
95 log debug "Compressing ${image_file} with ${compression}"
96
97 case "${compression}" in
98 "xz")
99 # Check that the file does not exist yet
100 if [ -f "${image_file}.xz" ]; then
101 log ERROR "Failed to compress the image. The file already exists"
102 return ${EXIT_ERROR}
103 fi
104 xz "-${level}" "${image_file}"
105 ;;
106 "zip")
107 # Check that the file does not exist yet
108 if [ -f "${image_file}.zip" ]; then
109 log ERROR "Failed to compress the image. The file already exists"
110 return ${EXIT_ERROR}
111
112 fi
113 zip "-${level}" "${image_file}.zip" "${image_file}"
114 # Remove the file which we compressed+
115 rm -f "${image_file}"
116 ;;
117
118 esac
119 }
120
121 reset_root_password() {
122 local root_dir=${1}
123 # Backup passwd file
124 cp -avf ${root_dir}/etc/passwd ${root_dir}/etc/passwd.orig
125
126 # Drop root password.
127 sed -e "s/^\(root:\)[^:]*:/\1:/" ${root_dir}/etc/passwd.orig > ${root_dir}/etc/passwd
128
129 # Remove passwd backup file.
130 rm -rvf ${root_dir}/etc/passwd.orig
131 }
132
133 clone_git_repos() {
134 # Dir where the repos should be located
135 local dir=${1}
136 shift
137 local repos="$@"
138
139 local repo
140
141 mkdir -pv ${dir}
142
143 (
144 cd ${dir}
145 # Clone git repositories.
146 for repo in ${repos}; do
147 git clone ${repo}
148 done
149 )
150 }
151
152 # This function is used to publish the produced images
153
154 publish() {
155 local path=${1}
156 # The image we created usually a img. file
157 local image_base_file=${2}
158
159 local image_name_final="$(generate_image_filename "${DISTRO}" "${VERSION}" "${ARCH}")"
160
161 local image_type
162 local compression_type
163
164 # Do these steps for every image format we like to publish
165 for image_type in ${IMAGE_TYPES_PUBLISH}; do
166 # Get compressioon type
167 compression_type="$(get_compression_type ${image_type})"
168 # Convert images to the type specified in IMAGE_TYPES_PUBLISH
169 convert_image "${image_type}" "${image_base_file}" "${image_name_final}"
170
171 # compress image.
172 compress_image "${compression_type}" "${image_name_final}.${image_type}" "1"
173
174 # Move images to this path
175 mv -f "${image_name_final}.${image_type}.${compression_type}" ${path}
176
177 # point the latest links to these images
178 ln -s -f "${path}/${image_name_final}.${image_type}.${compression_type}" \
179 "${path}/$(generate_image_filename "${DISTRO}" "latest" "${ARCH}").${image_type}.${compression_type}"
180
181 done
182
183 }
184
185 convert_image() {
186 local type=${1}
187 local from=${2}
188 local to=${3}
189
190 if [[ ${type} = "img" ]]; then
191 # We do not need to convert the image here but we need to rename
192 mv -f ${from} ${to}.${type}
193 fi
194
195 if [[ ${type} = "qcow2" ]]; then
196 local cmd="qemu-img convert -c -O ${type} ${from} ${to}.${type}"
197 else
198 local cmd="qemu-img convert -O ${type} ${from} ${to}.${type}"
199 fi
200
201 log debug "${cmd}"
202
203 # Execute the command to convert the image
204 ${cmd}
205 }
206
207 get_compression_type() {
208 local image_type=${1}
209
210 case "${image_type}" in
211 "qcow2" | "img")
212 # These types can be used only under Unix so we use xz as compression
213 echo "xz"
214 ;;
215 "vmdk" | "vdi")
216 # These types can be also under Windows so we use zip as compression
217 echo "zip"
218 ;;
219 esac
220
221 }
222
223 #
224 # General settings
225 #
226
227 ARCH="x86_64"
228 DISTRO="ipfire3"
229 VERSION="$(date +"%Y%m%d")"
230 WORKING_DIR=$(mktemp -d /tmp/ipfire3_image.XXXXXXXX)
231
232 log DEBUG "Working dir is ${WORKING_DIR}"
233
234 #
235 # Image
236 #
237
238 IMAGE_TYPE="img"
239 IMAGE_BASE_FILE="$(mktemp -u ${WORKING_DIR}/image_base_file.XXXXXXX.img)"
240 IMAGE_SIZE="8196"
241 IMAGE_MOUNT_DIR="$(mktemp -d ${WORKING_DIR}/image.XXXXXXX)"
242
243 IMAGE_TYPES_PUBLISH="qcow2 vmdk vdi img"
244
245 # The used filesystem.
246 FILESYSTEM="xfs"
247
248 # Additional packages which should be installed.
249 PACKAGES="xfsprogs kernel openssh-server wget htop tmux"
250
251 # Use git network stack. ( When using the git network stack,
252 # development tools and git automatically will be installed.)
253 USE_GIT_NETWORK_STACK="True"
254
255 # List of packages which are required to build the network stack.
256 NETWORK_BUILD_DEPS="autoconf automake docbook-xsl libnl3-devel libxslt systemd-devel"
257
258 # Install development tools.
259 INSTALL_DEV_PACKAGES="False"
260
261 # List of development tools which should be installed.
262 DEVELOPMENT_PACKAGES="make gcc libtool git"
263
264
265 # Git repositories which also should be checked, out.
266 GIT_REPOS=""
267
268 #
269 # Stuff for the local repo
270 #
271
272 # Use local repository.
273 USE_LOCAL_REPO="True"
274
275 # Source path for the local repo packages.
276 LOCAL_REPO_SOURCE_PATH="/var/lib/pakfire/local/"
277
278 # Path were the local repo is created
279 LOCAL_REPO_DIR="$(mktemp -d ${WORKING_DIR}/ipfire-repo.XXXXXXX)"
280
281 # Config file for the local repo
282 LOCAL_REPO_FILE="/etc/pakfire/repos/local.repo"
283
284
285 # Check that pakfire is working
286 check_for_pakfire
287
288
289 # Check that the image does not exist yet
290 if [ -f ${IMAGE_BASE_FILE} ]; then
291 log ERROR "Image file does already exists"
292 exit 1
293 fi
294
295 # Check that the local repo file does not exists yet.
296 # We do not want to override custom user configurations.
297 if [ -f "${LOCAL_REPO_FILE}" ]; then
298 log ERROR "Config file ${LOCAL_REPO_FILE} for the local repo does already exists"
299 exit 1
300 fi
301
302 # cd into working directory
303 cd ${WORKING_DIR} || exit ${EXIT_ERROR}
304
305 #
306 ## Create the disk image.
307 #
308 dd if=/dev/zero of=${IMAGE_BASE_FILE} seek=${IMAGE_SIZE}MB count=1k bs=1
309
310 # Setup the loopback device.
311 outlo=`losetup -f --show ${IMAGE_BASE_FILE}`
312
313 log INFO "Create partions and filesystem"
314
315 # Create and msdos compatible table on the image.
316 parted ${outlo} mklabel msdos
317
318 # Add a new partition to the image.
319 parted ${outlo} mkpart primary ${FILESYSTEM} 2048k 100% -a minimal
320
321 # Make the primary partition bootable.
322 parted ${outlo} set 1 boot on
323
324 # Notify the kernel about the new partition.
325 partx -a ${outlo}
326
327 #
328 ## Create the filesystem.
329 #
330 mkfs.${FILESYSTEM} ${outlo}p1
331
332 #
333 ## Mount the filesystem.
334 #
335
336 log INFO "Mount partion in ${IMAGE_MOUNT_DIR}"
337
338 # Create a temporary mountpoint.
339 mkdir -p ${IMAGE_MOUNT_DIR}
340
341 # Afterwards mount the image.
342 mount -t ${FILESYSTEM} ${outlo}p1 ${IMAGE_MOUNT_DIR}
343
344 #
345 ## Install IPFire 3.x.
346 #
347
348 # Add grub on x86_64 to the package list.
349 if [ "${ARCH}" == "x86_64" ] || [ "${ARCH}" == "i686" ]; then
350 PACKAGES="${PACKAGES} grub"
351
352 # Store, that grub is present.
353 HAVE_GRUB="True"
354 fi
355
356 # Check if the git network stack should be installed.
357 if [ "${USE_GIT_NETWORK_STACK}" == "True" ]; then
358 GIT_REPOS="${GIT_REPOS} git://git.ipfire.org/network.git"
359
360 # Add build dependencies of network package.
361 PACKAGES="${PACKAGES} ${NETWORK_BUILD_DEPS}"
362 fi
363
364 # Add develoment packes to the package list, if required.
365 if [ "${INSTALL_DEV_PACKAGES}" == "True" ] || [ ! -z "${GIT_REPOS}" ]; then
366 PACKAGES="${PACKAGES} ${DEVELOPMENT_PACKAGES}"
367 fi
368
369 log INFO "Create local respository"
370
371
372 # Check if the local repo should be used.
373 if [ "${USE_LOCAL_REPO}" == "True" ]; then
374 # Create local repository.
375 mkdir -pv "${LOCAL_REPO_DIR}"
376
377 # Master repository.
378 if ! pakfire-server repo create ${LOCAL_REPO_DIR} ${LOCAL_REPO_SOURCE_PATH}; then
379 log ERROR "Failed to create a local respository"
380 cleanup_after_failure
381 exit 1
382 fi
383
384 # Create temporary pakfire repo file.
385 echo "[repo:local]" >> "${LOCAL_REPO_FILE}"
386 echo "description = Local repository." >> "${LOCAL_REPO_FILE}"
387 echo "enabled = 0" >> "${LOCAL_REPO_FILE}"
388 echo "baseurl = ${LOCAL_REPO_DIR}" >> "${LOCAL_REPO_FILE}"
389
390 ENABLE_LOCAL="--enable-repo=local"
391 fi
392
393 # Install IPFire 3.x in the created image.
394 yes | pakfire --root=${IMAGE_MOUNT_DIR} ${ENABLE_LOCAL} install @Base ${PACKAGES}
395
396 #
397 # Enable serial console
398 #
399
400
401 #echo "GRUB_TERMINAL=\"serial console\"" >> "${IMAGE_MOUNT_DIR}/etc/default/grub"
402 #echo "GRUB_SERIAL_COMMAND=\"serial --unit=0 --speed=115200\"" >> "${IMAGE_MOUNT_DIR}/etc/default/grub"
403
404 #Hack to install a /etc/default/grub file
405
406 cp -f grub "${IMAGE_MOUNT_DIR}/etc/default"
407
408 #
409 ## Install grub2 if neccessary
410 #
411 if [ "${HAVE_GRUB}" == "True" ]; then
412 grub2-install --boot-directory=${IMAGE_MOUNT_DIR}/boot/ --modules="${FILESYSTEM} part_msdos" ${outlo}
413 fi
414
415 #
416 ## Generate fstab
417 #
418
419 # Gather the uuid of the partition.
420 FS_UUID=$(blkid -o value -s UUID ${outlo}p1)
421
422 # Write fstab.
423 echo "UUID=${FS_UUID} / ${FILESYSTEM} defaults 0 0" > ${IMAGE_MOUNT_DIR}/etc/fstab
424
425 #
426 ## Remove the password for user root.
427 #
428
429 reset_root_password "${IMAGE_MOUNT_DIR}"
430
431 #
432 ## Setup git repositories.
433 #
434
435 clone_git_repos "${IMAGE_MOUNT_DIR}/build" ${GIT_REPOS}
436
437 #
438 ## Prepare chrooting into the image.
439 #
440
441 # Check if the network stack should be build.
442 if [ "${USE_GIT_NETWORK_STACK}" == "True" ]; then
443 BUILD_NETWORK_CMDS="cd network/ && ./autogen.sh && ./configure && make && make install"
444 fi
445
446 ENABLE_GETTY="/bin/systemctl enable getty@.service"
447
448 # Check if the arch uses grub
449 if [ "${HAVE_GRUB}" == "True" ]; then
450 GENERATE_GRUB_CONF="grub-mkconfig -o /boot/grub2/grub.cfg"
451 fi
452
453 # Use systemd-nspawn to spawn a chroot environment and execute
454 # commands inside it.
455 #
456 # The first command enables the terminal on TTY1.
457 # The second command generates the configuration file for grub2.
458
459
460 systemd-nspawn -D ${IMAGE_MOUNT_DIR} --bind /dev --capability=CAP_SYS_ADMIN,CAP_SYS_RAWIO --bind /proc --bind /sys << END
461 echo "Execute commands inside chroot"
462 ${ENABLE_GETTY}
463 ${GENERATE_GRUB_CONF}
464 cd /git-repos/ && ls
465 ${BUILD_NETWORK_CMDS}
466 echo "All commands executed"
467 END
468
469
470
471 # Insert the UUID because grub-mkconfig often fails to
472 # detect that correctly
473
474 sed -i "${IMAGE_MOUNT_DIR}/boot/grub2/grub.cfg" \
475 -e "s/root=[A-Za-z0-9\/=-]*/root=UUID=${FS_UUID}/g"
476
477 cat "${IMAGE_MOUNT_DIR}/boot/grub2/grub.cfg"
478 #
479 ## Tidy up.
480 #
481
482 # Wait a second.
483 sleep 5
484
485 # Check filesystem for damage.
486 fsck.${FILESYSTEM} ${outlo}p1
487
488 # Cleanup
489 cleanup
490
491 publish "/home/jschlag/public/ipfire3-images" "${IMAGE_BASE_FILE}"
492
493 #compress_image "xz" "${IMAGE_BASE_FILE}"