Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cloud-init support for Raspberry Pi OS #950

Draft
wants to merge 16 commits into
base: qml
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,11 @@ set(SOURCES ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc ${QM_FILES})

if (WIN32)
# Adding WIN32 prevents a console window being opened on Windows
add_executable(${PROJECT_NAME} WIN32 ${SOURCES} ${HEADERS} ${DEPENDENCIES})
add_executable(${PROJECT_NAME} WIN32 ${SOURCES} ${HEADERS} ${DEPENDENCIES}
extraFiles.qrc)
else()
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS} ${DEPENDENCIES})
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS} ${DEPENDENCIES}
extraFiles.qrc)
endif()

set_property(TARGET ${PROJECT_NAME} PROPERTY AUTOMOC ON)
Expand Down
83 changes: 57 additions & 26 deletions src/OptionsPopup.qml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Window {
property string cloudinitrun
property string cloudinitwrite
property string cloudinitnetwork
property bool deviceUsbOtgSupport: false
property bool enableEtherGadget

signal saveSettingsSignal(var settings)

Expand Down Expand Up @@ -375,6 +377,11 @@ Window {
ColumnLayout {
// Remote access tab
Layout.fillWidth: true
ImCheckBox {
id: chkUSBEther
text: qsTr("Enable USB Ethernet Gadget")
enabled: deviceUsbOtgSupport
}

ImCheckBox {
id: chkSSH
Expand Down Expand Up @@ -433,7 +440,6 @@ Window {
id: publicKeyList
model: ListModel {
id: publicKeyModel

}
boundsBehavior: Flickable.StopAtBounds
anchors.fill: parent
Expand Down Expand Up @@ -744,6 +750,14 @@ Window {
}
}

if (imageWriter.checkHWAndSWCapability("usb_otg")) {
deviceUsbOtgSupport = true
} else {
deviceUsbOtgSupport = false
// make sure it isn't disabled and selected
chkUSBEther.checked = false
}

//open()
show()
raise()
Expand Down Expand Up @@ -808,19 +822,24 @@ Window {
addCloudInit("")
}

var isRpiosCloudInit = imageWriter.checkSWCapability("rpios_cloudinit");

if (chkSSH.checked || chkSetUser.checked) {
// First user may not be called 'pi' on all distributions, so look username up
addFirstRun("FIRSTUSER=`getent passwd 1000 | cut -d: -f1`");
addFirstRun("FIRSTUSERHOME=`getent passwd 1000 | cut -d: -f6`")

var cryptedPassword;
if (chkSetUser.checked) {
cryptedPassword = fieldUserPassword.alreadyCrypted ? fieldUserPassword.text : imageWriter.crypt(fieldUserPassword.text)
}

addCloudInit("users:")
addCloudInit("- name: "+fieldUserName.text)
addCloudInit("- name: " + fieldUserName.text)
addCloudInit(" groups: users,adm,dialout,audio,netdev,video,plugdev,cdrom,games,input,gpio,spi,i2c,render,sudo")
addCloudInit(" shell: /bin/bash")

var cryptedPassword;
if (chkSetUser.checked) {
cryptedPassword = fieldUserPassword.alreadyCrypted ? fieldUserPassword.text : imageWriter.crypt(fieldUserPassword.text)
addCloudInit(" lock_passwd: false")
addCloudInit(" passwd: "+cryptedPassword)
}
Expand Down Expand Up @@ -892,6 +911,14 @@ Window {
addFirstRun("fi")
}
addCloudInit("")

/*if (chkSetUser.checked) {
addCloudInit("final_message: \"Setup wizard has been skiped.\"")
addCloudInit("power_state:")
addCloudInit(" mode: reboot")
addCloudInit(" message: Rebooting machine")
addCloudInit("")
}*/
}
if (chkWifi.checked) {
var wpaconfig = "country="+fieldWifiCountry.editText+"\n"
Expand Down Expand Up @@ -927,21 +954,36 @@ Window {
addFirstRun("fi")


cloudinitnetwork = "version: 2\n"
cloudinitnetwork += "wifis:\n"
cloudinitnetwork += " renderer: networkd\n"
cloudinitnetwork += " wlan0:\n"
cloudinitnetwork += " dhcp4: true\n"
cloudinitnetwork += " optional: true\n"
cloudinitnetwork += " access-points:\n"
cloudinitnetwork += " \""+fieldWifiSSID.text+"\":\n"
cloudinitnetwork += " password: \""+cryptedPsk+"\"\n"
cloudinitnetwork = "network:\n"
cloudinitnetwork += " version: 2\n"
cloudinitnetwork += " renderer: " + (isRpiosCloudInit ? "NetworkManager" : "networkd") + "\n"
cloudinitnetwork += " wifis:\n"
cloudinitnetwork += " wlan0:\n"
cloudinitnetwork += " dhcp4: true\n"
cloudinitnetwork += " optional: true\n"
cloudinitnetwork += " access-points:\n"
cloudinitnetwork += " \""+fieldWifiSSID.text+"\":\n"
cloudinitnetwork += " password: \""+cryptedPsk+"\"\n"
if (chkWifiSSIDHidden.checked) {
cloudinitnetwork += " hidden: true\n"
cloudinitnetwork += " hidden: true\n"
}

addCmdline("cfg80211.ieee80211_regdom="+fieldWifiCountry.editText)
}
if (chkUSBEther.checked) {
// keep parity with cli.cpp
addConfig("dtoverlay=dwc2,dr_mode=peripheral")

enableEtherGadget = true;

addFirstRun("\nmv /boot/firmware/10usb.net /etc/systemd/network/10-usb.network")
addFirstRun("mv /boot/firmware/geth.cnf /etc/modprobe.d/g_ether.conf")
addFirstRun("mv /boot/firmware/gemod.cnf /etc/modules-load.d/usb-ether-gadget.conf\n")
addFirstRun("SERIAL=$(grep Serial /proc/cpuinfo | awk '{print $3}')")
addFirstRun("sed -i \"s/<serial>/$SERIAL/g\" /etc/modprobe.d/g_ether.conf")
addFirstRun("systemctl enable systemd-networkd\n")
}

if (chkLocale.checked) {
var kbdconfig = "XKBMODEL=\"pc105\"\n"
kbdconfig += "XKBLAYOUT=\""+fieldKeyboardLayout.editText+"\"\n"
Expand All @@ -967,17 +1009,6 @@ Window {
addCloudInit(" layout: \"" + fieldKeyboardLayout.editText + "\"")
}

if (firstrun.length) {
firstrun = "#!/bin/bash\n\n"+"set +e\n\n"+firstrun
addFirstRun("rm -f /boot/firstrun.sh")
addFirstRun("sed -i 's| systemd.run.*||g' /boot/cmdline.txt")
addFirstRun("exit 0")
/* using systemd.run_success_action=none does not seem to have desired effect
systemd then stays at "reached target kernel command line", so use reboot instead */
//addCmdline("systemd.run=/boot/firstrun.sh systemd.run_success_action=reboot systemd.unit=kernel-command-line.target")
// cmdline changing moved to DownloadThread::_customizeImage()
}

if (cloudinitwrite !== "") {
addCloudInit("write_files:\n"+cloudinitwrite+"\n")
}
Expand All @@ -986,7 +1017,7 @@ Window {
addCloudInit("runcmd:\n"+cloudinitrun+"\n")
}

imageWriter.setImageCustomization(config, cmdline, firstrun, cloudinit, cloudinitnetwork)
imageWriter.setImageCustomization(config, cmdline, firstrun, cloudinit, cloudinitnetwork, false, enableEtherGadget)
}

function saveSettings()
Expand Down
17 changes: 14 additions & 3 deletions src/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,22 @@ int Cli::run()
{"first-run-script", "Add firstrun.sh to image", "first-run-script", ""},
{"cloudinit-userdata", "Add cloud-init user-data file to image", "cloudinit-userdata", ""},
{"cloudinit-networkconfig", "Add cloud-init network-config file to image", "cloudinit-networkconfig", ""},
{"usb-ether-gadget", "Enable USB Ethernet Gadget mode (does not support --first-run-script)"},
{"disable-eject", "Disable automatic ejection of storage media after verification"},
{"debug", "Output debug messages to console"},
{"quiet", "Only write to console on error"},
});

parser.addVersionOption();
parser.addHelpOption();
parser.addPositionalArgument("src", "Image file/URL");
parser.addPositionalArgument("dst", "Destination device");
parser.process(*_app);

const QStringList args = parser.positionalArguments();
if (args.count() != 2)
{
std::cerr << "Usage: --cli [--disable-verify] [--disable-eject] [--sha256 <expected hash> [--cache-file <cache file>]] [--first-run-script <script>] [--debug] [--quiet] <image file to write> <destination drive device>" << std::endl;
std::cerr << "Usage: --cli [--disable-verify] [--disable-eject] [--sha256 <expected hash> [--cache-file <cache file>]] [--first-run-script <script>] [--usb-ether-gadget] [--debug] [--quiet] <image file to write> <destination drive device>" << std::endl;
return 1;
}

Expand Down Expand Up @@ -148,6 +151,8 @@ int Cli::run()
}
}

bool isEtherGadgetEnabled = parser.isSet("usb-ether-gadget");

if (!parser.value("cloudinit-userdata").isEmpty())
{
QByteArray userData, networkConfig;
Expand Down Expand Up @@ -186,10 +191,15 @@ int Cli::run()
return 1;
}

_imageWriter->setImageCustomization("", "", "", userData, networkConfig);
_imageWriter->setImageCustomization("", "", "", userData, networkConfig, false, isEtherGadgetEnabled);
}
else if (!parser.value("first-run-script").isEmpty())
{
if (isEtherGadgetEnabled) {
std::cerr << "Error: the --usb-ether-gadget option is not supported when --first-run-script is used.";
return 1;
}

QByteArray firstRunScript;
QFile f(parser.value("first-run-script"));
if (!f.exists())
Expand All @@ -208,11 +218,12 @@ int Cli::run()
return 1;
}

_imageWriter->setImageCustomization("", "", firstRunScript, "", "");
_imageWriter->setImageCustomization("", "", firstRunScript, "", "", true, isEtherGadgetEnabled);
}

_imageWriter->setDst(args[1]);
_imageWriter->setVerifyEnabled(!parser.isSet("disable-verify"));
_imageWriter->setEtherGadgetEnabled(isEtherGadgetEnabled);
_imageWriter->setSetting("eject", !parser.isSet("disable-eject"));

/* Run startWrite() in event loop (otherwise calling _app->exit() on error does not work) */
Expand Down
Loading