Utilizzare /boot/cmdline.txt per creare lo script di primo avvio


11

Sono state poste molte domande su come trovare il mio Pi sulla mia rete . Altri, incluso me stesso, hanno problemi di tempo durante il tentativo di distribuire un nuovo lotto di Pi.

Mentre la creazione di immagini personalizzate potrebbe essere una soluzione per questi problemi, mi chiedo se ci siano altre soluzioni.

Con (solo) la /bootdirectory aperta per l'accesso su macchine normali (Win / OSX), sarebbe possibile utilizzare /boot/cmdline.txtper reindirizzare il testo a uno script bash, eseguirlo ed eliminarlo in seguito?


1
Questa domanda è in discussione su Meta . Mi piacerebbe sentire la tua opinione, se possibile. Grazie.
Thomas Weller,

Risposte:


6

Ho creato una versione leggermente modificata di Raspberian-light che risponde a questa esigenza: esegue il tuo script /boot/firstboot.sh personalizzato al primo avvio:

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


Grazie! 4 anni dopo l'OP c'è finalmente una buona soluzione. Non in particolare la scienza missilistica per costruirlo da soli, ma ci sei per molte ore ogni volta che c'è una nuova versione. IMHO questo dovrebbe essere aggiunto al firmware principale.
EDP,

3

Per coloro che preferiscono una soluzione che coinvolge solo gli script rilasciati nella partizione di avvio FAT32 , ecco come farlo. [ Modifica: i file sono ora disponibili in un progetto pi-boot-script .]

Come menzionato in altre risposte, coinvolge gli argomenti della riga di comando con cui viene avviato il kernel Linux. Questi argomenti sono in /boot/cmdline.txt .

Ho provato questo su Raspbian Buster (v10.1) 2019-09-26. Funziona su una scheda SD appena flash o sull'immagine del disco .img scaricata , che è quindi possibile eseguire il flash su qualsiasi numero di schede SD.

1. Modifica gli argomenti del kernel

Apri il file di testo /boot/cmdline.txt , rimuovi qualsiasi init=parte da esso e aggiungilo alla fine della riga:

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

L'ultima parola su questa riga è il nome di uno script che deve essere eseguito dal kernel come primo processo (PID = 1) invece di / sbin / init . La pagina di aiuto degli argomenti del kernel dice solo argomenti senza .essere passati all'eseguibile init, quindi non puoi chiamare lo script unattended.sh o cose del genere.

2. Inserire lo script nella partizione di avvio

Salvare quanto segue nella partizione di avvio come / incustodito (il nome inserito nella riga di comando):

# 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

Questo script esegue una preparazione necessaria (capitolo 1), quindi tutto ciò che si desidera fare (2), quindi ripulitura e riavvio (3). Sostituisci le cose sotto 2 con i comandi che vuoi eseguire.

Per alcune attività di configurazione è probabilmente necessario un avvio normale per visualizzare la rete e altri servizi, quindi l'esempio in questa versione (spiegato di seguito) prepara solo l'esecuzione di uno script appropriato al riavvio del Pi.

3. Inserisci tutti gli altri file necessari per lo script nella partizione di avvio

...ovviamente.

Esempio

Insieme al mio script, ho inserito una cartella payload / nella partizione di avvio, che contiene i file che voglio spostare nella partizione Linux. Nello script incustodito sopra,

  • la riga A sposta i file nella directory pi-user. Ad esempio payload / home / pi / .bashrc viene spostato nel filesystem di root come /home/pi/.bashrc ;
  • la riga B sposta i file di proprietà root nella partizione Linux, incluso payload / usr / local / bin / one-time-script.sh che diventa /usr/local/bin/one-time-script.sh e simili per payload / lib / systemd / system / one-time-script.service ;
  • la linea C crea quindi un collegamento simbolico all'ultimo file, quindi il mio script di configurazione one-time-script.sh viene eseguito all'avvio successivo.

Quello script fa varie personalizzazioni che mi piacciono: crea e formatta un'altra partizione FAT32 e la aggiunge a / etc / fstab in modo che l'utente pi possa scrivergli (per i registri delle applicazioni ecc.); ridimensiona la partizione ext4 e il filesystem sul resto della scheda SD; modifica le impostazioni internazionali, fuso orario, nome host (in base al numero seriale della CPU), paese WiFi; imposta la rete WiFi e la passphrase; attiva SSH; risolve un problema relativo alle impostazioni della lingua per le sessioni SSH; configura l'avvio in una console senza auto-login; scrive alcuni dati sul sistema in un file sulla partizione di avvio; e ovviamente rimuove quel link simbolico in modo che non possa essere eseguito nuovamente all'avvio.

