Use /boot/cmdline.txt para crear el script de primer arranque

11

Se han hecho muchas preguntas sobre cómo encontrar mi Pi en mi red . Otros, incluyéndome a mí, tienen problemas que requieren mucho tiempo mientras intentan desplegar un lote de Pi nuevos.

Si bien la creación de imágenes personalizadas podría ser una solución para estos problemas, me pregunto si hay otras soluciones.

Con (sólo) el /bootdirectorio abierto para el acceso en regulares máquinas (Win / OSX), ¿sería posible utilizar /boot/cmdline.txtal texto tubo a una escritura del golpe, ejecutarlo y eliminarlo después?

EDP
fuente
1
Esta pregunta está bajo discusión en Meta . Me gustaría escuchar tu opinión, si es posible. Gracias.
Thomas Weller

Respuestas:

6

Creé una versión ligeramente modificada de Raspberian-light que responde a esta necesidad: ejecuta su script /boot/firstboot.sh personalizado en el primer arranque:

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

Ned McClain
fuente
¡Gracias! 4 años después de OP finalmente hay una buena solución. No es particularmente una ciencia de cohetes para construirlo usted mismo, pero se encuentra durante muchas horas cada vez que hay un nuevo lanzamiento. En mi humilde opinión esto se debe agregar al firmware principal.
EDP
3

Para aquellos que prefieren una solución que involucre solo scripts que se hayan caído en la partición de arranque FAT32 , aquí es cómo hacerlo. [ Editar: los archivos ahora están disponibles en un proyecto pi-boot-script .]

Como se menciona en otras respuestas, involucra los argumentos de la línea de comandos con los que se inicia el kernel de Linux. Esos argumentos están en /boot/cmdline.txt .

He probado esto en Raspbian Buster (v10.1) 2019-09-26. Funciona en una tarjeta SD recién flasheada o en la imagen de disco .img descargada , que luego puede flashear en cualquier cantidad de tarjetas SD.

1. Edite los argumentos del kernel

Abra el archivo de texto /boot/cmdline.txt , elimine cualquier init=parte de él y agréguelo al final de la línea:

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

La última palabra en esta línea es el nombre de un script que debe ejecutar el núcleo como primer proceso (PID = 1) en lugar de / sbin / init . La página de ayuda de argumentos del núcleo solo dice argumentos sin .pasar al ejecutable init, por lo que no puede llamar al script desatendido.sh o cosas así.

2. Ponga el script en la partición de arranque

