├── .flake8 ├── .gitattributes ├── .github └── workflows │ └── block-autosquash-commits.yml ├── .gitignore ├── 10-eos-oom-policy.conf ├── 50-disk-bfq.rules ├── 50-eos.preset ├── 50-usb-mouse-wakeup.rules ├── 60-suspend-mode.rules ├── COPYING ├── Makefile.am ├── autogen.sh ├── com.endlessm.TestMode.policy.in ├── configure.ac ├── debian ├── changelog ├── compat ├── control ├── copyright ├── eos-boot-helper.triggers ├── rules └── source │ └── format ├── dracut ├── Makefile.am ├── customization │ ├── Makefile.am │ ├── eos-install-custom-boot-splash │ ├── eos-install-custom-boot-splash.service │ └── module-setup.sh ├── endless.conf ├── image-boot │ ├── Makefile.am │ ├── eos-image-boot-generator │ ├── eos-image-boot-setup │ ├── eos-image-boot-setup.service │ ├── eos-live-storage-setup │ └── module-setup.sh └── repartition │ ├── Makefile.am │ ├── endless-repartition.service │ ├── endless-repartition.sh │ └── module-setup.sh ├── eos-config-journal ├── eos-config-journal.service ├── eos-enable-zram ├── eos-enable-zram.service ├── eos-esp-generator ├── eos-firewall-localonly ├── eos-firewall-localonly-nm ├── eos-firewall-localonly.service ├── eos-firstboot ├── eos-firstboot.service ├── eos-fix-flatpak-overrides.service ├── eos-live-boot-generator ├── eos-live-boot-overlayfs-setup ├── eos-live-boot-overlayfs-setup.service ├── eos-migrate-chromium-profile ├── eos-migrate-chromium-profile.service ├── eos-migrate-firefox-profile ├── eos-migrate-firefox-profile.service ├── eos-migrate-shotwell.service ├── eos-repartition-mbr ├── eos-test-mode ├── eos-transient-setup ├── eos-transient-setup.service ├── eos-update-efi-uuid.c ├── eos-update-flatpak-repos ├── eos-update-flatpak-repos.service ├── eos-update-system-ca.service ├── eos-vm-generator ├── factory-reset ├── Makefile.am ├── eos-factory-reset ├── eos-factory-reset-users └── eos-factory-reset-users.service ├── fallback-fb-setup ├── Makefile.am ├── fallback-fb-setup.c └── fallback-fb-setup.service ├── flatpak-repos ├── Makefile.am ├── eos-sdk.flatpakrepo └── flathub.flatpakrepo ├── nvidia ├── Makefile.am ├── asus-pci-blacklist ├── dmi-blacklist ├── dmi-board-blacklist ├── nouveau.conf ├── nvidia-graphics-setup └── nvidia-graphics.service ├── psi-monitor ├── Makefile.am ├── psi-monitor.c └── psi-monitor.service ├── pytest.ini ├── record-boot-success ├── Makefile.am ├── com.endlessm.RecordBootSuccess.conf ├── com.endlessm.RecordBootSuccess.service ├── record-boot-success ├── record-boot-success.desktop └── record-boot-success.service ├── tests ├── Makefile.am ├── __init__.py ├── check-syntax ├── conftest.py ├── efivars │ ├── Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0002-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Boot0003-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── BootOptionSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── ConIn-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── ConInDev-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── ConOut-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── ConOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── ErrOut-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── ErrOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── FALLBACK_VERBOSE-605dab50-e046-4300-abb6-3dd810dd8b23 │ ├── KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Key0000-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Key0001-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Lang-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── LangCodes-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── MTC-eb704011-1402-11d3-8e77-00a0c969723b │ ├── MokListRT-605dab50-e046-4300-abb6-3dd810dd8b23 │ ├── MokListTrustedRT-605dab50-e046-4300-abb6-3dd810dd8b23 │ ├── MokListXRT-605dab50-e046-4300-abb6-3dd810dd8b23 │ ├── OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── PK-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── PlatformLang-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── PlatformLangCodes-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── PlatformRecovery0000-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── SbatLevelRT-605dab50-e046-4300-abb6-3dd810dd8b23 │ ├── SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── SignatureSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── Timeout-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── VarErrorFlag-04b37fe8-f6ae-480b-bdd5-37d98c5e89aa │ ├── VendorKeys-8be4df61-93ca-11d2-aa0d-00e098032b8c │ ├── certdb-d9bee56e-75dc-49d9-b4d7-b534210f637a │ ├── certdbv-d9bee56e-75dc-49d9-b4d7-b534210f637a │ └── db-d719b2cb-3d3a-4596-a3bc-dad00e67656f ├── espgen │ ├── README.md │ ├── grub-gpt-fstab-efi-fstab.json │ ├── grub-gpt-fstab-efi-kcmdline.json │ ├── grub-gpt-fstab-efi-mounts-init.json │ ├── grub-gpt-fstab-efi-mounts-reload.json │ ├── grub-gpt-fstab-efi-partitions-init.json │ ├── grub-gpt-fstab-efi-partitions-reload.json │ ├── grub-gpt-fstab.json │ ├── grub-gpt-kcmdline.json │ ├── grub-gpt-mounts-init.json │ ├── grub-gpt-mounts-reload.json │ ├── grub-gpt-partitions-init.json │ ├── grub-gpt-partitions-reload.json │ ├── grub-mbr-fstab.json │ ├── grub-mbr-kcmdline.json │ ├── grub-mbr-mounts-init.json │ ├── grub-mbr-mounts-reload.json │ ├── grub-mbr-partitions-init.json │ ├── grub-mbr-partitions-reload.json │ ├── sdboot-fstab.json │ ├── sdboot-kcmdline.json │ ├── sdboot-mounts-init.json │ ├── sdboot-mounts-reload.json │ ├── sdboot-partitions-init.json │ ├── sdboot-partitions-reload.json │ ├── sdboot-xbootldr-fstab.json │ ├── sdboot-xbootldr-kcmdline.json │ ├── sdboot-xbootldr-mounts-init.json │ ├── sdboot-xbootldr-mounts-reload.json │ ├── sdboot-xbootldr-partitions-init.json │ ├── sdboot-xbootldr-partitions-reload.json │ ├── windows-efi-fstab.json │ ├── windows-efi-kcmdline.json │ ├── windows-efi-mounts-init.json │ ├── windows-efi-mounts-reload.json │ ├── windows-efi-partitions-init.json │ └── windows-efi-partitions-reload.json ├── run-tests ├── test_esp_generator.py ├── test_image_boot.py ├── test_live_storage.py ├── test_migrate_chromium_profile.py ├── test_migrate_firefox_profile.py ├── test_repartition.py ├── test_repartition_mbr.py ├── test_update_efi_uuid.py ├── test_update_flatpak_repos.py └── util.py └── tmpfiles.d ├── chromium-system-services.conf.in └── eos-boot-helper.conf /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | # The default is 79 characters. One popular Python code formatter 3 | # https://github.com/ambv/black#line-length defaults to 88 which is based on 4 | # some empirical research on reducing annoying line wrapping on real source 5 | # code. 6 | max-line-length = 88 7 | 8 | # Include scripts to check in addition to the default *.py. 9 | filename = 10 | *.py, 11 | ./eos-esp-generator, 12 | ./eos-migrate-chromium-profile, 13 | ./eos-migrate-firefox-profile, 14 | ./eos-update-flatpak-repos, 15 | ./factory-reset/eos-factory-reset, 16 | ./record-boot-success/record-boot-success, 17 | ./tests/check-syntax 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Define hexdump textconv with "git config diff.efivars.textconv hd" 2 | tests/efivars/* diff=efivars 3 | -------------------------------------------------------------------------------- /.github/workflows/block-autosquash-commits.yml: -------------------------------------------------------------------------------- 1 | on: pull_request 2 | 3 | name: Pull Requests 4 | 5 | jobs: 6 | message-check: 7 | name: Block Autosquash Commits 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Block Autosquash Commits 13 | uses: xt0rted/block-autosquash-commits-action@v2.0.0 14 | with: 15 | repo-token: ${{ secrets.GITHUB_TOKEN }} 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.[oa] 3 | .deps/ 4 | .pytest_cache/ 5 | __pycache__/ 6 | com.endlessm.TestMode.policy 7 | Makefile 8 | Makefile.in 9 | aclocal.m4 10 | autom4te.cache/ 11 | compile 12 | config.guess 13 | config.log 14 | config.status 15 | config.sub 16 | configure 17 | depcomp 18 | eos-update-efi-uuid 19 | fallback-fb-setup/fallback-fb-setup 20 | install-sh 21 | missing 22 | psi-monitor/psi-monitor 23 | tmpfiles.d/chromium-system-services.conf 24 | test-driver 25 | tests/*.log 26 | tests/*.trs 27 | -------------------------------------------------------------------------------- /10-eos-oom-policy.conf: -------------------------------------------------------------------------------- 1 | [Manager] 2 | # The default DefaultOOMPolicy=stop causes undesirable behaviour in Endless OS 3 | # when systemd-oomd is not being used. Instead, instruct systemd to log when 4 | # a process is killed but allow its associated unit to continue running. 5 | DefaultOOMPolicy=continue 6 | -------------------------------------------------------------------------------- /50-disk-bfq.rules: -------------------------------------------------------------------------------- 1 | ACTION=="add|change", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", ATTR{queue/scheduler}="bfq" 2 | -------------------------------------------------------------------------------- /50-eos.preset: -------------------------------------------------------------------------------- 1 | # Systemd unit file presets for EOS. We do not install a wildcard 2 | # "disable *" rule, so here we simply disable any units that aren't 3 | # desired. All others will be enabled by systemd. 4 | 5 | # Reverse systemd defaults 6 | disable systemd-networkd.service 7 | disable systemd-networkd.socket 8 | disable systemd-resolved.service 9 | disable systemd-networkd-wait-online.service 10 | disable systemd-sysupdate.service 11 | disable systemd-sysupdate.timer 12 | disable systemd-sysupdate-reboot.service 13 | disable systemd-sysupdate-reboot.timer 14 | 15 | # Disable other unwanted units 16 | disable apt-daily.timer 17 | disable apt-daily-upgrade.timer 18 | disable bluetooth-init.service 19 | disable checkbox-ng.service 20 | disable cni-dhcp.service 21 | disable crio.service 22 | disable crio-shutdown.service 23 | disable cron.service 24 | disable dpkg-db-backup.timer 25 | disable eos-factory-reset-users.service 26 | disable eos-firewall-localonly.service 27 | disable eos-safe-defaults.service 28 | disable eos-update-server.service 29 | disable eos-update-server.socket 30 | disable eos-updater-avahi.path 31 | disable eos-updater-avahi.service 32 | disable ifupdown-wait-online.service 33 | disable networking.service 34 | disable NetworkManager-wait-online.service 35 | disable logrotate.timer 36 | disable openvpn-client@.service 37 | disable openvpn-server@.service 38 | disable openvpn.service 39 | disable openvpn@.service 40 | disable podman-auto-update.service 41 | disable podman-auto-update.timer 42 | disable pppd-dns.service 43 | disable rtkit-daemon.service 44 | disable rsyslog.service 45 | disable serial-getty@.service 46 | disable smartmontools.service 47 | disable speech-dispatcherd.service 48 | disable strongswan-starter.service 49 | disable ssh*.service 50 | disable ssh.socket 51 | disable systemd-nspawn@.service 52 | disable wpa_supplicant.service 53 | disable wpa_supplicant@.service 54 | disable wpa_supplicant-nl80211@.service 55 | disable wpa_supplicant-wired@.service 56 | disable xl2tpd.service 57 | 58 | # Disable virtualbox-guest-utils.service by default. It will be enabled 59 | # dynamically by eos-vm-generator when running in virtualbox. 60 | # ConditionVirtualization=oracle on the unit is not enought as it will 61 | # prevent conflicted units (systemd-timesyncd.service) from starting. 62 | # https://phabricator.endlessm.com/T32330#906081 63 | disable virtualbox-guest-utils.service 64 | 65 | # Disable units masked by Debian, as systemctl preset-all fails to 66 | # handle them. Without this, `systemctl preset-all` will fail with: 67 | # 68 | # Operation failed: Cannot send after transport endpoint shutdown 69 | # 70 | # Units masked in systemd. See debian/systemd.links. 71 | disable x11-common.service 72 | disable hostname.service 73 | disable rmnologin.service 74 | disable bootmisc.service 75 | disable fuse.service 76 | disable bootlogd.service 77 | disable stop-bootlogd-single.service 78 | disable stop-bootlogd.service 79 | disable hwclock.service 80 | disable mountkernfs.service 81 | disable mountdevsubfs.service 82 | disable mountall.service 83 | disable mountall-bootclean.service 84 | disable mountnfs.service 85 | disable mountnfs-bootclean.service 86 | disable umountfs.service 87 | disable umountnfs.service 88 | disable umountroot.service 89 | disable checkfs.service 90 | disable checkroot.service 91 | disable checkroot-bootclean.service 92 | disable cryptdisks.service 93 | disable cryptdisks-early.service 94 | disable single.service 95 | disable killprocs.service 96 | disable sendsigs.service 97 | disable halt.service 98 | disable reboot.service 99 | disable rc.service 100 | disable rcS.service 101 | disable motd.service 102 | disable bootlogs.service 103 | # 104 | # Units masked by other packages. Run the following to find them: 105 | # 106 | # find /usr/lib/systemd/system -lname /dev/null -printf 'disable %f\n' | sort 107 | disable screen-cleanup.service 108 | -------------------------------------------------------------------------------- /50-usb-mouse-wakeup.rules: -------------------------------------------------------------------------------- 1 | # On desktop/AIO/mini pc/stick pc systems, enable USB-HID mouse devices supporting boot protocol 2 | # as a wakeup source. 3 | SUBSYSTEM=="usb", ATTRS{bInterfaceClass}=="03", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="02", ATTR{[dmi/id]chassis_type}=="3|13|35|36", RUN+="/bin/sh -c 'echo enabled > /sys$env{DEVPATH}/../power/wakeup'" 4 | -------------------------------------------------------------------------------- /60-suspend-mode.rules: -------------------------------------------------------------------------------- 1 | SUBSYSTEM=="dmi", ENV{ID_VENDOR}=="GIGABYTE", ENV{ID_MODEL}=="Mission one", RUN{program}+="/bin/sh -c 'echo s2idle > /sys/power/mem_sleep'" 2 | SUBSYSTEM=="dmi", ENV{ID_MODEL}=="GB-BXBT-2807", RUN{program}+="/bin/sh -c 'echo s2idle > /sys/power/mem_sleep'" 3 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = dracut factory-reset fallback-fb-setup flatpak-repos nvidia psi-monitor record-boot-success . tests 2 | EXTRA_DIST = debian .flake8 pytest.ini 3 | CLEANFILES = 4 | 5 | AM_CFLAGS = -Wall -Werror 6 | 7 | # Install systemd units, generators, preset files, and udev rules 8 | # under $prefix for distcheck 9 | AM_DISTCHECK_CONFIGURE_FLAGS = \ 10 | --with-systemdutildir='$${prefix}/lib/systemd' \ 11 | --with-systemdunitdir='$${prefix}/lib/systemd/system' \ 12 | --with-systemduserunitdir='$${prefix}/lib/systemd/user' \ 13 | --with-systemdgeneratordir='$${prefix}/lib/systemd/system-generators' \ 14 | --with-systemdpresetdir='$${prefix}/lib/systemd/system-preset' \ 15 | --with-udevdir='$${prefix}/lib/udev' \ 16 | --with-modprobedir='$${prefix}/lib/modprobe.d' \ 17 | --with-dbussystemconfigdir='$${prefix}/share/dbus-1/system.d' \ 18 | --with-system-bus-services-dir='$${prefix}/share/dbus-1/system-services' \ 19 | --with-polkitpolicydir='$${prefix}/share/polkit-1/actions' \ 20 | $(NULL) 21 | 22 | dist_systemdunit_DATA = \ 23 | eos-config-journal.service \ 24 | eos-enable-zram.service \ 25 | eos-firewall-localonly.service \ 26 | eos-firstboot.service \ 27 | eos-fix-flatpak-overrides.service \ 28 | eos-live-boot-overlayfs-setup.service \ 29 | eos-transient-setup.service \ 30 | eos-update-flatpak-repos.service \ 31 | eos-update-system-ca.service \ 32 | $(NULL) 33 | 34 | dist_systemduserunit_DATA = \ 35 | eos-migrate-chromium-profile.service \ 36 | eos-migrate-firefox-profile.service \ 37 | eos-migrate-shotwell.service \ 38 | $(NULL) 39 | 40 | dist_systemdgenerator_SCRIPTS = \ 41 | eos-esp-generator \ 42 | eos-live-boot-generator \ 43 | eos-vm-generator \ 44 | $(NULL) 45 | 46 | dist_systemdpreset_DATA = \ 47 | 50-eos.preset \ 48 | $(NULL) 49 | 50 | dist_systemdsystemmanagerconf_DATA = \ 51 | 10-eos-oom-policy.conf \ 52 | $(NULL) 53 | 54 | dist_systemdusermanagerconf_DATA = \ 55 | 10-eos-oom-policy.conf \ 56 | $(NULL) 57 | 58 | # Network Manager dispatcher script for the firewall - scripts which 59 | # are a symlink to no-wait.d will be run without blocking/ordering. 60 | # install the script here and make the symlink in install-data-hook 61 | # below 62 | networkscriptdir = $(sysconfdir)/NetworkManager/dispatcher.d 63 | networkscriptnowaitdir = $(networkscriptdir)/no-wait.d 64 | dist_networkscriptnowait_DATA = eos-firewall-localonly-nm 65 | 66 | install-data-hook: 67 | $(MKDIR_P) '$(DESTDIR)$(networkscriptdir)' 68 | ln -s 'no-wait.d/eos-firewall-localonly-nm' \ 69 | '$(DESTDIR)$(networkscriptdir)/eos-firewall-localonly-nm' 70 | chmod +x '$(DESTDIR)$(networkscriptdir)/eos-firewall-localonly-nm' 71 | $(MKDIR_P) '$(DESTDIR)$(systemduserunitdir)/gnome-session.target.wants' 72 | ln -s ../eos-migrate-chromium-profile.service '$(DESTDIR)$(systemduserunitdir)/gnome-session.target.wants' 73 | ln -s ../eos-migrate-firefox-profile.service '$(DESTDIR)$(systemduserunitdir)/gnome-session.target.wants' 74 | ln -s ../eos-migrate-shotwell.service '$(DESTDIR)$(systemduserunitdir)/gnome-session.target.wants' 75 | 76 | uninstall-hook: 77 | rm -f '$(DESTDIR)$(networkscriptdir)/eos-firewall-localonly-nm' 78 | 79 | udevrulesdir = $(udevdir)/rules.d 80 | dist_udevrules_DATA = \ 81 | 50-disk-bfq.rules \ 82 | 50-usb-mouse-wakeup.rules \ 83 | 60-suspend-mode.rules \ 84 | $(NULL) 85 | 86 | dist_sbin_SCRIPTS = \ 87 | eos-config-journal \ 88 | eos-enable-zram \ 89 | eos-firewall-localonly \ 90 | eos-firstboot \ 91 | eos-live-boot-overlayfs-setup \ 92 | eos-repartition-mbr \ 93 | eos-test-mode \ 94 | eos-transient-setup \ 95 | eos-update-flatpak-repos \ 96 | $(NULL) 97 | 98 | sbin_PROGRAMS = \ 99 | eos-update-efi-uuid \ 100 | $(NULL) 101 | 102 | eos_update_efi_uuid_SOURCES = eos-update-efi-uuid.c 103 | eos_update_efi_uuid_CFLAGS = $(AM_CFLAGS) $(EFIBOOT_CFLAGS) 104 | eos_update_efi_uuid_LDADD = $(EFIBOOT_LIBS) 105 | 106 | dist_libexec_SCRIPTS = \ 107 | eos-migrate-chromium-profile \ 108 | eos-migrate-firefox-profile \ 109 | $(NULL) 110 | 111 | tmpfilesdir = $(prefix)/lib/tmpfiles.d 112 | dist_tmpfiles_DATA = \ 113 | tmpfiles.d/chromium-system-services.conf \ 114 | tmpfiles.d/eos-boot-helper.conf \ 115 | $(NULL) 116 | 117 | com.endlessm.TestMode.policy: com.endlessm.TestMode.policy.in 118 | $(AM_V_GEN) $(SED) -e 's|[@]sbindir@|$(sbindir)|g' < "$<" > "$@" 119 | 120 | polkitpolicy_DATA = \ 121 | com.endlessm.TestMode.policy \ 122 | $(NULL) 123 | 124 | CLEANFILES += \ 125 | com.endlessm.TestMode.policy 126 | EXTRA_DIST += \ 127 | com.endlessm.TestMode.policy.in 128 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Run this to generate all the initial makefiles, etc. 3 | 4 | PROJECT=eos-boot-helper 5 | builddir=`pwd` 6 | srcdir=`dirname "$0"` 7 | [ -z "$srcdir" ] && srcdir=. 8 | 9 | # Rebuild the autotools 10 | cd "$srcdir" 11 | ${AUTORECONF-autoreconf} -iv || exit $? 12 | cd "$builddir" 13 | 14 | # Run configure unless NOCONFIGURE set. 15 | if [ -z "$NOCONFIGURE" ]; then 16 | "$srcdir"/configure "$@" 17 | echo "Now type 'make' to compile $PROJECT." 18 | else 19 | echo "Now type './configure && make' to compile $PROJECT." 20 | fi 21 | -------------------------------------------------------------------------------- /com.endlessm.TestMode.policy.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | Endless 8 | https://endlessm.com/ 9 | 10 | 11 | Initiate system test mode 12 | Authentication is required to start test mode 13 | 14 | auth_admin 15 | auth_admin 16 | auth_admin_keep 17 | 18 | @sbindir@/eos-test-mode 19 | 20 | 21 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | eos-boot-helper (1) eos; urgency=low 2 | 3 | * Initial release. 4 | 5 | -- Daniel Drake Thu, 12 Mar 2015 19:50:46 -0600 6 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 8 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: eos-boot-helper 2 | Section: admin 3 | Priority: optional 4 | Maintainer: Daniel Drake 5 | Build-Depends: automake, 6 | debhelper (>= 8), 7 | flake8 , 8 | gir1.2-flatpak-1.0 , 9 | gir1.2-glib-2.0 , 10 | gir1.2-ostree-1.0 , 11 | gir1.2-eosmetrics-0 , 12 | libdbus-1-dev, 13 | libefiboot-dev, 14 | libefivar-dev, 15 | libpolkit-gobject-1-dev, 16 | pkg-config, 17 | python3 , 18 | python3-gi , 19 | python3-parted , 20 | python3-pytest , 21 | python3-systemd , 22 | systemd, 23 | udev, 24 | Standards-Version: 3.9.1 25 | Homepage: https://endlessm.com 26 | 27 | Package: eos-boot-helper 28 | Architecture: any 29 | Description: Endless boot helper 30 | Depends: ${misc:Depends}, 31 | ca-certificates, 32 | dracut, 33 | iptables, 34 | ostree, 35 | flatpak, 36 | gir1.2-eosmetrics-0, 37 | gir1.2-flatpak-1.0, 38 | gir1.2-glib-2.0, 39 | gir1.2-ostree-1.0, 40 | python3, 41 | python3-gi, 42 | python3-parted, 43 | python3-systemd, 44 | util-linux (>= 2.30.0), 45 | Breaks: systemd (<= 232+dev145.a41a4e3-23) 46 | Replaces: systemd (<= 232+dev145.a41a4e3-23) 47 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | License: GPL-2 2 | This program is free software; you can redistribute it 3 | and/or modify it under the terms of the GNU General Public 4 | License as published by the Free Software Foundation; version 5 | 2 dated June 1991. 6 | . 7 | This program is distributed in the hope that it will be 8 | useful, but WITHOUT ANY WARRANTY; without even the implied 9 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 10 | PURPOSE. See the GNU General Public License for more 11 | details. 12 | . 13 | You should have received a copy of the GNU General Public 14 | License along with this package; if not, write to the Free 15 | Software Foundation, Inc., 51 Franklin St, Fifth Floor, 16 | Boston, MA 02110-1301 USA 17 | . 18 | On Debian systems, the full text of the GNU General Public 19 | License version 2 can be found in the file 20 | `/usr/share/common-licenses/GPL-2'. 21 | -------------------------------------------------------------------------------- /debian/eos-boot-helper.triggers: -------------------------------------------------------------------------------- 1 | activate update-initramfs 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | %: 4 | dh $@ --with systemd 5 | 6 | override_dh_systemd_start: 7 | dh_systemd_start --no-start 8 | 9 | # don't automatically enable eos-firewall-localonly 10 | override_dh_systemd_enable: 11 | dh_systemd_enable \ 12 | -Xeos-firewall-localonly.service \ 13 | $(NULL) 14 | dh_systemd_enable --no-enable \ 15 | eos-firewall-localonly.service \ 16 | $(NULL) 17 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /dracut/Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = repartition image-boot customization 2 | 3 | dracutconfdir = $(sysconfdir)/dracut.conf.d 4 | dist_dracutconf_DATA = endless.conf 5 | -------------------------------------------------------------------------------- /dracut/customization/Makefile.am: -------------------------------------------------------------------------------- 1 | dracutmoddir = $(prefix)/lib/dracut/modules.d/50eos-customization 2 | dist_dracutmod_SCRIPTS = module-setup.sh \ 3 | eos-install-custom-boot-splash 4 | dist_dracutmod_DATA = eos-install-custom-boot-splash.service 5 | -------------------------------------------------------------------------------- /dracut/customization/eos-install-custom-boot-splash: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CUSTOM_SPLASH_DIR="/eos-customization/plymouth" 4 | CUSTOM_SPLASH_CONFIG="${CUSTOM_SPLASH_DIR}/plymouthd.defaults" 5 | PLYMOUTH_RUNTIME_DIR="/run/plymouth" 6 | 7 | if [ ! -d ${CUSTOM_SPLASH_DIR} ] ; then 8 | echo "${CUSTOM_SPLASH_DIR} not found" >&2 9 | exit 1 10 | fi 11 | 12 | if [ ! -f ${CUSTOM_SPLASH_CONFIG} ] ; then 13 | echo "${CUSTOM_SPLASH_CONFIG} not found" >&2 14 | exit 1 15 | fi 16 | 17 | theme_name="`sed -n 's/Theme=\(.*\)/\1/p' ${CUSTOM_SPLASH_CONFIG}`" 18 | if [ -z ${theme_name} ]; then 19 | echo "Theme entry missing from ${CUSTOM_SPLASH_CONFIG}" >&2 20 | exit 1 21 | fi 22 | 23 | if [ ! -d ${CUSTOM_SPLASH_DIR}/themes/${theme_name} ] ; then 24 | echo "${CUSTOM_SPLASH_DIR}/themes/${theme_name} not found" >&2 25 | exit 1 26 | fi 27 | 28 | mkdir -p ${PLYMOUTH_RUNTIME_DIR} 29 | cp ${CUSTOM_SPLASH_CONFIG} ${PLYMOUTH_RUNTIME_DIR} 30 | cp -r ${CUSTOM_SPLASH_DIR}/themes ${PLYMOUTH_RUNTIME_DIR} 31 | sed -i "s:ImageDir=.*:ImageDir=${PLYMOUTH_RUNTIME_DIR}/themes/${theme_name}:" \ 32 | ${PLYMOUTH_RUNTIME_DIR}/themes/${theme_name}/${theme_name}.plymouth 33 | -------------------------------------------------------------------------------- /dracut/customization/eos-install-custom-boot-splash.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Install custom splash theme to runtime dir 3 | DefaultDependencies=no 4 | ConditionKernelCommandLine=splash 5 | ConditionPathExists=/etc/initrd-release 6 | ConditionDirectoryNotEmpty=/eos-customization/plymouth 7 | Before=plymouth-start.service 8 | 9 | [Service] 10 | Type=oneshot 11 | ExecStart=/bin/eos-install-custom-boot-splash 12 | -------------------------------------------------------------------------------- /dracut/customization/module-setup.sh: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2017 Endless Mobile, Inc. 2 | # Licensed under the GPLv2 3 | 4 | check() { 5 | return 0 6 | } 7 | 8 | depends() { 9 | echo systemd 10 | } 11 | 12 | install() { 13 | dracut_install sed 14 | inst_script "$moddir/eos-install-custom-boot-splash" \ 15 | /bin/eos-install-custom-boot-splash 16 | inst_simple "$moddir/eos-install-custom-boot-splash.service" \ 17 | "$systemdsystemunitdir/eos-install-custom-boot-splash.service" 18 | mkdir -p "${initdir}/$systemdsystemunitdir/plymouth-start.service.wants" 19 | ln_r "$systemdsystemunitdir/eos-install-custom-boot-splash.service" \ 20 | "$systemdsystemunitdir/plymouth-start.service.wants/eos-install-custom-boot-splash.service" 21 | } 22 | -------------------------------------------------------------------------------- /dracut/endless.conf: -------------------------------------------------------------------------------- 1 | compress="zstd" 2 | 3 | dracutmodules="dracut-systemd systemd-initrd dash drm eos-repartition eos-image-boot plymouth kernel-modules resume ostree systemd base fs-lib eos-customization busybox" 4 | fscks="fsck fsck.ext4" 5 | filesystems="isofs ntfs3 exfat" 6 | 7 | # Embed microcode into ramdisk 8 | early_microcode="yes" 9 | 10 | # Don't include nouveau in the initramfs, so that we can later choose 11 | # between nouveau and nvidia. 12 | # (nouveau cannot be unloaded after it has bound to a device) 13 | omit_drivers="nouveau" 14 | 15 | # Don't include simpledrm in the initramfs to avoid the frame buffer transition 16 | # on some platforms, like RPi 4B, which blocks system during plymouth splash. 17 | # 18 | # We should be able to drop this change with a newer plymouth that includes 19 | # commit 5dedca81 "ply-device-manager: Also ignore SimpleDRM devs in coldplug 20 | # enumeration path". See https://phabricator.endlessm.com/T34648 and 21 | # https://bugzilla.redhat.com/show_bug.cgi?id=2127663. 22 | omit_drivers+=" simpledrm " 23 | 24 | # Ship the bfq IO scheduler module in the initrd, as it's relied upon by 25 | # our udev rules that detect block devices 26 | add_drivers+=" bfq " 27 | 28 | # The initramfs will be distributed by ostree, so it needs to be generic. If 29 | # anyone needs to generate a host specific initramfs, they can pass the 30 | # "--hostonly" option. 31 | hostonly=no 32 | -------------------------------------------------------------------------------- /dracut/image-boot/Makefile.am: -------------------------------------------------------------------------------- 1 | dracutmoddir = $(prefix)/lib/dracut/modules.d/50eos-image-boot 2 | dist_dracutmod_SCRIPTS = \ 3 | eos-image-boot-generator \ 4 | eos-image-boot-setup \ 5 | eos-live-storage-setup \ 6 | module-setup.sh \ 7 | $(NULL) 8 | dist_dracutmod_DATA = \ 9 | eos-image-boot-setup.service \ 10 | $(NULL) 11 | -------------------------------------------------------------------------------- /dracut/image-boot/eos-image-boot-generator: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (C) 2016 Endless Mobile, Inc. 3 | # Licensed under the GPLv2 4 | # 5 | # Support booting from an image file hosted on a filesystem. 6 | # We parse the kernel parameters requesting this functionality and configure 7 | # other systemd units to go about setting up the block devices and mounting 8 | # the target filesystem. 9 | 10 | type getarg >/dev/null 2>&1 || . /lib/dracut-lib.sh 11 | 12 | GENERATOR_DIR="$1" 13 | 14 | image_device=$(getarg endless.image.device=) 15 | image_path=$(getarg endless.image.path=) 16 | 17 | [ -z "${image_device}" -o -z "${image_path}" ] && exit 0 18 | 19 | case "${image_device}" in 20 | UUID=*) 21 | image_device=/dev/disk/by-uuid/${image_device#UUID=} 22 | ;; 23 | esac 24 | 25 | image_device_escaped=$(systemd-escape -p "${image_device}") 26 | 27 | # When the image device appears, run the setup. 28 | # Note that when booting an ISO using HD emulation (e.g. booting an ISO file 29 | # flashed to USB), udev records that the ISO UUID applies to both the iso9660 30 | # data partition *and* the parent disk node. So it's a little unwise to 31 | # rely on the /dev/disk/by-uuid symlink, but it's good enough as a way 32 | # of ensuring that the overall device has appeared. 33 | # eos-image-boot-setup will then deal with the UUID clash problem. 34 | mkdir -p ${GENERATOR_DIR}/initrd-fs.target.wants 35 | ln -s /lib/systemd/system/eos-image-boot-setup.service \ 36 | ${GENERATOR_DIR}/initrd-fs.target.wants 37 | mkdir -p ${GENERATOR_DIR}/eos-image-boot-setup.service.d 38 | cat <${GENERATOR_DIR}/eos-image-boot-setup.service.d/50-device.conf 39 | [Unit] 40 | Requires=${image_device_escaped}.device 41 | After=${image_device_escaped}.device 42 | EOF 43 | 44 | if getargbool 0 endless.live_boot; then 45 | rwflag=ro 46 | else 47 | rwflag=rw 48 | fi 49 | 50 | # Once the actual EOS device has appeared, use the normal infrastructure 51 | # to fsck and mount it. 52 | cat <${GENERATOR_DIR}/sysroot.mount 53 | [Unit] 54 | Before=initrd-root-fs.target 55 | Requires=systemd-fsck-root.service 56 | After=systemd-fsck-root.service 57 | [Mount] 58 | What=/dev/disk/endless-image3 59 | Where=/sysroot 60 | Options=$rwflag 61 | EOF 62 | 63 | cat <${GENERATOR_DIR}/systemd-fsck-root.service 64 | [Unit] 65 | DefaultDependencies=no 66 | BindsTo=dev-disk-endless\x2dimage3.device 67 | After=initrd-root-device.target local-fs-pre.target 68 | Before=shutdown.target 69 | ConditionKernelCommandLine=!endless.live_boot 70 | [Service] 71 | Type=oneshot 72 | RemainAfterExit=yes 73 | ExecStart=/lib/systemd/systemd-fsck /dev/disk/endless-image3 74 | TimeoutSec=0 75 | EOF 76 | 77 | mkdir -p ${GENERATOR_DIR}/initrd-root-device.target.d 78 | cat <${GENERATOR_DIR}/initrd-root-device.target.d/50-device.conf 79 | [Unit] 80 | Requires=dev-disk-endless\x2dimage3.device 81 | After=dev-disk-endless\x2dimage3.device 82 | EOF 83 | 84 | mkdir -p ${GENERATOR_DIR}/initrd-root-fs.target.requires 85 | ln -s ${GENERATOR_DIR}/sysroot.mount \ 86 | ${GENERATOR_DIR}/initrd-root-fs.target.requires 87 | -------------------------------------------------------------------------------- /dracut/image-boot/eos-image-boot-setup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (C) 2016-2017 Endless Mobile, Inc. 3 | # Licensed under the GPLv2 4 | # 5 | # Support booting from an image file hosted on a filesystem, through the use 6 | # of loopback devices. 7 | 8 | if [ $# -ge 1 ]; then 9 | # For testing 10 | if [ "$1" = "--readonly" ]; then 11 | shift 12 | roflag="-r" 13 | fi 14 | 15 | host_device="$1" 16 | image_path="$2" 17 | else 18 | type getarg >/dev/null 2>&1 || . /lib/dracut-lib.sh 19 | 20 | image_device=$(getarg endless.image.device) 21 | image_path=$(getarg endless.image.path) 22 | 23 | # When booting an ISO using HD emulation (e.g. booting an ISO file flashed 24 | # to USB), udev records that the ISO UUID applies to both the iso9660 25 | # data partition and the parent disk node, so we have to be careful 26 | # when finding host_device. 27 | # 28 | # We wait for udev to finish probing the partitions, and rely on the data 29 | # partition node having been probed after the parent device, hence it 30 | # will win the race to own the by-uuid symlink. 31 | udevadm settle 32 | host_device=/dev/disk/by-uuid/${image_device#UUID=} 33 | if ! [ -b "${host_device}" ]; then 34 | echo "Failed to find host partition device" 35 | exit 1 36 | fi 37 | 38 | # If possible, create partition for persistent data storage 39 | eos-live-storage-setup "${host_device}" 40 | 41 | getargbool 0 endless.live_boot && roflag="-r" 42 | fi 43 | 44 | fstype=$(lsblk --noheadings -o FSTYPE "${host_device}") 45 | if [ $? != 0 ]; then 46 | echo "image-boot: failed to detect filesystem type" 47 | exit 1 48 | fi 49 | if [ "${fstype}" = "ntfs" ]; then 50 | fstype=ntfs3 51 | fi 52 | 53 | case "$image_path" in 54 | *.squash) squashfs=1 ;; 55 | *) squashfs= ;; 56 | esac 57 | 58 | # Identify the EOS image extents on the host device, and create a dm-linear 59 | # block device that maps exactly to that. If the image is a squashfs, then 60 | # first map that, then setup a loopback device for the uncompressed image 61 | # within it. 62 | 63 | # First, map the outermost image file (uncompressed or squashfs) 64 | mkdir -p /outer_image 65 | 66 | # /usr/bin/mount recently became busybox, and it seems to have a hard time 67 | # guessing the filesystem when the isofs kernel module isn't loaded. 68 | # If we specify it manually, it should be ok. 69 | # 70 | # Even with util-linux mount, manually specifying this saves a few 71 | # pointless read operations, so it's probably a good idea anyway. 72 | if ! mount -t $fstype ${host_device} /outer_image; then 73 | echo "Failed to mount ${host_device}" 74 | exit 1 75 | fi 76 | 77 | # Create a loopback device backing the image 78 | image_device=$(losetup -f) 79 | losetup ${roflag} ${image_device} /outer_image/${image_path} 80 | if [ $? != 0 ]; then 81 | echo "losetup failed for /outer_image/${image_path}" 82 | exit 1 83 | fi 84 | 85 | # If $image_path is a squashfs, now create a loopback device for the 86 | # uncompressed image within it 87 | if [ -n "$squashfs" ]; then 88 | # Mount the squashfs 89 | mkdir /squash 90 | if ! mount ${image_device} /squash; then 91 | echo "Failed to mount ${image_device}" 92 | exit 1 93 | fi 94 | 95 | # Create a loopback device backing the endless image 96 | image_device=$(losetup -f) 97 | losetup ${image_device} /squash/endless.img 98 | fi 99 | 100 | if ! partx -a -v "${image_device}"; then 101 | echo "image-boot: failed to probe partitions" 102 | exit 1 103 | fi 104 | 105 | # Setup a stable /dev/disk/endless-image symlink for our image device and 106 | # partitions. 107 | # 108 | # This is used by eos-installer, which doesn't have the knowledge how 109 | # to find the image inside a squashfs, so it uses this path. This also 110 | # requires the device node to have appropriate permissions. 111 | # This path is also used by the rest of the initramfs which is hardcoded 112 | # to find the rootfs at /dev/disk/endless-image3. 113 | image_device_name="${image_device##*/}" 114 | mkdir -p /run/udev/rules.d 115 | cat < /run/udev/rules.d/70-endless-image.rules 116 | SUBSYSTEM=="block", KERNEL=="${image_device_name}", MODE="0664", SYMLINK+="disk/endless-image" 117 | SUBSYSTEM=="block", KERNEL=="${image_device_name}p?", MODE="0664", SYMLINK+="disk/endless-image%n" 118 | EOF 119 | 120 | udevadm control --reload-rules 121 | 122 | # Ideally these rules could be placed before the disk devices appear and they 123 | # would then kick in automatically, but in practice, since there are some 124 | # devices like loop0 which always exist, it is necessary to explicitly trigger. 125 | udevadm trigger "${image_device}" 126 | 127 | exit 0 128 | -------------------------------------------------------------------------------- /dracut/image-boot/eos-image-boot-setup.service: -------------------------------------------------------------------------------- 1 | # This unit is dynamically enabled by endless-image-boot-generator 2 | [Unit] 3 | Description=EndlessOS image boot filesystem setup 4 | DefaultDependencies=no 5 | Before=initrd-switch-root.target initrd-fs.target 6 | ConditionKernelCommandLine=endless.image.device 7 | 8 | [Service] 9 | Type=oneshot 10 | ExecStart=/bin/eos-image-boot-setup 11 | RemainAfterExit=yes 12 | -------------------------------------------------------------------------------- /dracut/image-boot/eos-live-storage-setup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (C) 2019 Endless Mobile, Inc. 3 | # Licensed under the GPLv2 4 | # 5 | # When performing a live boot from an ISO image on a writeable medium, create a 3rd partition filling the remaining 6 | # space on the device. This partition will later be used for persistent data 7 | # storage, so that your documents are kept for next time you boot. 8 | # 9 | # The filesystem is not created here (within the initramfs) as that can be 10 | # deferred until we are running from the real root. We just write a textual 11 | # marker to the start of the partition to indicate that the partition is 12 | # pending filesystem creation. 13 | 14 | image_partition_dev=$(readlink -f "$1") 15 | image_partition_type=$(lsblk -d -n -o TYPE "${image_partition_dev}") 16 | image_partition_sysfs=/sys/class/block/$(lsblk -d -n -o KNAME "${image_partition_dev}") 17 | 18 | if [ "${image_partition_type}" = "rom" ]; then 19 | echo "Skipping storage setup - read only media" 20 | exit 0 21 | fi 22 | 23 | case ${image_partition_dev} in 24 | *1) 25 | # partition 1 - ok 26 | ;; 27 | *) 28 | echo "Skipping storage setup as image is not on partition 1" 29 | exit 0 30 | ;; 31 | esac 32 | 33 | case ${image_partition_dev} in 34 | /dev/loop*p?) 35 | using_loop=1 36 | ;; 37 | esac 38 | 39 | host_disk_dev=/dev/$(lsblk -d -n -o PKNAME "${image_partition_dev}") 40 | host_disk_size=$(blockdev --getsz "${host_disk_dev}") 41 | 42 | # Identify EFI partition, which precedes the partition we will create 43 | efi_partition_dev=${image_partition_dev%?}2 44 | [ -e "${efi_partition_dev}" ] || exit 0 45 | 46 | efi_partition_sysfs=/sys/class/block/$(lsblk -d -n -o KNAME "${efi_partition_dev}") 47 | efi_partition_start=$(cat "${efi_partition_sysfs}"/start) 48 | efi_partition_size=$(blockdev --getsz "${efi_partition_dev}") 49 | [ -z "${efi_partition_start}" ] && exit 1 50 | 51 | # Set up target storage partition 52 | storage_partition_dev=${image_partition_dev%?}3 53 | [ -e "${storage_partition_dev}" ] && exit 0 54 | 55 | # Find a start position after the EFI partition 56 | storage_partition_start=$(( efi_partition_start + efi_partition_size )) 57 | 58 | # Fill the remainder of the disk 59 | storage_partition_size=$(( host_disk_size - storage_partition_start )) 60 | 61 | # Only create a storage partition if we have more than 1GB available, 62 | # otherwise assume that live mode is preferable. 63 | if [ ${storage_partition_size} -lt 2097152 ]; then 64 | echo "Skip partition creation ($storage_partition_size sectors available)" 65 | exit 0 66 | fi 67 | 68 | # Align storage partition start to 1MB boundary 69 | residue=$(( storage_partition_start % 2048 )) 70 | if [ $residue -gt 0 ]; then 71 | storage_partition_start=$(( storage_partition_start + 2048 - residue )) 72 | storage_partition_size=$(( host_disk_size - storage_partition_start )) 73 | fi 74 | 75 | # Just in case we ever want to convert to GPT, reserve 33 sectors at the 76 | # end of the disk, big enough for the secondary GPT header 77 | storage_partition_size=$(( storage_partition_size - 33 )) 78 | 79 | # udev might still be busy probing the disk, meaning that it will be in use. 80 | udevadm settle 81 | 82 | # Apply partition table change 83 | echo "start=${storage_partition_start}" | 84 | sfdisk --no-reread --append "${host_disk_dev}" 85 | ret=$? 86 | echo "sfdisk returned $ret" 87 | udevadm settle 88 | 89 | # Loop devices need a prod for the new partition to show up 90 | if [ -n "${using_loop}" ]; then 91 | partprobe "$host_disk_dev" 92 | udevadm settle 93 | fi 94 | 95 | # write marker bytes to new partition, to trigger filesystem creation 96 | # later during boot 97 | echo "endless_live_storage_marker" > ${storage_partition_dev} 98 | exit 0 99 | -------------------------------------------------------------------------------- /dracut/image-boot/module-setup.sh: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016-2017 Endless Mobile, Inc. 2 | # Licensed under the GPLv2 3 | 4 | check() { 5 | return 0 6 | } 7 | 8 | depends() { 9 | echo systemd dm 10 | } 11 | 12 | install() { 13 | dracut_install blockdev partx lsblk losetup 14 | instmods overlay 15 | inst_rules 60-cdrom_id.rules 16 | inst_script "$moddir"/eos-image-boot-setup /bin/eos-image-boot-setup 17 | inst_script "$moddir"/eos-live-storage-setup /bin/eos-live-storage-setup 18 | inst_simple "$moddir"/eos-image-boot-setup.service \ 19 | "$systemdsystemunitdir"/eos-image-boot-setup.service 20 | mkdir -p "${initdir}${systemdsystemconfdir}/initrd-switch-root.target.wants" 21 | inst_script "$moddir/eos-image-boot-generator" $systemdutildir/system-generators/eos-image-boot-generator 22 | } 23 | -------------------------------------------------------------------------------- /dracut/repartition/Makefile.am: -------------------------------------------------------------------------------- 1 | dracutmoddir = $(prefix)/lib/dracut/modules.d/50eos-repartition 2 | dist_dracutmod_SCRIPTS = module-setup.sh endless-repartition.sh 3 | dist_dracutmod_DATA = endless-repartition.service 4 | -------------------------------------------------------------------------------- /dracut/repartition/endless-repartition.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=EOS repartitioning 3 | ConditionPathExists=/etc/initrd-release 4 | ConditionKernelCommandLine=!endless.live_boot 5 | 6 | # Our position in the boot order is important. Naturally, we need to run 7 | # after the root device is available, before the root fs is mounted. 8 | # We also need to run before fsck: the repartitioning process causes the disk 9 | # device to be reprobed, which would otherwise cause the fsck unit to run 10 | # multiple times. 11 | DefaultDependencies=no 12 | Before=initrd-root-fs.target sysroot.mount systemd-fsck-root.service 13 | After=initrd-root-device.target dracut-pre-mount.service 14 | 15 | [Service] 16 | Type=oneshot 17 | ExecStart=-/bin/endless-repartition 18 | RemainAfterExit=yes 19 | -------------------------------------------------------------------------------- /dracut/repartition/module-setup.sh: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2014 Endless Mobile, Inc. 2 | # Licensed under the GPLv2 3 | 4 | check() { 5 | return 0 6 | } 7 | 8 | depends() { 9 | echo systemd 10 | } 11 | 12 | install() { 13 | dracut_install eos-update-efi-uuid 14 | dracut_install sfdisk 15 | dracut_install blockdev 16 | dracut_install readlink 17 | dracut_install sed 18 | dracut_install tune2fs 19 | dracut_install iconv 20 | dracut_install blkid 21 | dracut_install partx 22 | inst_script "$moddir/endless-repartition.sh" /bin/endless-repartition 23 | inst_simple "$moddir/endless-repartition.service" \ 24 | "$systemdsystemunitdir/endless-repartition.service" 25 | mkdir -p "${initdir}/$systemdsystemunitdir/initrd.target.wants" 26 | ln_r "$systemdsystemunitdir/endless-repartition.service" \ 27 | "$systemdsystemunitdir/initrd.target.wants/endless-repartition.service" 28 | } 29 | -------------------------------------------------------------------------------- /eos-config-journal: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # Copyright (C) 2018 Endless Technologies, Inc. 3 | # Licensed under the GPLv2 4 | 5 | parentdir=/var/log 6 | # if this dir exists, the journal will be persistently stored to it 7 | JOURNALDIR=$parentdir/journal 8 | blockerfile=$parentdir/.no_journal_on_fragile_storage 9 | cmdname=${0##*/} 10 | 11 | # get the device which will hold the journal 12 | device=$(df --output=source $parentdir | tail -n 1) 13 | 14 | # will contain non-empty string if this device is an MMC or SD device (both of 15 | # which are susceptible to damage with excessive writing) 16 | subsystem=$(lsblk --noheading -o SUBSYSTEMS --inverse "$device" | grep '^block:mmc' \ 17 | || true) 18 | 19 | if [ "x$subsystem" != "x" ]; then 20 | echo "$cmdname: not enabling persistent journal: non-durable storage" \ 21 | "(MMC or SD)" 22 | touch $blockerfile 23 | exit 0 24 | fi 25 | 26 | if [ -e $blockerfile ]; then 27 | echo "$cmdname: not enabling persistent journal: $blockerfile exists" 28 | exit 0 29 | fi 30 | 31 | if [ ! -d $JOURNALDIR ]; then 32 | echo "$cmdname: enabling persistent journal on durable storage" 33 | mkdir $JOURNALDIR 34 | systemd-tmpfiles --create --prefix $JOURNALDIR 35 | fi 36 | -------------------------------------------------------------------------------- /eos-config-journal.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=configure persistent journal on durable storage 3 | 4 | # Setup /var/log/journal early after /var is mounted but before 5 | # systemd-journal-flush.service. Make sure journald is started like 6 | # systemd-journal-flush.service does. 7 | DefaultDependencies=no 8 | Conflicts=shutdown.target 9 | Before=shutdown.target 10 | After=systemd-remount-fs.service ostree-remount.service 11 | Before=systemd-journal-flush.service 12 | Wants=systemd-journald.service 13 | After=systemd-journald.service 14 | RequiresMountsFor=/var/log /var/log/journal 15 | 16 | ConditionPathExists=!/var/log/journal 17 | ConditionPathExists=!/var/log/.no_journal_on_fragile_storage 18 | ConditionKernelCommandLine=!endless.live_boot 19 | 20 | [Service] 21 | Type=oneshot 22 | RemainAfterExit=true 23 | ExecStart=/usr/sbin/eos-config-journal 24 | 25 | [Install] 26 | WantedBy=sysinit.target 27 | -------------------------------------------------------------------------------- /eos-enable-zram: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (C) 2018 Endless Mobile, Inc. 3 | # Licensed under the GPLv2 4 | 5 | if [ $# -lt 1 -o "$1" -lt 0 -o "$1" -gt 1 ]; then 6 | echo "Error: 0 or 1 must be specified" >&2 7 | exit 1 8 | fi 9 | 10 | ram_size_kb=$(awk '/MemTotal/{print $2}' /proc/meminfo) 11 | 12 | if [ "$1" -ne 0 ]; then 13 | set -e 14 | if [ ! -e /sys/block/zram0 ]; then 15 | modprobe zram 16 | fi 17 | echo $(($ram_size_kb * 3 / 2))K > /sys/block/zram0/disksize 18 | udevadm settle 19 | # For now, disable the swap partition if in use 20 | swapoff -a 21 | mkswap /dev/zram0 22 | swapon -d /dev/zram0 23 | else 24 | swapoff /dev/zram0 25 | echo 1 > /sys/block/zram0/reset 26 | fi 27 | -------------------------------------------------------------------------------- /eos-enable-zram.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=swap with zram 3 | Before=multi-user.target 4 | After=swap.target 5 | 6 | [Service] 7 | Type=oneshot 8 | RemainAfterExit=true 9 | ExecStart=/usr/sbin/eos-enable-zram 1 10 | ExecStopPost=/usr/sbin/eos-enable-zram 0 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /eos-firewall-localonly: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # eos-firewall-localonly - simple firewall to restrict host to local networks 4 | # 5 | # Copyright (C) 2017 Endless Mobile, Inc. 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | # this script sets up "localonly" iptables/ip6tables chains which are connected 22 | # to the OUTPUT chain, so that the host can reach typical local network and 23 | # local multicast address ranges, a couple of internet services (DNS and NTP) 24 | # but are otherwise left offline. a NetworkManager dispatcher hook adds 25 | # any other local network routes and looks up the IP addresses for the Endless 26 | # metrics servers too. 27 | 28 | LOCALONLY_CHAIN="localonly" 29 | METRICS_CHAIN="${LOCALONLY_CHAIN}-metrics" 30 | 31 | # deep madness lies in blocking any localhost traffic 32 | allowed_interfaces="lo" 33 | 34 | # allowed v4 ranges: 35 | # - the three usual ranges for private IPs (these should always be handled 36 | # by eos-firewall-localonly-nm, this is just belt & braces) 37 | # - the "link-local autoconf" address range 38 | # - link-local multicast for mDNS etc 39 | # - local broadcast (all 1s) for DHCP etc 40 | allowed_v4_networks="10.0.0.0/8 172.16.0.0/12 169.254.0.0/16 192.168.0.0/16 224.0.0.0/24 255.255.255.255" 41 | 42 | # allowed v6 ranges (IPv6 is so much more elegant!): 43 | # - unique local addresses 44 | # - link-local addresses 45 | # - link-local multicast ranges 46 | allowed_v6_networks="fc00::/7 fe80::/10 ff02::/16" 47 | 48 | # these are the services where we want to talk *outside* the local networks 49 | # so eg DHCP is not here: it should be permitted by the allowed networks 50 | allowed_internet_services="ntp domain" 51 | 52 | # add the localonly chain to OUTPUT 53 | enable() { 54 | for cmd in iptables ip6tables; do 55 | ${cmd} -F OUTPUT 56 | ${cmd} -A OUTPUT -j "${LOCALONLY_CHAIN}" 57 | ${cmd} -P OUTPUT DROP 58 | done 59 | } 60 | 61 | # remove the localonly chain from OUTPUT 62 | disable() { 63 | for cmd in iptables ip6tables; do 64 | ${cmd} -P OUTPUT ACCEPT 65 | ${cmd} -F OUTPUT 66 | done 67 | } 68 | 69 | # initialise the localonly chain 70 | setup() { 71 | for cmd in iptables ip6tables; do 72 | ${cmd} -N ${LOCALONLY_CHAIN} 73 | 74 | for i in ${allowed_interfaces}; do 75 | ${cmd} -A ${LOCALONLY_CHAIN} -o ${i} -j ACCEPT 76 | done 77 | 78 | for p in tcp udp; do 79 | for s in ${allowed_internet_services}; do 80 | ${cmd} -A ${LOCALONLY_CHAIN} -p ${p} --dport ${s} -j ACCEPT 81 | done 82 | done 83 | done 84 | 85 | for n in ${allowed_v4_networks}; do 86 | iptables -A ${LOCALONLY_CHAIN} -d ${n} -j ACCEPT 87 | done 88 | 89 | for n in ${allowed_v6_networks}; do 90 | ip6tables -A ${LOCALONLY_CHAIN} -d ${n} -j ACCEPT 91 | done 92 | 93 | # log anything we're about to DROP, but reset TCP connections for faster app feedback 94 | for cmd in iptables ip6tables; do 95 | ${cmd} -A ${LOCALONLY_CHAIN} -m limit --limit 1/min -j LOG --log-prefix "eos-firewall-localonly: " 96 | ${cmd} -A ${LOCALONLY_CHAIN} -p tcp -j REJECT --reject-with tcp-reset 97 | 98 | ${cmd} -A ${LOCALONLY_CHAIN} -j RETURN 99 | done 100 | } 101 | 102 | remove_localonly_chain() { 103 | local chain="${1}" 104 | 105 | for cmd in iptables ip6tables; do 106 | if ${cmd} -n -L "${chain}" >/dev/null 2>&1; then 107 | # ignore failure in case chain is not present 108 | ${cmd} -D "${LOCALONLY_CHAIN}" -j "${chain}" >/dev/null 2>&1 || true 109 | ${cmd} -F "${chain}" 110 | ${cmd} -X "${chain}" 111 | fi 112 | done 113 | } 114 | 115 | # clear and remove any localonly chains 116 | reset() { 117 | # disable first because otherwise we can't erase our chains 118 | disable 119 | 120 | for iface in $(find /sys/class/net -maxdepth 1 -type l -printf "%f\n"); do 121 | remove_localonly_chain "${LOCALONLY_CHAIN}-${iface}" 122 | done 123 | 124 | remove_localonly_chain "${METRICS_CHAIN}" 125 | 126 | # this will try and remove the localonly chain from itself, but the failure 127 | # is ignored so this saves code 128 | remove_localonly_chain "${LOCALONLY_CHAIN}" 129 | } 130 | 131 | usage() { 132 | cat </dev/null 2>&1; then 42 | exit 0 43 | fi 44 | done 45 | } 46 | 47 | # if the given chain doesn't exist, create it and prepend it to the localonly table 48 | ensure_localonly_chain() { 49 | local chain="${1}" 50 | 51 | for cmd in iptables ip6tables; do 52 | if ! ${cmd} -n -L "${chain}" >/dev/null 2>&1; then 53 | ${cmd} -N "${chain}" 54 | ${cmd} -A "${chain}" -j RETURN 55 | ${cmd} -I "${LOCALONLY_CHAIN}" -j "${chain}" 56 | fi 57 | done 58 | } 59 | 60 | # if the given ACCEPT doesn't exist, create it and prepend it to the given table 61 | ensure_accept_rule_v4() { 62 | local chain="${1}" 63 | local dst="${2}" 64 | 65 | if ! iptables -C "${chain}" -d "${dst}" -j ACCEPT >/dev/null 2>&1; then 66 | echo "eos-firewall-localonly-nm: allowing ${dst} on ${chain}" 67 | iptables -I "${chain}" -d "${dst}" -j ACCEPT 68 | fi 69 | } 70 | 71 | # if the given ACCEPT doesn't exist, create it and prepend it to the given table 72 | ensure_accept_rule_v6() { 73 | local chain="${1}" 74 | local dst="${2}" 75 | 76 | if ! ip6tables -C "${chain}" -d "${dst}" -j ACCEPT >/dev/null 2>&1; then 77 | echo "eos-firewall-localonly-nm: allowing ${dst} on ${chain}" 78 | ip6tables -I "${chain}" -d "${dst}" -j ACCEPT 79 | fi 80 | } 81 | 82 | # look up the v4 and v6 addresses for the hosts in METRICS_HOSTS and prepend 83 | # ACCEPT rules to the METRICS_CHAIN. we don't expect the addresses to change 84 | # often, so there isn't any real benefit to the complexity of trying to 85 | # atomically clean / remove the old ones 86 | update_metrics() { 87 | ensure_localonly_chain "${METRICS_CHAIN}" 88 | 89 | for addr in $(getent ahostsv4 ${METRICS_HOSTS} | cut -f1 -d' ' | sort -u); do 90 | ensure_accept_rule_v4 "${METRICS_CHAIN}" "${addr}" 91 | done 92 | 93 | # same but for v6 - filter out the v4 mapped addresses as these won't occur 94 | # in the v6 output chain - if an IPv6 app is accessing a ::ffff: address, it 95 | # will leave the host via the v4 stack 96 | for addr in $(getent ahostsv6 ${METRICS_HOSTS} | cut -f1 -d' ' | sort -u | grep -v ^::ffff:); do 97 | ensure_accept_rule_v6 "${METRICS_CHAIN}" "${addr}" 98 | done 99 | } 100 | 101 | # iterate the NetworkManager provided address/subnet and route/subnet variables 102 | # add ACCEPT rules for any missing ones to a chain named after the interface 103 | add_rules_v4() { 104 | local chain="${1}" 105 | local num_addrs="${2}" 106 | local num_routes="${3}" 107 | 108 | for ((i=0; i/dev/null 2>&1; then 141 | # ignore failure in case chain is empty 142 | ${cmd} -D "${LOCALONLY_CHAIN}" -j "${chain}" >/dev/null 2>&1 || true 143 | ${cmd} -F "${chain}" 144 | ${cmd} -X "${chain}" 145 | fi 146 | done 147 | } 148 | 149 | check_iptables_loaded 150 | check_for_localonly 151 | 152 | case "${ACTION}" in 153 | connectivity-change) 154 | # not interested 155 | exit 0 156 | ;; 157 | dhcp4-change) 158 | ensure_localonly_chain "${IFACE_CHAIN}" 159 | add_rules_v4 "${IFACE_CHAIN}" "${IP4_NUM_ADDRESSES}" "${IP4_NUM_ROUTES}" 160 | update_metrics 161 | ;; 162 | dhcp6-change) 163 | ensure_localonly_chain "${IFACE_CHAIN}" 164 | add_rules_v6 "${IFACE_CHAIN}" "${IP6_NUM_ADDRESSES}" "${IP6_NUM_ROUTES}" 165 | update_metrics 166 | ;; 167 | down) 168 | remove_localonly_chain "${IFACE_CHAIN}" 169 | ;; 170 | hostname) 171 | # not interested 172 | exit 0 173 | ;; 174 | pre-*) 175 | # not interested 176 | exit 0 177 | ;; 178 | up) 179 | ensure_localonly_chain "${IFACE_CHAIN}" 180 | add_rules_v4 "${IFACE_CHAIN}" "${IP4_NUM_ADDRESSES}" "${IP4_NUM_ROUTES}" 181 | add_rules_v6 "${IFACE_CHAIN}" "${IP6_NUM_ADDRESSES}" "${IP6_NUM_ROUTES}" 182 | update_metrics 183 | ;; 184 | vpn-*) 185 | # VPN interfaces coming and going also cause normal up/down events - these 186 | # events are for VPN-specific actions 187 | exit 0 188 | ;; 189 | *) 190 | echo "eos-firewall-localonly-nm: ignoring unknown action ${1}" 191 | exit 0 192 | ;; 193 | esac 194 | 195 | exit 0 196 | 197 | -------------------------------------------------------------------------------- /eos-firewall-localonly.service: -------------------------------------------------------------------------------- 1 | # Run eos-firewall-localonly before NetworkManager starts 2 | 3 | [Unit] 4 | Description=Endless local-only firewall 5 | Before=NetworkManager.service 6 | 7 | [Service] 8 | Type=oneshot 9 | ExecStart=/usr/sbin/eos-firewall-localonly start 10 | ExecStopPost=/usr/sbin/eos-firewall-localonly stop 11 | RemainAfterExit=true 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /eos-firstboot: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # Copyright (C) 2014 Endless Mobile, Inc. 3 | # Licensed under the GPLv2 4 | 5 | root_part=$(findmnt -rvnf -o SOURCE /) 6 | 7 | resize2fs "${root_part}" 8 | 9 | > /var/lib/eos-firstboot 10 | exit 0 11 | -------------------------------------------------------------------------------- /eos-firstboot.service: -------------------------------------------------------------------------------- 1 | # Run eos-firstboot early during boot 2 | 3 | [Unit] 4 | Description=Endless Boot Helper 5 | DefaultDependencies=no 6 | After=sysinit.target local-fs.target 7 | Before=basic.target 8 | ConditionPathExists=!/var/lib/%N 9 | ConditionKernelCommandLine=!endless.live_boot 10 | 11 | [Service] 12 | ExecStart=/usr/sbin/eos-firstboot 13 | ExecStartPost=-rm -f /var/eos-booted 14 | StandardOutput=journal+console 15 | 16 | [Install] 17 | WantedBy=basic.target 18 | -------------------------------------------------------------------------------- /eos-fix-flatpak-overrides.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Fix incorrect Flatpak override symlink 3 | DefaultDependencies=no 4 | Conflicts=shutdown.target 5 | Wants=local-fs.target 6 | After=local-fs.target 7 | 8 | # Only run on updates 9 | Before=multi-user.target systemd-update-done.service 10 | ConditionNeedsUpdate=|/etc 11 | ConditionNeedsUpdate=|/var 12 | 13 | # Run only if this path is a dangling symbolic link 14 | ConditionPathIsSymbolicLink=/var/lib/flatpak/overrides 15 | ConditionPathExists=!/var/lib/flatpak/overrides 16 | 17 | [Service] 18 | Type=oneshot 19 | ExecStart=rm -f /var/lib/flatpak/overrides 20 | 21 | [Install] 22 | WantedBy=multi-user.target 23 | -------------------------------------------------------------------------------- /eos-live-boot-generator: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Usage: eos-live-boot-generator normal-dir [...] 4 | # 5 | # Conditionally adds additional boot dependencies for live boots. 6 | # This script implements systemd.generator(7). 7 | 8 | dest_dir="${1:?normal-dir argument missing}" 9 | 10 | # Check $proc_cmdline, if set, for testing purposes 11 | if grep -q "\" "${proc_cmdline:-/proc/cmdline}"; then 12 | # Don't try to remount the (read-only) root filesystem read-write 13 | ln -sf /dev/null "$dest_dir/systemd-remount-fs.service" 14 | fi 15 | -------------------------------------------------------------------------------- /eos-live-boot-overlayfs-setup.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Endless live boot overlayfs setup 3 | DefaultDependencies=no 4 | After=ostree-remount.service var.mount 5 | Before=local-fs.target 6 | ConditionKernelCommandLine=endless.live_boot 7 | 8 | # Systemd ships several units that require access to /var but are not ordered 9 | # after local-fs.target. It would be better if they did, but that's not the 10 | # case today. Systemd doesn't expect the kind of mount post-processing that's 11 | # done here. 12 | # 13 | # You can find these with the following command: 14 | # 15 | # grep -rl -e StateDirectory -e CacheDirectory -e LogsDirectory -e var.mount \ 16 | # -e 'RequiresMountsFor=.*/var' /usr/lib/systemd/system \ 17 | # | xargs grep -l -e DefaultDependencies | sort 18 | Before=systemd-backlight@.service 19 | Before=systemd-coredump@.service 20 | Before=systemd-journal-flush.service 21 | Before=systemd-pstore.service 22 | Before=systemd-random-seed.service 23 | Before=systemd-rfkill.service 24 | Before=systemd-rfkill.socket 25 | Before=systemd-timesyncd.service 26 | Before=systemd-update-utmp-runlevel.service 27 | Before=systemd-update-utmp.service 28 | 29 | [Service] 30 | Type=oneshot 31 | ExecStart=/usr/sbin/eos-live-boot-overlayfs-setup 32 | RemainAfterExit=yes 33 | 34 | [Install] 35 | WantedBy=local-fs.target 36 | -------------------------------------------------------------------------------- /eos-migrate-chromium-profile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | # eos-migrate-chromium-profile: move users' Chromium profile to its new home 4 | # 5 | # This script is based on eos-migrate-firefox-profile. 6 | # 7 | # Copyright © 2020 Endless OS Foundation LLC 8 | # 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program; if not, write to the Free Software 21 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | 23 | import fileinput 24 | import os 25 | from gi.repository import GLib 26 | 27 | USER_HOME_DIR = os.path.expanduser("~/") 28 | DESKTOP_SHORTCUTS_DIR = os.path.join(USER_HOME_DIR, ".local", "share", "applications") 29 | OLD_CHROMIUM_CONFIG_DIR = os.path.join(USER_HOME_DIR, ".config", "chromium") 30 | NEW_CHROMIUM_DATA_DIR = os.path.join(USER_HOME_DIR, ".var", "app", 31 | "org.chromium.Chromium") 32 | NEW_CHROMIUM_CONFIG_DIR = os.path.join(NEW_CHROMIUM_DATA_DIR, "config", "chromium") 33 | 34 | OLD_WIDEVINE_SYSTEM_DIR = os.path.join(os.path.sep, "usr", "lib", 35 | "chromium-browser", "WidevineCdm") 36 | 37 | MIMEAPPS_LIST = os.path.join(GLib.get_user_config_dir(), "mimeapps.list") 38 | MIMEAPPS_GROUPS = [ 39 | "Default Applications", 40 | "Added Associations", 41 | "Removed Associations", 42 | ] 43 | OLD_DESKTOP_FILE = "chromium-browser.desktop" 44 | NEW_DESKTOP_FILE = "org.chromium.Chromium.desktop" 45 | 46 | 47 | def update_mimeapps_list(path): 48 | """Update file associations, which in particular includes x-scheme-handler/http and 49 | friends to specify the default web browser. 50 | 51 | We cannot use GLib's own API to query what mime types the old Chromium desktop file 52 | is a handler for, because we can't construct a GDesktopAppInfo for it, because its 53 | desktop file no longer exists. 54 | """ 55 | 56 | keyfile = GLib.KeyFile() 57 | try: 58 | keyfile.load_from_file( 59 | path, GLib.KeyFileFlags.KEEP_COMMENTS | GLib.KeyFileFlags.KEEP_TRANSLATIONS, 60 | ) 61 | except GLib.GError as gerror: 62 | if gerror.matches(GLib.file_error_quark(), GLib.FileError.NOENT): 63 | return 64 | 65 | raise 66 | 67 | changed = False 68 | 69 | for group in MIMEAPPS_GROUPS: 70 | if not keyfile.has_group(group): 71 | continue 72 | 73 | keys, _length = keyfile.get_keys(group) 74 | for key in keys: 75 | values = keyfile.get_string_list(group, key) 76 | try: 77 | i = values.index(OLD_DESKTOP_FILE) 78 | except ValueError: 79 | pass 80 | else: 81 | values[i] = NEW_DESKTOP_FILE 82 | keyfile.set_string_list(group, key, values) 83 | changed = True 84 | 85 | if changed: 86 | keyfile.save_to_file(path) 87 | 88 | 89 | def update_desktop_shortcut(path): 90 | keyfile = GLib.KeyFile() 91 | print("path: " + path) 92 | keyfile.load_from_file(path, GLib.KeyFileFlags.NONE) 93 | 94 | group = "Desktop Entry" 95 | if not keyfile.has_group(group): 96 | return 97 | 98 | exec_cmd = keyfile.get_string(group, "Exec") 99 | if not exec_cmd.startswith("/usr/bin/chromium-browser"): 100 | return 101 | 102 | keyfile.set_string(group, "Exec", 103 | "/usr/bin/flatpak run org.chromium.Chromium" + 104 | exec_cmd[len("/usr/bin/chromium-browser"):]) 105 | keyfile.set_string(group, "X-Flatpak-Part-Of", "org.chromium.Chromium") 106 | keyfile.save_to_file(path) 107 | 108 | 109 | def update_desktop_shortcuts(path=DESKTOP_SHORTCUTS_DIR): 110 | try: 111 | shortcuts = os.listdir(path) 112 | except FileNotFoundError: 113 | return 114 | 115 | for filename in shortcuts: 116 | if filename.endswith(".desktop"): 117 | update_desktop_shortcut(os.path.join(path, filename)) 118 | 119 | 120 | def update_old_config_references(path): 121 | if not os.path.isfile(path): 122 | return 123 | 124 | for line in fileinput.input(path, inplace=True): 125 | line = line.replace(OLD_CHROMIUM_CONFIG_DIR, NEW_CHROMIUM_CONFIG_DIR) 126 | line = line.replace(OLD_WIDEVINE_SYSTEM_DIR, "") 127 | print(line, end='') 128 | 129 | 130 | def main(): 131 | migrated_file = os.path.join(NEW_CHROMIUM_DATA_DIR, ".migrated") 132 | if os.path.exists(migrated_file): 133 | return 134 | 135 | update_mimeapps_list(MIMEAPPS_LIST) 136 | update_desktop_shortcuts() 137 | 138 | if ( 139 | os.path.isdir(OLD_CHROMIUM_CONFIG_DIR) and 140 | not os.path.isdir(NEW_CHROMIUM_CONFIG_DIR) 141 | ): 142 | update_old_config_references( 143 | os.path.join(OLD_CHROMIUM_CONFIG_DIR, 144 | "WidevineCdm", "latest-component-updated-widevine-cdm")) 145 | os.makedirs(os.path.dirname(NEW_CHROMIUM_CONFIG_DIR), exist_ok=True) 146 | os.rename(OLD_CHROMIUM_CONFIG_DIR, NEW_CHROMIUM_CONFIG_DIR) 147 | 148 | os.makedirs(NEW_CHROMIUM_DATA_DIR, exist_ok=True) 149 | 150 | os.mknod(migrated_file) 151 | 152 | 153 | if __name__ == "__main__": 154 | main() 155 | -------------------------------------------------------------------------------- /eos-migrate-chromium-profile.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Migrate Chromium profile to new path 3 | 4 | ConditionPathExists=!%h/.var/app/org.chromium.Chromium/.migrated 5 | 6 | [Service] 7 | Type=oneshot 8 | ExecStart=/usr/lib/eos-boot-helper/eos-migrate-chromium-profile 9 | Restart=no 10 | RemainAfterExit=yes 11 | 12 | [Install] 13 | WantedBy=gnome-session.target 14 | -------------------------------------------------------------------------------- /eos-migrate-firefox-profile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | # eos-migrate-firefox-profile: move users' Firefox profile to its new home 4 | # 5 | # Copyright © 2020 Endless OS LLC 6 | # Authors: 7 | # Will Thompson 8 | # 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program; if not, write to the Free Software 21 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 22 | # 23 | # 24 | # Endless's Firefox Flatpak stored its data in the real ~/.mozilla in the user's 25 | # home directory. Mozilla's Firefox Flatpak stores its data in what it thinks is 26 | # ~/.mozilla, but which is actually ~/.var/app/$APP_ID/.mozilla through the magic of 27 | # --persist=.mozilla. 28 | # 29 | # eos-update-flatpak-repos adjusts our Flatpak's permissions to match Mozilla's more 30 | # restrictive set. This script moves the actual user data. 31 | import os 32 | from gi.repository import GLib 33 | 34 | APP_DATA_DIR = os.path.expanduser("~/.var/app/org.mozilla.Firefox") 35 | APP_DATA_DIR_DOT_MOZILLA = os.path.join(APP_DATA_DIR, ".mozilla") 36 | HOME_DOT_MOZILLA = os.path.expanduser("~/.mozilla") 37 | PROFILES_INI = os.path.join(HOME_DOT_MOZILLA, "firefox", "profiles.ini") 38 | 39 | ENDLESS_FLATPAK_INSTALL_GROUP = "InstallFF6C2EBF42BF07E5" 40 | MOZILLA_FLATPAK_INSTALL_GROUP = "InstallCF146F38BCAB2D21" 41 | 42 | MIMEAPPS_LIST = os.path.join(GLib.get_user_config_dir(), "mimeapps.list") 43 | MIMEAPPS_GROUPS = [ 44 | "Default Applications", 45 | "Added Associations", 46 | "Removed Associations", 47 | ] 48 | OLD_DESKTOP_FILE = "org.mozilla.Firefox.desktop" 49 | NEW_DESKTOP_FILE = "org.mozilla.firefox.desktop" 50 | 51 | 52 | def update_mimeapps_list(path): 53 | """Update file associations, which in particular includes x-scheme-handler/http and 54 | friends to specify the default web browser. 55 | 56 | Strictly speaking, a user may have had the old Firefox specified as their default 57 | browser without ever having launched it (in which case this script would not be 58 | run); in practice, this seems vanishingly unlikely. 59 | 60 | We cannot use GLib's own API to query what mime types the old Firefox desktop file 61 | is a handler for, because we can't construct a GDesktopAppInfo for it, because its 62 | desktop file no longer exists. 63 | """ 64 | 65 | keyfile = GLib.KeyFile() 66 | try: 67 | keyfile.load_from_file( 68 | path, GLib.KeyFileFlags.KEEP_COMMENTS | GLib.KeyFileFlags.KEEP_TRANSLATIONS, 69 | ) 70 | except GLib.GError as gerror: 71 | if gerror.matches(GLib.file_error_quark(), GLib.FileError.NOENT): 72 | return 73 | 74 | raise 75 | 76 | changed = False 77 | 78 | for group in MIMEAPPS_GROUPS: 79 | if not keyfile.has_group(group): 80 | continue 81 | 82 | keys, _length = keyfile.get_keys(group) 83 | for key in keys: 84 | values = keyfile.get_string_list(group, key) 85 | try: 86 | i = values.index(OLD_DESKTOP_FILE) 87 | except ValueError: 88 | pass 89 | else: 90 | values[i] = NEW_DESKTOP_FILE 91 | keyfile.set_string_list(group, key, values) 92 | changed = True 93 | 94 | if changed: 95 | keyfile.save_to_file(path) 96 | 97 | 98 | def copy_install_section(path): 99 | """The default profile is keyed by a hash of the Firefox executable's installed 100 | path. Endless's Firefox Flatpak has the executable at a different path to Mozilla's. 101 | Copy the Endless installation's data to the group for Mozilla's.""" 102 | keyfile = GLib.KeyFile() 103 | keyfile.load_from_file( 104 | path, GLib.KeyFileFlags.KEEP_COMMENTS | GLib.KeyFileFlags.KEEP_TRANSLATIONS, 105 | ) 106 | 107 | has_mozilla_group = keyfile.has_group(MOZILLA_FLATPAK_INSTALL_GROUP) 108 | has_endless_group = keyfile.has_group(ENDLESS_FLATPAK_INSTALL_GROUP) 109 | 110 | if not has_mozilla_group and has_endless_group: 111 | keys, _length = keyfile.get_keys(ENDLESS_FLATPAK_INSTALL_GROUP) 112 | for key in keys: 113 | value = keyfile.get_string(ENDLESS_FLATPAK_INSTALL_GROUP, key) 114 | keyfile.set_string(MOZILLA_FLATPAK_INSTALL_GROUP, key, value) 115 | 116 | # Uses g_file_set_contents(), is reasonably atomic 117 | keyfile.save_to_file(path) 118 | 119 | 120 | def main(): 121 | if ( 122 | os.path.isdir(APP_DATA_DIR) 123 | and not os.path.isdir(APP_DATA_DIR_DOT_MOZILLA) 124 | and os.path.isdir(HOME_DOT_MOZILLA) 125 | ): 126 | update_mimeapps_list(MIMEAPPS_LIST) 127 | copy_install_section(PROFILES_INI) 128 | os.rename(HOME_DOT_MOZILLA, APP_DATA_DIR_DOT_MOZILLA) 129 | 130 | 131 | if __name__ == "__main__": 132 | main() 133 | -------------------------------------------------------------------------------- /eos-migrate-firefox-profile.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Migrate Firefox profile to new path 3 | 4 | ConditionPathIsDirectory=%h/.mozilla 5 | ConditionPathIsDirectory=%h/.var/app/org.mozilla.Firefox 6 | ConditionPathIsDirectory=!%h/.var/app/org.mozilla.Firefox/.mozilla 7 | 8 | [Service] 9 | Type=oneshot 10 | ExecStart=/usr/lib/eos-boot-helper/eos-migrate-firefox-profile 11 | Restart=no 12 | RemainAfterExit=yes 13 | 14 | [Install] 15 | WantedBy=gnome-session.target 16 | -------------------------------------------------------------------------------- /eos-migrate-shotwell.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Migrate Shotwell database to new flatpak path 3 | 4 | # Only run if we have something to migrate and a flatpak db does not exist 5 | ConditionPathExists=%h/.local/share/shotwell/data/photo.db 6 | ConditionPathExists=!%h/.var/app/org.gnome.Shotwell/data/shotwell/data/photo.db 7 | 8 | [Service] 9 | Type=oneshot 10 | ExecStartPre=/usr/bin/mkdir -p %h/.var/app/org.gnome.Shotwell/data/shotwell/data/ 11 | ExecStart=/usr/bin/cp %h/.local/share/shotwell/data/photo.db %h/.var/app/org.gnome.Shotwell/data/shotwell/data/photo.db 12 | Restart=no 13 | RemainAfterExit=yes 14 | 15 | [Install] 16 | WantedBy=gnome-session.target 17 | -------------------------------------------------------------------------------- /eos-repartition-mbr: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # Copyright (C) 2016-2017 Endless Mobile, Inc. 3 | # Licensed under the GPLv2 4 | 5 | # arguments checking 6 | if [ $# -lt 1 ]; then 7 | echo "Usage: $0 " >&2 8 | exit 1 9 | fi 10 | 11 | root_disk="$1" 12 | 13 | # udev might still be busy probing the disk, meaning that it will be in use. 14 | udevadm settle 15 | 16 | # take current partition table 17 | parts=$(sfdisk -d "$root_disk") 18 | 19 | check_partition_exists() { 20 | local name=${1:?No name supplied to ${FUNCNAME[0]}} 21 | local guid=${2:?No GUID supplied to ${FUNCNAME[0]}} 22 | 23 | if ! echo "$parts" | grep -iq "type=$guid"; then 24 | echo "$0: $name partition not found" >&2 25 | exit 1 26 | fi 27 | } 28 | 29 | esp_guid="C12A7328-F81F-11D2-BA4B-00A0C93EC93B" 30 | bios_boot_guid="21686148-6449-6E6F-744E-656564454649" 31 | dps_root_guid="4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709" 32 | 33 | check_partition_exists "ESP" "$esp_guid" 34 | check_partition_exists "BIOS boot" "$bios_boot_guid" 35 | check_partition_exists "Endless OS root" "$dps_root_guid" 36 | # If any other partitions exist, the call to sfdisk with the modified table 37 | # will fail due to type=UUid being invalid with 'label: dos' 38 | 39 | # “See if you can use ${variable//search/replace} instead.” For some of these, 40 | # we cannot. 41 | # shellcheck disable=SC2001 42 | { 43 | # Reset partition indexes 44 | parts=$(echo "$parts" | sed -e "s/^\/[^: ]\+//") 45 | 46 | # GPT -> DOS 47 | parts=$(echo "$parts" | sed -e "s/label: gpt/label: dos/") 48 | 49 | # Set ESP partition type to EF 50 | parts=$(echo "$parts" | sed -e "s/, type=$esp_guid/, type=EF/") 51 | # Remove BIOS boot partition (but leave its former contents intact) 52 | parts=$(echo "$parts" | grep -v "$bios_boot_guid") 53 | # Set MBR partition type on root partition, and mark as bootable (not strictly 54 | # true, but our MBR does not care - some BIOSes don't consider a drive to be 55 | # bootable without it) 56 | parts=$(echo "$parts" | sed -e "s/, type=$dps_root_guid/, type=83, bootable/") 57 | # Remove partition UUIDs and GPT attributes 58 | parts=$(echo "$parts" | sed -e "s/, \(uuid\|attrs\)=[^\s,]\+//g") 59 | } 60 | 61 | echo "$parts" | sfdisk --force --no-reread "$root_disk" 62 | udevadm settle 63 | partprobe "${root_disk}" 64 | udevadm settle 65 | 66 | # Set marker on 4th partition so that the partition gets enlarged to fill the 67 | # disk on first boot 68 | printf "\xdd" | dd of="$root_disk" bs=1 count=1 seek=498 conv=notrunc 69 | udevadm settle 70 | -------------------------------------------------------------------------------- /eos-test-mode: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # eos-test-mode - Setup system for non-persistent testing. 4 | # Copyright (C) 2015 Dan Nicholson 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License along 17 | # with this program; if not, write to the Free Software Foundation, Inc., 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | # Check to see if we've run eos-live-boot-overlayfs-setup already 21 | [ -d /run/eos-live ] && exit 0 22 | 23 | # Mount overlays over any directory that might be written to 24 | eos-live-boot-overlayfs-setup 25 | 26 | # Disable the updater for this boot 27 | systemctl mask --runtime --now eos-autoupdater.timer eos-autoupdater.service 28 | 29 | # Disable phoning home for this boot, too 30 | systemctl mask --runtime --now eos-phone-home.{service,timer,path} 31 | -------------------------------------------------------------------------------- /eos-transient-setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # eos-transient-setup – configures the system for transient sessions 3 | # Copyright (C) 2016-2018 Endless Mobile, Ltd. 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License along 16 | # with this program; if not, write to the Free Software Foundation, Inc., 17 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | 19 | import argparse 20 | import glob 21 | import json 22 | import logging 23 | import os 24 | import subprocess 25 | import sys 26 | import tempfile 27 | 28 | import gi 29 | gi.require_version('OSTree', '1.0') 30 | # pylint: disable=wrong-import-position 31 | from gi.repository import GLib, Gio, OSTree # noqa: E402 32 | 33 | log = logging.getLogger(sys.argv[0]) 34 | 35 | EOS_INSTALLER = 'com.endlessm.Installer.desktop' 36 | EOS_INSTALLER_PATH = os.path.join('/usr/share/applications', EOS_INSTALLER) 37 | LOCAL_APPS_DIR = '/usr/local/share/applications' 38 | LOCAL_DESKTOP_PATH = os.path.join(LOCAL_APPS_DIR, EOS_INSTALLER) 39 | 40 | LIVE_SETTINGS_DB = '/var/lib/eos-image-defaults/settings.live' 41 | USER_PROFILE_PATH = '/usr/local/share/dconf/profile/user' 42 | USER_PROFILE = '''user-db:user 43 | file-db:/var/lib/eos-image-defaults/settings.live 44 | file-db:/var/lib/eos-image-defaults/settings 45 | file-db:/usr/share/eos-default-settings/settings 46 | ''' 47 | 48 | SHELL_SCHEMA = 'org.gnome.shell' 49 | FAVORITE_APPS_KEY = 'favorite-apps' 50 | 51 | GS_SCHEMA = 'org.gnome.software' 52 | ALLOW_UPDATES = 'allow-updates' 53 | 54 | 55 | class AdjustGSettings: 56 | def __init__(self): 57 | self.keyfile = GLib.KeyFile() 58 | 59 | def update(self, schema, key, variant): 60 | """Stages 'variant' as the new value for 'key' in 'schema'.""" 61 | value = variant.print_(False) 62 | log.info('Updating %s: %s to %s', schema, key, value) 63 | self.keyfile.set_string(schema.replace('.', '/'), key, value) 64 | 65 | def prepare(self): 66 | """Stage all settings to be overridden.""" 67 | self.update_favorite_apps() 68 | self.disallow_app_center_updates() 69 | 70 | def write_dconf_compile(self): 71 | """Write dconf database with overridden settings. 72 | 73 | We also adjust the 'user' profile to use it. 74 | """ 75 | with tempfile.TemporaryDirectory(suffix='.d') as tempdir: 76 | keyfile_path = os.path.join(tempdir, '00-live') 77 | log.info('writing keyfile to %s', keyfile_path) 78 | self.keyfile.save_to_file(keyfile_path) 79 | 80 | os.makedirs(os.path.dirname(LIVE_SETTINGS_DB), exist_ok=True) 81 | 82 | args = ['dconf', 'compile', LIVE_SETTINGS_DB, tempdir] 83 | log.info('$ %s', ' '.join(args)) 84 | subprocess.check_call(args) 85 | 86 | log.info('Installing new DConf profile to %s', USER_PROFILE_PATH) 87 | os.makedirs(os.path.dirname(USER_PROFILE_PATH), exist_ok=True) 88 | with open(USER_PROFILE_PATH, 'w') as user_profile_file: 89 | user_profile_file.write(USER_PROFILE) 90 | 91 | def write_stdout(self): 92 | """Write keyfile with overridden settings to stdout, for debugging.""" 93 | data, _ = self.keyfile.to_data() 94 | print(data) 95 | 96 | def update_favorite_apps(self): 97 | """Adjust default favourite apps, which are shown on the taskbar.""" 98 | settings = Gio.Settings(schema=SHELL_SCHEMA) 99 | favorite_apps = settings.get_strv(FAVORITE_APPS_KEY) 100 | 101 | # Prepend installer icon 102 | if EOS_INSTALLER not in favorite_apps: 103 | favorite_apps.insert(0, EOS_INSTALLER) 104 | 105 | self.update(SHELL_SCHEMA, FAVORITE_APPS_KEY, 106 | GLib.Variant('as', favorite_apps)) 107 | 108 | def disallow_app_center_updates(self): 109 | """Forbid installing app updates.""" 110 | self.update(GS_SCHEMA, ALLOW_UPDATES, GLib.Variant('b', False)) 111 | 112 | 113 | def install_installer_desktop_file(): 114 | """Make eos-installer visible in user sessions. 115 | 116 | eos-installer is shipped in all images, but its desktop file contains 117 | NoDisplay=true. Make a copy with this setting removed so it can be added to 118 | the desktop and taskbar and found via search. 119 | """ 120 | log.info('Copying %s to %s with NoDisplay removed', 121 | EOS_INSTALLER_PATH, LOCAL_DESKTOP_PATH) 122 | os.makedirs(LOCAL_APPS_DIR, exist_ok=True) 123 | 124 | eos_installer_desktop = GLib.KeyFile() 125 | eos_installer_desktop.load_from_file( 126 | EOS_INSTALLER_PATH, 127 | GLib.KeyFileFlags.KEEP_COMMENTS | GLib.KeyFileFlags.KEEP_TRANSLATIONS, 128 | ) 129 | eos_installer_desktop.remove_key('Desktop Entry', 'NoDisplay') 130 | eos_installer_desktop.save_to_file(LOCAL_DESKTOP_PATH) 131 | 132 | 133 | def reduce_ostree_min_free_space(): 134 | '''Don't require any free space on disk when installing apps. On live 135 | systems, free space is at most half of physical RAM, and running out is not 136 | a big deal.''' 137 | repo = OSTree.Repo.new_default() 138 | repo.open() 139 | config = repo.copy_config() 140 | # -size alone takes precedence but set both for clarity. 141 | config.set_string('core', 'min-free-space-size', '0MB') 142 | config.set_integer('core', 'min-free-space-percent', 0) 143 | repo.write_config(config) 144 | 145 | 146 | def main(): 147 | """Configures system settings for live sessions.""" 148 | parser = argparse.ArgumentParser(description=main.__doc__) 149 | parser.add_argument( 150 | "--dry-run", 151 | action="store_true", 152 | help="Just print the DConf keyfile to stdout", 153 | ) 154 | args = parser.parse_args() 155 | 156 | logging.basicConfig( 157 | level=logging.INFO, 158 | format='%(name)s:%(lineno)-3s %(funcName)20s %(levelname)7s: ' 159 | '%(message)s') 160 | 161 | setup = AdjustGSettings() 162 | setup.prepare() 163 | 164 | if args.dry_run: 165 | setup.write_stdout() 166 | else: 167 | setup.write_dconf_compile() 168 | 169 | reduce_ostree_min_free_space() 170 | install_installer_desktop_file() 171 | 172 | 173 | if __name__ == '__main__': 174 | main() 175 | -------------------------------------------------------------------------------- /eos-transient-setup.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Adjust desktop settings for live boot 3 | After=eos-live-boot-overlayfs-setup.service 4 | Before=display-manager.service 5 | ConditionKernelCommandLine=endless.live_boot 6 | 7 | [Service] 8 | Type=oneshot 9 | ExecStart=/usr/sbin/eos-transient-setup 10 | RemainAfterExit=yes 11 | 12 | [Install] 13 | WantedBy=graphical.target 14 | -------------------------------------------------------------------------------- /eos-update-flatpak-repos.service: -------------------------------------------------------------------------------- 1 | # Clean up the flatpak repositories after an OS upgrade 2 | # so that existing users match our current default configuration 3 | 4 | [Unit] 5 | Description=Configure default flatpak repositories 6 | DefaultDependencies=no 7 | Conflicts=shutdown.target 8 | Wants=local-fs.target 9 | After=local-fs.target 10 | 11 | # Only run on updates 12 | Before=multi-user.target systemd-update-done.service 13 | ConditionNeedsUpdate=|/etc 14 | ConditionNeedsUpdate=|/var 15 | 16 | # Try to run before any ostree and flatpak clients so that they all see 17 | # the updated repo configuration 18 | Before=eos-autoupdater.service eos-updater.service 19 | Before=eos-updater-avahi.service eos-update-server.service 20 | Before=eos-updater-flatpak-installer.service 21 | 22 | [Service] 23 | Type=oneshot 24 | RemainAfterExit=true 25 | ExecStart=/usr/sbin/eos-update-flatpak-repos 26 | 27 | # Flatpak checks parental controls at deploy time. In order to do this, it 28 | # needs to talk to accountsservice on the system bus, neither of which are 29 | # running when this job runs. 30 | Environment=FLATPAK_SKIP_PARENTAL_CONTROLS_NO_SYSTEM_BUS=1 31 | 32 | [Install] 33 | WantedBy=multi-user.target 34 | -------------------------------------------------------------------------------- /eos-update-system-ca.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Update system CA store 3 | 4 | # Only run on updates 5 | DefaultDependencies=no 6 | Conflicts=shutdown.target 7 | Wants=local-fs.target 8 | After=local-fs.target 9 | Before=multi-user.target systemd-update-done.service 10 | ConditionNeedsUpdate=|/etc 11 | 12 | [Service] 13 | Type=oneshot 14 | RemainAfterExit=true 15 | ExecStart=/usr/sbin/update-ca-certificates 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /eos-vm-generator: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Usage: eos-vm-generator normal-dir [...] 4 | # 5 | # Conditionally adds additional boot dependencies for vm boots. 6 | # This script implements systemd.generator(7). 7 | 8 | dest_dir="${1:?normal-dir argument missing}" 9 | system_dir=/usr/lib/systemd/system 10 | multi_user_target_wants_dir="$dest_dir/multi-user.target.wants" 11 | 12 | # Only run on virtualbox 13 | vm_string=$(/usr/bin/systemd-detect-virt) 14 | if [ "$vm_string" != "oracle" ] ; then 15 | exit 0 16 | fi 17 | 18 | unit_path="$system_dir/virtualbox-guest-utils.service" 19 | mkdir -p "$multi_user_target_wants_dir" 20 | ln -sf "$unit_path" "$multi_user_target_wants_dir/" 21 | -------------------------------------------------------------------------------- /factory-reset/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_sbin_SCRIPTS = \ 2 | eos-factory-reset \ 3 | $(NULL) 4 | 5 | dist_systemdunit_DATA = \ 6 | eos-factory-reset-users.service \ 7 | $(NULL) 8 | 9 | eosfactoryresetdir = $(libexecdir)/eos-factory-reset 10 | dist_eosfactoryreset_SCRIPTS = \ 11 | eos-factory-reset-users \ 12 | $(NULL) 13 | -------------------------------------------------------------------------------- /factory-reset/eos-factory-reset: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import argparse 3 | import logging 4 | import subprocess 5 | import sys 6 | 7 | from systemd import journal 8 | 9 | # Options list 10 | # Each entry has the form (short name, long name, description) 11 | OPTS = [ 12 | ("D", "disable", "fully remove a scheduled factory reset"), 13 | ] 14 | 15 | # Stages dictionary 16 | # Each entry has the form {name: description} 17 | # When creating a new stage make sure to check both STAGES and OPTS so names 18 | # don't clash, as they both create optional command line arguments. 19 | STAGES = { 20 | "users": "remove all user accounts (and home directories)", 21 | } 22 | 23 | 24 | def yes_or_no(question): 25 | answer = "" 26 | while answer not in ["y", "n"]: 27 | answer = input(f"{question} ").lower() 28 | return answer == "y" 29 | 30 | 31 | def toggle_stage(logger, stage, action): 32 | unit = f"eos-factory-reset-{stage}.service" 33 | cmd = ["systemctl", "--quiet", action, unit] 34 | sp = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 35 | status, msg = sp.returncode, sp.stdout.decode().rstrip("\n") 36 | if status != 0: 37 | logger.error(f"Failed to {action} stage '{stage}': {msg}") 38 | return status, msg 39 | 40 | 41 | if __name__ == "__main__": 42 | desc = "reboot the machine and perform a factory reset." 43 | 44 | logger = logging.getLogger(__name__) 45 | logger.propagate = False 46 | logger.setLevel(logging.INFO) 47 | logger.addHandler(logging.StreamHandler()) 48 | logger.addHandler(journal.JournalHandler()) 49 | 50 | parser = argparse.ArgumentParser(description=desc, 51 | argument_default=argparse.SUPPRESS) 52 | for n, name, desc in OPTS: 53 | parser.add_argument(f"-{n}", f"--{name}", help=desc, action="store_true") 54 | 55 | group = parser.add_argument_group("stages to be performed on the next boot", 56 | "if no stage is selected all stages will" 57 | " be scheduled") 58 | for name, desc in STAGES.items(): 59 | group.add_argument(f"--{name}", help=desc, dest="stages", const=name, 60 | action="append_const") 61 | 62 | args = vars(parser.parse_args()) 63 | disable = args.pop("disable", False) 64 | if disable: 65 | stages = STAGES.keys() 66 | else: 67 | stages = args.pop("stages", STAGES.keys()) 68 | 69 | if not disable: 70 | action = "enable" 71 | warn_msg = [ 72 | f"Selected stages: {' '.join(stages)}", 73 | "WARNING: You are about to schedule IRREVERSIBLE changes to be", 74 | "performed on the next boot. If you do not want to schedule these", 75 | "changes for the next boot say 'n' now.", 76 | ] 77 | success_msg = [ 78 | "A factory reset procedure is scheduled for the next boot.", 79 | "Please reboot the machine to proceed with the reset process.", 80 | f"If this was a mistake, run '{parser.prog} --disable'.", 81 | ] 82 | else: 83 | action = "disable" 84 | warn_msg = [ 85 | "You are about to remove the scheduled factory reset process.", 86 | "Unless scheduled again, a factory reset will NOT be performed.", 87 | ] 88 | success_msg = [ 89 | "A factory reset procedure is not scheduled for the next boot.", 90 | ] 91 | 92 | print(*warn_msg, sep="\n", end="\n\n") 93 | if not yes_or_no("Do you want to proceed [y/n]?"): 94 | logging.shutdown() 95 | sys.exit(0) 96 | print() 97 | 98 | formatted_verb = f"{action[0].upper()}{action[1:-1]}ing" 99 | logger.info(f"{formatted_verb} stages: {' '.join(stages)}") 100 | for stage in stages: 101 | status, msg = toggle_stage(logger, stage, action) 102 | if status != 0: 103 | if not disable: 104 | logger.warning(f"Disabling selected stages: {' '.join(stages)}") 105 | for s in stages: 106 | toggle_stage(logger, s, "disable") 107 | logging.shutdown() 108 | sys.exit(1) 109 | 110 | logging.shutdown() 111 | print() 112 | print(*success_msg, sep="\n") 113 | -------------------------------------------------------------------------------- /factory-reset/eos-factory-reset-users: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ADM_GROUP="sudo" 4 | 5 | if [ -f /etc/adduser.conf ]; then 6 | . /etc/adduser.conf 7 | fi 8 | 9 | first_uid=${FIRST_UID:-1000} 10 | last_uid=${LAST_UID:-59999} 11 | 12 | admin_users=() 13 | nonadmin_users=() 14 | 15 | IFS=':' 16 | while read -r user _ uid _ _ _ _; do 17 | if [[ $uid -ge $first_uid && $uid -le $last_uid ]]; then 18 | /usr/bin/id -Gn $user | grep --word-regexp --quiet $ADM_GROUP 19 | if [[ $? -eq 0 ]] ; then 20 | echo "Found admin user '$user' ($uid)" 21 | admin_users+=($user) 22 | else 23 | echo "Found non-admin user '$user' ($uid)" 24 | nonadmin_users+=($user) 25 | fi 26 | fi 27 | done 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define FBDEV "/dev/fb0" 16 | #define TTYDEV "/dev/tty0" 17 | 18 | int main(int argc, char **argv) { 19 | int ret = 0, fb_fd = -1, tty_fd = -1, tty_mode; 20 | uint8_t *fbp = MAP_FAILED; 21 | size_t screensize, bytes_per_pixel; 22 | struct fb_var_screeninfo vinfo; 23 | 24 | tty_fd = open(TTYDEV, O_RDWR); 25 | if (tty_fd == -1) { 26 | perror("Failed to open " TTYDEV); 27 | ret = errno; 28 | goto exit; 29 | } 30 | 31 | if (ioctl(tty_fd, KDGETMODE, &tty_mode) == -1) { 32 | perror("KDGETMODE failed on " TTYDEV); 33 | ret = errno; 34 | goto exit; 35 | } 36 | 37 | if (tty_mode == KD_TEXT) { 38 | printf("VT is in text mode, exiting"); 39 | goto exit; 40 | } 41 | 42 | fb_fd = open(FBDEV, O_RDWR); 43 | if (fb_fd == -1) { 44 | perror("Failed to open " FBDEV); 45 | ret = errno; 46 | goto exit; 47 | } 48 | 49 | if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo) == -1) { 50 | perror("FBIOGET_VSCREENINFO failed on " FBDEV); 51 | ret = errno; 52 | goto exit; 53 | } 54 | 55 | bytes_per_pixel = vinfo.bits_per_pixel / 8; 56 | screensize = vinfo.xres * vinfo.yres * bytes_per_pixel; 57 | 58 | fbp = mmap(NULL, screensize, PROT_WRITE, MAP_SHARED, fb_fd, (off_t) 0); 59 | if (fbp == MAP_FAILED) { 60 | perror("Failed to mmap " FBDEV); 61 | ret = errno; 62 | goto exit; 63 | } 64 | 65 | memset(fbp, 0, screensize); 66 | printf("Cleared %s", FBDEV); 67 | 68 | exit: 69 | if (fbp != MAP_FAILED) 70 | munmap(fbp, screensize); 71 | 72 | if (fb_fd != -1) 73 | close(fb_fd); 74 | 75 | if (tty_fd != -1) 76 | close(tty_fd); 77 | 78 | return ret; 79 | } 80 | -------------------------------------------------------------------------------- /fallback-fb-setup/fallback-fb-setup.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Setup The Fallback Framebuffer 3 | Requires=display-manager.service 4 | After=display-manager.service 5 | 6 | [Service] 7 | ExecStart=/usr/lib/eos-boot-helper/fallback-fb-setup/fallback-fb-setup 8 | 9 | [Install] 10 | WantedBy=graphical.target 11 | -------------------------------------------------------------------------------- /flatpak-repos/Makefile.am: -------------------------------------------------------------------------------- 1 | flatpakrepodir = $(pkgdatadir)/flatpak-repos 2 | dist_flatpakrepo_DATA = \ 3 | eos-sdk.flatpakrepo \ 4 | flathub.flatpakrepo \ 5 | $(NULL) 6 | 7 | -------------------------------------------------------------------------------- /flatpak-repos/eos-sdk.flatpakrepo: -------------------------------------------------------------------------------- 1 | [Flatpak Repo] 2 | Title=Endless Apps Runtimes 3 | Url=https://ostree.endlessm.com/ostree/eos-sdk 4 | Homepage=https://endlessm.github.io/eos-knowledge-lib/ 5 | Comment=Flatpak runtime for developing apps for Endless OS 6 | Description=Endless apps runtimes are released on a three month schedule and contain the libraries necessary for developing and running offline content apps. They receive minor bug fixing and security updates, and should be considered ABI stable and frozen. 7 | Icon=https://endlessos.com/wp-content/themes/endless/assets/images/logo.svg 8 | DefaultBranch=stable 9 | GPGKey=mQINBFdcTroBEADFMZmOZ3ldxbL729lLOxfAlAHB+ELP0nbhRlV3sLtomUKzPGvFVUnKW7AQ6EcfwXMHx6tTguSKYHiRaMHOB+0+b1Ty+KuYiNGpoBfcedLOws2lok32obrSqqzlGNyXi+m/jqRjefIg1bliQoy2e4Z+UJvBqWHLAy2AEYG9IWUG4vQJ3+ae2VjD7Lh3zJjVDKgxv+cLwoIWsVzWcOZ59YYy3pHaOFx7zC0WV8q+3YTz0+1IT8vkFgN4U4GItMwu7uUna/bmNohz+/zBfbLBFtwAJ0g1/ad3Cm1djNdWLZz4uG+LzvRX/lIfZTpQ3DOUDVCPz3oxhky9iEbMrizdoWizImfEaOlf4OCmW9F1ISGFbBdfYOcppQDNkEN3y8zamgZNy73KUNAa9nFTqjChbIfW58P0qA9yrvFtgPCmjqoKue6VSu0y7vDOugi6OxYMywqCgfqPjEJJ+7GA6IMUowfSKzp+fldvQOsqPVRgK+ED+S31x9LrnfKvSptgxn95/B1VunfhBk27BIh2I0bHwsgpAFmg5KvBpznIMSerV69HvH52pbfAsTu2pWdopVb9M3G9sQ6uGXjUZwlVxl7R5cGsmQf2XCyAm33lUk6vDy14TgSsqgESfO6F0Bd4bZq0Ijyvv6XmAcrnZMeEi5AMUn5Tpdwqxq2Mv66Z5LWF+Jk2XwARAQABtDxFT1MgRmxhdHBhayBTaWduaW5nIEtleSAxIChFRlNLMSkgPG1haW50YWluZXJzQGVuZGxlc3NtLmNvbT6JAj4EEwECACgFAldcTroCGwMFCQlmAYAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEPzxexfx+OFXhJ8P/3J10fbBbK0q+v4udtEj3Jth9K5z9VfBBePNCk5r847CXLTPvS0ZpjqJfFGlPnR87vSR5S6HGU/X264AkovrLFb4Wco7iXH7N8d8I09c1oyl9knvcbHfuiWPTRyd9ZatYdSd8leh7uSJi+e5GGCUQsubMc/cfl8Ob8UBJ50bI6AflLg/LeAJSJZ5O/8hp0yj4MVKVyC8S25yExdWLHnJx98vhGXBTLrvdHUumntZBk3g9WTyoOjmaTRWeIwb3W1Zu0kqadUFAkHNes7XCmaz2eHe2JoudFC9HYVQAxVDm2Lk54w9PqUmsxW6dRylbQwEul0B78yn9dt1ISkHJHkBhxpsIJbfG3AwG6XqWRVfg/+8btiOeX2bPGXWhw3hzm5WDt3Uc3NgEOQdSblNkNiP4fL5Zy0CDqONiveNsxFXfV/JKbv5h/K7mFXqeqI8fiy54PPw1dRIdXqt+nwK259Gkuj+lPeCRt8OGDuEWwD3ATZFi38XEcrt7E3sVe8/JpblFI2+xErCSl3yCpKXF3vH1ElURuIrGSG+GZF/5Ft5Owkd4WQuUfFy/PY62I0O99pF+4v10Ubs4H+zEBsGeepTOUWtbAdhCymq8X37nuqbac733hJTubt90QqsCWKC48A3yss9LplBPEk2P1zrUbsfgJNCbHv71xJlSC4O0e9gaitZiQIcBBABCAAGBQJXXFNaAAoJEAI6RCDH7GkUqVUP/jIXisRZjiEJfTq0//ec5ShvAsXnq8m6udQEHNLhnmjY026cR2H3HdkFOyxgJOIVGJ2cYMv08E7/5Z5AlW8zm52AvO/hqpqdzq0d+ncr1s8zDxBYrfs60leaO3XC0OU6F15e1DjAqqdHCJgMj8L0P+YuqgVLRZztsc2xLyvFp5NWy/Zo2B9TtjFV07RcCP8/kzIkYlU1n7xM+60hObIbua47BSb4mJRKTug0Wl0QOWySI36seH1e471YDnYdz+Hkpq1OXr3u4Oxb07/I6raVGhCrtwuvgSWiaH6L+Y1H9pKworaZidlbin5R3lBIfRVbQ7LyrWZn3SFtBr2pCFUtkYN2mC+CGKajPp2UVPwJwlAeavaftvVz3IrR/hvXfGc56Rkq+Xl2X1hcaPovKsgV42GYNnV7KaW5gZuF5Of16Iwd4ljNJBaZN6pO+/o/e+XA4Z0DuWKPoHByy0vXn1Py9F1yOzbrEnD86svEOg5t8WA7C88gF+9zpsliig4eqV7gw0xghz8ae6i5T5k7vpJhgdL8T4os2dyAHZZFpgzKkA7JK+xL9YYQM7obUwTkivoRL8PS5LKJjWmEUGFwEQ0+s8UxzLcUB6YPkX/EMTuSQ6dwoQms4J8k+qjbWLMjTEtFhFxig3LzuSpVp/fn2sbRF+02DqGHb21Qr+DaMWfLh9rLiQIcBBABAgAGBQJXXzV6AAoJEGgQb330yfyYMqwP/1Sra8/vNyNwlKiMZY64nGKyOi+Za/h60bwpttJq+w9NfjTUhpUrqifHMMX1JF84WwL4jqzKUvWjNcPYUVusclCBhTqdwHUpsAj8Q4xr1qX8qCJcBwdstM+5aGW54H6mqrzZW0mKDqqWOCWtriTaJtwX0iNVX7RSP0zeGiRWHEdj6+gCdSUMxKu0iQ/rlZ6oIxwGFbeqSZO952ikw5jSh1vU80D6kGnvxuaN5ruRWJXJBbr34gKvFoJJ8MJPWKH7cqLBLxZ1ekY0TtXRKj3SWTWnNHw9KEnl12lbeazyRSZq8GrcEtwVDZdXau3+ccz/QrD9EQsZxMyl8Jw9F0l9UjMW3XT5DIZgHOENPyItjc3J+/1tQSrQs6g8bsZev6TU/WT6D+Q0H5f1EL/xbydI6j7fGSkI7Os5WlRt9InTBkhm7BgCVF0VXI23V87YqkKLnBDYirxEeKN04FOOEcS2oTN4r+1pmZfE6Sz+S9q5MDt59r1xZYZ5fAH7LxBVhd8VH+Oy5jTtkH1p8I5VR0XnvsqGjVJqYa6dtqAPg0yxO635dip4AjgIwxb8zP97deINB5Ajb6acByu7fggoRrnUQRfNQw8ckyzfLWJwRi7O3srz7n1eegN0TnvoR3Y/H7vogeOsuyLX3hVU7NcDBdLZ11XRIVoeseq79Q1JpwhLCGz/iQIcBBABAgAGBQJXXzYhAAoJENCXw0DhlsGK3xEP/2NPZHs0V3lOPt5uh3Zz9n/FgTWFISjU7eTUjDNzetO32YIPkp2roamu6zLchK3kkKNkP41J4zDqz4rxiMpwa5sH+gYZrMNHVWXb2ENshAWeZfoDAfNvRM8CDgsQV6Z9paVWbstFIbhSLaZkwilYGAvC4KMOsr4TQkmEkceAwX6Cf9mxlDUvkOulJxpIZKUZyuRZuTChtA37iRbslWjId3xlvGII0tNjXkUtBVwTL9zLDYC2PJ6nohMpVION/nHWJoHnCqu6MhC3/wvdv8YkJmx9TkbwuWiZy3j+WoI9U2P3n6Pf08TJSKQVmo0b7eqOtJe2LtG+ADBkzWrs1wGtn0KQWXBNrcWy6XblBQRooQkt2vR+5/svIpmM/s+GDYa6H3sIZSvPid5ckRTcrI/f/bwD6htevDRy9y3WFaJoIEXpy5VvFwQjtAIm105eeaCb1YvVlV6utxfo3AKCgQKRLcCmV89iC2/QUJKmzgpqZ1xaQ3XnF+J4MxTX0SOUthyLBVBbRimtHikS1Fyq4m68CauDwe9eMsj6hkrA2nD0UwC0ztW/l9moGp7v6hoUGNIZ9qRgB5fggaPCfABzVZMB3jquXvL5iSaeyRbq1pOmFlaQVatgfpGzYn6zL3iFVP3ThtuNQrpEH0DbH+mJ60MX3fpTvHX1IndrArloezIlE7dXuQINBFdcTroBEAC5JABANiR4HtDOC7YTHYexgLe8WR26aQBYO8wYSdbMgJ6ohsh4OWcGI3RjBvW8mF/tnZ3lxYE2o5EAGLn7dFeR29eV8t/gLjFouu8qNUWpCjDj9qO0f4SuNVJf8Y4BSikZ8cVmyw5x2fP50ETMR2xCErG1tyrY0m3JiB3ocx5s37Uqm9jNkHlEwVB16ene+v8XvUG7p3vo1STCsZKkjes2dp4xwyhaNubXj/KUDAcxrP3xQ5VseCkGb17kE+BgKCCjnv5n4fKyYicMgeK+2ZPBKEyyJj+zT8acjrYnCw7yq/3Lpc4OwyTZ9Lrm5VUvgHtkWZQLlFlnIlcbLKMZWNhvinRypisAxioAPSmmjpe+nVZjtxyY+hb/4KCiNzV8oSiUVRbMFDc5rUYwzXJEBlpYVml3UZ9VIRuECr+ArZqNNViRqFCVsItz41cz9CJmN0OESvPphMEHZIlvLG00MAAMpixQ+azjx7SLtGktoFNod7/5aBGV/ZQZodIebGAMdYPQ0aW4oV/3+ssB8N0es0LKAL47aZczYEbrhIm2ZqP6zE5dD6++QJQ7GmnxgScz3xvEUG+X4EJYFM1EmuJxOMh2DRVqyyAvw67xrjWlgPTcArbJc5EnXU2u+B6ISI/4Ho/tDXfVq/bUtZhTpYjij6dVMtpsYXNm87h7j7GpkIn6bQARAQABiQIlBBgBAgAPBQJXXE66AhsMBQkJZgGAAAoJEPzxexfx+OFXxAQP/AliqzbwvaGDEhVB1+O+CLMxw9YvozojTHSXZNiXTySRK6V9Wn5MEO+aQ0/lfDb6/R+8JJyCdiqONg/f4eXFTjMghU9qPttoJ//sJa14PXrXqx2heZ10lbLihidHudEmcXxLRAFijXx/ChDaIda56/1TizM1qwb7m09BXZLPR1ELP9j5MIbRDV4Jt3ZLPB9B8Wa1EqBXtygnBvd/aZVup1mXMeEqdtm6KoCOGpwYTfr5uieMXzbH9YxhEIQAsLq1XQQZ+jV6ulTuUpFX22UtJ4VwMyjaBvEfavjm5GjSWJ7hRhsMdnf8yP2iEndv2zZibyyadFzvJdMJtB21yIdlK3zTRrD/XLT3fS3jFbtgjVlmNZFqp1vM49xsEWnCigqYScvZLonkEkorMvZcbgdTwL8fI9VOzXSbKIZEiPqZGxlQ1HUIDb0/kFVUOu3501Ne/jU5X2IsNOl9b+oAjq84j0JQuZ/DQsoWgJuItLNu6WzM/4VnX7k784DT1aSy3s8PRD+prdrfphtRq6L+i18/wElQcDJLue2NcI6I2yCTr71aouJ37QGl6IQxwoV6vaRqNDFDr5MiDFOX0wDTp1QkFAgZjqz/amE9Rm5qePD+J4oTLvCTehf3oTbcjImlM5GtsI6yey7Xn+fgmZF+5Ki586dGnDgIemJbbeQ5vebVNUf/ 10 | -------------------------------------------------------------------------------- /flatpak-repos/flathub.flatpakrepo: -------------------------------------------------------------------------------- 1 | [Flatpak Repo] 2 | Title=Flathub 3 | Url=https://dl.flathub.org/repo/ 4 | Homepage=https://flathub.org/ 5 | Comment=Central repository of Flatpak applications 6 | Description=Central repository of Flatpak applications 7 | Icon=https://dl.flathub.org/repo/logo.svg 8 | GPGKey=mQINBFlD2sABEADsiUZUOYBg1UdDaWkEdJYkTSZD68214m8Q1fbrP5AptaUfCl8KYKFMNoAJRBXn9FbE6q6VBzghHXj/rSnA8WPnkbaEWR7xltOqzB1yHpCQ1l8xSfH5N02DMUBSRtD/rOYsBKbaJcOgW0K21sX+BecMY/AI2yADvCJEjhVKrjR9yfRX+NQEhDcbXUFRGt9ZT+TI5yT4xcwbvvTu7aFUR/dH7+wjrQ7lzoGlZGFFrQXSs2WI0WaYHWDeCwymtohXryF8lcWQkhH8UhfNJVBJFgCY8Q6UHkZG0FxMu8xnIDBMjBmSZKwKQn0nwzwM2afskZEnmNPYDI8nuNsSZBZSAw+ThhkdCZHZZRwzmjzyRuLLVFpOj3XryXwZcSefNMPDkZAuWWzPYjxS80cm2hG1WfqrG0Gl8+iX69cbQchb7gbEb0RtqNskTo9DDmO0bNKNnMbzmIJ3/rTbSahKSwtewklqSP/01o0WKZiy+n/RAkUKOFBprjJtWOZkc8SPXV/rnoS2dWsJWQZhuPPtv3tefdDiEyp7ePrfgfKxuHpZES0IZRiFI4J/nAUP5bix+srcIxOVqAam68CbAlPvWTivRUMRVbKjJiGXIOJ78wAMjqPg3QIC0GQ0EPAWwAOzzpdgbnG7TCQetaVV8rSYCuirlPYN+bJIwBtkOC9SWLoPMVZTwQARAQABtC5GbGF0aHViIFJlcG8gU2lnbmluZyBLZXkgPGZsYXRodWJAZmxhdGh1Yi5vcmc+iQJUBBMBCAA+FiEEblwF2XnHba+TwIE1QYTdTZB6fK4FAllD2sACGwMFCRLMAwAFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQQYTdTZB6fK5RJQ/+Ptd4sWxaiAW91FFk7+wmYOkEe1NY2UDNJjEEz34PNP/1RoxveHDt43kYJQ23OWaPJuZAbu+fWtjRYcMBzOsMCaFcRSHFiDIC9aTp4ux/mo+IEeyarYt/oyKb5t5lta6xaAqg7rwt65jW5/aQjnS4h7eFZ+dAKta7Y/fljNrOznUp81/SMcx4QA5G2Pw0hs4Xrxg59oONOTFGBgA6FF8WQghrpR7SnEe0FSEOVsAjwQ13Cfkfa7b70omXSWp7GWfUzgBKyoWxKTqzMN3RQHjjhPJcsQnrqH5enUu4Pcb2LcMFpzimHnUgb9ft72DP5wxfzHGAWOUiUXHbAekfq5iFks8cha/RST6wkxG3Rf44Zn09aOxh1btMcGL+5xb1G0BuCQnA0fP/kDYIPwh9z22EqwRQOspIcvGeLVkFeIfubxpcMdOfQqQnZtHMCabV5Q/Rk9K1ZGc8M2hlg8gHbXMFch2xJ0Wu72eXbA/UY5MskEeBgawTQnQOK/vNm7t0AJMpWK26Qg6178UmRghmeZDj9uNRc3EI1nSbgvmGlpDmCxaAGqaGL1zW4KPW5yN25/qeqXcgCvUjZLI9PNq3Kvizp1lUrbx7heRiSoazCucvHQ1VHUzcPVLUKKTkoTP8okThnRRRsBcZ1+jI4yMWIDLOCT7IW3FePr+3xyuy5eEo9a25Ag0EWUPa7AEQALT/CmSyZ8LWlRYQZKYw417p7Z2hxqd6TjwkwM3IQ1irumkWcTZBZIbBgrSOg6CcXD2oWydCQHWi9qaxhuhEl2bJL5LskmBcMxVdQeD0LLHd8QUnbnnIby8ocvWN1alPfvJFjCUTrmD22U1ycOzRw2lIe4kiQONbOZtdWrVImQQSndjFlisitbmlWHvHm2lOOYy8+GJB7YffVV193hmnBSJffCy4bvkuLxsI+n1DhOzc7MPV3z6HGk4HiEcF0yyt9tCYhpsxHFdBoq2h771HfAcS0s98EVAqYMFnf9em+4cnYpdI6mhIfS1FQiKl6DBAYA8tT3ggla00DurPo0JwX/zN+PaO5h/6O9aCZwV7G6rbkgMuqMergXaf8oP38gr0z+MqWnkfM63Bodq68GP4l4hd02BoFBbDf38TMuGQB14+twJMdfbAxo2MbgluvQgfwHfZ2ca6gyEY+9s/YD1gugLjV+S6CB51WkFNe1z4tAPgJZNxUcKCbeaHNbthl8Hks/pY9RCEseX/EdfzF18epbSjJMPh4DPQXbUoFwmyuYcoBOPmvZHNl9hK7B/1RP8w1ZrXk8qdupC0SNbafX7270B7lMMVImzZetGsM9ypXJ6llhp3FwW09iseNyGJGPsr/dvTMGDXqOPfU/9SAS1LSTY4K9PbRtdrBE318YX8mIk5ABEBAAGJBHIEGAEIACYWIQRuXAXZecdtr5PAgTVBhN1NkHp8rgUCWUPa7AIbAgUJEswDAAJACRBBhN1NkHp8rsF0IAQZAQgAHRYhBFSmzd2JGfsgQgDYrFYnAunj7X7oBQJZQ9rsAAoJEFYnAunj7X7oR6AP/0KYmiAFeqx14Z43/6s2gt3VhxlSd8bmcVV7oJFbMhdHBIeWBp2BvsUf00I0Zl14ZkwCKfLwbbORC2eIxvzJ+QWjGfPhDmS4XUSmhlXxWnYEveSek5Tde+fmu6lqKM8CHg5BNx4GWIX/vdLi1wWJZyhrUwwICAxkuhKxuP2Z1An48930eslTD2GGcjByc27+9cIZjHKa07I/aLffo04V+oMT9/tgzoquzgpVV4jwekADo2MJjhkkPveSNI420bgT+Q7Fi1l0X1aFUniBvQMsaBa27PngWm6xE2ZYvh7nWCdd5g0c0eLIHxWwzV1lZ4Ryx4ITO/VL25ItECcjhTRdYa64sA62MYSaB0x3eR+SihpgP3wSNPFu3MJo6FKTFdi4CBAEmpWHFW7FcRmd+cQXeFrHLN3iNVWryy0HK/CUEJmiZEmpNiXecl4vPIIuyF0zgSCztQtKoMr+injpmQGC/rF/ELBVZTUSLNB350S0Ztvw0FKWDAJSxFmoxt3xycqvvt47rxTrhi78nkk6jATKGyvP55sO+K7Q7Wh0DXA69hvPrYW2eu8jGCdVGxi6HX7L1qcfEd0378S71dZ3g9o6KKl1OsDWWQ6MJ6FGBZedl/ibRfs8p5+sbCX3lQSjEFy3rx6n0rUrXx8U2qb+RCLzJlmC5MNBOTDJwHPcX6gKsUcXZrEQALmRHoo3SrewO41RCr+5nUlqiqV3AohBMhnQbGzyHf2+drutIaoh7Rj80XRh2bkkuPLwlNPf+bTXwNVGse4bej7B3oV6Ae1N7lTNVF4Qh+1OowtGjmfJPWo0z1s6HFJVxoIof9z58Msvgao0zrKGqaMWaNQ6LUeC9g9Aj/9Uqjbo8X54aLiYs8Z1WNc06jKP+gv8AWLtv6CR+l2kLez1YMDucjm7v6iuCMVAmZdmxhg5I/X2+OM3vBsqPDdQpr2TPDLX3rCrSBiS0gOQ6DwN5N5QeTkxmY/7QO8bgLo/Wzu1iilH4vMKW6LBKCaRx5UEJxKpL4wkgITsYKneIt3NTHo5EOuaYk+y2+Dvt6EQFiuMsdbfUjs3seIHsghX/cbPJa4YUqZAL8C4OtVHaijwGo0ymt9MWvS9yNKMyT0JhN2/BdeOVWrHk7wXXJn/ZjpXilicXKPx4udCF76meE+6N2u/T+RYZ7fP1QMEtNZNmYDOfA6sViuPDfQSHLNbauJBo/n1sRYAsL5mcG22UDchJrlKvmK3EOADCQg+myrm8006LltubNB4wWNzHDJ0Ls2JGzQZCd/xGyVmUiidCBUrD537WdknOYE4FD7P0cHaM9brKJ/M8LkEH0zUlo73bY4XagbnCqve6PvQb5G2Z55qhWphd6f4B6DGed86zJEa/RhS 9 | -------------------------------------------------------------------------------- /nvidia/Makefile.am: -------------------------------------------------------------------------------- 1 | dist_systemdunit_DATA = \ 2 | nvidia-graphics.service \ 3 | $(NULL) 4 | 5 | dist_sbin_SCRIPTS = \ 6 | nvidia-graphics-setup \ 7 | $(NULL) 8 | 9 | dist_modprobe_DATA = \ 10 | nouveau.conf \ 11 | $(NULL) 12 | 13 | nvidiadatadir = $(datadir)/endless-external-drivers/nvidia 14 | dist_nvidiadata_DATA = \ 15 | asus-pci-blacklist \ 16 | dmi-blacklist \ 17 | dmi-board-blacklist 18 | -------------------------------------------------------------------------------- /nvidia/asus-pci-blacklist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/nvidia/asus-pci-blacklist -------------------------------------------------------------------------------- /nvidia/dmi-blacklist: -------------------------------------------------------------------------------- 1 | # List of platforms (matched by DMI data) where the proprietary nvidia driver 2 | # should not be loaded. 3 | # 4 | # Format: CSV 5 | # delimiter: , 6 | # Quote character: " 7 | 8 | # Massive graphics corruption (940MX) (T23366) 9 | Acer,TravelMate P648-G2-MG 10 | -------------------------------------------------------------------------------- /nvidia/dmi-board-blacklist: -------------------------------------------------------------------------------- 1 | # List of platforms (matched by DMI data) where the proprietary nvidia driver 2 | # should not be loaded. 3 | # 4 | # Format: CSV 5 | # delimiter: , 6 | # Quote character: " 7 | -------------------------------------------------------------------------------- /nvidia/nouveau.conf: -------------------------------------------------------------------------------- 1 | # Prevent nouveau from being auto-loaded. 2 | # We load it conditionally from nvidia-graphics.service 3 | blacklist nouveau 4 | -------------------------------------------------------------------------------- /nvidia/nvidia-graphics.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Nvidia graphics management 3 | Before=gdm.service 4 | 5 | [Service] 6 | Type=oneshot 7 | RemainAfterExit=yes 8 | ExecStart=/usr/sbin/nvidia-graphics-setup 9 | 10 | [Install] 11 | WantedBy=graphical.target 12 | -------------------------------------------------------------------------------- /psi-monitor/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CFLAGS = -Wall -Werror 2 | 3 | sbin_PROGRAMS = psi-monitor 4 | psi_monitor_SOURCES = psi-monitor.c 5 | 6 | dist_systemdunit_DATA = \ 7 | psi-monitor.service \ 8 | $(NULL) 9 | -------------------------------------------------------------------------------- /psi-monitor/psi-monitor.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* Daemon parameters */ 16 | static unsigned int poll_interval = 5; 17 | static unsigned int recovery_interval = 15; 18 | static unsigned int mem_threshold = 40; 19 | 20 | #define SYSRQ_TRIGGER_FILE "/proc/sysrq-trigger" 21 | /* 22 | * "/proc/pressure/memory" is memory pressure interface provided by kernel. 23 | * Please refer to PSI - Pressure Stall Information for more detail: 24 | * https://docs.kernel.org/accounting/psi.html 25 | */ 26 | #define PSI_MEMORY_FILE "/proc/pressure/memory" 27 | #define BUFSIZE 256 28 | 29 | static bool opt_debug = false; 30 | static const char *short_options = "m:p:r:dh"; 31 | static struct option long_options[] = { 32 | {"mem-threshold", 1, 0, 'm'}, 33 | {"poll-interval", 1, 0, 'p'}, 34 | {"recovery-interval", 1, 0, 'r'}, 35 | {"debug", 0, 0, 'd'}, 36 | {"help", 0, 0, 'h'}, 37 | {0, 0, 0, 0} 38 | }; 39 | 40 | static void usage(const char *progname) { 41 | printf("Usage: %s [OPTION]...\n" 42 | "Invoke out of memory killer on excessive memory pressure.\n" 43 | "\n" 44 | " -m, --mem-threshold PCT\tmemory threshold percentage (default: %u)\n" 45 | " -p, --poll-interval SEC\tpoll interval seconds (default: %u)\n" 46 | " -r, --recovery-interval SEC\trecovery interval seconds (default: %u)\n" 47 | " -d, --debug\t\t\tprint debugging messages\n" 48 | " -h, --help\t\t\tdisplay this help and exit\n", 49 | progname, mem_threshold, poll_interval, recovery_interval); 50 | } 51 | 52 | static void set_mem_threshold(const char *arg) { 53 | long val; 54 | char *endptr = NULL; 55 | 56 | errno = 0; 57 | val = strtol(arg, &endptr, 10); 58 | if (errno != 0) 59 | err(1, "Invalid memory threshold value \"%s\"", arg); 60 | if (endptr == arg) 61 | errx(1, "No memory threshold value provided"); 62 | if (val < 0) 63 | errx(1, "Memory threshold value cannot be negative"); 64 | if (val > 100) 65 | errx(1, "Memory threshold value cannot exceed 100"); 66 | mem_threshold = (unsigned int) val; 67 | } 68 | 69 | static void set_interval(unsigned int *var, const char *arg) { 70 | long val; 71 | char *endptr = NULL; 72 | 73 | assert(var != NULL); 74 | 75 | errno = 0; 76 | val = strtol(arg, &endptr, 10); 77 | if (errno != 0) 78 | err(1, "Invalid interval value \"%s\"", arg); 79 | if (endptr == arg) 80 | errx(1, "No interval value provided"); 81 | if (val < 0) 82 | errx(1, "Interval value cannot be negative"); 83 | if (val > UINT_MAX) 84 | errx(1, "Interval value cannot exceed %u", UINT_MAX); 85 | *var = (unsigned int) val; 86 | } 87 | 88 | static ssize_t fstr(const char *path, char *rbuf, const char *wbuf) { 89 | int fd; 90 | ssize_t n; 91 | 92 | /* one and only one operation per call */ 93 | if ((!rbuf && !wbuf) || (rbuf && wbuf)) 94 | return 0; 95 | 96 | fd = open(path, rbuf ? O_RDONLY : O_WRONLY); 97 | if (fd < 0) 98 | err(1, "%s", path); 99 | 100 | if (rbuf) 101 | n = read(fd, rbuf, BUFSIZE); 102 | else 103 | n = write(fd, wbuf, strlen(wbuf)); 104 | if (n < 0) 105 | err(1, "%s", path); 106 | close(fd); 107 | 108 | if (rbuf) 109 | rbuf[n-1] = '\0'; 110 | 111 | return n; 112 | } 113 | 114 | static void sysrq_trigger_oom() { 115 | fstr(SYSRQ_TRIGGER_FILE, NULL, "f"); 116 | sleep(recovery_interval); 117 | } 118 | 119 | int main(int argc, char **argv) { 120 | while (true) { 121 | int c = getopt_long(argc, argv, short_options, long_options, NULL); 122 | if (c == -1) 123 | break; 124 | 125 | switch (c) { 126 | case 'm': 127 | set_mem_threshold(optarg); 128 | break; 129 | case 'p': 130 | set_interval(&poll_interval, optarg); 131 | break; 132 | case 'r': 133 | set_interval(&recovery_interval, optarg); 134 | break; 135 | case 'd': 136 | opt_debug = true; 137 | break; 138 | case 'h': 139 | usage(argv[0]); 140 | return 0; 141 | default: 142 | return 1; 143 | } 144 | } 145 | 146 | setvbuf(stdout, NULL, _IOLBF, 0); 147 | printf("poll_interval=%us, recovery_interval=%us, mem_threshold=%u%%\n", 148 | poll_interval, recovery_interval, mem_threshold); 149 | 150 | while (true) { 151 | int i; 152 | char buf[BUFSIZE]; 153 | float full_avg10; 154 | 155 | fstr(PSI_MEMORY_FILE, buf, NULL); 156 | 157 | for (i = 0; buf[i] != '\n'; i++); 158 | i++; /* skip \n */ 159 | i+=11; /* skip "full avg10=" */ 160 | 161 | sscanf(buf+i, "%f", &full_avg10); 162 | if (opt_debug) printf("full_avg10=%f\n", full_avg10); 163 | 164 | if (full_avg10 > mem_threshold) { 165 | printf("Memory pressure %.1f%% above threshold limit %u%%, " 166 | "killing task and pausing %u seconds for recovery\n", 167 | full_avg10, mem_threshold, recovery_interval); 168 | sysrq_trigger_oom(); 169 | } else { 170 | sleep(poll_interval); 171 | } 172 | } 173 | 174 | return 0; 175 | } 176 | -------------------------------------------------------------------------------- /psi-monitor/psi-monitor.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Pressure Stall Information Monitor 3 | ConditionPathExists=/proc/pressure 4 | After=sysinit.target 5 | 6 | [Service] 7 | ExecStart=/usr/sbin/psi-monitor 8 | Restart=on-failure 9 | 10 | [Install] 11 | WantedBy=basic.target 12 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | # Run in verbose mode by default. Use -q to bump the verbosity down. 3 | addopts = -v 4 | 5 | # Enable debug logging by default to help track down issues. This is set 6 | # on the root logger, but it seems the only current log messages are 7 | # from our modules. The alternative is to use the caplog fixture on all 8 | # tests to just set the level on our loggers. 9 | log_level = DEBUG 10 | 11 | # Raise an error if tests marked xfail pass 12 | xfail_strict = True 13 | -------------------------------------------------------------------------------- /record-boot-success/Makefile.am: -------------------------------------------------------------------------------- 1 | recordbootsuccessdir = $(libexecdir)/record-boot-success 2 | dist_recordbootsuccess_SCRIPTS = \ 3 | record-boot-success \ 4 | $(NULL) 5 | 6 | dist_systemdunit_DATA = \ 7 | record-boot-success.service \ 8 | $(NULL) 9 | 10 | dist_systembusservices_DATA = \ 11 | com.endlessm.RecordBootSuccess.service \ 12 | $(NULL) 13 | 14 | dist_dbussystemconfig_DATA = \ 15 | com.endlessm.RecordBootSuccess.conf \ 16 | $(NULL) 17 | 18 | xdgautostartdir = $(sysconfdir)/xdg/autostart 19 | dist_xdgautostart_DATA = \ 20 | record-boot-success.desktop \ 21 | $(NULL) 22 | 23 | gdmgreeterautostartdir = $(prefix)/share/gdm/greeter/autostart 24 | dist_gdmgreeterautostart_DATA = \ 25 | record-boot-success.desktop \ 26 | $(NULL) 27 | -------------------------------------------------------------------------------- /record-boot-success/com.endlessm.RecordBootSuccess.conf: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /record-boot-success/com.endlessm.RecordBootSuccess.service: -------------------------------------------------------------------------------- 1 | [D-BUS Service] 2 | Name=com.endlessm.RecordBootSuccess 3 | User=root 4 | SystemdService=record-boot-success.service 5 | Exec=/usr/lib/eos-boot-helper/record-boot-success/record-boot-success 6 | -------------------------------------------------------------------------------- /record-boot-success/record-boot-success: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import dbus 3 | import dbus.service 4 | import logging 5 | import os 6 | import subprocess 7 | import sys 8 | 9 | from systemd import journal 10 | 11 | BUSNAME = "com.endlessm.RecordBootSuccess" 12 | 13 | 14 | def record_boot_success(): 15 | envfile = "/boot/grub/grubenv" 16 | program = "/usr/bin/grub-editenv" 17 | 18 | try: 19 | if os.path.getsize(envfile) == 0: 20 | os.remove(envfile) 21 | except FileNotFoundError: 22 | pass 23 | 24 | cmd = [program, envfile, "unset", "recordfail"] 25 | sp = subprocess.run(cmd) 26 | status = sp.returncode 27 | if status != 0: 28 | logger.error(f"{program} failed with exit code {status}") 29 | else: 30 | logger.info("Marked boot as successful") 31 | return status 32 | 33 | 34 | if __name__ == '__main__': 35 | logger = logging.getLogger(__name__) 36 | logger.propagate = False 37 | logger.setLevel(logging.INFO) 38 | logger.addHandler(logging.StreamHandler()) 39 | logger.addHandler(journal.JournalHandler()) 40 | 41 | status = record_boot_success() 42 | if status == 0: 43 | busname = dbus.service.BusName(BUSNAME, dbus.SystemBus()) 44 | 45 | logging.shutdown() 46 | sys.exit(status) 47 | -------------------------------------------------------------------------------- /record-boot-success/record-boot-success.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Record boot status 3 | Type=Application 4 | NoDisplay=true 5 | Exec=dbus-send --system --type=method_call --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.StartServiceByName string:"com.endlessm.RecordBootSuccess" uint32:0 6 | -------------------------------------------------------------------------------- /record-boot-success/record-boot-success.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Record Boot Success 3 | ConditionPathIsDirectory=/boot/grub 4 | ConditionFileIsExecutable=/usr/bin/grub-editenv 5 | 6 | [Service] 7 | Type=dbus 8 | BusName=com.endlessm.RecordBootSuccess 9 | ExecStart=/usr/lib/eos-boot-helper/record-boot-success/record-boot-success 10 | RemainAfterExit=yes 11 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_TESTS_ENVIRONMENT = \ 2 | BUILDDIR='$(builddir)' \ 3 | TOP_BUILDDIR='$(top_builddir)' \ 4 | ABS_BUILDDIR='$(abs_builddir)' \ 5 | ABS_TOP_BUILDDIR='$(abs_top_builddir)' \ 6 | SRCDIR='$(srcdir)' \ 7 | TOP_SRCDIR='$(top_srcdir)' \ 8 | ABS_SRCDIR='$(abs_srcdir)' \ 9 | ABS_TOP_SRCDIR='$(abs_top_srcdir)' \ 10 | $(NULL) 11 | 12 | TESTS = \ 13 | check-syntax \ 14 | run-tests \ 15 | $(NULL) 16 | 17 | EXTRA_DIST = \ 18 | $(TESTS) \ 19 | __init__.py \ 20 | conftest.py \ 21 | efivars \ 22 | espgen \ 23 | test_esp_generator.py \ 24 | test_image_boot.py \ 25 | test_live_storage.py \ 26 | test_migrate_chromium_profile.py \ 27 | test_migrate_firefox_profile.py \ 28 | test_repartition.py \ 29 | test_repartition_mbr.py \ 30 | test_update_efi_uuid.py \ 31 | test_update_flatpak_repos.py \ 32 | util.py \ 33 | $(NULL) 34 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/__init__.py -------------------------------------------------------------------------------- /tests/check-syntax: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import os 3 | import subprocess 4 | import glob 5 | 6 | ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 7 | 8 | 9 | def find_files(pattern): 10 | '''Find files matching 'pattern' on the first line.''' 11 | output = subprocess.check_output([ 12 | 'grep', '-r', '--files-with-match', '--null', 13 | # Only first line 14 | '--max-count', '1', 15 | # Skip .git and friends 16 | '--exclude-dir=.*', 17 | # Skip temporary files 18 | '--exclude=.*', '--exclude=*~', 19 | pattern, ROOT 20 | ]) 21 | return [x.decode('utf-8') for x in output.split(b'\x00') if x] 22 | 23 | 24 | def main(): 25 | '''Checks shell scripts' syntax, printing results in TAP format and 26 | exiting non-zero if any script has syntax errors.''' 27 | checks = [] 28 | 29 | for bash_script in find_files("^#!/bin/bash"): 30 | checks.append(["bash", "-n", bash_script]) 31 | 32 | for dash_script in find_files("^#!/bin/sh"): 33 | checks.append(["dash", "-n", dash_script]) 34 | 35 | # All files with a shebang, regardless of their extension (if any) 36 | python_scripts = set(find_files("^#!/usr/bin/python")) 37 | # .py files in the tests/ directory, which may or may not have a hashtag yell 38 | python_scripts.update(glob.glob(os.path.join(ROOT, 'tests', '*.py'))) 39 | checks.append(["flake8"] + sorted(python_scripts)) 40 | 41 | print('1..{}'.format(len(checks))) 42 | failed = False 43 | for i, check in enumerate(checks, 1): 44 | label = ' '.join(check) 45 | try: 46 | subprocess.check_call(check) 47 | except subprocess.CalledProcessError: 48 | result = 'not ok' 49 | failed = True 50 | else: 51 | result = 'ok' 52 | 53 | print('{} {} - {}'.format(result, i, label)) 54 | 55 | exit(failed) 56 | 57 | 58 | if __name__ == '__main__': 59 | main() 60 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Endless OS Foundation LLC 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | # pytest fixtures 5 | 6 | import pytest 7 | from shutil import copyfile, copytree 8 | 9 | from .util import EFIVARFS_PATH 10 | 11 | 12 | @pytest.fixture(autouse=True) 13 | def default_env_vars(monkeypatch): 14 | monkeypatch.setenv('LANG', 'C.UTF-8') 15 | 16 | 17 | @pytest.fixture 18 | def efivarfs(tmp_path, monkeypatch): 19 | """Temporary efivarfs data 20 | 21 | Copy the test efivarfs data to a temporary location. The environment 22 | variable EFIVARFS_PATH is set to the temporary location. This is supported 23 | by libefivar. 24 | """ 25 | # Only the data is copied in case EFIVARFS_PATH is read only like 26 | # during distcheck. None of the metadata matters here. 27 | tmp_efivarfs = tmp_path / 'efivars' 28 | copytree(EFIVARFS_PATH, tmp_efivarfs, copy_function=copyfile) 29 | 30 | # libefivar expects this to end with a trailing /. 31 | monkeypatch.setenv('EFIVARFS_PATH', f'{tmp_efivarfs}/') 32 | 33 | return tmp_efivarfs 34 | -------------------------------------------------------------------------------- /tests/efivars/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/Boot0002-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/Boot0002-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/Boot0003-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/Boot0003-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/efivars/BootOptionSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/efivars/BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/efivars/ConIn-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/ConIn-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/ConInDev-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/ConInDev-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/ConOut-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/ConOut-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/ConOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/ConOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/ErrOut-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/ErrOut-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/ErrOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/ErrOutDev-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/FALLBACK_VERBOSE-605dab50-e046-4300-abb6-3dd810dd8b23: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/efivars/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/Key0000-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/Key0000-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/Key0001-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/Key0001-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/Lang-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 | eng -------------------------------------------------------------------------------- /tests/efivars/LangCodes-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 | engfraengfra -------------------------------------------------------------------------------- /tests/efivars/MTC-eb704011-1402-11d3-8e77-00a0c969723b: -------------------------------------------------------------------------------- 1 | ' -------------------------------------------------------------------------------- /tests/efivars/MokListRT-605dab50-e046-4300-abb6-3dd810dd8b23: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/MokListRT-605dab50-e046-4300-abb6-3dd810dd8b23 -------------------------------------------------------------------------------- /tests/efivars/MokListTrustedRT-605dab50-e046-4300-abb6-3dd810dd8b23: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/efivars/MokListXRT-605dab50-e046-4300-abb6-3dd810dd8b23: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/MokListXRT-605dab50-e046-4300-abb6-3dd810dd8b23 -------------------------------------------------------------------------------- /tests/efivars/OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /tests/efivars/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/PK-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/PlatformLang-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 | en -------------------------------------------------------------------------------- /tests/efivars/PlatformLangCodes-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 | en;fr;en-US;fr-FR -------------------------------------------------------------------------------- /tests/efivars/PlatformRecovery0000-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/PlatformRecovery0000-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/SbatLevelRT-605dab50-e046-4300-abb6-3dd810dd8b23: -------------------------------------------------------------------------------- 1 | sbat,1,2024010900 2 | shim,4 3 | grub,3 4 | grub.debian,4 5 | -------------------------------------------------------------------------------- /tests/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/efivars/SetupMode-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/efivars/SignatureSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/SignatureSupport-8be4df61-93ca-11d2-aa0d-00e098032b8c -------------------------------------------------------------------------------- /tests/efivars/Timeout-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/efivars/VarErrorFlag-04b37fe8-f6ae-480b-bdd5-37d98c5e89aa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/VarErrorFlag-04b37fe8-f6ae-480b-bdd5-37d98c5e89aa -------------------------------------------------------------------------------- /tests/efivars/VendorKeys-8be4df61-93ca-11d2-aa0d-00e098032b8c: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/efivars/certdb-d9bee56e-75dc-49d9-b4d7-b534210f637a: -------------------------------------------------------------------------------- 1 | ' -------------------------------------------------------------------------------- /tests/efivars/certdbv-d9bee56e-75dc-49d9-b4d7-b534210f637a: -------------------------------------------------------------------------------- 1 | & -------------------------------------------------------------------------------- /tests/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endlessm/eos-boot-helper/4c265269fc7553d2b2acffbfd5ab8a056374da5b/tests/efivars/db-d719b2cb-3d3a-4596-a3bc-dad00e67656f -------------------------------------------------------------------------------- /tests/espgen/README.md: -------------------------------------------------------------------------------- 1 | Test data directory for use with `test_esp_generator.py`. The data here is 2 | created by running `eos-esp-generator` in gather mode. The easiest way to do 3 | that is to install it as 4 | `/etc/systemd/system-generators/eos-esp-generator-gather`. 5 | 6 | Reboot so that it runs as an early boot generator. After booting, run it again 7 | as a generator by calling `systemctl daemon-reload`. There will then be 2 8 | tarballs at `/run/espgen-data-*.tar.gz`. Unpack the tarballs and add the data 9 | files to this directory with a semi-descriptive prefix. The `mounts.json` and 10 | `partitions.json` data files should be differentiated by adding `-init` or 11 | `-reload` suffixes as appropriate. The `fstab.json` and `kcmdline.json` files 12 | don't need to be differentiated as they won't change between the 2 executions 13 | of the generator. 14 | 15 | Finally, wire up the data in the `ESP_MOUNT_TEST_DATA` dictionary in 16 | `test_esp_generator.py`. The key is the prefix added to the data files above. 17 | -------------------------------------------------------------------------------- /tests/espgen/grub-gpt-fstab-efi-fstab.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/vda3", 5 | "maj:min": null, 6 | "fstype": "ext4", 7 | "fsroot": null, 8 | "options": "errors=remount-ro" 9 | }, 10 | { 11 | "target": "/efi", 12 | "source": "/dev/vda1", 13 | "maj:min": null, 14 | "fstype": "vfat", 15 | "fsroot": null, 16 | "options": "umask=0077" 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /tests/espgen/grub-gpt-fstab-efi-kcmdline.json: -------------------------------------------------------------------------------- 1 | { 2 | "BOOT_IMAGE": "(hd0,gpt3)/boot/ostree/eos-078584ee3e2b33ee29896c7ebd1533b40f7f7a6d551dcc39cfecd4d8e8f75d51/vmlinuz-6.5.0-10-generic", 3 | "root": "UUID=cecb0576-cf95-48bf-8b72-aced790e83e9", 4 | "rw": null, 5 | "splash": null, 6 | "plymouth.ignore-serial-consoles": null, 7 | "quiet": null, 8 | "loglevel": "0", 9 | "ostree": "/ostree/boot.1/eos/078584ee3e2b33ee29896c7ebd1533b40f7f7a6d551dcc39cfecd4d8e8f75d51/0" 10 | } 11 | -------------------------------------------------------------------------------- /tests/espgen/grub-gpt-fstab-efi-mounts-init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/vda3", 5 | "maj:min": "253:3", 6 | "fstype": "ext4", 7 | "fsroot": "/ostree/deploy/eos/deploy/03b591bb31c1d4cb69df9e4a7d7097a48012354d4a3777179d90016ec1893738.0", 8 | "options": "ro,relatime" 9 | }, 10 | { 11 | "target": "/boot", 12 | "source": "/dev/vda3", 13 | "maj:min": "253:3", 14 | "fstype": "ext4", 15 | "fsroot": "/boot", 16 | "options": "ro,relatime" 17 | }, 18 | { 19 | "target": "/usr", 20 | "source": "/dev/vda3", 21 | "maj:min": "253:3", 22 | "fstype": "ext4", 23 | "fsroot": "/ostree/deploy/eos/deploy/03b591bb31c1d4cb69df9e4a7d7097a48012354d4a3777179d90016ec1893738.0/usr", 24 | "options": "ro,relatime" 25 | }, 26 | { 27 | "target": "/sysroot", 28 | "source": "/dev/vda3", 29 | "maj:min": "253:3", 30 | "fstype": "ext4", 31 | "fsroot": "/", 32 | "options": "ro,relatime" 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /tests/espgen/grub-gpt-fstab-efi-mounts-reload.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/vda3", 5 | "maj:min": "253:3", 6 | "fstype": "ext4", 7 | "fsroot": "/ostree/deploy/eos/deploy/03b591bb31c1d4cb69df9e4a7d7097a48012354d4a3777179d90016ec1893738.0", 8 | "options": "ro,relatime,errors=remount-ro" 9 | }, 10 | { 11 | "target": "/boot", 12 | "source": "/dev/vda3", 13 | "maj:min": "253:3", 14 | "fstype": "ext4", 15 | "fsroot": "/boot", 16 | "options": "ro,relatime,errors=remount-ro" 17 | }, 18 | { 19 | "target": "/usr", 20 | "source": "/dev/vda3", 21 | "maj:min": "253:3", 22 | "fstype": "ext4", 23 | "fsroot": "/ostree/deploy/eos/deploy/03b591bb31c1d4cb69df9e4a7d7097a48012354d4a3777179d90016ec1893738.0/usr", 24 | "options": "ro,relatime,errors=remount-ro" 25 | }, 26 | { 27 | "target": "/sysroot", 28 | "source": "/dev/vda3", 29 | "maj:min": "253:3", 30 | "fstype": "ext4", 31 | "fsroot": "/", 32 | "options": "ro,relatime,errors=remount-ro" 33 | }, 34 | { 35 | "target": "/var", 36 | "source": "/dev/vda3", 37 | "maj:min": "253:3", 38 | "fstype": "ext4", 39 | "fsroot": "/ostree/deploy/eos/var", 40 | "options": "ro,relatime,errors=remount-ro" 41 | }, 42 | { 43 | "target": "/efi", 44 | "source": "/dev/vda1", 45 | "maj:min": "253:1", 46 | "fstype": "vfat", 47 | "fsroot": "/", 48 | "options": "rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro" 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /tests/espgen/grub-gpt-fstab-efi-partitions-init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "DEVNAME": "/dev/vda2", 4 | "PART_ENTRY_SCHEME": "gpt", 5 | "PART_ENTRY_UUID": "9eaab1f8-c210-2b4e-8656-e8ed0beb1ab2", 6 | "PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649", 7 | "PART_ENTRY_NUMBER": "2", 8 | "PART_ENTRY_OFFSET": "129024", 9 | "PART_ENTRY_SIZE": "2048", 10 | "PART_ENTRY_DISK": "253:0" 11 | }, 12 | { 13 | "DEVNAME": "/dev/vda3", 14 | "LABEL": "ostree", 15 | "UUID": "cecb0576-cf95-48bf-8b72-aced790e83e9", 16 | "VERSION": "1.0", 17 | "BLOCK_SIZE": "4096", 18 | "TYPE": "ext4", 19 | "USAGE": "filesystem", 20 | "PART_ENTRY_SCHEME": "gpt", 21 | "PART_ENTRY_UUID": "eb219ed1-9e98-4048-9288-1c16db3dea72", 22 | "PART_ENTRY_TYPE": "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", 23 | "PART_ENTRY_NUMBER": "3", 24 | "PART_ENTRY_OFFSET": "131072", 25 | "PART_ENTRY_SIZE": "52297695", 26 | "PART_ENTRY_DISK": "253:0" 27 | }, 28 | { 29 | "DEVNAME": "/dev/vda1", 30 | "SEC_TYPE": "msdos", 31 | "UUID": "CD28-379D", 32 | "VERSION": "FAT16", 33 | "BLOCK_SIZE": "512", 34 | "TYPE": "vfat", 35 | "USAGE": "filesystem", 36 | "PART_ENTRY_SCHEME": "gpt", 37 | "PART_ENTRY_UUID": "7969ae8c-416d-6f4d-9bff-9e2047c79857", 38 | "PART_ENTRY_TYPE": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", 39 | "PART_ENTRY_NUMBER": "1", 40 | "PART_ENTRY_OFFSET": "2048", 41 | "PART_ENTRY_SIZE": "126976", 42 | "PART_ENTRY_DISK": "253:0" 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /tests/espgen/grub-gpt-fstab-efi-partitions-reload.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "DEVNAME": "/dev/zram0", 4 | "UUID": "1293710f-3872-47e8-a9a8-be9545021994", 5 | "VERSION": "1", 6 | "TYPE": "swap", 7 | "USAGE": "other" 8 | }, 9 | { 10 | "DEVNAME": "/dev/vda2", 11 | "PART_ENTRY_SCHEME": "gpt", 12 | "PART_ENTRY_UUID": "9eaab1f8-c210-2b4e-8656-e8ed0beb1ab2", 13 | "PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649", 14 | "PART_ENTRY_NUMBER": "2", 15 | "PART_ENTRY_OFFSET": "129024", 16 | "PART_ENTRY_SIZE": "2048", 17 | "PART_ENTRY_DISK": "253:0" 18 | }, 19 | { 20 | "DEVNAME": "/dev/vda3", 21 | "LABEL": "ostree", 22 | "UUID": "cecb0576-cf95-48bf-8b72-aced790e83e9", 23 | "VERSION": "1.0", 24 | "BLOCK_SIZE": "4096", 25 | "TYPE": "ext4", 26 | "USAGE": "filesystem", 27 | "PART_ENTRY_SCHEME": "gpt", 28 | "PART_ENTRY_UUID": "eb219ed1-9e98-4048-9288-1c16db3dea72", 29 | "PART_ENTRY_TYPE": "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", 30 | "PART_ENTRY_NUMBER": "3", 31 | "PART_ENTRY_OFFSET": "131072", 32 | "PART_ENTRY_SIZE": "52297695", 33 | "PART_ENTRY_DISK": "253:0" 34 | }, 35 | { 36 | "DEVNAME": "/dev/vda1", 37 | "SEC_TYPE": "msdos", 38 | "UUID": "CD28-379D", 39 | "VERSION": "FAT16", 40 | "BLOCK_SIZE": "512", 41 | "TYPE": "vfat", 42 | "USAGE": "filesystem", 43 | "PART_ENTRY_SCHEME": "gpt", 44 | "PART_ENTRY_UUID": "7969ae8c-416d-6f4d-9bff-9e2047c79857", 45 | "PART_ENTRY_TYPE": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", 46 | "PART_ENTRY_NUMBER": "1", 47 | "PART_ENTRY_OFFSET": "2048", 48 | "PART_ENTRY_SIZE": "126976", 49 | "PART_ENTRY_DISK": "253:0" 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /tests/espgen/grub-gpt-fstab.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/vda3", 5 | "maj:min": null, 6 | "fstype": "ext4", 7 | "fsroot": null, 8 | "options": "errors=remount-ro" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /tests/espgen/grub-gpt-kcmdline.json: -------------------------------------------------------------------------------- 1 | { 2 | "BOOT_IMAGE": "(hd0,gpt3)/boot/ostree/eos-078584ee3e2b33ee29896c7ebd1533b40f7f7a6d551dcc39cfecd4d8e8f75d51/vmlinuz-6.5.0-10-generic", 3 | "root": "UUID=cecb0576-cf95-48bf-8b72-aced790e83e9", 4 | "rw": null, 5 | "splash": null, 6 | "plymouth.ignore-serial-consoles": null, 7 | "quiet": null, 8 | "loglevel": "0", 9 | "ostree": "/ostree/boot.1/eos/078584ee3e2b33ee29896c7ebd1533b40f7f7a6d551dcc39cfecd4d8e8f75d51/0" 10 | } 11 | -------------------------------------------------------------------------------- /tests/espgen/grub-gpt-mounts-init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/vda3", 5 | "maj:min": "253:3", 6 | "fstype": "ext4", 7 | "fsroot": "/ostree/deploy/eos/deploy/03b591bb31c1d4cb69df9e4a7d7097a48012354d4a3777179d90016ec1893738.0", 8 | "options": "ro,relatime" 9 | }, 10 | { 11 | "target": "/boot", 12 | "source": "/dev/vda3", 13 | "maj:min": "253:3", 14 | "fstype": "ext4", 15 | "fsroot": "/boot", 16 | "options": "ro,relatime" 17 | }, 18 | { 19 | "target": "/usr", 20 | "source": "/dev/vda3", 21 | "maj:min": "253:3", 22 | "fstype": "ext4", 23 | "fsroot": "/ostree/deploy/eos/deploy/03b591bb31c1d4cb69df9e4a7d7097a48012354d4a3777179d90016ec1893738.0/usr", 24 | "options": "ro,relatime" 25 | }, 26 | { 27 | "target": "/sysroot", 28 | "source": "/dev/vda3", 29 | "maj:min": "253:3", 30 | "fstype": "ext4", 31 | "fsroot": "/", 32 | "options": "ro,relatime" 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /tests/espgen/grub-gpt-mounts-reload.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/vda3", 5 | "maj:min": "253:3", 6 | "fstype": "ext4", 7 | "fsroot": "/ostree/deploy/eos/deploy/03b591bb31c1d4cb69df9e4a7d7097a48012354d4a3777179d90016ec1893738.0", 8 | "options": "ro,relatime,errors=remount-ro" 9 | }, 10 | { 11 | "target": "/boot", 12 | "source": "/dev/vda3", 13 | "maj:min": "253:3", 14 | "fstype": "ext4", 15 | "fsroot": "/boot", 16 | "options": "ro,relatime,errors=remount-ro" 17 | }, 18 | { 19 | "target": "/usr", 20 | "source": "/dev/vda3", 21 | "maj:min": "253:3", 22 | "fstype": "ext4", 23 | "fsroot": "/ostree/deploy/eos/deploy/03b591bb31c1d4cb69df9e4a7d7097a48012354d4a3777179d90016ec1893738.0/usr", 24 | "options": "ro,relatime,errors=remount-ro" 25 | }, 26 | { 27 | "target": "/sysroot", 28 | "source": "/dev/vda3", 29 | "maj:min": "253:3", 30 | "fstype": "ext4", 31 | "fsroot": "/", 32 | "options": "ro,relatime,errors=remount-ro" 33 | }, 34 | { 35 | "target": "/var", 36 | "source": "/dev/vda3", 37 | "maj:min": "253:3", 38 | "fstype": "ext4", 39 | "fsroot": "/ostree/deploy/eos/var", 40 | "options": "ro,relatime,errors=remount-ro" 41 | }, 42 | { 43 | "target": "/efi", 44 | "source": "/dev/vda1", 45 | "maj:min": "253:1", 46 | "fstype": "vfat", 47 | "fsroot": "/", 48 | "options": "rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro" 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /tests/espgen/grub-gpt-partitions-init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "DEVNAME": "/dev/vda2", 4 | "PART_ENTRY_SCHEME": "gpt", 5 | "PART_ENTRY_UUID": "9eaab1f8-c210-2b4e-8656-e8ed0beb1ab2", 6 | "PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649", 7 | "PART_ENTRY_NUMBER": "2", 8 | "PART_ENTRY_OFFSET": "129024", 9 | "PART_ENTRY_SIZE": "2048", 10 | "PART_ENTRY_DISK": "253:0" 11 | }, 12 | { 13 | "DEVNAME": "/dev/vda3", 14 | "LABEL": "ostree", 15 | "UUID": "cecb0576-cf95-48bf-8b72-aced790e83e9", 16 | "VERSION": "1.0", 17 | "BLOCK_SIZE": "4096", 18 | "TYPE": "ext4", 19 | "USAGE": "filesystem", 20 | "PART_ENTRY_SCHEME": "gpt", 21 | "PART_ENTRY_UUID": "eb219ed1-9e98-4048-9288-1c16db3dea72", 22 | "PART_ENTRY_TYPE": "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", 23 | "PART_ENTRY_NUMBER": "3", 24 | "PART_ENTRY_OFFSET": "131072", 25 | "PART_ENTRY_SIZE": "52297695", 26 | "PART_ENTRY_DISK": "253:0" 27 | }, 28 | { 29 | "DEVNAME": "/dev/vda1", 30 | "SEC_TYPE": "msdos", 31 | "UUID": "CD28-379D", 32 | "VERSION": "FAT16", 33 | "BLOCK_SIZE": "512", 34 | "TYPE": "vfat", 35 | "USAGE": "filesystem", 36 | "PART_ENTRY_SCHEME": "gpt", 37 | "PART_ENTRY_UUID": "7969ae8c-416d-6f4d-9bff-9e2047c79857", 38 | "PART_ENTRY_TYPE": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", 39 | "PART_ENTRY_NUMBER": "1", 40 | "PART_ENTRY_OFFSET": "2048", 41 | "PART_ENTRY_SIZE": "126976", 42 | "PART_ENTRY_DISK": "253:0" 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /tests/espgen/grub-gpt-partitions-reload.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "DEVNAME": "/dev/zram0", 4 | "UUID": "1293710f-3872-47e8-a9a8-be9545021994", 5 | "VERSION": "1", 6 | "TYPE": "swap", 7 | "USAGE": "other" 8 | }, 9 | { 10 | "DEVNAME": "/dev/vda2", 11 | "PART_ENTRY_SCHEME": "gpt", 12 | "PART_ENTRY_UUID": "9eaab1f8-c210-2b4e-8656-e8ed0beb1ab2", 13 | "PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649", 14 | "PART_ENTRY_NUMBER": "2", 15 | "PART_ENTRY_OFFSET": "129024", 16 | "PART_ENTRY_SIZE": "2048", 17 | "PART_ENTRY_DISK": "253:0" 18 | }, 19 | { 20 | "DEVNAME": "/dev/vda3", 21 | "LABEL": "ostree", 22 | "UUID": "cecb0576-cf95-48bf-8b72-aced790e83e9", 23 | "VERSION": "1.0", 24 | "BLOCK_SIZE": "4096", 25 | "TYPE": "ext4", 26 | "USAGE": "filesystem", 27 | "PART_ENTRY_SCHEME": "gpt", 28 | "PART_ENTRY_UUID": "eb219ed1-9e98-4048-9288-1c16db3dea72", 29 | "PART_ENTRY_TYPE": "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", 30 | "PART_ENTRY_NUMBER": "3", 31 | "PART_ENTRY_OFFSET": "131072", 32 | "PART_ENTRY_SIZE": "52297695", 33 | "PART_ENTRY_DISK": "253:0" 34 | }, 35 | { 36 | "DEVNAME": "/dev/vda1", 37 | "SEC_TYPE": "msdos", 38 | "UUID": "CD28-379D", 39 | "VERSION": "FAT16", 40 | "BLOCK_SIZE": "512", 41 | "TYPE": "vfat", 42 | "USAGE": "filesystem", 43 | "PART_ENTRY_SCHEME": "gpt", 44 | "PART_ENTRY_UUID": "7969ae8c-416d-6f4d-9bff-9e2047c79857", 45 | "PART_ENTRY_TYPE": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", 46 | "PART_ENTRY_NUMBER": "1", 47 | "PART_ENTRY_OFFSET": "2048", 48 | "PART_ENTRY_SIZE": "126976", 49 | "PART_ENTRY_DISK": "253:0" 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /tests/espgen/grub-mbr-fstab.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/vda2", 5 | "maj:min": null, 6 | "fstype": "ext4", 7 | "fsroot": null, 8 | "options": "errors=remount-ro" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /tests/espgen/grub-mbr-kcmdline.json: -------------------------------------------------------------------------------- 1 | { 2 | "BOOT_IMAGE": "(hd0,msdos2)/boot/ostree/eos-078584ee3e2b33ee29896c7ebd1533b40f7f7a6d551dcc39cfecd4d8e8f75d51/vmlinuz-6.5.0-10-generic", 3 | "root": "UUID=dde5e094-c22d-4b11-86de-e6c880085f93", 4 | "rw": null, 5 | "splash": null, 6 | "plymouth.ignore-serial-consoles": null, 7 | "quiet": null, 8 | "loglevel": "0", 9 | "ostree": "/ostree/boot.0/eos/078584ee3e2b33ee29896c7ebd1533b40f7f7a6d551dcc39cfecd4d8e8f75d51/0" 10 | } 11 | -------------------------------------------------------------------------------- /tests/espgen/grub-mbr-mounts-init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/vda2", 5 | "maj:min": "253:2", 6 | "fstype": "ext4", 7 | "fsroot": "/ostree/deploy/eos/deploy/587b6f5ddcd32337a8b27ac3fe07f6141965fc455200f07f32d2a4ae7c089028.0", 8 | "options": "ro,relatime" 9 | }, 10 | { 11 | "target": "/boot", 12 | "source": "/dev/vda2", 13 | "maj:min": "253:2", 14 | "fstype": "ext4", 15 | "fsroot": "/boot", 16 | "options": "ro,relatime" 17 | }, 18 | { 19 | "target": "/usr", 20 | "source": "/dev/vda2", 21 | "maj:min": "253:2", 22 | "fstype": "ext4", 23 | "fsroot": "/ostree/deploy/eos/deploy/587b6f5ddcd32337a8b27ac3fe07f6141965fc455200f07f32d2a4ae7c089028.0/usr", 24 | "options": "ro,relatime" 25 | }, 26 | { 27 | "target": "/sysroot", 28 | "source": "/dev/vda2", 29 | "maj:min": "253:2", 30 | "fstype": "ext4", 31 | "fsroot": "/", 32 | "options": "ro,relatime" 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /tests/espgen/grub-mbr-mounts-reload.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/vda2", 5 | "maj:min": "253:2", 6 | "fstype": "ext4", 7 | "fsroot": "/ostree/deploy/eos/deploy/587b6f5ddcd32337a8b27ac3fe07f6141965fc455200f07f32d2a4ae7c089028.0", 8 | "options": "ro,relatime,errors=remount-ro" 9 | }, 10 | { 11 | "target": "/boot", 12 | "source": "/dev/vda2", 13 | "maj:min": "253:2", 14 | "fstype": "ext4", 15 | "fsroot": "/boot", 16 | "options": "ro,relatime,errors=remount-ro" 17 | }, 18 | { 19 | "target": "/usr", 20 | "source": "/dev/vda2", 21 | "maj:min": "253:2", 22 | "fstype": "ext4", 23 | "fsroot": "/ostree/deploy/eos/deploy/587b6f5ddcd32337a8b27ac3fe07f6141965fc455200f07f32d2a4ae7c089028.0/usr", 24 | "options": "ro,relatime,errors=remount-ro" 25 | }, 26 | { 27 | "target": "/sysroot", 28 | "source": "/dev/vda2", 29 | "maj:min": "253:2", 30 | "fstype": "ext4", 31 | "fsroot": "/", 32 | "options": "ro,relatime,errors=remount-ro" 33 | }, 34 | { 35 | "target": "/var", 36 | "source": "/dev/vda2", 37 | "maj:min": "253:2", 38 | "fstype": "ext4", 39 | "fsroot": "/ostree/deploy/eos/var", 40 | "options": "ro,relatime,errors=remount-ro" 41 | }, 42 | { 43 | "target": "/efi", 44 | "source": "/dev/vda1", 45 | "maj:min": "253:1", 46 | "fstype": "vfat", 47 | "fsroot": "/", 48 | "options": "rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro" 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /tests/espgen/grub-mbr-partitions-init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "DEVNAME": "/dev/vda2", 4 | "LABEL": "ostree", 5 | "UUID": "dde5e094-c22d-4b11-86de-e6c880085f93", 6 | "VERSION": "1.0", 7 | "BLOCK_SIZE": "4096", 8 | "TYPE": "ext4", 9 | "USAGE": "filesystem", 10 | "PART_ENTRY_SCHEME": "dos", 11 | "PART_ENTRY_UUID": "680dae45-02", 12 | "PART_ENTRY_TYPE": "0x83", 13 | "PART_ENTRY_FLAGS": "0x80", 14 | "PART_ENTRY_NUMBER": "2", 15 | "PART_ENTRY_OFFSET": "131072", 16 | "PART_ENTRY_SIZE": "88487695", 17 | "PART_ENTRY_DISK": "253:0" 18 | }, 19 | { 20 | "DEVNAME": "/dev/vda1", 21 | "SEC_TYPE": "msdos", 22 | "UUID": "6D78-30F7", 23 | "VERSION": "FAT16", 24 | "BLOCK_SIZE": "512", 25 | "TYPE": "vfat", 26 | "USAGE": "filesystem", 27 | "PART_ENTRY_SCHEME": "dos", 28 | "PART_ENTRY_UUID": "680dae45-01", 29 | "PART_ENTRY_TYPE": "0xef", 30 | "PART_ENTRY_NUMBER": "1", 31 | "PART_ENTRY_OFFSET": "2048", 32 | "PART_ENTRY_SIZE": "126976", 33 | "PART_ENTRY_DISK": "253:0" 34 | } 35 | ] 36 | -------------------------------------------------------------------------------- /tests/espgen/grub-mbr-partitions-reload.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "DEVNAME": "/dev/zram0", 4 | "UUID": "dc44bec6-3776-441e-96f5-b77aac2a7505", 5 | "VERSION": "1", 6 | "TYPE": "swap", 7 | "USAGE": "other" 8 | }, 9 | { 10 | "DEVNAME": "/dev/vda2", 11 | "LABEL": "ostree", 12 | "UUID": "dde5e094-c22d-4b11-86de-e6c880085f93", 13 | "VERSION": "1.0", 14 | "BLOCK_SIZE": "4096", 15 | "TYPE": "ext4", 16 | "USAGE": "filesystem", 17 | "PART_ENTRY_SCHEME": "dos", 18 | "PART_ENTRY_UUID": "680dae45-02", 19 | "PART_ENTRY_TYPE": "0x83", 20 | "PART_ENTRY_FLAGS": "0x80", 21 | "PART_ENTRY_NUMBER": "2", 22 | "PART_ENTRY_OFFSET": "131072", 23 | "PART_ENTRY_SIZE": "88487695", 24 | "PART_ENTRY_DISK": "253:0" 25 | }, 26 | { 27 | "DEVNAME": "/dev/vda1", 28 | "SEC_TYPE": "msdos", 29 | "UUID": "6D78-30F7", 30 | "VERSION": "FAT16", 31 | "BLOCK_SIZE": "512", 32 | "TYPE": "vfat", 33 | "USAGE": "filesystem", 34 | "PART_ENTRY_SCHEME": "dos", 35 | "PART_ENTRY_UUID": "680dae45-01", 36 | "PART_ENTRY_TYPE": "0xef", 37 | "PART_ENTRY_NUMBER": "1", 38 | "PART_ENTRY_OFFSET": "2048", 39 | "PART_ENTRY_SIZE": "126976", 40 | "PART_ENTRY_DISK": "253:0" 41 | } 42 | ] 43 | -------------------------------------------------------------------------------- /tests/espgen/sdboot-fstab.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/vda3", 5 | "maj:min": null, 6 | "fstype": "ext4", 7 | "fsroot": null, 8 | "options": "errors=remount-ro" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /tests/espgen/sdboot-kcmdline.json: -------------------------------------------------------------------------------- 1 | { 2 | "eospayg": null, 3 | "efi_no_storage_paranoia": null, 4 | "rd.shell": "0", 5 | "rw": null, 6 | "splash": null, 7 | "plymouth.ignore-serial-consoles": null, 8 | "quiet": null, 9 | "loglevel": "0", 10 | "ostree": "/ostree/boot.0/eos/de6ecb1835fa999f3d337c4b719b6b1259f829d4e938703a1316949622612ba6/0" 11 | } 12 | -------------------------------------------------------------------------------- /tests/espgen/sdboot-mounts-init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/sysroot", 4 | "source": "/dev/vda3", 5 | "maj:min": "253:3", 6 | "fstype": "ext4", 7 | "fsroot": "/", 8 | "options": "rw,relatime" 9 | }, 10 | { 11 | "target": "/", 12 | "source": "/dev/vda3", 13 | "maj:min": "253:3", 14 | "fstype": "ext4", 15 | "fsroot": "/ostree/deploy/eos/deploy/d590ffda3b7eebc41121fde0a05c10d5638c5a0facb51ec7437268399dfc51dd.0", 16 | "options": "rw,relatime" 17 | }, 18 | { 19 | "target": "/usr", 20 | "source": "/dev/vda3", 21 | "maj:min": "253:3", 22 | "fstype": "ext4", 23 | "fsroot": "/ostree/deploy/eos/deploy/d590ffda3b7eebc41121fde0a05c10d5638c5a0facb51ec7437268399dfc51dd.0/usr", 24 | "options": "ro,relatime" 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /tests/espgen/sdboot-mounts-reload.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/sysroot", 4 | "source": "/dev/vda3", 5 | "maj:min": "253:3", 6 | "fstype": "ext4", 7 | "fsroot": "/", 8 | "options": "rw,relatime,errors=remount-ro" 9 | }, 10 | { 11 | "target": "/", 12 | "source": "/dev/vda3", 13 | "maj:min": "253:3", 14 | "fstype": "ext4", 15 | "fsroot": "/ostree/deploy/eos/deploy/d590ffda3b7eebc41121fde0a05c10d5638c5a0facb51ec7437268399dfc51dd.0", 16 | "options": "rw,relatime,errors=remount-ro" 17 | }, 18 | { 19 | "target": "/usr", 20 | "source": "/dev/vda3", 21 | "maj:min": "253:3", 22 | "fstype": "ext4", 23 | "fsroot": "/ostree/deploy/eos/deploy/d590ffda3b7eebc41121fde0a05c10d5638c5a0facb51ec7437268399dfc51dd.0/usr", 24 | "options": "ro,relatime,errors=remount-ro" 25 | }, 26 | { 27 | "target": "/var", 28 | "source": "/dev/vda3", 29 | "maj:min": "253:3", 30 | "fstype": "ext4", 31 | "fsroot": "/ostree/deploy/eos/var", 32 | "options": "rw,relatime,errors=remount-ro" 33 | }, 34 | { 35 | "target": "/boot", 36 | "source": "/dev/vda1", 37 | "maj:min": "253:1", 38 | "fstype": "vfat", 39 | "fsroot": "/", 40 | "options": "rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro" 41 | } 42 | ] 43 | -------------------------------------------------------------------------------- /tests/espgen/sdboot-partitions-init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "DEVNAME": "/dev/vda1", 4 | "SEC_TYPE": "msdos", 5 | "UUID": "B5E9-EA49", 6 | "VERSION": "FAT16", 7 | "BLOCK_SIZE": "512", 8 | "TYPE": "vfat", 9 | "USAGE": "filesystem", 10 | "PART_ENTRY_SCHEME": "gpt", 11 | "PART_ENTRY_UUID": "0189f938-7e9e-194d-8757-e85edaaca5b3", 12 | "PART_ENTRY_TYPE": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", 13 | "PART_ENTRY_NUMBER": "1", 14 | "PART_ENTRY_OFFSET": "2048", 15 | "PART_ENTRY_SIZE": "1024000", 16 | "PART_ENTRY_DISK": "253:0" 17 | }, 18 | { 19 | "DEVNAME": "/dev/vda2", 20 | "PART_ENTRY_SCHEME": "gpt", 21 | "PART_ENTRY_UUID": "e7fb50ac-d074-5c4d-8d1e-92f91b4f0718", 22 | "PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649", 23 | "PART_ENTRY_NUMBER": "2", 24 | "PART_ENTRY_OFFSET": "1026048", 25 | "PART_ENTRY_SIZE": "2048", 26 | "PART_ENTRY_DISK": "253:0" 27 | }, 28 | { 29 | "DEVNAME": "/dev/vda3", 30 | "LABEL": "ostree", 31 | "UUID": "2bad2e5e-d97c-4c87-ae37-aa8ed2279c9a", 32 | "VERSION": "1.0", 33 | "BLOCK_SIZE": "4096", 34 | "TYPE": "ext4", 35 | "USAGE": "filesystem", 36 | "PART_ENTRY_SCHEME": "gpt", 37 | "PART_ENTRY_UUID": "b47e35b3-8d0d-7347-b2e1-6303cc628984", 38 | "PART_ENTRY_TYPE": "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", 39 | "PART_ENTRY_NUMBER": "3", 40 | "PART_ENTRY_OFFSET": "1028096", 41 | "PART_ENTRY_SIZE": "499090063", 42 | "PART_ENTRY_DISK": "253:0" 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /tests/espgen/sdboot-partitions-reload.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "DEVNAME": "/dev/vda1", 4 | "SEC_TYPE": "msdos", 5 | "UUID": "B5E9-EA49", 6 | "VERSION": "FAT16", 7 | "BLOCK_SIZE": "512", 8 | "TYPE": "vfat", 9 | "USAGE": "filesystem", 10 | "PART_ENTRY_SCHEME": "gpt", 11 | "PART_ENTRY_UUID": "0189f938-7e9e-194d-8757-e85edaaca5b3", 12 | "PART_ENTRY_TYPE": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", 13 | "PART_ENTRY_NUMBER": "1", 14 | "PART_ENTRY_OFFSET": "2048", 15 | "PART_ENTRY_SIZE": "1024000", 16 | "PART_ENTRY_DISK": "253:0" 17 | }, 18 | { 19 | "DEVNAME": "/dev/vda2", 20 | "PART_ENTRY_SCHEME": "gpt", 21 | "PART_ENTRY_UUID": "e7fb50ac-d074-5c4d-8d1e-92f91b4f0718", 22 | "PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649", 23 | "PART_ENTRY_NUMBER": "2", 24 | "PART_ENTRY_OFFSET": "1026048", 25 | "PART_ENTRY_SIZE": "2048", 26 | "PART_ENTRY_DISK": "253:0" 27 | }, 28 | { 29 | "DEVNAME": "/dev/vda3", 30 | "LABEL": "ostree", 31 | "UUID": "2bad2e5e-d97c-4c87-ae37-aa8ed2279c9a", 32 | "VERSION": "1.0", 33 | "BLOCK_SIZE": "4096", 34 | "TYPE": "ext4", 35 | "USAGE": "filesystem", 36 | "PART_ENTRY_SCHEME": "gpt", 37 | "PART_ENTRY_UUID": "b47e35b3-8d0d-7347-b2e1-6303cc628984", 38 | "PART_ENTRY_TYPE": "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", 39 | "PART_ENTRY_NUMBER": "3", 40 | "PART_ENTRY_OFFSET": "1028096", 41 | "PART_ENTRY_SIZE": "499090063", 42 | "PART_ENTRY_DISK": "253:0" 43 | }, 44 | { 45 | "DEVNAME": "/dev/zram0", 46 | "UUID": "6965bcd9-5f8b-42d2-8c2e-12d0e8d22163", 47 | "VERSION": "1", 48 | "TYPE": "swap", 49 | "USAGE": "other" 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /tests/espgen/sdboot-xbootldr-fstab.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/vda3", 5 | "maj:min": null, 6 | "fstype": "ext4", 7 | "fsroot": null, 8 | "options": "errors=remount-ro" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /tests/espgen/sdboot-xbootldr-kcmdline.json: -------------------------------------------------------------------------------- 1 | { 2 | "eospayg": null, 3 | "efi_no_storage_paranoia": null, 4 | "rd.shell": "0", 5 | "rw": null, 6 | "splash": null, 7 | "plymouth.ignore-serial-consoles": null, 8 | "quiet": null, 9 | "loglevel": "0", 10 | "ostree": "/ostree/boot.0/eos/de6ecb1835fa999f3d337c4b719b6b1259f829d4e938703a1316949622612ba6/0" 11 | } 12 | -------------------------------------------------------------------------------- /tests/espgen/sdboot-xbootldr-mounts-init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/sysroot", 4 | "source": "/dev/vda4", 5 | "maj:min": "253:4", 6 | "fstype": "ext4", 7 | "fsroot": "/", 8 | "options": "rw,relatime" 9 | }, 10 | { 11 | "target": "/", 12 | "source": "/dev/vda4", 13 | "maj:min": "253:4", 14 | "fstype": "ext4", 15 | "fsroot": "/ostree/deploy/eos/deploy/d590ffda3b7eebc41121fde0a05c10d5638c5a0facb51ec7437268399dfc51dd.0", 16 | "options": "rw,relatime" 17 | }, 18 | { 19 | "target": "/usr", 20 | "source": "/dev/vda4", 21 | "maj:min": "253:4", 22 | "fstype": "ext4", 23 | "fsroot": "/ostree/deploy/eos/deploy/d590ffda3b7eebc41121fde0a05c10d5638c5a0facb51ec7437268399dfc51dd.0/usr", 24 | "options": "ro,relatime" 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /tests/espgen/sdboot-xbootldr-mounts-reload.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/sysroot", 4 | "source": "/dev/vda4", 5 | "maj:min": "253:4", 6 | "fstype": "ext4", 7 | "fsroot": "/", 8 | "options": "rw,relatime,errors=remount-ro" 9 | }, 10 | { 11 | "target": "/", 12 | "source": "/dev/vda4", 13 | "maj:min": "253:4", 14 | "fstype": "ext4", 15 | "fsroot": "/ostree/deploy/eos/deploy/d590ffda3b7eebc41121fde0a05c10d5638c5a0facb51ec7437268399dfc51dd.0", 16 | "options": "rw,relatime,errors=remount-ro" 17 | }, 18 | { 19 | "target": "/usr", 20 | "source": "/dev/vda4", 21 | "maj:min": "253:4", 22 | "fstype": "ext4", 23 | "fsroot": "/ostree/deploy/eos/deploy/d590ffda3b7eebc41121fde0a05c10d5638c5a0facb51ec7437268399dfc51dd.0/usr", 24 | "options": "ro,relatime,errors=remount-ro" 25 | }, 26 | { 27 | "target": "/var", 28 | "source": "/dev/vda4", 29 | "maj:min": "253:4", 30 | "fstype": "ext4", 31 | "fsroot": "/ostree/deploy/eos/var", 32 | "options": "rw,relatime,errors=remount-ro" 33 | }, 34 | { 35 | "target": "/efi", 36 | "source": "/dev/vda1", 37 | "maj:min": "253:1", 38 | "fstype": "vfat", 39 | "fsroot": "/", 40 | "options": "rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro" 41 | }, 42 | { 43 | "target": "/boot", 44 | "source": "/dev/vda3", 45 | "maj:min": "253:3", 46 | "fstype": "vfat", 47 | "fsroot": "/", 48 | "options": "rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro" 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /tests/espgen/sdboot-xbootldr-partitions-init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "DEVNAME": "/dev/vda1", 4 | "SEC_TYPE": "msdos", 5 | "UUID": "B5E9-EA49", 6 | "VERSION": "FAT16", 7 | "BLOCK_SIZE": "512", 8 | "TYPE": "vfat", 9 | "USAGE": "filesystem", 10 | "PART_ENTRY_SCHEME": "gpt", 11 | "PART_ENTRY_UUID": "0189f938-7e9e-194d-8757-e85edaaca5b3", 12 | "PART_ENTRY_TYPE": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", 13 | "PART_ENTRY_NUMBER": "1", 14 | "PART_ENTRY_OFFSET": "2048", 15 | "PART_ENTRY_SIZE": "1024000", 16 | "PART_ENTRY_DISK": "253:0" 17 | }, 18 | { 19 | "DEVNAME": "/dev/vda2", 20 | "PART_ENTRY_SCHEME": "gpt", 21 | "PART_ENTRY_UUID": "4c4f3323-e487-f145-9a01-2afa67956dc6", 22 | "PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649", 23 | "part_ENTRY_NUMBER": "2", 24 | "PART_ENTRY_OFFSET": "1026048", 25 | "PART_ENTRY_SIZE": "2048", 26 | "PART_ENTRY_DISK": "253:0" 27 | }, 28 | { 29 | "DEVNAME": "/dev/vda3", 30 | "SEC_TYPE": "msdos", 31 | "UUID": "3C18-31F6", 32 | "VERSION": "FAT16", 33 | "BLOCK_SIZE": "512", 34 | "TYPE": "vfat", 35 | "USAGE": "filesystem", 36 | "PART_ENTRY_SCHEME": "gpt", 37 | "PART_ENTRY_UUID": "c3c8d283-ec36-534c-be18-1b41377f3d40", 38 | "PART_ENTRY_TYPE": "bc13c2ff-59e6-4262-a352-b275fd6f7172", 39 | "PART_ENTRY_NUMBER": "3", 40 | "PART_ENTRY_OFFSET": "1028096", 41 | "PART_ENTRY_SIZE": "409600", 42 | "PART_ENTRY_DISK": "253:0" 43 | }, 44 | { 45 | "DEVNAME": "/dev/vda4", 46 | "LABEL": "ostree", 47 | "UUID": "2bad2e5e-d97c-4c87-ae37-aa8ed2279c9a", 48 | "VERSION": "1.0", 49 | "BLOCK_SIZE": "4096", 50 | "TYPE": "ext4", 51 | "USAGE": "filesystem", 52 | "PART_ENTRY_SCHEME": "gpt", 53 | "PART_ENTRY_UUID": "b47e35b3-8d0d-7347-b2e1-6303cc628984", 54 | "PART_ENTRY_TYPE": "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", 55 | "PART_ENTRY_NUMBER": "4", 56 | "PART_ENTRY_OFFSET": "1028096", 57 | "PART_ENTRY_SIZE": "499090063", 58 | "PART_ENTRY_DISK": "253:0" 59 | } 60 | ] 61 | -------------------------------------------------------------------------------- /tests/espgen/sdboot-xbootldr-partitions-reload.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "DEVNAME": "/dev/vda1", 4 | "SEC_TYPE": "msdos", 5 | "UUID": "B5E9-EA49", 6 | "VERSION": "FAT16", 7 | "BLOCK_SIZE": "512", 8 | "TYPE": "vfat", 9 | "USAGE": "filesystem", 10 | "PART_ENTRY_SCHEME": "gpt", 11 | "PART_ENTRY_UUID": "0189f938-7e9e-194d-8757-e85edaaca5b3", 12 | "PART_ENTRY_TYPE": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", 13 | "PART_ENTRY_NUMBER": "1", 14 | "PART_ENTRY_OFFSET": "2048", 15 | "PART_ENTRY_SIZE": "1024000", 16 | "PART_ENTRY_DISK": "253:0" 17 | }, 18 | { 19 | "DEVNAME": "/dev/vda2", 20 | "PART_ENTRY_SCHEME": "gpt", 21 | "PART_ENTRY_UUID": "4c4f3323-e487-f145-9a01-2afa67956dc6", 22 | "PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649", 23 | "part_ENTRY_NUMBER": "2", 24 | "PART_ENTRY_OFFSET": "1026048", 25 | "PART_ENTRY_SIZE": "2048", 26 | "PART_ENTRY_DISK": "253:0" 27 | }, 28 | { 29 | "DEVNAME": "/dev/vda3", 30 | "SEC_TYPE": "msdos", 31 | "UUID": "3C18-31F6", 32 | "VERSION": "FAT16", 33 | "BLOCK_SIZE": "512", 34 | "TYPE": "vfat", 35 | "USAGE": "filesystem", 36 | "PART_ENTRY_SCHEME": "gpt", 37 | "PART_ENTRY_UUID": "c3c8d283-ec36-534c-be18-1b41377f3d40", 38 | "PART_ENTRY_TYPE": "bc13c2ff-59e6-4262-a352-b275fd6f7172", 39 | "PART_ENTRY_NUMBER": "3", 40 | "PART_ENTRY_OFFSET": "1028096", 41 | "PART_ENTRY_SIZE": "409600", 42 | "PART_ENTRY_DISK": "253:0" 43 | }, 44 | { 45 | "DEVNAME": "/dev/vda4", 46 | "LABEL": "ostree", 47 | "UUID": "2bad2e5e-d97c-4c87-ae37-aa8ed2279c9a", 48 | "VERSION": "1.0", 49 | "BLOCK_SIZE": "4096", 50 | "TYPE": "ext4", 51 | "USAGE": "filesystem", 52 | "PART_ENTRY_SCHEME": "gpt", 53 | "PART_ENTRY_UUID": "b47e35b3-8d0d-7347-b2e1-6303cc628984", 54 | "PART_ENTRY_TYPE": "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", 55 | "PART_ENTRY_NUMBER": "4", 56 | "PART_ENTRY_OFFSET": "1028096", 57 | "PART_ENTRY_SIZE": "499090063", 58 | "PART_ENTRY_DISK": "253:0" 59 | }, 60 | { 61 | "DEVNAME": "/dev/zram0", 62 | "UUID": "6965bcd9-5f8b-42d2-8c2e-12d0e8d22163", 63 | "VERSION": "1", 64 | "TYPE": "swap", 65 | "USAGE": "other" 66 | } 67 | ] 68 | -------------------------------------------------------------------------------- /tests/espgen/windows-efi-fstab.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/loop0p3", 5 | "maj:min": null, 6 | "fstype": "ext4", 7 | "fsroot": null, 8 | "options": "errors=remount-ro" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /tests/espgen/windows-efi-kcmdline.json: -------------------------------------------------------------------------------- 1 | { 2 | "BOOT_IMAGE": "(loop_img,gpt3)/boot/ostree/eos-05199b9a534975d167eff9621ff44b04342dff52cb692b76ab3f2cd2f5d6bb5e/vmlinuz-6.5.0-10-generic", 3 | "rw": null, 4 | "splash": null, 5 | "plymouth.ignore-serial-consoles": null, 6 | "quiet": null, 7 | "loglevel": "0", 8 | "ostree": "/ostree/boot.1/eos/05199b9a534975d167eff9621ff44b04342dff52cb692b76ab3f2cd2f5d6bb5e/0", 9 | "endless.image.device": "UUID=107EB4247EB4048E", 10 | "endless.image.path": "/endless/endless.img", 11 | "nohibernate": null 12 | } 13 | -------------------------------------------------------------------------------- /tests/espgen/windows-efi-mounts-init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/loop0p3", 5 | "maj:min": "259:2", 6 | "fstype": "ext4", 7 | "fsroot": "/ostree/deploy/eos/deploy/2b89dbb29d8a02e2fcc96b9812dcb996222f7b38d064e303c899bc04f845cbc3.0", 8 | "options": "ro,relatime" 9 | }, 10 | { 11 | "target": "/boot", 12 | "source": "/dev/loop0p3", 13 | "maj:min": "259:2", 14 | "fstype": "ext4", 15 | "fsroot": "/boot", 16 | "options": "ro,relatime" 17 | }, 18 | { 19 | "target": "/usr", 20 | "source": "/dev/loop0p3", 21 | "maj:min": "259:2", 22 | "fstype": "ext4", 23 | "fsroot": "/ostree/deploy/eos/deploy/2b89dbb29d8a02e2fcc96b9812dcb996222f7b38d064e303c899bc04f845cbc3.0/usr", 24 | "options": "ro,relatime" 25 | }, 26 | { 27 | "target": "/sysroot", 28 | "source": "/dev/loop0p3", 29 | "maj:min": "259:2", 30 | "fstype": "ext4", 31 | "fsroot": "/", 32 | "options": "ro,relatime" 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /tests/espgen/windows-efi-mounts-reload.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "target": "/", 4 | "source": "/dev/loop0p3", 5 | "maj:min": "259:2", 6 | "fstype": "ext4", 7 | "fsroot": "/ostree/deploy/eos/deploy/2b89dbb29d8a02e2fcc96b9812dcb996222f7b38d064e303c899bc04f845cbc3.0", 8 | "options": "ro,relatime,errors=remount-ro" 9 | }, 10 | { 11 | "target": "/boot", 12 | "source": "/dev/loop0p3", 13 | "maj:min": "259:2", 14 | "fstype": "ext4", 15 | "fsroot": "/boot", 16 | "options": "ro,relatime,errors=remount-ro" 17 | }, 18 | { 19 | "target": "/usr", 20 | "source": "/dev/loop0p3", 21 | "maj:min": "259:2", 22 | "fstype": "ext4", 23 | "fsroot": "/ostree/deploy/eos/deploy/2b89dbb29d8a02e2fcc96b9812dcb996222f7b38d064e303c899bc04f845cbc3.0/usr", 24 | "options": "ro,relatime,errors=remount-ro" 25 | }, 26 | { 27 | "target": "/sysroot", 28 | "source": "/dev/loop0p3", 29 | "maj:min": "259:2", 30 | "fstype": "ext4", 31 | "fsroot": "/", 32 | "options": "ro,relatime,errors=remount-ro" 33 | }, 34 | { 35 | "target": "/var", 36 | "source": "/dev/loop0p3", 37 | "maj:min": "259:2", 38 | "fstype": "ext4", 39 | "fsroot": "/ostree/deploy/eos/var", 40 | "options": "ro,relatime,errors=remount-ro" 41 | }, 42 | { 43 | "target": "/efi", 44 | "source": "/dev/sda1", 45 | "maj:min": "8:1", 46 | "fstype": "vfat", 47 | "fsroot": "/", 48 | "options": "rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro" 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /tests/espgen/windows-efi-partitions-init.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "DEVNAME": "/dev/loop0p3", 4 | "LABEL": "ostree", 5 | "UUID": "4bdeb166-7be1-40d8-a73c-6555db153bf5", 6 | "VERSION": "1.0", 7 | "BLOCK_SIZE": "4096", 8 | "TYPE": "ext4", 9 | "USAGE": "filesystem", 10 | "PART_ENTRY_SCHEME": "gpt", 11 | "PART_ENTRY_UUID": "cae19154-af25-7b4c-972c-ad7e1bf16501", 12 | "PART_ENTRY_TYPE": "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", 13 | "PART_ENTRY_NUMBER": "3", 14 | "PART_ENTRY_OFFSET": "131072", 15 | "PART_ENTRY_SIZE": "33933927", 16 | "PART_ENTRY_DISK": "7:0" 17 | }, 18 | { 19 | "DEVNAME": "/dev/loop0p1", 20 | "SEC_TYPE": "msdos", 21 | "UUID": "AE27-C340", 22 | "VERSION": "FAT16", 23 | "BLOCK_SIZE": "512", 24 | "TYPE": "vfat", 25 | "USAGE": "filesystem", 26 | "PART_ENTRY_SCHEME": "gpt", 27 | "PART_ENTRY_UUID": "f4293fff-3134-2a49-a106-3381175ea360", 28 | "PART_ENTRY_TYPE": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", 29 | "PART_ENTRY_NUMBER": "1", 30 | "PART_ENTRY_OFFSET": "2048", 31 | "PART_ENTRY_SIZE": "126976", 32 | "PART_ENTRY_DISK": "7:0" 33 | }, 34 | { 35 | "DEVNAME": "/dev/loop0p2", 36 | "PART_ENTRY_SCHEME": "gpt", 37 | "PART_ENTRY_UUID": "68b1bc0f-1961-ac4f-81cc-1b22ba659ce0", 38 | "PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649", 39 | "PART_ENTRY_NUMBER": "2", 40 | "PART_ENTRY_OFFSET": "129024", 41 | "PART_ENTRY_SIZE": "2048", 42 | "PART_ENTRY_DISK": "7:0" 43 | }, 44 | { 45 | "DEVNAME": "/dev/sda4", 46 | "BLOCK_SIZE": "512", 47 | "UUID": "ECACABC7ACAB8AA2", 48 | "TYPE": "ntfs", 49 | "USAGE": "filesystem", 50 | "PART_ENTRY_SCHEME": "gpt", 51 | "PART_ENTRY_UUID": "8236a774-8b07-41fc-b213-c426fe500760", 52 | "PART_ENTRY_TYPE": "de94bba4-06d1-4d40-a16a-bfd50179d6ac", 53 | "PART_ENTRY_FLAGS": "0x8000000000000001", 54 | "PART_ENTRY_NUMBER": "4", 55 | "PART_ENTRY_OFFSET": "103784448", 56 | "PART_ENTRY_SIZE": "1069056", 57 | "PART_ENTRY_DISK": "8:0" 58 | }, 59 | { 60 | "DEVNAME": "/dev/sda2", 61 | "PART_ENTRY_SCHEME": "gpt", 62 | "PART_ENTRY_NAME": "Microsoft reserved partition", 63 | "PART_ENTRY_UUID": "db00106a-064f-46ff-8310-ad78b8830282", 64 | "PART_ENTRY_TYPE": "e3c9e316-0b5c-4db8-817d-f92df00215ae", 65 | "PART_ENTRY_FLAGS": "0x8000000000000000", 66 | "PART_ENTRY_NUMBER": "2", 67 | "PART_ENTRY_OFFSET": "206848", 68 | "PART_ENTRY_SIZE": "32768", 69 | "PART_ENTRY_DISK": "8:0" 70 | }, 71 | { 72 | "DEVNAME": "/dev/sda3", 73 | "BLOCK_SIZE": "512", 74 | "UUID": "107EB4247EB4048E", 75 | "TYPE": "ntfs", 76 | "USAGE": "filesystem", 77 | "PART_ENTRY_SCHEME": "gpt", 78 | "PART_ENTRY_NAME": "Basic data partition", 79 | "PART_ENTRY_UUID": "6a418dd1-ef6e-420e-8473-c2c081649374", 80 | "PART_ENTRY_TYPE": "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7", 81 | "PART_ENTRY_NUMBER": "3", 82 | "PART_ENTRY_OFFSET": "239616", 83 | "PART_ENTRY_SIZE": "103543269", 84 | "PART_ENTRY_DISK": "8:0" 85 | }, 86 | { 87 | "DEVNAME": "/dev/sda1", 88 | "UUID": "A8B2-3D19", 89 | "VERSION": "FAT32", 90 | "BLOCK_SIZE": "512", 91 | "TYPE": "vfat", 92 | "USAGE": "filesystem", 93 | "PART_ENTRY_SCHEME": "gpt", 94 | "PART_ENTRY_NAME": "EFI system partition", 95 | "PART_ENTRY_UUID": "7e2d5124-6070-4167-8952-6cc10812caa0", 96 | "PART_ENTRY_TYPE": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", 97 | "PART_ENTRY_FLAGS": "0x8000000000000000", 98 | "PART_ENTRY_NUMBER": "1", 99 | "PART_ENTRY_OFFSET": "2048", 100 | "PART_ENTRY_SIZE": "204800", 101 | "PART_ENTRY_DISK": "8:0" 102 | } 103 | ] 104 | -------------------------------------------------------------------------------- /tests/espgen/windows-efi-partitions-reload.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "DEVNAME": "/dev/loop0p3", 4 | "LABEL": "ostree", 5 | "UUID": "4bdeb166-7be1-40d8-a73c-6555db153bf5", 6 | "VERSION": "1.0", 7 | "BLOCK_SIZE": "4096", 8 | "TYPE": "ext4", 9 | "USAGE": "filesystem", 10 | "PART_ENTRY_SCHEME": "gpt", 11 | "PART_ENTRY_UUID": "cae19154-af25-7b4c-972c-ad7e1bf16501", 12 | "PART_ENTRY_TYPE": "4f68bce3-e8cd-4db1-96e7-fbcaf984b709", 13 | "PART_ENTRY_NUMBER": "3", 14 | "PART_ENTRY_OFFSET": "131072", 15 | "PART_ENTRY_SIZE": "33933927", 16 | "PART_ENTRY_DISK": "7:0" 17 | }, 18 | { 19 | "DEVNAME": "/dev/loop0p1", 20 | "SEC_TYPE": "msdos", 21 | "UUID": "AE27-C340", 22 | "VERSION": "FAT16", 23 | "BLOCK_SIZE": "512", 24 | "TYPE": "vfat", 25 | "USAGE": "filesystem", 26 | "PART_ENTRY_SCHEME": "gpt", 27 | "PART_ENTRY_UUID": "f4293fff-3134-2a49-a106-3381175ea360", 28 | "PART_ENTRY_TYPE": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", 29 | "PART_ENTRY_NUMBER": "1", 30 | "PART_ENTRY_OFFSET": "2048", 31 | "PART_ENTRY_SIZE": "126976", 32 | "PART_ENTRY_DISK": "7:0" 33 | }, 34 | { 35 | "DEVNAME": "/dev/loop0p2", 36 | "PART_ENTRY_SCHEME": "gpt", 37 | "PART_ENTRY_UUID": "68b1bc0f-1961-ac4f-81cc-1b22ba659ce0", 38 | "PART_ENTRY_TYPE": "21686148-6449-6e6f-744e-656564454649", 39 | "PART_ENTRY_NUMBER": "2", 40 | "PART_ENTRY_OFFSET": "129024", 41 | "PART_ENTRY_SIZE": "2048", 42 | "PART_ENTRY_DISK": "7:0" 43 | }, 44 | { 45 | "DEVNAME": "/dev/sda4", 46 | "BLOCK_SIZE": "512", 47 | "UUID": "ECACABC7ACAB8AA2", 48 | "TYPE": "ntfs", 49 | "USAGE": "filesystem", 50 | "PART_ENTRY_SCHEME": "gpt", 51 | "PART_ENTRY_UUID": "8236a774-8b07-41fc-b213-c426fe500760", 52 | "PART_ENTRY_TYPE": "de94bba4-06d1-4d40-a16a-bfd50179d6ac", 53 | "PART_ENTRY_FLAGS": "0x8000000000000001", 54 | "PART_ENTRY_NUMBER": "4", 55 | "PART_ENTRY_OFFSET": "103784448", 56 | "PART_ENTRY_SIZE": "1069056", 57 | "PART_ENTRY_DISK": "8:0" 58 | }, 59 | { 60 | "DEVNAME": "/dev/sda2", 61 | "PART_ENTRY_SCHEME": "gpt", 62 | "PART_ENTRY_NAME": "Microsoft reserved partition", 63 | "PART_ENTRY_UUID": "db00106a-064f-46ff-8310-ad78b8830282", 64 | "PART_ENTRY_TYPE": "e3c9e316-0b5c-4db8-817d-f92df00215ae", 65 | "PART_ENTRY_FLAGS": "0x8000000000000000", 66 | "PART_ENTRY_NUMBER": "2", 67 | "PART_ENTRY_OFFSET": "206848", 68 | "PART_ENTRY_SIZE": "32768", 69 | "PART_ENTRY_DISK": "8:0" 70 | }, 71 | { 72 | "DEVNAME": "/dev/sda3", 73 | "BLOCK_SIZE": "512", 74 | "UUID": "107EB4247EB4048E", 75 | "TYPE": "ntfs", 76 | "USAGE": "filesystem", 77 | "PART_ENTRY_SCHEME": "gpt", 78 | "PART_ENTRY_NAME": "Basic data partition", 79 | "PART_ENTRY_UUID": "6a418dd1-ef6e-420e-8473-c2c081649374", 80 | "PART_ENTRY_TYPE": "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7", 81 | "PART_ENTRY_NUMBER": "3", 82 | "PART_ENTRY_OFFSET": "239616", 83 | "PART_ENTRY_SIZE": "103543269", 84 | "PART_ENTRY_DISK": "8:0" 85 | }, 86 | { 87 | "DEVNAME": "/dev/sda1", 88 | "UUID": "A8B2-3D19", 89 | "VERSION": "FAT32", 90 | "BLOCK_SIZE": "512", 91 | "TYPE": "vfat", 92 | "USAGE": "filesystem", 93 | "PART_ENTRY_SCHEME": "gpt", 94 | "PART_ENTRY_NAME": "EFI system partition", 95 | "PART_ENTRY_UUID": "7e2d5124-6070-4167-8952-6cc10812caa0", 96 | "PART_ENTRY_TYPE": "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", 97 | "PART_ENTRY_FLAGS": "0x8000000000000000", 98 | "PART_ENTRY_NUMBER": "1", 99 | "PART_ENTRY_OFFSET": "2048", 100 | "PART_ENTRY_SIZE": "204800", 101 | "PART_ENTRY_DISK": "8:0" 102 | }, 103 | { 104 | "DEVNAME": "/dev/zram0", 105 | "UUID": "10c3a2ce-da4a-4173-bd6b-b4b2a913dcc6", 106 | "VERSION": "1", 107 | "TYPE": "swap", 108 | "USAGE": "other" 109 | } 110 | ] 111 | -------------------------------------------------------------------------------- /tests/run-tests: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd "$(dirname "$0")" 5 | exec python3 -m pytest "$@" 6 | -------------------------------------------------------------------------------- /tests/test_image_boot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | ''' 3 | Must be run as a user privileged enough to run 4 | `losetup`. If run as an unprivileged user, all tests are skipped. 5 | ''' 6 | 7 | import contextlib 8 | import os 9 | import tempfile 10 | import shutil 11 | import subprocess 12 | 13 | from .util import ( 14 | BaseTestCase, 15 | dracut_script, 16 | get_lsblk_field, 17 | losetup, 18 | mount, 19 | needs_root, 20 | sfdisk, 21 | udevadm_settle, 22 | ) 23 | 24 | EOS_IMAGE_BOOT_SETUP = dracut_script('image-boot', 'eos-image-boot-setup') 25 | 26 | 27 | class ImageTestCase(BaseTestCase): 28 | '''Base class for image-boot tests.''' 29 | @contextlib.contextmanager 30 | def make_host_device(self, filesystem): 31 | '''Yields the path to a temporary loopback device, formatted as 32 | 'filesystem' by running 'mkfs', to host test image files.''' 33 | mkfs = 'mkfs.{}'.format(filesystem) 34 | with tempfile.NamedTemporaryFile() as host_img: 35 | host_img.truncate(4 * 1024 * 1024) 36 | sfdisk(host_img.name, b'start=64KiB, type=0x07') 37 | 38 | with losetup(host_img.name) as host_disk: 39 | host_device = host_disk + 'p1' 40 | subprocess.check_call([mkfs, host_device]) 41 | 42 | # Wait for udev to probe new filesystem type 43 | udevadm_settle() 44 | 45 | self.assert_fstype(host_device, filesystem) 46 | yield host_device 47 | 48 | def assert_readonly(self, device, readonly): 49 | self.assertEqual( 50 | get_lsblk_field(device, 'ro'), 51 | '1' if readonly else '0') 52 | 53 | 54 | class TestImageBootSetup(ImageTestCase): 55 | '''Tests entire eos-image-boot-setup script.''' 56 | def setUp(self): 57 | super().setUp() 58 | 59 | self.tmpdir = tempfile.mkdtemp() 60 | self.endless_img = os.path.join(self.tmpdir, 'endless.img') 61 | 62 | with open(self.endless_img, 'wb') as f: 63 | f.truncate(1 * 1024 * 1024) 64 | sfdisk(f.name, 65 | b'start=64KiB, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709', 66 | label='gpt') 67 | 68 | def tearDown(self): 69 | shutil.rmtree(self.tmpdir) 70 | 71 | def _mkisofs(self, *contents): 72 | endless_iso = os.path.join(self.tmpdir, 'endless.iso') 73 | subprocess.check_call(( 74 | 'xorriso', '-as', 'mkisofs', 75 | '-o', endless_iso, 76 | ) + contents) 77 | return endless_iso 78 | 79 | def _mksquashfs(self, *contents): 80 | endless_squash = os.path.join(self.tmpdir, 'endless.squash') 81 | subprocess.check_call(('mksquashfs',) + contents + (endless_squash,)) 82 | return endless_squash 83 | 84 | @needs_root 85 | def test_image_boot_iso(self): 86 | '''Tests ISO > uncompressed image''' 87 | iso = self._mkisofs(self.endless_img) 88 | with losetup(iso) as host_device: 89 | self._go(host_device, 'endless.img', readonly=True) 90 | 91 | @needs_root 92 | def test_image_boot_iso_squashfs(self): 93 | '''Tests ISO > SquashFS > uncompressed image''' 94 | iso = self._mkisofs(self._mksquashfs(self.endless_img)) 95 | with losetup(iso) as host_device: 96 | self._go(host_device, 'endless.squash', readonly=True) 97 | 98 | @needs_root 99 | def test_image_boot_exfat(self): 100 | '''Tests exFAT > uncompressed image''' 101 | with self.make_host_device('exfat') as host_device: 102 | with mount(host_device) as host_mount: 103 | shutil.copyfile(self.endless_img, host_mount + '/endless.img') 104 | 105 | self._go(host_device, 'endless.img', readonly=False) 106 | 107 | @needs_root 108 | def test_image_boot_exfat_squashfs(self): 109 | '''Tests exFAT > SquashFS > uncompressed image''' 110 | endless_squash = self._mksquashfs(self.endless_img) 111 | with self.make_host_device('exfat') as host_device: 112 | with mount(host_device) as host_mount: 113 | shutil.copyfile(endless_squash, host_mount + '/endless.squash') 114 | 115 | self._go(host_device, 'endless.squash', readonly=True) 116 | 117 | @needs_root 118 | def test_image_boot_ntfs(self): 119 | '''Tests NTFS > uncompressed image''' 120 | with self.make_host_device('ntfs') as host_device: 121 | with mount(host_device) as host_mount: 122 | shutil.copyfile(self.endless_img, host_mount + '/endless.img') 123 | 124 | self._go(host_device, 'endless.img', readonly=False) 125 | 126 | def _detach_if_exists(self, image): 127 | if os.path.exists(image): 128 | dev = subprocess.check_output(( 129 | 'losetup', '--output', 'NAME', '--noheadings', 130 | '--associated', image, 131 | )) 132 | subprocess.call(('losetup', '--detach', dev.strip())) 133 | 134 | def _go(self, host_device, image_path, readonly=False): 135 | mapped_dev = '/dev/disk/endless-image' 136 | mapped_part = '/dev/disk/endless-image1' 137 | 138 | try: 139 | args = [EOS_IMAGE_BOOT_SETUP, host_device, image_path] 140 | if readonly: 141 | args.insert(1, '--readonly') 142 | subprocess.check_call(args) 143 | udevadm_settle() 144 | self.assertTrue(os.path.exists(mapped_dev)) 145 | self.assert_readonly(mapped_dev, readonly) 146 | self.assertTrue(os.path.exists(mapped_part)) 147 | self.assert_readonly(mapped_part, readonly) 148 | # We could be more zealous about checking the contents of the 149 | # mapped partition, but TestMapImageFile -- plus the fact the 150 | # partition was found within the device -- is good enough. 151 | finally: 152 | # This cleanup is all best-effort and reliant on implementation 153 | # details of eos-image-boot-setup, whose effects are not really 154 | # intended to be undone -- in normal use, the mapped OS image and 155 | # everything behind it must exist until the machine is shut down. 156 | subprocess.call(('partx', '-d', '-v', mapped_dev)) 157 | 158 | self._detach_if_exists('/squash/endless.img') 159 | if os.path.exists('/squash'): 160 | subprocess.call(('umount', '/squash')) 161 | os.rmdir(('/squash')) 162 | 163 | self._detach_if_exists('/outer_image/endless.squash') 164 | self._detach_if_exists('/outer_image/endless.img') 165 | 166 | if os.path.exists('/outer_image'): 167 | subprocess.call(('umount', '/outer_image')) 168 | os.rmdir('/outer_image') 169 | -------------------------------------------------------------------------------- /tests/test_live_storage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | ''' 3 | Tests eos-live-storage-setup. Must be run as a user privileged enough to run 4 | `losetup`. If run as an unprivileged user, all tests are skipped. 5 | ''' 6 | from contextlib import contextmanager 7 | from subprocess import check_call, check_output 8 | import tempfile 9 | 10 | from .util import ( 11 | BaseTestCase, 12 | dracut_script, 13 | losetup, 14 | needs_root, 15 | sfdisk, 16 | ) 17 | 18 | 19 | EOS_LIVE_STORAGE_SETUP = dracut_script('image-boot', 'eos-live-storage-setup') 20 | DISK_SIZE_BYTES = 4 * (1024 ** 3) 21 | STANDARD_PARTITION_TABLE = ''' 22 | label: dos 23 | unit: sectors 24 | label-id: 0xdeadbeef 25 | 26 | start= 2048, size= 4251900, type=0, bootable 27 | start= 4255996, size= 28672, type=ef 28 | '''.strip().encode('ascii') # noqa: E501 29 | 30 | 31 | @contextmanager 32 | def _prepare(initial_table): 33 | with tempfile.NamedTemporaryFile(buffering=0) as img: 34 | img.truncate(DISK_SIZE_BYTES) 35 | sfdisk(img.name, initial_table) 36 | 37 | with losetup(img.name) as img_device: 38 | yield img, img_device 39 | 40 | 41 | class TestLiveStorage(BaseTestCase): 42 | @needs_root 43 | def test_data_partition_added(self): 44 | with _prepare(STANDARD_PARTITION_TABLE) as (img, img_device): 45 | try: 46 | check_call([EOS_LIVE_STORAGE_SETUP, img_device + "p1"]) 47 | 48 | new_table = check_output(["sfdisk", "--dump", img_device], 49 | universal_newlines=True) 50 | expected_table = ''' 51 | label: dos 52 | label-id: 0xdeadbeef 53 | device: {img_device} 54 | unit: sectors 55 | sector-size: 512 56 | 57 | {img_device}p1 : start= 2048, size= 4251900, type=0, bootable 58 | {img_device}p2 : start= 4255996, size= 28672, type=ef 59 | {img_device}p3 : start= 4286464, size= 4102144, type=83 60 | '''.strip().format(img_device=img_device) 61 | 62 | # Check that data partition was added as expected 63 | # Note that the values above also ensure that the data partition was 64 | # aligned to a 1mb boundary, leaving a few unused sectors after p2. 65 | self.assertEqual(expected_table, new_table.strip()) 66 | 67 | # Check that the marker was written 68 | with open(img_device + "p3", "r") as fd: 69 | self.assertEqual(fd.read(27), 70 | "endless_live_storage_marker") 71 | except Exception: 72 | # Log the current state to aid debugging 73 | check_call(["sfdisk", "--dump", img_device]) 74 | raise 75 | 76 | # Test that no action is taken if we already have 3 partitions 77 | @needs_root 78 | def test_already_have_data_partition(self): 79 | table = ''' 80 | start= 2048, size= 4251900, type=0, bootable 81 | start= 4255996, size= 28672, type=ef 82 | size=65536, type=83 83 | '''.strip().encode('ascii') # noqa: E501 84 | with _prepare(table) as (img, img_device): 85 | try: 86 | orig_table = check_output(["sfdisk", "--dump", img_device], 87 | universal_newlines=True) 88 | 89 | check_call([EOS_LIVE_STORAGE_SETUP, img_device + "p1"]) 90 | 91 | new_table = check_output(["sfdisk", "--dump", img_device], 92 | universal_newlines=True) 93 | self.assertEqual(orig_table, new_table) 94 | except Exception: 95 | # Log the current state to aid debugging 96 | check_call(["sfdisk", "--dump", img_device]) 97 | raise 98 | 99 | # Test that no action is taken if the disk was already completely full 100 | @needs_root 101 | def test_no_space_available(self): 102 | table = ''' 103 | start= 2048, size= 4251900, type=0, bootable 104 | start= 4255996, type=ef 105 | '''.strip().encode('ascii') # noqa: E501 106 | with _prepare(table) as (img, img_device): 107 | try: 108 | orig_table = check_output(["sfdisk", "--dump", img_device], 109 | universal_newlines=True) 110 | 111 | check_call([EOS_LIVE_STORAGE_SETUP, img_device + "p1"]) 112 | 113 | new_table = check_output(["sfdisk", "--dump", img_device], 114 | universal_newlines=True) 115 | self.assertEqual(orig_table, new_table) 116 | except Exception: 117 | # Log the current state to aid debugging 118 | check_call(["sfdisk", "--dump", img_device]) 119 | raise 120 | 121 | # Test that no action is taken if the disk doesn't have much space left 122 | @needs_root 123 | def test_not_much_space_available(self): 124 | table = ''' 125 | start= 2048, size= 4251900, type=0, bootable 126 | start= 4255996, size= 4130000, type=ef 127 | '''.strip().encode('ascii') # noqa: E501 128 | with _prepare(table) as (img, img_device): 129 | try: 130 | orig_table = check_output(["sfdisk", "--dump", img_device], 131 | universal_newlines=True) 132 | 133 | check_call([EOS_LIVE_STORAGE_SETUP, img_device + "p1"]) 134 | 135 | new_table = check_output(["sfdisk", "--dump", img_device], 136 | universal_newlines=True) 137 | self.assertEqual(orig_table, new_table) 138 | except Exception: 139 | # Log the current state to aid debugging 140 | check_call(["sfdisk", "--dump", img_device]) 141 | raise 142 | -------------------------------------------------------------------------------- /tests/test_migrate_chromium_profile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Tests eos-migrate-chromium-profile 4 | 5 | This test is based on test_migrate_firefox_profile.py. 6 | """ 7 | 8 | import tempfile 9 | import textwrap 10 | import os 11 | 12 | from .util import BaseTestCase, system_script, import_script_as_module 13 | 14 | emfp = import_script_as_module("emfp", system_script("eos-migrate-chromium-profile")) 15 | 16 | 17 | class TestUpdateMimeappsList(BaseTestCase): 18 | def setUp(self): 19 | super().setUp() 20 | 21 | self.tmp = tempfile.TemporaryDirectory() 22 | 23 | def tearDown(self): 24 | self.tmp.cleanup() 25 | 26 | def test_nonexistent(self): 27 | emfp.update_mimeapps_list(os.path.join(self.tmp.name, "mimeapps.list")) 28 | 29 | def test_not_there(self): 30 | orig_data = textwrap.dedent( 31 | """ 32 | [Default Applications] 33 | image/jpeg=eog.desktop 34 | """ 35 | ).lstrip() 36 | expected_data = orig_data 37 | 38 | self._test(orig_data, expected_data) 39 | 40 | def test_there(self): 41 | orig_data = textwrap.dedent( 42 | """ 43 | [Default Applications] 44 | text/html=chromium-browser.desktop 45 | image/jpeg=eog.desktop 46 | 47 | [Added Associations] 48 | text/xml=google-chrome.desktop;org.mozilla.firefox.desktop;chromium-browser.desktop; 49 | """ 50 | ).lstrip() 51 | """ 52 | The trailing semicolon in the [Default Applications] text/html entry is legal. 53 | 54 | https://specifications.freedesktop.org/mime-apps-spec/latest/ar01s03.html says: 55 | 56 | The value is a semicolon-separated list of desktop file IDs (as defined in 57 | the desktop entry spec). 58 | 59 | https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s04.html 60 | says: 61 | 62 | The multiple values should be separated by a semicolon and the value of the 63 | key may be optionally terminated by a semicolon. 64 | 65 | GKeyFile always adds the semicolon. What's fun is that when GLib itself updates 66 | [Default Applications], it uses set_string() rather than set_string_list(), even 67 | though it parses these as lists. 68 | """ 69 | expected_data = textwrap.dedent( 70 | """ 71 | [Default Applications] 72 | text/html=org.chromium.Chromium.desktop; 73 | image/jpeg=eog.desktop 74 | 75 | [Added Associations] 76 | text/xml=google-chrome.desktop;org.mozilla.firefox.desktop;org.chromium.Chromium.desktop; 77 | """ 78 | ).lstrip() 79 | 80 | self._test(orig_data, expected_data) 81 | 82 | def _test(self, orig_data, expected_data): 83 | mimeapps_list = os.path.join(self.tmp.name, "mimeapps.list") 84 | with open(mimeapps_list, "w") as f: 85 | f.write(orig_data) 86 | 87 | emfp.update_mimeapps_list(mimeapps_list) 88 | with open(mimeapps_list, "r") as f: 89 | new_data = f.read() 90 | 91 | self.assertEqual(expected_data, new_data) 92 | 93 | 94 | class TestUpdateDesktopShortcuts(BaseTestCase): 95 | def setUp(self): 96 | super().setUp() 97 | 98 | self.tmp = tempfile.TemporaryDirectory() 99 | 100 | def tearDown(self): 101 | self.tmp.cleanup() 102 | 103 | def test_no_shortcuts_dir(self): 104 | emfp.update_desktop_shortcuts(os.path.join(self.tmp.name, "no-such-directory")) 105 | 106 | def test_empty_shortcuts_dir(self): 107 | emfp.update_desktop_shortcuts(self.tmp.name) 108 | 109 | def test_not_there(self): 110 | orig_data = textwrap.dedent( 111 | """ 112 | [Desktop Entry] 113 | Exec=/usr/bin/eos-google-chrome --profile-directory=Default 114 | """ 115 | ).lstrip() 116 | expected_data = orig_data 117 | 118 | self._test(orig_data, expected_data) 119 | 120 | def test_there(self): 121 | orig_data = textwrap.dedent( 122 | """ 123 | [Desktop Entry] 124 | Exec=/usr/bin/chromium-browser --profile-directory=Default 125 | """ 126 | ).lstrip() 127 | expected_data = textwrap.dedent( 128 | """ 129 | [Desktop Entry] 130 | Exec=/usr/bin/flatpak run org.chromium.Chromium --profile-directory=Default 131 | X-Flatpak-Part-Of=org.chromium.Chromium 132 | """ 133 | ).lstrip() 134 | 135 | self._test(orig_data, expected_data) 136 | 137 | def _test(self, orig_data, expected_data): 138 | test_desktop = os.path.join(self.tmp.name, 139 | "test.desktop") 140 | with open(test_desktop, "w") as f: 141 | f.write(orig_data) 142 | 143 | emfp.update_desktop_shortcuts(self.tmp.name) 144 | with open(test_desktop, "r") as f: 145 | new_data = f.read() 146 | 147 | self.assertEqual(expected_data, new_data) 148 | 149 | 150 | class TestUpdateOldConfigReferences(BaseTestCase): 151 | def setUp(self): 152 | super().setUp() 153 | 154 | self.tmp = tempfile.TemporaryDirectory() 155 | 156 | def tearDown(self): 157 | self.tmp.cleanup() 158 | 159 | def test_nonexistent(self): 160 | emfp.update_mimeapps_list(os.path.join(self.tmp.name, "mimeapps.list")) 161 | 162 | def test_not_there(self): 163 | orig_data = "" 164 | expected_data = orig_data 165 | 166 | self._test(orig_data, expected_data) 167 | 168 | def test_there_system(self): 169 | orig_data = '{"Path":"/usr/lib/chromium-browser/WidevineCdm"}' 170 | expected_data = '{"Path":""}' 171 | 172 | self._test(orig_data, expected_data) 173 | 174 | def test_there_local(self): 175 | user_home_dir = os.path.expanduser("~/") 176 | orig_data = \ 177 | '{"Path":"' + user_home_dir + '.config/chromium/WidevineCdm/4.10.1610.0"}' 178 | expected_data = \ 179 | '{"Path":"' + user_home_dir + \ 180 | '.var/app/org.chromium.Chromium/config/chromium/WidevineCdm/4.10.1610.0"}' 181 | 182 | self._test(orig_data, expected_data) 183 | 184 | def _test(self, orig_data, expected_data): 185 | last_updated_widevine = os.path.join(self.tmp.name, 186 | "latest-component-updated-widevine-cdm") 187 | with open(last_updated_widevine, "w") as f: 188 | f.write(orig_data) 189 | 190 | emfp.update_old_config_references(last_updated_widevine) 191 | with open(last_updated_widevine, "r") as f: 192 | new_data = f.read() 193 | 194 | self.assertEqual(expected_data, new_data) 195 | -------------------------------------------------------------------------------- /tests/test_migrate_firefox_profile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Tests eos-migrate-firefox-profile 4 | """ 5 | 6 | import tempfile 7 | import textwrap 8 | import os 9 | 10 | from .util import BaseTestCase, system_script, import_script_as_module 11 | 12 | emfp = import_script_as_module("emfp", system_script("eos-migrate-firefox-profile")) 13 | 14 | 15 | class TestUpdateMimeappsList(BaseTestCase): 16 | def setUp(self): 17 | super().setUp() 18 | 19 | self.tmp = tempfile.TemporaryDirectory() 20 | 21 | def tearDown(self): 22 | self.tmp.cleanup() 23 | 24 | def test_nonexistent(self): 25 | emfp.update_mimeapps_list(os.path.join(self.tmp.name, "mimeapps.list")) 26 | 27 | def test_not_there(self): 28 | orig_data = textwrap.dedent( 29 | """ 30 | [Default Applications] 31 | image/jpeg=eog.desktop 32 | """ 33 | ).lstrip() 34 | expected_data = orig_data 35 | 36 | self._test(orig_data, expected_data) 37 | 38 | def test_there(self): 39 | orig_data = textwrap.dedent( 40 | """ 41 | [Default Applications] 42 | text/html=org.mozilla.Firefox.desktop 43 | image/jpeg=eog.desktop 44 | 45 | [Added Associations] 46 | text/xml=google-chrome.desktop;org.mozilla.Firefox.desktop;chromium-browser.desktop; 47 | """ 48 | ).lstrip() 49 | """ 50 | The trailing semicolon in the [Default Applications] text/html entry is legal. 51 | 52 | https://specifications.freedesktop.org/mime-apps-spec/latest/ar01s03.html says: 53 | 54 | The value is a semicolon-separated list of desktop file IDs (as defined in 55 | the desktop entry spec). 56 | 57 | https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s04.html 58 | says: 59 | 60 | The multiple values should be separated by a semicolon and the value of the 61 | key may be optionally terminated by a semicolon. 62 | 63 | GKeyFile always adds the semicolon. What's fun is that when GLib itself updates 64 | [Default Applications], it uses set_string() rather than set_string_list(), even 65 | though it parses these as lists. 66 | """ 67 | expected_data = textwrap.dedent( 68 | """ 69 | [Default Applications] 70 | text/html=org.mozilla.firefox.desktop; 71 | image/jpeg=eog.desktop 72 | 73 | [Added Associations] 74 | text/xml=google-chrome.desktop;org.mozilla.firefox.desktop;chromium-browser.desktop; 75 | """ 76 | ).lstrip() 77 | 78 | self._test(orig_data, expected_data) 79 | 80 | def _test(self, orig_data, expected_data): 81 | mimeapps_list = os.path.join(self.tmp.name, "mimeapps.list") 82 | with open(mimeapps_list, "w") as f: 83 | f.write(orig_data) 84 | 85 | emfp.update_mimeapps_list(mimeapps_list) 86 | with open(mimeapps_list, "r") as f: 87 | new_data = f.read() 88 | 89 | self.assertEqual(expected_data, new_data) 90 | 91 | 92 | class TestMangleMetadataAndDesktopFile(BaseTestCase): 93 | def setUp(self): 94 | super().setUp() 95 | 96 | self.tmp = tempfile.TemporaryDirectory() 97 | 98 | def tearDown(self): 99 | self.tmp.cleanup() 100 | 101 | def test_copy_endless_install_section(self): 102 | orig_data = textwrap.dedent( 103 | """ 104 | [Profile1] 105 | Name=default 106 | IsRelative=1 107 | Path=lgcq05lh.default 108 | Default=1 109 | 110 | [Profile0] 111 | Name=default-release 112 | IsRelative=1 113 | Path=x4p6znk4.default-release 114 | 115 | [General] 116 | StartWithLastProfile=1 117 | Version=2 118 | 119 | [InstallFF6C2EBF42BF07E5] 120 | Default=x4p6znk4.default-release 121 | Locked=1 122 | """ 123 | ).lstrip() 124 | expected_data = textwrap.dedent( 125 | """ 126 | [Profile1] 127 | Name=default 128 | IsRelative=1 129 | Path=lgcq05lh.default 130 | Default=1 131 | 132 | [Profile0] 133 | Name=default-release 134 | IsRelative=1 135 | Path=x4p6znk4.default-release 136 | 137 | [General] 138 | StartWithLastProfile=1 139 | Version=2 140 | 141 | [InstallFF6C2EBF42BF07E5] 142 | Default=x4p6znk4.default-release 143 | Locked=1 144 | 145 | [InstallCF146F38BCAB2D21] 146 | Default=x4p6znk4.default-release 147 | Locked=1 148 | """ 149 | ).lstrip() 150 | 151 | self._test(orig_data, expected_data) 152 | 153 | def test_no_action_if_no_endless_section(self): 154 | orig_data = "" 155 | expected_data = "" 156 | self._test(orig_data, expected_data) 157 | 158 | def test_no_action_if_mozilla_section_exists(self): 159 | orig_data = textwrap.dedent( 160 | """ 161 | [Profile1] 162 | Name=default 163 | IsRelative=1 164 | Path=lgcq05lh.default 165 | Default=1 166 | 167 | [Profile0] 168 | Name=default-release 169 | IsRelative=1 170 | Path=x4p6znk4.default-release 171 | 172 | [General] 173 | StartWithLastProfile=1 174 | Version=2 175 | 176 | [InstallFF6C2EBF42BF07E5] 177 | Default=x4p6znk4.default-release 178 | Locked=1 179 | 180 | [InstallCF146F38BCAB2D21] 181 | Default=lgcq05lh.default 182 | Locked=1 183 | """ 184 | ).lstrip() 185 | expected_data = orig_data 186 | 187 | self._test(orig_data, expected_data) 188 | 189 | def _test(self, orig_data, expected_data): 190 | profiles_ini = os.path.join(self.tmp.name, "profiles.ini") 191 | with open(profiles_ini, "w") as f: 192 | f.write(orig_data) 193 | 194 | emfp.copy_install_section(profiles_ini) 195 | with open(profiles_ini, "r") as f: 196 | new_data = f.read() 197 | 198 | self.assertEqual(expected_data, new_data) 199 | -------------------------------------------------------------------------------- /tests/test_repartition.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | ''' 3 | Tests endless-repartition.sh. Must be run as a user privileged enough to run 4 | `losetup`. If run as an unprivileged user, all tests are skipped. 5 | ''' 6 | from subprocess import check_call 7 | import tempfile 8 | 9 | from .util import ( 10 | BaseTestCase, 11 | dracut_script, 12 | losetup, 13 | needs_root, 14 | partprobe, 15 | sfdisk, 16 | udevadm_settle, 17 | ) 18 | 19 | 20 | ENDLESS_REPARTITION_SH = dracut_script('repartition', 'endless-repartition.sh') 21 | 22 | 23 | class TestRepartition(BaseTestCase): 24 | @needs_root 25 | def test_preserves_basic_data_no_swap(self): 26 | ''' 27 | Even with larger disks we no longer create a swap partition. 28 | The trailing NTFS partition should be preserved. 29 | ''' 30 | 31 | disk_size_bytes = 256071351296 32 | partition_table = ''' 33 | label: gpt 34 | unit: sectors 35 | 36 | start= 2048, size= 126976, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B 37 | start= 129024, size= 2048, type=21686148-6449-6E6F-744E-656564454649 38 | start= 131072, size= 20.4G, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, attrs=GUID:55 39 | start= 482312879, size= 17825792, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, name="Basic data partition" 40 | '''.strip().encode('utf-8') # noqa: E501 41 | 42 | self._go(disk_size_bytes, partition_table, bd_pre='p4', bd_post='p4') 43 | 44 | @needs_root 45 | def test_preserves_basic_data_removes_swap(self): 46 | ''' 47 | If we have a swap partition already created, we should remove it 48 | and preserve the NTFS partition (with a decremented partition number). 49 | Note that this requires the repartition marker (GUID:55) to be set, 50 | which is normally removed after the repartitioning is complete. 51 | ''' 52 | 53 | disk_size_bytes = 256071351296 54 | partition_table = ''' 55 | label: gpt 56 | unit: sectors 57 | 58 | start= 2048, size= 126976, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B 59 | start= 129024, size= 2048, type=21686148-6449-6E6F-744E-656564454649 60 | start= 131072, size= 473794560, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, attrs=GUID:55 61 | start= 473925632, size= 8387247, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F 62 | start= 482312879, size= 17825792, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, name="Basic data partition" 63 | '''.strip().encode('utf-8') # noqa: E501 64 | 65 | self._go(disk_size_bytes, partition_table, bd_pre='p5', bd_post='p4') 66 | 67 | def _go(self, disk_size_bytes, partition_table, 68 | bd_pre=None, bd_post=None, swap=None): 69 | with tempfile.NamedTemporaryFile() as img: 70 | img.truncate(disk_size_bytes) 71 | sfdisk(img.name, partition_table) 72 | 73 | with losetup(img.name) as img_device: 74 | try: 75 | if bd_pre is not None: 76 | bd_partition = img_device + bd_pre 77 | check_call(['mkfs.ntfs', '-Q', bd_partition]) 78 | udevadm_settle() 79 | self.assert_fstype(bd_partition, 'ntfs') 80 | 81 | root = img_device + 'p3' 82 | check_call(["/bin/sh", "-x", ENDLESS_REPARTITION_SH, root]) 83 | partprobe(img_device) 84 | udevadm_settle() 85 | 86 | if bd_post is not None: 87 | self.assert_fstype(img_device + bd_post, 'ntfs') 88 | 89 | except Exception: 90 | # Log the current state to aid debugging 91 | check_call(["sfdisk", "--dump", img_device]) 92 | check_call(["lsblk", "-o", "+fstype", img_device]) 93 | # Pause 94 | # check_call(["cat"]) 95 | raise 96 | -------------------------------------------------------------------------------- /tests/test_repartition_mbr.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | ''' 3 | Tests eos-repartition-mbr. Must be run as a user privileged enough to run 4 | `losetup`. If run as an unprivileged user, all tests are skipped. 5 | ''' 6 | from contextlib import contextmanager 7 | import os 8 | import re 9 | from subprocess import check_call, check_output, CalledProcessError 10 | import tempfile 11 | 12 | from .util import ( 13 | BaseTestCase, 14 | system_script, 15 | losetup, 16 | needs_root, 17 | sfdisk, 18 | ) 19 | 20 | 21 | EOS_REPARTITION_MBR = system_script('eos-repartition-mbr') 22 | SECTOR = 512 23 | # Dummy bootloaders 24 | MBR = b'x' * 440 25 | BIOS_BOOT_OFFSET = 129024 * SECTOR 26 | BIOS_BOOT = b'y' * (2014 * SECTOR) 27 | DISK_SIZE_BYTES = 21 * (1024 ** 3) 28 | STANDARD_PARTITION_TABLE = ''' 29 | label: gpt 30 | unit: sectors 31 | 32 | start= 2048, size= 126976, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B 33 | start= 129024, size= 2048, type=21686148-6449-6E6F-744E-656564454649 34 | start= 131072, size= 42762240, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, attrs=GUID:55 35 | '''.strip().encode('ascii') # noqa: E501 36 | 37 | 38 | def check_bootloader(img): 39 | img.seek(0) 40 | assert img.read(len(MBR)) == MBR 41 | 42 | img.seek(BIOS_BOOT_OFFSET) 43 | assert img.read(len(BIOS_BOOT)) == BIOS_BOOT 44 | 45 | 46 | def check_gpts(img, expected): 47 | n = len(expected) 48 | 49 | img.seek(SECTOR) 50 | assert img.read(n) == expected 51 | 52 | img.seek(-SECTOR, os.SEEK_END) 53 | assert img.read(n) == expected 54 | 55 | 56 | @contextmanager 57 | def _prepare(initial_table): 58 | with tempfile.NamedTemporaryFile(buffering=0) as img: 59 | img.write(MBR) 60 | 61 | img.seek(BIOS_BOOT_OFFSET) 62 | img.write(BIOS_BOOT) 63 | 64 | img.truncate(DISK_SIZE_BYTES) 65 | 66 | sfdisk(img.name, initial_table) 67 | 68 | # Double-check that the image looks right before we start 69 | check_bootloader(img) 70 | check_gpts(img, b'EFI PART') 71 | 72 | with losetup(img.name) as img_device: 73 | yield img, img_device 74 | 75 | 76 | class TestRepartitionMbr(BaseTestCase): 77 | @needs_root 78 | def test_repartition_mbr(self): 79 | with _prepare(STANDARD_PARTITION_TABLE) as (img, img_device): 80 | try: 81 | check_call([EOS_REPARTITION_MBR, img_device]) 82 | 83 | new_table = check_output(["sfdisk", "--dump", img_device], 84 | universal_newlines=True) 85 | # Normalize the one bit that varies 86 | new_table = re.sub(r'^label-id: 0x[0-9a-f]{8}$', 87 | '''label-id: 0xdeadbeef''', 88 | new_table, 89 | flags=re.MULTILINE) 90 | 91 | expected_table = ''' 92 | label: dos 93 | label-id: 0xdeadbeef 94 | device: {img_device} 95 | unit: sectors 96 | 97 | {img_device}p1 : start= 2048, size= 126976, type=ef 98 | {img_device}p2 : start= 131072, size= 42762240, type=83, bootable 99 | {img_device}p4 : start= 0, size= 0, type=dd 100 | '''.strip().format(img_device=img_device).split('\n') 101 | 102 | assert expected_table == new_table.strip().split('\n') 103 | 104 | # BIOS bootloaders should be intact 105 | check_bootloader(img) 106 | 107 | # GPTs should be erased 108 | check_gpts(img, b'\0' * len(b'EFI PART')) 109 | except Exception: 110 | # Log the current state to aid debugging 111 | check_call(["sfdisk", "--dump", img_device]) 112 | raise 113 | 114 | @needs_root 115 | def test_fails_if_esp_missing(self): 116 | self._test_missing(0) 117 | 118 | @needs_root 119 | def test_fails_if_bios_boot_missing(self): 120 | self._test_missing(1) 121 | 122 | @needs_root 123 | def test_fails_if_root_missing(self): 124 | self._test_missing(2) 125 | 126 | def _test_missing(self, index): 127 | lines = STANDARD_PARTITION_TABLE.split(b'\n') 128 | i = index + 3 129 | initial_table = b'\n'.join(lines[:i] + lines[i+1:]) 130 | 131 | self._test_fails(initial_table) 132 | 133 | @needs_root 134 | def test_fails_with_swap_partition(self): 135 | initial_table = STANDARD_PARTITION_TABLE + b''' 136 | start= 42893312, size= 1024000, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F''' # noqa: E501 137 | 138 | self._test_fails(initial_table) 139 | 140 | @needs_root 141 | def test_fails_with_unknown_partition(self): 142 | # type= is a fresh UUID generated for this test 143 | initial_table = STANDARD_PARTITION_TABLE + b''' 144 | start= 42893312, size= 1024000, type=18B660C0-CAB5-4990-8898-07169F986163''' # noqa: E501 145 | 146 | self._test_fails(initial_table) 147 | 148 | def _test_fails(self, initial_table): 149 | with _prepare(initial_table) as (img, img_device): 150 | # Read back the partition table. This includes extra fields like 151 | # 'label-id' and the device path, both of which vary between runs. 152 | # It's easier to do this than to try to post-process new_table to 153 | # make it match initial_table. 154 | written_table = check_output(["sfdisk", "--dump", img_device], 155 | universal_newlines=True) 156 | 157 | with self.assertRaises(CalledProcessError): 158 | check_call([EOS_REPARTITION_MBR, img_device]) 159 | 160 | # Check nothing was modified 161 | new_table = check_output(["sfdisk", "--dump", img_device], 162 | universal_newlines=True) 163 | assert written_table.split('\n') == new_table.split('\n') 164 | check_bootloader(img) 165 | check_gpts(img, b'EFI PART') 166 | -------------------------------------------------------------------------------- /tests/test_update_efi_uuid.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Endless OS Foundation LLC 2 | # SPDX-License-Identifier: GPL-2.0-or-later 3 | 4 | import logging 5 | import shlex 6 | import subprocess 7 | import sys 8 | from uuid import UUID 9 | 10 | from .util import built_program, EFIVARFS_PATH 11 | 12 | logger = logging.getLogger(__name__) 13 | 14 | EFI_UPDATE_UUID = built_program('eos-update-efi-uuid') 15 | 16 | ORIG_ESP_UUID = '9cf7d938-86c5-4f09-8401-fd0d6e4c646c' 17 | NEW_ESP_UUID = '0de64583-f397-4783-a8f0-f101dd91def4' 18 | OTHER_ESP_UUID = '5538ba7e-e641-4560-84c9-6194f68b8d32' 19 | ENDLESS_LOAD_OPTION = 'Boot0003-8be4df61-93ca-11d2-aa0d-00e098032b8c' 20 | 21 | # Offset info the EFI variable where the UUID is located. Note that this 22 | # is dependent on the length of the title from EFI/endless/BOOTX64.CSV. 23 | # This comes from the shim package and is currently "Endless OS". 24 | UUID_OFFSET = 56 25 | 26 | 27 | def run(cmd, check=True, log_level=logging.INFO, **kwargs): 28 | cmd_str = shlex.join(cmd) 29 | logger.log(log_level, f'Executing {cmd_str}') 30 | return subprocess.run(cmd, check=check, **kwargs) 31 | 32 | 33 | def hexdump(path): 34 | proc = run( 35 | ['hexdump', '-C', str(path)], 36 | check=True, 37 | stdout=subprocess.PIPE, 38 | text=True, 39 | log_level=logging.DEBUG, 40 | ) 41 | return proc.stdout 42 | 43 | 44 | def test_update(efivarfs): 45 | """Update with correct partition UUID""" 46 | run([EFI_UPDATE_UUID, '-v', ORIG_ESP_UUID, NEW_ESP_UUID]) 47 | 48 | # Keep track of the hexdumps for a repeat. 49 | test_hexdumps = {} 50 | 51 | for test_path in efivarfs.iterdir(): 52 | var = test_path.name 53 | test_hex = hexdump(test_path) 54 | test_hexdumps[str(test_path)] = test_hex 55 | src_path = EFIVARFS_PATH / var 56 | src_hex = hexdump(src_path) 57 | 58 | if test_path.name != ENDLESS_LOAD_OPTION: 59 | assert test_hex == src_hex, f'{var} has changed' 60 | continue 61 | 62 | assert test_hex != src_hex, f'{var} has not changed' 63 | with open(src_path, 'rb') as src, open(test_path, 'rb') as test: 64 | src_data = src.read() 65 | test_data = test.read() 66 | 67 | assert len(src_data) == len(test_data), f'{var} size has changed' 68 | 69 | # Up until the UUID offset should match. 70 | assert test_data[:UUID_OFFSET] == src_data[:UUID_OFFSET] 71 | 72 | # The next 16 bytes are the UUID. 73 | uuid_end = UUID_OFFSET + 16 74 | src_uuid = UUID(ORIG_ESP_UUID) 75 | test_uuid = UUID(NEW_ESP_UUID) 76 | if sys.byteorder == 'little': 77 | src_uuid_bytes = src_uuid.bytes_le 78 | test_uuid_bytes = test_uuid.bytes_le 79 | else: 80 | src_uuid_bytes = src_uuid.bytes 81 | test_uuid_bytes = test_uuid.bytes 82 | assert test_data[UUID_OFFSET:uuid_end] != src_data[UUID_OFFSET:uuid_end] 83 | assert src_data[UUID_OFFSET:uuid_end] == src_uuid_bytes 84 | assert test_data[UUID_OFFSET:uuid_end] == test_uuid_bytes 85 | 86 | # The rest should match. 87 | assert test_data[uuid_end:] == src_data[uuid_end:] 88 | 89 | # Running again should cause no changes since there aren't any load 90 | # options matching the original UUID. 91 | run([EFI_UPDATE_UUID, '-v', ORIG_ESP_UUID, NEW_ESP_UUID]) 92 | for test_path in efivarfs.iterdir(): 93 | test_hex = hexdump(test_path) 94 | prev_hex = test_hexdumps[str(test_path)] 95 | assert test_hex == prev_hex, f'{test_path.name} has changed' 96 | 97 | 98 | def test_dry_run(efivarfs): 99 | """Dry run should produce no changes""" 100 | run([EFI_UPDATE_UUID, '-v', '--dry-run', ORIG_ESP_UUID, NEW_ESP_UUID]) 101 | 102 | for test_path in efivarfs.iterdir(): 103 | test_hex = hexdump(test_path) 104 | src_path = EFIVARFS_PATH / test_path.name 105 | src_hex = hexdump(src_path) 106 | 107 | assert test_hex == src_hex, f'{test_path.name} has changed' 108 | 109 | 110 | def test_other(efivarfs): 111 | """Other partition UUID should produce no changes""" 112 | run([EFI_UPDATE_UUID, '-v', OTHER_ESP_UUID, NEW_ESP_UUID]) 113 | 114 | for test_path in efivarfs.iterdir(): 115 | test_hex = hexdump(test_path) 116 | src_path = EFIVARFS_PATH / test_path.name 117 | src_hex = hexdump(src_path) 118 | 119 | assert test_hex == src_hex, f'{test_path.name} has changed' 120 | -------------------------------------------------------------------------------- /tests/util.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import importlib.machinery 3 | import importlib.util 4 | import os 5 | from pathlib import Path 6 | import subprocess 7 | import tempfile 8 | import unittest 9 | 10 | TESTS_PATH = Path(__file__).parent.resolve() 11 | EFIVARFS_PATH = TESTS_PATH / 'efivars' 12 | 13 | run_needs_root_tests = bool(os.environ.get('EBH_ROOT_TESTS')) 14 | needs_root = unittest.skipIf(not run_needs_root_tests, 15 | "needs root; set EBH_ROOT_TESTS=1 to enable") 16 | 17 | 18 | def built_program(program): 19 | '''Gets the absolute path to a built program in the top level of this 20 | repository. 21 | 22 | This uses the ABS_TOP_BUILDDIR environment variable to find the top build 23 | directory. Otherwise it falls back to system_script, which uses the top 24 | source directory. 25 | ''' 26 | abs_top_builddir = os.environ.get('ABS_TOP_BUILDDIR') 27 | if abs_top_builddir: 28 | return os.path.join(abs_top_builddir, program) 29 | return system_script(program) 30 | 31 | 32 | def system_script(script): 33 | '''Gets the absolute path to a script in the top level of this 34 | repository.''' 35 | return os.path.abspath(os.path.join( 36 | os.path.dirname(__file__), '..', script)) 37 | 38 | 39 | def dracut_script(module, script): 40 | '''Gets the absolute path to a script in a dracut module in this 41 | repository.''' 42 | return system_script(os.path.join('dracut', module, script)) 43 | 44 | 45 | def partprobe(device): 46 | subprocess.check_call(['partprobe', device]) 47 | 48 | 49 | def udevadm_settle(): 50 | subprocess.check_call(['udevadm', 'settle']) 51 | 52 | 53 | def get_lsblk_field(device, field): 54 | return subprocess.check_output([ 55 | 'lsblk', '--nodeps', '--noheading', '--output', field, device 56 | ]).decode('utf-8').strip() 57 | 58 | 59 | def fstype(device): 60 | return get_lsblk_field(device, 'fstype') 61 | 62 | 63 | def sfdisk(device, partition_table, label='dos'): 64 | args = ("sfdisk", "--label", label, device) 65 | sfdisk_proc = subprocess.Popen(args, stdin=subprocess.PIPE) 66 | sfdisk_proc.communicate(partition_table) 67 | if sfdisk_proc.returncode: 68 | raise subprocess.CalledProcessError(sfdisk_proc.returncode, args) 69 | 70 | 71 | @contextlib.contextmanager 72 | def mount(device): 73 | '''Mounts device on a freshly-created mount point.''' 74 | with tempfile.TemporaryDirectory() as mount_point: 75 | subprocess.check_call(['mount', device, mount_point]) 76 | 77 | try: 78 | yield mount_point 79 | finally: 80 | subprocess.check_call(['umount', mount_point]) 81 | 82 | 83 | @contextlib.contextmanager 84 | def losetup(path): 85 | '''Yields a loopback device for path.''' 86 | args = ('losetup', '--find', '--show', path,) 87 | output = subprocess.check_output(args) 88 | 89 | device = output.decode('utf-8').strip() 90 | try: 91 | subprocess.call(['partx', '-a', device]) 92 | udevadm_settle() 93 | yield device 94 | finally: 95 | subprocess.call(['partx', '-d', device]) 96 | subprocess.check_call(['losetup', '--detach', device]) 97 | udevadm_settle() 98 | 99 | 100 | class BaseTestCase(unittest.TestCase): 101 | def assert_fstype(self, partition, type_): 102 | '''Asserts that 'partition' contains a 'type_' filesystem.''' 103 | msg = 'expected {} to have type {!r}'.format(partition, type_) 104 | self.assertEqual(fstype(partition), type_, msg=msg) 105 | 106 | 107 | def import_script_as_module(module_name, filename): 108 | # Import script as a module, despite its filename not being a legal module name 109 | spec = importlib.util.spec_from_loader( 110 | module_name, 111 | importlib.machinery.SourceFileLoader( 112 | module_name, system_script(filename) 113 | ), 114 | ) 115 | module = importlib.util.module_from_spec(spec) 116 | spec.loader.exec_module(module) 117 | return module 118 | 119 | 120 | def relative_link(src, dst): 121 | """Create a relative symbolic link pointing to src named dst.""" 122 | if not os.path.isabs(src): 123 | raise ValueError(f'Source path "{src}" must be absolute') 124 | if not os.path.isabs(dst): 125 | raise ValueError(f'Destination path "{dst}" must be absolute') 126 | dstdir = os.path.dirname(dst) 127 | target = os.path.relpath(src, dstdir) 128 | os.makedirs(dstdir, exist_ok=True) 129 | os.symlink(target, dst) 130 | -------------------------------------------------------------------------------- /tmpfiles.d/chromium-system-services.conf.in: -------------------------------------------------------------------------------- 1 | d /etc/chromium-browser 0755 - - - 2 | d /run/flatpak/extension/org.chromium.Chromium.Policy.system-policies 0755 - - - 3 | d /run/flatpak/extension/org.chromium.Chromium.Policy.system-policies/@FLATPAK_ARCH@ 0755 - - - 4 | L /run/flatpak/extension/org.chromium.Chromium.Policy.system-policies/@FLATPAK_ARCH@/1 - - - - /etc/chromium-browser 5 | -------------------------------------------------------------------------------- /tmpfiles.d/eos-boot-helper.conf: -------------------------------------------------------------------------------- 1 | # Remove stamp file from eos-prune-printers (added on upgrade to Endless OS 4) 2 | r /var/lib/eos4-prune-printers 3 | 4 | # Remove stamp file from eos-reclaim-swap (itself removed in Endless OS 4) 5 | r /var/eos-swap-reclaimed 6 | 7 | # remove stamp file from eos-fix-home-dir-permissions 8 | # (from 3.1.6 change to default home dir permissions) 9 | r /var/lib/misc/eos-dir-mode-700 10 | --------------------------------------------------------------------------------