Verwenden Sie /boot/cmdline.txt, um ein First-Boot-Skript zu erstellen

11

Es wurden viele Fragen gestellt, wie ich meinen Pi in meinem Netzwerk finden kann . Andere - einschließlich ich - haben zeitaufwändige Probleme beim Versuch, eine Menge neuer PIs bereitzustellen.

Während die Erstellung von benutzerdefinierten Bildern eine Lösung für diese Probleme sein könnte, frage ich mich, ob es andere Lösungen gibt.

Wäre es möglich, mit (nur) dem /bootVerzeichnis, das auf normalen Computern (Win / OSX) für den Zugriff geöffnet ist /boot/cmdline.txt, Text an ein Bash-Skript weiterzuleiten, auszuführen und anschließend zu löschen?

EDV
quelle
1
Diese Frage wird derzeit auf Meta diskutiert . Ich würde gerne Ihre Meinung hören, wenn möglich. Vielen Dank.
Thomas Weller

Antworten:

5

Ich habe eine leicht modifizierte Version von Raspberian-light erstellt, die diese Anforderung erfüllt. Sie führt Ihr benutzerdefiniertes Skript /boot/firstboot.sh beim ersten Start aus:

https://github.com/nmcclain/raspberian-firstboot

Ned McClain
quelle
Vielen Dank! 4 Jahre nach OP gibt es endlich eine gute Lösung. Keine besondere Raketenwissenschaft, um es selbst zu bauen, aber jedes Mal, wenn es eine neue Version gibt, sind Sie viele Stunden dabei. IMHO sollte dies zur Hauptfirmware hinzugefügt werden.
EDV
2

Sie können bewirken, dass Code ausgeführt wird, indem Sie mit der Kernel-Befehlszeile herumspielen. Die naheliegendste Methode besteht darin, init durch etwas anderes zu ersetzen. Die häufigste Anwendung hierfür ist das Starten einer Shell sehr früh im Startvorgang, normalerweise, weil Sie etwas reparieren müssen oder weil alles andere sehr stark beschädigt ist, z.

init=/bin/bash

Beachten Sie, dass zu diesem Zeitpunkt des Startvorgangs alle Dateisysteme noch schreibgeschützt bereitgestellt werden. Darüber hinaus gibt es eine ganze Reihe von Dingen, die einfach nicht richtig funktionieren. Da kein richtiger Init ausgeführt wird, funktioniert das Herunterfahren und Neustarten nicht. Sie müssen das Root-Dateisystem manuell erneut schreibgeschützt bereitstellen und beispielsweise reboot -fzum Neustart aufrufen .

Ich habe keine Ahnung, ob Sie auf diese Weise Argumente an bash übergeben können. Ich habe es nie versucht. Wenn Sie theoretisch -czu Bash übergehen können , können Sie diesem Bash-Prozess anweisen, alles zu tun. Aber es könnte sich zu einem ziemlich langen Streit entwickeln, und ich weiß nicht, ob der Kernel solche Dinge zulassen würde.

Zweitens können Sie tun. Sie können ein erstes Ramfs (Initramfs) in das Dateisystem kopieren und den Bootloader für die Verwendung konfigurieren config.txt. Es gibt verschiedene Möglichkeiten, Skripte in ein initramfs zu bekommen, um spezielle Dinge zu tun. Sie müssen jedoch ein spezielles initramfs für diesen Zweck vorbereiten (siehe initramfs-tools (8)), daher bin ich mir nicht sicher, ob dies eine bessere Lösung als ein benutzerdefiniertes Image ist.

Sie könnten das Skript in / boot einfügen (ich habe über Ihren Vorschlag zu "normalen" Computern gelacht, aber dies wäre das Bit, auf das Sie von diesen Computern aus zugreifen können) und versuchen, dies über die Kernel-Init-Zeile zu starten, aber Dateien auf dos-Dateisystemen sind nicht vorhanden Es ist nicht ausführbar, es sei denn, Sie machen es für das gesamte Dateisystem.

Wenn ich es wäre, würde ich ein benutzerdefiniertes Image erstellen, das DHCP zum Konfigurieren des Netzwerks verwendet und ein benutzerdefiniertes Skript enthält, das beim Booten ausgeführt wird. Dieses Skript sucht nach einer bestimmten Datei, die als Flag fungiert. Wenn die Datei vorhanden ist, tun Sie nichts. Wenn nicht, konfigurieren Sie die Dinge und erstellen Sie die Flag-Datei.

Ihr Konfigurationsskript könnte sogar das Original von einem http-Server abrufen. Dies bedeutet, dass Sie kein neues Bild erstellen müssen, wenn Sie etwas optimieren müssen.

