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