Используйте /boot/cmdline.txt для создания скрипта первой загрузки

11

Было задано много вопросов о том, как найти мой Pi в моей сети . Другие, в том числе и я, сталкиваются с трудоемкими проблемами при попытке развернуть партию свежих Пи.

Хотя создание пользовательских изображений может быть решением этих проблем, мне интересно, есть ли другие решения.

Если (только) /bootкаталог, открытый для доступа на обычных машинах (Win / OSX), можно ли будет использовать /boot/cmdline.txtдля передачи текста в сценарий bash, его запуска и последующего удаления?

электронная обработка данных
источник
1
Этот вопрос обсуждается на Meta . Я хотел бы услышать ваше мнение, если это возможно. Спасибо.
Томас Уэллер

Ответы:

6

Я создал слегка модифицированную версию Raspberian-light, которая решает эту проблему - он выполняет ваш собственный скрипт /boot/firstboot.sh при первой загрузке:

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

Нед МакКлейн
источник
Спасибо! Через 4 года после ОП наконец-то есть хорошее решение. Не особенно ракетостроение, чтобы создать его самостоятельно, но вы будете тратить много часов каждый раз, когда выходит новый релиз. ИМХО это надо добавить в основную прошивку.
EDP
3

Для тех, кто предпочитает решение, включающее только сценарии, добавленные в загрузочный раздел FAT32 , вот как это сделать. [ Редактировать: файлы теперь доступны в проекте pi-boot-script .]

Как упоминалось в других ответах, он включает аргументы командной строки, с помощью которых запускается ядро ​​Linux. Эти аргументы находятся в /boot/cmdline.txt .

Я проверил это на Raspbian Buster (v10.1) 2019-09-26. Он работает на недавно перепрошитой SD-карте или на загруженном образе диска .img , который затем можно перенести на любое количество SD-карт.

1. Отредактируйте аргументы ядра

Откройте текстовый файл /boot/cmdline.txt , удалите init=из него любую часть и добавьте в конце строки:

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

Последнее слово в этой строке - это имя скрипта, который будет запускаться ядром в качестве первого процесса (PID = 1) вместо / sbin / init . На странице справки по параметрам ядра указываются только аргументы, которые не .передаются исполняемому файлу init, поэтому вы не можете назвать скрипт unattended.sh или тому подобное.

2. Поместите скрипт в загрузочный раздел

Сохраните следующее в загрузочный раздел как / unattended (имя, которое вы вводите в командной строке):

# 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

Этот скрипт выполняет некоторую необходимую подготовку (глава 1), затем все, что вы хотите сделать (2), а затем очистите и перезагрузите компьютер (3). Замените содержимое под 2 с командами, которые вы хотите запустить.

Для некоторых задач по настройке вам, вероятно, понадобится обычная загрузка для запуска сетевых и других служб, поэтому пример в этой версии (описанный ниже) готовит только надлежащий сценарий для запуска при перезагрузке Pi.

3. Поместите все остальные файлы, которые нужны вашему скрипту, в загрузочный раздел

... очевидно.

пример

Вместе со своим сценарием я поместил полезную папку / в загрузочный раздел, в котором содержатся файлы, которые я хочу переместить в раздел Linux. В сценарии без присмотра выше,

  • строка A перемещает файлы в каталог пользователя pi. Например, полезная нагрузка / home / pi / .bashrc перемещается в корневую файловую систему как /home/pi/.bashrc ;
  • строка B перемещает файлы с правами root в раздел Linux, включая payload / usr / local / bin / one-time-script.sh, который становится /usr/local/bin/one-time-script.sh и аналогичным для payload / lib / systemd / system / one-time-script.service ;
  • Затем строка C создает символическую ссылку на этот последний файл, поэтому мой скрипт конфигурации one-time-script.sh запускается при следующей загрузке.

Этот скрипт выполняет различные настройки, которые мне нравятся: он создает и форматирует другой раздел FAT32 и добавляет его в / etc / fstab, чтобы пользователь pi мог писать в него (для журналов приложений и т. Д.); изменяет размеры раздела ext4 и файловой системы на оставшуюся часть SD-карты; меняет локаль, часовой пояс, имя хоста (в зависимости от серийного номера процессора), страну WiFi; устанавливает WiFi сеть и пароль; включает SSH; исправляет проблему языковых настроек для сессий SSH; настраивает загрузку в консоли без автоматического входа в систему; записывает некоторые данные о системе в файл загрузочного раздела; и, конечно, он удаляет эту символическую ссылку, чтобы она не запускалась снова при загрузке.

