使用/boot/cmdline.txt创建首次启动脚本


11

关于如何在网络上找到我的Pi的问题很多。其他人(包括我自己)在尝试部署一批新的Pi时遇到了耗时的问题。

尽管创建自定义图像可以解决这些问题,但我想知道是否还有其他解决方案。

/boot打开(仅)目录以供在常规计算机(Win / OSX)上访问时,是否可以使用它将/boot/cmdline.txt文本通过管道传输到bash脚本,运行它并随后将其删除?


1
这个问题正在Meta讨论中。如果可以的话,我想听听您的意见。谢谢。
托马斯·韦勒

Answers:


6

我创建了一个轻度修改的Raspberian-light版本来解决此需求-它在首次启动时执行您的自定义/boot/firstboot.sh脚本:

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


谢谢!OP推出4年后,终于有了一个好的解决方案。并不是特别用火箭科学来自己建造它,但是每次有新版本发布时,您都会花费很多时间。恕我直言,这应该添加到主固件。
EDP

3

对于那些只希望将脚本放到FAT32 引导分区中的脚本的解决方案的人,这里是这样做的方法。[ 编辑:这些文件现在可以在项目pi-boot-script中使用。]

如其他答案所述,它涉及启动Linux内核的命令行参数。这些参数在/boot/cmdline.txt中

我已经在Raspbian Buster(v10.1)2019-09-26上对此进行了测试。它可以在新刷新的SD卡或下载的.img磁盘映像上工作,然后可以将其闪存到任意数量的SD卡上。

1.编辑内核参数

打开文本文件/boot/cmdline.txtinit=从其中删除任何部分,并将其添加到该行的末尾:

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.将脚本需要的任何其他文件放在引导分区上

...明显。

与脚本一起,我在启动分区上放置了一个目录payload /,其中包含要移至Linux分区的文件。在上面无人参与的脚本中,

  • A行将文件移动到pi用户的目录中。例如,有效负载/home/pi/.bashrc作为/home/pi/.bashrc移入根文件系统;
  • B行将根目录拥有的文件移动到Linux分区中,其中包括有效负载/usr/local/bin/one-time-script.sh,它变为/usr/local/bin/one-time-script.sh,对于payload / lib / systemd / system / one-time-script.service ;
  • C行然后创建到该最后一个文件的符号链接,因此我的配置脚本onetime-script.sh在下次启动时运行。

该脚本可以进行各种我喜欢的自定义:它创建并格式化另一个FAT32分区,并将其添加到/ etc / fstab中,以便pi用户可以对其进行写入(用于应用程序日志等);将ext4分区和文件系统调整为SD卡的其余部分;更改语言环境,时区,主机名(基于CPU序列号),WiFi国家/地区;设置WiFi网络和密码;开启SSH;修复了SSH会话的语言设置问题;配置引导到控制台而无需自动登录;将有关系统的一些数据写入引导分区上的文件;当然,它会删除该符号链接,因此它不会在启动时再次运行。

大多数用户会发现这是不必要的,他们更喜欢使用PiBakerypi-init2或自定义ext4映像,这是很好的解决方案。我更喜欢这样做,因为我可以完全理解它,而不必运行其他软件。而且它也可以正常工作:使用.img文件,我将脚本放入其中,所有操作都需要刷新SD卡+将其放入Pi中+使其运行以自行配置需要6分钟。

来源我在默认运行的init_resize.sh脚本中找到了将脚本作为init=内核参数以及mount使该脚本工作所需的命令的想法,以调整Linux分区的大小。


2

您可以通过混淆内核命令行来执行代码。最明显的方法是用其他东西代替init。最常见的应用是在引导过程的早期就启动外壳程序,通常是因为您需要修复某些东西,或者因为其他所有东西都严重损坏,例如:

init=/bin/bash

请记住,在引导过程的这一点上,文件系统都仍以只读方式安装。此外,还有很多事情将无法正常工作。因为您没有真正的init运行,所以关机和重新启动将不起作用。例如,您必须手动以只读方式重新挂载根文件系统并调用reboot -f以重新引导。

我不知道您是否可以通过这种方式将参数传递给bash。我从未尝试过。从理论上讲,如果您可以传递-c给bash,则可以告诉bash进程可以执行任何操作。但这可能会变成一个相当长的争论,而且我不知道内核是否会允许这样的事情。