Guarde lo siguiente en la partición de arranque como / desatendido (el nombre que puso en la línea de 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

Este script hace una preparación necesaria (capítulo 1), luego cualquier cosa que desee hacer (2) y luego limpie y reinicie (3). Reemplace las cosas debajo de 2 con los comandos que desea ejecutar.

Para algunas tareas de configuración, es probable que necesite un arranque normal para que aparezca la red y otros servicios, por lo que el ejemplo en esta versión (explicado a continuación) solo se prepara para ejecutar una secuencia de comandos adecuada cuando se reinicia el Pi.

3. Coloque cualquier otro archivo que su script necesite en la partición de arranque

...obviamente.

Ejemplo

Junto con mi script, pongo una carpeta de carga útil / en la partición de arranque, que contiene los archivos que quiero mover a la partición de Linux. En el script desatendido arriba,

  • la línea A mueve los archivos al directorio del usuario pi. Por ejemplo, payload / home / pi / .bashrc se mueve al sistema de archivos raíz como /home/pi/.bashrc ;
  • la línea B mueve los archivos propiedad de la raíz a la partición de Linux, incluyendo payload / usr / local / bin / one-time-script.sh que se convierte en /usr/local/bin/one-time-script.sh , y similar para payload / lib / systemd / system / one-time-script.service ;
  • la línea C luego crea un enlace simbólico a ese último archivo, por lo que mi script de configuración one-time-script.sh se ejecuta en el próximo arranque.

Ese script hace varias personalizaciones que me gustan: crea y formatea otra partición FAT32 y la agrega a / etc / fstab para que el usuario pi pueda escribir en ella (para registros de aplicaciones, etc.); cambia el tamaño de la partición ext4 y el sistema de archivos al resto de la tarjeta SD; cambia la configuración regional, la zona horaria, el nombre de host (según el número de serie de la CPU), el país de WiFi; establece la red WiFi y la frase de contraseña; enciende SSH; corrige un problema de configuración de idioma para sesiones SSH; configura el arranque en una consola sin inicio de sesión automático; escribe algunos datos sobre el sistema en un archivo en la partición de arranque; y, por supuesto, elimina ese enlace simbólico para que no se vuelva a ejecutar en el arranque.

La mayoría de los usuarios encontrarán esto innecesario y preferirán usar PiBakery , pi-init2 o una imagen ext4 personalizada, que son excelentes soluciones. Prefiero esto porque puedo entenderlo completamente y no tengo que ejecutar otro software. Y también funciona: con el archivo .img en el que he puesto mis scripts, flashear una tarjeta SD + ponerla en un Pi + y dejar que se ejecute para configurarse solo lleva 6 minutos.

Fuente Encontré la idea de un script como init=argumento del núcleo, y los mountcomandos necesarios para que funcione, en el script init_resize.sh que se ejecuta por defecto para cambiar el tamaño de la partición de Linux.

Jim Danner
fuente
2

PUEDE hacer que el código se ejecute jugando con la línea de comando del núcleo. El método más obvio es reemplazar init con otra cosa. La aplicación más común de esto es lanzar un shell muy temprano en el proceso de arranque, generalmente porque necesita arreglar algo o porque todo lo demás está muy dañado, por ejemplo:

init=/bin/bash

Tenga en cuenta que en este punto del proceso de arranque, todos los sistemas de archivos todavía están montados de solo lectura. Además, hay un montón de cosas que simplemente no funcionarán correctamente. Debido a que no tiene un verdadero init ejecutándose, el apagado y el reinicio no funcionarán. Debe reboot -fvolver a montar manualmente el sistema de archivos raíz de solo lectura y llamar para reiniciar, por ejemplo.

No tengo idea si puedes pasar argumentos para golpear de esta manera. Nunca lo he intentado. En teoría, si puede pasar -ca bash, puede decirle a ese proceso de bash que haga cualquier cosa. Pero podría convertirse en un argumento bastante largo, y no sé si el núcleo permitiría tales cosas.

Lo segundo que puedes hacer. Puede copiar un ramfs inicial (initramfs) al sistema de archivos y configurar el gestor de arranque para usarlo config.txt. Hay varias formas de obtener scripts en un initramfs para hacer cosas especiales. Sin embargo, tendrá que preparar un initramfs especial para este propósito (vea initramfs-tools (8)), por lo que no estoy seguro de si esta es una mejor solución que una imagen personalizada.

Podrías incluir el script en / boot (me reí de tu sugerencia sobre máquinas "normales", pero esto sería el bit al que puedes acceder desde esas máquinas) e intentar iniciarlo usando la línea de inicio del kernel, pero los archivos en los sistemas de archivos DOS no son No es ejecutable a menos que lo haga para todo el sistema de archivos.

Si fuera yo, crearía una imagen personalizada que usa dhcp para configurar la red y que contiene un script personalizado que se ejecuta en el arranque. Este script busca un archivo específico que actúe como un indicador. Si el archivo existe, no haga nada. Si no, configure las cosas, luego cree el archivo de bandera.

Su script de configuración podría incluso extraer lo real de un servidor http. Esto significa que no tiene que hacer una nueva imagen si tiene que modificar algo.

Esa debería ser la solución menos estresante.

Una última posibilidad, pero tendrá que hacer esto en una máquina "no regular" :-) Puede montar el sistema de archivos ext4 en un dispositivo de bucle y copiar archivos en él sin escribirlo primero en la tarjeta SD. Para una imagen estándar de Raspbian Jessie, sería algo como esto:

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

Me gusta hacer un fsck forzado en mis sistemas de archivos antes de hacer imágenes. Establece el recuento de montaje en cero en el primer arranque :-)

EDITAR : Después de muchos meses y más experiencia. Quieres mirar u-boot. Reemplace el cargador de arranque con u-boot. Esto se puede hacer desde una "máquina normal". Una vez que tiene u-boot en su lugar, puede arrancar en red una distribución desde la que puede flashear fácilmente la tarjeta sd, o en teoría podría flashear la tarjeta directamente, aunque no tengo idea de lo difícil que sería.

Esencialmente, u-boot trae arranque de red a la Raspberry Pi, algo que no admite por sí solo.

