From 2f6f116d550a49d4239b0aecbaf4562ef922bab9 Mon Sep 17 00:00:00 2001 From: David P Date: Tue, 2 Feb 2021 00:59:26 -0300 Subject: sync with archiso Imported changes: f0ef2f3 (HEAD -> master, tag: v51, origin/master, origin/HEAD) Add changelog file b588c52 build-host.sh: Style and syntax fixes 428bf47 Add build stage for continuous integration 2a24429 Add continuous integration scripts to linting 0723949 Add scripts for continuous integration f9a7b20 configs/releng: move locale-gen from customize_airootfs.sh to a pacman hook a46c740 config/releng: remove pacman hooks specific to ISO build process from airootfs after they run 767c095 Update my email in AUTHORS dd51457 rm un-used set_image() dupe of check_image() in run_archiso.sh 23c5d52 add vorburger to AUTHORS.rst 0f20a11 Support setting more variables in profiledef.sh and rework the way overrides are applied e7306a3 add cloud-init to baseline 744b8f1 make baseline have working ethernet networking, like releng 94dd194 add SSH server to baseline 221a921 use same airootfs_image_tool_options in baseline as in releng 4f4047a configs/releng: move the mirror uncommenting sed command from customize_airootfs.sh to a pacman hook a2c8dd3 archiso_pxe_common: remove resolv.conf before copy b30d1ca mkarchiso: add version information Signed-off-by: David P --- AUTHORS.rst | 5 +- CHANGELOG.rst | 30 +++ Makefile | 4 +- README.rst | 13 + configs/baseline/airootfs/etc/ssh/sshd_config | 116 +++++++++ .../etc/systemd/network/20-ethernet.network | 13 + .../cloud-init.target.wants/cloud-config.service | 1 + .../cloud-init.target.wants/cloud-final.service | 1 + .../cloud-init-local.service | 1 + .../cloud-init.target.wants/cloud-init.service | 1 + .../system/multi-user.target.wants/sshd.service | 1 + .../systemd-networkd.service | 1 + .../systemd-resolved.service | 1 + .../systemd-networkd-wait-online.service | 1 + .../sockets.target.wants/systemd-networkd.socket | 1 + .../wait-for-only-one-interface.conf | 5 + configs/baseline/packages.x86_64 | 2 + configs/baseline/profiledef.sh | 1 + .../airootfs/etc/pacman.d/hooks/40-locale-gen.hook | 13 + .../etc/pacman.d/hooks/uncomment-mirrors.hook | 12 + .../zzzz99-remove-custom-hooks-from-airootfs.hook | 18 ++ .../airootfs/root/customize_airootfs.sh | 19 -- .../airootfs/etc/pacman.d/hooks/40-locale-gen.hook | 13 + .../etc/pacman.d/hooks/uncomment-mirrors.hook | 12 + .../zzzz99-remove-custom-hooks-from-airootfs.hook | 18 ++ .../airootfs/root/customize_airootfs.sh | 12 - .../airootfs/etc/pacman.d/hooks/40-locale-gen.hook | 13 + .../etc/pacman.d/hooks/uncomment-mirrors.hook | 13 + .../zzzz99-remove-custom-hooks-from-airootfs.hook | 18 ++ configs/releng/airootfs/root/customize_airootfs.sh | 12 - parabolaiso/initcpio/hooks/parabolaiso_pxe_common | 1 + parabolaiso/mkparabolaiso | 272 +++++++++++++-------- scripts/run_parabolaiso.sh | 12 - 33 files changed, 490 insertions(+), 166 deletions(-) create mode 100644 CHANGELOG.rst create mode 100644 configs/baseline/airootfs/etc/ssh/sshd_config create mode 100644 configs/baseline/airootfs/etc/systemd/network/20-ethernet.network create mode 120000 configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-config.service create mode 120000 configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-final.service create mode 120000 configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-local.service create mode 120000 configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init.service create mode 120000 configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/sshd.service create mode 120000 configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/systemd-networkd.service create mode 120000 configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/systemd-resolved.service create mode 120000 configs/baseline/airootfs/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service create mode 120000 configs/baseline/airootfs/etc/systemd/system/sockets.target.wants/systemd-networkd.socket create mode 100644 configs/baseline/airootfs/etc/systemd/system/systemd-networkd-wait-online.service.d/wait-for-only-one-interface.conf create mode 100644 configs/lxde-openrc/airootfs/etc/pacman.d/hooks/40-locale-gen.hook create mode 100644 configs/lxde-openrc/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook create mode 100644 configs/lxde-openrc/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook delete mode 100755 configs/lxde-openrc/airootfs/root/customize_airootfs.sh create mode 100644 configs/releng-openrc/airootfs/etc/pacman.d/hooks/40-locale-gen.hook create mode 100644 configs/releng-openrc/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook create mode 100644 configs/releng-openrc/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook delete mode 100755 configs/releng-openrc/airootfs/root/customize_airootfs.sh create mode 100644 configs/releng/airootfs/etc/pacman.d/hooks/40-locale-gen.hook create mode 100644 configs/releng/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook create mode 100644 configs/releng/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook delete mode 100755 configs/releng/airootfs/root/customize_airootfs.sh diff --git a/AUTHORS.rst b/AUTHORS.rst index d76dfa5..9dc5520 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -23,11 +23,12 @@ Archiso Authors * Loui Chang * Lukas Fleischer * Martin Damian Fernandez +* Michael Vorburger * Pierre Schmitz * Sean Enck * Simo Leone * Steffen Bönigk -* Sven-Hendrik Haase +* Sven-Hendrik Haase * Thomas Bächler * Yu Li-Yu * nl6720 @@ -45,4 +46,4 @@ Parabolaiso Authors * Jorge Lopez * bill-auger * chandan -* coadde [Márcio Alexandre Silva Delgado] +* Márcio Alexandre Silva Delgado diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..5b1e5b5 --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,30 @@ +######### +Changelog +######### + +[51] - 2021-02-01 +================= + +Added +----- + +- VNC support for `run_parabolaiso` +- SSH enabled by default in baseline and releng profiles +- Add cloud-init support to baseline and releng profiles +- Add simple port forwarding to `run_parabolaiso` to allow testing of SSH +- Add support for loading cloud-init user data images to `run_parabolaiso` +- Add version information to images generated with `mkparabolaiso` +- Use pacman hooks for things previously done in `customize_airootfs.sh` (e.g. generating locale, uncommenting mirror + list) +- Add network setup for the baseline profile + +Changed +------- + +- Change upstream URL in vendored profiles to pararbola.nu +- Reduce the amount of sed calls in mkparabolaiso +- Fix typos in `mkparabolaiso` +- mkinitcpio-parabolaiso: Remove resolv.conf before copy to circumvent its use +- Remove `customize_airootfs.sh` from the vendored profiles +- Support overriding more variables in `profiledef.sh` and refactor their use in `mkparabolaiso` +- Cleanup unused code in `run_parabolaiso` diff --git a/Makefile b/Makefile index cbd85dd..b3f9caa 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,7 @@ lint: $(wildcard configs/*/profiledef.sh) \ configs/releng/airootfs/root/.automated_script.sh \ configs/releng/airootfs/usr/local/bin/choose-mirror \ + configs/releng/airootfs/usr/local/bin/livecd-sound \ configs/releng-openrc/airootfs/root/.automated_script.sh \ configs/releng-openrc/airootfs/etc/local.d/pacman-init.start \ configs/releng-openrc/airootfs/etc/local.d/choose-mirror.start \ @@ -39,8 +40,7 @@ lint: configs/lxde-openrc/airootfs/etc/local.d/choose-mirror.start \ configs/lxde-openrc/airootfs/etc/local.d/etc-pacman.d-gnupg.start \ configs/lxde-openrc/airootfs/etc/NetworkManager/dispatcher.d/reflector \ - configs/lxde-openrc/airootfs/usr/local/bin/choose-mirror \ - configs/releng/airootfs/usr/local/bin/livecd-sound + configs/lxde-openrc/airootfs/usr/local/bin/choose-mirror shellcheck -s dash $(HOOKS_FILES) $(SCRIPT_FILES) install: install-program install-examples install-doc diff --git a/README.rst b/README.rst index 30f73ef..8380fa3 100644 --- a/README.rst +++ b/README.rst @@ -119,6 +119,19 @@ Optionally install parabolaiso's mkinitcpio hooks: make install-initcpio +Optional Features +================= + +The iso image contains a grub environment block holding the iso version. This allows to boot the iso image from grub +with a version specific cow directory to mitigate overlay clashes. + + .. code:: grub + loopback loop parabola.iso + load_env -f (loop)/parabola/grubenv + linux (loop)/parabola/boot/x86_64/vmlinuz-linux-libre ... \ + cow_directory=parabola/${VERSION} ... + initrd (loop)/parabola/boot/x86_64/initramfs-linux-libre-lts.img + Contribute ========== diff --git a/configs/baseline/airootfs/etc/ssh/sshd_config b/configs/baseline/airootfs/etc/ssh/sshd_config new file mode 100644 index 0000000..8ef1758 --- /dev/null +++ b/configs/baseline/airootfs/etc/ssh/sshd_config @@ -0,0 +1,116 @@ +# $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $ + +# This is the sshd server system-wide configuration file. See +# sshd_config(5) for more information. + +# This sshd was compiled with PATH=/usr/local/sbin:/usr/local/bin:/usr/bin + +# The strategy used for options in the default sshd_config shipped with +# OpenSSH is to specify options with their default value where +# possible, but leave them commented. Uncommented options override the +# default value. + +#Port 22 +#AddressFamily any +#ListenAddress 0.0.0.0 +#ListenAddress :: + +#HostKey /etc/ssh/ssh_host_rsa_key +#HostKey /etc/ssh/ssh_host_ecdsa_key +#HostKey /etc/ssh/ssh_host_ed25519_key + +# Ciphers and keying +#RekeyLimit default none + +# Logging +#SyslogFacility AUTH +#LogLevel INFO + +# Authentication: + +#LoginGraceTime 2m +PermitRootLogin yes +#StrictModes yes +#MaxAuthTries 6 +#MaxSessions 10 + +#PubkeyAuthentication yes + +# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 +# but this is overridden so installations will only check .ssh/authorized_keys +AuthorizedKeysFile .ssh/authorized_keys + +#AuthorizedPrincipalsFile none + +#AuthorizedKeysCommand none +#AuthorizedKeysCommandUser nobody + +# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts +#HostbasedAuthentication no +# Change to yes if you don't trust ~/.ssh/known_hosts for +# HostbasedAuthentication +#IgnoreUserKnownHosts no +# Don't read the user's ~/.rhosts and ~/.shosts files +#IgnoreRhosts yes + +# To disable tunneled clear text passwords, change to no here! +#PasswordAuthentication yes +#PermitEmptyPasswords no + +# Change to no to disable s/key passwords +ChallengeResponseAuthentication no + +# Kerberos options +#KerberosAuthentication no +#KerberosOrLocalPasswd yes +#KerberosTicketCleanup yes +#KerberosGetAFSToken no + +# GSSAPI options +#GSSAPIAuthentication no +#GSSAPICleanupCredentials yes + +# Set this to 'yes' to enable PAM authentication, account processing, +# and session processing. If this is enabled, PAM authentication will +# be allowed through the ChallengeResponseAuthentication and +# PasswordAuthentication. Depending on your PAM configuration, +# PAM authentication via ChallengeResponseAuthentication may bypass +# the setting of "PermitRootLogin without-password". +# If you just want the PAM account and session checks to run without +# PAM authentication, then enable this but set PasswordAuthentication +# and ChallengeResponseAuthentication to 'no'. +UsePAM yes + +#AllowAgentForwarding yes +#AllowTcpForwarding yes +#GatewayPorts no +#X11Forwarding no +#X11DisplayOffset 10 +#X11UseLocalhost yes +#PermitTTY yes +PrintMotd no # pam does that +#PrintLastLog yes +#TCPKeepAlive yes +#PermitUserEnvironment no +#Compression delayed +#ClientAliveInterval 0 +#ClientAliveCountMax 3 +#UseDNS no +#PidFile /run/sshd.pid +#MaxStartups 10:30:100 +#PermitTunnel no +#ChrootDirectory none +#VersionAddendum none + +# no default banner path +#Banner none + +# override default of no subsystems +Subsystem sftp /usr/lib/ssh/sftp-server + +# Example of overriding settings on a per-user basis +#Match User anoncvs +# X11Forwarding no +# AllowTcpForwarding no +# PermitTTY no +# ForceCommand cvs server diff --git a/configs/baseline/airootfs/etc/systemd/network/20-ethernet.network b/configs/baseline/airootfs/etc/systemd/network/20-ethernet.network new file mode 100644 index 0000000..efa309c --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/network/20-ethernet.network @@ -0,0 +1,13 @@ +# +# SPDX-License-Identifier: GPL-3.0-or-later + +[Match] +Name=en* +Name=eth* + +[Network] +DHCP=yes +IPv6PrivacyExtensions=yes + +[DHCP] +RouteMetric=512 diff --git a/configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-config.service b/configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-config.service new file mode 120000 index 0000000..ebc50f0 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-config.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/cloud-config.service \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-final.service b/configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-final.service new file mode 120000 index 0000000..80fa3c8 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-final.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/cloud-final.service \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-local.service b/configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-local.service new file mode 120000 index 0000000..dd8e9f1 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-local.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/cloud-init-local.service \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init.service b/configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init.service new file mode 120000 index 0000000..24c7a26 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/cloud-init.service \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/sshd.service b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/sshd.service new file mode 120000 index 0000000..d21ebd9 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/sshd.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/sshd.service \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/systemd-networkd.service b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/systemd-networkd.service new file mode 120000 index 0000000..4c158e6 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/systemd-networkd.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-networkd.service \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/systemd-resolved.service b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/systemd-resolved.service new file mode 120000 index 0000000..4f6ae34 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/systemd-resolved.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-resolved.service \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service b/configs/baseline/airootfs/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service new file mode 120000 index 0000000..7d6ad92 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-networkd-wait-online.service \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/sockets.target.wants/systemd-networkd.socket b/configs/baseline/airootfs/etc/systemd/system/sockets.target.wants/systemd-networkd.socket new file mode 120000 index 0000000..51942c8 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/sockets.target.wants/systemd-networkd.socket @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-networkd.socket \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/systemd-networkd-wait-online.service.d/wait-for-only-one-interface.conf b/configs/baseline/airootfs/etc/systemd/system/systemd-networkd-wait-online.service.d/wait-for-only-one-interface.conf new file mode 100644 index 0000000..a4d7442 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/systemd-networkd-wait-online.service.d/wait-for-only-one-interface.conf @@ -0,0 +1,5 @@ +# +# SPDX-License-Identifier: GPL-3.0-or-later + +[Service] +ExecStart=/usr/lib/systemd/systemd-networkd-wait-online --any diff --git a/configs/baseline/packages.x86_64 b/configs/baseline/packages.x86_64 index 62a9103..560143f 100644 --- a/configs/baseline/packages.x86_64 +++ b/configs/baseline/packages.x86_64 @@ -1,7 +1,9 @@ # # SPDX-License-Identifier: GPL-3.0-or-later base +cloud-init linux-libre mkinitcpio mkinitcpio-parabolaiso +openssh syslinux diff --git a/configs/baseline/profiledef.sh b/configs/baseline/profiledef.sh index ca5fac9..e51376d 100644 --- a/configs/baseline/profiledef.sh +++ b/configs/baseline/profiledef.sh @@ -10,6 +10,7 @@ install_dir="parabola" bootmodes=('bios.syslinux.mbr' 'bios.syslinux.eltorito' 'uefi-x64.systemd-boot.esp' 'uefi-x64.systemd-boot.eltorito') arch="x86_64" pacman_conf="pacman.conf" +airootfs_image_tool_options=('-comp' 'xz' '-Xbcj' 'x86' '-b' '1M' '-Xdict-size' '1M') file_permissions=( ["/etc/shadow"]="0:0:400" ) diff --git a/configs/lxde-openrc/airootfs/etc/pacman.d/hooks/40-locale-gen.hook b/configs/lxde-openrc/airootfs/etc/pacman.d/hooks/40-locale-gen.hook new file mode 100644 index 0000000..82dd199 --- /dev/null +++ b/configs/lxde-openrc/airootfs/etc/pacman.d/hooks/40-locale-gen.hook @@ -0,0 +1,13 @@ +# remove from airootfs! +[Trigger] +Operation = Install +Type = Package +Target = glibc + +[Action] +Description = Uncommenting en_US.UTF-8 locale and running locale-gen... +When = PostTransaction +Depends = glibc +Depends = sed +Depends = sh +Exec = /bin/sh -c "sed -i 's/#\(en_US\.UTF-8\)/\1/' /etc/locale.gen && locale-gen" diff --git a/configs/lxde-openrc/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook b/configs/lxde-openrc/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook new file mode 100644 index 0000000..ad0b5ba --- /dev/null +++ b/configs/lxde-openrc/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook @@ -0,0 +1,12 @@ +[Trigger] +Operation = Install +Operation = Upgrade +Type = Package +Target = pacman-mirrorlist + +[Action] +Description = Uncommenting all mirrors in /etc/pacman.d/mirrorlist... +When = PostTransaction +Depends = pacman-mirrorlist +Depends = sed +Exec = /usr/bin/sed -i "s/#Server/Server/g" /etc/pacman.d/mirrorlist diff --git a/configs/lxde-openrc/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook b/configs/lxde-openrc/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook new file mode 100644 index 0000000..8dfb943 --- /dev/null +++ b/configs/lxde-openrc/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook @@ -0,0 +1,18 @@ +# remove from airootfs! +# As a workaround for https://bugs.archlinux.org/task/49347 , remove pacman hooks specific to the ISO build process. +# If not, they would be used when pacstrap is run in the live environment. + +[Trigger] +Operation = Install +Operation = Upgrade +Operation = Remove +Type = Package +Target = * + +[Action] +Description = Work around FS#49347 by removing custom pacman hooks that are only required during ISO build... +When = PostTransaction +Depends = sh +Depends = coreutils +Depends = grep +Exec = /bin/sh -c "rm -- $(grep -Frl 'remove from airootfs' /etc/pacman.d/hooks/)" diff --git a/configs/lxde-openrc/airootfs/root/customize_airootfs.sh b/configs/lxde-openrc/airootfs/root/customize_airootfs.sh deleted file mode 100755 index 05001d9..0000000 --- a/configs/lxde-openrc/airootfs/root/customize_airootfs.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -# -# SPDX-License-Identifier: GPL-3.0-or-later - -set -e -u - -# Warning: customize_airootfs.sh is deprecated! Support for it will be removed in a future parabolaiso version. - -sed -i 's/#\(en_US\.UTF-8\)/\1/' /etc/locale.gen -sed -i 's/#\(es_ES\.UTF-8\)/\1/' /etc/locale.gen -sed -i 's/#\(gl_ES\.UTF-8\)/\1/' /etc/locale.gen -sed -i 's/#\(pt_BR\.UTF-8\)/\1/' /etc/locale.gen -sed -i 's/#\(pl_PL\.UTF-8\)/\1/' /etc/locale.gen -sed -i 's/#\(it_IT\.UTF-8\)/\1/' /etc/locale.gen -sed -i 's/#\(fr_FR\.UTF-8\)/\1/' /etc/locale.gen -sed -i 's/#\(eo\)/\1/' /etc/locale.gen -locale-gen - -sed -i "s/#Server/Server/g" /etc/pacman.d/mirrorlist diff --git a/configs/releng-openrc/airootfs/etc/pacman.d/hooks/40-locale-gen.hook b/configs/releng-openrc/airootfs/etc/pacman.d/hooks/40-locale-gen.hook new file mode 100644 index 0000000..82dd199 --- /dev/null +++ b/configs/releng-openrc/airootfs/etc/pacman.d/hooks/40-locale-gen.hook @@ -0,0 +1,13 @@ +# remove from airootfs! +[Trigger] +Operation = Install +Type = Package +Target = glibc + +[Action] +Description = Uncommenting en_US.UTF-8 locale and running locale-gen... +When = PostTransaction +Depends = glibc +Depends = sed +Depends = sh +Exec = /bin/sh -c "sed -i 's/#\(en_US\.UTF-8\)/\1/' /etc/locale.gen && locale-gen" diff --git a/configs/releng-openrc/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook b/configs/releng-openrc/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook new file mode 100644 index 0000000..ad0b5ba --- /dev/null +++ b/configs/releng-openrc/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook @@ -0,0 +1,12 @@ +[Trigger] +Operation = Install +Operation = Upgrade +Type = Package +Target = pacman-mirrorlist + +[Action] +Description = Uncommenting all mirrors in /etc/pacman.d/mirrorlist... +When = PostTransaction +Depends = pacman-mirrorlist +Depends = sed +Exec = /usr/bin/sed -i "s/#Server/Server/g" /etc/pacman.d/mirrorlist diff --git a/configs/releng-openrc/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook b/configs/releng-openrc/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook new file mode 100644 index 0000000..8dfb943 --- /dev/null +++ b/configs/releng-openrc/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook @@ -0,0 +1,18 @@ +# remove from airootfs! +# As a workaround for https://bugs.archlinux.org/task/49347 , remove pacman hooks specific to the ISO build process. +# If not, they would be used when pacstrap is run in the live environment. + +[Trigger] +Operation = Install +Operation = Upgrade +Operation = Remove +Type = Package +Target = * + +[Action] +Description = Work around FS#49347 by removing custom pacman hooks that are only required during ISO build... +When = PostTransaction +Depends = sh +Depends = coreutils +Depends = grep +Exec = /bin/sh -c "rm -- $(grep -Frl 'remove from airootfs' /etc/pacman.d/hooks/)" diff --git a/configs/releng-openrc/airootfs/root/customize_airootfs.sh b/configs/releng-openrc/airootfs/root/customize_airootfs.sh deleted file mode 100755 index 92e57de..0000000 --- a/configs/releng-openrc/airootfs/root/customize_airootfs.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -# -# SPDX-License-Identifier: GPL-3.0-or-later - -set -e -u - -# Warning: customize_airootfs.sh is deprecated! Support for it will be removed in a future parabolaiso version. - -sed -i 's/#\(en_US\.UTF-8\)/\1/' /etc/locale.gen -locale-gen - -sed -i "s/#Server/Server/g" /etc/pacman.d/mirrorlist diff --git a/configs/releng/airootfs/etc/pacman.d/hooks/40-locale-gen.hook b/configs/releng/airootfs/etc/pacman.d/hooks/40-locale-gen.hook new file mode 100644 index 0000000..82dd199 --- /dev/null +++ b/configs/releng/airootfs/etc/pacman.d/hooks/40-locale-gen.hook @@ -0,0 +1,13 @@ +# remove from airootfs! +[Trigger] +Operation = Install +Type = Package +Target = glibc + +[Action] +Description = Uncommenting en_US.UTF-8 locale and running locale-gen... +When = PostTransaction +Depends = glibc +Depends = sed +Depends = sh +Exec = /bin/sh -c "sed -i 's/#\(en_US\.UTF-8\)/\1/' /etc/locale.gen && locale-gen" diff --git a/configs/releng/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook b/configs/releng/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook new file mode 100644 index 0000000..342aa95 --- /dev/null +++ b/configs/releng/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook @@ -0,0 +1,13 @@ +# remove from airootfs! +[Trigger] +Operation = Install +Operation = Upgrade +Type = Package +Target = pacman-mirrorlist + +[Action] +Description = Uncommenting all mirrors in /etc/pacman.d/mirrorlist... +When = PostTransaction +Depends = pacman-mirrorlist +Depends = sed +Exec = /usr/bin/sed -i "s/#Server/Server/g" /etc/pacman.d/mirrorlist diff --git a/configs/releng/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook b/configs/releng/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook new file mode 100644 index 0000000..8dfb943 --- /dev/null +++ b/configs/releng/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook @@ -0,0 +1,18 @@ +# remove from airootfs! +# As a workaround for https://bugs.archlinux.org/task/49347 , remove pacman hooks specific to the ISO build process. +# If not, they would be used when pacstrap is run in the live environment. + +[Trigger] +Operation = Install +Operation = Upgrade +Operation = Remove +Type = Package +Target = * + +[Action] +Description = Work around FS#49347 by removing custom pacman hooks that are only required during ISO build... +When = PostTransaction +Depends = sh +Depends = coreutils +Depends = grep +Exec = /bin/sh -c "rm -- $(grep -Frl 'remove from airootfs' /etc/pacman.d/hooks/)" diff --git a/configs/releng/airootfs/root/customize_airootfs.sh b/configs/releng/airootfs/root/customize_airootfs.sh deleted file mode 100755 index 92e57de..0000000 --- a/configs/releng/airootfs/root/customize_airootfs.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -# -# SPDX-License-Identifier: GPL-3.0-or-later - -set -e -u - -# Warning: customize_airootfs.sh is deprecated! Support for it will be removed in a future parabolaiso version. - -sed -i 's/#\(en_US\.UTF-8\)/\1/' /etc/locale.gen -locale-gen - -sed -i "s/#Server/Server/g" /etc/pacman.d/mirrorlist diff --git a/parabolaiso/initcpio/hooks/parabolaiso_pxe_common b/parabolaiso/initcpio/hooks/parabolaiso_pxe_common index 035a3e9..4144983 100644 --- a/parabolaiso/initcpio/hooks/parabolaiso_pxe_common +++ b/parabolaiso/initcpio/hooks/parabolaiso_pxe_common @@ -72,6 +72,7 @@ run_latehook () { ip link set "${bootif_dev}" down fi elif [ "${copy_resolvconf}" != "n" ] && [ -f /etc/resolv.conf ]; then + rm -f /new_root/etc/resolv.conf cp /etc/resolv.conf /new_root/etc/resolv.conf fi fi diff --git a/parabolaiso/mkparabolaiso b/parabolaiso/mkparabolaiso index a3bb49b..1bb0887 100755 --- a/parabolaiso/mkparabolaiso +++ b/parabolaiso/mkparabolaiso @@ -9,34 +9,29 @@ umask 0022 export LANG="C" export SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-"$(date +%s)"}" -# mkparabolaiso defaults +# Set application name from the script's file name app_name="${0##*/}" + +# Define global variables. All of them will be overwritten later pkg_list=() -quiet="y" -work_dir="work" -out_dir="out" -img_name="${app_name}.iso" +quiet="" +work_dir="" +out_dir="" +img_name="" gpg_key="" -override_gpg_key="" - -# profile defaults -profile="" -iso_name="${app_name}" -iso_label="${app_name^^}" -override_iso_label="" -iso_publisher="${app_name}" -override_iso_publisher="" -iso_application="${app_name} iso" -override_iso_application="" +iso_name="" +iso_label="" +iso_publisher="" +iso_application="" iso_version="" -install_dir="${app_name}" -override_install_dir="" -arch="$(uname -m)" -pacman_conf="/etc/pacman.conf" -override_pacman_conf="" +install_dir="" +arch="" +pacman_conf="" +packages="" +packages_dual="" bootmodes=() -airootfs_image_type="squashfs" -airootfs_image_tool_options=('-comp' 'xz') +airootfs_image_type="" +airootfs_image_tool_options=() declare -A file_permissions=() @@ -103,7 +98,8 @@ usage: ${app_name} [options] -h This message -o Set the output directory Default: '${out_dir}' - -p PACKAGE(S) Package(s) to install, can be used multiple times + -p PACKAGE(S) Package(s) to install. + Multiple packages are provided as quoted, space delimited list. -v Enable verbose output -w Set the working directory Default: '${work_dir}' @@ -164,6 +160,11 @@ _cleanup_airootfs() { } _run_mksquashfs() { + # Set default mksquashfs options + if (( ${#airootfs_image_tool_options[@]} < 1 )); then + airootfs_image_tool_options=('-comp' 'xz') + fi + local image_path="${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" if [[ "${quiet}" == "y" ]]; then mksquashfs "$@" "${image_path}" -noappend "${airootfs_image_tool_options[@]}" -no-progress > /dev/null @@ -288,7 +289,7 @@ _make_custom_airootfs() { for filename in "${!file_permissions[@]}"; do IFS=':' read -ra permissions <<< "${file_permissions["${filename}"]}" # Prevent file path traversal outside of $airootfs_dir - if [[ "$(realpath -q -- "${airootfs_dir}${filename}")" != "$(realpath -q -- "${airootfs_dir}")"* ]]; then + if [[ "$(realpath -q -- "${airootfs_dir}${filename}")" != "${airootfs_dir}"* ]]; then _msg_error "Failed to set permissions on '${airootfs_dir}${filename}'. Outside of valid path." 1 # Warn if the file does not exist elif [[ ! -e "${airootfs_dir}${filename}" ]]; then @@ -333,7 +334,7 @@ _make_customize_airootfs() { local passwd=() if [[ -e "${profile}/airootfs/etc/passwd" ]]; then - _msg_info "Copying /etc/skel/* to user homes..." + _msg_info "Copying /etc/skel/* to ${arch} user homes..." while IFS=':' read -a passwd -r; do # Only operate on UIDs in range 1000–59999 (( passwd[2] >= 1000 && passwd[2] < 60000 )) || continue @@ -919,10 +920,6 @@ _build_iso() { # Read profile's values from profiledef.sh _read_profile() { - local validation_error=0 - local bootmode - - _msg_info "Reading profile..." if [[ -z "${profile}" ]]; then _msg_error "No profile specified!" 1 fi @@ -937,102 +934,166 @@ _read_profile() { # shellcheck source=configs/releng/profiledef.sh . "${profile}/profiledef.sh" - # Resolve paths + # Resolve paths of files that are expected to reside in the profile's directory if [[ "${arch}" == "dual" ]]; then - packages_dual="$(realpath -- "${profile}"/packages.{both,i686,x86_64})" - pacman_conf="$(realpath -- "${pacman_conf}")" + [[ -n "$packages_dual" ]] || packages_dual=("${profile}/packages."{both,i686,x86_64}) + # shellcheck disable=SC2178 + packages_dual="$(realpath -- "${packages_dual[@]}")" else - packages="$(realpath -- "${profile}/packages.${arch}")" - pacman_conf="$(realpath -- "${pacman_conf}")" + [[ -n "$packages" ]] || packages="${profile}/packages.${arch}" + packages="$(realpath -- "${packages}")" fi + pacman_conf="$(realpath -- "${pacman_conf}")" cd -- "${OLDPWD}" + fi +} - # Validate profile - # Check if the package list file exists and read packages from it - if [[ "${arch}" == "dual" ]]; then - for packages in ${packages_dual}; do - if [[ "${packages##*/}" = "packages.both" ]]; then - if [[ -e "${packages}" ]]; then - mapfile -t pkg_list < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${packages}") - if (( ${#pkg_list} < 1 )); then - (( validation_error=validation_error+1 )) - _msg_error "No package specified in '${packages}'." 0 - fi - else +# Validate set options +_validate_options() { + local validation_error=0 bootmode + local pkg_list_from_file=() + _msg_info "Validating options..." + # Check if the package list file exists and read packages from it + if [[ "${arch}" == "dual" ]]; then + # shellcheck disable=SC2128 + for packages in ${packages_dual}; do + if [[ "${packages##*/}" = "packages.both" ]]; then + if [[ -e "${packages}" ]]; then + mapfile -t pkg_list_from_file < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${packages}") + pkg_list+=("${pkg_list_from_file[@]}") + if (( ${#pkg_list_from_file} < 1 )); then (( validation_error=validation_error+1 )) - _msg_error "File '${packages}' does not exist." 0 + _msg_error "No package specified in '${packages}'." 0 fi - elif [[ -e "${packages}" ]]; then - mapfile -t "pkg_list_${packages##*.}" < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${packages}") - fi - done - else - if [[ -e "${packages}" ]]; then - mapfile -t pkg_list < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${packages}") - if (( ${#pkg_list} < 1 )); then + else (( validation_error=validation_error+1 )) - _msg_error "No package specified in '${packages}'." 0 + _msg_error "File '${packages}' does not exist." 0 fi - else + elif [[ -e "${packages}" ]]; then + mapfile -t "pkg_list_from_file_${packages##*.}" < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${packages}") + eval "pkg_list_${packages##*.}+=(\${pkg_list_from_file_${packages##*.}[@]})" + fi + done + else + if [[ -e "${packages}" ]]; then + mapfile -t pkg_list_from_file < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${packages}") + pkg_list+=("${pkg_list_from_file[@]}") + if (( ${#pkg_list_from_file} < 1 )); then (( validation_error=validation_error+1 )) - _msg_error "File '${packages}' does not exist." 0 + _msg_error "No package specified in '${packages}'." 0 fi - fi - # Check if pacman configuration file exists - if [[ ! -e "${pacman_conf}" ]]; then + else (( validation_error=validation_error+1 )) - _msg_error "File '${pacman_conf}' does not exist." 0 + _msg_error "File '${packages}' does not exist." 0 + fi - # Check if the specified bootmodes are supported - for bootmode in "${bootmodes[@]}"; do - if typeset -f "_make_bootmode_${bootmode}" &> /dev/null; then - if typeset -f "_validate_requirements_bootmode_${bootmode}" &> /dev/null; then - "_validate_requirements_bootmode_${bootmode}" - else - _msg_warning "Function '_validate_requirements_bootmode_${bootmode}' does not exist. Validating the requirements of '${bootmode}' boot mode will not be possible." - fi - else - (( validation_error=validation_error+1 )) - _msg_error "${bootmode} is not a valid boot mode!" 0 - fi - done - # Check if the specified airootfs_image_type is supported - if typeset -f "_mkairootfs_${airootfs_image_type}" &> /dev/null; then - if typeset -f "_validate_requirements_airootfs_image_type_${airootfs_image_type}" &> /dev/null; then - "_validate_requirements_airootfs_image_type_${airootfs_image_type}" + fi + + # Check if pacman configuration file exists + if [[ ! -e "${pacman_conf}" ]]; then + (( validation_error=validation_error+1 )) + _msg_error "File '${pacman_conf}' does not exist." 0 + fi + # Check if the specified bootmodes are supported + for bootmode in "${bootmodes[@]}"; do + if typeset -f "_make_bootmode_${bootmode}" &> /dev/null; then + if typeset -f "_validate_requirements_bootmode_${bootmode}" &> /dev/null; then + "_validate_requirements_bootmode_${bootmode}" else - _msg_warning "Function '_validate_requirements_airootfs_image_type_${airootfs_image_type}' does not exist. Validating the requirements of '${airootfs_image_type}' airootfs image type will not be possible." + _msg_warning "Function '_validate_requirements_bootmode_${bootmode}' does not exist. Validating the requirements of '${bootmode}' boot mode will not be possible." fi else (( validation_error=validation_error+1 )) - _msg_error "Unsupported image type: '${airootfs_image_type}'" 0 + _msg_error "${bootmode} is not a valid boot mode!" 0 fi - if (( validation_error )); then - _msg_error "${validation_error} errors were encountered while validating the profile. Aborting." 1 + done + # Check if the specified airootfs_image_type is supported + if typeset -f "_mkairootfs_${airootfs_image_type}" &> /dev/null; then + if typeset -f "_validate_requirements_airootfs_image_type_${airootfs_image_type}" &> /dev/null; then + "_validate_requirements_airootfs_image_type_${airootfs_image_type}" + else + _msg_warning "Function '_validate_requirements_airootfs_image_type_${airootfs_image_type}' does not exist. Validating the requirements of '${airootfs_image_type}' airootfs image type will not be possible." fi + else + (( validation_error=validation_error+1 )) + _msg_error "Unsupported image type: '${airootfs_image_type}'" 0 + fi + if (( validation_error )); then + _msg_error "${validation_error} errors were encountered while validating the profile. Aborting." 1 fi _msg_info "Done!" } -# set overrides from mkparabolaiso option parameters, if present +# Set defaults and, if present, overrides from mkparabolaiso command line option parameters _set_overrides() { - _msg_info "Setting overrides..." - [[ -n "$override_iso_label" ]] && iso_label="$override_iso_label" - [[ -n "$override_iso_publisher" ]] && iso_publisher="$override_iso_publisher" - [[ -n "$override_iso_application" ]] && iso_application="$override_iso_application" - [[ -n "$override_install_dir" ]] && install_dir="$override_install_dir" - [[ -n "$override_pacman_conf" ]] && pacman_conf="$override_pacman_conf" - [[ -n "$override_gpg_key" ]] && gpg_key="$override_gpg_key" - # NOTE: the call to _msg_info() conveniently guards this function from evaluating to false - _msg_info "Done!" -} + # Set variables that have command line overrides + if [[ -v override_work_dir ]]; then + work_dir="$override_work_dir" + elif [[ -z "$work_dir" ]]; then + work_dir='./work' + fi + work_dir="$(realpath -- "$work_dir")" + if [[ -v override_out_dir ]]; then + out_dir="$override_out_dir" + elif [[ -z "$out_dir" ]]; then + out_dir='./out' + fi + out_dir="$(realpath -- "$out_dir")" + if [[ -v override_pacman_conf ]]; then + pacman_conf="$override_pacman_conf" + elif [[ -z "$pacman_conf" ]]; then + pacman_conf="/etc/pacman.conf" + fi + pacman_conf="$(realpath -- "$pacman_conf")" + [[ ! -v override_pkg_list ]] || pkg_list+=("${override_pkg_list[@]}") + if [[ -v override_iso_label ]]; then + iso_label="$override_iso_label" + elif [[ -z "$iso_label" ]]; then + iso_label="${app_name^^}" + fi + if [[ -v override_iso_publisher ]]; then + iso_publisher="$override_iso_publisher" + elif [[ -z "$iso_publisher" ]]; then + iso_publisher="${app_name}" + fi + if [[ -v override_iso_application ]]; then + iso_application="$override_iso_application" + elif [[ -z "$iso_application" ]]; then + iso_application="${app_name} iso" + fi + if [[ -v override_install_dir ]]; then + install_dir="$override_install_dir" + elif [[ -z "$install_dir" ]]; then + install_dir="${app_name}" + fi + [[ ! -v override_gpg_key ]] || gpg_key="$override_gpg_key" + if [[ -v override_quiet ]]; then + quiet="$override_quiet" + elif [[ -z "$quiet" ]]; then + quiet="y" + fi + # Set variables that do not have overrides + [[ -n "$arch" ]] || arch="$(uname -m)" + [[ -n "$airootfs_image_type" ]] || airootfs_image_type="squashfs" + [[ -n "$iso_name" ]] || iso_name="${app_name}" + [[ -n "$img_name" ]] || img_name="${iso_name}-${iso_version}-${arch}.iso" +} _export_gpg_publickey() { gpg --batch --output "${work_dir}/pubkey.gpg" --export "${gpg_key}" } +_make_version() { + install -d -m 0755 -- "${isofs_dir}/${install_dir}" + _msg_info "Creating ${arch} files with iso version..." + printf '%s\n' "${iso_version}" > "${airootfs_dir}/version" + printf '%s\n' "${iso_version}" > "${isofs_dir}/${install_dir}/version" + printf '%.1024s' "$(printf '# GRUB Environment Block\nVERSION=%s\n%s' "${iso_version}" \ + "$(printf '%0.1s' "#"{1..1024})")" > "${isofs_dir}/${install_dir}/grubenv" + _msg_info "Done!" +} _make_pkglist() { install -d -m 0755 -- "${isofs_dir}/${install_dir}" @@ -1045,8 +1106,6 @@ _build_profile() { # Set up essential directory paths airootfs_dir="${work_dir}/${arch}/airootfs" isofs_dir="${work_dir}/iso" - # Set ISO file name - img_name="${iso_name}-${iso_version}-${arch}.iso" # Create working directory [[ -d "${work_dir}" ]] || install -d -- "${work_dir}" # Write build date to file or if the file exists, read it from there @@ -1056,11 +1115,12 @@ _build_profile() { printf '%s\n' "$SOURCE_DATE_EPOCH" > "${work_dir}/build_date" fi - [[ "${quiet}" == "n" ]] && _show_config + [[ "${quiet}" == "y" ]] || _show_config _run_dual '_run_once _make_pacman_conf' - [[ -n "${gpg_key}" ]] && _run_once _export_gpg_publickey + [[ -z "${gpg_key}" ]] || _run_once _export_gpg_publickey _run_dual '_run_once _make_custom_airootfs' \ '_run_once _make_packages' + _run_dual '_run_once _make_version' _run_dual '_run_once _make_customize_airootfs' _run_dual '_run_once _make_pkglist' _make_bootmodes @@ -1071,19 +1131,16 @@ _build_profile() { while getopts 'p:C:L:P:A:D:w:o:g:vh?' arg; do case "${arg}" in - p) - read -r -a opt_pkg_list <<< "${OPTARG}" - pkg_list+=("${opt_pkg_list[@]}") - ;; - C) override_pacman_conf="$(realpath -- "${OPTARG}")" ;; + p) read -r -a override_pkg_list <<< "${OPTARG}" ;; + C) override_pacman_conf="${OPTARG}" ;; L) override_iso_label="${OPTARG}" ;; P) override_iso_publisher="${OPTARG}" ;; A) override_iso_application="${OPTARG}" ;; D) override_install_dir="${OPTARG}" ;; - w) work_dir="$(realpath -- "${OPTARG}")" ;; - o) out_dir="$(realpath -- "${OPTARG}")" ;; + w) override_work_dir="${OPTARG}" ;; + o) override_out_dir="${OPTARG}" ;; g) override_gpg_key="${OPTARG}" ;; - v) quiet="n" ;; + v) override_quiet="n" ;; h|?) _usage 0 ;; *) _msg_error "Invalid argument '${arg}'" 0 @@ -1108,6 +1165,7 @@ profile="$(realpath -- "${1}")" _read_profile _set_overrides +_validate_options _build_profile # vim:ts=4:sw=4:et: diff --git a/scripts/run_parabolaiso.sh b/scripts/run_parabolaiso.sh index cea5d2c..63f6c86 100755 --- a/scripts/run_parabolaiso.sh +++ b/scripts/run_parabolaiso.sh @@ -115,18 +115,6 @@ run_image() { -no-reboot } -set_image() { - if [[ -z "$image" ]]; then - printf 'ERROR: %s\n' "Image name can not be empty." - exit 1 - fi - if [[ ! -f "$image" ]]; then - printf 'ERROR: %s\n' "Image (${image}) does not exist." - exit 1 - fi - image="$1" -} - image='' oddimage='' accessibility='' -- cgit v1.2.2