Большинство пользователей сочтут это ненужным и предпочитают использовать PiBakery , pi-init2 или пользовательский образ ext4, которые являются отличными решениями. Я предпочитаю это, потому что я полностью понимаю это, и мне не нужно запускать другое программное обеспечение. И это также работает: с файлом .img, в который я поместил свои сценарии, все перепрошивки SD-карты + установка ее в Pi +, позволяющая запустить ее для настройки, занимает 6 минут.

Источник Я нашел идею сценария в качестве init=аргумента ядра и mountкоманды, необходимые для его работы, в сценарии init_resize.sh, который запускается по умолчанию для изменения размера раздела Linux.

Джим Даннер
источник
2

Вы МОЖЕТЕ заставить код выполняться, путаясь с командной строкой ядра. Самый очевидный способ - заменить init чем-то другим. Наиболее распространенным применением этого является запуск оболочки в самом начале процесса загрузки, обычно потому, что вам нужно что-то исправить или потому что все остальное очень сильно сломано, например:

init=/bin/bash

Помните, что на этом этапе процесса загрузки файловые системы все еще монтируются только для чтения. Кроме того, есть множество вещей, которые просто не будут работать правильно. Поскольку у вас нет реального запуска init, выключение и перезагрузка не будут работать. Вы должны вручную перемонтировать корневую файловую систему только для чтения и вызвать reboot -fперезагрузку, например.

Я понятия не имею, можете ли вы передать аргументы bash таким способом. Я никогда не пробовал. Теоретически, если вы можете перейти -cк bash, вы можете сказать, что процесс bash делает что угодно. Но это может превратиться в довольно длинный аргумент, и я не знаю, допустит ли ядро ​​такие вещи.

Второе, что вы можете сделать. Вы можете скопировать начальные ramfs (initramfs) в файловую систему и настроить загрузчик для его использования в config.txt. Есть несколько способов получить скрипты в initramfs для выполнения специальных задач. Вы должны будете подготовить специальные initramfs для этой цели (см. Initramfs-tools (8)), поэтому я не уверен, что это лучшее решение, чем пользовательский образ.

Вы можете включить скрипт в / boot (я посмеялся над вашим предложением о «обычных» машинах, но это будет бит, к которому вы можете получить доступ с этих машин) и попытаться запустить его, используя строку инициализации ядра, но файлы в файловых системах dos не отображаются. не исполняемый, если вы не сделаете это для всей файловой системы.

Если бы это был я, я бы сделал собственный образ, который использует dhcp для настройки сети и который содержит собственный скрипт, который запускается при загрузке. Этот скрипт проверяет определенный файл, который действует как флаг. Если файл существует, ничего не делать. Если нет, настройте параметры, а затем создайте файл флага.

Ваш скрипт конфигурации может даже получить реальную вещь с http-сервера. Это означает, что вам не нужно создавать новое изображение, если вам нужно что-то настроить.

Это должно быть наименее стрессовым решением.

Последняя возможность, но вам придется сделать это на «нестандартной» машине :-) Вы можете смонтировать файловую систему ext4 на устройство цикла и скопировать на него файлы, не записывая их сначала в SDCard. Для стандартного Raspbian Jessie-образа это будет примерно так:

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

Мне нравится делать принудительный fsck на моих файловых системах перед созданием изображений. Устанавливает счетчик монтирования на ноль при первой загрузке :-)

РЕДАКТИРОВАТЬ : после многих месяцев и больше опыта. Вы хотите посмотреть на U-Boot. Замените загрузчик на u-boot. Это можно сделать с «обычной машины». После того, как вы установили u-boot, вы можете либо загрузить сетевой дистрибутив, из которого вы можете легко прошить SD-карту, либо теоретически вы можете напрямую прошить карту, хотя я понятия не имею, насколько это будет сложно.

По сути, u-boot приносит Raspberry Pi сетевую загрузку, которую он не поддерживает самостоятельно.