izak
fuente
Ok, el sistema de archivos es de solo lectura en esa etapa. ¿Qué hay de init=script & init? El script se ejecutará en segundo plano mientras init se inicia normalmente. El script necesitaría alguna comprobación de condición al principio y, por ejemplo, continuar cuando init haya terminado su trabajo.
Thomas Weller
1
Usar & para hacer algo en el fondo es algo muy complejo. A menos que le diga al núcleo que ejecute un comando específico en un shell (por ejemplo: bash -c "algún comando y otro comando") que no funcionará, y ya creo que esta es una mala idea. Pero extendí mi respuesta y agregué la opción u-boot, algo que descubrí recientemente.
izak
1
Acabo de intentarlo ;-) No, realmente no funciona
Thomas Weller
1

No recomendaría tocar nada en el área de arranque (excepto config.txt) a menos que tenga una comprensión detallada de lo que está haciendo ese material. cmdline.txtno está diseñado para ejecutar cosas cuando comienza el RPi. Se utiliza para pasar parámetros al kernel de Linux en el arranque.

Sugeriría hacer esto a través de SSH. Un script en su escritorio podría empujar un bash / python / java / c / cualquier programa al RPi, ejecutarlo y luego eliminarlo cuando haya terminado. Agregue hilos al script en su escritorio y puede enviarlo a tantos dispositivos como desee al mismo tiempo.

Jacobm001
fuente
1
Así es como lo hacemos ahora, pero estoy buscando una solución más fácil.
EDP
1
Leer: ejecutar la configuración iniciada por el cliente en lugar de iniciada por el servidor
EDP
@EDP: no hay forma de obtener lo que desea simplemente editando la posición de arranque. Puede escribir un script que extraiga el archivo de un servidor en el primer arranque del RPi y luego hacer que ese programa elimine el script de inicio. Eso requeriría que uses una imagen personalizada.
Jacobm001
1
"Una secuencia de comandos en su escritorio" - ¿Qué secuencia de comandos en mi escritorio? En el primer arranque, no hay script en el escritorio. "Hacer todo esto a través de SSH": en el primer arranque, el Pi podría no tener la configuración correcta de Ethernet o WLAN.
Thomas Weller
Ni siquiera necesita enhebrar, consulte el proyecto de la tela. IIRC su único rewuirement es SSH
Steve Robillard
1

Podría decirse que si está de acuerdo con modificar la imagen para ejecutar automáticamente un script en el primer arranque, simplemente puede modificar la imagen de la manera en que lo haría su script, y luego guardar esa tarjeta SD en un archivo de imagen y usarla para flashear Tarjetas SD que vas a usar con nuevos RPis. Por ejemplo, si desea que todos sus RPis tengan una determinada entrada /etc/fstab, simplemente puede modificarse /etc/fstaben lugar de escribir un script que realice la modificación.

Si es absolutamente necesario acciones de secuencias de comandos (por ejemplo, si cada imagen debe ser modificado de una manera diferente), se podía mover su /etc/rc.locala /etc/rc.baky poner un guión en /etc/rc.localel que se reemplaza con /etc/rc.bakel último comando. Ese script podría realizar las primeras acciones de arranque por sí mismo, o podría llamar a un script particular desde la /bootpartición si lo prefiere.

Es posible realizar una ejecución automática tocando solo la /bootpartición, proporcionando una imagen de disco de arranque especial al núcleo como se describe aquí . Esa imagen contendría los scripts para modificar la partición raíz y luego se eliminaría automáticamente config.txt. Sin embargo, no estoy seguro de si vale la pena.

Dmitry Grigoryev
fuente
-2

Es posible que desee ver mi proyecto Nard, que tiene una solución para su problema:

1) A cada tarjeta SD se le puede asignar una identificación única con una PC con Windows normal como se describe aquí:
http://www.arbetsmyra.dyndns.org/nard/#devsettingsid

2) Enciende todos tus Pis

3) Desactiva el firewall de la PC si es posible

4) Abra una ventana de solicitud de DOS y haga ping a la dirección de transmisión de subred

5) Enumere la tabla ARP con el comando de Windows "arp -a". En la lista encontrará las direcciones MAC e IP de todas las Raspberry Pi cercanas.

6) Conéctese a cada dispositivo con telnet (generalmente también disponible en Windows). La frase de bienvenida mostrará la ID asignada en el paso 1.

Ronny Nilsson
fuente
Quizás mi descripción inicial no fue lo suficientemente clara. No busco en particular una forma de identificar los Pi. Ya tengo eso cubierto. Lo que estoy buscando es ejecutar uno o más comandos bash alterando el archivo /boot/cmdline.txt. Todo esto sin la necesidad de iniciar sesión a través de ssh, ni siquiera una vez.
EDP
Probablemente no podrá 'hacer ping a la dirección de transmisión de subred', por lo general se evitan los ataques de pitufos.
CrackerJack9