-
Notifications
You must be signed in to change notification settings - Fork 245
/
init-script
executable file
·447 lines (365 loc) · 12.8 KB
/
init-script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
#!/bin/sh
#
# Hybris adaptation bootstrapping initramfs init script.
#
# Copyright (c) 2014 Jolla Oy
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License version 2 as published by the
# Free Software Foundation.
#
# Authors:
# - Tom Swindell <[email protected]>
# - David Greaves <[email protected]>
#
# This init script runs in early boot in initrd and does a switch_root
# to the real rootfs. It can also be copied into rootfs and provide
# monitoring of the boot process there too.
# Be careful - you can't run any external commands until busybox has installed.
# General logging
set -x
exec > /init.log 2>&1
echo "Running Mer Boat Loader"
BOOTLOGO=%BOOTLOGO%
ALWAYSDEBUG=%ALWAYSDEBUG%
DATA_PARTITION=%DATA_PART%
DEFAULT_OS=%DEFAULT_OS%
set_welcome_msg(){
cat <<EOF > /etc/issue.net
Welcome to the Mer/SailfishOS Boat loader debug init system.
Log so far is in /init.log
To make post-switch_root halt before starting systemd, perform:
EOF
if [ "$DONE_SWITCH" = "no" ]; then
cat <<EOF >> /etc/issue.net
touch /target/init_enter_debug2
EOF
else
cat <<EOF >> /etc/issue.net
touch /init_enter_debug2
EOF
fi
cat <<EOF >> /etc/issue.net
(When run post-switch_root, telnet is on port 2323, not 23)
EOF
HALT_BOOT="${1:-y}"
if [ "$HALT_BOOT" = "y" ]; then
cat <<EOF >> /etc/issue.net
You may inject commands into init shell process (PID 1):
To see output of commands as they're injected:
tail -f /init.log &
To run a command:
echo "ls -l /" >/init-ctl/stdin
(Be careful if you experiment with exec as you need to terminate
daemons and disable busybox hotplug handling)
To allow init to continue:
echo "continue" >/init-ctl/stdin
EOF
fi
if [ "$DONE_SWITCH" = "no" ]; then
cat <<EOF >> /etc/issue.net
In order to work safely with the device's mmc you should
echo "umount_stowaways" >/init-ctl/stdin
Then you can mount and modify exported mass storage on host. When done
echo "mount_stowaways" >/init-ctl/stdin
EOF
fi
}
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
# Default setting is rndis - add mass_storage for a debug boot
# enable using usb_setup
USB_FUNCTIONS=rndis
ANDROID_USB=/sys/class/android_usb/android0
GADGET_DIR=/config/usb_gadget
LOCAL_IP=192.168.2.15
DONE_SWITCH=no
# Are we running in real rootfs
if [ "$0" = "/init-debug" ]; then
DONE_SWITCH=yes
fi
# Get options from kernel command line
get_opt() {
for param in $(cat /proc/cmdline); do
echo "$param" | grep "^$1=*" | cut -d'=' -f2
done
}
# Minimal mounts for initrd or pre-init debug session
do_mount_devprocsys()
{
echo "########################## mounting devprocsys"
mkdir /dev
mount -t devtmpfs devtmpfs /dev
# telnetd needs /dev/pts/ entries
mkdir /dev/pts
mount -t devpts devpts /dev/pts
mkdir /proc
mkdir /sys
mount -t sysfs sysfs /sys
mount -t proc proc /proc
mkdir /config
mount -t configfs none /config
}
do_hotplug_scan()
{
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
# There is no way to know when all hotplug events have been processed :(
sleep 2
}
bootsplash() {
if [ x$BOOTLOGO = x1 ]; then
zcat /bootsplash.gz > /dev/fb0
fi
}
load_kernel_modules() {
CFG_FILE=/lib/modules/modules.load.recovery
if [ ! -f $CFG_FILE ]; then
return
fi
ln -s /lib/modules "/lib/modules/$(uname -r)"
cat $CFG_FILE | while read line; do
set -- $line
# Skip commented entries
[ "$1" = "#" ] && continue
modprobe $(basename "$1" .ko)
done
}
mount_stowaways() {
echo "########################## mounting stowaways"
if [ ! -z $DATA_PARTITION ]; then
data_subdir="$(get_opt data_subdir)"
mkdir /data
mkdir /target
mount $DATA_PARTITION /data
mount --bind /data/${data_subdir}/.stowaways/${DEFAULT_OS} /target
mkdir /target/data # in new fs
mount --bind /data/${data_subdir} /target/data
else
echo "Failed to mount /target, device node '$DATA_PARTITION' not found!" >> /diagnosis.log
fi
mount
}
umount_stowaways() {
if [ ! -z $DATA_PARTITION ]; then
umount /target/data
umount /target
umount /data
fi
}
# Sugar for accessing usb config
write() {
echo -n "$2" > "$1"
}
inject_loop() {
INJ_DIR=/init-ctl
INJ_STDIN=$INJ_DIR/stdin
mkdir $INJ_DIR
mkfifo $INJ_STDIN
echo "This entire directory is for debugging init - it can safely be removed" > $INJ_DIR/README
echo "########################## Beginning inject loop"
while : ; do
while read IN; do
if [ "$IN" = "continue" ]; then break 2;fi
$IN
done <$INJ_STDIN
done
rm -rf $INJ_DIR # Clean up if we exited nicely
echo "########################## inject loop done"
}
# This sets up the USB with whatever USB_FUNCTIONS are set to via configfs
usb_setup_configfs() {
G_USB_ISERIAL=$GADGET_DIR/g1/strings/0x409/serialnumber
mkdir $GADGET_DIR/g1
write $GADGET_DIR/g1/idVendor "0x18D1"
write $GADGET_DIR/g1/idProduct "0xD001"
mkdir $GADGET_DIR/g1/strings/0x409
write $GADGET_DIR/g1/strings/0x409/serialnumber "$1"
write $GADGET_DIR/g1/strings/0x409/manufacturer "Mer Boat Loader"
write $GADGET_DIR/g1/strings/0x409/product "$CUSTOMPRODUCT"
if echo $USB_FUNCTIONS | grep -q "rndis"; then
mkdir $GADGET_DIR/g1/functions/rndis.usb0
mkdir $GADGET_DIR/g1/functions/rndis_bam.rndis
fi
echo $USB_FUNCTIONS | grep -q "mass_storage" && mkdir $GADGET_DIR/g1/functions/storage.0
mkdir $GADGET_DIR/g1/configs/c.1
mkdir $GADGET_DIR/g1/configs/c.1/strings/0x409
write $GADGET_DIR/g1/configs/c.1/strings/0x409/configuration "$USB_FUNCTIONS"
if echo $USB_FUNCTIONS | grep -q "rndis"; then
ln -s $GADGET_DIR/g1/functions/rndis.usb0 $GADGET_DIR/g1/configs/c.1
ln -s $GADGET_DIR/g1/functions/rndis_bam.rndis $GADGET_DIR/g1/configs/c.1
fi
echo $USB_FUNCTIONS | grep -q "mass_storage" && ln -s $GADGET_DIR/g1/functions/storage.0 $GADGET_DIR/g1/configs/c.1
udc="$(get_opt androidboot.usbcontroller | head -1)"
if [ -z "$udc" ]; then
for udc_path in /sys/class/udc/* ; do
case "$udc_path" in
*dummy) true ;;
*) udc="${udc_path##*/}" ; break ;;
esac
done
fi
write /sys/class/udc/"$udc"/device/../mode peripheral
echo "$udc" > $GADGET_DIR/g1/UDC
}
# This sets up the USB with whatever USB_FUNCTIONS are set to via android_usb
usb_setup_android_usb() {
G_USB_ISERIAL=$ANDROID_USB/iSerial
write $ANDROID_USB/enable 0
write $ANDROID_USB/functions ""
write $ANDROID_USB/enable 1
usleep 500000 # 0.5 delay to attempt to remove rndis function
write $ANDROID_USB/enable 0
write $ANDROID_USB/idVendor 18D1
write $ANDROID_USB/idProduct D001
write $ANDROID_USB/iManufacturer "Mer Boat Loader"
write $ANDROID_USB/iProduct "$CUSTOMPRODUCT"
write $ANDROID_USB/iSerial "$1"
write $ANDROID_USB/functions $USB_FUNCTIONS
write $ANDROID_USB/enable 1
}
# This determines which USB setup method is going to be used
usb_setup() {
if [ -d $ANDROID_USB ]; then
usb_setup_android_usb $1
elif [ -d $GADGET_DIR ]; then
usb_setup_configfs $1
fi
}
# This lets us communicate errors to host (if it needs disable/enable then that's a problem)
usb_info() {
# make sure USB is settled
echo "########################## usb_info: $1"
sleep 1
write $G_USB_ISERIAL "$1"
}
run_debug_session() {
CUSTOMPRODUCT=$1
echo "########################## Debug session : $1"
usb_setup "Mer Debug setting up (DONE_SWITCH=$DONE_SWITCH)"
USB_IFACE=notfound
/sbin/ifconfig rndis0 $LOCAL_IP && USB_IFACE=rndis0
if [ x$USB_IFACE = xnotfound ]; then
/sbin/ifconfig usb0 $LOCAL_IP && USB_IFACE=usb0
fi
# Report for the logs
/sbin/ifconfig -a
# Unable to set up USB interface? Reboot.
if [ x$USB_IFACE = xnotfound ]; then
usb_info "Mer Debug: ERROR: could not setup USB as usb0 or rndis0"
dmesg
sleep 60 # plenty long enough to check usb on host
reboot -f
fi
# Create /etc/udhcpd.conf file.
echo "start 192.168.2.20" > /etc/udhcpd.conf
echo "end 192.168.2.90" >> /etc/udhcpd.conf
echo "lease_file /var/udhcpd.leases" >> /etc/udhcpd.conf
echo "interface $USB_IFACE" >> /etc/udhcpd.conf
echo "option subnet 255.255.255.0" >> /etc/udhcpd.conf
# Be explicit about busybox so this works in a rootfs too
echo "########################## starting dhcpd"
$EXPLICIT_BUSYBOX udhcpd
HALT_BOOT="${2:-y}"
set_welcome_msg $HALT_BOOT
if [ -z $DISABLE_TELNET ]; then
# Non-blocking telnetd
echo "########################## starting telnetd"
# We run telnetd on different ports pre/post-switch_root This
# avoids problems with an unterminated pre-switch_root telnetd
# hogging the port
$EXPLICIT_BUSYBOX telnetd -b ${LOCAL_IP}:${TELNET_DEBUG_PORT} -l /bin/sh
# For some reason this does not work in rootfs
usb_info "Mer Debug telnet on port $TELNET_DEBUG_PORT on $USB_IFACE $LOCAL_IP - also running udhcpd"
fi
if [ "$HALT_BOOT" = "y" ]; then
# Some logging output
ps -wlT
ps -ef
netstat -lnp
cat /proc/mounts
sync
# Run command injection loop = can be exited via 'continue'
inject_loop
fi
}
# writes to /diagnosis.log if there's a problem
check_kernel_config() {
echo "Checking kernel config"
if [ ! -e /proc/config.gz ]; then
echo "No /proc/config.gz. Enable CONFIG_IKCONFIG and CONFIG_IKCONFIG_PROC" >> /diagnosis.log
else
# Must be =y
for x in CONFIG_CGROUPS CONFIG_AUTOFS4_FS CONFIG_DEVTMPFS_MOUNT CONFIG_DEVTMPFS CONFIG_UNIX CONFIG_INOTIFY_USER CONFIG_SYSVIPC CONFIG_NET CONFIG_PROC_FS CONFIG_SIGNALFD CONFIG_SYSFS CONFIG_TMPFS_POSIX_ACL CONFIG_VT; do
zcat /proc/config.gz | grep -E "^$x=y\$" || echo "$x=y not found in /proc/config.gz" >> /diagnosis.log
done
# Must not be =y
for x in CONFIG_DUMMY CONFIG_SYSFS_DEPRECATED; do
zcat /proc/config.gz | grep -E "^$x=y\$" && echo "$x=y found in /proc/config.gz, must be disabled" >> /diagnosis.log
done
fi
}
# Now either initrd or rootfs sequence
if [ "$DONE_SWITCH" = "no" ]; then
EXPLICIT_BUSYBOX=""
TELNET_DEBUG_PORT=23
/bin/busybox --install -s
date
do_mount_devprocsys
load_kernel_modules
do_hotplug_scan
# Support /dev/block/mmcXXX only in initrd phase
ln -s . /dev/block
ln -s /proc/mounts /etc/mtab
check_kernel_config
bootsplash
mount_stowaways
# No target debug unless we debug here too (for now)
DBG_REASON=""
[ -e /diagnosis.log ] && DBG_REASON="Refusing to boot. See /diagnosis.log (in initrd only)"
[ "$(get_opt bootmode)" = "debug" ] && DBG_REASON="bootmode=debug on kernel command line"
[ x$ALWAYSDEBUG = x1 ] && DBG_REASON="Always debug: rndis + mass_storage"
[ -f /target/init_enter_debug ] && DBG_REASON="/init_enter_debug exists"
[ -f /target/init_disable_telnet ] && DISABLE_TELNET="y"
if ! [ "$DBG_REASON" = "" ] ; then
# During debug we export mmc too (some variations in location here)
lun=/sys/class/android_usb/f_mass_storage/lun/file
if [ -f $lun ]; then echo /dev/mmcblk0 > $lun; fi
lun=/sys/class/android_usb/f_mass_storage/lun0/file
if [ -f $lun ]; then echo /dev/mmcblk0 > $lun; fi
USB_FUNCTIONS=rndis,mass_storage
run_debug_session $DBG_REASON
# Tidy up before we switch_root (rootfs init-debug leaves these running during bootup)
killall telnetd
killall udhcpd
USB_FUNCTIONS=rndis
usb_setup "Mer Debug: done debug, disabling storage"
fi
# Disable mdev hotplug now - let udev handle it in main boot
echo "" > /proc/sys/kernel/hotplug
if [ -f "/target/init-debug" ]; then
exec switch_root /target /init-debug &> /target/init-debug-stderrout
else
# Prefer /sbin/preinit over /sbin/init
[ -x /target/sbin/preinit ] && INIT=/sbin/preinit || INIT=/sbin/init
if [ -x "/target$INIT" ]; then
exec switch_root /target $INIT --log-level=debug --log-target=kmsg &> /target/init-stderrout
fi
fi
run_debug_session "Failed to boot init in real rootfs"
else
# We're in the real rootfs running as init-debug
EXPLICIT_BUSYBOX="/bin/busybox-static"
TELNET_DEBUG_PORT=2323
do_mount_devprocsys
HALT_BOOT="n"
[ -f /init_enter_debug2 ] && HALT_BOOT="y"
[ -f /init_disable_telnet ] && DISABLE_TELNET="y"
run_debug_session "init-debug in real rootfs" $HALT_BOOT
# If we don't do this then udev will not be able to create /dev/block/*
rm /dev/block
# Now try to boot the real init
# Prefer /sbin/preinit over /sbin/init
[ -x /sbin/preinit ] && INIT=/sbin/preinit || INIT=/sbin/init
exec $INIT --log-level=debug --log-target=kmsg &> /boot/systemd_stdouterr
run_debug_session "init in real rootfs failed"
fi