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