-
-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathretro-home-image
executable file
·901 lines (781 loc) · 36.3 KB
/
retro-home-image
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
#!/usr/bin/env bash
LC_ALL=C
# Display help usage
function usage () {
echo
echo "Usage"
echo " $0 --device <targetdevice>"
echo
echo "Supported (or planned) devices are:"
echo " raspi: Rasberry Pi 2, 3 and 4"
echo " raspi-desktop: Rasberry Pi 2, 3 and 4 (with desktop)"
echo " deskpi: DeskPi Pro/Lite for Rasberry Pi 4"
echo " deskpi-desktop: DeskPi Pro/Lite for Rasberry Pi 4 (with desktop)"
echo " gamehat: Waveshare Game HAT"
echo " gamepi20: Waveshare GamePi20"
echo " gamepi43: Waveshare GamePi43"
echo " gpicase: RetroFlag GPi Case"
echo " gpicase2: RetroFlag GPi Case 2"
echo " gpimate: RetroFlag GPi Case with GPiMate for CM3+Lite"
echo " gpimateplus: RetroFlag GPi Case with GPiMate Plus for CM4+Lite"
echo " gamepm2803: Waveshare GPM2803 with CM3"
echo " gamepm2804: Waveshare GPM2804 with CM4"
echo " nespi: RetroFlag NESPi Case+"
echo " nespi4: RetroFlag NESPi 4 Case"
echo " megapi: RetroFlag MEGAPi Case"
echo " minin64 Megabit Mini N64"
echo " minips1 Megabit Mini PS1 (Non-NFC)"
echo " monster: Monster Arcade Controller Kit"
echo " picade: Pimoroni Picade"
echo " picadeconsole: Pimoroni Picade Console"
echo " pistation RetroFlag PiStation Case"
echo " pistationlcd RetroFlag PiStation Case with LCD"
echo " superpi: RetroFlag SUPERPi Case-U and Case-J"
echo " superpi4: RetroFlag SUPERPi 4 Case"
}
function not_implemented() {
echo "[!] Hardware support for ${REMIX} is not yet implemented, but is planned."
exit 1
}
function sync_from() {
if [ -z "${1}" ] || [ -z "${2}" ]; then
echo "ERROR! Source stages to sync were not passed."
exit 1
fi
local B_SOURCE="${1}"
local R_SOURCE="${2}"
if [ -d "${B}" ] && [ -d "${R}" ]; then
echo "Syncing from ${B_SOURCE}..."
rsync -aHAXx --delete "${B_SOURCE}/" "${B}/"
echo "Syncing from ${R_SOURCE}..."
rsync -aHAXx --delete "${R_SOURCE}/" "${R}/"
else
echo "ERROR! Either ${B} or ${R} do not exist!"
exit 1
fi
}
function nspawn() {
# Create basic resolv.conf for bind mounting inside the container
echo "nameserver 1.1.1.1" > "${R_STAGE_0}/resolv.conf"
if pidof apt-cacher-ng && [ -d "${R}/etc/apt/apt.conf.d" ]; then
echo "Acquire::http { Proxy \"http://${APT_CACHE_IP}:3142\"; }" > "${R}/etc/apt/apt.conf.d/90cache"
fi
# Make sure the container has a machine-id
systemd-machine-id-setup --root "${R}" --print
echo "Running: ${@}"
# Bind mount resolv.conf and the firmware, set the hostname and spawn
systemd-nspawn \
--resolv-conf=off \
--bind-ro="${R_STAGE_0}/resolv.conf":/etc/resolv.conf \
--bind="${B}":/boot/firmware \
--machine="retro-home-${REMIX}" \
--directory "${R}" "${@}"
if [ -e "${R}/etc/apt/apt.conf.d/90cache" ]; then
rm -f "${R}/etc/apt/apt.conf.d/90cache"
fi
}
function stage_01_bootstrap() {
local REPO=""
export B="${B_STAGE_1}"
export R="${R_STAGE_1}"
rm -rf "${B_STAGE_1}"/*
rm -rf "${R_STAGE_1}"/*
# Required tools on the host
apt-get -y install binfmt-support debootstrap device-tree-compiler git \
graphicsmagick-imagemagick-compat iproute2 optipng qemu-user-static rsync \
systemd-container ubuntu-keyring util-linux whois xz-utils
# Bootstrap a minimal Ubuntu. Ludo uses connman so that is bootstrapped
# Includes:
# - avahi-daemon: Multicast DNS Service Discovery
# - cloud-guest-utils: instead of cloud-image-utils which requires qemu
# - connman: provides network management
# - nano: the best text editor ever
# Excludes:
# - isc-dhcp-*: Replaced by connman
# - netplan.io: Replaced by connman
# - networkd-dispatcher: Replaced by connman
# - ubuntu-advantage-tools: not required
if pidof apt-cacher-ng; then
REPO="http://localhost:3142/ports.ubuntu.com/"
else
REPO="http://ports.ubuntu.com/"
fi
debootstrap \
--arch="${IMG_ARCH}" \
--cache-dir="${R_STAGE_0}" \
--components=main,restricted,universe,multiverse \
--exclude=debconf-i18n,distro-info,dmsetup,eject,info,isc-dhcp-client,isc-dhcp-common,libnetplan0,netcat-openbsd,netplan.io,networkd-dispatcher,ubuntu-minimal,ubuntu-advantage-tools,vim-tiny,vim-common,xxd \
--foreign \
--include=avahi-daemon,cloud-guest-utils,connman,nano \
"${IMG_RELEASE}" "${R}" "${REPO}"
nspawn /debootstrap/debootstrap \
--second-stage
cp "root/etc/apt/sources.list" "${R}/etc/apt/"
sed -i "s|RELEASE|${IMG_RELEASE}|g" "${R}/etc/apt/sources.list"
echo "${REMIX}" > "${R}/etc/hostname"
sed -i "1s|.*|127.0.0.1\tlocalhost ${REMIX}|" "${R}/etc/hosts"
# Set locale to C.UTF-8 by default.
# https://git.launchpad.net/livecd-rootfs/tree/live-build/auto/build#n159
echo "LANG=C.UTF-8" > "${R}/etc/default/locale"
# Disable systemd-networkd et al; we're using connman
# - https://www.naut.ca/blog/2018/12/12/disabling-systemd-networking/
nspawn systemctl disable systemd-networkd.socket systemd-networkd systemd-networkd-wait-online
nspawn systemctl disable systemd-resolved.service
# Disable cron; systemd timers have replaced cron
nspawn systemctl disable cron
# Disable motd-news, Retro Home has a bespoke (static) motd
nspawn systemctl disable motd-news.service motd-news.timer
# I've grappled with this.
nspawn systemctl disable apt-daily.timer apt-daily-upgrade.timer
nspawn systemctl disable setvtrgb.service
nspawn apt-get -y update
nspawn apt-get -y upgrade
nspawn apt-get -y dist-upgrade
# # Add some of ubuntu-standard tools & first boot filesystem expansion
# - Insure that cloud-image-utils do not get installed
nspawn apt-get -y install --no-install-recommends cloud-guest-utils \
cloud-initramfs-growroot dosfstools file hdparm lshw lsof man-db \
pciutils pci.ids psmisc usbutils usb.ids wget xz-utils
}
function stage_02_ludo() {
export B="${B_STAGE_2}"
export R="${R_STAGE_2}"
sync_from "${B_STAGE_1}" "${R_STAGE_1}"
if $(${INCLUDE_LUDO}); then
# Xorg; as small as possible
nspawn apt-get -y install --no-install-recommends unclutter-xfixes \
xauth xdg-utils xserver-common
nspawn apt-get -y install libgl1-mesa-dri mesa-va-drivers \
mesa-vdpau-drivers mesa-vulkan-drivers x11-utils x11-xserver-utils \
xserver-xorg-core xserver-xorg-input-all xserver-xorg-video-fbdev
# GLFW; used by Ludo
nspawn apt-get -y install --install-suggests libglfw3
if $(${INCLUDE_DESKTOP}); then
# Display manager
nspawn apt-get -y install --no-install-recommends lightdm \
lightdm-gtk-greeter yaru-theme-gtk yaru-theme-icon yaru-theme-sound \
yaru-theme-unity
# Enable auto-login to run Retro Home via LightDM
cp -v root/usr/share/lightdm/lightdm.conf.d/*.conf "${R}"/usr/share/lightdm/lightdm.conf.d/
cp -v root/usr/share/lightdm/lightdm-gtk-greeter.conf.d/*.conf "${R}"/usr/share/lightdm/lightdm-gtk-greeter.conf.d/
# Run watch-ludo-settings in the desktop session automatically
cp root/etc/xdg/autostart/watch-ludo-settings.desktop "${R}"/etc/xdg/autostart/
else
nspawn apt-get -y install --no-install-recommends xinit
cp "root/usr/lib/systemd/system/x11-autologin.service" "${R}/usr/lib/systemd/system/"
nspawn systemctl enable x11-autologin.service
fi
# Install Splash
nspawn apt-get -y install --no-install-recommends fbi fontconfig fonts-mplus
mkdir -p "${R}/etc/udev/rules.d"
cp root/etc/udev/rules.d/30-fb0.rules "${R}/etc/udev/rules.d/30-fb0.rules"
cp root/usr/lib/systemd/system/splash-fbi.service "${R}/usr/lib/systemd/system/"
nspawn systemctl enable splash-fbi.service
cp "root/usr/lib/systemd/system/retro-maint-fc-cache.service" "${R}/usr/lib/systemd/system/"
nspawn systemctl enable retro-maint-fc-cache.service
# Make logo.png
convert -density 1200 -resize ${SIZE_LOGO}x${SIZE_LOGO} -background none .github/logo.svg /tmp/logo.png
# Make Retro Home background
mkdir -p "${R}/usr/share/splash"
convert -size "${SIZE_FB}" xc:black /tmp/background.png
composite -gravity center /tmp/logo.png /tmp/background.png "${R}/usr/share/splash/splash.png"
optipng -strip all -o7 "${R}/usr/share/splash/splash.png"
# Make frame.png
convert -size ${SIZE_FRAME}x${SIZE_FRAME} xc:black /tmp/frame.png
composite -gravity center /tmp/logo.png /tmp/frame.png /tmp/logo-framed.png
rm /tmp/frame.png
rm /tmp/logo.png
for ((i = 1 ; i < 6 ; i++)); do
composite -gravity south ".github/throbber-000${i}.png" /tmp/logo-framed.png "/tmp/logo-000${i}.png"
composite -gravity center "/tmp/logo-000${i}.png" /tmp/background.png "${R}/usr/share/splash/splash-000${i}.png"
optipng -strip all -o7 "${R}/usr/share/splash/splash-000${i}.png"
rm "/tmp/logo-000${i}.png"
done
rm /tmp/logo-framed.png
rm /tmp/background.png
# LudOS requirements
nspawn apt-get -y install --no-install-recommends bluez connman \
samba ssh wireless-regdb wpasupplicant xli
# Disable connman-wait-online; it prevent the graphical.target being reach with no network
nspawn systemctl disable connman-wait-online.service
local AUDIO_SYSTEM="pipewire"
case "${AUDIO_SYSTEM}" in
pipewire)
nspawn apt-get -y install pipewire-audio-client-libraries pulsemixer wireplumber libfdk-aac2 libopenaptx0 libspa-0.2-bluetooth libspa-0.2-jack;;
pulseaudio)
nspawn apt-get -y install pulseaudio pulseaudio-module-bluetooth pulsemixer;;
esac
# deb-get
nspawn apt-get -y install --no-install-recommends curl wget
nspawn wget -q "https://raw.githubusercontent.com/wimpysworld/deb-get/0.3.0/deb-get" -O /usr/bin/deb-get
chmod 755 "${R}/usr/bin/deb-get"
nspawn deb-get install deb-get
# Disable unattended-upgrades
nspawn systemctl disable unattended-upgrades
# Ludo
nspawn deb-get install ludo
# Add systemd drop-in units so bluetooth, ssh and samba honor Ludo preferences
for SERVICE in bluetooth sshd smbd; do
mkdir -p "${R}/etc/systemd/system/${SERVICE}.service.d"
case "${SERVICE}" in
bluetooth) echo -e "[Unit]\nConditionPathExists=/storage/.cache/services/bluez.conf\nAfter=graphical.target" > "${R}/etc/systemd/system/${SERVICE}.service.d/override.conf";;
smbd) echo -e "[Unit]\nConditionPathExists=/storage/.cache/services/samba.conf" > "${R}/etc/systemd/system/${SERVICE}.service.d/override.conf";;
sshd) echo -e "[Unit]\nConditionPathExists=/storage/.cache/services/sshd.conf" > "${R}/etc/systemd/system/${SERVICE}.service.d/override.conf";;
esac
done
cp "root/etc/ludo.toml" "${R}/etc/"
# Configure Samba
mv "${R}/etc/samba/smb.conf" "${R}/etc/samba/smb.conf.dist"
echo -e "[global]\nconfig file = /usr/local/etc/samba/smb.conf\ndisable netbios = yes" > "${R}/etc/samba/smb.conf"
mkdir -p "${R}/usr/local/etc/samba/"
cp "root/usr/local/etc/samba/smb.conf" "${R}/usr/local/etc/samba/"
sed -i "s|REMIX|${REMIX}|g" "${R}/usr/local/etc/samba/smb.conf"
# Really, really, disable nmbd. NetBIOS is not needed
nspawn systemctl disable nmbd.service
# Enforce the bespoke configuration
cp "root/usr/lib/systemd/system/retro-maint-samba.service" "${R}/usr/lib/systemd/system/"
nspawn systemctl enable retro-maint-samba.service
# Create directories Ludo requires
for DOT_DIR in .cache/connman .cache/services .config/ludo .local/ludo .local/share/ludo .ludo .update; do
mkdir -p "${R}/storage/${DOT_DIR}"
done
for CONF_FILE in .cache/services/bluez.conf .cache/services/samba.conf .cache/services/sshd.conf .local/share/ludo/history.csv; do
touch "${R}/storage/${CONF_FILE}"
done
# Disable samba by default; can be enabled via Ludo Settings
rm "${R}/storage/.cache/services/samba.conf"
for LUDO_DIR in playlists roms savefiles savestates screenshots system thumbnails; do
mkdir -p "${R}/storage/Retro/${LUDO_DIR}"
done
chown -Rv 1000:1000 "${R}/storage"
# Make LibreElec/LudOS updates fail; this is intended
chown -v root:root "${R}/storage/.update"
chmod 700 "${R}/storage/.update"
cp "root/usr/lib/systemd/system/retro-maint-ludo-update.service" "${R}/usr/lib/systemd/system/"
nspawn systemctl enable retro-maint-ludo-update.service
# Allow members of the 'sudo' group to start/stop services
# Required so ludos can enable/disable SSH, Bluetooth and Samba
# - https://unix.stackexchange.com/questions/504806/systemd-start-as-unprivileged-user-in-a-group/557634#557634
cp root/etc/polkit-1/localauthority/50-local.d/service-auth.pkla "${R}/etc/polkit-1/localauthority/50-local.d/"
# Add inotify-tools for watch-ludo-settings so it can react to settings changes
nspawn apt-get -y install --no-install-recommends inotify-tools
# Add the retro-home script that launch LudOs
for BIN in hdmi-refresh retro-home splash-xli watch-ludo-settings; do
cp "root/usr/local/bin/${BIN}" "${R}/usr/local/bin/"
chmod 755 "${R}/usr/local/bin/${BIN}"
done
fi
}
function stage_03_micro_desktop() {
export B="${B_STAGE_3}"
export R="${R_STAGE_3}"
sync_from "${B_STAGE_2}" "${R_STAGE_2}"
if $(${INCLUDE_DESKTOP}); then
nspawn apt-get -y install --no-install-recommends language-pack-gnome-en
nspawn apt-get -y install --no-install-recommends connman-gtk \
file-roller fonts-mplus gedit gnome-bluetooth gnome-control-center \
gnome-control-center-faces gnome-disk-utility gnome-session-flashback \
gnome-startup-applications gnome-terminal libcanberra-pulse librsvg2-2 \
librsvg2-bin librsvg2-common transmission-gtk yaru-theme-gtk \
yaru-theme-icon yaru-theme-sound yaru-theme-unity
# Disable tracker
rm "${R}/etc/systemd/user/default.target.wants/tracker-extract-3.service"
rm "${R}/etc/systemd/user/gnome-session.target.wants/tracker-extract-3.service"
rm "${R}/etc/systemd/user/gnome-session.target.wants/tracker-miner-fs-3.service"
# Install gschemas that prevent gnome-control-center from crashing
nspawn apt-get -y install --no-install-recommends gnome-shell-common mutter-common
# Indicators
nspawn apt-get -y install --no-install-recommends gkbd-capplet gucharmap \
indicator-applet indicator-application indicator-bluetooth \
indicator-datetime indicator-keyboard indicator-session indicator-sound
# Dev tools
#nspawn apt-get -y install --no-install-recommends dconf-editor \
#gcolor3 lightdm-gtk-greeter-settings pavucontrol
# Firefox ESR
nspawn apt-add-repository -y ppa:mozillateam/ppa
nspawn apt-get -y install firefox-esr
# Create custom panel layout
cp "root/usr/share/gnome-panel/layouts/retro-home.layout" "${R}/usr/share/gnome-panel/layouts/"
# Create gschema override
cp "root/usr/share/glib-2.0/schemas/90_retro-home.gschema.override" "${R}/usr/share/glib-2.0/schemas/"
nspawn glib-compile-schemas /usr/share/glib-2.0/schemas/
# Generate ludo icons
optipng -strip all -o7 "${R}/usr/share/icons/hicolor/1024x1024/apps/ludo.png"
for SIZE in 512 256 192 128 96 72 64 48 36 32 24 22 16; do
mkdir -p "${R}/usr/share/icons/hicolor/${SIZE}x${SIZE}/apps"
convert -resize ${SIZE}x${SIZE} -background none "${R}/usr/share/icons/hicolor/1024x1024/apps/ludo.png" "${R}/usr/share/icons/hicolor/${SIZE}x${SIZE}/apps/ludo.png"
optipng -strip all -o7 "${R}/usr/share/icons/hicolor/${SIZE}x${SIZE}/apps/ludo.png"
done
# Generate retro-home icons
mkdir -p "${R}/usr/share/icons/hicolor/scalable/apps"
cp .github/logo.svg "${R}/usr/share/icons/hicolor/scalable/apps/retro-home.svg"
for SIZE in 1024 512 256 192 128 96 72 64 48 36 32 24 22 16; do
mkdir -p "${R}/usr/share/icons/hicolor/${SIZE}x${SIZE}/apps"
convert -density 1200 -resize ${SIZE}x${SIZE} -background none "${R}/usr/share/icons/hicolor/scalable/apps/retro-home.svg" "${R}/usr/share/icons/hicolor/${SIZE}x${SIZE}/apps/retro-home.png"
optipng -strip all -o7 "${R}/usr/share/icons/hicolor/${SIZE}x${SIZE}/apps/retro-home.png"
done
nspawn gtk-update-icon-cache /usr/share/icons/hicolor/
# Composite the Retro Home wallpaper
mkdir -p "${R}/usr/share/backgrounds/" 2>/dev/null
composite -gravity center "${R}/usr/share/icons/hicolor/256x256/apps/retro-home.png" root/usr/share/backgrounds/Jammy-Jellyfish_WP_1920x1080_Grey_blank.png "${R}/usr/share/backgrounds/Retro-Home-Wallpaper.png"
cp root/usr/share/backgrounds/Jammy-Jellyfish_WP_1920x1080_Grey_blank.png "${R}/usr/share/backgrounds/Retro-Home-Wallpaper-blank.png"
optipng -strip all -o7 "${R}"/usr/share/backgrounds/*.png
# Add Connman Settings menu entry and hide the TeXInfo menu entry
mkdir -p "${R}/usr/share/gnome-flashback-metacity/applications"
cp "root/usr/share/gnome-flashback-metacity/applications/connman-gtk.desktop" "${R}/usr/share/gnome-flashback-metacity/applications/"
cp "root/usr/share/gnome-flashback-metacity/applications/info.desktop" "${R}/usr/share/gnome-flashback-metacity/applications/"
nspawn ln -s /usr/share/gnome-flashback-metacity /usr/share/retro-desktop
# Create Retro Home and Retro Desktop xsessions
mkdir -p "${R}"/usr/share/xsessions
cp root/usr/share/xsessions/retro-home.desktop "${R}"/usr/share/xsessions/
cp root/usr/share/xsessions/retro-desktop.desktop "${R}"/usr/share/xsessions/
mkdir -p "${R}"/usr/libexec
cp root/usr/libexec/retro-desktop-metacity "${R}"/usr/libexec/
# Disable GNOME Flashback sessions
chmod 600 "${R}"/usr/share/xsessions/gnome-flashback-*.desktop
nspawn update-desktop-database
fi
}
function stage_04_kernel() {
export B="${B_STAGE_4}"
export R="${R_STAGE_4}"
sync_from "${B_STAGE_3}" "${R_STAGE_3}"
cp "root/etc/fstab" "${R}/etc/"
# Adds lz4 and z3fold modules to initramfs.
# - https://ubuntu.com/blog/how-low-can-you-go-running-ubuntu-desktop-on-a-2gb-raspberry-pi-4
echo lz4 >> "${R}/etc/initramfs-tools/modules"
echo z3fold >> "${R}/etc/initramfs-tools/modules"
# Swap
# - https://git.launchpad.net/livecd-rootfs/tree/live-build/ubuntu/hooks/099-ubuntu-image-customization.chroot
mkdir -p "${R}/usr/lib/systemd/system/swap.target.wants/"
cp "root/usr/lib/systemd/system/mkswap.service" "${R}/usr/lib/systemd/system/mkswap.service"
nspawn ln -s /usr/lib/systemd/system/mkswap.service /usr/lib/systemd/system/swap.target.wants/mkswap.service
cp "root/usr/lib/systemd/system/swapfile.swap" "${R}/usr/lib/systemd/system/swapfile.swap"
nspawn ln -s /usr/lib/systemd/system/swapfile.swap /usr/lib/systemd/system/swap.target.wants/swapfile.swap
nspawn apt-get -y install --no-install-recommends flash-kernel \
libraspberrypi0 libraspberrypi-bin linux-firmware-raspi \
linux-image-raspi linux-modules-extra-raspi pi-bluetooth raspi-config \
rpi-eeprom ubuntu-raspi-settings
# Disable raspi-config, which allows switching the cpu governor by holding
# shift during boot
nspawn systemctl disable raspi-config
nspawn systemctl disable systemctl disable getty@tty1
local NEW_KERNEL=$(ls -1 "${R}"/boot/vmlinuz-* | tail -n1 | awk -F/ '{print $NF}' | cut -d'-' -f2-4)
if [ -z "${NEW_KERNEL}" ]; then
echo "ERROR! Could not detect the new kernel version"
exit 1
fi
echo "Kernel: ${NEW_KERNEL}"
# Copy firmware, devicetree, overlays and kernel to the boot file system
cp -v "${R}/lib/linux-firmware-raspi/"* "${B}/"
cp -av "${R}/lib/firmware/${NEW_KERNEL}/device-tree/"* "${B}/"
# Move the arm64 device-tree
if [ -d "${B}/broadcom" ]; then
mv -v "${B}/broadcom/"*.dtb "${B}"/
rm -rf "${B}/broadcom"
fi
cp -av "${R}/boot/vmlinuz-${NEW_KERNEL}" "${B}/vmlinuz"
cp -av "${R}/boot/initrd.img-${NEW_KERNEL}" "${B}/initrd.img"
# Supress kernel messages
# - https://superuser.com/questions/351387/how-to-stop-kernel-messages-from-flooding-my-console
echo "kernel.printk = 1 4 1 7" > "${R}/etc/sysctl.d/20-printk.conf"
}
function stage_05_config() {
export B="${B_STAGE_5}"
export R="${R_STAGE_5}"
sync_from "${B_STAGE_4}" "${R_STAGE_4}"
# Append case specific cmdline.txt & config.txt
cp -v "boot/README" "${B}/"
if [ -f "case/${REMIX}/config.txt" ]; then
cp -v "case/${REMIX}/config.txt" "${B}/"
else
cp "boot/config.txt" "${B}/"
fi
if [ -f "case/${REMIX}/cmdline.txt" ]; then
cp -v "case/${REMIX}/cmdline.txt" "${B}/"
else
cp "boot/cmdline.txt" "${B}/"
fi
# Apply ARM64 tweaks
if [ "${IMG_ARCH}" == "arm64" ]; then
echo -e "\n# Config settings specific to arm64
arm_64bit=1
dtoverlay=dwc2" >> "${B}/config.txt"
fi
# Create user and groups
local DATE=""
DATE=$(date +%m%H%M%S)
local PASSWD=""
PASSWD=$(mkpasswd -m sha-512 "${USER_PASS}" "${DATE}")
nspawn addgroup --gid 1000 "${USER_NAME}"
nspawn adduser --gecos "${USER_NAME^}" --add_extra_groups --disabled-password --home "${USER_HOME}" --gid 1000 --uid 1000 "${USER_NAME}"
nspawn usermod -a -G adm,sudo -p "${PASSWD}" "${USER_NAME}"
# Regenerate ssh host keys on first boot
if [ -d "${R}/etc/ssh" ]; then
cp "root/usr/lib/systemd/system/ssh-regenerate-hostkeys.service" "${R}/usr/lib/systemd/system/"
nspawn systemctl enable ssh-regenerate-hostkeys.service
fi
# Disable the existing motd scripts
for MOTD in 00-header 10-help-text 50-motd-news 92-unattended-upgrades; do
chmod 644 "${R}/etc/update-motd.d/${MOTD}"
done
# Add the Retro Home motd scripts and enable them
cp -v root/etc/update-motd.d/*-retro-home* "${R}/etc/update-motd.d/"
chmod 755 "${R}"/etc/update-motd.d/*-retro-home*
local CASE_SUPPORT=""
# Overlay and case specific files
if [ -d "case/${REMIX}/root" ]; then
rsync -av --chown=root:root "case/${REMIX}/root/" "${R}"/
fi
if [ -d "case/${REMIX}/boot/overlays" ]; then
cp "case/${REMIX}/boot/overlays/"*.dtbo "${B}"/overlays/
fi
case ${REMIX} in
deskpi*)
# https://github.com/DeskPi-Team/deskpi
# https://github.com/DeskPi-Team/deskpi/tree/master/drivers
nspawn apt-get -y install --no-install-recommends lirc python3-serial python3-setproctitle
# Configure lirc for DeskPi Pro
mv "${R}"/etc/lirc/lircd.conf.d/devinput.lircd.conf "${R}"/etc/lirc/lircd.conf.d/devinput.lircd.dist
sed -i 's|= devinput|= default|' "${R}"/etc/lirc/lirc_options.conf
sed -i 's|= auto|= /dev/lirc0|' "${R}"/etc/lirc/lirc_options.conf
nspawn systemctl enable deskpi-fancontrol.service
nspawn systemctl enable deskpi-poweroff.service
;;
gpicase|gpimate*)
nspawn apt-get -y install --no-install-recommends python3-gpiozero python3-lgpio python3-psutil python3-setproctitle
CASE_SUPPORT="gpi.py"
#dtc -W no-unit_address_vs_reg -I dts -O dtb -o "${B}/overlays/dpi24-gpi.dtbo" case/${REMIX}/boot/overlays/dpi24-gpi-overlay.dts
#dtc -W no-unit_address_vs_reg -I dts -O dtb -o "${B}/overlays/pwm-audio-pi-zero-gpi.dtbo" case/${REMIX}/boot/overlays/pwm-audio-pi-zero-gpi-overlay.dts
#dtc -W no-unit_address_vs_reg -I dts -O dtb -o "${B}/overlays/disable-pcie.dtbo" case/${REMIX}/boot/overlays/disable-pcie-overlay.dts
;;
megabit)
nspawn apt-get -y install --no-install-recommends python3-gpiozero python3-lgpio python3-psutil python3-setproctitle
CASE_SUPPORT="megabit.py"
;;
retroflag)
nspawn apt-get -y install --no-install-recommends python3-gpiozero python3-lgpio python3-psutil python3-setproctitle
nspawn systemctl enable retroflag-safeshutdown.service
# NES Pi 4 Case No usb-storage quirks are not required as the kernel
# in Ubuntu 22.04 is new enough to include an up to date quirks list
# sudo dmesg | grep Quirks
# usb-storage 2-1:1.0: Quirks match for vid 152d pid 0578: 1000000
# https://forums.raspberrypi.com/viewtopic.php?f=28&t=245931
;;
esac
if [ -n "${CASE_SUPPORT}" ]; then
cp "root/usr/lib/systemd/system/retro-home-case-support.service" "${R}/usr/lib/systemd/system/"
sed -i "s|CASE_SUPPORT|${CASE_SUPPORT}|g" "${R}/usr/lib/systemd/system/retro-home-case-support.service"
nspawn systemctl enable retro-home-case-support.service
fi
}
function stage_06_clean() {
export B="${B_STAGE_6}"
export R="${R_STAGE_6}"
sync_from "${B_STAGE_5}" "${R_STAGE_5}"
nspawn apt-get -y autoremove
nspawn apt-get -y autoclean
nspawn apt-get -y clean
rm -f "${B}"/{*.bak,*.old}
rm -f "${R}"/wget-log
rm -f "${R}"/boot/{*.bak,*.old}
rm -f "${R}"/etc/ssh/ssh_host_*_key*
rm -f "${R}"/etc/apt/*.save
rm -f "${R}"/etc/apt/apt.conf.d/90cache
rm -f "${R}"/etc/apt/sources.list.d/*.save
rm -f "${R}"/root/.wget-hsts
rm -rf "${R}"/tmp/*
rm -f "${R}"/var/log/apt/*
rm -f "${R}"/var/log/alternatives.log
rm -f "${R}"/var/log/bootstrap.log
rm -f "${R}"/var/log/dpkg.log
rm -f "${R}"/var/log/fontconfig.log
rm -f "${R}"/var/cache/debconf/*-old
rm -f "${R}"/var/cache/deb-get/*.json
rm -f "${R}"/var/cache/fontconfig/CACHEDIR.TAG
rm -f "${R}"/var/crash/*
rm -rf "${R}"/var/lib/apt/lists/*
rm -f "${R}"/var/lib/dpkg/*-old
[ -L "${R}"/var/lib/dbus/machine-id ] || rm -f "${R}"/var/lib/dbus/machine-id
echo '' > "${R}"/etc/machine-id
# Only required if Connman has been made the default network renderer
cd "${R}/etc"
ln -sf ../run/connman/resolv.conf resolv.conf
cd -
}
function stage_07_image() {
export B="${B_STAGE_6}"
export R="${R_STAGE_6}"
# Build the image file
local SIZE_BOOT="256"
local SIZE_ROOT=0
local SIZE_IMG=0
local SIZE_PAD=0
# Calculate image size accounting for boot parition + 5%
SIZE_ROOT=$(du -cs --block-size=MB "${R}" | tail -n1 | cut -d'M' -f1)
SIZE_PAD=$(( (SIZE_ROOT / 10) / 2 ))
SIZE_IMG=$((SIZE_BOOT + SIZE_ROOT + SIZE_PAD))
# Create an empty file file.
rm -fv "${TMP_DIR}/${IMG_OUT}"
fallocate -l "${SIZE_IMG}"M "${TMP_DIR}/${IMG_OUT}"
# Initialising: msdos
parted -s "${TMP_DIR}/${IMG_OUT}" mktable msdos
echo "Creating /boot/firmware partition"
parted -a optimal -s "${TMP_DIR}/${IMG_OUT}" mkpart primary fat32 1 "${SIZE_BOOT}MB"
echo "Creating / partition"
parted -a optimal -s "${TMP_DIR}/${IMG_OUT}" mkpart primary ext4 "${SIZE_BOOT}MB" 100%
echo "Making partition 1 bootable"
parted -s "${TMP_DIR}/${IMG_OUT}" set 1 boot on
PARTED_OUT=$(parted -s "${TMP_DIR}/${IMG_OUT}" unit b print)
BOOT_OFFSET=$(echo "${PARTED_OUT}" | grep -e '^ 1'| xargs echo -n \
| cut -d" " -f 2 | tr -d B)
BOOT_LENGTH=$(echo "${PARTED_OUT}" | grep -e '^ 1'| xargs echo -n \
| cut -d" " -f 4 | tr -d B)
ROOT_OFFSET=$(echo "${PARTED_OUT}" | grep -e '^ 2'| xargs echo -n \
| cut -d" " -f 2 | tr -d B)
ROOT_LENGTH=$(echo "${PARTED_OUT}" | grep -e '^ 2'| xargs echo -n \
| cut -d" " -f 4 | tr -d B)
BOOT_LOOP=$(losetup --show -f -o "${BOOT_OFFSET}" --sizelimit "${BOOT_LENGTH}" "${TMP_DIR}/${IMG_OUT}")
ROOT_LOOP=$(losetup --show -f -o "${ROOT_OFFSET}" --sizelimit "${ROOT_LENGTH}" "${TMP_DIR}/${IMG_OUT}")
echo "/boot/firmware: offset ${BOOT_OFFSET}, length ${BOOT_LENGTH}"
echo "/: offset ${ROOT_OFFSET}, length ${ROOT_LENGTH}"
mkfs.vfat -n system-boot -S 512 -s 16 -v "${BOOT_LOOP}"
mkfs.ext4 -L writable -m 0 "${ROOT_LOOP}"
MOUNTDIR="${TMP_DIR}/image"
mkdir -p "${MOUNTDIR}"
mount -v "${ROOT_LOOP}" "${MOUNTDIR}" -t ext4
mkdir -p "${MOUNTDIR}/boot/firmware"
mount -v "${BOOT_LOOP}" "${MOUNTDIR}/boot/firmware" -t vfat
echo "Syncing root..."
rsync -aHAXx --delete "${R}"/ "${MOUNTDIR}/"
echo "Syncing boot..."
rsync -aHAXx --delete "${B}"/ "${MOUNTDIR}/boot/firmware/"
mkdir -p "${MOUNTDIR}/.disk"
date +"%Y%m%d" > "${MOUNTDIR}/.disk/info"
sync
umount -l "${MOUNTDIR}/boot/firmware"
umount -l "${MOUNTDIR}"
losetup -d "${ROOT_LOOP}"
losetup -d "${BOOT_LOOP}"
ls -lh "${TMP_DIR}/${IMG_OUT}"
rm -rf "${MOUNTDIR}"
}
function stage_08_compress() {
export B="${B_STAGE_6}"
export R="${R_STAGE_6}"
# NOTE! Disabled while iterating
echo "Compressing ${IMG_OUT}.xz"
rm "${TMP_DIR}/${IMG_OUT}.xz" 2>/dev/null
xz --keep -T 0 "${TMP_DIR}/${IMG_OUT}"
ls -lh "${TMP_DIR}/${IMG_OUT}.xz"
local HASH="sha256"
local OUT_HASH="${TMP_DIR}/${IMG_OUT}.xz.${HASH}"
rm -f "${OUT_HASH}"
if [ -e "${TMP_DIR}/${IMG_OUT}.xz" ]; then
echo "Hashing ${IMG_OUT}.xz"
${HASH}sum "${TMP_DIR}/${IMG_OUT}.xz" > "${OUT_HASH}"
sed -i -r "s/ .*\/(.+)/ \1/g" "${OUT_HASH}"
else
echo "WARNING! Didn't find ${TMP_DIR}/${IMG_OUT} to hash."
fi
}
if [ -z "${SUDO_USER}" ]; then
echo "ERROR! You must use sudo to run this script: sudo ./$(basename "${0}")"
exit 1
else
SUDO_HOME=$(getent passwd "${SUDO_USER}" | cut -d: -f6)
fi
# Take command line arguments
if [ $# -lt 1 ]; then
usage
exit 0
else
while [ $# -gt 0 ]; do
case "${1}" in
-device|--device)
REMIX="${2}"
shift
shift;;
*)
echo "[!] ERROR: \"${1}\" is not a supported parameter."
usage
exit 1;;
esac
done
fi
INCLUDE_DESKTOP="false"
INCLUDE_LUDO="true"
SIZE_LOGO="256"
SIZE_FRAME="384"
SIZE_FB="1920x1080"
# Set variables based on chosen desktop environment
case "${REMIX}" in
raspi)
echo "[+] Specified Rasberry Pi 2, 3, 4 or 400"
shift;;
raspi-desktop)
echo "[+] Specified Rasberry Pi 2, 3, 4 or 400 (with desktop)"
INCLUDE_DESKTOP="true"
shift;;
deskpi)
# https://wiki.deskpi.com/deskpi/
# https://github.com/DeskPi-Team/deskpi
echo "[+] Specified DeskPi Pro/Lite for Raspberry Pi 4"
shift;;
deskpi-desktop)
echo "[+] Specified DeskPi Pro/Lite for Raspberry Pi 4 (with desktop)"
INCLUDE_DESKTOP="true"
shift;;
gamehat)
#https://www.waveshare.com/wiki/Game_HAT
#patched driver for > 4.15 kernel
# https://github.com/cmitu/mk_arcade_joystick_rpi/tree/retropie
# reffered by https://github.com/RetroPie/RetroPie-Setup/blob/master/scriptmodules/supplementary/mkarcadejoystick.sh
echo "[+] Specified Waveshare Game HAT"
not_implemented
shift;;
gamepi20)
#https://www.waveshare.com/wiki/GamePi20
#SAME driver as above just additional SCREEN over i2c
echo "[+] Specified Waveshare GamePi20"
not_implemented
shift;;
gamepi43)
#https://www.waveshare.com/wiki/GamePi43
echo "[+] Specified Waveshare GamePi43"
not_implemented
shift;;
gpicase|gpimate*)
# GPi Case: https://retroflag.com/GPi-CASE.html
# GPi Mate: https://retrogamerestore.com/store/gpimate/
# GPi Mate Plus: https://retrogamerestore.com/store/gpimateplus/
echo "[+] Specified RetroFlag GPi Case with:"
echo " - Raspberry Pi Zero 2 W"
echo " - GPiMatePlus and CM3+Lite"
echo " - GPiMatePlus and CM4+Lite"
SIZE_FB="320x240"
SIZE_LOGO="128"
SIZE_FRAME="192"
shift;;
gpicase2)
echo "[+] Specified RetroFlag GPi Case 2 with CM4+Lite"
SIZE_FB="640x480"
not_implemented
shift;;
gpm2803)
# https://www.waveshare.com/wiki/GPM280
echo "[+] Specified Waveshare GPM2803 with CM3+Lite"
not_implemented
shift;;
gpm2804)
# https://www.waveshare.com/wiki/GPM2804
SIZE_FB="640x480"
echo "[+] Specified Waveshare GPM2804 with CM4+Lite"
shift;;
megapi)
echo "[+] Specified RetroFlag MEGAPi Case"
REMIX="retroflag"
shift;;
minin64)
# https://megabitnes.com/store/n64-pi4/
echo "[+] Specified Megabit Mini N64"
REMIX="megabit"
shift;;
minips1)
# https://megabitnes.com/store/mini-ps1-case-non-nfc/
# https://megabitnes.com/non-nfc/
echo "[+] Specified Megabit Mini PS1 (Non-NFC)"
REMIX="megabit"
shift;;
monster)
#https://github.com/RetroPie/RetroPie-Setup/blob/master/scriptmodules/supplementary/snesdev.sh
#V1 and V2 differs this needs to be setup on first boot
echo "[+] Specified Monster Arcade Controller Kit"
not_implemented
shift;;
nespi|retroflag)
echo "[+] Specified RetroFlag NESPi Case+"
REMIX="retroflag"
shift;;
nespi4)
echo "[+] Specified RetroFlag NESPi 4 Case"
REMIX="retroflag"
shift;;
picade)
#https://github.com/pimoroni/picade-hat
#recommend hot_plug enabled
echo "[+] Specified Pimoroni Picade"
not_implemented
shift;;
picadeconsole)
#Same as the previous one screen attached with hdmi, recommend hot_plug enabled
echo "[+] Specified Pimoroni Picade Console"
not_implemented
shift;;
pistation)
echo "[+] Specified RetroFlag PiStation Case"
REMIX="retroflag"
shift;;
pistationlcd)
#same as retroflag LCD attaches to first microHDMI and connects to USB-C power
echo "[+] Specified RetroFlag PiStation Case with LCD"
REMIX="retroflag"
shift;;
superpi)
echo "[+] Specified RetroFlag SUPERPi Case"
REMIX="retroflag"
shift;;
superpi4)
echo "[+] Specified RetroFlag SUPERPi 4 Case"
REMIX="retroflag"
shift;;
*)
if [ -z "${REMIX}" ]; then
echo "[!] ERROR: Please specifiy a target device"
else
echo "[!] ERROR: ${REMIX} is not a valid target device"
fi
usage
exit 1;;
esac
# Install apt-cacher-ng on the host and this script will use it.
APT_CACHE_IP=$(ip route get 1.1.1.1 | head -n 1 | cut -d' ' -f 7)
IMG_QUALITY="-alpha1"
IMG_VER="22.04"
IMG_RELEASE="jammy"
IMG_ARCH="armhf"
IMG_STAMP=$(date +%y.%j)
IMG_OUT="retro-home-${REMIX}-${IMG_STAMP}${IMG_QUALITY}-${IMG_ARCH}.img"
USER_NAME="ludo"
USER_PASS="retro"
USER_HOME="/storage"
TMP_DIR="${SUDO_HOME}/Builds"
# Create caches
for LOOP in 0 1 2 3 4 5 6 7 8 9; do
case ${LOOP} in
0)
export R_STAGE_${LOOP}="${TMP_DIR}/${IMG_ARCH}/retro-home/${IMG_VER}/${LOOP}_cache"
mkdir -p "${TMP_DIR}/${IMG_ARCH}/retro-home/${IMG_VER}/${LOOP}_cache" 2>/dev/null
;;
1)
export B_STAGE_${LOOP}="${TMP_DIR}/${IMG_ARCH}/retro-home/${IMG_VER}/${LOOP}_boot"
export R_STAGE_${LOOP}="${TMP_DIR}/${IMG_ARCH}/retro-home/${IMG_VER}/${LOOP}_root"
mkdir -p "${TMP_DIR}/${IMG_ARCH}/retro-home/${IMG_VER}/${LOOP}_boot" 2>/dev/null
mkdir -p "${TMP_DIR}/${IMG_ARCH}/retro-home/${IMG_VER}/${LOOP}_root/boot/firmware" 2>/dev/null
;;
*)
export B_STAGE_${LOOP}="${TMP_DIR}/${IMG_ARCH}/retro-home-${REMIX}/${IMG_VER}/${LOOP}_boot"
export R_STAGE_${LOOP}="${TMP_DIR}/${IMG_ARCH}/retro-home-${REMIX}/${IMG_VER}/${LOOP}_root"
mkdir -p "${TMP_DIR}/${IMG_ARCH}/retro-home-${REMIX}/${IMG_VER}/${LOOP}_boot" 2>/dev/null
mkdir -p "${TMP_DIR}/${IMG_ARCH}/retro-home-${REMIX}/${IMG_VER}/${LOOP}_root/boot/firmware" 2>/dev/null
;;
esac
done
stage_01_bootstrap
stage_02_ludo
stage_03_micro_desktop
stage_04_kernel
stage_05_config
stage_06_clean
stage_07_image
stage_08_compress