La maggior parte degli utenti lo troverà superfluo e preferirà usare PiBakery , pi-init2 o un'immagine ext4 personalizzata, che sono ottime soluzioni. Preferisco questo perché posso comprenderlo appieno e non devo eseguire altri software. E funziona anche: con il file .img in cui ho inserito i miei script, tutto il flashing di una scheda SD + metterlo in un Pi + per farlo funzionare per configurarsi richiede 6 minuti.

Fonte Ho trovato l'idea di uno script come init=argomento del kernel e i mountcomandi necessari per farlo funzionare, nello script init_resize.sh che viene eseguito di default per ridimensionare la partizione Linux.


2

È possibile causare l'esecuzione del codice facendo confusione con la riga di comando del kernel. Il metodo più ovvio è sostituire init con qualcos'altro. L'applicazione più comune di questo è lanciare una shell molto presto nel processo di avvio, di solito perché è necessario riparare qualcosa o perché tutto il resto è molto gravemente rotto, ad esempio:

init=/bin/bash

Tieni presente che a questo punto del processo di avvio, i filesystem sono ancora montati in sola lettura. Inoltre, ci sono un sacco di cose che non funzionano correttamente. Poiché non hai un vero init in esecuzione, l'arresto e il riavvio non funzioneranno. Devi rimontare manualmente il filesystem di root in sola lettura e chiamare reboot -fper riavviare, per esempio.

Non ho idea se puoi passare argomenti per bash in questo modo. Non ho mai provato. In teoria, se riesci a passare -ca bash, puoi dire a quel processo bash di fare qualsiasi cosa. Ma potrebbe trasformarsi in un argomento abbastanza lungo, e non so se il kernel consentirebbe tali cose.

La seconda cosa che puoi fare. È possibile copiare un ramfs iniziale (initramfs) nel filesystem e configurare il bootloader per utilizzarlo config.txt. Esistono diversi modi per ottenere script in un initramfs per fare cose speciali. Dovrai preparare uno speciale initramfs per questo scopo (vedi initramfs-tools (8)), quindi non sono sicuro che questa sia una soluzione migliore di un'immagine personalizzata.

Potresti includere lo script in / boot (ho riso del tuo suggerimento su macchine "normali", ma questo sarebbe il bit a cui puoi accedere da quelle macchine) e tentare di avviarlo usando la riga init del kernel, ma i file su filesystem dos non sono eseguibile a meno che tu non lo faccia per l'intero filesystem.

Se fossi in me, creerei un'immagine personalizzata che utilizza dhcp per configurare la rete e che contiene uno script personalizzato che viene eseguito all'avvio. Questo script verifica la presenza di un file specifico che funge da flag. Se il file esiste, non fare nulla. In caso contrario, configura le cose, quindi crea il file flag.

Il tuo script di configurazione potrebbe persino estrarre la cosa reale da un server http. Questo significa che non devi creare una nuova immagine se devi modificare qualcosa.

Questa dovrebbe essere la soluzione meno stressante.

Un'ultima possibilità, ma dovrai farlo su una macchina "non regolare" :-) Puoi montare il filesystem ext4 su un dispositivo loop e copiare i file su di esso senza scriverlo prima su sdcard. Per un'immagine standard di Raspbian Jessie, sarebbe qualcosa del genere:

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

Mi piace fare un fsck forzato sul mio filesystem prima di creare immagini. Imposta il conteggio dei montaggi su zero al primo avvio :-)

EDIT : dopo molti mesi e più esperienza. Vuoi guardare u-boot. Sostituisci il bootloader con u-boot. Questo può essere fatto da una "macchina normale". Dopo aver avviato u-boot, è possibile avviare in rete una distribuzione da cui è possibile eseguire facilmente il flashing della scheda SD, oppure in teoria è possibile eseguire il flashing della scheda direttamente, anche se non ho idea di quanto sia difficile.

Essenzialmente u-boot porta l'avvio di rete su Raspberry Pi, qualcosa che non supporta da solo.


Ok, il file system è di sola lettura in quella fase. Che dire init=script & init? Lo script verrebbe eseguito in background mentre init si avvia normalmente. Lo script avrebbe bisogno di un controllo delle condizioni all'inizio e, ad esempio, continuare quando init ha terminato il suo lavoro.
Thomas Weller,