Das sollte die am wenigsten stressige Lösung sein.

Eine letzte Möglichkeit, aber Sie müssen dies auf einem "nicht regulären" Computer tun :-) Sie können das ext4-Dateisystem auf ein Loop-Gerät mounten und Dateien darauf kopieren, ohne es zuerst auf die SD-Karte zu schreiben. Für ein Standardbild von Raspbian Jessie wäre es ungefähr so:

sudo losetup /dev/loop0 /tmp/gw.img -o 62914560
sudo mount /dev/loop0 /mnt
sudo cp /my/superduper/script.sh /mnt
sudo umount /dev/loop0
sudo fsck -f /dev/loop0 # This is optional
sudo losetup -d /dev/loop0

Ich mache gerne einen erzwungenen Fsck auf meinen Dateisystemen, bevor ich Bilder mache. Setzt die Anzahl der Mount beim ersten Start auf Null :-)

EDIT : Nach vielen Monaten und mehr Erfahrung. Sie möchten sich U-Boot ansehen. Ersetzen Sie den Bootloader durch U-Boot. Dies kann von einer "normalen Maschine" aus erfolgen. Sobald Sie U-Boot installiert haben, können Sie entweder eine Distribution über das Netzwerk booten, von der aus Sie die SD-Karte problemlos flashen können, oder Sie können die Karte theoretisch direkt flashen, obwohl ich keine Ahnung habe, wie schwer das sein würde.

Im Wesentlichen bringt U-Boot Netzwerk-Boot auf den Raspberry Pi, was es alleine nicht unterstützt.

izak
quelle
Ok, das Dateisystem ist zu diesem Zeitpunkt schreibgeschützt. Was ist mit init=script & init? Das Skript wird im Hintergrund ausgeführt, während init normal gestartet wird. Das Skript würde zu Beginn eine Bedingungsprüfung benötigen und z. B. fortfahren, wenn init seine Arbeit beendet hat.
Thomas Weller
1
Die Verwendung von & als Hintergrund ist eine Shell-Sache. Es sei denn, Sie weisen den Kernel an, einen bestimmten Befehl in einer Shell auszuführen (z. B. bash -c "irgendein Befehl und ein anderer Befehl"), der nicht funktioniert, und ich halte dies bereits für eine schlechte Idee. Aber ich habe meine Antwort erweitert und die U-Boot-Option hinzugefügt, die ich kürzlich entdeckt habe.
Izak
1
Ich habe es gerade versucht ;-) Nein, es funktioniert wirklich nicht
Thomas Weller
2

Für diejenigen, die eine Lösung bevorzugen, die nur Skripte enthält, die in der FAT32- Bootpartition abgelegt wurden , finden Sie hier eine Vorgehensweise. [ Bearbeiten: Die Dateien sind jetzt in einem Projekt- Pi-Boot-Skript verfügbar .]

Wie in anderen Antworten erwähnt, handelt es sich um die Befehlszeilenargumente, mit denen der Linux-Kernel gestartet wird. Diese Argumente befinden sich in /boot/cmdline.txt .

Ich habe dies auf Raspbian Buster (v10.1) 2019-09-26 getestet. Es funktioniert auf einer neu geflashten SD-Karte oder auf dem heruntergeladenen IMG -Image, das Sie dann auf eine beliebige Anzahl von SD-Karten flashen können.

1. Bearbeiten Sie die Kernel-Argumente

Öffnen Sie die Textdatei /boot/cmdline.txt , entfernen Sie alle init=Teile daraus und fügen Sie diese am Ende der Zeile hinzu:

init=/bin/bash -c "mount -t proc proc /proc; mount -t sysfs sys /sys; mount /boot; source /boot/unattended"

Das letzte Wort in dieser Zeile ist der Name eines Skripts, das vom Kernel als erster Prozess (PID = 1) anstelle von / sbin / init ausgeführt wird . Die Hilfeseite zu Kernel-Argumenten enthält nur Argumente ., die nicht an die ausführbare Datei init übergeben werden. Sie können das Skript also nicht unbeaufsichtigt aufrufen .

2. Legen Sie das Skript auf die Startpartition

Speichern Sie Folgendes auf der Boot-Partition als / unbeaufsichtigt (den Namen, den Sie in die Befehlszeile eingegeben haben):

# 1. MAKING THE SYSTEM WORK. DO NOT REMOVE
mount -t tmpfs tmp /run
mkdir -p /run/systemd
mount / -o remount,rw
sed -i 's| init=.*||' /boot/cmdline.txt