Izak
источник
Хорошо, на этом этапе файловая система только для чтения. Как насчет init=script & init? Скрипт будет работать в фоновом режиме, пока init запускается нормально. Сценарию потребуется некоторая проверка условий в начале и, например, продолжение, когда init завершит свою работу.
Томас Уэллер
1
Использование & для фона чего-то является оболочкой. Если вы не скажете ядру запустить определенную команду в оболочке (например, bash -c "некоторая команда и другая команда"), которая не будет работать, и я уже думаю, что это плохая идея. Но я расширил свой ответ и добавил опцию u-boot, которую я недавно обнаружил.
Изак
1
Только что закончил ;-) Нет, это действительно не работает
Томас Уэллер
1

Я не рекомендовал бы трогать что-либо в области загрузки (кроме config.txt), если у вас нет детального понимания того, что делает этот материал. cmdline.txtне предназначен для запуска вещей при запуске RPi. Он используется для передачи параметров ядру Linux при загрузке.

Я бы предложил сделать это через SSH. Сценарий на вашем рабочем столе может отправить bash / python / java / c / любую программу в RPi, выполнить ее, а затем удалить, когда она будет завершена. Добавьте потоки в сценарий на рабочем столе, и вы можете отправлять его на любое количество устройств одновременно.

Jacobm001
источник
1
Вот как мы это делаем сейчас, но я ищу более простое решение.
EDP
1
Читайте: запустите установку, инициированную клиентом, а не сервером
EDP
@EDP: нет способа получить то, что вы хотите, просто отредактировав загрузочную позицию. Вы можете написать скрипт, который извлекает файл с сервера при первой загрузке RPi, а затем заставить эту программу удалить скрипт запуска. Это потребует от вас использовать пользовательское изображение.
Jacobm001
1
«Скрипт на вашем рабочем столе» - Какой скрипт на моем рабочем столе? При первой загрузке на рабочем столе нет сценария. «Делать это через SSH» - при первой загрузке Pi может не иметь правильных настроек Ethernet или WLAN.
Томас Уэллер
Вы даже не нуждаетесь в пронизывании, проверьте проект ткани. IIRC его единственное изменение - SSH
Стив Робиллард
1

Возможно, если вы согласны с изменением образа для автоматического запуска скрипта при первой загрузке, вы можете просто изменить образ так, как это делает ваш скрипт, а затем сохранить эту SD-карту в файл изображения и использовать его для прошивки. SD-карты, которые вы собираетесь использовать с новым RPis. Например, если вы хотите, чтобы все ваши RPis имели определенную запись /etc/fstab, вы можете просто изменить /etc/fstabсебя вместо написания скрипта, который выполняет изменение.

Если вам абсолютно необходимы действия по сценарию (например, если каждое изображение должно быть изменено по-разному), вы можете переместить его /etc/rc.localв /etc/rc.bakи поместить скрипт, в /etc/rc.localкотором он будет заменен /etc/rc.bakв последней команде. Этот сценарий может сам выполнять первые действия при загрузке или может вызывать определенный сценарий из /bootраздела, если хотите.

Можно выполнить автоматический запуск, коснувшись только /bootраздела, предоставив ядру специальный образ загрузочного виртуального диска, как описано здесь . Это изображение будет содержать сценарии для изменения корневого раздела и последующего самостоятельного удаления config.txt. Я не уверен, стоит ли это того.

Дмитрий Григорьев
источник
-2

Вы можете посмотреть на мой проект Nard, в котором есть решение вашей проблемы:

1) Каждой SD-карте может быть присвоен уникальный идентификатор с обычным ПК с Windows, как описано здесь:
http://www.arbetsmyra.dyndns.org/nard/#devsettingsid

2) Включите все ваши Пис

3) Отключите брандмауэр ПК, если это возможно

4) Откройте окно приглашения DOS и пропингуйте широковещательный адрес подсети.

5) Перечислите таблицу ARP с помощью команды Windows «arp -a». В списке вы найдете MAC и IP-адреса всех соседних Raspberry Pi.

6) Подключитесь к каждому устройству с помощью telnet (обычно также доступно в Windows). Приветственная фраза будет отображать идентификатор, назначенный на шаге 1.

Ронни Нильссон
источник
Возможно, мое первоначальное описание было недостаточно ясным. Я не особо ищу способ идентификации Пи. Я уже покрыл это. Мне нужно выполнить одну или несколько команд bash, изменив файл /boot/cmdline.txt. Это все без необходимости входа в систему через SSH - даже один раз.
EDP
Вероятно, вы не сможете «пропинговать широковещательный адрес подсети», атаки Smurf обычно предотвращаются.
CrackerJack9