1
L'uso e lo sfondo di qualcosa è una cosa di shell. A meno che tu non dica al kernel di eseguire un comando specifico in una shell (es: bash -c "un comando e un altro comando") che non funzionerà, e penso già che sia una cattiva idea. Ma ho esteso la mia risposta e ho aggiunto l'opzione u-boot, qualcosa che ho scoperto di recente.
Ikak,

1
Ho appena finito di provare ;-) No, davvero non funziona
Thomas Weller,

1

Non consiglierei di toccare nulla nell'area di avvio (tranne config.txt) a meno che tu non abbia una comprensione dettagliata di ciò che quella roba sta facendo. cmdline.txtnon è progettato per eseguire cose all'avvio di RPi. È usato per passare parametri al kernel Linux all'avvio.

Suggerirei di farlo attraverso SSH. Uno script sul desktop potrebbe inviare un programma bash / python / java / c / su RPi, eseguirlo e quindi eliminarlo al termine. Aggiungi il threading allo script sul desktop e puoi inviarlo a tutti i dispositivi che vuoi tutti allo stesso tempo.


1
Ecco come lo facciamo ora ma sto cercando una soluzione più semplice.
EDP,

1
Leggi: esegui l'installazione avviata dal client anziché avviata dal server
EDP

@EDP: non c'è modo di ottenere ciò che desideri semplicemente modificando la posizione di avvio. È possibile scrivere uno script che estrae il file da un server al primo avvio di RPi, quindi fare in modo che quel programma rimuova lo script di avvio. Ciò richiederebbe di utilizzare un'immagine personalizzata.
Jacobm001

1
"Uno script sul desktop" - Quale script sul mio desktop? Al primo avvio, non ci sono script sul desktop. "Fallo attraverso SSH": al primo avvio il Pi potrebbe non avere le impostazioni Ethernet o WLAN corrette.
Thomas Weller,

Non hai nemmeno bisogno di threading controlla il progetto fabric. IIRC il suo unico rewuirement è SSH
Steve Robillard,

1

Probabilmente, se stai bene modificando l'immagine per eseguire automaticamente uno script al primo avvio, potresti semplicemente modificare l'immagine come farebbe il tuo script, quindi salvare quella scheda SD in un file di immagine e usarla per flash Schede SD che utilizzerai con i nuovi RPis. Ad esempio, se vuoi che tutti i tuoi RPis abbiano una certa voce in entrata /etc/fstab, potresti semplicemente modificare /etc/fstabse stesso invece di scrivere uno script che fa la modifica.

Se è assolutamente necessario azioni script (ad esempio, se ogni immagine deve essere modificato in modo diverso), è possibile spostare il vostro /etc/rc.localper /etc/rc.bake mettere uno script in /etc/rc.localche si sostituisce con /etc/rc.bakl'ultimo comando. Quello script potrebbe eseguire da solo le prime azioni di avvio oppure, /bootse preferisci , potrebbe chiamare uno script particolare dalla partizione.

È possibile eseguire un auto-run toccando solo la /bootpartizione, fornendo al kernel un'immagine speciale di ramdisk di avvio come descritto qui . Quell'immagine conterrebbe gli script per modificare la partizione di root e quindi eliminare da sé config.txt. Non sono sicuro però se valga la pena.


-2

Potresti voler guardare il mio progetto Nard che ha una soluzione per il tuo problema:

1) A ogni scheda SD può essere assegnato un ID univoco con un normale PC Windows, come descritto qui:
http://www.arbetsmyra.dyndns.org/nard/#devsettingsid

2) Accendi tutti i tuoi Pis

3) Disabilitare il firewall del PC, se possibile

4) Aprire una finestra del prompt di DOS e eseguire il ping dell'indirizzo di trasmissione della sottorete

5) Elencare la tabella ARP con il comando di Windows "arp -a". Nell'elenco troverai gli indirizzi MAC e IP di tutti i vicini Raspberry Pi.

6) Connettiti a ciascun dispositivo con telnet (solitamente disponibile anche in Windows). La frase di benvenuto visualizzerà l'ID assegnato al passaggio 1.


Forse la mia descrizione iniziale non era abbastanza chiara. Non sto particolarmente cercando un modo per identificare i Pi. L'ho già coperto. Quello che sto cercando è eseguire uno o più comandi bash modificando il file /boot/cmdline.txt. Tutto questo senza la necessità di accedere tramite ssh - anche una volta.
EDP,

Probabilmente non sarai in grado di "pingare l'indirizzo di trasmissione della sottorete", gli Smurf Attack sono generalmente prevenuti.
CrackerJack9,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.