├── 1piboot ├── 010-disable-triggerhappy.sh-Sample ├── 030-disable-rsyslog.sh-Sample └── 1piboot.conf ├── CHANGELOG.md ├── Docs ├── 8Upgrade-Notes.md ├── 9Upgrade-Notes.md ├── BatchBurn-SSD-SDs-with-sdm-gburn.md ├── Burn-Scripts.md ├── Captive-Portal.md ├── Command-Details.md ├── Compatibility.md ├── Cool-Things-You-Can-Do-Hotspot.md ├── Cool-Things-You-Can-Do-SSHkey.md ├── Cool-Things-You-Can-Do-VPN.md ├── Cool-Things-You-Can-Do-with-sdm.md ├── Credits.md ├── Custom-Phase-Script.md ├── Description.md ├── Detailed-Installation-Guide.md ├── Disk-Encryption.md ├── Disks-Partitions.md ├── Example-Burn-Multiple-Hosts-From-Single-IMG.md ├── Example-Burn-Scripts.md ├── Example-Commands.md ├── Example-Custom-Phase-Script.md ├── Example-Plugin.md ├── First-Boot-Scripts-and-Configurations.md ├── First-Boot-Service.md ├── Hint-Using-sdm-on-2022-04-04-and-later-images.md ├── Hints-Commands.md ├── Hints-NetworkManager.md ├── Hotspot.md ├── Index.md ├── Installing-or-Removing-sdm.md ├── Known-Issues.md ├── LED-flashing.md ├── Operating-Details.md ├── Passwords.md ├── Performance-How-Fast-is-sdm.md ├── Plugins.md ├── Programming-Plugins-and-Custom-Phase-Scripts.md ├── Script-Details.md ├── Terminal-Colors.md ├── Troubleshooting.md ├── Using-Docker.md ├── Using-LXDE-Config.md ├── Using-Labwc-Config.md ├── Using-sdm-Plugins-on-uncustomized-system.md ├── Using-sdm-on-Windows-WSL.md ├── apt-Cacher-NG.md ├── fstab.md └── raspiconfig-and-1piboot.md ├── EZsdmInstaller ├── LICENSE ├── QuickStart.md ├── README.md ├── extras └── rpnc ├── ezsdm ├── plugins ├── L10n ├── apps ├── apt-addrepo ├── apt-cacher-ng ├── apt-file ├── bootconfig ├── btwifiset ├── burnpwd ├── chrony ├── clockfake ├── cmdline ├── copydir ├── copyfile ├── cryptroot ├── disables ├── docker-install ├── dovecot-imap ├── explore ├── extractfs ├── gadgetmode ├── git-clone ├── graphics ├── hotspot ├── imon ├── knockd ├── labwc ├── logwatch ├── lxde ├── mkdir ├── modattr ├── ndm ├── network ├── parted ├── piapps ├── pistrong ├── postburn ├── postfix ├── quietness ├── raspiconfig ├── runatboot ├── runscript ├── rxapp ├── samba ├── sdm-plugin-template ├── serial ├── speedtest ├── sshd ├── sshhostkey ├── sshkey ├── syncthing ├── system ├── trim-enable ├── ufw ├── user ├── venv ├── vnc ├── wificonfig └── wsdd ├── satrim ├── sdm ├── sdm-add-luks-key ├── sdm-apps-example ├── sdm-apt ├── sdm-apt-cacher ├── sdm-cmdsubs ├── sdm-collect-labwc-config ├── sdm-cparse ├── sdm-cportal ├── sdm-cryptconfig ├── sdm-customphase ├── sdm-firstboot ├── sdm-gburn ├── sdm-hotspot ├── sdm-logmsg ├── sdm-make-luks-usb-key ├── sdm-phase0 ├── sdm-phase1 ├── sdm-readparams ├── sdm-rpcsubs ├── sdm-ssh-initramfs ├── sdm-xapps-example └── sdmcryptfs /1piboot/010-disable-triggerhappy.sh-Sample: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | logger "FirstBoot: Disabling TriggerHappy..." 3 | [ -f /usr/sbin/thd ] && mv /usr/sbin/thd /usr/sbin/.thd # Speeds up /etc/init.d/raspi-config.service 4 | systemctl disable triggerhappy.service 5 | systemctl disable triggerhappy.socket 6 | # Eliminate thd.socket journalctl errors 7 | [ -f /lib/udev/rules.d/60-triggerhappy.rules ] && mv /lib/udev/rules.d/60-triggerhappy.rules /lib/udev/rules.d/.60-triggerhappy.rules 8 | -------------------------------------------------------------------------------- /1piboot/030-disable-rsyslog.sh-Sample: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | logger "FirstBoot: Disabling rsyslog and creating permanent journal..." 4 | systemctl disable rsyslog.service 5 | mkdir -p /etc/systemd/journald.conf.d 6 | cat > /etc/systemd/journald.conf.d/030-sdm-no-syslog.conf <are fully described here 15 | 16 | * `apps` — The `apps` plugin replaces the `--apps` and `--xapps` command line switches. Use the `name=` argument to set a name for each one. The apps plugin can be specified on the command line as many times as desired, with a different `name=` for each one. 17 | 18 | Where you used `--apps @myapplist --xapps @myxapplist` prior to V8, you would now use 19 | 20 | ``` 21 | --plugin apps:"apps=@myapplist|name=myapps" --plugin apps:"apps=@myxapps|name=myxapps" 22 | ``` 23 | 24 | * `graphics` — Use the `graphics` plugin to configure graphics. This includes Wayland vs X11, graphics on the console, the video mode in cmdline.txt, and setting a left-handed mouse. 25 | * `network` — Use the `network` plugin to configure various network settings, such as NetworkManager vs dhcpcd, WiFi configuration, and additional configuration for both dhcpcd and NetworkManager. 26 | * `quietness` — The `quietness` plugin controls the cmdline.txt settings `quiet` and `splash`, as well as the plymouth graphical startup splash screens. 27 | * `wificonfig` — Use the `wificonfig` plugin to enable over-the-air WiFi configuration of a Pi during the first system boot. 28 | 29 | ## Why move command line switches to plugins? 30 | 31 | This change enables more modular code in sdm, which furthers additional development. The documentation is now clearer since each function is documented in one place, rather than being spread around the various pages. 32 | 33 | A side effect of some of the plugins, such as the `apps` plugin in particular, is that there is no restriction on the number of times the plugin can be called. This enables you to partition your app install lists, for example, into `core-apps`, `core-xapps`, `non-core-apps`, and `non-core-xapps`. 34 | 35 | 36 | ## Other Useful info 37 | 38 | * Bookworm — V8 has initial support for Bookworm. As this support was added based on a preview RasPiOS release, additional changes may be required once it is formally released. 39 | 40 | Releases of sdm prior to V8 do not include any formal Bookworm support. 41 | 42 |
43 | 44 |
45 | -------------------------------------------------------------------------------- /Docs/9Upgrade-Notes.md: -------------------------------------------------------------------------------- 1 | # Migrating to sdm v9 2 | 3 | This page should answer most questions about switching from sdm v8 or earlier to v9. 4 | 5 | ## Why convert all these switches to plugins? 6 | 7 | As sdm has acquired functionality, the number of switches grew without bound, which creates user adoption issues (wow! there are so many switches), as well as adding complexity to adding new features. In the non-plugin model, at least 3, and sometimes more scripts required modification to add a new feature. 8 | 9 | Now, all the functionality for a particular plugin is encapsulated in a single script. This simplifies usage, code, documentation, and future improvements. 10 | 11 | ## What plugin is this switch in? 12 | 13 | Each relocated switch is an argument with the same name, unless specifically mentioned otherwise. The values for each argument are the same as the switch values, with a couple of special notes: 14 | 15 | * Each argument can only be specified once in a plugin argument list, but many of the arguments take a list of something or another. See the plugin documentation for details. 16 | * Plugins can be provided multiple times on the command line if desired, and can be used on both customize and burn 17 | 18 | These switches have relocated to the `system` plugin (Documentation) 19 | 20 | * `--cron-d` 21 | * `--cron-hourly` 22 | * `--cron-daily` 23 | * `--cron-weekly` 24 | * `--cron-monthly` 25 | * `--cron-systemd` 26 | * `--eeprom` 27 | * `--exports` 28 | * `--fstab` 29 | * `--journal` 30 | * `--modprobe` 31 | * `--motd` 32 | * `--rclocal` 33 | * `--sysctl` 34 | * `--systemd-config` 35 | * `--swap` 36 | * `--udev` 37 | * `--svc*disable` 38 | * `--svc*enable` 39 | 40 | These switches have relocated to the `bootconfig` plugin (Documentation) 41 | 42 | * `--bootadd` 43 | * `--bootconfig` 44 | * `--dtoverlay` 45 | * `--dtparam` 46 | * `--hdmigroup` 47 | * `--hdmimode` 48 | * `--hdmi-force-hotplug` 49 | * `--hdmi-ignore-edid` 50 | 51 | This switch is in the `disables` plugin (Documentation) 52 | 53 | * `--disable` 54 | 55 | This switch is in the `raspiconfig` plugin (Documentation) 56 | 57 | * `--bootset` 58 | 59 | These switches have relocated to the `L10n` plugin (Documentation) 60 | 61 | * `--keymap` 62 | * `--locale` 63 | * `--timezone` 64 | * `--L10n` 65 | 66 | These switches have relocated to the `user` plugin (Documentation) 67 | 68 | * `--user` 69 | * `--nouser` 70 | * `--nopassword` 71 | * `--uid` 72 | * `--password-pi` 73 | * `--password-user` 74 | * `--password-root` 75 | * `--password-same` 76 | * `--rename-pi` 77 | * `--rootpwd` 78 | 79 | These switches are in the `lxde` plugin (Documentation) 80 | 81 | * `--lxde-config` 82 | * `--lhmouse` 83 | 84 | These switches are in the `network` plugin (Documentation) 85 | 86 | * `--netman` 87 | * `--dhcpcdwait` 88 | * `--dhcpcd` 89 | * `--ssh` 90 | * `--wificountry` 91 | * `--wpa` 92 | 93 | ## What is the replacement for this plugin? 94 | 95 | * `adduser` — The `adduser` plugin is now the `user` plugin 96 | * `burnpwd` — The `user` plugin includes the ability to prompt for a user's password 97 | 98 |
99 |
100 | 101 |
102 | -------------------------------------------------------------------------------- /Docs/Burn-Scripts.md: -------------------------------------------------------------------------------- 1 | # Burn Scripts 2 | 3 | Burn scripts enable you to do updates that are specific to a single system as you burn the media for it. An obvious use case for burn scripts is for use with scripted burning of IMGs. For instance `sdm-gburn` uses burn scripts to programmatically perform per-destination custom updates. 4 | 5 | For more general use, Plugins are a better solution. Plugins can be used during IMG customization, and hence available to all systems burned from that IMG, or burning an IMG and only available on that particular burn target. 6 | 7 | ## Overview 8 | 9 | There are cases where it is desirable to do per-system customization on the SD Card or burn image after it has been burned. Examples include: 10 | 11 | * Copy additional files onto the SD Card 12 | * Configuration file customizations for a specific system 13 | * Apps installed for a specific system 14 | 15 | If the only differences between your "standard" image and per-device customizations are relatively modest (from your perspective), you can use Burn Scripts to implement these customizations on the burn output device or file. 16 | 17 | In the first case (copying additional files), your script will need access to both the host system and the SD Card. `--b0script` should be used for this. The execution environment for `--b0script` is the same as *Phase 0* (see Custom Phase Script), and should follow the guidelines for a Custom Phase Script Phase 0. sdm will invoke the procedure `do_b0script` in the b0script file. 18 | 19 | In the second and third cases, your script wants to do things in the context of the newly-created system. Use `--b1script` for that. sdm will nspawn into the SD Card, so your script should follow the guidelines for a Custom Phase Script Phase 1. 20 | 21 | The argument to both switches is a /complete/path/to/script. 22 | 23 | ## Logging to /etc/sdm/history 24 | * For `--b0script`: Use `logtoboth "string to log"` in your `--b0script` 25 | * For `--b1script`: `source /etc/sdm/sdm-readparams` at the top of your script, then use `logtoboth "string to log"` 26 | 27 | The `--b1script` script will be copied to /etc/sdm/assets on the SD Card/image before the nspawn, and is not deleted. 28 | 29 | See Example Burn Scripts for examples of b0script and b1script 30 | 31 | ## Switches that work with --burn and --burnfile 32 | 33 | These switches can be used with `--burn` and `--burnfile`. When used this way, they affect only the output IMG/SSD/SD Card, and not the source IMG file. 34 | 35 | * `--autologin` 36 | * `--b0script` 37 | * `--b1script` 38 | * `--bootscripts` 39 | * `--burn-plugin` 40 | * `--expand-root` 41 | * `--no-expand-root` 42 | * `--hostname` 43 | * `--noreboot` and `--norestart` 44 | * `--nowait-timesync` 45 | * `--plugin` 46 | * `--plugin-debug` 47 | * `--reboot` 48 | * `--redact` 49 | * `--regen-ssh-host-keys` 50 | * `--update-plugins` 51 | 52 | In addition to the above switches, any plugin can be run from the `--burn` or `--burnfile` command line, exactly as you can when customizing an IMG. 53 |
54 |
55 |
56 | 57 |
58 | -------------------------------------------------------------------------------- /Docs/Compatibility.md: -------------------------------------------------------------------------------- 1 | # Compatibility 2 | 3 | ## Compatibility — Non-Pi Linux and Pi 32-bit vs 64-bit 4 | 5 | sdm only performs customization of RasPiOS images. However, sdm itself can run on other Linux platforms. 6 | 7 | sdm, written in Bash, is largely Linux distro-independent and is completely 32-vs-64-bit agnostic. 8 | 9 | As of sdm V7.2, sdm itself can now be run on x86_64 Debian-like distros. In addition, sdm running on 32-bit ARM can now operate on 64-bit ARM RasPiOS images. sdm also runs on the Windows Subsystem for Linux) 10 | 11 | In order to do image customization or use `--explore` on an image when running on a non-RasPiOS host (e.g., x86 or x86_64), the following packages must be installed. EZsdmInstaller installs these packages, and is the recommended method for installing sdm. See Installing sdm 12 | ```sh 13 | sudo apt install qemu-user-static binfmt-support systemd-container parted 14 | ``` 15 | These components enable image customization and `--explore` on an RasPiOS image. If this doesn't work on your Linux system, it may be too old and lacking updated support or fixes. I have tested this on Ubuntu 20.04, and it's able to operate on both RasPiOS 32 and 64-bit images. 16 | 17 | Running on **64-bit RasPiOS** sdm can perform all functions: customize, explore, burn, and mount both 32-bit and 64-bit RasPiOS images. 18 | 19 | Running on **32-bit RasPiOS** sdm can also perform all functions. However, when operating on 64-bit RasPiOS images qemu emulation (which runs more slowly) must be used in Phase1, post-install, and burn b0script/b1script functions, as well as Phase 1 and post-install phases in Plugins. 20 | 21 | Running on **x64 Debian-like distros**, sdm can also perform all functions. However, as with 32-bit RasPiOS, qemu emulation must be used in Phase1, post-install, and burn b0script/b1script functions and Plugin Phase 1 and post-install phases 22 | 23 | ## Pi hardware-specific issues 24 | 25 | * Pi5 (at least): Running a 32-bit host OS and customizing a 32-bit IMG fails spectularly. See this issue for further information. 26 |
27 |
28 | 29 |
30 | -------------------------------------------------------------------------------- /Docs/Cool-Things-You-Can-Do-Hotspot.md: -------------------------------------------------------------------------------- 1 | # Cool and Useful Things You Can Do: Hotspot 2 | 3 | Your Pi can be used as a hotspot, providing network connectivity to other devices connected to it via WiFi or USB. The `hotspot` plugin enables you to easily configure either one. 4 | 5 | ## Configure Pi as a WiFi Hotspot 6 | 7 | This example uses a hotspot served via WiFi wlan0 with traffic forwarded to the internet via eth0. The config file contents are described here. 8 | 9 | ``` 10 | device=wlan0 11 | hsenable=y 12 | hsname=myhotspot 13 | ipforward=eth0 14 | type=routed 15 | wifipassword=SecretPassword 16 | wifissid=myhotspot 17 | ``` 18 | 19 | Invoke the hotspot plugin with `--plugin hotspot:config=/path/to/hotspot-config.txt` 20 | 21 | `hotspot` plugin documentation 22 | 23 | ## Configure Pi as a USB Hotspot (Tether Host) 24 | 25 | Similarly, your Pi can provide a hotspot via usb0 (tether host) with traffic forwarded to the internet via wlan0 or another device of your choice (e.g., eth0). 26 | 27 | When a device such as a Pi configured for tether (client) is plugged into a USB port, the usb0 hotspot will be activated. The `wifissid` and `wifipassword` arguments aren't used with USB hotspots. 28 | 29 | ``` 30 | device=usb0 31 | hsenable=y 32 | hsname=myhotspot 33 | ipforward=wlan0 34 | type=routed 35 | wifipassword=SecretPassword 36 | wifissid=myhotspot 37 | ``` 38 | 39 | Change the ipforward device to be the device that is connected to the network you want to route to. 40 | 41 | The USB hotspot plugin is configured the same way as a WiFi hotspot: `--plugin hotspot:config=/path/to/usb-hotspot-config.txt` 42 | 43 | `hotspot` plugin documentation 44 | 45 | ## Configure Pi as a USB Tether Client 46 | 47 | Configure a Pi with the `gadgetmode` plugin and plug this tether client via USB to a Pi with a USB hotspot plugin (above), or an appropriately configured MacOS or Windows system. 48 | 49 | Invoke the `gadgetmode` plugin `--plugin gadgetmode:static-mac` 50 | 51 | `gadgetmode` plugin documentation 52 | 53 |
54 |
55 | 56 |
57 | -------------------------------------------------------------------------------- /Docs/Cool-Things-You-Can-Do-SSHkey.md: -------------------------------------------------------------------------------- 1 | # Cool and Useful Things: SSHkey 2 | 3 | Generate a host-specific SSH key when burning the disk and retrieve it for use elsewhere. 4 | 5 | Use the `sshkey` plugin to create an SSH key for a specific user/disk. Use the `postburn` plugin to run a script that extracts certs from the burned disk to a host-based directory. 6 | ``` 7 | --plugin sshkey:"sshuser=myuser|keyname=mykeyname|passphrase=mypassphrase" 8 | --burn-plugin postburn:"runscript=/path/to/postburn-get-certs|runphase=phase0|where=host" 9 | ``` 10 | 11 | with the script post-burn-get-certs: 12 | 13 | ``` 14 | #!/bin/bash 15 | 16 | mydir="/path/to/my/dir" 17 | echo "> Copy certs to save location on the host" 18 | # This will require your customization. Use $SDMPT to reference files and directories on the burned disk 19 | # 20 | #cp $SDMPT/path/cert-file /path/on/host/dir 21 | # 22 | # For example 23 | # 24 | cp $SDMPT/home/myuser/.ssh/mykeyname /path/on/host/dir 25 | ``` 26 | 27 | `sshkey` plugin documentation
28 | `postburn` plugin documentation 29 | 30 | 31 |
32 |
33 | 34 |
35 | -------------------------------------------------------------------------------- /Docs/Cool-Things-You-Can-Do-with-sdm.md: -------------------------------------------------------------------------------- 1 | # Cool and Useful Things You Can Do With sdm 2 | 3 | Here are some complete examples of some cool things that you can easily implement with sdm. 4 | 5 | * Configure Pi as a Hotspot — Configure a Hotspot for use over WiFi or USB; enable gadget mode on a tether client 6 | * Host-specific SSH keys — Generate and retrieve a host-specific SSH key for a user 7 | * VPN Configuration — Quickly and easily build a 2-node site-to-site VPN with an optional client/server VPN 8 | 9 |
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /Docs/Credits.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | 3 | sdm was inspired by posts by @HawaiianPi and @sakaki in the Raspberry Pi Forum: [STICKY: Making your own custom burn-n-boot Raspbian image](https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=231762). 4 | 5 | The sdm documentation reorg inspired by feedback from github user @mcguirepr89. 6 | 7 | Thanks to @dshaw619 and @tiefenauer for their wonderful suggestions and patience. 8 | 9 | Thanks to @tinker2much for his in-depth Doc reviews and testing of the new plugins. 10 | 11 | And thanks to the many other users who have provided constructive feedback and patiently worked with me to resolve problems. 12 | 13 | Thanks! 14 | 15 |
16 |
17 | 18 |
19 | -------------------------------------------------------------------------------- /Docs/Custom-Phase-Script.md: -------------------------------------------------------------------------------- 1 | # Custom Phase Script 2 | 3 | ## Overview 4 | 5 | A Custom Phase Script is a script provided by you. You put all the commands in the Custom Phase Script that you want to have done to the IMG. 6 | 7 | Historically, sdm supported Custom Phase Scripts first. Plugins, which were added in V7.0, are logically the same as Custom Phase Scripts, but can also be used at burn time. sdm will continue to support Custom Phase Scripts, but Plugins are the recommended approach for extending sdm capabilities or doing personal customizations. 8 | 9 | The Custom Phase Script is called 3 times: Once for Phase 0, once for Phase 1, and once after Phase 1 has completed. The first argument indicates the current phase ("0", "1", or "post-install"). The Custom Phase Script needs to be aware of the phase, as there are contextual differences: 10 | 11 | * In Phase 0, the host file system is fully available. The IMG file is typically mounted on /mnt/sdm (although it may be mounted on /mnt/sdm.xxxx if /mnt/sdm is busy), so all references to the IMG file system must be appropriately referenced by prefacing the directory string with $SDMPT, which is always correct. This enables the Custom Phase script to copy files from the host file system into the IMG file. 12 | 13 | * In Phase 1 and post-install (both inside nspawn) the script runs in the context of the IMG. This is where you install and configure any additional or apps with special install requirements. In both Phase 1 and post-install, the host file system is not available at all. Thus, if a file is needed in Phase 1, Phase 0 must copy it into the IMG. References to /mnt/sdm will fail in Phase 1. If preferred, you can use $SDMPT in Phase 1 and the post-install phase also, as sdm defines it as "". 14 | 15 | If a Custom Phase Script wants to run a script at boot time, even if `--bootscripts` is not specified, the Custom Phase script should put the script in the IMG in /etc/sdm/0piboot and named 0*-*.sh (e.g., 010-customize-something.sh). These scripts are always run by FirstBoot. 16 | 17 | The best way to build a Custom Phase Script is to start with a copy of the example Custom Phase Script `sdm-customphase`, and extend it as desired. 18 | 19 | ## Command line switches 20 | 21 | * `--cscript` *scriptname* — Specifies the path to your Custom Phase Script, which will be run as described in the Custom Phase Script section below. 22 | * `--csrc` */path/to/csrcdir* — A source directory string that can be used in your Custom Phase Script. One use for this is to have a directory tree where all your customizations are kept, and pass in the directory tree to sdm with `--csrc`. 23 | * `--custom[1-4]` — 4 variables (custom1, custom2, custom3, and custom4) that can be used to further customize your Custom Phase Script. 24 |
25 |
26 |
27 | 28 |
29 | -------------------------------------------------------------------------------- /Docs/Description.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | `sdm` provides a quick and easy way to build consistent, ready-to-go SSDs and/or SD cards for the Raspberry Pi. This command line management tool is especially useful if you: 4 | 5 | * have multiple Raspberry Pi systems and you want them all to start from an identical and consistent set of installed software packages, configuration scripts and settings, etc. 6 | 7 | * want to rebuild your Pi system in a consistent manner with all your favorite packages and customizations already installed. Every time. 8 | 9 | * want to do the above repeatedly and a LOT more quickly. 10 | 11 | What does *ready-to-go* mean? It means that every one of your systems is fully configured with Keyboard mapping, Locale, Timezone, and WiFi set up as you want, all of your personal customizations and all desired RasPiOS packages and updates installed. 12 | 13 | In other words, all ready to work on your next project. 14 | 15 | With `sdm` you'll spend a lot less time rebuilding SSDs/SD Cards, configuring your system, and installing packages, and more time on the things you really want to do with your Pi. 16 | 17 | Someone in the RaspberryPi.org forums said *"Generally I get by by reflashing an SD card and reinstalling everything from the notes I made previously. That is not such a long winded process."* 18 | 19 | While better than not having *any* notes, this approach requires relatively complete notes, and careful attention to detail each and every time you need to reflash a card. 20 | 21 | `sdm` lets you keep your notes in simple working bash code and comments, and makes a "not such a long winded process" into a single command that you run whenever you need to create a new SD card or SSD. And the disk is built with ALL of your favorite apps installed and all your favorite customizations. 22 | 23 | `sdm` is for RasPiOS IMGs, and runs on RasPiOS Stretch, Buster, Bullseye, and Bookworm. It can also run on other Linux systems. See Compatibility. `sdm` requires a USB SD Card reader to write a new SD Card, or a USB adapter to write a new SSD. You cannot use `sdm` to rewrite the running system's system disk. 24 | 25 | `sdm` is written completely in Bash, except for the Captive Portal module, which is Python. This means that you can: 26 | 27 | * **Easily inspect** EVERYTHING that sdm does 28 | * **Easily make changes to sdm**, although sdm makes it easy to implement your customizations so you shouldn't need to modify sdm itself 29 | 30 | Have questions about sdm? Please don't hesitate to ask in the Issues section of this github. If you don't have a github account (so can't post an issue/question here), please feel free to email me at: [gitbls@outlook.com](mailto:gitbls@outlook.com). 31 | 32 | You can watch sdm in action [here](https://youtu.be/CpntmXK2wpA) 33 |
34 |
35 | 36 |
37 | -------------------------------------------------------------------------------- /Docs/Detailed-Installation-Guide.md: -------------------------------------------------------------------------------- 1 | # Detailed Installation Guide 2 | 3 | ## Simple install 4 | 5 | To install sdm into /usr/local/sdm from the latest release use 6 | 7 | ```sh 8 | curl -L https://raw.githubusercontent.com/gitbls/sdm/master/EZsdmInstaller | bash 9 | ``` 10 | 11 | ## Flexible install 12 | 13 | Alternatively, if you want to install a specific branch or use a directory other than /usr/local/sdm: 14 | ```sh 15 | curl -L https://raw.githubusercontent.com/gitbls/sdm//EZsdmInstaller -o /path/to/EZsdmInstaller 16 | chmod 755 /path/to/EZsdmInstaller 17 | # Inspect the EZsdmInstaller script if desired 18 | sudo /path/to/EZsdmInstaller 19 | ``` 20 | For instance, to install the V8.6 version: 21 | ```sh 22 | curl -L https://raw.githubusercontent.com/gitbls/sdm/V8.6/EZsdmInstaller -o /path/to/EZsdmInstaller 23 | chmod 755 /path/to/EZsdmInstaller 24 | # Inspect the EZsdmInstaller script if desired 25 | sudo /path/to/EZsdmInstaller V8.6 26 | ``` 27 | 28 | ## EZsdmInstaller command line documentation 29 | 30 | Full command syntax: 31 | 32 | ```sh 33 | sudo /path/to/EZsdmInstaller branch hostdir 34 | ``` 35 | Where: 36 | 37 | * `branch` is the name of the release branch install [Default: latest, which is `master`] 38 | * `hostdir` is the full path to where sdm should be installed [Default: /usr/local/sdm] 39 | 40 | Both arguments are optional and will use the above defaults. 41 | 42 | ### Examples 43 | 44 | Install the latest release into the default directory (/usr/local/sdm) 45 | 46 | ```sh 47 | sudo /path/to/EZsdmInstaller 48 | ``` 49 | Install the latest release into a specific directory 50 | ```sh 51 | sudo /path/to/EZsdmInstaller "" /usr/local/zsdm 52 | ``` 53 | 54 | Install a specific branch into the default directory 55 | ```sh 56 | sudo /path/to/EZsdmInstaller V9.1 57 | ``` 58 | Install a specific branch into a specific directory 59 | ```sh 60 | sudo /path/to/EZsdmInstaller V9.1 /usr/local/zsdm 61 | ``` 62 | 63 | **NOTE:** In order to install a version other than the latest release, be sure to use the EZsdmInstaller for that version. To pick up, for instance, the V8.6 EZsdmInstaller, use: 64 | 65 | 66 | curl -L https://raw.githubusercontent.com/gitbls/sdm/V8.6/EZsdmInstaller -o /path/to/EZsdmInstaller 67 | 68 | ## Removing sdm 69 | 70 | ```sh 71 | sudo rm -rf /usr/local/sdm 72 | sudo rm -f /usr/local/bin/sdm 73 | ``` 74 | 75 |
76 |
77 | 78 |
79 | -------------------------------------------------------------------------------- /Docs/Disks-Partitions.md: -------------------------------------------------------------------------------- 1 | # Disks and Partitions 2 | 3 | This is a collection of disk and partition-related information relative to sdm. 4 | 5 | sdm doesn't have any specific issues with various disk drives and formats. 6 | 7 | sdm does need to know about partition numbers, and it can correctly handle the partition names on mmcblk\* and nvme0n\* disks as well as /dev/sd\* 8 | 9 | sdm supports RasPiOS disks with two partitions: a FAT32 bootfs (partition 1) and an ext4 rootfs (partition 2), that is, the standard RasPiOS IMG files up through Bookworm. 10 | 11 | By default sdm `--burn` will do an image copy of the IMG file to the burn disk with `dd`, so the burned disk is MBR format with a FAT32 bootfs and an ext4 rootfs. When the system boots, RasPiOS will expand the rootfs partition to fill the disk. In other words, a standard RasPiOS bootable disk. 12 | 13 | There are a few switches that change the burn function with respect to disks and partitions. 14 | 15 | * `--gpt` — Convert the burned disk to GPT partition format 16 | * `--burn-plugin parted` — Use the `parted` burn plugin to do disk partitioning functions after the burn has completed. See Plugins 17 | * `--convert-root` fmt — Convert the rootfs to a different file system, rather than ext4. See the next section 18 | * `--expand-root` — sdm will expand the rootfs to the whole disk after burning 19 | * `--no-expand-root` — sdm will not expand rootfs and will disable automatic RasPiOS rootfs expansion 20 | 21 | ## rootfs conversion 22 | 23 | The `--convert-root` switch is used to specify a different rootfs file system format. Supported formats: 24 | * `btrfs` 25 | * `ext4` 26 | * `lvm` 27 | 28 | rootfs conversion requires that the files are copied via the file system (using rsync) rather than a block mode copy, which is inherently faster. 29 | 30 | Using `--convert-root lvm` causes `--gpt` to be set. 31 | 32 | ## Example commands 33 | 34 | * `sdm --burn /dev/sdc --convert-root btrfs --expand-root /path/to/2023-12-05-raspios-bookworm-arm64.img` — Burn the IMG to /dev/sdc with a `btrfs` file system for rootfs 35 | * `sdm --burn /dev/sdc --gpt --expand-root /path/to/2023-12-05-raspios-bookworm-arm64.img` — Burn the IMG to /dev/sdc with a GPT partition table 36 | 37 |
38 |
39 | 40 |
41 | -------------------------------------------------------------------------------- /Docs/Example-Burn-Multiple-Hosts-From-Single-IMG.md: -------------------------------------------------------------------------------- 1 | # Example: Burn Multiple Hosts From a Single IMG 2 | 3 | This is a very simple script that doesn't do much. See the sdm script sdm-gburn if your needs for this are more complex. 4 | 5 | ```sh 6 | #!/bin/bash 7 | # 8 | # Burn a bunch of SSDs/SD Cards from the same image 9 | # Each will have a unique hostname 10 | # 11 | function askyn() { 12 | # 13 | # $1: Prompt string 14 | # $2: default answer: "y" or "n" 15 | # 16 | local ans 17 | echo -n "$1 " ; read -n 1 ans 18 | [ "$ans" == "" ] && ans="$2" || echo "" 19 | case "${ans,,}" in 20 | y*) return 0 ;; 21 | *) return 1 ;; 22 | esac 23 | } 24 | 25 | # Change this to be the name of the device you want to burn to 26 | odev="/dev/sdc" 27 | # Change this to be the full path to the IMG you want to burn 28 | img="/path/to/customized.img" 29 | 30 | # Change the host names to suit your needs. You'll need one SSD/SD Card 31 | # for each of the hosts 32 | # 33 | for hn in host1 host2 host3 34 | do 35 | echo "* Insert SSD/SD Card in $odev then press Enter to burn" 36 | if askyn "Burn host '$hn' on $odev? [Y/n]:" "y" 37 | then 38 | sdm --burn $odev --hostname $hn --expand-root $img 39 | fi 40 | done 41 | ``` 42 |
43 |
44 | 45 |
46 | -------------------------------------------------------------------------------- /Docs/Example-Burn-Scripts.md: -------------------------------------------------------------------------------- 1 | # Example: Burn Scripts 2 | 3 | Another way to accomplish these types of customizations is to use Plugins, which are more capable and easier to understand. 4 | 5 | ## b0script 6 | ```sh 7 | #!/bin/bash 8 | # 9 | # Script run after burn, in a --mount mode (running system and burned disk both accessible) 10 | # 11 | function do_b0script() { 12 | logtoboth "* Start b0script for host '$hostname'" 13 | [ "$SDMPT" == "" ] && echo "? SDMPT is undefined in do_b0script ; aborting" && return 14 | # 15 | # Copy to final location if related service already installed (or no service) 16 | # Copy to /etc/sdm/local-assets if service not yet installed (logwatch, apcupsd, chrony, etc) 17 | # 18 | # Logwatch 19 | # 20 | logtoboth "> Copy assets for 'logwatch'" 21 | rsync -a /rpi/etc/logwatch/ $SDMPT/etc/sdm/local-assets/logwatch 22 | # 23 | # apcupsd 24 | # 25 | logtoboth "> Copy assets for 'apcupsd'" 26 | mkdir -p $SDMPT/etc/sdm/local-assets/apcupsd 27 | cp /rpi/pisrv1/etc/apcupsd/apcupsd.conf $SDMPT/etc/sdm/local-assets/apcupsd 28 | # 29 | # chronyd 30 | # 31 | logtoboth "> Copy assets for 'chronyd'" 32 | mkdir -p $SDMPT/etc/sdm/local-assets/chrony 33 | cp /rpi/pisrv1/etc/chrony/chrony.conf $SDMPT/etc/sdm/local-assets/chrony 34 | # 35 | # Other configuration stuff 36 | # 37 | logtoboth "> Copy other configuration assets" 38 | logtoboth "> ...to /etc/sdm/local-assets/config" 39 | mkdir -p $SDMPT/etc/sdm/local-assets/config 40 | [ -f /my/safe-place/$hostname/dhcpcd-append.conf ] && cp /my/safe-place/$hostname/dhcpcd-append.conf $SDMPT/etc/sdm/local-assets/config 41 | [ -f /my/safe-place/$hostname/exports-append.conf ] && cp /my/safe-place/$hostname/exports-append.conf $SDMPT/etc/sdm/local-assets/config 42 | } 43 | ``` 44 | ## b1script 45 | ```sh 46 | #!/bin/bash 47 | # 48 | # Runs in an nspawn so references are direct 49 | # 50 | source /etc/sdm/sdm-readparams 51 | logtoboth "* Start b1script for host '$hostname'" 52 | 53 | # Install pvn-specific packages 54 | 55 | logtoboth "> Install 'logwatch' and place assets" 56 | doapt "install --yes --no-install-recommends logwatch" $showapt 57 | rsync -a /etc/sdm/local-assets/logwatch/ /etc/logwatch 58 | 59 | logtoboth "> Install 'chrony' and place assets" 60 | doapt "install --yes --no-install-recommends chrony" 61 | [ -f /etc/chrony/chrony.conf ] && mv /etc/chrony/chrony.conf /etc/chrony/chrony.conf.orig 62 | cp $SDMPT/etc/sdm/local-assets/chrony/chrony.conf /etc/chrony/chrony.conf 63 | 64 | logtoboth "> Install 'apcupsd' and place assets" 65 | doapt "install --yes --no-install-recommends apcupsd" 66 | [ -f /etc/apcupsd/apcupsd.conf ] && mv /etc/apcupsd/apcupsd.conf /etc/apcupsd/apcupsd.conf.orig 67 | cp $SDMPT/etc/sdm/local-assets/apcupsd/apcupsd.conf /etc/apcupsd/apcupsd.conf 68 | 69 | logtoboth "> b1script Complete" 70 | ``` 71 |
72 |
73 | 74 |
75 | -------------------------------------------------------------------------------- /Docs/Example-Custom-Phase-Script.md: -------------------------------------------------------------------------------- 1 | # Example: Custom Phase Script 2 | 3 | This is my (lightly edited) personal Custom Phase script that I used to use. I am now using a Plugin, which you can see here for comparison and contrast. 4 | ```sh 5 | #!/bin/bash 6 | # My sdm customizations 7 | # 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | # 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # 14 | phase=$1 15 | pfx="$(basename $0)" 16 | if [ "$phase" == "0" ] 17 | loadparams 18 | then 19 | # 20 | # In Phase 0 all references to directories in the image 21 | # must be preceded by $SDMPT. References not preceded 22 | # by $SDMPT refer to the system on which sdm is running. 23 | # Hence easy to copy additional files into the image. 24 | # 25 | # 26 | logtoboth "* $pfx Phase 0" 27 | logfreespace "at start of $pfx Custom Phase 0" 28 | logtoboth "> $pfx Create NFS mount points" 29 | for f in h k l rpi ssd 30 | do 31 | [ ! -d $SDMPT/$f ] && mkdir $SDMPT/$f 32 | done 33 | 34 | echo "#/home/${myuser} 192.168.42.0/24(rw,no_root_squash,no_subtree_check,insecure)" >> $SDMPT/etc/.my-personal-exports 35 | 36 | logtoboth "> $pfx Copy /usr/local/bin config scripts" 37 | for f in \ 38 | wlanset \ 39 | btset \ 40 | xdmset \ 41 | tman 42 | do 43 | cp -f /rpi/local/$f $SDMPT/usr/local/bin 44 | done 45 | 46 | if [ "$myuser" != "" ] 47 | then 48 | # I like to have the same environment on all my systems 49 | # Copy in the files that I require everywhere 50 | logtoboth "> $pfx Copy $myuser login files to $SDMPT/home/$myuser" 51 | [ ! -d $SDMPT/home/$myuser ] && mkdir $SDMPT/home/$myuser 52 | cp -f /home/$myuser/{.bashrc,.colordiffrc,.dircolors,.emacs,.inputrc,.vimrc,.Xmodmap,.Xdefaults,.tmux.conf} $SDMPT/home/$myuser 53 | echo "source /home/$myuser/.bashrc" > $SDMPT/home/$myuser/.bash_profile 54 | chmod 755 $SDMPT/home/$myuser/.bash_profile 55 | [ ! -d $SDMPT/home/$myuser/bin ] && mkdir $SDMPT/home/$myuser/bin 56 | for fd in .icewm \ 57 | .lftp \ 58 | .ncftp 59 | do 60 | [ -d /home/$myuser/$fd ] && cp -a -f /home/$myuser/$fd $SDMPT/home/$myuser/$fd 61 | done 62 | 63 | logtoboth "> $pfx Copy ssh files" 64 | cp -a /rpi/my-ssh-files $SDMPT/home/$myuser/.ssh 65 | chmod 700 $SDMPT/home/$myuser/.ssh 66 | logtoboth "> $pfx Copy login scripts to $SDMPT/root" 67 | [ ! -d $SDMPT/root/orig ] && mkdir $SDMPT/root/orig && mv $SDMPT/root/.bashrc $SDMPT/root/orig 68 | [ -f $SDMPT/root/.bash_profile ] && mv $SDMPT/root/.bash_profile $SDMPT/root/orig 69 | cp /home/$myuser/{.bashrc,.colordiffrc,.dircolors,.emacs,.inputrc,.tmux.conf} $SDMPT/root 70 | echo "source /root/.bashrc" > $SDMPT/root/.bash_profile 71 | chmod 755 $SDMPT/root/.bash_profile 72 | cp -a $SDMPT/home/$myuser/.ssh $SDMPT/root/.ssh 73 | chown -R root.root $SDMPT/root/.ssh 74 | fi 75 | 76 | logtoboth "> $pfx Copy systemd services" 77 | cp $csrc/systemd/*.service $SDMPT/etc/systemd/system 78 | 79 | logtoboth "> $pfx Disable $SDMPT/etc/profile.d/wifi-check.sh and sshpwd.sh" 80 | [ -f $SDMPT/etc/profile.d/wifi-check.sh ] && mv $SDMPT/etc/profile.d/wifi-check.sh $SDMPT/etc/profile.d/sdm.wifi-check.sh 81 | [ -f $SDMPT/etc/profile.d/sshpwd.sh ] && mv $SDMPT/etc/profile.d/sshpwd.sh $SDMPT/etc/profile.d/sdm.sshpwd.sh 82 | 83 | logfreespace "at end of $pfx Custom Phase 0" 84 | logtoboth "* $pfx Phase 0 Completed" 85 | 86 | elif [ "$phase" == "1" ] 87 | then 88 | # 89 | # Phase 1 (in nspawn) 90 | # 91 | # In Phase 1 all references to directories in the image can be direct 92 | # 93 | logtoboth "* $pfx Phase 1" 94 | logfreespace "at start of $pfx Custom Phase 1" 95 | 96 | logtoboth "> $pfx Add group 'mygroup'" 97 | groupadd -g 3700 mygroup 98 | usermod -a -G 4300 bls 99 | 100 | logfreespace "at end of $pfx Custom Phase 1" 101 | logtoboth "* $pfx Custom Phase 1 completed" 102 | else 103 | # 104 | # Post-install edits 105 | # 106 | logtoboth "* $pfx Custom Phase post-install" 107 | logfreespace "at start of $pfx Custom Phase post-install" 108 | 109 | logfreespace "at end of $pfx Custom Phase post-install" 110 | logtoboth "* $pfx Custom Phase post-install Completed" 111 | fi 112 | exit 0 113 | 114 | ``` 115 |
116 |
117 | 118 |
119 | -------------------------------------------------------------------------------- /Docs/First-Boot-Scripts-and-Configurations.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This document collects together everything you need to know about first boot scripts and configuration files 4 | 5 | ## Statically-built boot scripts 6 | 7 | Scripts placed in `/usr/local/sdm/1piboot/0*-*.sh` will be run automatically at system First Boot if `--bootscripts` is specified. These scripts are static in that they are copied from the host system into the IMG during customization, and sdm provides no way to update the file once it's customized into an IMG. It does, however, provide one way to implement your customization functionality. 8 | 9 | Another way to run scripts at First system boot is via executable scripts placed in `/etc/sdm/0piboot/0*-*.sh` These scripts are run regardless of the `--bootscripts` state. These are typically used by Plugins and Custom Phase Scripts to delay running something until First system boot, rather than during customization. See Handling plugin deferred actions for an example. 10 | 11 | 12 | ## Configuration files 13 | 14 | sdm uses a couple of different configuration files to transmit configuration information from customization to the First Boot service. 15 | 16 | ## Statically-built configuration file 17 | 18 | As above, this file is copied from the host system's `/usr/local/sdm/1piboot/1piboot.conf` into the IMG during customization, and processed at First system boot if there are any non-comment, non-blank lines in the file. See 1piboot/1piboot.conf on this GitHub or /usr/local/sdm/1piboot/1piboot.conf on your host system. 19 | 20 | ## Dynamically-built configuration file 21 | 22 | The file `/etc/sdm/auto-1piboot.conf` is populated by sdm as needed during customization and processed at First system boot. 23 | 24 | There are several plugins that implement some of their functionality by writing to auto-1piboot.conf. These include: 25 | 26 | * `graphics` plugin — delayed_boot_behavior 27 | * `raspiconfig` plugin — All settings except `serial`, which is set immediately using `raspi-config` 28 | * `system` plugin — `fstab`, `service-enable` and `service-disable` functions 29 | 30 | In addition, sdm will write the `delayed_boot_behavior` setting to auto-1piboot.conf if the IMG being customized has a known Display Manager (lightdm, xdm, or wdm) or Window Manager (LXDE). sdm will set the boot behavior for the First system boot to `console no login`, and during the First system boot then sets the boot behavior as specified in `delayed_boot_behavior` for subsequent system boots. 31 |
32 |
33 | 34 |
35 | -------------------------------------------------------------------------------- /Docs/First-Boot-Service.md: -------------------------------------------------------------------------------- 1 | # sdm FirstBoot Service 2 | 3 | sdm adds the sdm-firstboot service to each image during Customization. The FirstBoot service implements settings that must be delayed until the target system has actually booted. 4 | 5 | The FirstBoot service looks at these settings and implements them as needed: 6 | 7 | * Runs custom scripts in /etc/sdm/0piboot/0*-*.sh. See First Boot Scripts for details. 8 | * Optionally (if `--bootscripts`) executes custom scripts in /usr/local/sdm/thispi/1piboot/0*-*.sh. sdm Phase 0 copies these files from /usr/local/sdm/1piboot on the running system. 9 | * Sets keyboard layout 10 | * Sets WiFi Country 11 | * Resets the system console boot behaviour (See NOTE below). 12 | 13 | Note that the hostname does not need to be set since you typically set it when you burn the disk with sdm. 14 | 15 | After all First System Boot processing has been done, FirstBoot waits until the system boot process has fully completed. If `--restart` or `--reboot` were specified (recommended!), FirstBoot will then restart the system. 16 | 17 | First Boot Automatic System Restart is useful for a couple reasons: 18 | 19 | * if access to the system requires a configuration setting modified during the First Boot. A restart ensures that all configuration settings are fully enabled. 20 | * You want it to reboot to make it easier to ensure that your configuration and services are as desired 21 | * You want the system to be fully operational so you can get started! 22 | 23 | **NOTE:** If `--restart` is specified on **RasPiOS Full with Desktop** (or **RasPiOS Lite** with any of lightdm, xdm, or wdm), sdm changes the boot_behaviour to **B1** (Text console with no autologin) so that the sdm FirstBoot messages are visible. In this case the boot_behaviour is reset to **B4** (Graphical Desktop with autologin) for all subsequent reboots, unless the command line included `--plugin raspiconfig:"boot_behaviour:xx"` command switch was specified. 24 | 25 |
26 |
27 | 28 |
29 | -------------------------------------------------------------------------------- /Docs/Hint-Using-sdm-on-2022-04-04-and-later-images.md: -------------------------------------------------------------------------------- 1 | # Hint: Using sdm on 2022-04-04 and Later IMGs 2 | 3 | sdm works fine on RasPiOS 2022-04-04 images (and presumably later). Key considerations for using sdm on these images: 4 | * Add `--plugin disables:piwiz` to the sdm command line if you have fully configured the image with sdm using: 5 | * Use the `user` plugin to add, delete, or modify users as appropriate 6 | * Use the `L10n` plugin to configure the system keymap, locale, and timezone 7 | 8 | You can use `--plugin disables:piwiz` on both Lite and Desktop versions and on either the `--customize` command or the `--burn` command . 9 | 10 | On Lite sdm disables the userconfig service. On Desktop versions, sdm also prevents the piwiz desktop app from starting. 11 |
12 |
13 | 14 |
15 | -------------------------------------------------------------------------------- /Docs/Hints-Commands.md: -------------------------------------------------------------------------------- 1 | # sdm Command Hints and Tricks 2 | 3 | I'll expand this with useful commands and what they do, often leaning more toward the intermediate to advanced user. Please feel free to open an issue with additional tips/suggestions! 4 | 5 | * Do the most **minimal sdm command** to quickly test out a new plugin, or some other setting that depends only on the base system 6 | 7 | Test a new plugin doing as little as possible so that plugin test run is quick 8 | ```sh 9 | sudo sdm --customize --nouser --poptions noupdate,noupgrade,noautoremove --plugin myplugin 2023-02-21-raspios-bullseye-arm64.img 10 | ``` 11 | * **Consider using the `--plugin` switch with a plugin name of @file** if you have more than one or two plugins. This enables you to put your plugin details in a text file. This enables you to: 12 | * Rearrange the plugin ordering more easily 13 | * Worry a lot less about a missing double quote or breaking bash syntax when updating (double quotes not required in pluglist file) 14 | * See Plugins for details 15 | * The **sdm history file (/etc/sdm/history) coding** is easily-missed, but provides additional information. Each message line (except for message continuations) starts with an indicator about the line content. 16 | * `*` Start of a new 'section' (e.g., `* Start Phase 1 image customization`) 17 | * `>` Inform you of something that sdm is doing 18 | * `%` Warning you that something isn't quite right, but forging ahead 19 | * `?` An error was identified. sdm will stop operation 20 | 21 |
22 |
23 | 24 |
25 | -------------------------------------------------------------------------------- /Docs/Hints-NetworkManager.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Although NetworkManager (nm) has been around for a while, not many people have used it on RasPiOS because dhcpcd was the default prior to Bookworm. 4 | 5 | RasPiOS Bookworm now uses nm by default. 6 | 7 | This page discusses nm sdm-fu and overall more depth on the `network` plugin. 8 | 9 | ## What happens by default and how do I control? 10 | 11 | On Bullseye and earlier systems, the `network` plugin by default uses dhcpcd for `netman`, as it is on an uncustomized RasPiOS. The `network` plugin accepts a file via the `dhcpcdappend` argument that is appended to /etc/dhcpcd.conf. The `wpa` argument file will be copied to /etc/wpa_supplicant/wpa_supplicant.conf. Alternatively, if the arguments `wifissid`, `wifipassword`, and `wificountry` are all set, wpa_supplicant.conf will be written with this configuration. 12 | 13 | On Bookworm, nm is used by default for network management. You can also select nm on Bullseye with the `netman=nm` argument to the `network` plugin. Likewise, if you want to use dhcpcd on Bookworm, use `netman=dhcpcd`. 14 | 15 | When configuring nm, a WiFi connection can be configured with the arguments `wifissid`, `wifipassword`, and `wificountry`. If all 3 are provided to the network plugin an nm connection file will be created using the `wifissid` as the connection name. If these arguments are not provided, the plugin will get the values from a provided `wpa` argument file. If neither of these are provided, no WiFi connection will be configured unless one is provided with an `nmconn` argument. 16 | 17 | In addition to several arguments there are two nm-specific arguments: `nmconf`, and `nmconn`. Using these provides you with the best nm control in sdm. 18 | 19 | ## Generating `nmconf` and `nmconn` files 20 | 21 | As with other plugins, the best sdm-fu results from building static configuration files, and copying them into the IMG. With this approach the system can be fully configured from the first system boot. 22 | 23 | `nmconf` files are nm configuration files. The `nmconf` argument can provide a comma-separated list of files on the host system. nm expects these files to be named something.conf 24 | 25 | Similarly, `nmconn` files are nm connection keyfiles. These are also text-based. Network manager expects these to be named something.nmconnection. Each WiFi network requires a .nmconnection file. 26 | 27 | One way to generate a .nmconnection is to use nmcli. This technique requires use of the nmcli `--offline` switch, which is supported only in Bookworm and later. This example on Bookworm is one long command line. See a note about WiFi autoconnect here. 28 | ```sh 29 | nmcli --offline c add type wifi con-name your-name ifname wlan0 ssid your-ssid | \ 30 | nmcli --offline c modify wifi-sec.key-mgmt wpa-psk wifi-sec.psk your-password autoconnect false > your-connfile.nmconnection 31 | ``` 32 | The above command would create your-connfile.nmconnection: 33 | ``` 34 | [connection] 35 | id=your-name 36 | uuid=d45eba01-916c-4b77-8f0e-01071f955727 37 | type=wifi 38 | autoconnect=false 39 | interface-name=wlan0 40 | 41 | [wifi] 42 | mode=infrastructure 43 | ssid=your-ssid 44 | 45 | [wifi-security] 46 | key-mgmt=wpa-psk 47 | psk=your-password 48 | 49 | [ipv4] 50 | method=auto 51 | 52 | [ipv6] 53 | addr-gen-mode=default 54 | method=auto 55 | 56 | [proxy] 57 | ``` 58 | 59 | In order to generate a .nmconnection file using Bullseye or earlier, you must use nm running on a booted system. Once that system is configured with nm and available you can: 60 | 61 | nmcli c add type wifi con-name your-name ifname wlan0 ssid your-ssid 62 | nmcli c modify your-name wifi-sec.key-mgmt wpa-psk wifi-sec.psk your-password 63 | 64 | ### Other useful nmcli commands 65 | 66 | Disable IPV6 on a WiFi connection 67 | 68 | nmcli c modify your-name ipv6.method disabled 69 | 70 | Disable IPV6 on the default eth0 connection 71 | 72 | nmcli c modify Wired\ connection\ 1 ipv6.method disabled 73 | 74 | ## One last note on NetworkManager 75 | 76 | Although nm uses wpa_supplicant, it does not specifically use the wpa_supplicant.conf file except as described above (by the `network` plugin). 77 | 78 | Instead, nm communicates WiFi configuration information to wpa_supplicant over dbus. This enables a key feature of nm, the ability to easily switch WiFi connections. 79 | 80 | ## WiFi Autoconnect 81 | 82 | By default, a new WiFi connection is created with `autoconnect true`. Perfect if you only have one WiFi connection. But, when you add a second one, which one is the one to connect by default? 83 | 84 | For secondary, non-autoconnect connections, specify `autconnect false` so that they are actually not autoconnected. 85 | 86 | ## NetworkManager information and documentation 87 | 88 | * `man nm-settings-keyfile` — Connection keyfile 89 | * `man nm-settings` — More Connection settings (Advanced) 90 | * `man NetworkManager.conf` — Overview of Network Manager configuration (Advanced) 91 | * Complete NetworkManager documentation 92 | -------------------------------------------------------------------------------- /Docs/Hotspot.md: -------------------------------------------------------------------------------- 1 | # Hotspot 2 | 3 | The --hotspot switch is used to install a hotspot into the image 4 | 5 | Hotspot configuration is done with the `--hotspot` switch. 6 | 7 | * `--hotspot` *config-file* — Install and Configure a hotspot (Access Point). This is done in accordance with the guides on the Raspberry Pi website: 8 | * Routed Access Point Configuration 9 | * Bridged Access Point Configuration 10 | 11 | When `--hotspot` is used, the hotspot is installed and configured at the end of Phase 1. The system is set to automatically restart at the completion of FirstBoot to help ensure that the hotspot is correctly configured. **Check the logs!** 12 | 13 | The hotspot configuration is specified in *config-file*, which contains a set of directives, one per line. The settings shown here are the defaults: 14 | 15 | ```sh 16 | # Type of hotspot 17 | # local: Clients can only access the hotspot IP itself 18 | # routed: Clients can access the hotspot IP; non-local traffic is routed to the Pi's eth0 network 19 | # bridged: The Client network is bridged onto the Pi's eth0 network 20 | config="local" 21 | # Channel to use 22 | channel="36" 23 | # WiFi mode: "g" for 2.4Ghz, "a" for 5Ghz 24 | # See https://en.wikipedia.org/wiki/List_of_WLAN_channels for legal channels/modes per country 25 | hwmode="a" 26 | # Country: defaults to --wifi-country setting but can be changed here 27 | country="us" 28 | # Network device to use. Default is "wlan0" 29 | dev="wlan0" 30 | # IP address for the hotspot WiFi network device 31 | wlanip="192.168.4.1" 32 | # Range of IP addresses and netmask to use for DHCP server on the hotspot network 33 | dhcprange="192.168.4.2,192.168.4.32,255.255.255.0" 34 | # Lease time for IP addresses leased on the hotspot network 35 | leasetime="24h" 36 | # SSID for the hotspot network 37 | ssid="MyPiNet" 38 | # Passphrase for the hotspot network 39 | passphrase="password" 40 | # Domain name for the hotspot network 41 | domain="wlan.net" 42 | # If enable=true, the hotspot will be enabled at system boot 43 | enable="true" 44 | # If non-null, specifies a file that is concatenated onto /etc/hostapd/hostapd.conf 45 | include="" 46 | ``` 47 |
48 |
49 | 50 |
51 | -------------------------------------------------------------------------------- /Docs/Index.md: -------------------------------------------------------------------------------- 1 |

Index

2 | 3 | apt-cacher-ng 4 |
Batch Burn SSDs/SDs with sdm-gburn 5 |
Burn Scripts 6 |
Captive Portal 7 |
Detailed command and switches description 8 |
Cool and useful things you can do with sdm 9 |
Cool and useful things: Hotspot 10 |
Cool and useful things: User-specific unique SSH key 11 |
Cool and useful things: VPN 12 |
Compatibility 13 |
Credits 14 |
Custom Phase Script 15 |
Description of sdm 16 |
Disk Encryption 17 |
Disks and Partitions 18 |
Example: Burn Multiple Hosts from a Single IMG 19 |
Example: Commands 20 |
Example: Custom Phase Script 21 |
Example: Plugin 22 |
Example: Installing Software Following an Installation Guide 23 |
sdm FirstBoot Service 24 |
First Boot Script and Configuration Information 25 |
fstab 26 |
Hints: sdm Commands 27 |
Hints: Configuring Network Manager 28 |
Hint: Using sdm on 2022-04-04 and Later IMGs 29 |
Hotspot 30 |
Installing and Removing sdm 31 |
Known Issues 32 |
Operating Details 33 |
Passwords 34 |
Performance: How Fast is sdm 35 |
Plugins 36 |
Programming: Plugins and Custom Phase Scripts 37 |
sdm Script Details 38 |
Terminal Colors 39 |
Troubleshooting: Overview 40 |
Upgrading to sdm V9 41 |
Upgrading to sdm V8 42 |
Using sdm with Docker 43 |
Using the lxde plugin 44 |
Using the labwc plugin 45 |
Using sdm on Windows WSL 46 |
Using sdm on an uncustomized system 47 | -------------------------------------------------------------------------------- /Docs/Installing-or-Removing-sdm.md: -------------------------------------------------------------------------------- 1 | # Installing or Removing sdm 2 | 3 | ## Installing sdm 4 | 5 | ```sh 6 | curl -L https://raw.githubusercontent.com/gitbls/sdm/master/EZsdmInstaller | bash 7 | ``` 8 | EZsdmInstaller is a simple script that downloads the sdm files to /usr/local/sdm, and installs packages systemd-container, qemu-user-static, binfmt-support, file, and parted if they are not installed. 9 | 10 | **Or, download the Installer script to examine it before running:** 11 | ```sh 12 | curl -L https://raw.githubusercontent.com/gitbls/sdm/master/EZsdmInstaller -o ./EZsdmInstaller 13 | chmod 755 ./EZsdmInstaller 14 | # Inspect the EZsdmInstaller script if desired 15 | sudo ./EZsdmInstaller 16 | ``` 17 | ## Removing sdm 18 | 19 | ```sh 20 | sudo rm -rf /usr/local/sdm 21 | sudo rm -f /usr/local/bin/sdm 22 | ``` 23 |
24 |
25 | 26 |
27 | -------------------------------------------------------------------------------- /Docs/Known-Issues.md: -------------------------------------------------------------------------------- 1 | # Known Issues 2 | 3 | * sdm must be run as root or with `sudo` 4 | * sdm has been tested on RasPiOS Buster, Bullseye, and Bookworm Desktop and Lite IMG files. Testing is no longer done on Stretch. 5 | * Using sdm on a 32-bit x86 system will fail. 6 |
7 |
8 | 9 |
10 | -------------------------------------------------------------------------------- /Docs/LED-flashing.md: -------------------------------------------------------------------------------- 1 | # LED Flashing 2 | 3 | Using the `--loadlocal usb,flashled` will cause the First Boot process to flash the Green Pi LED with progress/problem indicators. This is very useful if the Pi doesn't have a monitor attached. The flash codes are ("." is a short flash, and "-" is a long flash): 4 | 5 | * `..- ..- ..-` — First Boot is waiting for an unmounted USB device to appear with the file `local-settings.txt` on it. 6 | * `... --- ...` — An error was found in `local-settings.txt`. Errors can include: 7 | * ssid or password are not specified, or are the null string 8 | * An invalid WiFi Country was specified 9 | * An invalid Keymap, Locale, or Timezone was specified 10 | * `-- -- -- --` — WiFi did not connect 11 | * `..... ..... .....` — WiFi connected 12 | * `.-.-.- .-.-.- .-.-.- .-.-.-` — Internet is accessible 13 | * `-.-.-. -.-.-. -.-.-. -.-.-.` — Internet is not accessible 14 | * `..-. ..-. ..-.` — Waiting for a DHCP-assigned IP address 15 |
16 |
17 |
18 | 19 |
20 | -------------------------------------------------------------------------------- /Docs/Operating-Details.md: -------------------------------------------------------------------------------- 1 | # Operating Details 2 | 3 | sdm operates on the SD Card image in distinct phases: 4 | 5 | * **Phase 0:** *Operating in the logical context of your host system, copying files into the RasPiOS IMG file.* sdm takes care of Phase 0 for you. The Phase 0 script `sdm-phase0` performs the Phase 0 copying. It will also optionally call a Custom Phase Script provided by you to perform customized personal steps. In addition, all plugins specified will be called for Phase 0. 6 | 7 | * **Phase 1:** *Operating inside the IMG file container and in the context of that system (via systemd-nspawn or chroot)*. When operating in this context, all changes made only affect the SD Card IMG, not the host system on which sdm is running 8 | 9 | Most, but not all commands can be used in Phase 1. For instance, most `systemctl` commands don't work because systemd is not running in the nspawn'ed image. Importantly, however, `systemctl disable`, `systemctl enable`, `systemctl mask`, and `systemctl unmask` ***do*** work. 10 | 11 | Other functions you might want to do in Phase 1 include: add new users, set or change passwords, install packages, update configuration files, etc. In other words, you can do almost everything you want to configure a system for repeated SD card burns. 12 | 13 | Once sdm has started the nspawn container, it will automatically run `/usr/local/sdm/sdm-phase1` to perform Phase 1 customization. As with Phase 0, your optional Custom Phase Script will be called, as will selected plugins. After Phase 1 completes, sdm will provide a command prompt inside the container unless you specified `--batch`, in which case sdm will exit the container. **NOTE:** When sdm provides a command prompt, either with Phase 1 customization or with `--mount`, the terminal colors are changed (if your terminal supports it) to remind you that the IMG is mounted. See Terminal Colors. 14 | 15 | * **Post-Install:** The post-install phase runs after Phase 1. In the post-install phase, custom phase scripts and selected plugins, which are both called, can count on all packages being installed, so that packages can be configured, etc. 16 | 17 | * **Phase 3:** *Write the SD Card*. Using the `sdm --burn` command, the IMG is written to the new physical SD card using ***dd***, and the new system name is written to the SD card. *This enables a single IMG file to be the source for as many Pi systems as you'd like.* Of course, you can burn the SD Card using a different tool if you'd prefer, although you'll need to set the hostname with another mechanism. 18 | 19 | Plugins can be included on the burn command line, which will cause them to run Phase 0, phase 1, and post-install phase on the SD card. 20 | 21 | * **Phase 4:** *Boot the newly-created SD card on a Pi*. When the new system boots the first time, the systemd service sdm-firstboot.service sets WiFi Country, and any device-specific settings you've enabled (see below), and then disables itself so it doesn't run on subsequent system boots. 22 | 23 | Once the IMG is completed (Phase 0, Phase 1, and post-install), **Phase 3** and **Phase 4** can be repeated as often as needed to create fresh bootable devices for one or more of your Pi fleet, configured exactly as you want them to be. 24 |
25 | ## Ordered list of sdm steps 26 | * Initialization — Copy sdm into the IMG 27 | * Phase 0 — Run sdm Phase 0 steps on the IMG 28 | * Plugins Phase 0 — Run Phase 0 for all enabled plugins in command-line order 29 | * Phase 1 — Run sdm Phase1 steps on the IMG 30 | * Plugins Phase 1 — Run Phase 1 for all enabled plugins in command-line order 31 | * Install apps — Install apps specified by `--apps` 32 | * Network configuration — Configure network as specified by `--plugin network` 33 | * apt upgrade — Upgrade all installed packages with available updates 34 | * apt autoremove — Auto remove any unnecessary packages 35 | * Plugins post-install — Run Phase post-install for all enabled plugins in command-line order 36 | 37 | ## sdm customization and /etc/resolv.conf 38 | 39 | In order to use the network during customization for updates, upgrades, etc, the file /etc/resolv.conf must be set up. `systemd-nspawn`, which is used by sdm for Phase 1 and post-install, copies the /etc/resolv.conf from the host into the IMG being customized. 40 | 41 | If you need to use `--chroot` for any reason, sdm will add a default /etc/resolv.conf. 42 | 43 |
44 | 45 |
46 | -------------------------------------------------------------------------------- /Docs/Passwords.md: -------------------------------------------------------------------------------- 1 | # Passwords 2 | 3 | Starting with sdm V9.0, user account passwords are set using the `user` plugin. 4 | 5 | See the user plugin documentation for full details. 6 | 7 | If you forget any of the passwords, you can use sdm to reset a password by using `sdm --explore` into the IMG or SD Card, and then changing the password for the desired account(s) with the `passwd` command. 8 | 9 | You can use the `--redact` switch with `--customize` or `--burn`. sdm will replace all occurrences of the matching strings in /etc/sdm/history with the string **REDACTED**. The redaction is a simple string replacement. Note that if `--redact` is used, the argument list will not be printed in the log or on the console when ANY plugins are run. However, if the `user` plugin argument `redact` is used without `--redact`, argument lists with passwords WILL be printed on the console. 10 | 11 | If no printed passwords (in the log or on the console) is important to you, use `--redact`. 12 |
13 |
14 | 15 |
16 | -------------------------------------------------------------------------------- /Docs/Performance-How-Fast-is-sdm.md: -------------------------------------------------------------------------------- 1 | # Performance: How Fast is sdm 2 | 3 | I timed how long it took to build my personal IMG from a "stock" RasPiOS Bullseye IMG. Times rounded to the closest minute. The Lite customization takes longer because I install XWindows, xdm, icewm, xterm, chromium-browser, firefox, and all the packages that those apps pull in, as opposed to the "with Desktop" system which doesn't need to install XWindows, the display manager, and window manager, since they're already installed. 4 | 5 | * Running RasPiOS on an SD Card and customizing an IMG located locally on that SD card 6 | * RasPiOS with Desktop: 7m 7 | * RasPiOS Lite: 9m 8 | * Running RasPiOS on an SSD and customizing an IMG located on an NFS-mounted SSD 9 | * RasPiOS with Desktop: 5m 10 | * RasPiOS Lite: 6m 11 | * Running RasPiOS on an SSD and customizing an IMG located locally on that SSD 12 | * RasPiOS with Desktop: 4m 13 | * RasPiOS Lite: 5m 14 | 15 | Burning RasPiOS Lite with --expand-root to an SSD and booting it: 16 | * 00:00:00 Start burn to SSD 17 | * 00:01:15 Burn complete 18 | * 00:01:45 Power on Pi. System boots and among other things runs the sdm FirstBoot script 19 | * 00:02:52 System automatically reboots via sdm FirstBoot script 20 | * 00:03:26 System at command prompt ready to login 21 | 22 | You read that correctly...In just a few minutes you can go from a "stock" RasPiOS IMG to having your own customized system booted and ready to go. 23 | 24 | And, if you keep your customized image around, you can have another freshly-made system booted and ready to go in less than 5 minutes. With all your customizations already in place. How sweet is that? 25 |
26 |
27 | 28 |
29 | -------------------------------------------------------------------------------- /Docs/Script-Details.md: -------------------------------------------------------------------------------- 1 | # sdm Script Details 2 | 3 | sdm consists of a primary script sdm and several supporting scripts: 4 | 5 | * **sdm** — The sdm main program. Collects the command line details and starts the customization process. Also directly handles some commands such as `--burn`, `--shrink`, and `--ppart`. 6 | 7 | * **sdm-phase0** — Script run by sdm before nspawn-ing into the IMG file. sdm-phase0 has access to the running Pi system as well as the file system within the IMG file. sdm-phase0 performs several steps: 8 | 9 | * Copy files from the running system into the IMG, as specified by command line switches, for use in Phase 1. 10 | 11 | * Calls selected Plugins and/or Custom Phase Scripts for Phase 0 if specified. 12 | 13 | You can extend what's done in Phase 0 by using a Plugin (preferred) or Custom Phase Script. 14 | 15 | * **sdm-phase1** — If `--aptcache` was specified, the IMG is enabled as an apt-cacher-ng client. See apt Cacher NG for details on apt-cacher-ng. 16 | 17 | * App installation is accomplished using the Bootset and 1piboot for details. This directory will also be installed onto the SD Card in /usr/local/sdm/1piboot. 26 | If enabled, the custom scripts in 1piboot/0*-*.sh are run when the system first boots, and can perform system tuning improvements. The custom scripts are enabled by the switch `--bootscripts` on either the command line that builds the IMG, or on the `sdm --burn` command when burning a new SD card. The scripts can do anything you want, of course, although having several small focused scripts is probably preferable for your sanity over the long term. 27 | 28 | * **sdm-cparse** — Helper script that defines some sdm-internal bash functions. 29 | 30 | * **sdm-cportal** — Implements the Captive Portal for `--loadlocal wifi` 31 | 32 | * **sdm-cmdsubs** — Implements functions used by sdm to process `--burn`, `--shrink`, and `--ppart` commands 33 | 34 | * **sdm-logmsg** — Helper script for the Captive Portal. 35 | 36 | * **sdm-customphase** — Custom Phase Script skeleton. Use this as a starting point to build your Custom Phase Script 37 | 38 | * **sdm-readparams** — Helper script that reads the sdm configuration file creating bash variables, and sources sdm-cparse to make its functions available. sdm-readparams is copied to /etc/sdm in an IMG being customized, and is called as needed by sdm, Plugins, and Custom Phase Scripts. 39 | 40 | * **sdm-apt-cacher** — Configures and installs apt-cacher-ng. This is optional, but highly recommended, especially with slower internet connections. sdm will use this with the `--aptcache` command switch. apt Cacher NG for details. 41 | 42 | * **plugins/sdm-plugin-template** — Plugin skeleton. Use this as a starting point to build your Plugin. 43 | 44 | * **plugins/*all other files*** — Plugins that can be used with the `--plugin` switch. 45 | 46 |
47 |
48 | 49 |
50 | -------------------------------------------------------------------------------- /Docs/Terminal-Colors.md: -------------------------------------------------------------------------------- 1 | # Terminal Colors 2 | 3 | If your terminal supports it (xterm and lxterminal definitely work), sdm changes the terminal colors when providing a command prompt in Phase 1, or when using the `--mount` command, to remind you that things are not quite "normal". 4 | 5 | The colors used by --explore are controlled by the `--ecolors` command switch, which takes an argument specified as 3 colors. The default is `--ecolors blue:gray:red` which sets the foreground (text) blue, the background gray, and the cursor red. 6 | 7 | The colors for the `--mount` command are controlled by the `--mcolors` switch; the default is `--mcolors black:LightSalmon1:blue`. 8 | 9 | If your terminal emulator doesn't do a good job of setting and/or restoring the terminal colors as described above (for example `konsole` seems to have a problem restoring the terminal colors), you can use the `--ecolors 0` and `--mcolors 0` switches to disable sdm's terminal coloring. 10 | 11 |
12 |
13 | 14 |
15 | -------------------------------------------------------------------------------- /Docs/Troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | sdm places important files in the IMG in `/etc/sdm` such as logs and information to control its operation. 4 | 5 | * *apt.log* contains all the apt command output (package installs) done during the SD Card creation 6 | 7 | * *cparams* contains the parameters with which sdm was initially run on the image 8 | 9 | * *history* is the log written by sdm 10 | 11 | * *1piboot.conf* is the configuration file used when the IMG was customized 12 | 13 | * *auto-1piboot* is used by sdm to implement settings that must be delayed until the FirstBoot service runs. See First Boot Service 14 | 15 | * *custom.ized* tells sdm that the image has been customized. If this exists, sdm will not rerun Phase 0 unless you answer Y to the query at the start of customization. You can avoid the prompt by including the `--redo-customize` switch on the command line. 16 | 17 | The most important files to look at when troubleshooting are 18 | * `history` — Provides a detailed look at all the steps sdm has done 19 | * `apt.log` — Displays errors that occur during apt operations 20 | 21 | ## Cleaning up dangling mounts and loop devices 22 | 23 | sdm tries very hard to clean up after itself. If sdm runs to completion or is able to complete its cleanup handling, there will be no dangling mounts or dangling loop devices. 24 | 25 | But, sometimes *stuff happens* and sdm is prevented from cleaning up. 26 | 27 | ### Dangling Mounts 28 | 29 | If something is not working correctly, make sure that there are no dangling mounts in the running RasPiOS system. You can end up with a dangling mount if sdm terminates abnormally, either with an error (please report!) or via an operator-induced termination. If sdm is not running, you should see no "/mnt/sdm" mounts (identified with `sudo df'). 30 | 31 | You can unmount them by manually using `sudo umount -v /mnt/sdm/{boot,}`. This will umount /mnt/sdm/boot and then /mnt/sdm. You'll need to delete the dangling loop device also. 32 | 33 | ### Dangling Loop devices 34 | 35 | A couple of quick notes on loop devices, which are used to mount the IMG file into the running system. 36 | 37 | * `losetup -a` lists all in-use loop devices 38 | 39 | * `losetup -d /dev/loopX` deletes the loop device /dev/loopX (e.g., /dev/loop0). You may need to do this to finish cleaning up from dangling mounts (which you do first, before deleting the loop device). 40 | 41 | * If your system doesn't have enough loop devices, you can increase the number by adding max_loop=n on end of /boot/cmdline.txt and reboot. 42 |
43 |
44 | 45 |
46 | -------------------------------------------------------------------------------- /Docs/Using-Docker.md: -------------------------------------------------------------------------------- 1 | # Using Docker 2 | 3 | sdm does not currently work with Docker. 4 | 5 | If you are interested in seeing this work, AND are fairly Docker-enlightened, please consider providing me with a "sdm on Docker" cookbook that an unenlightened Docker user like me can follow. 6 | 7 | Once such a guide is provided and I am able to verify that it works, I'll include it in the sdm Documentation. 8 | 9 |
10 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /Docs/Using-LXDE-Config.md: -------------------------------------------------------------------------------- 1 | # Using the `lxde` plugin to customize LXDE 2 | 3 | The `lxde` plugin enables you to load specified app LXDE configuration files into the image in the /home/*user*/.config directory tree. 4 | 5 | The `lxde` plugin accepts the argument `lxde-config` which takes a comma-separated argument. The complete argument specification is: 6 | ```sh 7 | --plugin lxde:"lxde-config pcmanfm:/path/to/pcmanfm.conf,libfm:/path/to/libfm.conf,lxterminal:/path/to/lxterminal.conf" 8 | ``` 9 | You only need to specify the config files you want to provide. If you are customizing pcmanfm, you'll need to specify config files for both pcmanfm AND libfm (I have no idea why pcmanfm uses two config files!) 10 | 11 | Here's how to establish your custom configuration files: 12 | 13 | * Boot a RasPiOS Desktop system with LXDE 14 | * Customize lxterminal and/or pcmanfm preferences in the apps as desired 15 | * Copy the configuration files from your Pi to a shared directory, so that they are available on the Pi that you'll be using for sdm. They don't really need to be in a *shared directory* per se, just a directory available to sdm. The config files can be found at 16 | * **libfm:** /home/*user*/.config/libfm.conf 17 | * **pcmanfm:** /home/*user*/.config/pcmanfm/LXDE-pi/pcmanfm.conf 18 | * **lxterminal:** /home/*user*/.config/lxterminal/lxterminal.conf 19 | * Add the `lxde` plugin with the `lxde-config` argument and values to your sdm command line 20 | * The specified files will be copied into the IMG during Phase 0, when both the host and IMG are acessible 21 | * The files will be moved to the correct directory locations in /home/*user*/.config during Phase 1 22 | * When you boot your newly-created customized image, your settings will be in place 23 | 24 | If the target IMG does not have LXDE installed no changes will be made, although the files will be copied to /etc/sdm/assets/lxde. 25 |
26 |
27 | 28 |
29 | -------------------------------------------------------------------------------- /Docs/Using-Labwc-Config.md: -------------------------------------------------------------------------------- 1 | # Using the `labwc` plugin to customize Labwc 2 | 3 | The `labwc` plugin enables you to load specified labwc configuration files into the image in the /home/*user*/.config directory tree. 4 | 5 | The `labwc` plugin accepts several arguments. The easiest way to configure labwc is to use the argument `all-config` which takes a directory path. The complete argument specification is: 6 | ```sh 7 | --plugin labwc:"all-config=/path/to/labwc-config-dir" 8 | ``` 9 | Here's how to establish your custom configuration files: 10 | 11 | * Boot a RasPiOS Desktop system with labwc 12 | * Customize lxterminal and/or pcmanfm preferences in the apps as desired 13 | * Customize labwc settings using the GUI configuration mechanisms 14 | * Save the customized configuration using the script `sdm-collect-labwc-config` 15 | * Copy the directory of saved configurations as needed for use in sdm customizations 16 | * Add the `labwc` plugin with the `all-config` argument and directory with the collected config files to your sdm command line 17 | * The specified files will be copied into the IMG during Phase 0, when both the host and IMG are acessible 18 | * The files will be moved to the correct directory locations in /home/*user*/.config during Phase 1 19 | * When you boot your newly-created customized image, your settings will be in place 20 | 21 | If the target IMG does not have Labwc installed no changes will be made, although the files will be copied to /etc/sdm/assets/labwc 22 | 23 | NOTE: the `labwc` plugin takes other arguments as well. See labwc plugin documentation for complete details. 24 |
25 |
26 | 27 |
28 | -------------------------------------------------------------------------------- /Docs/Using-sdm-Plugins-on-uncustomized-system.md: -------------------------------------------------------------------------------- 1 | # Using sdm plugins on a system created without sdm 2 | 3 | So you have a system that you configured up without using sdm, and now you want to use one of the sdm Plugins because they're so cool? 4 | 5 | It's pretty simple. Here's how: 6 | 7 | ``` 8 | curl -L https://raw.githubusercontent.com/gitbls/sdm/master/EZsdmInstaller | bash 9 | ``` 10 | 11 | EZsdmInstaller will: 12 | 13 | * Download and install sdm into `/usr/local/sdm` 14 | * Create and populate the directory tree `/etc/sdm` 15 | * Create a link from `/usr/local/bin/sdm` to `/usr/local/sdm/sdm` 16 | 17 | That's it! Then, to run a plugin (in this case, the `hotspot` plugin) on your running system: 18 | ``` 19 | sudo sdm --runonly plugins --oklive --plugin hotspot 20 | ``` 21 | 22 | This should work for all plugins except for `explore`, `extractfs`, and `parted`. 23 | 24 | Of course you can also use sdm to customize IMGs and burn them to SSDs/SD Cards. 25 | 26 |
27 |
28 | 29 |
30 | -------------------------------------------------------------------------------- /Docs/Using-sdm-on-Windows-WSL.md: -------------------------------------------------------------------------------- 1 | # Using sdm on Windows WSL 2 | 3 | sdm can run on Windows WSL2 distros. sdm can be used for customize, explore, mount, and burnfile. At the current time, however, it cannot be used to burn an SD Card due to constraints in WSL. 4 | 5 | sdm automatically detects that it's running on a WSL distro and enables sdm to work seamlessly for supported operations. 6 | 7 | The restriction around burning SSDs/SD cards has to do with WSL being unable to properly address USB storage. If and when this restriction is lifted, you'll be able to burn SSDs/SD Cards on WSL, just like you can on other supported platforms. 8 | 9 | You can take advantage of sdm's burn-time customizations by using `--burnfile` to burn to a .IMG file, and then using another tool such as Win32 Disk Imager, Cygwin dd, etc. to burn the IMG to the target SSD/SD Card. 10 | 11 | For information on installing WSL and a Distro on Windows see Install WSL. You can see the available distros with this command (either Cmd prompt or Powershell): `wsl --list --online`. I recommend using Debian, since it's the most like RasPiOS, it's well-supported, and it's stable. 12 |
13 |
14 | 15 |
16 | -------------------------------------------------------------------------------- /Docs/apt-Cacher-NG.md: -------------------------------------------------------------------------------- 1 | # Local apt caching server 2 | 3 | apt-cacher-ng is a great RasPiOS package, and practically essential if you have more than a couple of Pi systems. The savings in download MB and installation wait time is really quite impressive. 4 | 5 | For example, a quick demonstration at 9:44 in the video https://www.youtube.com/watch?v=CpntmXK2wpA shows a 175MB apt update reduced from *40 seconds* with 175MB of internet download to ***5 seconds*** of LAN-only (no internet) download. Per Pi. The more Pis you have and the more you rebuild those Pi systems, the more you save. 6 | 7 | apt-cacher-ng requires a system running the apt-cacher server. For your sanity and the best and most reliable results, run this on a "production", always available Pi. 8 | 9 | You can use `--plugin apt-cacher-ng` when burning the SD card for the system you've targeted to be the server. 10 | 11 | Alternatively, if the system is already running, copy sdm-apt-cacher to the server and execute the command `sudo /path/to/sdm-apt-cacher server`. This will install apt-cacher-ng on the server and configure it for use. If the server firewall blocks port 3142 you'll need to add a rule to allow it. 12 | 13 | Once you have the apt-cacher server configured you can use the `--aptcache` *IPaddr* sdm command switch to configure the IMG system for all your other systems to use the APT cacher. 14 | 15 | If you have other existing, running Pis that you want to convert to using your apt-cacher server, copy sdm-apt-cacher to each one and execute the command `sudo /path/to/sdm-apt-cacher client`. 16 |
17 |
18 | 19 |
20 | -------------------------------------------------------------------------------- /Docs/fstab.md: -------------------------------------------------------------------------------- 1 | # fstab 2 | 3 | sdm does not touch the lines in /etc/fstab created by RasPiOS except to modify the PARTUUID when the IMG is burned. 4 | 5 | You may want to append one or more lines to /etc/fstab to set up other mounts, such as SMB, NFS, etc, and smb provides a couple of different ways to handle importing and appending your custom /etc/fstab into your image. 6 | 7 | One way to do this is via a Custom Phase Script. In your Custom Phase Script, your Phase 0 code copies the fstab extension file into the IMG somewhere. Then, your Phase 1 code appends the copied fstab extension to the etc/fstab. 8 | 9 | One drawback with this approach is that your fstab additions will be processed during the system FirstBoot. Network timeouts, etc could be an issue. This can be solved by using a Custom bootscript to append your custom fstab file to /etc/fstab. 10 | 11 | That's exactly what the `fstab` argument to the `system` plugin does for you. 12 | 13 | The `system` plugin copies the file(s) you provide to /etc/sdm/assets in the IMG, and then processes that during the system FirstBoot, by appending it to /etc/fstab. 14 | 15 | **NOTE:** No matter which mechanism you use, you'll need to create the mount point directories in the image during Phase 1 using a personal Plugin. See Example Custom Phase Script where custom mount points are created. 16 |
17 |
18 | 19 |
20 | -------------------------------------------------------------------------------- /Docs/raspiconfig-and-1piboot.md: -------------------------------------------------------------------------------- 1 | # raspiconfig and 1piboot 2 | ## raspi-config control using 1piboot.conf and the `raspiconfig` plugin 3 | 4 | 1piboot/1piboot.conf is a configuration file that describes RasPiOS-related configuration settings to be made in your image. Configuration settings are made when the system first boots. All of these settings use raspi-config to make the actual changes to the system. sdm does not syntax check the settings. 5 | 6 | The settings in 1piboot.conf can be controlled by editing the config file, or via the `raspiconfig` plugin. For instance, you can set `serial=0` in 1piboot.conf or you can use the `--plugin raspiconfig:"serial=0"` command switch. In addition, you can use the `raspiconfig` plugin when you customize the image and override the setting when you `--burn` the SD Card or `--burnfile` a new IMG file. To set multiple values, separate them with a comma: `--plugin raspiconfig:"serial=0,boot_behaviour=B4,camera=0"` 7 | 8 | ## First Boot configuration settings 9 | 10 | The following can only be set in the context of a running system, so are set during the first boot of the operating system. Details on each of these settings are available in the `sudo raspi-config` command. Unless otherwise specified, you enable the setting by uncommenting the corresponding line in 1piboot.conf and setting it to 0 (enabled) or other value as noted (e.g., *audio*, *pi4video*, *boot_behaviour*, and *boot_order*). 11 | 12 | * **boot_splash** — Enable a splash screen at boot time 13 | * **boot_wait** — Wait for a network connection to be established 14 | * **camera** — Enable the camera 15 | * **i2c** — Enable the ARM I2C interface 16 | * **net_names** — Enable predictable device names 17 | * **onewire** — Enable the one-wire interface 18 | * **rgpio** — Enable the network-accessible GPIO server 19 | * **spi** — Enable the SPI interface 20 | * **blanking** — Enable screen blanking 21 | * **overscan** — Enable compensation for displays with overscan. 22 | * **pixdub** — Enable pixel doubling 23 | * **powerled** — **0**:Enable disk activity flashing on Power LED, **1**:Power LED always on (Pi Zero and Pi400 only) 24 | * **audio** — Set the audio setting. Valid settings are: **0**:Auto, **1**:Force 3.5mm jack, **2**:Force HDMI 25 | * **pi4video** — Set the Pi4 video mode. Valid settings are: **V1**:4Kp60, **V2**:Analog TV out, **V3**:Disable both 4Kp60 and Analog 26 | * **boot_behaviour** — Set the boot behavior. Valid settings are: **B1**:Text console no autologin, **B2**:Text console with autologin, **B3**:Graphical Desktop no autologin, and **B4**:Graphical Desktop with autologin. 27 | 28 | **NOTE:** If `--plugin user` was specified with the `setpassword` or `adduser` arugment, autologin will be set for the first such user. 29 | 30 | * **boot_order** — Set the boot order. Valid settings are: **B1**:Boot from SD Card if available else boot from USB, **B2**:Boot from USB USB if available else boot from SD Card, **B3**: Network boot if SD Card boot fails. See the "Boot Order" section below. 31 | * **overclock** — Enable overclocking. Valid settings are: **None**, **Modest**, **Medium**, **High**, **Turbo**. This setting is for Pi 1 and 2 only and will silently fail on all other Pi models. 32 | 33 | **NOTE:** Not all of the above settings have been thoroughy tested and verified. They simply call `raspi-config`, so *should just work*. If you run into a problem, please open an issue on this github. 34 | 35 | ## Boot Order 36 | 37 | The *boot_order* configuration setting is different than other settings, in that in modifies the Raspberry Pi eeprom so that boot from USB disk or boot from Network are enabled. If your Pi already has a current system on it, you can use the command `sudo raspi-config do_boot_order XX nonint` to set the boot_order to B1 (Boot from SD Card if available else USB device), B2 (Boot from USB if available else SD Card) or B3 (Boot from Network if SD Card boot fails). 38 | 39 | If the target system doesn't have a current system on it, you can update the eeprom with sdm by setting up a separate image that is enabled with boot_order, and has all updates installed. Burn that image to an SD card and boot up the target Pi hardware. The system will use raspi-config to change the boot_order setting, and the restart again. 40 | 41 | At that point, you can remove the SD card and move ahead with setting up your SSD or Network boot as desired. 42 | 43 | ## raspiconfig vs bootconfig 44 | 45 | The `raspiconfig` plugin is used to configure settings that are managed using the RasPiOS command `raspi-config`. 46 | 47 | The `bootconfig` plugin is used to configure the contents of /boot/config.txt 48 |
49 |
50 | 51 |
52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 gitbls 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /QuickStart.md: -------------------------------------------------------------------------------- 1 | # sdm Quick Start 2 | 3 | ## Customize a RasPiOS IMG file and burn it to an SSD or SD Card in three commands. 4 | 5 | It's assumed that there is a freshly downloaded copy of a RasPiOS IMG file (e.g., 2023-05-03-raspios-bullseye-armhf.img or 2023-05-03-raspios-bullseye-armhf-lite.img) in the current directory, and that there is an SD Card in /dev/sde. 6 | 7 | ### Install sdm and required system software 8 | 9 | `curl -L https://raw.githubusercontent.com/gitbls/sdm/master/EZsdmInstaller | bash` 10 | 11 | This command will: 12 | * Download sdm from GitHub to /usr/local/sdm 13 | * Create a link for /usr/local/sdm/sdm in /usr/local/bin/sdm for ease of use 14 | * Install required system software using apt: systemd-container qemu-user-static binfmt-support file parted 15 | 16 | ### Customize the image 17 | 18 | `sudo sdm --customize 2023-05-03-raspios-bullseye-armhf.img --plugin user:"adduser=myuser|prompt" --plugin disables:piwiz --plugin L10n:host --plugin network:"wpa=/path/to/wpa_supplicant.conf" --restart` 19 | 20 | sdm will make the following customizations to your IMG file: 21 | * Copy your Localization settings (Keymap, Locale, Timezone, and WiFi Country) from the system on which it's running 22 | * Copy the wpa_supplicant.conf you specified into the IMG file at /etc/wpa_supplicant/wpa_supplicant.conf 23 | * Configure the system in the IMG file to have SSH enabled 24 | * Prompt for a new password for user `myuser` (the user 'pi' will remain on the system without a password) 25 | * Perform an `apt update` and `apt upgrade` 26 | 27 | After your first run with sdm it's best practice to look at the console output in detail. One common error is running out of disk space in the IMG file. sdm will flag that in the console output. You can extend it one time by using `sudo sdm --extend 2023-05-03-raspios-bullseye-armhf.img --xmb 2048` and then redoing the customization command. 28 | 29 | ### Burn the image onto the SSD/SD Card 30 | 31 | `sudo sdm --burn /dev/sde --hostname mypi1 --expand-root --regen-ssh-host-keys 2023-05-03-raspios-bullseye-armhf.img` 32 | 33 | *OR* you can use your favorite SD burning tool such as Raspberry Pi Imager or Win32DiskImager. If you use a tool besides sdm you'll need to set the hostname with raspi-config after the system has booted. 34 | 35 | ### Boot the newly-created SSD/SD Card 36 | 37 | Load the SD card into a Pi and power it up. The system will come up (almost) as it always does: 38 | * RasPiOS will NOT: 39 | * Resize the root file system and immediately reboot because that's already been done by sdm 40 | * Ask for keyboard configuration because that's already been done 41 | * Ask for anything about users, because that's already benn done 42 | * Toward the end of the first boot process an sdm systemd service (sdm-firstboot) runs once and takes care of several config settings that must be done on the running system 43 | * When the system first boot is fully complete the system automatically restarts again because of the `--restart` command switch 44 | 45 | Then, when the system comes back up your Pi is all happy, ready to go, and configured with: 46 | * The latest RasPiOS updates installed for installed packages 47 | * User `myuser` is configured and ready for you to login 48 | * Hostname set to mypi1, or whatever you choose for the hostname 49 | * Keymap, Locale, and Timezone configured the same as the system on which you are running sdm. No need to spend time remembering how to do this in raspi-config! 50 | * Wifi configured and operational 51 | * SSH enabled 52 | 53 | If you want to build a disk for another Pi, you only need redo the `sdm --burn` command, specifying the new host. 54 | 55 | ### See sdm in action! 56 | 57 | Watch a short video discussing sdm with video segments showing it in action [here](https://youtu.be/CpntmXK2wpA) 58 | -------------------------------------------------------------------------------- /extras/rpnc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Install sdm on an already-running system and configure it so that sdm plugins can be run 4 | # 5 | 6 | function askyn() { 7 | local ans 8 | echo -n "$1" '[y/n]? ' ; read $2 ans Install sdm into /usr/local/sdm" 35 | curl -L https://raw.githubusercontent.com/gitbls/sdm/master/EZsdmInstaller | bash 36 | echo "> Create /etc/sdm/cparams" 37 | myuser=$(whoami) 38 | hostname=$(hostname) 39 | echo "> sdm primary user will be '$myuser'; sudo edit /etc/sdm/cparams to change this" 40 | (cat </etc/sdm/cparams" 100 | 101 | $sudo touch /etc/sdm/history 102 | 103 | cat <|$assets/my.plugins" 30 | 31 | (cat <<'EOF' 32 | 33 | # Delete user pi if it exists 34 | user:deluser=pi 35 | 36 | # Add a new user ** change 'myuser' and 'mypassword' ** 37 | user:adduser=myuser|password=mypassword 38 | 39 | # Install btwifiset (Control Pi's WiFi from your phone) 40 | # https://github.com/gitbls/sdm/blob/master/Docs/Plugins.md#btwifiset 41 | btwifiset:country=US|timeout=30 42 | 43 | # Install apps 44 | # https://github.com/gitbls/sdm/blob/master/Docs/Plugins.md#apps 45 | apps:name=mybrowsers|apps=firefox,chromium-browser 46 | apps:name=mytools|apps=keychain,lsof,iperf3,dnsutils 47 | 48 | # Configure network ** change 'myssid' and 'mywifipassword' ** 49 | # https://github.com/gitbls/sdm/blob/master/Docs/Plugins.md#network 50 | network:ifname=wlan0|wifissid=myssid|wifipassword=mywifipassword|wificountry=US 51 | 52 | # This configuration eliminates the need for piwiz so disable it 53 | disables:piwiz 54 | 55 | # Uncomment to enable trim on all disks 56 | # https://github.com/gitbls/sdm/blob/master/Docs/Plugins.md#trim-enable 57 | #trim-enable 58 | 59 | # Configure localization settings to the same as this system 60 | # https://github.com/gitbls/sdm/blob/master/Docs/Plugins.md#l10n 61 | L10n:host 62 | EOF 63 | ) | bash -c "cat >>$assets/my.plugins" 64 | 65 | $sudo sdm --customize --plugin @$assets/my.plugins --extend --xmb 2048 --restart --regen-ssh-host-keys $img 66 | -------------------------------------------------------------------------------- /plugins/L10n: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: l10n 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | function readl10n() { 13 | # 14 | # Get the L10N config from the running system 15 | # Sets: locale, timezone, and keymap 16 | # 17 | local tz=$(realpath /etc/localtime) 18 | # Keyboard 19 | if [ -f /etc/default/keyboard ] 20 | then 21 | source /etc/default/keyboard 22 | else 23 | logtoboth "% /etc/default/keyboard missing; Keyboard set to 'us'" 24 | XKBLAYOUT="us" 25 | fi 26 | keymap="$XKBLAYOUT" 27 | # Locale 28 | source /etc/default/locale 29 | locale="$LANG" 30 | # Timezone 31 | timezone=${tz##/usr/share/zoneinfo/} 32 | # WiFi Country 33 | [ -f /etc/wpa_supplicant/wpa_supplicant.conf ] && IFS="=" read a lwificountry <<<$(grep 'country=' /etc/wpa_supplicant/wpa_supplicant.conf) 34 | [ "$lwificountry" == "" -a -f /etc/wpa_supplicant/wpa_supplicant-wlan0.conf ] && IFS="=" read a lwificountry <<<$(grep 'country=' /etc/wpa_supplicant/wpa_supplicant-wlan0.conf) 35 | } 36 | 37 | # $1 is the phase: "0", "1", or "post-install" 38 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 39 | # 40 | # Main code for the Plugin 41 | # 42 | phase=$1 43 | pfx="$(basename $0)" #For messages 44 | args="$2" 45 | loadparams 46 | vldargs="|keymap|locale|timezone|wificountry|host|" 47 | rqdargs="" 48 | assetdir="$SDMPT/etc/sdm/locale" 49 | if [ "$phase" == "0" ] 50 | then 51 | # 52 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 53 | # 54 | logtoboth "* Plugin $pfx: Start Phase 0" 55 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 56 | # 57 | # Print the keys found (example usage). plugin_getargs returns the list of found keys in $foundkeys 58 | # 59 | plugin_printkeys 60 | mkdir -p $assetdir 61 | if [ -v host ] 62 | then 63 | logtoboth "> Plugin $pfx: Read L10n configuration from host system" 64 | keymap="" ; locale="" ; timezone="" ; lwificountry="" 65 | readl10n 66 | [ "$wificountry" == "" ] && wificountry=$lwificountry 67 | logtoboth "> Plugin $pfx: Load Localization (L10N) settings from running system" 68 | logtoboth "> Plugin $pfx: Keymap: $keymap" 69 | logtoboth "> Plugin $pfx: Locale: $locale" 70 | logtoboth "> Plugin $pfx: Timezone: $timezone" 71 | logtoboth "> Plugin $pfx: WiFi Country: $wificountry" 72 | fi 73 | logtoboth "> Plugin $pfx: Save L10n configuration" 74 | for kn in keymap locale timezone wificountry 75 | do 76 | if [ -v $kn ] 77 | then 78 | value="${!kn}" 79 | if ckl10n $kn "$value" 80 | then 81 | echo "$value" >| $assetdir/$kn 82 | else 83 | logtoboth "? Plugin $pfx: Unrecognized $kn '$value'" 84 | fi 85 | fi 86 | done 87 | 88 | logtoboth "* Plugin $pfx: Complete Phase 0" 89 | 90 | elif [ "$phase" == "1" ] 91 | then 92 | # 93 | # Phase 1 (in nspawn) 94 | # 95 | logtoboth "* Plugin $pfx: Start Phase 1" 96 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 97 | for kn in keymap locale timezone wificountry 98 | do 99 | if [ -f $assetdir/$kn ] 100 | then 101 | read value < $assetdir/$kn 102 | case "$kn" in 103 | keymap) 104 | # actual keymap setting deferred until first boot 105 | if [ "$value" != "" ] 106 | then 107 | logtoboth "> Plugin $pfx: Keymap '$value' will be set during sdm FirstBoot" 108 | logtoboth "> Plugin $pfx: Disable keyboard-setup service; will be re-enabled during sdm FirstBoot" 109 | systemctl disable keyboard-setup 110 | fi 111 | ;; 112 | locale) 113 | [ "$value" != "" ] && logtoboth "> Plugin $pfx: Locale '$value' will be set during sdm FirstBoot" 114 | ;; 115 | timezone) 116 | [ "$value" != "" ] && logtoboth "> Plugin $pfx: Timezone '$value' will be set during sdm FirstBoot" 117 | ;; 118 | wificountry) 119 | [ "$value" != "" ] && logtoboth "> Plugin $pfx: Wifi country '$value' will be set during sdm FirstBoot" 120 | ;; 121 | esac 122 | else 123 | logtoboth "% Plugin $pfx: L10n item '$kn' was not set" 124 | fi 125 | done 126 | logtoboth "* Plugin $pfx: Complete Phase 1" 127 | 128 | else 129 | logtoboth "* Plugin $pfx: Start Phase post-install" 130 | logtoboth "* Plugin $pfx: Complete Phase post-install" 131 | fi 132 | -------------------------------------------------------------------------------- /plugins/apps: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: apps 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | loadparams 21 | vldargs="|apps|name|remove|" 22 | rqdargs="" 23 | assetdir="$SDMPT/etc/sdm/assets/apps" 24 | sdatefmt="%Y-%m-%d-%H:%M:%S.%N" 25 | if [ "$phase" == "0" ] 26 | then 27 | # 28 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 29 | # 30 | logtoboth "* Plugin $pfx: Start Phase 0" 31 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 32 | # 33 | # Print the keys found (example usage). plugin_getargs returns the list of found keys in $foundkeys 34 | # 35 | plugin_printkeys 36 | 37 | if [[ "$apps" == "" ]] && [[ "$remove" == "" ]] 38 | then 39 | logtobothex "? Plugin $pfx: Neither 'apps' nor 'remove' specified; Nothing to do" 40 | fi 41 | [ "$name" == "" ] && name="default" 42 | mkdir -p $assetdir 43 | if [ "$apps" != "" ] 44 | then 45 | appfile=$(findappfile "$apps") 46 | exitiferr "$appfile" 47 | apps=$(getapplist "$appfile") 48 | if [ -f $assetdir/$name ] 49 | then 50 | read oapps < $assetdir/$name 51 | apps="$oapps $apps" 52 | fd=$(date -d "$(stat --printf "%y" $assetdir/$name)" +$sdatefmt) 53 | logtoboth "> Plugin $pfx: Save prior 'apps' list '$name' as '$name-$fd'" 54 | mv $assetdir/$name $assetdir/$name-$fd 55 | fi 56 | echo "$apps" >| $assetdir/$name 57 | logtoboth "> Plugin $pfx [$name]: Saved 'apps' list: $apps" 58 | fi 59 | if [ "$remove" != "" ] 60 | then 61 | removefile=$(findappfile "$remove") 62 | exitiferr "$removefile" 63 | removes=$(getapplist "$removefile") 64 | if [ -f $assetdir/$name-removes ] 65 | then 66 | read oapps < $assetdir/$name-removes 67 | removes="$oapps $removes" 68 | fd=$(date -d "$(stat --printf "%y" $assetdir/$name-removes)" +$sdatefmt) 69 | logtoboth "> Plugin $pfx: Save prior 'removes' list '$name-removes' as '$name-removes-$fd'" 70 | mv $assetdir/$name-removes $assetdir/$name-removes-$fd 71 | fi 72 | echo "$removes" >| $assetdir/$name-removes 73 | logtoboth "> Plugin $pfx [$name]: Saved 'remove' list: $removes" 74 | fi 75 | 76 | logtoboth "* Plugin $pfx: Complete Phase 0" 77 | 78 | elif [ "$phase" == "1" ] 79 | then 80 | # 81 | # Phase 1 (in nspawn) 82 | # 83 | logtoboth "* Plugin $pfx: Start Phase 1" 84 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 85 | [ "$name" == "" ] && name="default" 86 | if [ -f $assetdir/$name-removes ] 87 | then 88 | logfreespace "at start of [$name] Application Removes" 89 | read removes < $assetdir/$name-removes 90 | logtoboth "> Plugin $pfx: Remove application(s)" 91 | IFS=" " read -a alist <<< "$removes" 92 | for a in "${alist[@]}" 93 | do 94 | if ispkginstalled $a 95 | then 96 | logtoboth " -- $a" 97 | doapt "remove --yes $a" $showapt 98 | else 99 | logtoboth " -- $a (not installed)" 100 | fi 101 | done 102 | fd=$(date -d "$(stat --printf "%y" $assetdir/$name-removes)" +$sdatefmt) 103 | logtoboth "> Plugin $pfx: Save completed 'removes' list '$name-removes' as '$name-removes-$fd'" 104 | mv $assetdir/$name-removes $assetdir/$name-removes-$fd 105 | fi 106 | if [ -f $assetdir/$name ] 107 | then 108 | logfreespace "at start of [$name] Application Installs" 109 | read svapps < $assetdir/$name 110 | if [[ "$virtmode" == "chroot" ]] && [[ "$svapps" =~ "qemu-user-static" ]] 111 | then 112 | svapps=${svapps/qemu-user-static} 113 | deferqemu 114 | fi 115 | doinstalls "$svapps" "[$name] Application Installs" || exit $? 116 | fd=$(date -d "$(stat --printf "%y" $assetdir/$name)" +$sdatefmt) 117 | logtoboth "> Plugin $pfx: Save completed 'apps' list '$name' as '$name-$fd'" 118 | mv $assetdir/$name $assetdir/$name-$fd 119 | fi 120 | 121 | logfreespace "at end of $pfx [$name] Phase 1" 122 | logtoboth "* Plugin $pfx: Complete Phase 1" 123 | else 124 | # 125 | # Plugin Post-install edits 126 | # 127 | logtoboth "* Plugin $pfx: Start Phase post-install" 128 | #plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 129 | #logfreespace "at start of Plugin $pfx Phase post-install" 130 | # 131 | #logfreespace "at end of $pfx Custom Phase post-install" 132 | logtoboth "* Plugin $pfx: Complete Phase post-install" 133 | fi 134 | -------------------------------------------------------------------------------- /plugins/apt-addrepo: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: apt-addrepo 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | loadparams 21 | vldargs="|repo|name|repofile|gpgkey|gpgkeyname|" #"|list|of|valid|args|" 22 | rqdargs="" # |list|of|required|args|or|nullstring| 23 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 24 | 25 | if [ "$phase" == "0" ] 26 | then 27 | # 28 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 29 | # 30 | logtoboth "* Plugin $pfx: Start Phase 0" 31 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 32 | plugin_printkeys 33 | if [[ -v repo ]] && [[ "$repo" != "" ]] 34 | then 35 | logtoboth "> Plugin $pfx: Add repo '$repo' to apt sources" 36 | [ "$name" == "" ] && name="sdm-sourced-repo" 37 | if [ -f $SDMPT/etc/apt/sources.list.d/$name.list ] 38 | then 39 | logtoboth "? Plugin $pfx: Repo '$name' already exists; Add argument 'name=somename' to this apps plugin invocation" 40 | logtobothex " NOTE: This plugin is not idempotent; Restart with a fresh uncustomized IMG if this IMG is previously customized" 41 | fi 42 | echo "$repo" >> $SDMPT/etc/apt/sources.list.d/$name.list 43 | fi 44 | if [[ -v repofile ]] && [[ "$repofile" != "" ]] 45 | then 46 | if [ -f $repofile ] 47 | then 48 | rbn=$(basename $repofile) 49 | [[ "${rbn##*.}" != "list" ]] && logtobothex "? Plugin $pfx: apt Repo file '$repofile' must have file type '.list'" 50 | logtoboth "> Plugin $pfx: Add apt repo sources file '$repofile'" 51 | cp $repofile $SDMPT/etc/apt/sources.list.d 52 | else 53 | logtobothex "? Plugin $pfx: Repo source file '$repofile' not found" 54 | fi 55 | fi 56 | 57 | # Some key URLs just point to a directory, so getting key basename isn't always very helpful. Use name arg for naming control 58 | if [ "$gpgkeyname" == "" ] 59 | then 60 | [ -f $gpgkey ] && gpgkeyname=$(basename "$gpgkey") 61 | fi 62 | gpgkeyname=${gpgkeyname%.*} # Strip file type 63 | [ "$gpgkeyname" == "" ] && gpgkeyname="$name" 64 | 65 | if [[ -v gpgkey ]] && [[ -f $gpgkey ]] 66 | then 67 | logtoboth "> Plugin $pfx: Copy gpg key '$gpgkey' to $SDMPT/etc/apt/trusted.gpg.d/$gpgkeyname.gpg" 68 | cp $gpgkey $SDMPT/etc/apt/trusted.gpg.d 69 | else 70 | [[ "$(type -p gpg)" == "" ]] && logtobothex "? Please sudo apt install gpg" 71 | logtoboth "> Plugin $pfx: Download gpg key from $gpgkey and add to apt trusted keys as '$gpgkeyname.gpg'" 72 | curl -sS $gpgkey | gpg --dearmor | tee $SDMPT/etc/apt/trusted.gpg.d/$gpgkeyname.gpg >/dev/null 73 | fi 74 | logtoboth "* Plugin $pfx: Complete Phase 0" 75 | elif [ "$phase" == "1" ] 76 | then 77 | # 78 | # Phase 1 (in nspawn) 79 | # 80 | logtoboth "* Plugin $pfx: Start Phase 1" 81 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 82 | logtoboth "* Plugin $pfx: Complete Phase 1" 83 | elif [ "$phase" == "post-install" ] 84 | then 85 | # 86 | # Plugin Post-install edits 87 | # 88 | logtoboth "* Plugin $pfx: Start Phase post-install" 89 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 90 | logtoboth "* Plugin $pfx: Complete Phase post-install" 91 | fi 92 | -------------------------------------------------------------------------------- /plugins/apt-cacher-ng: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: apt-cacher-ng 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1:arg2=val2:arg3=val3: ... 14 | 15 | # 16 | # Main code for the script 17 | # 18 | phase=$1 19 | pfx="$(basename $0)" #For messages 20 | args="$2" 21 | vldargs="|gentargetmode|bindaddress|cachedir|port|tunnelenable|proxy|" 22 | rqdargs="" 23 | loadparams 24 | 25 | if [ "$phase" == "0" ] 26 | then 27 | # 28 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 29 | # 30 | logtoboth "* Plugin $pfx: Start Phase 0" 31 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 32 | 33 | # INSERT Plugin Phase 0 code here 34 | 35 | logtoboth "* Plugin $pfx: Complete Phase 0" 36 | 37 | elif [ "$phase" == "1" ] 38 | then 39 | # 40 | # Phase 1 (in nspawn) 41 | # 42 | logtoboth "* Plugin $pfx: Start Phase 1" 43 | #logfreespace "at start of Plugin $pfx Phase 1" 44 | # 45 | plugin_getargs $pfx "$args" "|gentargetmode|bindaddress|cachedir|port|tunnelenable|proxy|" 46 | plugin_printkeys 47 | 48 | # Possible choices FOR gentargetmode: Set up once, Set up now and update later, No automated setup 49 | # Other settings: "keep" keeps the value from the default install 50 | 51 | [ "$gentargetmode" == "" ] && gentargetmode="No automated setup" 52 | [ "$bindaddress" == "" ] && bindaddress="0.0.0.0" 53 | [ "$cachedir" == "" ] && cachedir="keep" 54 | [ "$port" == "" ] && port="keep" 55 | [ "$tunnelenable" == "" ] && tunnelenable="false" 56 | [ "$proxy" == "" ] && proxy="keep" 57 | 58 | debconf-set-selections <<< "apt-cacher-ng apt-cacher-ng/gentargetmode string $gentargetmode" 59 | debconf-set-selections <<< "apt-cacher-ng apt-cacher-ng/bindaddress string $bindaddress" 60 | debconf-set-selections <<< "apt-cacher-ng apt-cacher-ng/cachedir string $cachedir" 61 | debconf-set-selections <<< "apt-cacher-ng apt-cacher-ng/port string $port" 62 | debconf-set-selections <<< "apt-cacher-ng apt-cacher-ng/tunnelenable string $tunnelenable" 63 | debconf-set-selections <<< "apt-cacher-ng apt-cacher-ng/proxy string $proxy" 64 | 65 | logtoboth "> Plugin $pfx: Install apt-cacher-ng server" 66 | logtoboth "> Plugin $pfx: With arguments:" 67 | logtoboth "> Plugin $pfx: gentargetmode: $gentargetmode" 68 | logtoboth "> Plugin $pfx: bindaddress: $bindaddress" 69 | logtoboth "> Plugin $pfx: cachedir: $cachedir" 70 | logtoboth "> Plugin $pfx: port: $port" 71 | logtoboth "> Plugin $pfx: tunnelenable: $tunnelenable" 72 | logtoboth "> Plugin $pfx: proxy: $proxy" 73 | installpkgsif apt-cacher-ng 74 | 75 | #logfreespace "at end of $pfx Phase 1" 76 | logtoboth "* Plugin $pfx: Complete Phase 1" 77 | else 78 | # 79 | # Plugin Post-install edits 80 | # 81 | logtoboth "* Plugin $pfx: Start Phase post-install" 82 | #logfreespace "at start of Plugin $pfx Phase post-install" 83 | # 84 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 85 | plugin_printkeys 86 | 87 | # Possible choices FOR gentargetmode: Set up once, Set up now and update later, No automated setup 88 | # Other settings: "keep" keeps the value from the default install 89 | 90 | [ "$gentargetmode" == "" ] && gentargetmode="No automated setup" 91 | [ "$bindaddress" == "" ] && bindaddress="keep" 92 | [ "$cachedir" == "" ] && cachedir="keep" 93 | [ "$port" == "" ] && port="keep" 94 | [ "$tunnelenable" == "" ] && tunnelenable="false" 95 | [ "$proxy" == "" ] && proxy="keep" 96 | 97 | logtoboth "> Plugin $pfx: Set apt-cacher-ng to enable and enable apt to use it (127.0.0.1) after sdm FirstBoot" 98 | acs01="/etc/sdm/0piboot/099-enable-apt-cacher-ng.sh" 99 | cat > $acs01 <> /etc/apt/apt.conf.d/02proxy 103 | systemctl enable apt-cacher-ng > /dev/null 2>&1 104 | EOF 105 | 106 | logtoboth "> Plugin $pfx: Create cacher reset script /usr/local/bin/reset-apt-cacher" 107 | cat > /usr/local/bin/reset-apt-cacher < Plugin $pfx: Install apt-file" 42 | installpkgsif apt-file 43 | # 44 | #logfreespace "at end of $pfx Phase 1" 45 | logtoboth "* Plugin $pfx: Complete Phase 1" 46 | else 47 | # 48 | # Plugin Post-install edits 49 | # 50 | logtoboth "* Plugin $pfx: Start Phase post-install" 51 | #logfreespace "at start of Plugin $pfx Phase post-install" 52 | # 53 | logtoboth "> Plugin $pfx: Generate apt-file database" 54 | if type -P apt-file > /dev/null 55 | then 56 | doapt update $showapt apt-file || logapterror 57 | else 58 | logtobothex "? Plugin $pfx: apt-file appears to not be installed" 59 | fi 60 | 61 | # 62 | #logfreespace "at end of $pfx Custom Phase post-install" 63 | logtoboth "* Plugin $pfx: Complete Phase post-install" 64 | fi 65 | -------------------------------------------------------------------------------- /plugins/burnpwd: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: burnpwd 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | function getpassword() { 13 | local pwd 14 | while [ true ] 15 | do 16 | echo -n "Password for $pwuser: " && read -s pwd 17 | echo "" 18 | [ "$pwd" != "" ] && break 19 | done 20 | printf -v userpwd "%s" "$pwd" 21 | } 22 | 23 | function logpassword() { 24 | local puser="$1" pwd="$2" 25 | if [ "$log" != "" ] 26 | then 27 | [ -f $log ] || printf "%-19s %-16s %-16s %s\n" "Date" "Hostname" "User" "Password" > $log 28 | printf "%-19s %-16s %-16s %s\n" "$(date +'%Y-%m-%d %H:%M:%S')" "$hostname" "$puser" "$pwd" >> $log 29 | logtoboth "> Plugin $pfx: Log password for user '$puser' to host file '$log'" 30 | fi 31 | } 32 | 33 | function savepassword() { 34 | local puser="$1" pwd="$2" 35 | echo $pwd > $SDMPT$burnpwd 36 | logpassword "$puser" "$pwd" 37 | } 38 | 39 | # $1 is the phase: "0", "1", or "post-install" 40 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 41 | # 42 | # Main code for the Plugin 43 | # 44 | phase=$1 45 | pfx="$(basename $0)" #For messages 46 | args="$2" 47 | vldargs="|method|log|user|length|" 48 | dlength=20 49 | loadparams 50 | 51 | if [ "$phase" == "0" ] 52 | then 53 | logtoboth "* Plugin $pfx: Start Phase 0" 54 | plugin_getargs $pfx "$args" "$vldargs" 55 | [ "$method" == "" ] && method="prompt" 56 | [ "$user" != "" ] && pwuser=$user || pwuser=$myuser 57 | burnpwd="/etc/sdm/local-assets/$pwuser-burnpwd" 58 | if [ "$length" != "" ] 59 | then 60 | if ! [[ "$length" =~ ^[0-9]+$ ]] 61 | then 62 | logtoboth "% Plugin $pfx: Length value '$length' is not numeric; Using $dlength" 63 | length=$dlength 64 | fi 65 | else 66 | length=$dlength 67 | fi 68 | plugin_printkeys 69 | errors=0 70 | if [ "$pwuser" == "" ] 71 | then 72 | logtoboth "% Plugin $pfx: No username provided and no user found in customized IMG" 73 | errors=$((errors+1)) 74 | # ** This is Phase 0 so can't use getent 75 | elif ! grep -q ^$pwuser: $SDMPT/etc/passwd 76 | then 77 | logtoboth "% Plugin $pfx: Username '$pwuser' not found in customized IMG" 78 | savepassword "$pwuser" "sdmNOUSER" 79 | errors=$((errors+1)) 80 | fi 81 | if [ $errors -eq 0 ] 82 | then 83 | case "$method" 84 | in 85 | prompt) getpassword # into userpwd 86 | savepassword "$pwuser" "$userpwd" 87 | ;; 88 | random) userpwd=$(date +%s | sha256sum | base64 | head -c $length ; echo) 89 | # Another method: < /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-$length};echo 90 | savepassword "$pwuser" "$userpwd" 91 | [ "$log" == "" ] && logtoboth "%!Plugin $pfx: No log specified to remember random password; Hope you have ESP!" 92 | ;; 93 | *) logtoboth "% Plugin $pfx: Unrecognized password method '$method' ignored" 94 | ;; 95 | esac 96 | fi 97 | logtoboth "* Plugin $pfx: Complete Phase 0" 98 | 99 | elif [ "$phase" == "1" ] 100 | then 101 | # 102 | # Phase 1 (in nspawn) 103 | # 104 | logtoboth "* Plugin $pfx: Start Phase 1" 105 | plugin_getargs $pfx "$args" "$vldargs" 106 | [ "$method" == "" ] && method="prompt" 107 | [ "$user" != "" ] && pwuser=$user || pwuser=$myuser 108 | burnpwd="/etc/sdm/local-assets/$pwuser-burnpwd" 109 | if [ -f $burnpwd ] 110 | then 111 | read userpwd < $burnpwd 112 | if [ "$userpwd" != "sdmNOUSER" ] 113 | then 114 | [ $showpwd -eq 1 ] && logtoboth "> Plugin $pfx: Set password '$userpwd' for user '$pwuser'" || logtoboth "> Plugin $pfx: Set password for user '$pwuser'" 115 | chpasswd < Plugin $pfx: Copy '$id' config file '$idf' to $assetdir/$did" 48 | mkdir -p $assetdir/$did 49 | cp -a $idf $assetdir/$did 50 | else 51 | logtobothex "? Plugin $pfx: '$id' config file '$idf' not found" 52 | fi 53 | fi 54 | done 55 | logtoboth "* Plugin $pfx: Complete Phase 0" 56 | 57 | elif [ "$phase" == "1" ] 58 | then 59 | # 60 | # Phase 1 (in nspawn) 61 | # 62 | logtoboth "* Plugin $pfx: Start Phase 1" 63 | plugin_getargs $pfx "$args" "$vldargs" 64 | logtoboth "> Plugin $pfx: Install chrony" 65 | installpkgsif chrony 66 | logtoboth "* Plugin $pfx: Complete Phase 1" 67 | else 68 | # 69 | # Plugin Post-install edits 70 | # 71 | logtoboth "* Plugin $pfx: Start Phase post-install" 72 | plugin_getargs $pfx "$args" "$vldargs" 73 | for id in conf sources 74 | do 75 | if compgen -G "$assetdir/$id/*.$id" > /dev/null 76 | then 77 | logtoboth "> Prefix $pfx: Copy $id config files to /etc/chrony/$id.d" 78 | cp -a $assetdir/$id/*.$id /etc/chrony/$id.d 79 | fi 80 | done 81 | 82 | if [ -v nodistsources ] 83 | then 84 | if compgen -G "$assetdir/sources/*.sources" > /dev/null 85 | then 86 | logtoboth "> Plugin $pfx: Disable time servers defined in /etc/chrony.conf" 87 | cp -a /etc/chrony/chrony.conf /etc/chrony/chrony.conf.sdm 88 | sed -i "s/^pool/#pool/" /etc/chrony/chrony.conf 89 | fi 90 | fi 91 | 92 | logtoboth "* Plugin $pfx: Complete Phase post-install" 93 | fi 94 | -------------------------------------------------------------------------------- /plugins/clockfake: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: clockfake 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | vargs="|burntime|interval|" 21 | rqdargs="" 22 | loadparams 23 | 24 | if [ "$phase" == "0" ] 25 | then 26 | # 27 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 28 | # 29 | logtoboth "* Plugin $pfx: Start Phase 0" 30 | logtoboth "* Plugin $pfx: Complete Phase 0" 31 | 32 | elif [ "$phase" == "1" ] 33 | then 34 | # 35 | # Phase 1 (in nspawn) 36 | # 37 | logtoboth "* Plugin $pfx: Start Phase 1" 38 | plugin_getargs $pfx "$args" "$vargs" "$rqdargs" || exit 39 | plugin_printkeys 40 | 41 | [ "$interval" == "" ] && interval=60 42 | logtoboth "* Plugin $pfx: Install clockfake" 43 | rm -f /usr/local/bin/clockfake 44 | cat >> /usr/local/bin/clockfake <> /etc/systemd/system/clockfake.service < /dev/null 2>&1 87 | if [[ "$SDMNSPAWN" =~ "Live" ]] 88 | then 89 | systemctl daemon-reload 90 | systemctl restart clockfake > /dev/null 2&>1 91 | fi 92 | logtoboth "* Plugin $pfx: Disable RasPiOS fake-hwclock hourly update" 93 | [ -f /etc/cron.hourly/fake-hwclock ] && mv /etc/cron.hourly/fake-hwclock /etc/cron.hourly/.fake-hwclock 94 | # 95 | # Run the program once here to set the fake hw clock 96 | # 97 | if [ "$burntime" == "y" ] 98 | then 99 | logtoboth "> Plugin $pfx: Update fake hw clock" 100 | python3 < /dev/tty 40 | done 41 | done 42 | cmd=${cmd# } 43 | echo "${cmd/ / }" 44 | } 45 | 46 | function addelem() { 47 | # 48 | # $1: cmdline 49 | # $2: elements to add space-separated 50 | # 51 | local cmd="$1" alladds="$2" add0 52 | 53 | IFS=" " read -a adds <<< "$alladds " 54 | for add0 in "${adds[@]}" 55 | do 56 | cmd="$(rmelem "$cmd" "${add0/=*/}") $add0" # Remove it if it exists and append new at end of cmd 57 | done 58 | cmd=${cmd## } 59 | echo "${cmd/ / }" 60 | } 61 | 62 | # $1 is the phase: "0", "1", or "post-install" 63 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 64 | # 65 | # Main code for the Plugin 66 | # 67 | phase=$1 68 | pfx="$(basename $0)" #For messages 69 | args="$2" 70 | loadparams 71 | vldargs="|replace|add|delete|" #"|list|of|valid|args|" 72 | rqdargs="" # |list|of|required|args|or|nullstring| 73 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 74 | 75 | if [ "$phase" == "0" ] 76 | then 77 | # 78 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 79 | # 80 | logtoboth "* Plugin $pfx: Start Phase 0" 81 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 82 | # 83 | # Print the keys found (example usage). plugin_getargs returns the list of found keys in $foundkeys 84 | # 85 | plugin_printkeys 86 | mkdir -p $assetdir 87 | logtoboth "* Plugin $pfx: Complete Phase 0" 88 | elif [ "$phase" == "1" ] 89 | then 90 | # 91 | # Phase 1 (in nspawn) 92 | # 93 | logtoboth "* Plugin $pfx: Start Phase 1" 94 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 95 | #logfreespace "at start of Plugin $pfx Phase 1" 96 | 97 | [ -d /boot/firmware ] && fdir="/boot/firmware" || fdir="/boot" 98 | read cmdline < $fdir/cmdline.txt 99 | logtoboth "> Plugin $pfx: Initial cmdline.txt: '$cmdline'" 100 | if [ "$replace" != "" ] 101 | then 102 | logtoboth "> Plugin $pfx: Replace cmdline.txt: '$replace'" 103 | cmdline="$replace" 104 | fi 105 | if [ "$delete" != "" ] 106 | then 107 | logtoboth "> Plugin $pfx: Delete '$delete' from cmdline.txt" 108 | cmdline=$(rmelem "$cmdline" "$delete") 109 | fi 110 | if [ "$add" != "" ] 111 | then 112 | logtoboth "> Plugin $pfx: Add '$add' to cmdline.txt" 113 | cmdline=$(addelem "$cmdline" "$add") 114 | fi 115 | logtoboth "> Plugin $pfx: New cmdline.txt: '$cmdline'" 116 | cat $fdir/cmdline.txt >| $fdir/cmdline.txt.sdm.orig 117 | echo "$cmdline" >| $fdir/cmdline.txt 118 | 119 | #logfreespace "at end of $pfx Phase 1" 120 | logtoboth "* Plugin $pfx: Complete Phase 1" 121 | elif [ "$phase" == "post-install" ] 122 | then 123 | # 124 | # Plugin Post-install edits 125 | # 126 | logtoboth "* Plugin $pfx: Start Phase post-install" 127 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 128 | #logfreespace "at start of Plugin $pfx Phase post-install" 129 | 130 | #logfreespace "at end of $pfx Custom Phase post-install" 131 | logtoboth "* Plugin $pfx: Complete Phase post-install" 132 | fi 133 | -------------------------------------------------------------------------------- /plugins/copydir: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: copydir 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | function geninstance() { 13 | # 14 | # make a unique instance name for assets 15 | # 16 | instance="$from-$to" 17 | instance=${instance//\/} # Remove slashes 18 | } 19 | 20 | function runrsync() { 21 | # 22 | # $1: from 23 | # $2: to 24 | # $3: rsyncopts 25 | # $4: stdout 26 | # $5: stderr 27 | local from=$1 to=$2 rsyncopts=$3 stdout="$4" stderr="$5" 28 | [ "$rsyncopts" == "" ] && aa="-a" || aa="" 29 | [ "$stdout" == "" ] && stdout="/dev/null" 30 | [ "$stderr" == "" ] && stderr="/dev/null" 31 | logtoboth "> Plugin $pfx: rsync $aa $rsyncopts $from $to" 32 | # Log the command in stdout so they can be differentiated if needed 33 | echo "rsync $aa $rsyncopts $from $to" >>$stdout 34 | rsync $aa $rsyncopts $from $to >>$stdout 2>>$stderr 35 | echo "" >>$stdout 36 | } 37 | 38 | # $1 is the phase: "0", "1", or "post-install" 39 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 40 | # 41 | # Main code for the Plugin 42 | # 43 | phase=$1 44 | pfx="$(basename $0)" #For messages 45 | args="$2" 46 | loadparams 47 | vldargs="|from|to|rsyncopts|stdout|stderr|" 48 | rqdargs="|from|to|" # |list|of|required|args|or|nullstring| 49 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 50 | instance="" 51 | 52 | if [ "$phase" == "0" ] 53 | then 54 | # 55 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 56 | # 57 | logtoboth "* Plugin $pfx: Start Phase 0" 58 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 59 | plugin_printkeys 60 | mkdir -p $assetdir 61 | # nodirect will never be defined so will always use direct copy 62 | if [ ! -v nodirect ] 63 | then 64 | # Direct copy in Phase 0 using rsync 65 | runrsync $from $SDMPT/$to "$rsyncopts" "$stdout" "$stderr" 66 | else 67 | # Stage it into $assetdir 68 | geninstance 69 | runrsync $from $assetdir/$instance "$rsyncopts" "$stdout" "$stderr" 70 | fi 71 | logtoboth "* Plugin $pfx: Complete Phase 0" 72 | 73 | elif [ "$phase" == "1" ] 74 | then 75 | # 76 | # Phase 1 (in nspawn) 77 | # 78 | logtoboth "* Plugin $pfx: Start Phase 1" 79 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 80 | #logfreespace "at start of Plugin $pfx Phase 1" 81 | # 82 | #logfreespace "at end of $pfx Phase 1" 83 | logtoboth "* Plugin $pfx: Complete Phase 1" 84 | else 85 | # 86 | # Plugin Post-install edits 87 | # 88 | logtoboth "* Plugin $pfx: Start Phase post-install" 89 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 90 | #logfreespace "at start of Plugin $pfx Phase post-install" 91 | if [ -v nodirect ] 92 | then 93 | geninstance 94 | runrsync $assetdir/$instance/ $to "$rsyncopts" "$stdout" "$stderr" 95 | fi 96 | #logfreespace "at end of $pfx Custom Phase post-install" 97 | logtoboth "* Plugin $pfx: Complete Phase post-install" 98 | fi 99 | -------------------------------------------------------------------------------- /plugins/disables: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: disables 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | loadparams 21 | vldargs="|bluetooth|piwiz|triggerhappy|wifi|" 22 | rqdargs="" 23 | 24 | if [ "$phase" == "0" ] 25 | then 26 | # 27 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 28 | # 29 | logtoboth "* Plugin $pfx: Start Phase 0" 30 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 31 | # 32 | # Print the keys found (example usage). plugin_getargs returns the list of found keys in $foundkeys 33 | # 34 | plugin_printkeys 35 | logtoboth "* Plugin $pfx: Complete Phase 0" 36 | 37 | elif [ "$phase" == "1" ] 38 | then 39 | # 40 | # Phase 1 (in nspawn) 41 | # 42 | logtoboth "* Plugin $pfx: Start Phase 1" 43 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 44 | #logfreespace "at start of Plugin $pfx Phase 1" 45 | # 46 | # 47 | #logfreespace "at end of $pfx Phase 1" 48 | logtoboth "* Plugin $pfx: Complete Phase 1" 49 | else 50 | # 51 | # Plugin Post-install edits 52 | # 53 | logtoboth "* Plugin $pfx: Start Phase post-install" 54 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 55 | #logfreespace "at start of Plugin $pfx Phase post-install" 56 | # 57 | dbls="$1|" 58 | IFS="|" read -a dlist <<< "$vldargs" 59 | for ditem in "${dlist[@]}" 60 | do 61 | if [ -v $ditem ] 62 | then 63 | case "$ditem" in 64 | bluetooth) 65 | fndis="/etc/sdm/0piboot/050-disable-bt.sh" 66 | if [ -f $fndis ] 67 | then 68 | logtoboth "% Plugin $pfx: Bluetooth already disabled" 69 | else 70 | logtoboth "> Plugin $pfx: Set Bluetooth to disable in sdm FirstBoot" 71 | cat > $fndis < /etc/modprobe.d/\$blfile 76 | systemctl disable hciuart 77 | EOF 78 | fi 79 | ;; 80 | piwiz) 81 | logtoboth "> Plugin $pfx: Disable running piwiz and userconfig on first system boot" 82 | [ -f /etc/xdg/autostart/piwiz.desktop ] && mv /etc/xdg/autostart/piwiz.desktop /etc/xdg/autostart/.piwiz.desktop.sdm 83 | systemctl disable userconfig.service > /dev/null 2>&1 84 | systemctl mask userconfig.service > /dev/null 2>&1 85 | getent passwd rpi-first-boot-wizard > /dev/null && userdel -r rpi-first-boot-wizard > /dev/null 2>&1 86 | [ -f /etc/systemd/system/getty@tty1.service.d/autologin.conf ] && mv /etc/systemd/system/getty@tty1.service.d/autologin.conf /etc/systemd/system/getty@tty1.service.d/.autologin.conf.sdm 87 | rm -f /etc/sudoers.d/010_wiz-nopasswd 88 | rm -f /etc/xdg/autostart/deluser.desktop 89 | ;; 90 | triggerhappy) 91 | logtoboth "> Plugin $pfx: Disable triggerhappy service" 92 | systemctl disable triggerhappy.service 93 | systemctl disable triggerhappy.socket 94 | #Eliminate thd.socket errors from udev 95 | [ -f /lib/udev/rules.d/60-triggerhappy.rules ] && mv /lib/udev/rules.d/60-triggerhappy.rules /lib/udev/rules.d/.60-triggerhappy-sdm.rules 96 | [ -f /usr/sbin/thd ] && mv /usr/sbin/thd /usr/sbin/thd.sdm 97 | ;; 98 | wifi) 99 | fndis="/etc/sdm/0piboot/055-disable-wifi.sh" 100 | if [ -f $fndis ] 101 | then 102 | logtoboth "% Plugin $pfx: WiFi already disabled" 103 | else 104 | logtoboth "> Plugin $pfx: Set WiFi to disable in sdm FirstBoot" 105 | cat > $fndis < /etc/modprobe.d/\$blfile 110 | EOF 111 | fi 112 | ;; 113 | esac 114 | fi 115 | done 116 | # 117 | #logfreespace "at end of $pfx Custom Phase post-install" 118 | logtoboth "* Plugin $pfx: Complete Phase post-install" 119 | fi 120 | -------------------------------------------------------------------------------- /plugins/docker-install: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: docker-install 4 | # Install Guide: https://docs.docker.com/engine/install/debian/ 5 | # 6 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 7 | # 8 | 9 | function loadparams() { 10 | source $SDMPT/etc/sdm/sdm-readparams 11 | } 12 | 13 | # $1 is the phase: "0", "1", or "post-install" 14 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 15 | # 16 | # Main code for the Plugin 17 | # 18 | phase=$1 19 | pfx="$(basename $0)" #For messages 20 | args="$2" 21 | loadparams 22 | vldargs="|list|of|valid|args|" 23 | rqdargs="" # |list|of|required|args|or|nullstring| 24 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 25 | 26 | if [ "$phase" == "0" ] 27 | then 28 | # 29 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 30 | # 31 | logtoboth "* Plugin $pfx: Start Phase 0" 32 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 33 | # 34 | # Print the keys found (example usage). plugin_getargs returns the list of found keys in $foundkeys 35 | # 36 | plugin_printkeys 37 | mkdir -p $assetdir 38 | plugin_dbgprint "This is how to do a Plugin Debug printout" # Will only be printed if --plugin-debug specified 39 | logtoboth "* Plugin $pfx: Complete Phase 0" 40 | elif [ "$phase" == "1" ] 41 | then 42 | # 43 | # Phase 1 (in nspawn) 44 | # 45 | logtoboth "* Plugin $pfx: Start Phase 1" 46 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 47 | logfreespace "at start of Plugin $pfx Phase 1" 48 | # 49 | logtoboth "> Plugin $pfx: Install ca-certificates and curl" 50 | installpkgsif "ca-certificates curl" 51 | install -m 0755 -d /etc/apt/keyrings 52 | curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc 53 | chmod a+r /etc/apt/keyrings/docker.asc 54 | 55 | # Add the repository to Apt sources 56 | logtoboth "> Plugin $pfx: Create apt docker.list sources file" 57 | echo \ 58 | "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \ 59 | $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ 60 | tee /etc/apt/sources.list.d/docker.list > /dev/null 61 | logtoboth "> Plugin $pfx: Perform apt update to get new sources" 62 | doaptrpterror "update" $showapt 63 | 64 | logtoboth "> Plugin $pfx: Install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin" 65 | installpkgsif "docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin" 66 | 67 | logfreespace "at end of $pfx Phase 1" 68 | logtoboth "* Plugin $pfx: Complete Phase 1" 69 | elif [ "$phase" == "post-install" ] 70 | then 71 | # 72 | # Plugin Post-install edits 73 | # 74 | logtoboth "* Plugin $pfx: Start Phase post-install" 75 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 76 | #logfreespace "at start of Plugin $pfx Phase post-install" 77 | # 78 | # INSERT Your Plugin's post-install code here 79 | # In Phase post-install all references to directories in the image can be direct 80 | # 81 | #logfreespace "at end of $pfx Custom Phase post-install" 82 | logtoboth "* Plugin $pfx: Complete Phase post-install" 83 | fi 84 | -------------------------------------------------------------------------------- /plugins/explore: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: explore 4 | # 5 | # The plugin is a burn plugin and must be invoked during a burn operation with --burn-plugin 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | function logtoboth() { 13 | echo "$1" 14 | } 15 | 16 | 17 | # $1 is the phase: "0", "1", or "post-install" 18 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 19 | # 20 | # Main code for the Plugin 21 | # 22 | phase=$1 23 | pfx="$(basename $0)" #For messages 24 | args="$2" 25 | loadparams 26 | vldargs="|mount|burndev|burnfilefile|imgtype|" 27 | rqdargs="|imgtype|" # |list|of|required|args|or|nullstring| 28 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 29 | 30 | # Redefine logtoboth so it just does an echo as the log is inaccessible from now on 31 | 32 | function logtoboth() { 33 | echo "$1" 34 | } 35 | 36 | if [ "$phase" == "burn-complete" ] 37 | then 38 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 39 | plugin_printkeys 40 | if [ -t 0 ] 41 | then 42 | dimg=$burndev 43 | [ "$dimg" == "" ] && dimg=$burnfilefile 44 | declare -x SDMPT=$(makemtpt) 45 | domount "$dimg" $imgtype 46 | echo "* $imgtype '$dimg' mounted on $SDMPT" 47 | if [ -v mount ] 48 | then 49 | echo $"** BE VERY CAREFUL!! ** 50 | ** Precede all path references by '$SDMPT' or you WILL modify your running system ** 51 | ** Use 'exit' to Exit the bash shell and unmount the $dimgdevname" 52 | IFS=":" read mfg mbg mcursor <<< $mcolors 53 | [ "$mfg" == "" ] && mfg="black" 54 | [ "$mbg" == "" ] && mbg="LightSalmon1" 55 | [ "$mcursor" == "" ] && mcursor="blue" 56 | stermcolors "$mfg" "$mbg" "$mcursor" xt 57 | cd $SDMPT 58 | bash 59 | cd - > /dev/null 60 | resetcolors xt 61 | else 62 | echo "* Enter $imgtype '$dimg'" 63 | spawncmd="/bin/bash" 64 | IFS=":" read efg ebg ecursor <<< $ecolors 65 | [ "$efg" == "" ] && efg="blue" 66 | [ "$ebg" == "" ] && ebg="gray" 67 | [ "$ecursor" == "" ] && ecursor="red" 68 | stermcolors "$efg" "$ebg" "$ecursor" xt 69 | sdm_spawn "$nspawnsw" Phase1 $spawncmd 70 | resetcolors xt 71 | fi 72 | fi 73 | fi 74 | -------------------------------------------------------------------------------- /plugins/extractfs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm burn plugin for: extractfs 4 | # 5 | 6 | function loadparams() { 7 | source $SDMPT/etc/sdm/sdm-readparams 8 | } 9 | 10 | function logtoboth() { 11 | echo "$1" 12 | } 13 | 14 | # $1 is the phase: "0", "1", or "post-install" 15 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 16 | # 17 | # Main code for the Plugin 18 | # 19 | phase=$1 20 | pfx="$(basename $0)" #For messages 21 | args="$2" 22 | loadparams 23 | vldargs="|bootfs|rootfs|burndev|burnfilefile|imgtype|" #"|list|of|valid|args|" 24 | rqdargs="|imgtype|" # |list|of|required|args|or|nullstring| 25 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 26 | 27 | if [[ "$phase" == "burn-complete" ]] 28 | then 29 | logtoboth "* Plugin $pfx: Start Phase $phase" 30 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 31 | # 32 | # Print the keys found (example usage). plugin_getargs returns the list of found keys in $foundkeys 33 | # 34 | plugin_printkeys 35 | [ -d $bootfs ] || logtobothex "? Plugin $pfx: bootfs target '$bootfs' must be a directory" 36 | [ -d $rootfs ] || logtobothex "? Plugin $pfx: rootfs target '$rootfs' must be a directory" 37 | dimg=$burndev 38 | [ "$dimg" == "" ] && dimg="$burnfilefile" 39 | declare -x SDMPT=$(makemtpt) 40 | domount "$dimg" $imgtype 41 | if [ "$bootfs" != "" ] 42 | then 43 | logtoboth "> Plugin $pfx: Copy bootfs from $SDMPT/boot/firmware to $bootfs" 44 | rsync -a --info=progress2 $SDMPT/boot/firmware $bootfs 45 | fi 46 | if [ "$rootfs" != "" ] 47 | then 48 | logtoboth "> Plugin $pfx: Copy rootfs from $SDMPT/boot to $rootfs" 49 | # Need to use -H b/c /usr/lib/aarch64-linux-gnu/ has 50+ hard-linked files 50 | rsync -aH --info=progress2 --exclude boot/firmware/* $SDMPT/ $rootfs 51 | fi 52 | docleanup 53 | exit 54 | fi 55 | -------------------------------------------------------------------------------- /plugins/imon: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: imon 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | loadparams 21 | 22 | if [ "$phase" == "0" ] 23 | then 24 | # 25 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 26 | # 27 | logtoboth "* Plugin $pfx: Start Phase 0" 28 | 29 | logtoboth "> Plugin $pfx: download imon from GitHub to /usr/local/bin/imon and set protection" 30 | curl -L https://raw.githubusercontent.com/gitbls/imon/main/imon -o $SDMPT/usr/local/bin/imon 31 | curl -L https://raw.githubusercontent.com/gitbls/imon/main/imon-configure -o $SDMPT/usr/local/bin/imon-configure 32 | curl -L https://raw.githubusercontent.com/gitbls/imon/main/imon-action.sample -o $SDMPT/usr/local/bin/imon-action.sample 33 | curl -L https://raw.githubusercontent.com/gitbls/imon/main/imon@.service -o $SDMPT/etc/systemd/system/imon@.service 34 | chmod 755 $SDMPT/usr/local/bin/{imon,imon-configure,imon-action.sample} 35 | 36 | logtoboth "* Plugin $pfx: Complete Phase 0" 37 | 38 | elif [ "$phase" == "1" ] 39 | then 40 | # 41 | # Phase 1 (in nspawn) 42 | # 43 | logtoboth "* Plugin $pfx: Start Phase 1" 44 | # Install pip (if needed) then create create venv and install icmplib and requests into it 45 | [ "$(type -p pip3)" == "" ] && logtoboth "> Plugin $pfx: Install python3-pip" && installpkgsif python3-pip 46 | logtoboth "> Plugin $pfx: Create venv /root/.imon-venv" 47 | python3 -m venv /root/.imon-venv 48 | logtoboth "> Plugin $pfx: install icmplib and requests with pip3 into venv: /root/.imon-venv" 49 | /root/.imon-venv/bin/pip3 install icmplib requests 50 | logtoboth "* Plugin $pfx: Complete Phase 1" 51 | else 52 | # 53 | # Plugin Post-install edits 54 | # 55 | logtoboth "* Plugin $pfx: Start Phase post-install" 56 | 57 | plugin_addnote "" 58 | plugin_addnote "*** imon service notes ***" 59 | plugin_addnote "" 60 | plugin_addnote " * Use 'sudo imon-configure' to configure imon's service parameters" 61 | plugin_addnote " * Then use 'sudo systemctl enable --now imon@instancename'" 62 | plugin_addnote " where 'instancename' is the name you created in imon-configure" 63 | plugin_addnote "" 64 | plugin_addnote " * See /usr/local/bin/imon-action.sample for a sample imon action script" 65 | plugin_addnote " to customize event actions" 66 | plugin_addnote "" 67 | plugin_addnote " * For complete details: https://github.com/gitbls/imon" 68 | plugin_addnote "" 69 | 70 | logtoboth "* Plugin $pfx: Complete Phase post-install" 71 | fi 72 | -------------------------------------------------------------------------------- /plugins/knockd: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: knockd 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | loadparams 21 | vldargs="|config|localsrc|" 22 | assetdir="$SDMPT/etc/sdm/assets/knockd" 23 | 24 | if [ "$phase" == "0" ] 25 | then 26 | # 27 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 28 | # 29 | logtoboth "* Plugin $pfx: Start Phase 0" 30 | plugin_getargs $pfx "$args" "$vldargs" "" || exit 31 | mkdir -p $assetdir 32 | # 33 | # Print the keys found (example usage). plugin_getargs returns the list of found keys in $foundkeys 34 | # 35 | plugin_printkeys 36 | # 37 | # Copy pktables and knockd.conf files 38 | # 39 | srcurl="https://raw.githubusercontent.com/gitbls/pktables/master" 40 | if [ "$config" != "" ] 41 | then 42 | logtoboth "> Plugin $pfx: Copy knockd config from $localsrc to $assetdir" 43 | [ -f $config ] && cp -a $config $assetdir/knockd.conf 44 | fi 45 | if [ "$localsrc" != "" ] 46 | then 47 | # 48 | # Copy the appropriate files from local stash into the IMG 49 | # 50 | logtoboth "> Plugin $pfx: Copy pktables and knockd-helper from $localsrc to $assetdir" 51 | [ -f "$localsrc/pktables" ] && cp -a $localsrc/pktables $assetdir 52 | [ -f "$localsrc/knockd-helper" ] && cp -a $localsrc/knockd-helper $assetdir 53 | [ -f "$localsrc/knockd.service" ] && cp -a $localsrc/knockd.service $assetdir 54 | if [ "$config" == "" ] 55 | then 56 | logtoboth "> Plugin $pfx: Copy knockd.conf from $localsrc to $assetdir" 57 | [ -f "$localsrc/knockd.conf" ] && cp -a $localsrc/knockd.conf $assetdir 58 | fi 59 | else 60 | logtoboth "> Plugin $pfx: Download pktables from GitHub..." 61 | gsts=0 62 | for f in pktables knockd-helper knockd.service 63 | do 64 | wget $srcurl/$f --output-document=$assetdir/$f 65 | [ $? -ne 0 ] && gsts=1 66 | done 67 | if [ $gsts -ne 0 ] 68 | then 69 | logtobothex "? Plugin $pfx: Unable to download pktables from $srcurl" 70 | fi 71 | fi 72 | logtoboth "* Plugin $pfx: Complete Phase 0" 73 | elif [ "$phase" == "1" ] 74 | then 75 | # 76 | # Phase 1 (in nspawn) 77 | # 78 | logtoboth "* Plugin $pfx: Start Phase 1" 79 | plugin_getargs $pfx "$args" "$vldargs" "" 80 | #logfreespace "at start of Plugin $pfx Phase 1" 81 | # 82 | logtoboth "> Plugin $pfx: Install knockd and iptables" 83 | installpkgsif "knockd iptables" 84 | # 85 | #logfreespace "at end of $pfx Phase 1" 86 | logtoboth "* Plugin $pfx: Complete Phase 1" 87 | else 88 | # 89 | # Plugin Post-install edits 90 | # 91 | logtoboth "* Plugin $pfx: Start Phase post-install" 92 | plugin_getargs $pfx "$args" "$vldargs" "" 93 | #logfreespace "at start of Plugin $pfx Phase post-install" 94 | # 95 | [ -f $assetdir/pktables ] && cp -a $assetdir/pktables /usr/local/bin && setfileownmode /usr/local/bin/pktables 755 96 | [ -f $assetdir/knockd-helper ] && cp -a $assetdir/knockd-helper /usr/local/bin && setfileownmode /usr/local/bin/knockd-helper 755 97 | if [ -f $assetdir/knockd.conf ] 98 | then 99 | [ -f /etc/knockd.conf ] && mv /etc/knockd.conf /etc/knockd.conf.sdm 100 | cp -a $assetdir/knockd.conf /etc 101 | setfileownmode /etc/knockd.conf 644 102 | fi 103 | if [ -f $assetdir/knockd.service ] 104 | then 105 | cp -a $assetdir/knockd.service /etc/systemd/system 106 | setfileownmode /etc/systemd/system/knockd.service 644 107 | systemctl daemon-reload > /dev/null 2>&1 108 | fi 109 | # 110 | #logfreespace "at end of $pfx Custom Phase post-install" 111 | plugin_addnote "" 112 | plugin_addnote "*** knockd Configuration Notes ***" 113 | plugin_addnote "" 114 | plugin_addnote " * sudoedit /etc/knockd.conf and configure your port knocking" 115 | plugin_addnote " * sudoedit /usr/local/bin/knockd-helper" 116 | plugin_addnote " and add 'pktables init' commands for each service in your knockd.conf " 117 | plugin_addnote " * sudo systemctl enable knockd" 118 | plugin_addnote " * Reboot" 119 | plugin_addnote " * Check for errors: sudo journalctl -b" 120 | plugin_addnote " * Make sure port forwards for ALL your knock sequences are set in your router" 121 | plugin_addnote "" 122 | plugin_addnote " * pktables documentation: https://github.com/gitbls/pktables" 123 | plugin_addnote " * knockd and knockd.conf documentation: man knockd" 124 | plugin_addnote "" 125 | plugin_addnote " * Complete Phase post-install" 126 | fi 127 | -------------------------------------------------------------------------------- /plugins/logwatch: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: logwatch 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | vldargs="|config|sendto|sendfrom|" 21 | assetdir="$SDMPT/etc/sdm/assets/logwatch" 22 | loadparams 23 | 24 | if [ "$phase" == "0" ] 25 | then 26 | # 27 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 28 | # 29 | 30 | logtoboth "* Plugin $pfx: Start Phase 0" 31 | plugin_getargs $pfx "$args" "$vldargs" || exit 32 | plugin_printkeys 33 | mkdir -p $assetdir 34 | if [ "$config" != "" ] 35 | then 36 | if [ -d $config ] 37 | then 38 | logtoboth "> Plugin $pfx: Copy local logwatch config subtree to $assetdir/config" 39 | mkdir -p $assetdir/config 40 | rsync -a $config/ $assetdir/config 41 | 42 | fi 43 | fi 44 | logtoboth "* Plugin $pfx: Complete Phase 0" 45 | 46 | elif [ "$phase" == "1" ] 47 | then 48 | # 49 | # Phase 1 (in nspawn) 50 | # 51 | logtoboth "* Plugin $pfx: Start Phase 1" 52 | plugin_getargs $pfx "$args" "$vldargs" || exit 53 | logtoboth "> Plugin $pfx: Install logwatch" 54 | installpkgsif logwatch 55 | if [ -d $assetdir/config ] 56 | then 57 | logtoboth "> Plugin $pfx: Copy local logwatch config files into /etc/logwatch" 58 | rsync -a $assetdir/config/ /etc/logwatch 59 | if [ -f /etc/logwatch/conf/logwatch.conf ] 60 | then 61 | cp /etc/logwatch/conf/logwatch.conf /etc/logwatch/conf/logwatch.conf.sdm 62 | if [ "$sendto" != "" ] 63 | then 64 | sendto=$(stripquotes "$sendto") 65 | logtoboth "> Plugin $pfx: Set MailTo '$sendto' in logwatch.conf" 66 | sed -i "s/^MailTo = .*/MailTo = $sendto/" /etc/logwatch/conf/logwatch.conf 67 | fi 68 | if [ "$sendfrom" != "" ] 69 | then 70 | sendfrom=$(stripquotes "$sendfrom") 71 | logtoboth "> Plugin $pfx: Set MailFrom '$sendfrom' in logwatch.conf" 72 | sed -i "s/^MailFrom = .*/MailFrom = $sendfrom/" /etc/logwatch/conf/logwatch.conf 73 | fi 74 | fi 75 | fi 76 | logtoboth "* Plugin $pfx: Complete Phase 1" 77 | else 78 | # 79 | # Plugin Post-install edits 80 | # 81 | logtoboth "* Plugin $pfx: Start Phase post-install" 82 | 83 | logtoboth "* Plugin $pfx: Complete Phase post-install" 84 | fi 85 | -------------------------------------------------------------------------------- /plugins/mkdir: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: mkdir 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | loadparams 21 | vldargs="|dir|chown|chmod|" 22 | rqdargs="|dir|" # |list|of|required|args|or|nullstring| 23 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 24 | 25 | if [ "$phase" == "0" ] 26 | then 27 | # 28 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 29 | # 30 | logtoboth "* Plugin $pfx: Start Phase 0" 31 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 32 | plugin_printkeys 33 | logtoboth "> Plugin $pfx: Create directory '$dir'" 34 | mkdir -p $SDMPT/$dir 35 | logtoboth "* Plugin $pfx: Complete Phase 0" 36 | 37 | elif [ "$phase" == "1" ] 38 | then 39 | # 40 | # Phase 1 (in nspawn) 41 | # 42 | logtoboth "* Plugin $pfx: Start Phase 1" 43 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 44 | #logfreespace "at start of Plugin $pfx Phase 1" 45 | # 46 | #logfreespace "at end of $pfx Phase 1" 47 | logtoboth "* Plugin $pfx: Complete Phase 1" 48 | else 49 | # 50 | # Plugin Post-install edits 51 | # 52 | logtoboth "* Plugin $pfx: Start Phase post-install" 53 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 54 | #logfreespace "at start of Plugin $pfx Phase post-install" 55 | if [ -v chown ] 56 | then 57 | logtoboth "> Plugin $pfx: Change owner on '$dir' to '$chown'" 58 | chown $chown $dir 59 | fi 60 | if [ -v chmod ] 61 | then 62 | logtoboth "> Plugin $pfx: Change directory protection for '$dir' to '$chmod'" 63 | chmod $chmod $dir 64 | fi 65 | #logfreespace "at end of $pfx Custom Phase post-install" 66 | logtoboth "* Plugin $pfx: Complete Phase post-install" 67 | fi 68 | -------------------------------------------------------------------------------- /plugins/piapps: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: piapps 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | loadparams 21 | vldargs="|user|" 22 | rqdargs="" # |list|of|required|args|or|nullstring| 23 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 24 | 25 | if [ "$phase" == "0" ] 26 | then 27 | # 28 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 29 | # 30 | logtoboth "* Plugin $pfx: Start Phase 0" 31 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 32 | # 33 | # Print the keys found (example usage). plugin_getargs returns the list of found keys in $foundkeys 34 | # 35 | plugin_printkeys 36 | #mkdir -p $assetdir 37 | logtoboth "* Plugin $pfx: Complete Phase 0" 38 | elif [ "$phase" == "1" ] 39 | then 40 | # 41 | # Phase 1 (in nspawn) 42 | # 43 | logtoboth "* Plugin $pfx: Start Phase 1" 44 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 45 | #logfreespace "at start of Plugin $pfx Phase 1" 46 | # 47 | #logfreespace "at end of $pfx Phase 1" 48 | logtoboth "* Plugin $pfx: Complete Phase 1" 49 | elif [ "$phase" == "post-install" ] 50 | then 51 | # 52 | # Plugin Post-install edits 53 | # 54 | logtoboth "* Plugin $pfx: Start Phase post-install" 55 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 56 | #logfreespace "at start of Plugin $pfx Phase post-install" 57 | [ "$user" != "" ] && buser=$user 58 | [ "$buser" == "" ] && buser=$myuser 59 | if [[ "$buser" != "" ]] 60 | then 61 | pgrep systemd >/dev/null 2>&1 || { ohost=$(hostname) ; hostname $thishost ; } 62 | logtoboth "> Plugin $pfx: Install pi-apps from https://github.com/Botspot/pi-apps using user $buser" 63 | su $buser -c 'wget -qO- https://raw.githubusercontent.com/Botspot/pi-apps/master/install | bash' 64 | pgrep systemd >/dev/null 2>&1 || hostname $ohost 65 | else 66 | logtobothex "? Plugin $pfx: User not specified with 'user' argument nor established with the 'user' plugin" 67 | fi 68 | plugin_addnote "" 69 | plugin_addnote "*** piapps notes ***" 70 | plugin_addnote "" 71 | plugin_addnote " * Desktop asks what you want to do with Pi-Apps executable script" 72 | plugin_addnote " * File Manager: Edit|Preferences|Don't ask options on launch executable file" 73 | plugin_addnote "" 74 | plugin_addnote "See https://github.com/Botspot/pi-apps for questions/issues/etc" 75 | 76 | #logfreespace "at end of $pfx Custom Phase post-install" 77 | logtoboth "* Plugin $pfx: Complete Phase post-install" 78 | fi 79 | -------------------------------------------------------------------------------- /plugins/postburn: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: postburn 4 | # 5 | # The plugin is a burn plugin and must be invoked during a burn operation with --burn-plugin 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | function ckfilex() { 13 | runscript="$1" 14 | [ -f $runscript ] || logtobothex "? Plugin $pfx: runscript file '$runscript' not found" 15 | [ -x $runscript ] || logtobothex "? Plugin $pfx: runscript file '$runscript' not executable" 16 | } 17 | 18 | function copyfile() { 19 | # 20 | # $1: from file 21 | # $2: to directory 22 | # 23 | local cfrom="$1" cto="$2" 24 | local sfn 25 | 26 | while read sfn 27 | do 28 | logtoboth "> Plugin $pfx: Copy '$sfn' to '$cto'" 29 | cp $sfn $cto 30 | sts=$? 31 | [ $sts -ne 0 ] && logtobothex "? Plugin $pfx: Error '$sts' copying '$sfn' to '$cto'" 32 | done < <(compgen -G "${SDMPT}${cfrom}") 33 | } 34 | 35 | function logtoboth() { 36 | echo "$1" 37 | } 38 | 39 | 40 | # $1 is the phase: "0", "1", or "post-install" 41 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 42 | # 43 | # Main code for the Plugin 44 | # 45 | phase=$1 46 | pfx="$(basename $0)" #For messages 47 | args="$2" 48 | loadparams 49 | vldargs="|savefrom|saveto|runscript|runphase|where|burndev|burnfilefile|imgtype|" 50 | rqdargs="|imgtype|" 51 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 52 | 53 | # Redefine logtoboth so it just does an echo as the log is inaccessible from now on 54 | 55 | #function logtoboth() { 56 | # echo "$1" 57 | #} 58 | 59 | #function logtobothex() { 60 | # echo "$1" 61 | # exit 1 62 | #} 63 | 64 | if [ "$phase" == "burn-complete" ] 65 | then 66 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 67 | plugin_printkeys 68 | if [[ "$savefrom" == "" ]] && [[ "$runscript" == "" ]] 69 | then 70 | logtobothex "? Plugin $pfx: Neither 'savefrom' or 'runscript' specified; nothing to do" 71 | fi 72 | dimg=$burndev 73 | [ "$dimg" == "" ] && dimg=$burnfilefile 74 | declare -x SDMPT=$(makemtpt) 75 | domount "$dimg" $imgtype 76 | echo "* $imgtype '$dimg' mounted on $SDMPT" 77 | if [ "$savefrom" != "" ] 78 | then 79 | [ "$saveto" == "" ] && logtobothex "? Plugin $pfx: No 'saveto' provided" 80 | [ -d $saveto ] || logtobothex "? Plugin $pfx: 'saveto' location '$saveto' is not a directory" 81 | # savefrom can be a file or an @file 82 | # save files to 'saveto' 83 | if [ "${savefrom:0:1}" == "@" ] 84 | then 85 | sfn=${savefrom:1:999} 86 | [ -f $sfn ] || logtobothex "? Plugin $pfx: 'savefrom' @file '$sfn' not found" 87 | #logtoboth "> Plugin $pfx: Copy files listed in '$sfn' to '$saveto'" 88 | while read fn 89 | do 90 | copyfile $fn $saveto 91 | done < $sfn 92 | else 93 | copyfile $savefrom $saveto 94 | fi 95 | fi 96 | if [ "$runscript" != "" ] 97 | then 98 | [ "$runphase" == "" ] && logtoboth "% Plugin $pfx: 'runphase' not specified; using phase1 for safety" && runphase="phase1" 99 | case "${runphase,,}" in 100 | phase0) 101 | logtoboth "> Plugin $pfx: Run '$runscript' in Phase 0 environment" 102 | [ "$where" != "host" ] && runscript="${SDMPT}${runscript}" 103 | ckfilex "$runscript" 104 | $runscript 105 | ;; 106 | phase1) 107 | rs=$runscript 108 | if [ "$where" == "host" ] 109 | then 110 | cs="$SDMPT/usr/local/bin/$(basename $runscript)" 111 | cp $runscript $cs 112 | rs="/usr/local/bin/$(basename $runscript)" 113 | else 114 | cs=$SDMPT/$rs 115 | fi 116 | ckfilex $cs 117 | logtoboth "> Plugin $pfx: Run '$runscript' in Phase 1 environment" 118 | sdm_spawn "" Phase1 $rs 119 | [ "$where" == "host" ] && rm -f $cs 120 | ;; 121 | *) 122 | logtobothex "? Plugin $pfx: runphase '$runphase' not recognized" 123 | ;; 124 | esac 125 | fi 126 | docleanup 127 | fi 128 | -------------------------------------------------------------------------------- /plugins/raspiconfig: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: raspiconfig 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | loadparams 21 | vldargs="|audio|audioconf|blanking|boot_behavior|boot_behaviour|boot_order|boot_splash|boot_wait|camera|composite|glamor|gldriver|i2c|memory_split|legacy|net_names|onewire|overclock|overscan|pixdub|pi4video|powerled|leds|overlayfs|rgpio|serial|spi|xcompmgr|vnc_resolution|" 22 | # These take 2 args, need more research: overscan_kms, fan, boot_rom, resolution, proxy 23 | rqdargs="" 24 | immedargs="|serial|" 25 | # 26 | # The settings are all stored in auto-1piboot and run when the system first boots 27 | # Some (many?) of the settings require that the system be up and running on the target hardware 28 | # Rather than sort out which is which, everything gets deferred except for 'serial', which may be interesting even during first boot 29 | # ** Need to update 1piboot/1piboot.conf whenever this changes 30 | # 31 | 32 | if [ "$phase" == "0" ] 33 | then 34 | # 35 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 36 | # 37 | logtoboth "* Plugin $pfx: Start Phase 0" 38 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 39 | logtoboth "* Plugin $pfx: Complete Phase 0" 40 | 41 | elif [ "$phase" == "1" ] 42 | then 43 | # 44 | # Phase 1 (in nspawn) 45 | # 46 | logtoboth "* Plugin $pfx: Start Phase 1" 47 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 48 | #logfreespace "at start of Plugin $pfx Phase 1" 49 | # 50 | if [ "$foundkeys" != "" ] 51 | then 52 | logtoboth "> Plugin $pfx: Processing settings..." 53 | IFS="|" read -a fargs <<< "$foundkeys" 54 | for c in "${fargs[@]}" 55 | do 56 | # The construct ${!c} gets the value of the variable 'pointed to' by contents of $c 57 | value="${!c}" 58 | chyp=${c//__/-} # Convert double underscore back to hyphen 59 | [ "$value" == "" ] && value="0" 60 | # Remove any old entry for this key and write the new one 61 | if ! grep "$chyp=$value" $SDMPT/etc/sdm/auto-1piboot.conf > /dev/null 2>&1 62 | then 63 | if [[ "$immedargs" =~ "$chyp" ]] 64 | then 65 | logtoboth "> Plugin $pfx: Set ${chyp} ${value} immediately" 66 | doconfigitem ${chyp} ${value} logtoboth 67 | else 68 | case "$chyp" in 69 | overlayfs) 70 | logtoboth "> Plugin $pfx: Install overlayroot for overlayfs" 71 | installpkgsif overlayroot 72 | [ "$value" == "0" ] && value="ro" 73 | ;; 74 | esac 75 | logtoboth "> Plugin $pfx: Set ${chyp} '${value}' to be enabled during sdm FirstBoot" 76 | if [[ "$chyp" =~ "boot_behav" ]] 77 | then 78 | setdelayedbbh "$value" 79 | else 80 | sed -i "/^$chyp=/d" $SDMPT/etc/sdm/auto-1piboot.conf 81 | echo "$chyp=$value" >> $SDMPT/etc/sdm/auto-1piboot.conf 82 | fi 83 | fi 84 | fi 85 | done 86 | fi 87 | # 88 | #logfreespace "at end of $pfx Phase 1" 89 | logtoboth "* Plugin $pfx: Complete Phase 1" 90 | else 91 | # 92 | # Plugin Post-install edits 93 | # 94 | logtoboth "* Plugin $pfx: Start Phase post-install" 95 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 96 | #logfreespace "at start of Plugin $pfx Phase post-install" 97 | # 98 | # INSERT Your Plugin's post-install code here 99 | # In Phase post-install all references to directories in the image can be direct 100 | # 101 | #logfreespace "at end of $pfx Custom Phase post-install" 102 | logtoboth "* Plugin $pfx: Complete Phase post-install" 103 | fi 104 | -------------------------------------------------------------------------------- /plugins/runatboot: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: runatboot 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | allargs="$2" 20 | loadparams 21 | vldargs="|script|user|sudoswitches|args|output|error|" 22 | rqdargs="|script|" 23 | assetdir="$SDMPT/etc/sdm/assets/runatboot" 24 | 25 | if [ "$phase" == "0" ] 26 | then 27 | # 28 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 29 | # 30 | logtoboth "* Plugin $pfx: Start Phase 0" 31 | plugin_getargs $pfx "$allargs" "$vldargs" "$rqdargs" || exit 32 | plugin_printkeys 33 | mkdir -p $assetdir 34 | if [ -f $script ] 35 | then 36 | sbn=$(basename $script) 37 | cp -a $script $assetdir/${sbn} 38 | sfn="$SDMPT/etc/sdm/0piboot/060-runatboot.sh" 39 | if [ ! -f $sfn ] 40 | then 41 | cat > $sfn < Plugin $pfx: Set script '$sbn' in '$sfn' to run at first system boot${xstr}" 57 | cat >> $sfn < /dev/null 61 | then 62 | logger "sdm FirstBoot: User '$user' for runatboot plugin script '$sbn' does not exist" 63 | exit 64 | fi 65 | logger "sdm FirstBoot: Copy runatboot script /etc/sdm/assets/runatboot/${sbn} to /tmp to run${xstr}" 66 | cp /etc/sdm/assets/runatboot/${sbn} /tmp 67 | chown $user /tmp/${sbn} 68 | runfile="/tmp/${sbn}" 69 | else 70 | runfile="/etc/sdm/assets/runatboot/${sbn}" 71 | fi 72 | logger "sdm FirstBoot: Run runatboot script '${sbn}'${xstr}: \$runfile $args $output $error" 73 | chmod 755 \$runfile 74 | ${xdo}\$runfile $args $output $error 75 | EOF 76 | else 77 | logtobothex "? Plugin $pfx: Script file '$script' not found" 78 | fi 79 | logtoboth "* Plugin $pfx: Complete Phase 0" 80 | 81 | elif [ "$phase" == "1" ] 82 | then 83 | # 84 | # Phase 1 (in nspawn) 85 | # 86 | logtoboth "* Plugin $pfx: Start Phase 1" 87 | plugin_getargs $pfx "$allargs" "$vldargs" "$rqdargs" 88 | #logfreespace "at start of Plugin $pfx Phase 1" 89 | # 90 | 91 | # 92 | #logfreespace "at end of $pfx Phase 1" 93 | logtoboth "* Plugin $pfx: Complete Phase 1" 94 | else 95 | # 96 | # Plugin Post-install edits 97 | # 98 | logtoboth "* Plugin $pfx: Start Phase post-install" 99 | plugin_getargs $pfx "$allargs" "$vldargs" "$rqdargs" 100 | #logfreespace "at start of Plugin $pfx Phase post-install" 101 | # 102 | 103 | # 104 | #logfreespace "at end of $pfx Custom Phase post-install" 105 | logtoboth "* Plugin $pfx: Complete Phase post-install" 106 | fi 107 | -------------------------------------------------------------------------------- /plugins/runscript: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: runscript 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | function do_runscript() { 13 | # 14 | # $1: Phase to run 15 | local thisscript="$1" thisphase="$2" 16 | local line runphase rundir script user stdout stderr savehost 17 | local xdo="" xo="root" xu="" xdir="$assetdir" 18 | IFS="|" read script runphase rundir user stdout stderr < $assetdir/$thisscript-info 19 | if [[ "$runphase" == "$thisphase" ]] 20 | then 21 | savehost=$(hostname) 22 | if [ "$rundir" != "" ] 23 | then 24 | logtoboth "> Plugin $pfx: Create directory $rundir" 25 | mkdir -p $rundir 26 | setfileownmode $rundir 0755 $xo 27 | xdir="$rundir" 28 | else 29 | rundir=$assetdir 30 | fi 31 | [ "$stdout" == "" ] && stdout="$xdir/$script.out" 32 | [ "$stderr" == "" ] && stderr="$xdir/$script.error" 33 | if [ "$user" != "" ] 34 | then 35 | [ "$(getent passwd $user)" == "" ] && logtobothex "? Plugin $pfx: User '$user' not found" 36 | xu="as user $user" 37 | xdo="sudo -i -u $user" 38 | xo="$user" 39 | hostname $thishost 40 | fi 41 | cp $assetdir/$script /tmp 42 | [ "$user" != "" ] && setfileownmode /tmp/$script 0755 $xo 43 | logtoboth "> Plugin $pfx: Run script $script $xu" 44 | echo "[ Run script $script at $(date +"%Y-%m-%d") ]" >>$stdout 45 | echo "" >> $stdout 46 | echo "[ Run script $script at $(date +"%Y-%m-%d") ]" >>$stderr 47 | echo "" >> $stderr 48 | pushd $rundir >/dev/null 49 | ${xdo} /tmp/$script $thisphase >>$stdout 2>>$stderr || logtobothex "? Plugin $pfx: Script $script exited with error" 50 | popd >/dev/null 51 | hostname $savehost 52 | fi 53 | } 54 | 55 | # $1 is the phase: "0", "1", or "post-install" 56 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 57 | # 58 | # Main code for the Plugin 59 | # 60 | phase=$1 61 | pfx="$(basename $0)" #For messages 62 | allargs="$2" 63 | loadparams 64 | vldargs="|dir|runphase|script|user|stdout|stderr|" 65 | rqdargs="|script|" 66 | assetdir="$SDMPT/etc/sdm/assets/runscript" 67 | 68 | if [ "$phase" == "0" ] 69 | then 70 | # 71 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 72 | # 73 | logtoboth "* Plugin $pfx: Start Phase 0" 74 | plugin_getargs $pfx "$allargs" "$vldargs" "$rqdargs" || exit 75 | plugin_printkeys 76 | [ "$runphase" == "" ] && runphase="1" 77 | [[ "1|post-install" =~ "$runphase" ]] || logtobothex "? Plugin $pfx: Phase '$runphase' not recognized" 78 | mkdir -p $assetdir 79 | if [ -f $script ] 80 | then 81 | sbn=$(basename $script) 82 | cp -a $script $assetdir/${sbn} 83 | chmod 755 $assetdir/$sbn 84 | logtoboth "> Plugin $pfx: Script '$sbn' will be run during Phase '$runphase'" 85 | echo "${sbn}|$runphase|$dir|$user|$stdout|$stderr|" >> $assetdir/$sbn-info 86 | else 87 | logtobothex "? Plugin $pfx: Script file '$script' not found" 88 | fi 89 | logtoboth "* Plugin $pfx: Complete Phase 0" 90 | elif [ "$phase" == "1" ] 91 | then 92 | # 93 | # Phase 1 (in nspawn) 94 | # 95 | logtoboth "* Plugin $pfx: Start Phase 1" 96 | plugin_getargs $pfx "$allargs" "$vldargs" "$rqdargs" 97 | plugin_printkeys 98 | do_runscript $(basename $script) "$phase" 99 | logtoboth "* Plugin $pfx: Complete Phase 1" 100 | else 101 | # 102 | # Plugin Post-install edits 103 | # 104 | logtoboth "* Plugin $pfx: Start Phase post-install" 105 | plugin_getargs $pfx "$allargs" "$vldargs" "$rqdargs" 106 | plugin_printkeys 107 | do_runscript $(basename $script) "$phase" 108 | logtoboth "* Plugin $pfx: Complete Phase post-install" 109 | fi 110 | -------------------------------------------------------------------------------- /plugins/rxapp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: rxapp 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | loadparams 21 | 22 | if [ "$phase" == "0" ] 23 | then 24 | # 25 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 26 | # 27 | logtoboth "* Plugin $pfx: Start Phase 0" 28 | 29 | logtoboth "> Plugin $pfx: download rxapp from GitHub to /usr/local/bin/rxapp and set protection" 30 | curl -L https://raw.githubusercontent.com/gitbls/rxapp/master/rxapp -o $SDMPT/usr/local/bin/rxapp 31 | chmod 755 $SDMPT/usr/local/bin/rxapp 32 | 33 | logtoboth "* Plugin $pfx: Complete Phase 0" 34 | 35 | elif [ "$phase" == "1" ] 36 | then 37 | # 38 | # Phase 1 (in nspawn) 39 | # 40 | logtoboth "* Plugin $pfx: Start Phase 1" 41 | 42 | logtoboth "* Plugin $pfx: Complete Phase 1" 43 | else 44 | # 45 | # Plugin Post-install edits 46 | # 47 | logtoboth "* Plugin $pfx: Start Phase post-install" 48 | 49 | logtoboth "* Plugin $pfx: Complete Phase post-install" 50 | fi 51 | -------------------------------------------------------------------------------- /plugins/sdm-plugin-template: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: sample 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | loadparams 21 | vldargs="|list|of|valid|args|" 22 | rqdargs="" # |list|of|required|args|or|nullstring| 23 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 24 | 25 | if [ "$phase" == "0" ] 26 | then 27 | # 28 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 29 | # 30 | logtoboth "* Plugin $pfx: Start Phase 0" 31 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 32 | # 33 | # Print the keys found (example usage). plugin_getargs returns the list of found keys in $foundkeys 34 | # 35 | plugin_printkeys 36 | mkdir -p $assetdir 37 | plugin_dbgprint "This is how to do a Plugin Debug printout" # Will only be printed if --plugin-debug specified 38 | logtoboth "* Plugin $pfx: Complete Phase 0" 39 | elif [ "$phase" == "1" ] 40 | then 41 | # 42 | # Phase 1 (in nspawn) 43 | # 44 | logtoboth "* Plugin $pfx: Start Phase 1" 45 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 46 | #logfreespace "at start of Plugin $pfx Phase 1" 47 | # 48 | # INSERT your Plugin Phase 1 customization stuff here 49 | # In Phase 1 all references to directories in the image can be direct 50 | # 51 | #logfreespace "at end of $pfx Phase 1" 52 | logtoboth "* Plugin $pfx: Complete Phase 1" 53 | elif [ "$phase" == "post-install" ] 54 | then 55 | # 56 | # Plugin Post-install edits 57 | # 58 | logtoboth "* Plugin $pfx: Start Phase post-install" 59 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 60 | #logfreespace "at start of Plugin $pfx Phase post-install" 61 | # 62 | # INSERT Your Plugin's post-install code here 63 | # In Phase post-install all references to directories in the image can be direct 64 | # 65 | #logfreespace "at end of $pfx Custom Phase post-install" 66 | logtoboth "* Plugin $pfx: Complete Phase post-install" 67 | fi 68 | -------------------------------------------------------------------------------- /plugins/sshd: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: ssh 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | function checkargval() { 13 | # $1: argument 14 | # $2: value 15 | # $3: all|valid|values 16 | local arg=$1 val=$2 valids="$3" 17 | 18 | [[ "|$valids|" =~ "|$val|" ]] || logtobothex "? Plugin $pfx: Invalid value '$val' for argument '$arg'" 19 | } 20 | 21 | function converty() { 22 | [ "$enablesvc" == "" ] && enablesvc=yes # Set the default 23 | [[ -v address__family ]] && [[ "$address__family" == "" ]] && address__family="any" 24 | } 25 | 26 | # $1 is the phase: "0", "1", or "post-install" 27 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 28 | # 29 | # Main code for the Plugin 30 | # 31 | phase=$1 32 | pfx="$(basename $0)" #For messages 33 | args="$2" 34 | loadparams 35 | vldargs="|address-family|enablesvc|listen-address|password-authentication|port|" #"|list|of|valid|args|" 36 | rqdargs="" # |list|of|required|args|or|nullstring| 37 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 38 | 39 | if [ "$phase" == "0" ] 40 | then 41 | # 42 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 43 | # 44 | logtoboth "* Plugin $pfx: Start Phase 0" 45 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 46 | plugin_printkeys 47 | converty 48 | [ "$address__family" != "" ] && checkargval "address-family" "$address__family" "any|inet|inet6" 49 | [[ "$enablesvc" != "socket" ]] && checkargval "enablesvc" "$enablesvc" "yes|no" 50 | [ -v password__authentication ] && checkargval "password-authentication" "$password__authentication" "yes|no" 51 | logtoboth "* Plugin $pfx: Complete Phase 0" 52 | elif [ "$phase" == "1" ] 53 | then 54 | # 55 | # Phase 1 (in nspawn) 56 | # 57 | logtoboth "* Plugin $pfx: Start Phase 1" 58 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 59 | converty 60 | 61 | if [ "$enablesvc" == "yes" ] 62 | then 63 | sshd="service" 64 | else 65 | [ "$enablesvc" == "socket" ] && sshd="socket" || sshd="none" 66 | fi 67 | dosshsetup "$sshd" sshd 68 | # 69 | # Update sshd configuration based on inputs 70 | # 71 | # listen-address, password-authentication, port 72 | # 73 | cp /etc/ssh/sshd_config /etc/ssh/.sdm.sshd_config 74 | if [ "$address__family" != "" ] 75 | then 76 | logtoboth "> Plugin $pfx: Set sshd AddressFamily to '$address__family'" 77 | sed -i "/#AddressFamily any.*$/a AddressFamily $address__family" /etc/ssh/sshd_config 78 | fi 79 | if [ "$listen__address" != "" ] 80 | then 81 | logtoboth "> Plugin $pfx: Set sshd ListenAddress to '$listen__address'" 82 | sed -i "/#ListenAddress ::.*$/a ListenAddress $listen__address" /etc/ssh/sshd_config 83 | fi 84 | if [ "$password__authentication" != "" ] 85 | then 86 | logtoboth "> Plugin $pfx: Set sshd PasswordAuthentication to '$password__authentication'" 87 | sed -i "/#PasswordAuthentication yes.*$/a PasswordAuthentication $password__authentication" /etc/ssh/sshd_config 88 | fi 89 | if [ "$port" != "" ] 90 | then 91 | logtoboth "> Plugin $pfx: Set sshd Port to '$port'" 92 | sed -i "/#Port 22/a Port $port" /etc/ssh/sshd_config 93 | fi 94 | logtoboth "* Plugin $pfx: Complete Phase 1" 95 | elif [ "$phase" == "post-install" ] 96 | then 97 | # 98 | # Plugin Post-install edits 99 | # 100 | logtoboth "* Plugin $pfx: Start Phase post-install" 101 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 102 | logtoboth "* Plugin $pfx: Complete Phase post-install" 103 | fi 104 | -------------------------------------------------------------------------------- /plugins/sshhostkey: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: sshhostkey 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | loadparams 21 | vldargs="|import-keys|generate-keys|" 22 | rqdargs="" # |list|of|required|args|or|nullstring| 23 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 24 | 25 | if [ "$phase" == "0" ] 26 | then 27 | # 28 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 29 | # 30 | logtoboth "* Plugin $pfx: Start Phase 0" 31 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 32 | plugin_printkeys 33 | 34 | mkdir -p $assetdir 35 | if ! [[ -v import__keys ]] && ! [[ -v generate__keys ]] 36 | then 37 | logtobothex "? Plugin $pfx: At least one of 'import-keys' or 'generate-keys' must be specified" 38 | fi 39 | if [ "$import__keys" != "" ] 40 | then 41 | [ -d $import__keys ] || logtobothex "? Plugin $pfx: import-keys directory '$import__keys' not found" 42 | logtoboth "> Plugin $pfx: Copy keys from import-keys directory '$import__keys' to $assetdir" 43 | compgen -G "$import__keys/ssh_host_*_key" >/dev/null && cp -a $import__keys/ssh_host_*_key $assetdir 44 | compgen -G "$import__keys/ssh_host_*_key.pub" >/dev/null && cp -a $import__keys/ssh_host_*_key.pub $assetdir 45 | fi 46 | logtoboth "* Plugin $pfx: Complete Phase 0" 47 | elif [ "$phase" == "1" ] 48 | then 49 | # 50 | # Phase 1 (in nspawn) 51 | # 52 | logtoboth "* Plugin $pfx: Start Phase 1" 53 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 54 | #logfreespace "at start of Plugin $pfx Phase 1" 55 | 56 | # We allow both generate-keys and import-keys at the same time. 57 | # This enables partial overwrites (e.g. import the RSA key, but re-gen everything else) 58 | if [ -v generate__keys ] 59 | then 60 | logtoboth "> Plugin $pfx: Delete existing SSH host keys and generate new keys" 61 | rm -f /etc/ssh/ssh_host_*_key /etc/ssh/ssh_host_*_key.pub 62 | ssh-keygen -v -A > $assetdir/keygen.log 2>&1 63 | [ $? -ne 0 ] && logtobothex "? Plugin $pfx: ssh-keygen returned an error; see $assetdir/keygen.log" 64 | fi 65 | if [ "$import__keys" != "" ] 66 | then 67 | logtoboth "> Plugin $pfx: Copy SSH host keys from $assetdir to /etc/ssh" 68 | compgen -G "$assetdir/ssh_host_*_key" >/dev/null && cp -a $assetdir/ssh_host_*_key /etc/ssh && setfileownmode "/etc/ssh/ssh_host_*_key" 600 root:root 69 | compgen -G "$assetdir/ssh_host_*_key.pub" >/dev/null && cp -a $assetdir/ssh_host_*_key.pub /etc/ssh && setfileownmode "/etc/ssh/ssh_host_*_key.pub" 644 root:root 70 | fi 71 | # 72 | #logfreespace "at end of $pfx Phase 1" 73 | logtoboth "* Plugin $pfx: Complete Phase 1" 74 | elif [ "$phase" == "post-install" ] 75 | then 76 | # 77 | # Plugin Post-install edits 78 | # 79 | logtoboth "* Plugin $pfx: Start Phase post-install" 80 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 81 | #logfreespace "at start of Plugin $pfx Phase post-install" 82 | # 83 | 84 | # 85 | #logfreespace "at end of $pfx Custom Phase post-install" 86 | logtoboth "* Plugin $pfx: Complete Phase post-install" 87 | fi 88 | -------------------------------------------------------------------------------- /plugins/sshkey: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: sshkeys 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | function makesshdir() { 13 | # 14 | # Ensure user's .ssh directory exists and is properly owned/protected 15 | # 16 | mkdir -p $xhdir/.ssh 17 | setfileownmode $xhdir/.ssh 700 $sshuser:$xgx 18 | } 19 | 20 | # $1 is the phase: "0", "1", or "post-install" 21 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 22 | # 23 | # Main code for the Plugin 24 | # 25 | phase=$1 26 | pfx="$(basename $0)" #For messages 27 | args="$2" 28 | loadparams 29 | vldargs="|sshuser|authkey|import-key|keyname|keytype|passphrase|putty-keyname|" 30 | rqdargs="sshuser" # |list|of|required|args|or|nullstring| 31 | vkeytypes="ecdsa|ecdsa-sk|ed25519|ed25519-sk|rsa|" 32 | redactargs="passphrase" 33 | assetdir="$SDMPT/etc/sdm/assets/$pfx" 34 | 35 | if [ "$phase" == "0" ] 36 | then 37 | # 38 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 39 | # 40 | logtoboth "* Plugin $pfx: Start Phase 0" 41 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 42 | 43 | assetdir="$assetdir/$sshuser" 44 | mkdir -p $assetdir 45 | plugin_printkeys "$redactargs" 46 | if [ "$import__key" != "" ] 47 | then 48 | [ -f $import__key ] || logtobothex "? Plugin $pfx: Import key file '$import__key' not found" 49 | logtoboth "> Plugin $pfx: Copy import-key file '$import__key' to $assetdir" 50 | cp $import__key $assetdir 51 | fi 52 | [ "$keytype" == "" ] && keytype=ecdsa 53 | [[ "$vkeytypes" =~ "$keytype|" ]] || logtobothex "> Plugin $pfx: Unrecognized ssh key type '$keytype'" 54 | logtoboth "* Plugin $pfx: Complete Phase 0" 55 | elif [ "$phase" == "1" ] 56 | then 57 | # 58 | # Phase 1 (in nspawn) 59 | # 60 | logtoboth "* Plugin $pfx: Start Phase 1" 61 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 62 | #logfreespace "at start of Plugin $pfx Phase 1" 63 | # 64 | assetdir="$assetdir/$sshuser" 65 | IFS=":" read xuser xpw xuid xgx xname xhdir rest <<< $(getent passwd $sshuser) 66 | [ "$keyname" == "" ] && keyname=sshkey 67 | if [ "$putty__keyname" != "" ] 68 | then 69 | installpkgsif putty-tools 70 | putty__keyname="${putty__keyname%.ppk}.ppk" 71 | fi 72 | 73 | if [ "$import__key" == "" ] 74 | then 75 | [ "$keytype" == "" ] && keytype=ecdsa 76 | [ -v authkey ] && authkey=y 77 | [ -n "$authkey" ] && aks=" and add public key to authorized_keys" 78 | logtoboth "> Plugin $pfx: Create key '$keyname' type '$keytype' for user '$sshuser'$aks" 79 | makesshdir 80 | rm -f $xhdir/.ssh/$keyname $xhdir/.ssh/$keyname.pub 81 | ssh-keygen -C $keyname -f $xhdir/.ssh/$keyname -t $keytype -N "$passphrase" <<< \$'y' >$assetdir/keygen-$keyname.log 2>&1 82 | setfileownmode $xhdir/.ssh/$keyname 600 $xuser:$xgx 83 | setfileownmode $xhdir/.ssh/$keyname.pub 644 $xuser:$xgx 84 | [ $? -ne 0 ] && logtobothex "? Plugin $pfx: ssh-keygen returned an error; see $assetdir/keygen-$keyname.log" 85 | [ "$authkey" != "" ] && cat $xhdir/.ssh/$keyname.pub >> $xhdir/.ssh/authorized_keys && setfileownmode $xhdir/.ssh/authorized_keys 600 $xuser:$xgx 86 | else 87 | ikfn="$(basename $import__key)" 88 | logtoboth "> Plugin $pfx: Copy SSH key '$(basename $import__key)' from $assetdir to user '$sshuser' .ssh directory" 89 | makesshdir 90 | cp $assetdir/$ikfn $xhdir/.ssh 91 | setfileownmode $xhdir/.ssh/$ikfn 600 $sshuser:$xgx 92 | keyname=$(basename $import__key) 93 | fi 94 | if [ "$putty__keyname" != "" ] 95 | then 96 | [ "$passphrase" != "" ] && echo "$passphrase" >/tmp/pw.tmp && opswitch="--old-passphrase /tmp/pw.tmp" 97 | logtoboth "> Plugin $pfx: Create putty private key '$putty__keyname'" 98 | installpkgsif putty-tools 99 | puttygen $xhdir/.ssh/$keyname -O private -o $xhdir/.ssh/$putty__keyname $opswitch 100 | setfileownmode $xhdir/.ssh/$putty__keyname 600 $xuser:$xgx 101 | fi 102 | # 103 | #logfreespace "at end of $pfx Phase 1" 104 | logtoboth "* Plugin $pfx: Complete Phase 1" 105 | elif [ "$phase" == "post-install" ] 106 | then 107 | # 108 | # Plugin Post-install edits 109 | # 110 | logtoboth "* Plugin $pfx: Start Phase post-install" 111 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 112 | #logfreespace "at start of Plugin $pfx Phase post-install" 113 | # 114 | 115 | # 116 | #logfreespace "at end of $pfx Custom Phase post-install" 117 | logtoboth "* Plugin $pfx: Complete Phase post-install" 118 | fi 119 | 120 | -------------------------------------------------------------------------------- /plugins/ufw: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: ufw 4 | # 5 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 6 | # 7 | 8 | function loadparams() { 9 | source $SDMPT/etc/sdm/sdm-readparams 10 | } 11 | 12 | # $1 is the phase: "0", "1", or "post-install" 13 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 14 | # 15 | # Main code for the Plugin 16 | # 17 | phase=$1 18 | pfx="$(basename $0)" #For messages 19 | args="$2" 20 | loadparams 21 | vldargs="|ufwscript|savescriptdir|" 22 | rqdargs="" 23 | assetdir="$SDMPT/etc/sdm/assets/ufw" 24 | 25 | if [ "$phase" == "0" ] 26 | then 27 | # 28 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 29 | # 30 | logtoboth "* Plugin $pfx: Start Phase 0" 31 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 32 | plugin_printkeys 33 | mkdir -p $assetdir 34 | if [ "$ufwscript" != "" ] 35 | then 36 | IFS="," read -a citems <<< "$ufwscript" 37 | for c in "${citems[@]}" 38 | do 39 | if [ -f $c ] 40 | then 41 | bnc=$(basename $c) 42 | logtoboth "> Plugin $pfx: copy ufwscript $c to $assetdir/$bnc" 43 | cp -a $c $assetdir/$bnc 44 | setfileownmode $assetdir/$bnc 755 # We'll run these during firstboot 45 | [ "$savescriptdir" == "" ] && savescriptdir="/usr/local/bin" 46 | logtoboth "> Plugin $pfx: Save ufwscript $c in $savescriptdir for normal use" 47 | cp -a $c $SDMPT/$savescriptdir/$bnc 48 | setfileownmode $SDMPT/$savescriptdir/$bnc 755 49 | else 50 | logtobothex "? Plugin $pfx: Cannot find ufwscript '$c'" 51 | fi 52 | done 53 | else 54 | logtoboth "% Plugin $pfx: No ufwscript provided; ufw firewall will deny all incoming traffic" 55 | fi 56 | logtoboth "> Plugin $pfx: Defer final ufw configuration/enable to sdm FirstBoot" 57 | cat > $SDMPT/etc/sdm/0piboot/090-ufw.sh < /dev/null 61 | then 62 | for cufw in /etc/sdm/assets/ufw/* 63 | do 64 | logger "sdm FirstBoot: Run ufwscript \$cufw" 65 | \$cufw 66 | done 67 | fi 68 | ufw enable 69 | [ \$? -ne 0 ] && logger "sdm FirstBoot: ? ufw enable failed" 70 | EOF 71 | logtoboth "* Plugin $pfx: Complete Phase post-install" 72 | #plugin_dbgprint "This is how to do a Plugin Debug printout" # Will only be printed if --plugin-debug specified 73 | logtoboth "* Plugin $pfx: Complete Phase 0" 74 | 75 | elif [ "$phase" == "1" ] 76 | then 77 | # 78 | # Phase 1 (in nspawn) 79 | # 80 | logtoboth "* Plugin $pfx: Start Phase 1" 81 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 82 | #logfreespace "at start of Plugin $pfx Phase 1" 83 | # 84 | logtoboth "> Plugin $pfx: install ufw" 85 | installpkgsif ufw 86 | # 87 | #logfreespace "at end of $pfx Phase 1" 88 | logtoboth "* Plugin $pfx: Complete Phase 1" 89 | else 90 | # 91 | # Plugin Post-install edits 92 | # 93 | logtoboth "* Plugin $pfx: Start Phase post-install" 94 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 95 | logtoboth "* Plugin $pfx: Complete Phase post-install" 96 | fi 97 | -------------------------------------------------------------------------------- /plugins/wificonfig: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This is an sdm plugin for: wificonfig 4 | # 5 | # wificonfig runs a Captive Portal at system First Boot to gather and test WiFi Credentials 6 | # 7 | # The plugin is called three times: for Phase 0, Phase 1, and post-install. 8 | # 9 | 10 | function loadparams() { 11 | source $SDMPT/etc/sdm/sdm-readparams 12 | } 13 | 14 | # $1 is the phase: "0", "1", or "post-install" 15 | # $2 is the argument list: arg1=val1|arg2=val2|arg3=val3| ... 16 | # 17 | # Main code for the Plugin 18 | # 19 | phase=$1 20 | pfx="$(basename $0)" #For messages 21 | args="$2" 22 | vldargs="|apssid|apip|country|defaults|facility|retries|timeout|wifilog|" 23 | rqdargs="" 24 | loadparams 25 | assetdir="$SDMPT/etc/sdm/wificonfig" 26 | 27 | if [ "$phase" == "0" ] 28 | then 29 | # 30 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 31 | # 32 | logtoboth "* Plugin $pfx: Start Phase 0" 33 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" || exit 34 | # 35 | # Print the keys found (example usage). plugin_getargs returns the list of found keys in $foundkeys 36 | # 37 | plugin_printkeys 38 | # Check keys validity: defaults, country, retries, timeout 39 | mkdir -p $assetdir 40 | if [ "$defaults" != "" ] 41 | then 42 | if [ -f $defaults ] 43 | then 44 | logtoboth "> Plugin $pfx: Copy defaults file '$defaults' to IMG $assetdir/wificonfig-defaults" 45 | cp $defaults $assetdir/wificonfig-defaults 46 | else 47 | logtoboth "% Plugin $pfx: Defaults file '$defaults' not found; ignoring" 48 | fi 49 | fi 50 | mustfix=0 51 | if [ "$country" != "" ] 52 | then 53 | ! (ckwificountry ${country^^}) && logtoboth "% Plugin $pfx: Unrecognized Country key value '$country'" && mustfix=1 54 | fi 55 | if [ "$retries" != "" ] 56 | then 57 | [ "$((retries))" == 0 ] && logtoboth "% Plugin $pfx: Invalid retries key value '$retries'" && mustfix=1 58 | fi 59 | if [ "$timeout" != "" ] 60 | then 61 | [ "$((timeout))" == 0 ] && logtoboth "% Plugin $pfx: Invalid timeout key value '$timeout'" && mustfix=1 62 | fi 63 | [ $mustfix -eq 1 ] && logtoboth "? Plugin $pfx: Above key value errors MUST be corrected to ensure proper operation" 64 | logtoboth "* Plugin $pfx: Complete Phase 0" 65 | 66 | elif [ "$phase" == "1" ] 67 | then 68 | # 69 | # Phase 1 (in nspawn) 70 | # 71 | logtoboth "* Plugin $pfx: Start Phase 1" 72 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 73 | #logfreespace "at start of Plugin $pfx Phase 1" 74 | 75 | #logfreespace "at end of $pfx Phase 1" 76 | logtoboth "* Plugin $pfx: Complete Phase 1" 77 | else 78 | # 79 | # Plugin Post-install edits 80 | # 81 | logtoboth "* Plugin $pfx: Start Phase post-install" 82 | plugin_getargs $pfx "$args" "$vldargs" "$rqdargs" 83 | #logfreespace "at start of Plugin $pfx Phase post-install" 84 | # Undo sdm disable wifi if done 85 | [ -f /etc/sdm/0piboot/055-disable-wifi.sh ] && mv /etc/sdm/0piboot/055-disable-wifi.sh /etc/sdm/0piboot/.055-disable-wifi.sh-wificonfig 86 | # Configure for FirstBoot: Set loadlocal="plugin" and create plugin-wificonfig.sh 87 | switches="" 88 | [ "$apssid" != "" ] && switches="$switches --apssid $apssid" 89 | [ "$apip" != "" ] && switches="$switches --apip $apip" 90 | [ "$country" != "" ] && switches="$switches --country $country" 91 | if [ "$defaults" != "" ] 92 | then 93 | [ -f $assetdir/wificonfig-defaults ] && switches="$switches --defaults $assetdir/wificonfig-defaults" 94 | fi 95 | [ "$facility" != "" ] && switches="$switches --facility $facility" 96 | [ "$retries" != "" ] && switches="$switches --retries $retries" 97 | [ "$timeout" != "" ] && switches="$switches --timeout $timeout" 98 | [ "$wifilog" == "" ] && wifilog="$assetdir/wifi-config.log" 99 | [ "$wifilog" != "" ] && dbgout=">$wifilog 2>&1" || dbgout="" 100 | writeconfig 101 | cat > /etc/sdm/xpiboot/020-plugin-wificonfig.sh < Plugin $pfx: Copy wsdd from '$localsrc'" 47 | cp -a $localsrc/wsdd.py $SDMPT/usr/bin/wsdd 48 | setfileownmode $SDMPT/usr/bin/wsdd 755 49 | cp -a $localsrc/wsdd.8 $SDMPT/usr/share/man/man8/wsdd.i 50 | cp -a $localsrc/wsdd.defaults $SDMPT/etc/default/wsdd 51 | cp -a $localsrc/wsdd.service $SDMPT/etc/systemd/system/wsdd.service 52 | else 53 | logtoboth "> Plugin $pfx: download wsdd from GitHub" 54 | 55 | logtoboth " curl -L https://github.com/christgau/wsdd/raw/master/src/wsdd.py -o $SDMPT/usr/bin/wsdd" 56 | curl -L https://github.com/christgau/wsdd/raw/master/src/wsdd.py -o $SDMPT/usr/bin/wsdd 57 | 58 | logtoboth " curl -L https://github.com/christgau/wsdd/raw/master/man/wsdd.8 -o $SDMPT/usr/share/man/man8/wsdd.8" 59 | curl -L https://github.com/christgau/wsdd/raw/master/man/wsdd.8 -o $SDMPT/usr/share/man/man8/wsdd.8 60 | 61 | logtoboth " curl -L https://github.com/christgau/wsdd/raw/master/etc/systemd/wsdd.defaults -o $SDMPT/etc/default/wsdd" 62 | curl -L https://github.com/christgau/wsdd/raw/master/etc/systemd/wsdd.defaults -o $SDMPT/etc/default/wsdd 63 | 64 | logtoboth " curl -L https://github.com/christgau/wsdd/raw/master/etc/systemd/wsdd.service -o $SDMPT/etc/systemd/system/wsdd.service" 65 | curl -L https://github.com/christgau/wsdd/raw/master/etc/systemd/wsdd.service -o $SDMPT/etc/systemd/system/wsdd.service 66 | fi 67 | chmod 755 $SDMPT/usr/bin/wsdd 68 | logtoboth "> Plugin $pfx: Configure '/etc/default/wsdd'" 69 | sed -i "s/WSDD_PARAMS=\"\"/WSDD_PARAMS=\"--user wsdd:wsdd --shortlog $wsddswitches\"/" $SDMPT/etc/default/wsdd 70 | logtoboth "* Plugin $pfx: Complete Phase 0" 71 | 72 | elif [ "$phase" == "1" ] 73 | then 74 | # 75 | # Phase 1 (in nspawn) 76 | # 77 | logtoboth "* Plugin $pfx: Start Phase 1" 78 | if [ ${raspiosver} -ge 12 ] 79 | then 80 | logtoboth "> Plugin $pfx: Install wsdd" 81 | installpkgsif wsdd 82 | exit 0 83 | else 84 | logtoboth "* Plugin $pfx: Add unprivileged user 'wsdd' in group 'wsdd'" 85 | groupadd --system wsdd 86 | useradd --no-create-home --system --gid wsdd wsdd 87 | logtoboth "* Plugin $pfx: Enable wsdd service" 88 | systemctl enable wsdd > /dev/null 2>&1 89 | logtoboth "* Plugin $pfx: Complete Phase 1" 90 | fi 91 | else 92 | # 93 | # Plugin Post-install edits 94 | # 95 | logtoboth "* Plugin $pfx: Start Phase post-install" 96 | 97 | logtoboth "* Plugin $pfx: Complete Phase post-install" 98 | fi 99 | -------------------------------------------------------------------------------- /sdm-add-luks-key: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Add the specified LUKS encryption key to an encrypted rootfs 4 | # 5 | function pmsg() { 6 | echo "$1" 7 | } 8 | 9 | function errexit() { 10 | echo -e "$1" 11 | exit 1 12 | } 13 | 14 | function usage() { 15 | echo $"Usage: sudo $0 /path/to/keyfile.lek" 16 | exit 1 17 | } 18 | 19 | keyfn=$1 20 | [ "$keyfn" == "" ] && usage 21 | 22 | [ "$(type -p cryptsetup)" == "" ] && errexit "? cryptsetup not found; Does this host have an encrypted rootfs?" 23 | kfn=$(basename $keyfn) 24 | # 25 | # Add to running system 26 | # 27 | if [ ! -f /bin/sdmluksunlock ] 28 | then 29 | cat > /bin/sdmluksunlock </dev/null; then 40 | if [ -e /mnt/\$CRYPTTAB_KEY.lek ]; then 41 | cat /mnt/\$CRYPTTAB_KEY.lek 42 | umount \$usbdevice || continue 43 | exit 44 | fi 45 | umount \$usbdevice || continue 46 | fi 47 | done 48 | return 0 49 | } 50 | 51 | if [ ! -e /mnt ]; then 52 | mkdir -p /mnt 53 | fi 54 | if [ "\$CRYPTTAB_TRIED" == "0" ] ; then 55 | sleep 4 # Wait a bit for disk to appear 56 | fi 57 | set +e 58 | trydisks 59 | kbd=\$(dmesg | grep -i keyboard | grep -v keyboard-setup) 60 | set -e 61 | if [ "\$kbd" != "" ] ; then 62 | trydisks 63 | /lib/cryptsetup/askpass "Insert USB Keyfile Disk (or type passphrase) then press ENTER: " 64 | else 65 | while :; do 66 | set +e 67 | kbd=\$(dmesg | grep -i keyboard | grep -v keyboard-setup) 68 | set -e 69 | [ "\$kbd" != "" ] && break 70 | trydisks 71 | echo "Insert USB Keyfile Disk" >/dev/console 72 | sleep 1 73 | done 74 | /lib/cryptsetup/askpass "Insert USB Keyfile Disk (or type passphrase) then press ENTER: " 75 | fi 76 | EOF 77 | chmod 755 /bin/sdmluksunlock 78 | fi 79 | 80 | # Update /etc/crypttab 81 | 82 | mapname=$(findmnt --noheadings --output source /) 83 | mapname=$(basename $mapname) 84 | read mapname rootfs rest <<< "$(grep $mapname /etc/crypttab)" 85 | kfuuid=${kfn%.lek} 86 | pmsg "> Update /etc/crypttab for USB unlock" 87 | cp /etc/crypttab /etc/crypttab.orig.sdm 88 | sed -i /etc/crypttab -e "s|$mapname.*|$mapname $rootfs $kfuuid luks,discard,keyscript=/bin/sdmluksunlock|" 89 | 90 | pmsg "> Add LUKS key '$kfn' from file $keyfn" 91 | pmsg " When prompted for a passphrase use the one you created when you encrypted the rootfs" 92 | pmsg " NOTE: This operation may take some time..." 93 | cryptsetup luksAddKey $rootfs $keyfn 94 | [ $? -ne 0 ] && errexit "? cryptsetup returned an error adding key '$keyfn': $?" 95 | pmsg "> LUKS key successfully added" 96 | pmsg "" 97 | pmsg "> Configure initramfs" 98 | echo "$kfn" > /etc/sdmkeyfile 99 | update-initramfs -u 100 | pmsg "" 101 | pmsg "> Reboot the system with the USB key disk in a drive" 102 | -------------------------------------------------------------------------------- /sdm-apps-example: -------------------------------------------------------------------------------- 1 | # This file lists all the apps you want installed into your system IMG by sdm 2 | # A line can be 3 | # A blank line 4 | # Comment line (everything following # is ignored) 5 | # The name of a single package 6 | # 7 | 8 | # Some editors 9 | vim 10 | emacs 11 | at-spi2-core # Needed for X windows emacs 12 | 13 | # Network utilities 14 | dnsutils 15 | lftp 16 | ncftp 17 | putty 18 | # Miscellaneous tools and system utilities 19 | aptitude 20 | qpdfview 21 | avahi-utils 22 | python3-pip 23 | uuid-runtime 24 | tmux 25 | mc 26 | iftop 27 | nfs-kernel-server 28 | whois 29 | lsof 30 | iperf3 31 | zip 32 | nmap 33 | feh 34 | links2 35 | systemd-container 36 | zerofree 37 | -------------------------------------------------------------------------------- /sdm-apt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Perform apt commands inside the nspawn and log the results 4 | # apt commands supported include: update, upgrade, install, remove 5 | # 6 | # $1: 0 to not show apt output on console 7 | # 1 show apt output on console 8 | # $2-$8: words of the apt command 9 | # 10 | # Examples: 11 | # sdm-apt 0 install "emacs vim putty" 12 | # sdm-apt 0 "install --no-install-recommends --yes emacs vim putty" 13 | # 14 | # Read configuration information from sdm (/etc/sdm/cparams) 15 | # 16 | source /etc/sdm/sdm-readparams 17 | 18 | logtoboth "* Start manual apt operation" 19 | logtoboth "> apt command: $2 $3 $4 $5 $6 $7 $8" 20 | 21 | doapt "$2 $3 $4 $5 $6 $7 $8" "$1" 22 | 23 | logtoboth "* Manual apt operation completed" 24 | -------------------------------------------------------------------------------- /sdm-apt-cacher: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | econf=0 # 0 to edit /etc/apt/apt.conf.d/02proxy (Cleaner, but not visible on terminal) 4 | #!0 to edit /etc/apt/sources.list and /etc/apt/sources.list.d/raspi.list (more intrusive, but visible on terminal) 5 | 6 | function enablecacher () { 7 | 8 | if [ $econf -eq 0 ] 9 | then 10 | [ ! -f /etc/apt/apt.conf.d/02proxy ] && echo "Acquire::http::proxy \"http://$1\";" >> /etc/apt/apt.conf.d/02proxy 11 | else 12 | if ! (grep $1 /etc/apt/sources.list > /dev/null 2>&1) 13 | then 14 | sudo sed -i "s/http:\/\//http:\/\/$1\//" /etc/apt/sources.list 15 | sudo sed -i "s/http:\/\//http:\/\/$1\//" /etc/apt/sources.list.d/raspi.list 16 | fi 17 | fi 18 | } 19 | 20 | [ "$2" != "" ] && serverip=$2 21 | port=3142 22 | mode="$1" 23 | [ "$mode" == "" ] && mode=client && echo "% 'client' or 'server' not specified; assuming 'client'" 24 | 25 | if [ "$mode" == "client" ] 26 | then 27 | enablecacher $serverip:$port # Point the client to the apt caching server 28 | else 29 | echo "% Setting up apt caching server" 30 | echo "% Answer NO to enabling HTTPS tunnels" 31 | sudo apt install -y apt-cacher-ng 32 | enablecacher 127.0.0.1:$port 33 | sudo rm -rf /var/lib/apt/lists 34 | sudo rm -rf /var/cache/apt/* 35 | fi 36 | 37 | # 38 | # Resetting the apt-cacher-ng server cache 39 | # 40 | # No need to do it regularly unless disk space or other issues 41 | # 42 | # sudo systemctl stop apt-cacher-ng 43 | # sudo rm -rf /var/cache/apt-cacher-ng 44 | # sudo mkdir -p /var/cache/apt-cacher-ng 45 | # sudo chown -R apt-cacher-ng:apt-cacher-ng /var/cache/apt-cacher-ng 46 | # sudo systemctl start apt-cacher-ng 47 | # 48 | -------------------------------------------------------------------------------- /sdm-collect-labwc-config: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Picks up all the relevant config files so your labwc config can be 4 | # easily transported to a new system using sdm's labwc config 5 | # 6 | 7 | function usage() { 8 | echo $"Usage: $0 /path/to/savedir" 9 | exit 10 | } 11 | 12 | function askyn() { 13 | local ans 14 | echo -n "$1" '[y/n]? ' ; read $2 ans 15 | case "$ans" in 16 | y*|Y*) return 0 ;; 17 | *) return 1 ;; 18 | esac 19 | } 20 | 21 | config=$HOME/.config 22 | destdir="$1" 23 | [ "$destdir" == "" ] && echo "> Output directory not specified; Using /tmp/labwc" && destdir=/tmp/labwc 24 | if [ -d $destdir ] && ! [[ "$destdir" =~ ^/tmp ]] 25 | then 26 | echo "** Directory '$destdir' exists" 27 | askyn "Remove and recreate directory '$destdir'" || exit 1 28 | fi 29 | echo "> Delete and re-create '$destdir'" 30 | rm -rf $destdir 31 | mkdir -p $destdir 32 | 33 | for f in autostart environment menu.xml rc.xml shutdown themerc 34 | do 35 | if [ -f $config/labwc/$f ] 36 | then 37 | echo "> Copy '$config/labwc/$f' to '$destdir'" 38 | cp -a $config/labwc/$f $destdir 39 | fi 40 | done 41 | 42 | for f in bookmarks desktop-items libfm pcmanfm lxterminal wf-panel-pi 43 | do 44 | case $f in 45 | bookmarks) 46 | if [ -f $HOME/.gtk-bookmarks ] 47 | then 48 | echo "> Copy '$HOME/.gtk-bookmarks' to '$destdir/bookmarks'" 49 | cp -a $HOME/.gtk-bookmarks $destdir/bookmarks 50 | fi 51 | ;; 52 | desktop-items) 53 | if [ "$(compgen -G "$config/pcmanfm/LXDE-pi/desktop-items*.conf")" != "" ] 54 | then 55 | echo "> Copy '$config/pcmanfm/LXDE-PI/desktop-items*.conf' to '$destdir'" 56 | cp -a $config/pcmanfm/LXDE-pi/desktop-items*.conf $destdir 57 | fi 58 | ;; 59 | libfm|lxterminal) 60 | if [ -f $config/$f/$f.conf ] 61 | then 62 | echo "> Copy '$config/$f/$f.conf' to '$destdir'" 63 | cp -a $config/$f/$f.conf $destdir 64 | fi 65 | ;; 66 | pcmanfm) 67 | if [ -f $config/pcmanfm/LXDE-pi/pcmanfm.conf ] 68 | then 69 | echo "> Copy '$config/pcmanfm/LXDE-pi/pcmanfm.conf' to '$destdir'" 70 | cp -a $config/pcmanfm/LXDE-pi/pcmanfm.conf $destdir 71 | fi 72 | ;; 73 | wf-panel-pi) 74 | if [ -f $config/wf-panel-pi.ini ] 75 | then 76 | echo "> Copy '$config/wf-panel-pi.ini' to '$destdir'" 77 | cp -a $config/wf-panel-pi.ini $destdir 78 | fi 79 | esac 80 | done 81 | if [ -f $config/kanshi/config ] 82 | then 83 | echo "> Copy '$config/kanshi/config' to '$destdir'" 84 | cp -a $config/kanshi/config $destdir/kanshi.conf 85 | fi 86 | echo $" 87 | Copy the directory '$destdir' to the system where you use sdm. 88 | 89 | Then use the labwc plugin to customize new systems: 90 | 91 | --plugin labwc:\"all-config=/path/to/the/dir\" 92 | " 93 | 94 | exit 95 | -------------------------------------------------------------------------------- /sdm-customphase: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Sample sdm Custom Phase script 4 | # 5 | function loadparams() { 6 | source $SDMPT/etc/sdm/sdm-readparams 7 | } 8 | # $1 is the phase: "0", "1", or "post-install" 9 | 10 | # 11 | # Main code for the script 12 | # 13 | phase=$1 14 | pfx="$(basename $0)" #For messages 15 | loadparams 16 | 17 | if [ $phase == "0" ] 18 | then 19 | # 20 | # In Phase 0 all references to directories in the image must be preceded by $SDMPT 21 | # 22 | logtoboth "* $pfx Phase 0" 23 | 24 | # INSERT Your Custom Phase 0 code here 25 | 26 | logtoboth "* $pfx Phase 0 Completed" 27 | 28 | elif [ "$phase" == "1" ] 29 | then 30 | # 31 | # Phase 1 (in nspawn) 32 | # 33 | logtoboth "* $pfx Phase 1" 34 | logfreespace "at start of $pfx Phase 1" 35 | # 36 | # INSERT your Custom Phase 1 customization stuff here 37 | # In Phase 1 all references to directories in the image can be direct 38 | # 39 | logfreespace "at end of $pfx Phase 1" 40 | logtoboth "* $pfx Phase 1 Completed" 41 | else 42 | # 43 | # Post-install edits 44 | # 45 | logtoboth "* $pfx Custom Phase post-install" 46 | logfreespace "at start of $pfx Custom Phase post-install" 47 | # 48 | # INSERT Your Custom Phase post-install code here 49 | # In Phase post-install all references to directories in the image can be direct 50 | # 51 | 52 | logfreespace "at end of $pfx Custom Phase post-install" 53 | logtoboth "* $pfx Custom Phase post-install Completed" 54 | fi 55 | -------------------------------------------------------------------------------- /sdm-logmsg: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Script called by sdm-cportal to log boot-time messages 4 | # 5 | # $1: Message to write 6 | # 7 | source /etc/sdm/sdm-readparams 8 | bootlog "$1" 9 | -------------------------------------------------------------------------------- /sdm-phase0: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Read configuration information from sdm (/etc/sdm/cparams) 5 | # 6 | source $SDMPT/etc/sdm/sdm-readparams 7 | 8 | logtoboth "* Start Phase 0 image customization" 9 | 10 | # 11 | # Copy b1script into the IMG if specified 12 | # 13 | if [ "$b1script" != "" ] 14 | then 15 | logtoboth "> Copy --b1script '$b1script' to $SDMPT/etc/sdm/assets" 16 | cp $b1script $SDMPT/etc/sdm/assets 17 | setfileownmode $SDMPT/etc/sdm/assets/$(basename $b1script) 18 | fi 19 | # 20 | # Run custom Phase script Phase 0 21 | # 22 | if [ "$cscript" != "" ] 23 | then 24 | csfn="$SDMPT${sdmdir}/$(basename $cscript)" 25 | logtoboth "> Run Custom Phase Script '$csfn' Phase 0" 26 | $csfn 0 || exit 27 | fi 28 | # 29 | # Run plugins Phase 0 30 | # 31 | runplugins "$plugins" 0 || exit 32 | 33 | logtoboth "* Phase 0 Completed" 34 | -------------------------------------------------------------------------------- /sdm-readparams: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This file is sourced by other sdm scripts 4 | # 5 | function readparams() { 6 | # 7 | # Restores the global variables from /etc/sdm/cparams 8 | # 9 | local rpifun value cf="$SDMPT/etc/sdm/cparams" snolq 10 | if [ -f $cf ] 11 | then 12 | while IFS=":" read -r rpifun value 13 | do 14 | if [[ ! $rpifun =~ ^\ *# && -n $rpifun ]] # skip comment and malformed lines 15 | then 16 | # Cant' use stripquotes, it's in sdm-cparse, and can't source it w/o processing this first ($sdmdir) 17 | #value="${value%%\#*}" # Del EOL comments 18 | value="${value%"${value##*[^[:blank:]]}"}" # Del trailing spaces/tabs 19 | snolq="${value#\"}" # Get string without open quote 20 | if [ "$snolq" != "$value" ] # if lq was there, then update string and also del close quote 21 | then 22 | value=$snolq 23 | value="${value%\"}" 24 | fi 25 | snolq="${value#\'}" # Get string without open quote 26 | if [ "$snolq" != "$value" ] # Ditto double quote comment 27 | then 28 | value=$snolq 29 | value="${value%\'}" 30 | fi 31 | printf -v "$rpifun" "%s" "$value" #eval "${rpifun}=\"$value\"" 32 | 33 | fi 34 | done < $cf 35 | fi 36 | [ -f $SDMPT/$sdmdir/sdm-cparse ] && source $SDMPT/$sdmdir/sdm-cparse || echo "? Internal error: File '$SDMPT/$sdmdir/sdm-cparse' not found in readparams" 37 | return 38 | } 39 | # Call the function after we've been sourced 40 | readparams 41 | -------------------------------------------------------------------------------- /sdm-rpcsubs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # raspi-config do_vnc implementation only works if wayfire is running, but it's not during firstboot 5 | # So, copy the code here and do it here. 6 | # 7 | function enable_wayvnc() { 8 | # 9 | # $1: wayvnc headless width 10 | # $2: wayvnc headless height 11 | # 12 | local waywidth=$1 wayheight=$2 gx 13 | local useservice=0 # Cheap way to keep old code around 14 | if [[ $useservice -eq 0 ]] && [[ -f /lib/systemd/system/wayvnc.service ]] 15 | then 16 | systemctl enable wayvnc > /dev/null 2>&1 17 | else 18 | # This code is out of date. can delete soon 19 | HOMEDIR="/home/$myuser" 20 | systemctl --now disable vncserver-x11-serviced.service > /dev/null 2>&1 21 | mkdir -p $HOMEDIR/.config/wayvnc 22 | if ! [ -e $HOMEDIR/.config/wayvnc/config ] 23 | then 24 | cat << EOF > $HOMEDIR/.config/wayvnc/config 25 | use_relative_paths=true 26 | address=0.0.0.0 27 | enable_auth=true 28 | enable_pam=true 29 | private_key_file=key.pem 30 | certificate_file=cert.pem 31 | rsa_private_key_file=rsa_key.pem 32 | EOF 33 | fi 34 | if ! [ -e $HOMEDIR/.config/wayvnc/key.pem ] || ! [ -e $HOMEDIR/.config/wayvnc/cert.pem ] 35 | then 36 | logger "FirstBoot: Generate wayvnc keys..." 37 | HOSTNAME=$(cat /etc/hostname) 38 | IP=$(ifconfig | grep inet | head -n 1 | cut -d ' ' -f 10) 39 | openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout $HOMEDIR/.config/wayvnc/key.pem -out $HOMEDIR/.config/wayvnc/cert.pem -subj /CN=$HOSTNAME -addext subjectAltName=DNS:$HOSTNAME,DNS:$HOSTNAME,IP:$IP 2> /dev/null 40 | fi 41 | if ! [ -e $HOMEDIR/.config/wayvnc/rsa_key.pem ] 42 | then 43 | logger "FirstBoot: Generate wayvnc RSA key..." 44 | ssh-keygen -m pem -f $HOMEDIR/.config/wayvnc/rsa_key.pem -t rsa -N "" > /dev/null 45 | fi 46 | KBL=$(grep XKBLAYOUT /etc/default/keyboard | cut -d \" -f 2) 47 | KBV=$(grep XKBVARIANT /etc/default/keyboard | cut -d \" -f 2) 48 | [ -z $KBV ] && VNCKBD="$KBL" || VNCKBD="$KBL-$KBV" 49 | cat << EOF > /etc/xdg/autostart/wayvnc.desktop 50 | [Desktop Entry] 51 | Type=Application 52 | Name=wayvnc 53 | Comment=Start wayvnc 54 | NoDisplay=true 55 | Exec=/usr/bin/wayvnc --render-cursor --keyboard=$VNCKBD 56 | OnlyShowIn=wayfire 57 | EOF 58 | chown -R $myuser:users $HOMEDIR/.config/wayvnc 59 | fi 60 | # 61 | # Set resolution for wayfire if specified 62 | # 63 | if [[ "$waywidth" != "" ]] && [[ "$wayheight" != "" ]] 64 | then 65 | cat << EOF >> $HOMEDIR/.config/wayfire.ini 66 | 67 | [output] 68 | headless_width=$waywidth 69 | headless_height=$wayheight 70 | EOF 71 | fi 72 | chown $myuser:$(getfilegroup $HOMEDIR) $HOMEDIR/.config/wayfire.ini 73 | } 74 | -------------------------------------------------------------------------------- /sdm-xapps-example: -------------------------------------------------------------------------------- 1 | # This file lists all the X11 apps you want installed into your system IMG by sdm 2 | # A line can be 3 | # A blank line 4 | # Comment line (everything following # is ignored) 5 | # The name of a single package 6 | # 7 | # This example assumes the base system is Raspbian Lite 8 | # It installs 9 | # the basic X11 Windows system 10 | # A Display Manager (xdm). litedm is another popular display manager 11 | # A Window Manager (icewm). There are a lot of Window Managers to choose from 12 | # The TigerVNC server 13 | # Chromium and Firefox browsers 14 | # A few basic X11 apps 15 | # 16 | # Base components 17 | xserver-xorg 18 | xserver-xorg-core 19 | xserver-common 20 | xterm 21 | x11-apps 22 | xfonts-base 23 | xfonts-100dpi 24 | xfonts-75dpi 25 | xfonts-scalable 26 | 27 | # Display manager 28 | xdm 29 | 30 | # Window manager 31 | icewm 32 | 33 | # VNC 34 | tigervnc-standalone-server 35 | 36 | # Apps that require X Windows 37 | chromium 38 | firefox-esr 39 | xclip 40 | xcolors 41 | xcolorsel 42 | xcolmix 43 | retext 44 | --------------------------------------------------------------------------------