slitaz-modular view initramfs/lib/livekitlib @ rev 175

initramfs: Add new linux-live scripts support.
author Christopher Rogers <slaxemulator@gmail.com>
date Wed Mar 13 17:07:55 2013 +0000 (2013-03-13)
parents
children
line source
1 #!/bin/sh
3 # Functions library :: for Linux Live Kit scripts
4 # Author: Tomas M. <http://www.linux-live.org>
5 #
7 # =================================================================
8 # debug and output functions
9 # =================================================================
11 debug_start()
12 {
13 if grep -q debug /proc/cmdline; then
14 DEBUG_IS_ENABLED=1
15 else
16 DEBUG_IS_ENABLED=
17 fi
18 }
20 debug_log()
21 {
22 if [ "$DEBUG_IS_ENABLED" ]; then
23 echo "- debug: $*" >&2
24 log "- debug: $*"
25 fi
26 }
28 # header
29 # $1 = text to show
30 #
31 header()
32 {
33 echo """$@"""
34 }
37 # echo green star
38 #
39 echo_green_star()
40 {
41 echo -ne """* """
42 }
44 # log - store given text in /var/log/livedbg
45 log()
46 {
47 echo "$@" 2>/dev/null >>/var/log/livedbg
48 }
50 echolog()
51 {
52 echo "$@"
53 log "$@"
54 }
56 # show information about the debug shell
57 show_debug_banner()
58 {
59 echo
60 echo "====="
61 echo ": Debugging started. Here is the root shell for you."
62 echo ": Type your desired commands or hit Ctrl+D to continue booting."
63 echo
64 }
66 # debug_shell
67 # executed when debug boot parameter is present
68 #
69 debug_shell()
70 {
71 if [ "$DEBUG_IS_ENABLED" ]; then
72 show_debug_banner
73 setsid sh -c 'exec sh < /dev/tty1 >/dev/tty1 2>&1'
74 echo
75 fi
76 }
78 fatal()
79 {
80 echolog
81 header "Fatal error occured - $1"
82 echolog "Something went wrong and we can't continue. This should never happen."
83 echolog "Please reboot your computer with Ctrl+Alt+Delete ..."
84 echolog
85 setsid sh -c 'exec sh < /dev/tty1 >/dev/tty1 2>&1'
86 }
88 # get value of commandline parameter $1
89 # $1 = parameter to search for
90 #
91 cmdline_value()
92 {
93 cat /proc/cmdline | egrep -o "(^|[[:space:]])$1=[^[:space:]]+" | tr -d " " | cut -d "=" -f 2- | tail -n 1
94 }
97 # test if the script is started by root user. If not, exit
98 #
99 allow_only_root()
100 {
101 if [ "0$UID" -ne 0 ]; then
102 echo "Only root can run $(basename $0)"; exit 1
103 fi
104 }
106 # Create bundle
107 # call mksquashfs with apropriate arguments
108 # $1 = directory which will be compressed to squashfs bundle
109 # $2 = output file
110 # $3..$9 = optional arguments like -keep-as-directory or -b 123456789
111 #
112 create_bundle()
113 {
114 debug_log "create_module" "$*"
115 rm -f "$2" # overwrite, never append to existing file
116 mksquashfs "$1" "$2" -comp xz -b 512K $3 $4 $5 $6 $7 $8 $9>/dev/null
117 }
120 # Move entire initramfs tree to tmpfs mount.
121 # It's a bit tricky but is necessray to enable pivot_root
122 # even for initramfs boot image
123 #
124 transfer_initramfs()
125 {
126 if [ ! -r /lib/initramfs_escaped ]; then
127 echo "switch root from initramfs to ramfs"
128 SWITCH=/m # one letter directory
129 mkdir -p $SWITCH
130 mount -t tmpfs -o size="100%" tmpfs $SWITCH
131 cp -a /??* $SWITCH 2>/dev/null # only copy two-and-more-letter directories
132 cd $SWITCH
133 echo "This file indicates that we successfully escaped initramfs" > $SWITCH/lib/initramfs_escaped
134 exec switch_root -c /dev/console . $0
135 fi
136 }
138 copy_initramfs()
139 {
140 SWITCH=$1 # one letter directory
141 mkdir -p $SWITCH
142 SYS_DIR="dev bin etc boot lib sbin home root run usr var"
143 for dir in $SYS_DIR; do
144 cp -a /$dir /$SWITCH
145 done
147 }
149 # mount virtual filesystems like proc etc
150 #
151 init_proc_sysfs()
152 {
153 debug_log "init_proc_sysfs" "$*"
154 mkdir -p /proc /sys /etc $MEMORY
155 mount -n -t proc proc /proc
156 echo "0" >/proc/sys/kernel/printk
157 mount -n -t sysfs sysfs /sys
158 mount -n -o remount,rw rootfs /
159 ln -sf /proc/mounts /etc/mtab
160 }
162 # make sure some devices are there
163 init_devs()
164 {
165 debug_log "init_devs" "$*"
166 echo /sbin/mdev > /proc/sys/kernel/hotplug
167 mdev -s
168 modprobe zram 2>/dev/null
169 modprobe loop 2>/dev/null
170 modprobe squashfs 2>/dev/null
171 modprobe fuse 2>/dev/null
172 }
174 # Activate zram (auto-compression of RAM)
175 # Compressed RAM consumes 1/2 or even 1/4 of original size
176 # Setup static size of 500MB
177 #
178 init_zram()
179 {
180 debug_log "init_zram" "$*"
181 echo_green_star
182 echo "Setting dynamic RAM compression using ZRAM"
183 echo 536870912 > /sys/block/zram0/disksize # 512MB
184 mkswap /dev/zram0 >/dev/null
185 swapon /dev/zram0 -p 32767
186 echo 100 > /proc/sys/vm/swappiness
187 }
189 # load the AUFS kernel module if needed
190 #
191 init_aufs()
192 {
193 debug_log "init_aufs" "$*"
194 # TODO maybe check here if aufs support is working at all
195 # and procude useful error message if user has no aufs
196 modprobe aufs brs=1 2>/dev/null
197 }
199 # Setup empty union
200 # $1 = changes directory (ramfs or persistent changes)
201 # $2 = union directory where to mount the union
202 #
203 init_union()
204 {
205 debug_log "init_union" "$*"
207 echo_green_star
208 echo "Setting up union using AUFS 3"
209 mkdir -p "$1"
210 mkdir -p "$2"
211 mount -t aufs -o nowarn_perm,xino="/.xino",trunc_xino,br="$1" aufs "$2"
212 if [ $? -ne 0 ]; then dmesg | tail -n 1; fatal "can't setup union (aufs)"; fi
213 }
215 # Return device mounted for given directory
216 # $1 = directory
217 #
218 mounted_device()
219 {
220 debug_log "mounted_device" "$*"
222 local MNT TARGET
223 MNT="$1"
224 while [ "$MNT" != "/" -a "$MNT" != "." -a "$MNT" != "" ]; do
225 TARGET="$(grep -F " $MNT " /proc/mounts | cut -d " " -f 1)"
226 if [ "$TARGET" != "" ]; then
227 echo "$TARGET"
228 return
229 fi
230 MNT="$(dirname "$MNT")"
231 done
232 }
234 # Return mounted dir for given directory
235 # $1 = directory
236 #
237 mounted_dir()
238 {
239 debug_log "mounted_dir" "$*"
241 local MNT
242 MNT="$1"
243 while [ "$MNT" != "/" -a "$MNT" != "." -a "$MNT" != "" ]; do
244 if mountpoint -q "$MNT" 2>/dev/null; then
245 echo "$MNT"
246 return
247 fi
248 MNT="$(dirname "$MNT")"
249 done
250 }
252 # Make sure to mount FAT12/16/32 using vfat
253 # in order to support long filenames
254 # $1 = device
255 #
256 device_bestfs()
257 {
258 debug_log "device_bestfs" "$*"
259 local FS
261 FS="$(blkid "$1" | sed -r "s/.* TYPE=//" | sed -r 's/ .*//' | tr -d '"' | tr [A-Z] [a-z])"
262 if [ "$FS" = "msdos" -o "$FS" = "fat" -o "$FS" = "vfat" ]; then
263 FS="vfat"
264 elif [ "$FS" = "ntfs" ]; then
265 FS="ntfs-3g"
266 elif [ "$1" = "iso9660" -o "$1" = "udf" ]; then
267 echo -n ",ro"
268 else
269 echo "-t $FS"
270 fi
271 }
273 # Filesystem options for mount
274 # $1 = filesystem or '-t filesystem'
275 #
276 fs_options()
277 {
278 debug_log "fs_options" "$*"
280 if [ "$1" = "-t" ]; then
281 shift
282 fi
283 if [ "$1" = "vfat" ]; then
284 echo "-o check=s,shortname=mixed,iocharset=utf8"
285 fi
286 }
289 # Modprobe network kernel modules until a working driver is found.
290 # These drivers are (or used to be) probed in Slackware's initrd.
291 # The function returns the first device found, yet it doesn't have
292 # to be a working one, eg. if the computer has two network interfaces
293 # and ethernet cable is plugged only to one of them.
294 #
295 init_network_dev()
296 {
297 debug_log "init_network_dev" "$*"
298 local MODULE ETH
300 for MODULE in 3c59x acenic de4x5 e1000 e1000e e100 epic100 hp100 \
301 ne2k-pci pcnet32 8139too 8139cp tulip via-rhine r8169 atl1e yellowfin \
302 tg3 dl2k ns83820 atl1 b44 bnx2 skge sky2 tulip depca 3c501 3c503 \
303 3c505 3c507 3c509 3c515 ac3200 at1700 cosa cs89x0 de600 de620 e2100 \
304 eepro eexpress eth16i ewrk3 forcedeth hostess_sv11 hp-plus hp ni52 \
305 ni65 sb1000 sealevel smc-ultra sis900 smc9194 wd; do
306 modprobe $MODULE 2>/dev/null
307 ETH="$(cat /proc/net/dev | grep : | grep -v lo: | cut -d : -f 1 | tr -d " " | head -n 1)"
308 if [ "$ETH" != "" ]; then
309 echo $ETH
310 return 0
311 fi
312 rmmod $MODULE 2>/dev/null
313 done
315 # If we are here, none of the above specified modules worked.
316 # As a last chance, try to modprobe everything.
317 find /lib/modules/ | xargs -n 1 modprobe
318 cat /proc/net/dev | grep : | grep -v lo: | cut -d : -f 1 | tr -d " " | head -n 1
319 }
322 # Download data from tftp
323 # $1 = target (store downloaded files there)
324 #
325 download_data_pxe()
326 {
327 debug_log "download_data_pxe" "$*"
328 local CMD CLIENT SERVER GW MASK PORT ETH PROTOCOL
330 mkdir -p "$1/$LIVEKITNAME"
332 cmdline_value ip | while IFS=":" read CLIENT SERVER GW MASK PORT; do
333 echo_green_star >&2
334 echo "Downloading files from $SERVER ..." >&2
336 ETH=$(init_network_dev)
337 if [ "$PORT" = "" ]; then PORT="7529"; fi
339 # set IP address as given by boot paramter
340 if [ "$CLIENT" != "" -a "$MASK" != "" ]; then
341 ifconfig $ETH "$CLIENT" netmask "$MASK"
342 route add default gw "$GW"
343 else
344 # if client ip is unknown, try to get a DHCP lease
345 udhcpc -i $ETH -f -q
346 fi
348 # well known IP address of Google public DNS service
349 echo nameserver 8.8.8.8 >> /etc/resolv.conf
351 PROTOCOL=http
352 wget -q -O "$1/PXEFILELIST" "http://$SERVER:$PORT/PXEFILELIST?$(uname -r):$(uname -m)"
353 if [ $? -ne 0 ]; then
354 echo "Error downloading from http://$SERVER:$PORT, trying TFTP" >&2
355 PROTOCOL=tftp
356 tftp -g -r PXEFILELIST -l "$1/PXEFILELIST" $SERVER
357 fi
359 cat "$1/PXEFILELIST" | while read FILE; do
360 if [ "$PROTOCOL" = "http" ]; then
361 wget -O "$1/$LIVEKITNAME/$FILE" "http://$SERVER:$PORT/$FILE"
362 else
363 echo "* $FILE ..." >&2
364 tftp -g -r $FILE -l "$1/$LIVEKITNAME/$FILE" $SERVER
365 fi
366 done
367 done
369 echo "$1/$LIVEKITNAME"
370 }
372 # Find LIVEKIT data by mounting all devices
373 # If found, keep mounted, else unmount
374 # $1 = data directory target (mount here)
375 # $2 = data directory which contains compressed bundles
376 #
377 find_data_try()
378 {
379 debug_log "find_data_try" "$*"
381 local DEVICE FS FROM OPTIONS
383 mkdir -p "$1"
384 blkid | sort | cut -d: -f 1 | grep -E -v "/loop|/ram|/zram" | while read DEVICE; do
385 FROM="$2"
386 FS="$(device_bestfs "$DEVICE")"
387 OPTIONS="$(fs_options $FS)"
388 mount -r "$DEVICE" "$1" $FS $OPTIONS 2>/dev/null
390 # if the FROM parameter is actual file, mount it again as loop (eg. iso)
391 if [ -f "$1/$FROM" ]; then
392 mount -o remount,rw "$DEVICE" "$1" 2>/dev/null
393 mkdir -p "$1/../iso"
394 mount -o loop,ro "$1/$FROM" "$1/../iso" 2>/dev/null
395 FROM="../iso/$LIVEKITNAME"
396 fi
398 # search for bundles in the mounted directory
399 if [ "$(find "$1/$FROM" -maxdepth 2 -name "*.$BEXT" 2>/dev/null)" != "" ]; then
400 # we found at least one bundle/module here
401 mount -o remount,rw "$DEVICE" "$1" 2>/dev/null
402 echo "$1/$FROM" | tr -s "/" | sed -r "s:/[^/]+/[.][.]/:/:g"
403 return
404 fi
406 # unmount twice, since there could be mounted ISO as loop too. If not, it doesn't hurt
407 umount "$1" 2>/dev/null
408 umount "$1" 2>/dev/null
409 done
410 }
412 # Retry finding LIVEKIT data several times,
413 # until timeouted or until data is found
414 # $1 = timeout
415 # $2 = data directory target (mount here)
416 #
417 find_data()
418 {
419 debug_log "find_data" "$*"
421 local DATA FROM
423 if [ "$(cmdline_value ip)" != "" ]; then
424 download_data_pxe "$2"
425 return
426 fi
428 FROM="$(cmdline_value from)"
429 if [ "$FROM" = "" ]; then FROM="$LIVEKITNAME"; fi
431 echo_green_star >&2
432 echo -n "Looking for $LIVEKITNAME data in /$FROM .." | tr -s "/" >&2
433 for timeout in $(seq 1 $1); do
434 echo -n "." >&2
435 DATA="$(find_data_try "$2" "$FROM")"
436 if [ "$DATA" != "" ]; then
437 echo "" >&2
438 echo "* Found on $(mounted_device "$2")" >&2
439 echo "$DATA"
440 return
441 fi
442 sleep 1
443 done
444 echo "" >&2
446 if [ "$DATA" = "" ]; then
447 fatal "$LIVEKITNAME data not found"
448 fi
450 }
452 # Activate persistent changes
453 # $1 = data directory
454 # $2 = target changes directory
455 #
456 persistent_changes()
457 {
458 debug_log "persistent_changes" "$*"
460 local CHANGES T1 T2
462 CHANGES="$1/$(basename "$2")"
463 T1="$CHANGES/.empty"
464 T2="$T1"2
466 # Setup the directory anyway, it will be used in all cases
467 mkdir -p "$2"
469 # If persistent changes are not requested, end here
470 if grep -vq perch /proc/cmdline; then
471 return
472 fi
474 # check if changes directory exists and is writable
475 touch "$T1" 2>/dev/null && rm -f "$T1" 2>/dev/null
477 # if not, simply return back
478 if [ $? -ne 0 ]; then
479 echo "* Persistent changes not writable or not used"
480 return
481 fi
483 echo_green_star
484 echo "Testing persistent changes for posix compatibility"
485 touch "$T1" && ln -sf "$T1" "$T2" 2>/dev/null && \
486 chmod +x "$T1" 2>/dev/null && test -x "$T1" && \
487 chmod -x "$T1" 2>/dev/null && test ! -x "$T1" && \
488 rm "$T1" "$T2" 2>/dev/null
490 if [ $? -ne 0 ]; then
491 echo "* Activating dynamic sized storage for persistent changes"
492 rm "$T1" "$T2" 2>/dev/null
494 mount.dynfilefs "$CHANGES/changes.dat" 4000 "$2"
495 if [ "$(device_bestfs "$2/loop.fs" | tr -d " ")" = "-t" ]; then
496 mke2fs -F "$2/loop.fs" >/dev/null
497 fi
498 mount -o loop,sync "$2/loop.fs" "$2"
499 rmdir "$2/lost+found" 2>/dev/null
500 else
501 echo "* Activating native persistent changes"
502 mount --bind "$CHANGES" "$2"
503 fi
504 }
506 # Copy content of rootcopy directory to union
507 # $1 = data directory
508 # $2 = union directory
509 copy_rootcopy_content()
510 {
511 debug_log "copy_rootcopy_content" "$*"
513 if [ "$(ls -1 "$1/rootcopy/" 2>/dev/null)" != "" ]; then
514 echo_green_star
515 echo "Copying content of rootcopy directory..."
516 cp -a "$1"/rootcopy/* "$2"
517 fi
518 }
520 # Copy data to RAM if requested
521 # $1 = live data directory
522 # $2 = changes directory
523 #
524 copy_to_ram()
525 {
526 debug_log "copy_to_ram" "$*"
528 local MDIR MDEV RAM CHANGES
530 if grep -vq toram /proc/cmdline; then
531 echo "$1"
532 return
533 fi
535 echo "* Copying $LIVEKITNAME data to RAM..." >&2
536 RAM="$(dirname "$2")"/toram
537 mkdir -p "$RAM"
538 cp -a "$1"/* "$RAM"
539 echo "$RAM"
541 MDIR="$(mounted_dir "$1")"
542 MDEV="$(mounted_device "$1")"
543 MDEV="$(losetup $MDEV 2>/dev/null | cut -d " " -f 3)"
544 umount "$MDIR" 2>/dev/null
546 if [ "$MDEV" ]; then # iso was mounted here, try to unmount the FS it resides on too
547 MDEV="$(mounted_device "$MDEV")"
548 umount "$MDEV" 2>/dev/null
549 fi
550 }
552 # load filter
553 #
554 filter_load()
555 {
556 local FILTER
557 FILTER=$(cmdline_value load)
558 if [ "$FILTER" = "" ]; then
559 cat -
560 else
561 cat - | egrep "$FILTER"
562 fi
563 }
565 # noload filter
566 #
567 filter_noload()
568 {
569 local FILTER
570 FILTER=$(cmdline_value load)
571 if [ "$FILTER" = "" ]; then
572 cat -
573 else
574 cat - | egrep -v "$FILTER"
575 fi
576 }
578 # sort modules by number even if they are in subdirectory
579 #
580 sortmod()
581 {
582 cat - | sed -r "s,(.*/(.*)),\\2:\\1," | sort -n | cut -d : -f 2-
583 }
585 # Mount squashfs filesystem bundles
586 # and add them to union
587 # $1 = directory where to search for bundles
588 # $2 = directory where to mount bundles
589 # $3 = directory where union is mounted
590 #
591 union_append_bundles()
592 {
593 debug_log "union_append_bundles" "$*"
595 local BUN
597 echo_green_star
598 echo "Adding bundles to union"
599 ( ls -1 "$1" | sort -n ; cd "$1" ; find base/ 2>/dev/null ; find modules/ 2>/dev/null | sortmod | filter_load) | grep '[.]'$BEXT'$' | filter_noload | while read BUNDLE; do
600 echo "* $BUNDLE"
601 BUN="$(basename "$BUNDLE")"
602 mkdir -p "$2/$BUN"
603 mount -o loop -t squashfs "$1/$BUNDLE" "$2/$BUN"
604 mount -n -o remount,add:1:$2/$BUN=rr aufs $3
605 done
606 }
608 # Create empty fstab properly
609 # $1 = root directory
610 #
611 fstab_create()
612 {
613 debug_log "fstab_create" "$*"
615 local FSTAB
616 FSTAB="$1/etc/fstab"
617 echo aufs / aufs defaults 0 0 > $FSTAB
618 echo proc /proc proc defaults 0 0 >> $FSTAB
619 echo sysfs /sys sysfs defaults 0 0 >> $FSTAB
620 echo devpts /dev/pts devpts gid=5,mode=620 0 0 >> $FSTAB
621 echo tmpfs /dev/shm tmpfs defaults 0 0 >> $FSTAB
622 }
625 # Change root and execute init
626 # $1 = where to change root
627 #
628 change_root()
629 {
630 debug_log "change_root" "$*"
632 umount /proc
633 umount /sys
635 cd "$1"
637 # make sure important device files and directories are in union
638 mkdir -p boot dev proc sys tmp mnt run
639 chmod 1777 tmp
640 if [ ! -e dev/console ]; then mknod dev/console c 5 1; fi
641 if [ ! -e dev/tty ]; then mknod dev/tty c 5 0; fi
642 if [ ! -e dev/tty0 ]; then mknod dev/tty0 c 4 0; fi
643 if [ ! -e dev/tty1 ]; then mknod dev/tty1 c 4 1; fi
644 if [ ! -e dev/null ]; then mknod dev/null c 1 3; fi
645 if [ ! -e sbin/fsck.aufs ]; then ln -s /bin/true sbin/fsck.aufs; fi
647 # find chroot and init
648 if [ -x bin/chroot ]; then CHROOT=bin/chroot; fi
649 if [ -x sbin/chroot ]; then CHROOT=sbin/chroot; fi
650 if [ -x usr/bin/chroot ]; then CHROOT=usr/bin/chroot; fi
651 if [ -x usr/sbin/chroot ]; then CHROOT=usr/sbin/chroot; fi
652 if [ "$CHROOT" = "" ]; then fatal "Can't find executable chroot command"; fi
654 if [ -x bin/init ]; then INIT=bin/init; fi
655 if [ -x sbin/init ]; then INIT=sbin/init; fi
656 if [ "$INIT" = "" ]; then fatal "Can't find executable init command"; fi
658 mkdir -p mnt/live
659 mount -n -o remount,ro aufs .
660 pivot_root . mnt/live
661 exec $CHROOT . $INIT < dev/console > dev/console 2>&1
662 }