]>
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 | ||
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 | ||
5bbf7721 JS |
34 | # Remove partition from the kernel table. |
35 | partx -d ${outlo}p1 | |
36 | ||
37 | # Remove loopback device | |
38 | losetup -d ${outlo} | |
39 | ||
91a03596 JS |
40 | # Drop working dir |
41 | if [ -d "${WORKING_DIR}" ]; then | |
42 | rm -dfR "${WORKING_DIR}" | |
5bbf7721 JS |
43 | fi |
44 | } | |
45 | ||
46 | generate_image_filename() { | |
47 | local distro=${1} | |
48 | local version=${2} | |
e04a0de0 | 49 | local arch=${3} |
5bbf7721 | 50 | |
e04a0de0 | 51 | echo "${distro}-${version}-${arch}" |
5bbf7721 JS |
52 | } |
53 | ||
54 | check_for_pakfire() { | |
55 | local return_value=0 | |
56 | ||
57 | # TODO | |
58 | # Check that pakfire-server binary is available | |
59 | # Check that pakfire binary is available | |
60 | ||
61 | # Check that repo files are installed. (pakfire need to know which repos exist) | |
62 | local repo_dir="/etc/pakfire/repos" | |
63 | ||
64 | if [ ! -d "${repo_dir}" ]; then | |
65 | log ERROR "Could not find respository directory ${repo_dir}" | |
66 | return_value=1 | |
67 | fi | |
68 | ||
69 | return ${return_value} | |
70 | } | |
71 | ||
72 | compress_image() { | |
73 | local compression=${1} | |
74 | local image_file=${2} | |
75 | local level=${3} | |
76 | ||
77 | log debug "Compressing ${image_file} with ${compression}" | |
78 | ||
79 | case "${compression}" in | |
80 | "xz") | |
81 | # Check that the file does not exist yet | |
82 | if [ -f "${image_file}.xz" ]; then | |
83 | log ERROR "Failed to compress the image. The file already exists" | |
84 | return ${EXIT_ERROR} | |
85 | fi | |
86 | xz "-${level}" "${image_file}" | |
87 | ;; | |
88 | "zip") | |
89 | # Check that the file does not exist yet | |
90 | if [ -f "${image_file}.zip" ]; then | |
91 | log ERROR "Failed to compress the image. The file already exists" | |
92 | return ${EXIT_ERROR} | |
93 | ||
94 | fi | |
95 | zip "-${level}" "${image_file}.zip" "${image_file}" | |
96 | # Remove the file which we compressed+ | |
97 | rm -f "${image_file}" | |
98 | ;; | |
99 | ||
100 | esac | |
101 | } | |
102 | ||
103 | reset_root_password() { | |
104 | local root_dir=${1} | |
105 | # Backup passwd file | |
106 | cp -avf ${root_dir}/etc/passwd ${root_dir}/etc/passwd.orig | |
107 | ||
108 | # Drop root password. | |
109 | sed -e "s/^\(root:\)[^:]*:/\1:/" ${root_dir}/etc/passwd.orig > ${root_dir}/etc/passwd | |
110 | ||
111 | # Remove passwd backup file. | |
112 | rm -rvf ${root_dir}/etc/passwd.orig | |
113 | } | |
114 | ||
115 | clone_git_repos() { | |
116 | # Dir where the repos should be located | |
117 | local dir=${1} | |
118 | shift | |
119 | local repos="$@" | |
120 | ||
121 | local repo | |
122 | ||
123 | mkdir -pv ${dir} | |
124 | ||
125 | ( | |
126 | cd ${dir} | |
127 | # Clone git repositories. | |
128 | for repo in ${repos}; do | |
129 | git clone ${repo} | |
130 | done | |
131 | ) | |
132 | } | |
133 | ||
134 | # This function is used to publish the produced images | |
135 | ||
136 | publish() { | |
137 | local path=${1} | |
138 | # The image we created usually a img. file | |
e04a0de0 | 139 | local image_base_file=${2} |
5bbf7721 | 140 | |
e04a0de0 | 141 | local image_name_final="$(generate_image_filename "${DISTRO}" "${VERSION}" "${ARCH}")" |
5bbf7721 | 142 | |
e04a0de0 | 143 | local image_type |
5bbf7721 JS |
144 | local compression_type |
145 | ||
146 | # Do these steps for every image format we like to publish | |
e04a0de0 | 147 | for image_type in ${IMAGE_TYPES_PUBLISH}; do |
5bbf7721 | 148 | # Get compressioon type |
e04a0de0 | 149 | compression_type="$(get_compression_type ${image_type})" |
5bbf7721 | 150 | # Convert images to the type specified in IMAGE_TYPES_PUBLISH |
e04a0de0 | 151 | convert_image "${image_type}" "${image_base_file}" "${image_name_final}" |
5bbf7721 JS |
152 | |
153 | # compress image. | |
e04a0de0 | 154 | compress_image "${compression_type}" "${image_name_final}.${image_type}" "1" |
5bbf7721 JS |
155 | |
156 | # Move images to this path | |
e04a0de0 | 157 | mv -f "${image_name_final}.${image_type}.${compression_type}" ${path} |
5bbf7721 JS |
158 | |
159 | # point the latest links to these images | |
e04a0de0 JS |
160 | ln -s -f "${path}/${image_name_final}.${image_type}.${compression_type}" \ |
161 | "${path}/$(generate_image_filename "${DISTRO}" "latest" "${ARCH}").${image_type}.${compression_type}" | |
5bbf7721 JS |
162 | |
163 | done | |
164 | ||
165 | } | |
166 | ||
167 | convert_image() { | |
168 | local type=${1} | |
169 | local from=${2} | |
170 | local to=${3} | |
171 | ||
172 | if [[ ${type} = "img" ]]; then | |
e04a0de0 JS |
173 | # We do not need to convert the image here but we need to rename |
174 | mv -f ${from} ${to}.${type} | |
5bbf7721 JS |
175 | fi |
176 | ||
177 | if [[ ${type} = "qcow2" ]]; then | |
178 | local cmd="qemu-img convert -c -O ${type} ${from} ${to}.${type}" | |
179 | else | |
180 | local cmd="qemu-img convert -O ${type} ${from} ${to}.${type}" | |
181 | fi | |
182 | ||
183 | log debug "${cmd}" | |
184 | ||
185 | # Execute the command to convert the image | |
186 | ${cmd} | |
187 | } | |
188 | ||
189 | get_compression_type() { | |
190 | local image_type=${1} | |
191 | ||
192 | case "${image_type}" in | |
193 | "qcow2" | "img") | |
194 | # These types can be used only under Unix so we use xz as compression | |
195 | echo "xz" | |
196 | ;; | |
197 | "vmdk" | "vdi") | |
198 | # These types can be also under Windows so we use zip as compression | |
199 | echo "zip" | |
200 | ;; | |
201 | esac | |
202 | ||
203 | } | |
204 | ||
c38c098c JS |
205 | check_for_free_space() { |
206 | local space=${1} | |
207 | local path=${2} | |
208 | ||
209 | # Space needs to passed in MB | |
210 | # df returns bytes so we multiple by 1024² | |
211 | space=$(( ${space} * 1024 * 1024 )) | |
212 | ||
213 | if [[ $(df --output=avail ${path} | tail -n 1) -lt ${space} ]]; then | |
214 | log error "Not enough free space available under ${path}" | |
215 | log error "Free space is $(df -h -B MB --output=avail ${path} | tail -n 1) but we need at least $(( ${space} / 1024 / 1024 ))MB" | |
216 | return ${EXIT_ERROR} | |
217 | fi | |
218 | } | |
219 | ||
5bbf7721 JS |
220 | # |
221 | # General settings | |
222 | # | |
223 | ||
224 | ARCH="x86_64" | |
225 | DISTRO="ipfire3" | |
226 | VERSION="$(date +"%Y%m%d")" | |
e04a0de0 JS |
227 | WORKING_DIR=$(mktemp -d /tmp/ipfire3_image.XXXXXXXX) |
228 | ||
229 | log DEBUG "Working dir is ${WORKING_DIR}" | |
5bbf7721 JS |
230 | |
231 | # | |
232 | # Image | |
233 | # | |
234 | ||
235 | IMAGE_TYPE="img" | |
e04a0de0 | 236 | IMAGE_BASE_FILE="$(mktemp -u ${WORKING_DIR}/image_base_file.XXXXXXX.img)" |
535dee06 | 237 | IMAGE_SIZE="8100" |
e04a0de0 | 238 | IMAGE_MOUNT_DIR="$(mktemp -d ${WORKING_DIR}/image.XXXXXXX)" |
5bbf7721 JS |
239 | |
240 | IMAGE_TYPES_PUBLISH="qcow2 vmdk vdi img" | |
241 | ||
242 | # The used filesystem. | |
243 | FILESYSTEM="xfs" | |
244 | ||
245 | # Additional packages which should be installed. | |
246 | PACKAGES="xfsprogs kernel openssh-server wget htop tmux" | |
247 | ||
248 | # Use git network stack. ( When using the git network stack, | |
249 | # development tools and git automatically will be installed.) | |
250 | USE_GIT_NETWORK_STACK="True" | |
251 | ||
252 | # List of packages which are required to build the network stack. | |
253 | NETWORK_BUILD_DEPS="autoconf automake docbook-xsl libnl3-devel libxslt systemd-devel" | |
254 | ||
255 | # Install development tools. | |
256 | INSTALL_DEV_PACKAGES="False" | |
257 | ||
258 | # List of development tools which should be installed. | |
259 | DEVELOPMENT_PACKAGES="make gcc libtool git" | |
260 | ||
261 | ||
262 | # Git repositories which also should be checked, out. | |
263 | GIT_REPOS="" | |
264 | ||
265 | # | |
266 | # Stuff for the local repo | |
267 | # | |
268 | ||
269 | # Use local repository. | |
270 | USE_LOCAL_REPO="True" | |
271 | ||
272 | # Source path for the local repo packages. | |
273 | LOCAL_REPO_SOURCE_PATH="/var/lib/pakfire/local/" | |
274 | ||
275 | # Path were the local repo is created | |
e04a0de0 | 276 | LOCAL_REPO_DIR="$(mktemp -d ${WORKING_DIR}/ipfire-repo.XXXXXXX)" |
5bbf7721 JS |
277 | |
278 | # Config file for the local repo | |
279 | LOCAL_REPO_FILE="/etc/pakfire/repos/local.repo" | |
280 | ||
281 | ||
282 | # Check that pakfire is working | |
283 | check_for_pakfire | |
284 | ||
c38c098c JS |
285 | if ! check_for_free_space 10000 "${WORKING_DIR}"; then |
286 | exit ${EXIT_ERROR} | |
287 | fi | |
5bbf7721 JS |
288 | |
289 | # Check that the image does not exist yet | |
e04a0de0 | 290 | if [ -f ${IMAGE_BASE_FILE} ]; then |
5bbf7721 JS |
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 | ||
e04a0de0 JS |
302 | # cd into working directory |
303 | cd ${WORKING_DIR} || exit ${EXIT_ERROR} | |
304 | ||
5bbf7721 JS |
305 | # |
306 | ## Create the disk image. | |
307 | # | |
e04a0de0 | 308 | dd if=/dev/zero of=${IMAGE_BASE_FILE} seek=${IMAGE_SIZE}MB count=1k bs=1 |
5bbf7721 JS |
309 | |
310 | # Setup the loopback device. | |
e04a0de0 | 311 | outlo=`losetup -f --show ${IMAGE_BASE_FILE}` |
5bbf7721 JS |
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" | |
91a03596 | 380 | cleanup |
5bbf7721 JS |
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 | ||
e04a0de0 | 491 | publish "/home/jschlag/public/ipfire3-images" "${IMAGE_BASE_FILE}" |
5bbf7721 | 492 | |
e04a0de0 | 493 | #compress_image "xz" "${IMAGE_BASE_FILE}" |