您可以做的第二件事。您可以将初始ramfs(initramfs)复制到文件系统,并配置bootloader以在中使用它config.txt。有几种方法可以将脚本放入initramfs中以执行特殊操作。但是,您必须为此准备一个特殊的initramfs(请参阅initramfs-tools(8)),所以我不确定这是否比自定义图像更好的解决方案。

您可以将脚本包含在/ boot中(我对您对“常规”计算机的建议很满意,但这是您可以从这些计算机访问的位),然后尝试使用内核init行启动该脚本,但是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,它本身并不支持某些功能。


好的,文件系统在那个阶段是只读的。那init=script & init呢 初始化正常启动时,该脚本将在后台运行。该脚本在开始时需要进行一些条件检查,例如,在init完成工作后继续。
托马斯·韦勒

1
使用&设置背景为空。除非您告诉内核在外壳程序中运行特定命令(例如:bash -c“某条命令和另一条命令”),否则该命令将不起作用,而且我已经认为这是个坏主意。但是我确实扩展了答案,并添加了u-boot选项,这是我最近发现的。
izak

1
刚尝试完;-)不,它真的不起作用
Thomas Weller

1

我不建议您触摸引导区中的任何内容(除外config.txt),除非您对这些东西在做什么有所了解。cmdline.txtRPi启动时并不是为了运行事物而设计的。它用于在启动时将参数传递给Linux内核。

我建议通过SSH完成所有操作。桌面上的脚本可以将bash / python / java / c /任何程序推送到RPi,执行该程序,然后在完成后将其删除。将线程添加到桌面上的脚本中,然后可以将其同时发送到任意数量的设备。


1
我们现在就是这样做的,但是我正在寻找一个更简单的解决方案。
EDP​​ 2016年

1
阅读:运行安装程序由客户端启动而不是由服务器启动
EDP​​ 2016年

@EDP:仅通过编辑启动位置就无法获得所需的内容。您可以编写一个脚本,该脚本在RPi首次启动时从服务器中提取文件,然后让该程序删除启动脚本。这将需要您使用自定义图像。
Jacobm001

1
“您桌面上的脚本”-我桌面上的哪个脚本?首次启动时,桌面上没有脚本。“通过SSH完成所有操作”-Pi首次启动时可能没有正确的以太网或WLAN设置。
托马斯·韦勒

您甚至不需要线程就可以检出Fabric项目。IIRC唯一的重新部署是SSH
Steve Robillard

1

可以说,如果您可以通过在第一次启动时修改映像以自动运行脚本的方式来确定,则可以按照脚本的方式修改映像,然后将该SD卡保存到映像文件中并使用它进行闪存您要与新RPis一起使用的SD卡。例如,如果您希望所有RPis在中都有某个条目/etc/fstab,则可以简单地进行/etc/fstab自我修改,而无需编写脚本进行修改。

如果你绝对需要脚本操作(例如,如果每个图像应该以不同的方式进行修改),你可以将你/etc/rc.local/etc/rc.bak放一个脚本/etc/rc.local与替换自己/etc/rc.bak在最后的命令。该脚本本身可以执行第一次引导操作,或者可以根据需要从/boot分区中调用特定脚本。

这是可能通过触摸仅作自动运行/boot的分区,通过描述内核提供一个特殊的引导ramdisk映像这里。该映像将包含用于修改根分区,然后从中自删除的脚本config.txt。我不确定是否值得这样做。


-2

您可能想看一下我的Nard项目,它为您的问题提供了解决方案:

1)可以使用常规Windows PC为每个SD卡分配唯一的ID,如下所述:http :
//www.arbetsmyra.dyndns.org/nard/#devsettingsid

2)开启所有Pi

3)尽可能禁用PC防火墙

4)打开DOS提示符窗口并ping子网广播地址

5)使用Windows命令“ arp -a”列出ARP表。在列表中,您将找到附近所有Raspberry Pi的MAC和IP地址。

6)使用telnet连接到每个设备(通常在Windows中也可用)。欢迎词将显示在步骤1中分配的ID。


也许我的最初描述不够清楚。我并不是特别在寻找一种识别Pi的方法。我已经覆盖了。我正在寻找的是通过更改/boot/cmdline.txt文件来运行一个或多个bash命令。这一切都不需要通过ssh登录-甚至一次。
EDP​​ 2015年

您可能将无法“ ping子网广播地址”,通常可以防止蓝精灵攻击。
CrackerJack9
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.