From 1717558b3fe6b88ca6c9a97809a08a6f245dc408 Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Tue, 16 Jul 2024 13:22:51 +0200 Subject: [PATCH 1/6] Ported the bootable image creation to 24 This PR ports the current bootable image creation to Core Desktop 24. It still uses the old script to create the ISO. --- Makefile | 53 ++++++++-- create_iso.sh | 134 +++++++++++++++++++++++++ image_data/06_quiet.cfg | 4 + image_data/autoinstall.yaml | 7 ++ image_data/core-desktop.yaml.in | 96 ++++++++++++++++++ image_data/install-sources.yaml.in | 11 ++ image_data/no-compact.conf | 4 + image_data/no-hardening.conf | 4 + image_data/no-rate-limit.conf | 2 + image_data/subiquity-enable | 44 ++++++++ image_data/subiquity-installer.service | 8 ++ image_data/subiquity-launch | 5 + image_data/subiquity-launcher.service | 16 +++ 13 files changed, 380 insertions(+), 8 deletions(-) create mode 100755 create_iso.sh create mode 100755 image_data/06_quiet.cfg create mode 100644 image_data/autoinstall.yaml create mode 100644 image_data/core-desktop.yaml.in create mode 100644 image_data/install-sources.yaml.in create mode 100644 image_data/no-compact.conf create mode 100644 image_data/no-hardening.conf create mode 100644 image_data/no-rate-limit.conf create mode 100755 image_data/subiquity-enable create mode 100644 image_data/subiquity-installer.service create mode 100755 image_data/subiquity-launch create mode 100644 image_data/subiquity-launcher.service diff --git a/Makefile b/Makefile index 1ac38e1..45109a4 100644 --- a/Makefile +++ b/Makefile @@ -3,17 +3,21 @@ EXTRA_SNAPS = ALL_SNAPS = $(EXTRA_SNAPS) firefox all: pc.tar.gz +bootable: ubuntu-core-desktop-24-amd64.iso +bootable-dangerous: ubuntu-core-desktop-24-dangerous-amd64.iso + +define build_img = +rm -rf img${1} +ubuntu-image snap --output-dir img${1} --image-size ${2}G \ + $(foreach snap,$(ALL_SNAPS),--snap $(snap)) $< +mv img${1}/${3}.img ./$@ +endef + pc.img: ubuntu-core-desktop-24-amd64.model $(EXTRA_SNAPS) - rm -rf img/ - ubuntu-image snap --output-dir img --image-size 20G \ - $(foreach snap,$(ALL_SNAPS),--snap $(snap)) $< - mv img/pc.img . + $(call build_img,,20,pc) pc-dangerous.img: ubuntu-core-desktop-24-amd64-dangerous.model $(EXTRA_SNAPS) - rm -rf dangerous/ - ubuntu-image snap --output-dir dangerous --image-size 20G \ - $(foreach snap,$(ALL_SNAPS),--snap $(snap)) $< - mv dangerous/pc.img pc-dangerous.img + $(call build_img,-dangerous,20,pc) pi.img: ubuntu-core-desktop-22-pi.model $(EXTRA_SNAPS) rm -rf dangerous/ @@ -29,10 +33,43 @@ pi-dangerous.img: ubuntu-core-desktop-22-pi-dangerous.model $(EXTRA_SNAPS) %.tar.gz: %.img tar czSf $@ $< +%.img.xz: %.img + xz -k --force --threads=0 -vv $< + .PHONY: all +define build_bootable_img = +rm -rf output/ +mkdir -p image +cat image_data/install-sources.yaml.in |sed "s/@FILE@/$ image/install-sources.yaml +cat image_data/core-desktop.yaml.in |sed "s/@FILE@/$ image/core-desktop.yaml +sudo ubuntu-image classic --debug -O output/ image/core-desktop.yaml +sudo chown -R $(shell id -u):$(shell id -g) output +mv output/$@ . +endef + +ubuntu-core-desktop-24-amd64.img: pc.img.xz image_data/core-desktop.yaml.in image_data/install-sources.yaml.in + $(call build_bootable_img) + +ubuntu-core-desktop-24-dangerous-amd64.img: pc-dangerous.img.xz image_data/core-desktop.yaml.in image_data/install-sources.yaml.in + $(call build_bootable_img) + +ubuntu-core-desktop-24-%.iso: ubuntu-core-desktop-24-%.img + ./create_iso.sh $< + clean: sudo rm -rf img sudo rm -rf output sudo rm -rf image + sudo rm -rf image2 + sudo rm -rf dangerous + sudo rm -rf livecd-rootfs sudo rm -f pc*.img.xz pc*.img pc*.tar.gz ubuntu-core-desktop-*.img ubuntu-core-desktop-*.img.xz ubuntu-core-desktop-*.iso image/install-sources.yaml + +clean-bootable: + sudo rm -rf img + sudo rm -rf output + sudo rm -rf image + sudo rm -rf image2 + sudo rm -rf dangerous + sudo rm -f ubuntu-core-desktop-*.img ubuntu-core-desktop-*.img.xz ubuntu-core-desktop-*.iso image/install-sources.yaml image/core-desktop.yaml diff --git a/create_iso.sh b/create_iso.sh new file mode 100755 index 0000000..d824ac3 --- /dev/null +++ b/create_iso.sh @@ -0,0 +1,134 @@ +#!/bin/sh + +# Process obtained from https://itnext.io/how-to-create-a-custom-ubuntu-live-from-scratch-dd3b3f213f81 + +# we expect a .xz compressed image (see below where we decompress it) +ORIGINAL_DISK_IMAGE=$1 +DISK_IMAGE=$PWD/output/disk.img + +# where to mount the disk image +CHROOT=$PWD/output/mnt + +cp -a ${ORIGINAL_DISK_IMAGE} ${DISK_IMAGE} + +#cd output +# decompress the image +#xz -v -d ${DISK_IMAGE} +#cd .. + +mkdir -p $CHROOT +# since we know that the partition with the data is the third one, we use awk to extract the start sector +sudo mount -o loop,offset=$(expr 512 \* $(fdisk -l ${DISK_IMAGE} |grep img3 | awk '{print $2}')) ${DISK_IMAGE} $CHROOT + +# we prepare the chroot environment to ensure that everything works inside.. +sudo mount -o bind /dev ${CHROOT}/dev +sudo mount -o bind /dev/pts ${CHROOT}/dev/pts +sudo mount -o bind /proc ${CHROOT}/proc +sudo mount -o bind /sys ${CHROOT}/sys +sudo mount -o bind /run ${CHROOT}/run +# install the required packages +sudo chroot ${CHROOT} apt install -y casper +# and, if needed, open a bash shell to do manual checks +# chroot ${CHROOT} /bin/bash +sudo umount ${CHROOT}/run +sudo umount ${CHROOT}/sys/firmware/efi/efivars +sudo umount ${CHROOT}/sys +sudo umount ${CHROOT}/proc +sudo umount ${CHROOT}/dev/pts +sudo umount ${CHROOT}/dev + +rm -rf $PWD/image2 + +mkdir -p $PWD/image2/casper +mkdir -p $PWD/image2/isolinux +mkdir -p $PWD/image2/install + +mv $CHROOT/cdrom/casper/* $PWD/image2/casper/ + +sudo cp $CHROOT/boot/vmlinuz-**-**-generic image2/casper/vmlinuz +sudo cp $CHROOT/boot/initrd.img-**-**-generic image2/casper/initrd + +touch image2/ubuntu + +cat < image2/isolinux/grub.cfg + +search --set=root --file /ubuntu + +insmod all_video + +set default="0" +set timeout=3 + +menuentry "Install Ubuntu Core Desktop" { + linux /casper/vmlinuz boot=casper quiet splash --- + initrd /casper/initrd +} +EOF + +sudo mksquashfs $CHROOT image2/casper/filesystem.squashfs + +printf $(sudo du -sx --block-size=1 $CHROOT | cut -f1) > image2/casper/filesystem.size + +# we are done with the original disk image +#sudo umount ${CHROOT}/sys/firmware/efi/efivars +#sudo umount ${CHROOT}/sys +sudo umount ${CHROOT} +rmdir ${CHROOT} + +cd $PWD/image2 + +grub-mkstandalone \ + --format=x86_64-efi \ + --output=isolinux/bootx64.efi \ + --locales="" \ + --fonts="" \ + "boot/grub/grub.cfg=isolinux/grub.cfg" + +( + cd isolinux && \ + dd if=/dev/zero of=efiboot.img bs=1M count=10 && \ + sudo mkfs.vfat efiboot.img && \ + LC_CTYPE=C mmd -i efiboot.img efi efi/boot && \ + LC_CTYPE=C mcopy -i efiboot.img ./bootx64.efi ::efi/boot/ +) + +grub-mkstandalone \ + --format=i386-pc \ + --output=isolinux/core.img \ + --install-modules="linux16 linux normal iso9660 biosdisk memdisk search tar ls" \ + --modules="linux16 linux normal iso9660 biosdisk search" \ + --locales="" \ + --fonts="" \ + "boot/grub/grub.cfg=isolinux/grub.cfg" + +cat /usr/lib/grub/i386-pc/cdboot.img isolinux/core.img > isolinux/bios.img + +sudo /bin/bash -c "(find . -type f -print0 | xargs -0 md5sum | grep -v "\./md5sum.txt" > md5sum.txt)" + +sudo xorriso \ + -as mkisofs \ + -iso-level 3 \ + -full-iso9660-filenames \ + -volid "Ubuntu Core Desktop" \ + -output ${DISK_IMAGE%.*}.iso \ + -eltorito-boot boot/grub/bios.img \ + -no-emul-boot \ + -boot-load-size 4 \ + -boot-info-table \ + --eltorito-catalog boot/grub/boot.cat \ + --grub2-boot-info \ + --grub2-mbr /usr/lib/grub/i386-pc/boot_hybrid.img \ + -eltorito-alt-boot \ + -e EFI/efiboot.img \ + -no-emul-boot \ + -append_partition 2 0xef isolinux/efiboot.img \ + -m "isolinux/efiboot.img" \ + -m "isolinux/bios.img" \ + -graft-points \ + "/EFI/efiboot.img=isolinux/efiboot.img" \ + "/boot/grub/bios.img=isolinux/bios.img" \ + "." + +cd .. +mv ${DISK_IMAGE%.*}.iso ${ORIGINAL_DISK_IMAGE%.*}.iso +rm -rf image2 diff --git a/image_data/06_quiet.cfg b/image_data/06_quiet.cfg new file mode 100755 index 0000000..a614e72 --- /dev/null +++ b/image_data/06_quiet.cfg @@ -0,0 +1,4 @@ +output: {all: '>> /var/log/cloud-init-output.log'} +no_ssh_fingerprints: true +ssh: + emit_keys_to_console: false diff --git a/image_data/autoinstall.yaml b/image_data/autoinstall.yaml new file mode 100644 index 0000000..35a8d0b --- /dev/null +++ b/image_data/autoinstall.yaml @@ -0,0 +1,7 @@ +version: 1 +source: + id: ubuntu-core-desktop + search_drivers: false +interactive-sections: + - locale + - storage diff --git a/image_data/core-desktop.yaml.in b/image_data/core-desktop.yaml.in new file mode 100644 index 0000000..a808028 --- /dev/null +++ b/image_data/core-desktop.yaml.in @@ -0,0 +1,96 @@ +name: ubuntu-core-desktop-amd64 +display-name: Ubuntu Core Desktop amd64 +revision: 1 +architecture: amd64 +series: noble +class: preinstalled +kernel: linux-image-generic +gadget: + url: "https://git.launchpad.net/~canonical-foundations/snap-pc/+git/github-mirror-amd64" + branch: classic + type: "git" +rootfs: + components: + - main + - restricted + seed: + urls: + - "git://git.launchpad.net/~ubuntu-core-dev/ubuntu-seeds/+git/" + branch: noble + names: + - minimal +customization: + cloud-init: + user-data: | + #cloud-config + chpasswd: + expire: false + users: + - name: ubuntu + password: ubuntu + type: text + meta-data: | + #cloud-config + dsmode: local + instance_id: ubuntu-core-desktop + extra-packages: + - name: squashfs-tools + - name: snapd + - name: cloud-init + - name: lshw + - name: bzip2 + - name: tar + - name: lvm2 + - name: parted + - name: dosfstools + - name: xz-utils + - name: console-setup-linux + - name: whois + - name: mdadm + extra-snaps: + - name: subiquity + - name: core22 + - name: snapd + manual: + make-dirs: + - path: /cdrom/casper + permissions: 0755 + - path: /etc/systemd/journald.conf.d + permissions: 0755 + - path: /etc/systemd/system/systemd-journald.service.d + permissions: 0755 + - path: /etc/cloud/cloud.cfg.d + permissions: 0755 + copy-file: + - source: install-sources.yaml + destination: /cdrom/casper/install-sources.yaml + - source: ../image_data/autoinstall.yaml + destination: /autoinstall.yaml + - source: ../@FILE@ + destination: /cdrom/casper/@FILE@ + - source: ../image_data/subiquity-enable + destination: /sbin/subiquity-enable + - source: ../image_data/subiquity-launcher.service + destination: /lib/systemd/system/subiquity-launcher.service + - source: ../image_data/subiquity-launch + destination: /sbin/subiquity-launch + #- source: installer-data.tar.bz2 + # destination: /installer-data.tar.bz2 + - source: ../image_data/no-rate-limit.conf + destination: /etc/systemd/journald.conf.d/no-rate-limit.conf + - source: ../image_data/no-compact.conf + destination: /etc/systemd/system/systemd-journald.service.d/no-compact.conf + - source: ../image_data/no-hardening.conf + destination: /etc/systemd/system/systemd-journald.service.d/no-hardening.conf + - source: ../image_data/06_quiet.cfg + destination: /etc/cloud/cloud.cfg.d/06_quiet.cfg + execute: + - path: /usr/bin/systemd-machine-id-setup + - path: /sbin/subiquity-enable +artifacts: + img: + - + name: @OUTPUT@ + manifest: + name: ubuntu-core-desktop-pc-amd64.manifest + diff --git a/image_data/install-sources.yaml.in b/image_data/install-sources.yaml.in new file mode 100644 index 0000000..4684b03 --- /dev/null +++ b/image_data/install-sources.yaml.in @@ -0,0 +1,11 @@ +- default: true + description: + en: Ubuntu Core Desktop. + id: ubuntu-core-desktop + locale_support: locale-only + name: + en: Ubuntu Core Desktop + path: @FILE@ + type: dd-xz:file + size: @SIZE@ + variant: core diff --git a/image_data/no-compact.conf b/image_data/no-compact.conf new file mode 100644 index 0000000..a0701e5 --- /dev/null +++ b/image_data/no-compact.conf @@ -0,0 +1,4 @@ +# systemd in 23.04+ uses a newer "compact" format by default which is not +# understood by the systemd libraries from jammy used in the subiquity snap. +[Service] +Environment="SYSTEMD_JOURNAL_COMPACT=0" diff --git a/image_data/no-hardening.conf b/image_data/no-hardening.conf new file mode 100644 index 0000000..c4262d2 --- /dev/null +++ b/image_data/no-hardening.conf @@ -0,0 +1,4 @@ +# systemd in 22.04+ uses "hash table hardening" by default which is not +# understood by the systemd libraries from focal used in the subiquity snap. +[Service] +Environment="SYSTEMD_JOURNAL_KEYED_HASH=0" diff --git a/image_data/no-rate-limit.conf b/image_data/no-rate-limit.conf new file mode 100644 index 0000000..fbaab95 --- /dev/null +++ b/image_data/no-rate-limit.conf @@ -0,0 +1,2 @@ +[Journal] +RateLimitIntervalSec=0 diff --git a/image_data/subiquity-enable b/image_data/subiquity-enable new file mode 100755 index 0000000..b0bbb68 --- /dev/null +++ b/image_data/subiquity-enable @@ -0,0 +1,44 @@ +#!/bin/sh + +# cd / +# mkdir -p installer-data +# cd installer-data +# tar xvf ../installer-data.tar.bz2 +# rm -rf etc/systemd/system/dev-disk* +# rm -f etc/cloud/cloud.cfg +# cp -a etc/* /etc/ +# cd .. +# rm -rf installer-data + +cat </etc/default/console-setup + +# CONFIGURATION FILE FOR SETUPCON + +# Consult the console-setup(5) manual page. + +ACTIVE_CONSOLES="/dev/tty[1-6]" + +CHARMAP="UTF-8" + +CODESET="guess" +FONTFACE="Fixed" +FONTSIZE="8x16" + +VIDEOMODE= + +# The following is an example how to use a braille font +# FONT='lat9w-08.psf.gz brl-8x8.psf' + +EOF + +/usr/bin/systemctl disable getty@tty1.service +/usr/bin/systemctl enable snap.subiquity.subiquity-server.service +/usr/bin/systemctl enable subiquity-launcher.service +/usr/bin/systemctl enable console-setup.service + +mkdir -p /var/lib/cloud/seed/nocloud +touch /var/lib/cloud/seed/nocloud/meta-data +touch /var/lib/cloud/seed/nocloud/user-data + +useradd ubuntu -p `mkpasswd --method=SHA-512 ubuntu` -s /bin/bash +hostname ubuntu diff --git a/image_data/subiquity-installer.service b/image_data/subiquity-installer.service new file mode 100644 index 0000000..15b0e08 --- /dev/null +++ b/image_data/subiquity-installer.service @@ -0,0 +1,8 @@ +[Unit] +Description=Launches subiquity +After=getty@tty1.service +After=snap.subiquity.subiquity-server + +[Service] +Type=simple +ExecStart=bin/sh sudo /snap/subiquity/current/usr/bin/subiquity-server diff --git a/image_data/subiquity-launch b/image_data/subiquity-launch new file mode 100755 index 0000000..b88114d --- /dev/null +++ b/image_data/subiquity-launch @@ -0,0 +1,5 @@ +#!/bin/sh + +dmesg -D +#setfont Uni2-Terminus16 +sudo snap run subiquity diff --git a/image_data/subiquity-launcher.service b/image_data/subiquity-launcher.service new file mode 100644 index 0000000..3993bf9 --- /dev/null +++ b/image_data/subiquity-launcher.service @@ -0,0 +1,16 @@ +[Unit] +Description=Launches subiquity, both back and front-end +DefaultDependencies=no +After=multi-user.target +After=cloud-final.service + +[Service] +Type=simple +ExecStart=/sbin/subiquity-launch +StandardInput=tty +StandardOutput=tty +StandardError=tty +TTYPath=/dev/tty1 + +[Install] +WantedBy=multi-user.target From c4e1562ef02f5e17fdd203b9c4e73c5a3d6602e5 Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Tue, 16 Jul 2024 18:01:11 +0200 Subject: [PATCH 2/6] Create a bootable ISO by default --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 45109a4..3014796 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ EXTRA_SNAPS = ALL_SNAPS = $(EXTRA_SNAPS) firefox -all: pc.tar.gz +all: bootable-iso -bootable: ubuntu-core-desktop-24-amd64.iso -bootable-dangerous: ubuntu-core-desktop-24-dangerous-amd64.iso +bootable-iso: ubuntu-core-desktop-24-amd64.iso +bootable-iso-dangerous: ubuntu-core-desktop-24-dangerous-amd64.iso define build_img = rm -rf img${1} @@ -62,7 +62,7 @@ clean: sudo rm -rf output sudo rm -rf image sudo rm -rf image2 - sudo rm -rf dangerous + sudo rm -rf img-dangerous sudo rm -rf livecd-rootfs sudo rm -f pc*.img.xz pc*.img pc*.tar.gz ubuntu-core-desktop-*.img ubuntu-core-desktop-*.img.xz ubuntu-core-desktop-*.iso image/install-sources.yaml @@ -71,5 +71,5 @@ clean-bootable: sudo rm -rf output sudo rm -rf image sudo rm -rf image2 - sudo rm -rf dangerous + sudo rm -rf img-dangerous sudo rm -f ubuntu-core-desktop-*.img ubuntu-core-desktop-*.img.xz ubuntu-core-desktop-*.iso image/install-sources.yaml image/core-desktop.yaml From 77312d4a6b7582cb09663644b1fa5ae039945033 Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Tue, 16 Jul 2024 18:05:23 +0200 Subject: [PATCH 3/6] Update README and add extra dependencies --- .github/workflows/build-image.yaml | 7 ++++--- README.md | 10 +++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-image.yaml b/.github/workflows/build-image.yaml index d384829..fd79219 100644 --- a/.github/workflows/build-image.yaml +++ b/.github/workflows/build-image.yaml @@ -16,7 +16,7 @@ jobs: submodules: true - name: Install build dependencies run: | - sudo apt install -y make squashfs-tools dosfstools mtools + sudo apt install -y make squashfs-tools dosfstools mtools xorriso sudo snap refresh --edge snapd sudo snap install --classic ubuntu-image - name: Build image @@ -28,5 +28,6 @@ jobs: name: image path: | README.md - *.tar.gz - compression-level: 0 # content is already compressed + *.iso + *.img.xz + compression-level: 0 diff --git a/README.md b/README.md index 778092b..482488f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ # GDM on Ubuntu Core -This directory contains an image of Ubuntu Core 22 with the GDM -display manager loaded into the boot file system. It can be launched -in a Qemu virtual machine by following these instructions: +This directory contains an image of Ubuntu Core 24 with the GDM +display manager loaded into the boot file system, and an ISO image +with an installer for that same image. + +The ISO image can be used in any virtual machine. The image, in .zx +compressed format, can be launched in a Qemu virtual machine by +following these instructions: 1. Download and decompress the two image files and place them in the same directory. From 95c66ea3ace9532af9054f0ef399d34603765f50 Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Tue, 16 Jul 2024 18:09:19 +0200 Subject: [PATCH 4/6] Extra dependencies --- .github/workflows/build-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-image.yaml b/.github/workflows/build-image.yaml index fd79219..6b697ef 100644 --- a/.github/workflows/build-image.yaml +++ b/.github/workflows/build-image.yaml @@ -16,7 +16,7 @@ jobs: submodules: true - name: Install build dependencies run: | - sudo apt install -y make squashfs-tools dosfstools mtools xorriso + sudo apt install -y make squashfs-tools dosfstools mtools xorriso grub-common grub-pc-bin sudo snap refresh --edge snapd sudo snap install --classic ubuntu-image - name: Build image From c3b5844fc7843152be5ccc5820f0069e0c3c641e Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Tue, 16 Jul 2024 18:41:06 +0200 Subject: [PATCH 5/6] Separate artifacts --- .github/workflows/build-image.yaml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-image.yaml b/.github/workflows/build-image.yaml index 6b697ef..0ac468e 100644 --- a/.github/workflows/build-image.yaml +++ b/.github/workflows/build-image.yaml @@ -22,12 +22,18 @@ jobs: - name: Build image run: | make - - name: Upload artifacts + - name: Upload image artifact uses: actions/upload-artifact@v4 with: - name: image + name: bootable-image path: | README.md - *.iso *.img.xz compression-level: 0 + - name: Upload ISO artifact + uses: actions/upload-artifact@v4 + with: + name: bootable-iso + path: | + *.iso + compression-level: 0 From 9b199be8d1891c9b207d55a8996dea34d6c3c08d Mon Sep 17 00:00:00 2001 From: Sergio Costas Rodriguez Date: Tue, 16 Jul 2024 18:41:50 +0200 Subject: [PATCH 6/6] Compress the ISO --- .github/workflows/build-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-image.yaml b/.github/workflows/build-image.yaml index 0ac468e..e3b1593 100644 --- a/.github/workflows/build-image.yaml +++ b/.github/workflows/build-image.yaml @@ -36,4 +36,4 @@ jobs: name: bootable-iso path: | *.iso - compression-level: 0 + compression-level: 6