# 2. THE USEFUL PART OF THE SCRIPT
# Example:
[[ -d /boot/payload/home/pi ]] && sudo -u pi cp --preserve=timestamps -r\
 /boot/payload/home/pi /home/ && rm -rf /boot/payload/home/pi              # A
[[ -d /boot/payload ]] && cp --preserve=timestamps -r /boot/payload/* /\
 && rm -rf /boot/payload                                                   # B
ln -s /lib/systemd/system/one-time-script.service\
 /etc/systemd/system/multi-user.target.wants/                              # C

# 3. CLEANING UP AND REBOOTING
sync
umount /boot
mount / -o remount,ro
sync
echo 1 > /proc/sys/kernel/sysrq
echo b > /proc/sysrq-trigger
sleep 5

Dieses Skript führt einige notwendige Vorbereitungen durch (Kapitel 1), dann alles, was Sie tun möchten (2) und dann bereinigen und neu starten (3). Ersetzen Sie das Zeug unter 2 durch die Befehle, die Sie ausführen möchten.

Für einige Konfigurationsaufgaben benötigen Sie wahrscheinlich einen normalen Start, um Netzwerk- und andere Dienste aufzurufen. Daher bereitet das Beispiel in dieser Version (siehe unten) nur die Ausführung eines geeigneten Skripts vor, wenn der Pi neu gestartet wird.

3. Legen Sie alle anderen Dateien, die Ihr Skript benötigt, auf der Startpartition ab

...offensichtlich.

Beispiel

Zusammen mit meinem Skript habe ich einen Ordner payload / auf die Boot-Partition gelegt, der die Dateien enthält, die ich auf die Linux-Partition verschieben möchte. In dem oben unbeaufsichtigten Skript

  • Zeile A verschiebt Dateien in das Verzeichnis des pi-Benutzers. Beispielsweise wird payload / home / pi / .bashrc als /home/pi/.bashrc in das Root-Dateisystem verschoben .
  • Zeile B verschiebt Root-Dateien in die Linux-Partition, einschließlich payload / usr / local / bin / one-time-script.sh, das zu /usr/local/bin/one-time-script.sh wird , und ähnliches für payload / lib / systemd / system / one-time-script.service ;
  • Zeile C erstellt dann einen Symlink zu dieser letzten Datei, sodass mein Konfigurationsskript one-time-script.sh beim nächsten Start ausgeführt wird.

Dieses Skript nimmt verschiedene Anpassungen vor, die mir gefallen: Es erstellt und formatiert eine andere FAT32-Partition und fügt sie zu / etc / fstab hinzu, damit der pi-Benutzer darauf schreiben kann (für Anwendungsprotokolle usw.); Ändert die Größe der ext4-Partition und des Dateisystems auf den Rest der SD-Karte. ändert das Gebietsschema, die Zeitzone, den Hostnamen (basierend auf der Seriennummer der CPU) und das WLAN-Land; Legt das WiFi-Netzwerk und die Passphrase fest. schaltet SSH ein; behebt ein Problem mit den Spracheinstellungen für SSH-Sitzungen; Konfiguriert das Booten in einer Konsole ohne automatische Anmeldung. schreibt einige Daten über das System in eine Datei auf der Boot-Partition; und natürlich wird dieser Symlink entfernt, damit er beim Booten nicht erneut ausgeführt wird.

Die meisten Benutzer werden dies für unnötig halten und bevorzugen die Verwendung von PiBakery , pi-init2 oder einem benutzerdefinierten ext4-Image, die großartige Lösungen darstellen. Ich bevorzuge dies, weil ich es vollständig verstehen kann und keine andere Software ausführen muss. Und es funktioniert auch: Mit der IMG- Datei, in die ich meine Skripte eingefügt habe, dauert es 6 Minuten, eine SD-Karte zu flashen + in einen Pi zu legen + sie laufen zu lassen, um sich selbst zu konfigurieren.

Quelle Ich fand die Idee eines Skripts als init=Kernel-Argument und die mountBefehle, die erforderlich sind, damit es funktioniert, im Skript init_resize.sh , das standardmäßig ausgeführt wird, um die Größe der Linux-Partition zu ändern.

Jim Danner
quelle
1

Ich würde nicht empfehlen, irgendetwas im Boot-Bereich (außer config.txt) zu berühren, es sei denn, Sie haben ein detailliertes Verständnis dafür, was das Zeug tut. cmdline.txtist nicht dafür ausgelegt, Dinge auszuführen, wenn das RPi startet. Es wird verwendet, um Parameter beim Booten an den Linux-Kernel zu übergeben.

Ich würde vorschlagen, dies alles über SSH zu tun. Ein Skript auf Ihrem Desktop kann ein bash / python / java / c / any-Programm auf das RPi übertragen, ausführen und dann löschen, wenn es fertig ist. Wenn Sie dem Skript auf Ihrem Desktop Threading hinzufügen, können Sie es gleichzeitig an beliebig viele Geräte senden.

Jacobm001
quelle
1
So machen wir es jetzt, aber ich suche nach einer einfacheren Lösung.
EDV
1
Lesen Sie: Führen Sie das Setup Client-initiiert statt Server-initiiert
EDV
@EDP: Es gibt keine Möglichkeit, einfach durch Bearbeiten der Startposition das zu erreichen, was Sie möchten. Sie könnten ein Skript schreiben, das die Datei beim ersten Start des RPi von einem Server abruft, und dann das Startskript von diesem Programm entfernen lassen. Dazu müssten Sie ein benutzerdefiniertes Bild verwenden.
Jacobm001
1
"Ein Skript auf Ihrem Desktop" - Welches Skript auf meinem Desktop? Beim ersten Start befindet sich kein Skript auf dem Desktop. "Dies alles über SSH erledigen" - Beim ersten Start verfügt der Pi möglicherweise nicht über die richtigen Ethernet- oder WLAN-Einstellungen.
Thomas Weller
Sie müssen nicht einmal das Fabric-Projekt einfädeln. IIRC seine einzige Wiederaufnahme ist SSH
Steve Robillard
1

Wenn Sie das Image so ändern können, dass beim ersten Start ein Skript automatisch ausgeführt wird, können Sie das Image einfach so ändern, wie es Ihr Skript tun würde, und diese SD-Karte dann in einer Image-Datei speichern und zum Flashen verwenden SD-Karten, die Sie mit neuen RPis verwenden werden. Wenn Sie beispielsweise möchten, dass alle Ihre RPis einen bestimmten Eintrag in haben /etc/fstab, können Sie sich einfach /etc/fstabselbst ändern , anstatt ein Skript zu schreiben, das die Änderung vornimmt .

Wenn Sie unbedingt Skript - Aktionen benötigen (zB wenn jedes Bild sollte in einer anderen Art und Weise modifiziert werden), könnten Sie Ihre bewegen /etc/rc.localzu /etc/rc.bakund ein Skript setzen in /etc/rc.localdem sich mit ersetzt /etc/rc.bakin den letzten Befehl. Dieses Skript kann die ersten Startaktionen selbst ausführen oder ein bestimmtes Skript von der /bootPartition aus aufrufen, wenn Sie dies bevorzugen.

Es ist möglich, eine automatische Ausführung /bootdurchzuführen, indem nur die Partition berührt wird , indem dem Kernel ein spezielles Boot-Ramdisk-Image bereitgestellt wird, wie hier beschrieben . Dieses Image würde die Skripte enthalten, um die Root-Partition zu ändern und dann selbst zu löschen config.txt. Ich bin mir nicht sicher, ob es die Mühe wert ist.

Dmitry Grigoryev
quelle
-2

Vielleicht möchten Sie sich mein Projekt Nard ansehen, das eine Lösung für Ihr Problem bietet:

1) Jeder SD-Karte kann mit einem normalen Windows-PC eine eindeutige ID zugewiesen werden, wie hier beschrieben:
http://www.arbetsmyra.dyndns.org/nard/#devsettingsid

2) Schalten Sie alle Ihre Pis ein

3) Deaktivieren Sie nach Möglichkeit die PC-Firewall

4) Öffnen Sie ein DOS-Eingabeaufforderungsfenster und pingen Sie die Subnetz-Broadcast-Adresse an

5) Listen Sie die ARP-Tabelle mit dem Windows-Befehl "arp -a" auf. In der Liste finden Sie die MAC- und IP-Adressen aller in der Nähe befindlichen Raspberry Pi.

6) Stellen Sie mit Telnet eine Verbindung zu jedem Gerät her (normalerweise auch unter Windows verfügbar). Die Begrüßungsphrase zeigt die in Schritt 1 zugewiesene ID an.

Ronny Nilsson
quelle
Vielleicht war meine ursprüngliche Beschreibung nicht klar genug. Ich bin nicht besonders auf der Suche nach einer Möglichkeit, die Pi zu identifizieren. Ich habe das schon abgedeckt. Ich suche nach einem oder mehreren Bash-Befehlen, indem ich die Datei /boot/cmdline.txt ändere. Dies alles ohne dass Sie sich über ssh anmelden müssen - auch nur einmal.
EDV
Sie werden wahrscheinlich nicht in der Lage sein, die Subnetz-Broadcast-Adresse zu pingen. Schlumpfangriffe werden normalerweise verhindert.
CrackerJack9