armbian_rootenc_setup.sh 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265
  1. #!/bin/bash
  2. PATH="$PATH:/usr/sbin:/sbin"
  3. RED="\e[31;1m" GREEN="\e[32;1m" YELLOW="\e[33;1m" BLUE="\e[34;1m" PURPLE="\e[35;1m" RESET="\e[0m"
  4. PROGNAME=$(basename $0)
  5. TITLE='Armbian Encrypted Root Filesystem Setup'
  6. CONFIG_VARS='
  7. ARMBIAN_IMAGE
  8. BOOTPART_LABEL
  9. ROOTFS_NAME
  10. DISK_PASSWD
  11. UNLOCKING_USERHOST
  12. IP_ADDRESS
  13. NETMASK
  14. ADD_ALL_MODS
  15. ADD_MODS
  16. USE_LOCAL_AUTHORIZED_KEYS
  17. '
  18. STATES='
  19. card_partitioned
  20. bootpart_copied
  21. bootpart_label_created
  22. rootpart_copied
  23. target_configured
  24. '
  25. USER_OPTS_INFO="
  26. NO_CLEANUP - no cleanup of mounts after program run
  27. FORCE_REBUILD - force full rebuild
  28. FORCE_RECONFIGURE - force reconfiguration
  29. FORCE_REFORMAT_ROOT - force reformat of encrypted root partition
  30. ADD_ALL_MODS - add all currently loaded modules to initramfs
  31. ADD_MODS y add specified modules to initramfs
  32. USE_LOCAL_AUTHORIZED_KEYS - use local 'authorized_keys' file
  33. PARTITION_ONLY - partition and create filesystems only
  34. ERASE - zero boot sector, boot partition and beginning of root partition
  35. ROOTENC_REUSE_FS - reuse existing filesystems (for development only)
  36. ROOTENC_TESTING - developer tweaks
  37. ROOTENC_PAUSE - pause along the way
  38. ROOTENC_IGNORE_APT_ERRORS - continue even if apt update fails
  39. SERIAL_CONSOLE - enable disk unlocking via serial console
  40. VERBOSE - produce verbose output
  41. "
  42. RSYNC_VERBOSITY='--info=progress2'
  43. print_help() {
  44. echo " ${PROGNAME^^}: Create an Armbian image with encrypted root filesystem
  45. USAGE: $PROGNAME [options] <SD card device name>
  46. OPTIONS: '-h' Print this help message
  47. '-C' Don't perform unmounts or clean up build directory at exit
  48. '-d' Produce tons of debugging output
  49. '-f' Force reconfiguration of target system
  50. '-F' Force a complete rebuild of target system
  51. '-m' Add all currently loaded modules to the initramfs (may help
  52. fix blank screen on bootup issues)
  53. '-o' Add specified modules to the initramfs (comma-separated list)
  54. '-M' Mount source and target systems and exit
  55. '-U' Unmount source and target systems and exit
  56. '-p' Partition and create filesystems only. Do not copy data
  57. '-R' Force reformat of encrypted root partition
  58. '-s' Use 'authorized_keys' file from working directory, if available
  59. (see below)
  60. '-v' Be more verbose
  61. '-u' Perform an 'apt upgrade' after each 'apt update'
  62. '-z' Erase boot sector and first partition of SD card before partitioning
  63. (an extra paranoia step, but it can’t hurt)
  64. For non-interactive operation, set the following variables in your environment
  65. or on the command line:
  66. ROOTFS_NAME - device mapper name of target root filesystem
  67. IP_ADDRESS - IP address of target (set to 'dhcp' for dynamic IP
  68. or 'none' to disable remote SSH unlocking support)
  69. BOOTPART_LABEL - Boot partition label of target
  70. DISK_PASSWD - Disk password of target root filesystem
  71. UNLOCKING_USERHOST - USER@HOST of remote unlocking host
  72. SERIAL_CONSOLE - Set this to 'y' to enable disk unlocking from the
  73. serial console
  74. INSTRUCTIONS FOR USE
  75. This script must be invoked as superuser on a running Armbian system.
  76. Packages will be installed using APT, so the system must be Internet-
  77. connected and its clock correctly set.
  78. If remote unlocking via SSH is desired, the unlocking host must be reachable.
  79. Alternatively, SSH public keys for the unlocking host or hosts may be listed
  80. in the file 'authorized_keys' in the current directory. This file has the
  81. same format as a standard SSH 'authorized_keys' file.
  82. Architecture of host and target (e.g. 64-bit or 32-bit ARM) must be the same.
  83. For best results, the host and target hardware should also be identical or
  84. similar. Building on a host with more memory than the target, for example,
  85. may lead to disk unlocking failure on the target. For most users, who’ll be
  86. building for the currently-running board, this point is a non-issue.
  87. 1. Place an Armbian boot image file for the target system in the current
  88. directory. For best results, the image file should match the Debian
  89. or Ubuntu release of the host system.
  90. 2. Insert a USB card reader with a blank micro-SD card for the target
  91. system into the host’s USB port.
  92. 3. Determine the SD card’s device name using 'dmesg' or 'lsblk'.
  93. 4. Invoke the script with the device name as argument. If any options
  94. are desired, they must precede the device name.
  95. If the board has an eMMC, it may be used as the target device instead of
  96. an SD card." | less
  97. }
  98. pause() {
  99. echo -ne $GREEN'(Press any key to continue)'$RESET >&$stderr_dup
  100. read
  101. no_fmsg=1
  102. }
  103. _debug_pause() { [ "$ROOTENC_PAUSE" ] && pause; true; }
  104. imsg() { echo -e "$1" >&$stdout_dup; no_fmsg=1; }
  105. imsg_nonl() { echo -ne "$1" >&$stdout_dup; no_fmsg=1; }
  106. tmsg() {
  107. no_fmsg=1
  108. [ "$ROOTENC_TESTING" ] || return 0
  109. echo -e "$1" >&$stdout_dup
  110. }
  111. warn() { echo -e "$YELLOW$1$RESET" >&$stdout_dup; no_fmsg=1; }
  112. warn_nonl() { echo -ne "$YELLOW$1$RESET" >&$stdout_dup; no_fmsg=1; }
  113. rmsg() { echo -e "$RED$1$RESET" >&$stdout_dup; no_fmsg=1; }
  114. gmsg() { echo -e "$GREEN$1$RESET" >&$stdout_dup; no_fmsg=1; }
  115. pu_msg() { echo -e "$PURPLE$1$RESET" >&$stdout_dup; no_fmsg=1; }
  116. do_partprobe() {
  117. if [ "$VERBOSE" ]; then partprobe; else partprobe 2>/dev/null; fi
  118. no_fmsg=1
  119. }
  120. _show_output() { [ "$VERBOSE" ] || exec 1>&$stdout_dup 2>&$stderr_dup; }
  121. _hide_output() { [ "$VERBOSE" ] || exec &>'/dev/null'; }
  122. bail() { exit; }
  123. die() {
  124. echo -e "$RED$1$RESET" >&$stdout_dup
  125. no_fmsg=1
  126. exit 1
  127. }
  128. _return_handler() {
  129. local funcname=${FUNCNAME[1]} exitval=$? res
  130. if [ "${funcname:0:1}" == '_' -o "$no_fmsg" ]; then
  131. no_fmsg=
  132. return 0
  133. fi
  134. if [ "$exitval" -eq 0 ]; then res='OK'; else res="False ($exitval)"; fi
  135. printf "$BLUE%-32s $res$RESET\n" "$funcname" >&$stdout_dup
  136. }
  137. _sigint_handler() {
  138. warn "\nExiting at user request"
  139. exit 1
  140. }
  141. _error_handler() {
  142. local exitval=$?
  143. warn "$(basename ${BASH_SOURCE[1]}):${BASH_LINENO[0]}: ${FUNCNAME[1]}() failed at command '$BASH_COMMAND'"
  144. rmsg "$SCRIPT_DESC exiting with error ($exitval)"
  145. }
  146. _do_header() {
  147. echo
  148. local reply
  149. if banner=$(toilet --filter border --filter gay --width 51 -s -f smbraille "$TITLE" 2>/dev/null); then
  150. while read reply; do
  151. echo -e " $reply"
  152. done <<-EOF
  153. $banner
  154. EOF
  155. else
  156. echo -n ' '
  157. echo $TITLE
  158. echo
  159. fi
  160. echo " For detailed usage information,"
  161. echo " invoke with the '-h' switch"
  162. echo
  163. }
  164. _warn_user_opts() {
  165. local out opt have_optarg text
  166. while read opt have_optarg text; do
  167. [ "$opt" ] || continue
  168. if [ "${!opt}" ]; then
  169. if [ $have_optarg == 'y' ]; then
  170. out+=" + $text (${!opt})\n"
  171. else
  172. out+=" + $text\n"
  173. fi
  174. fi
  175. done <<<$USER_OPTS_INFO
  176. if [ "$out" ]; then
  177. warn "\n The following user options are in effect:"
  178. warn_nonl "${out}"
  179. fi
  180. }
  181. _set_host_vars() {
  182. BUILD_DIR='armbian_rootenc_build'
  183. SRC_ROOT="$BUILD_DIR/src"
  184. BOOT_ROOT="$BUILD_DIR/boot"
  185. TARGET_ROOT="$BUILD_DIR/target"
  186. CONFIG_VARS_FILE="$BOOT_ROOT/.rootenc_config_vars"
  187. host_distro=$(lsb_release --short --codename)
  188. host_kernel=$(ls '/boot' | egrep '^vmlinu[xz]') # allow 'vmlinux' or 'vmlinuz'
  189. }
  190. check_sdcard_name_and_params() {
  191. local dev chk
  192. dev=$1
  193. [ "$dev" ] || die "You must supply a device name"
  194. [ "${dev:0:5}" == '/dev/' ] || dev="/dev/$dev"
  195. [ -e "$dev" ] || die "$dev does not exist"
  196. chk="$(lsblk --noheadings --nodeps --list --output=TYPE $dev 2>/dev/null)"
  197. [ "$chk" != 'disk' ] && {
  198. [ "$chk" == 'part' ] && die "$dev is a partition, not a block device!"
  199. die "$dev is not a block device!"
  200. }
  201. local pttype size nodos oversize removable non_removable part_sep
  202. pttype=$(blkid --output=udev $dev | grep TYPE | cut -d '=' -f2)
  203. size="$(lsblk --noheadings --nodeps --list --output=SIZE --bytes $dev 2>/dev/null)"
  204. removable="$(lsblk --noheadings --nodeps --list --output=RM $dev 2>/dev/null)"
  205. nodos=$([ "$pttype" -a "$pttype" != 'dos' ] && echo "Partition type is ${pttype^^}") || true
  206. oversize=$([ $size -gt 137438953472 ] && echo 'Size is > 128GiB') || true
  207. non_removable=$([ $removable -ne 0 ] || echo 'Device is non-removable')
  208. SD_INFO="$(lsblk --noheadings --nodeps --list --output=VENDOR,MODEL,SIZE $dev 2>/dev/null)"
  209. SD_INFO=${SD_INFO// / }
  210. if [ "$nodos" -o "$oversize" -o "$non_removable" ]; then
  211. warn " $dev ($SD_INFO) doesn’t appear to be an SD card"
  212. warn " for the following reasons:"
  213. if [ "$non_removable" ]; then warn " $non_removable"; fi
  214. if [ "$nodos" ]; then warn " $nodos"; fi
  215. if [ "$oversize" ]; then warn " $oversize"; fi
  216. _user_confirm ' Are you sure this is the correct device of your blank SD card?' 'no'
  217. fi
  218. SDCARD_DEVNAME=${dev:5}
  219. [ "${SDCARD_DEVNAME%[0-9]}" == $SDCARD_DEVNAME ] || part_sep='p'
  220. BOOT_DEVNAME=$SDCARD_DEVNAME${part_sep}1
  221. ROOT_DEVNAME=$SDCARD_DEVNAME${part_sep}2
  222. [ "$SDCARD_DEVNAME" ] || die 'You must supply a device name for the SD card!'
  223. pu_msg "Will write to target $dev ($SD_INFO)"
  224. }
  225. _get_user_var() {
  226. local var desc dfl prompt pat pat_errmsg vtest cprompt seen_prompt reply redo
  227. var=$1 desc=$2 dfl=$3 prompt=$4 pat=$5 pat_errmsg=$6 vtest=$7
  228. [ "$MOUNT_TARGET_ONLY" ] && [[ ! $var =~ ^(DISK_PASSWD|ROOTFS_NAME)$ ]] && {
  229. eval "$var=$dfl"
  230. return 0
  231. }
  232. while true; do
  233. if [ -z "${!var}" -o "$seen_prompt" -o "$redo" ]; then
  234. if [ "$seen_prompt" ]; then
  235. echo -n " Enter $desc: "
  236. else
  237. cprompt=
  238. while read reply; do
  239. cprompt+=" ${reply## }\n"
  240. done <<-EOF
  241. $prompt
  242. EOF
  243. echo
  244. if [ "$dfl" ]; then
  245. printf "${cprompt:0:-2} " "$dfl"
  246. else
  247. echo -ne "${cprompt:0:-2} "
  248. fi
  249. seen_prompt=1
  250. fi
  251. eval "read $var"
  252. fi
  253. redo=1
  254. if [ -z "${!var}" -a "$dfl" ]; then eval "$var=$dfl"; fi
  255. [ "${!var}" ] || {
  256. rmsg " $desc must not be empty"
  257. continue
  258. }
  259. if [ "$pat" ]; then
  260. local rpat=$pat
  261. if [ "$pat" == 'bool' ]; then
  262. rpat='^[ynYN]*$'
  263. pat_errmsg="You must type 'y' or 'n'"
  264. fi
  265. echo "${!var}" | egrep -qi "$rpat" || {
  266. rmsg " ${!var}: $pat_errmsg"
  267. continue
  268. }
  269. if [ "$pat" == 'bool' ]; then
  270. if [[ ${!var} =~ ^[Yy]$ ]]; then eval "$var=yes"; else eval "$var="; fi
  271. fi
  272. fi
  273. if [ "$vtest" ]; then
  274. $vtest || continue
  275. fi
  276. break
  277. done
  278. }
  279. _get_user_vars() {
  280. local dq='[0-9]{1,3}'
  281. _get_user_var 'IP_ADDRESS' 'IP address' '' \
  282. "Enter the IP address of the target machine.
  283. Enter 'dhcp' for a dynamic IP or 'none' for no remote SSH unlocking support
  284. IP address:" \
  285. "^(dhcp|none|$dq\.$dq\.$dq\.$dq)$" \
  286. 'malformed IP address'
  287. IP_ADDRESS=${IP_ADDRESS,,}
  288. [[ $IP_ADDRESS =~ ^(dhcp|none)$ ]] || {
  289. _get_user_var 'NETMASK' 'netmask' '255.255.255.0' \
  290. "Enter the netmask of the target machine,
  291. or hit ENTER for the default (%s): " \
  292. "^($dq\.$dq\.$dq\.$dq)$" \
  293. 'malformed netmask'
  294. }
  295. _get_user_var 'BOOTPART_LABEL' 'boot partition label' 'ARMBIAN_BOOT' \
  296. "Enter a boot partition label for the target machine,
  297. or hit ENTER for the default (%s): " \
  298. '^[A-Za-z0-9_]{1,16}$' \
  299. "Label must contain no more than 16 characters in the set 'A-Za-z0-9_'"
  300. _get_user_var 'ROOTFS_NAME' 'root filesystem device name' 'rootfs' \
  301. "Enter a device name for the encrypted root filesystem,
  302. or hit ENTER for the default (%s):" \
  303. '^[a-z0-9_]{1,48}$' \
  304. "Name must contain no more than 48 characters in the set 'a-z0-9_'" \
  305. '_test_rootfs_mounted'
  306. if [ "$MOUNT_TARGET_ONLY" ]; then
  307. local pw_prompt="Enter disk password:"
  308. else
  309. local pw_prompt="Choose a simple disk password for the installation process.
  310. Once your encrypted system is up and running, you can change
  311. the password using the 'cryptsetup' command.
  312. Enter password:"
  313. fi
  314. _get_user_var 'DISK_PASSWD' 'disk password' '' "$pw_prompt" \
  315. '^[A-Za-z0-9_ ]{1,10}$' \
  316. "Temporary disk password must contain no more than 10 characters in the set 'A-Za-z0-9_ '"
  317. if [ "$IP_ADDRESS" == 'none' ]; then
  318. UNLOCKING_USERHOST=
  319. elif [ -e 'authorized_keys' -a "$USE_LOCAL_AUTHORIZED_KEYS" ]; then
  320. UNLOCKING_USERHOST=
  321. else
  322. _get_user_var 'UNLOCKING_USERHOST' 'USER@HOST' '' \
  323. "Enter the user@host of the machine you'll be unlocking from:" \
  324. '\S+@\S+' \
  325. 'malformed USER@HOST' \
  326. '_test_unlocking_host_available'
  327. fi
  328. _get_user_var 'SERIAL_CONSOLE' 'serial console unlocking' '' \
  329. "Unlock the disk from the serial console. WARNING: enabling this will
  330. make it impossible to unlock the disk using the keyboard and monitor,
  331. though unlocking via SSH will still work.
  332. Enable unlocking via serial console? (y/n):" \
  333. 'bool'
  334. true
  335. }
  336. _test_rootfs_mounted() {
  337. [ -e "/dev/mapper/$ROOTFS_NAME" ] && {
  338. local mnt=$(lsblk --list --noheadings --output=MOUNTPOINT /dev/mapper/$ROOTFS_NAME)
  339. [ "$mnt" ] && {
  340. rmsg " Device '$ROOTFS_NAME' is in use and mounted on $mnt"
  341. return 1
  342. }
  343. }
  344. return 0
  345. }
  346. _test_unlocking_host_available() {
  347. local ul_host=${UNLOCKING_USERHOST#*@}
  348. ping -c1 $ul_host &>/dev/null || {
  349. rmsg " Unable to ping host '$ul_host'"
  350. return 1
  351. }
  352. }
  353. _test_sdcard_mounted() {
  354. local chk="$(lsblk --noheadings --list --output=MOUNTPOINT /dev/$SDCARD_DEVNAME)"
  355. [ -z "$chk" ] || {
  356. lsblk --output=NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT /dev/$SDCARD_DEVNAME
  357. die "Device /dev/$SDCARD_DEVNAME has mounted partitions!"
  358. }
  359. }
  360. get_authorized_keys() {
  361. [ -e 'authorized_keys' -a "$USE_LOCAL_AUTHORIZED_KEYS" ] || {
  362. rsync "$UNLOCKING_USERHOST:.ssh/id_*.pub" 'authorized_keys'
  363. }
  364. }
  365. _apt_update() {
  366. [ "$ROOTENC_IGNORE_APT_ERRORS" ] && set +e
  367. apt --yes update
  368. [ "$APT_UPGRADE" ] && apt --yes upgrade
  369. [ "$ROOTENC_IGNORE_APT_ERRORS" ] && set -e
  370. true
  371. }
  372. _print_pkgs_to_install() {
  373. local pkgs pkgs_ssh
  374. case $1 in
  375. 'host')
  376. case "$host_distro" in
  377. focal|bionic|buster) pkgs='cryptsetup-bin ed' ;;
  378. *) pkgs='cryptsetup ed'
  379. warn "Warning: unrecognized host distribution '$host_distro'" ;;
  380. esac ;;
  381. 'target')
  382. case "$target_distro" in
  383. focal|buster) pkgs='cryptsetup-initramfs' pkgs_ssh='dropbear-initramfs' ;;
  384. bionic) pkgs='cryptsetup' pkgs_ssh='dropbear-initramfs' ;;
  385. *) pkgs='cryptsetup' pkgs_ssh='dropbear'
  386. warn "Warning: unrecognized target distribution '$target_distro'" ;;
  387. esac
  388. [ "$IP_ADDRESS" != 'none' ] && pkgs+=" $pkgs_ssh" ;;
  389. esac
  390. for i in $pkgs; do
  391. dpkg -l $i 2>/dev/null | grep -q ^ii || echo $i
  392. done
  393. }
  394. apt_install_host() {
  395. local pkgs=$(_print_pkgs_to_install 'host')
  396. [ "$pkgs" ] && {
  397. _apt_update
  398. apt --yes install $pkgs
  399. }
  400. true
  401. }
  402. create_build_dir() {
  403. mkdir -p $BUILD_DIR
  404. mkdir -p $SRC_ROOT
  405. mkdir -p $BOOT_ROOT
  406. mkdir -p $TARGET_ROOT
  407. }
  408. umount_target() {
  409. for i in $BOOT_ROOT $TARGET_ROOT; do
  410. while mountpoint -q $i; do
  411. umount -Rl $i
  412. done
  413. done
  414. }
  415. remove_build_dir() {
  416. [ -d $TARGET_ROOT ] && rmdir $TARGET_ROOT
  417. [ -d $BOOT_ROOT ] && rmdir $BOOT_ROOT
  418. [ -d $SRC_ROOT ] && rmdir $SRC_ROOT
  419. [ -d $BUILD_DIR ] && rmdir $BUILD_DIR
  420. true
  421. }
  422. _get_device_maps() {
  423. local dm_type=$1 varname="device_maps_$1" dm_name ls mp
  424. eval "$varname="
  425. for dm_name in $(dmsetup ls | awk '{print $1}'); do
  426. [ $(lsblk --noheadings --nodeps -o fstype /dev/mapper/$dm_name) == 'ext4' ] || continue
  427. ls=$(findmnt -n --source /dev/mapper/$dm_name | awk '{print $1}')
  428. if [ "$ls" -a "$dm_type" == 'mounted_on_target' ]; then
  429. for mp in $ls; do
  430. if [ "${mp: -${#TARGET_ROOT}}" == "$TARGET_ROOT" ]; then
  431. eval "$varname+=$dm_name "
  432. fi
  433. done
  434. elif [ -z "$ls" -a "$dm_type" == 'unmounted' ]; then
  435. eval "$varname+=$dm_name "
  436. fi
  437. done
  438. tmsg "$varname=[${!varname}]"
  439. }
  440. _close_device_maps() {
  441. local dm_type=$1
  442. local varname="device_maps_${dm_type}"
  443. for i in ${!varname}; do
  444. tmsg "closing $i"
  445. cryptsetup status $i > '/dev/null' && cryptsetup luksClose $i
  446. done
  447. true
  448. }
  449. _preclean() {
  450. close_loopmount
  451. _get_device_maps 'unmounted'
  452. _close_device_maps 'unmounted'
  453. _get_device_maps 'mounted_on_target'
  454. umount_target
  455. _close_device_maps 'mounted_on_target'
  456. remove_build_dir
  457. }
  458. _clean() {
  459. pu_msg "Cleaning up, please wait..."
  460. _show_output
  461. close_loopmount
  462. _get_device_maps 'mounted_on_target'
  463. umount_target
  464. update_config_vars_file
  465. _close_device_maps 'mounted_on_target'
  466. [ -e 'authorized_keys' -a -z "$USE_LOCAL_AUTHORIZED_KEYS" ] && shred -u 'authorized_keys'
  467. remove_build_dir
  468. }
  469. get_armbian_image() {
  470. ARMBIAN_IMAGE="$(ls *.img)"
  471. [ "$ARMBIAN_IMAGE" ] || die 'You must place an Armbian image in the current directory!'
  472. local count=$(echo "$ARMBIAN_IMAGE" | wc -l)
  473. [ "$count" == 1 ] || die "More than one image file present!:\n$ARMBIAN_IMAGE"
  474. }
  475. _confirm_user_vars() {
  476. echo
  477. echo " Armbian image: $ARMBIAN_IMAGE"
  478. echo " Target device: /dev/$SDCARD_DEVNAME ($SD_INFO)"
  479. echo " Root filesystem device name: /dev/mapper/$ROOTFS_NAME"
  480. echo " Target IP address: $IP_ADDRESS"
  481. [ "$NETMASK" ] && echo " Target netmask: $NETMASK"
  482. echo " Boot partition label: $BOOTPART_LABEL"
  483. echo " Disk password: $DISK_PASSWD"
  484. [ "$UNLOCKING_USERHOST" ] && echo " user@host of unlocking host: $UNLOCKING_USERHOST"
  485. echo " Serial console unlocking: ${SERIAL_CONSOLE:-no}"
  486. echo
  487. _user_confirm ' Are these settings correct?' 'yes'
  488. }
  489. setup_loopmount() {
  490. LOOP_DEV=$(losetup -f)
  491. losetup -P $LOOP_DEV $ARMBIAN_IMAGE
  492. mount ${LOOP_DEV}p1 $SRC_ROOT
  493. START_SECTOR=$(fdisk -l $LOOP_DEV -o Start | tail -n1 | tr -d ' ') # usually 32768
  494. BOOT_SECTORS=409600 # 200MB
  495. }
  496. _umount_with_check() {
  497. mountpoint -q $1 && umount $1
  498. }
  499. update_config_vars_file() {
  500. mount "/dev/$BOOT_DEVNAME" $BOOT_ROOT
  501. _print_config_vars $CONFIG_VARS_FILE
  502. umount $BOOT_ROOT
  503. }
  504. _print_states() {
  505. for i in $STATES; do
  506. echo $i: ${!i}
  507. done
  508. }
  509. _update_state_from_config_vars() {
  510. [ -e $CONFIG_VARS_FILE ] || return 0
  511. local reply
  512. while read reply; do
  513. eval "c$reply"
  514. done <<<$(cat $CONFIG_VARS_FILE)
  515. local saved_states cfgvar_changed
  516. saved_states="$(_print_states)"
  517. cfgvar_changed=
  518. [ $cARMBIAN_IMAGE != $ARMBIAN_IMAGE ] && cfgvar_changed+=' ARMBIAN_IMAGE' card_partitioned='n'
  519. [ $cBOOTPART_LABEL != $BOOTPART_LABEL ] && cfgvar_changed+=' BOOTPART_LABEL' bootpart_label_created='n'
  520. [ $cROOTFS_NAME != $ROOTFS_NAME ] && cfgvar_changed+=' ROOTFS_NAME' target_configured='n'
  521. [ $cDISK_PASSWD != $DISK_PASSWD ] && cfgvar_changed+=' DISK_PASSWD' rootpart_copied='n'
  522. [ "$UNLOCKING_USERHOST" -a "$cUNLOCKING_USERHOST" != "$UNLOCKING_USERHOST" ] && {
  523. cfgvar_changed+=' UNLOCKING_USERHOST' target_configured='n'
  524. }
  525. [ $cIP_ADDRESS != $IP_ADDRESS ] && cfgvar_changed+=' IP_ADDRESS' target_configured='n'
  526. [ "$cNETMASK" != "$NETMASK" ] && cfgvar_changed+=' NETMASK' target_configured='n'
  527. [ "$cADD_ALL_MODS" != "$ADD_ALL_MODS" ] && cfgvar_changed+=' ADD_ALL_MODS' target_configured='n'
  528. [ "$cADD_MODS" != "$ADD_MODS" ] && cfgvar_changed+=' ADD_MODS' target_configured='n'
  529. [ "$IP_ADDRESS" -a "$cUSE_LOCAL_AUTHORIZED_KEYS" != "$USE_LOCAL_AUTHORIZED_KEYS" ] && {
  530. cfgvar_changed+=' USE_LOCAL_AUTHORIZED_KEYS' target_configured='n'
  531. }
  532. [ $card_partitioned == 'n' ] && {
  533. bootpart_copied='n'
  534. bootpart_label_created='n'
  535. rootpart_copied='n'
  536. target_configured='n'
  537. }
  538. [ $bootpart_copied == 'n' ] && bootpart_label_created='n'
  539. [ $rootpart_copied == 'n' ] && target_configured='n'
  540. [ "$saved_states" != "$(_print_states)" ] && {
  541. warn "Install state altered due to changed config vars:$cfgvar_changed"
  542. for i in $STATES; do
  543. if [ "${!i}" == 'n' ]; then
  544. imsg " $i: ${RED}no$RESET"
  545. else
  546. imsg " $i: ${GREEN}yes$RESET"
  547. fi
  548. done
  549. _delete_state_files
  550. }
  551. true
  552. }
  553. _add_state_file() {
  554. local state=$1 cmd=$2
  555. if [ "$cmd" == 'target' ]; then
  556. touch "$TARGET_ROOT/boot/.rootenc_install_state/$state"
  557. else
  558. [ "$cmd" == 'mount' ] && mount "/dev/$BOOT_DEVNAME" $BOOT_ROOT
  559. mkdir -p "$BOOT_ROOT/.rootenc_install_state"
  560. touch "$BOOT_ROOT/.rootenc_install_state/$state"
  561. [ "$cmd" == 'mount' ] && umount $BOOT_ROOT
  562. fi
  563. eval "$state='y'"
  564. tmsg "added state file '$state'"
  565. }
  566. _delete_state_files() {
  567. for i in $STATES; do
  568. local fn="$BOOT_ROOT/.rootenc_install_state/$i"
  569. [ ${!i} == 'n' -a -e $fn ] && /bin/rm $fn
  570. done
  571. true
  572. }
  573. _get_state_from_state_files() {
  574. for i in $STATES; do
  575. if [ -e "$BOOT_ROOT/.rootenc_install_state/$i" ]; then
  576. eval "$i=y"
  577. else
  578. eval "$i=n"
  579. fi
  580. done
  581. }
  582. _print_state_from_state_files() {
  583. imsg 'Install state:'
  584. for i in $STATES; do
  585. if [ -e "$BOOT_ROOT/.rootenc_install_state/$i" ]; then
  586. imsg " $i: ${GREEN}yes$RESET"
  587. else
  588. imsg " $i: ${RED}no$RESET"
  589. fi
  590. done
  591. }
  592. check_install_state() {
  593. for i in $STATES; do eval "$i=n"; done
  594. if [ "$FORCE_REBUILD" ]; then
  595. return
  596. else
  597. do_partprobe
  598. lsblk --noheadings --list /dev/$SDCARD_DEVNAME -o 'NAME' | grep -q $BOOT_DEVNAME || return 0
  599. lsblk --noheadings --list /dev/$BOOT_DEVNAME -o 'FSTYPE' | grep -q 'ext4' || return 0
  600. mount "/dev/$BOOT_DEVNAME" $BOOT_ROOT
  601. _get_state_from_state_files
  602. if [ "$target_configured" == 'y' -a "$FORCE_RECONFIGURE" ]; then
  603. target_configured='n'
  604. _delete_state_files
  605. fi
  606. _print_state_from_state_files
  607. _update_state_from_config_vars
  608. _umount_with_check $BOOT_ROOT
  609. fi
  610. }
  611. close_loopmount() {
  612. while mountpoint -q $SRC_ROOT; do
  613. umount $SRC_ROOT
  614. done
  615. for i in $(losetup --noheadings --raw --list -j $ARMBIAN_IMAGE | awk '{print $1}'); do
  616. losetup -d $i
  617. done
  618. }
  619. _user_confirm() {
  620. local prompt1 prompt2 dfl_action reply
  621. prompt1=$1 dfl_action=$2
  622. if [ "$dfl_action" == 'yes' ]; then
  623. prompt2='(Y/n)'
  624. else
  625. prompt2='(y/N)'
  626. fi
  627. imsg_nonl "$prompt1 $prompt2 "
  628. read -n1 reply
  629. [ "$reply" ] && imsg ''
  630. [ "$dfl_action" == 'yes' -a -z "$reply" ] && return
  631. [ "$reply" == 'y' -o "$reply" == 'Y' ] && return
  632. warn "Exiting at user request"
  633. exit 1
  634. }
  635. erase_boot_sector_and_first_partition() {
  636. local sectors count
  637. sectors=$((START_SECTOR+BOOT_SECTORS+100))
  638. count=$(((sectors/8192)+1))
  639. pu_msg "Erasing up to beginning of second partition ($sectors sectors, ${count}M):"
  640. _show_output
  641. dd if=/dev/zero \
  642. of=/dev/$SDCARD_DEVNAME \
  643. status=progress \
  644. bs=$((512*8192)) \
  645. count=$count
  646. _hide_output
  647. }
  648. create_partition_label() {
  649. pu_msg "Creating new partition label on /dev/$SDCARD_DEVNAME"
  650. local fdisk_cmds="o\nw\n"
  651. set +e
  652. echo -e "$fdisk_cmds" | fdisk "/dev/$SDCARD_DEVNAME"
  653. set -e
  654. do_partprobe
  655. }
  656. copy_boot_loader() {
  657. local count
  658. count=$((START_SECTOR/2048))
  659. pu_msg "Copying boot loader ($START_SECTOR sectors, ${count}M):"
  660. _show_output
  661. dd if=$ARMBIAN_IMAGE \
  662. of=/dev/$SDCARD_DEVNAME \
  663. status=progress \
  664. bs=$((512*2048)) \
  665. count=$count
  666. _hide_output
  667. do_partprobe
  668. }
  669. _print_config_vars() {
  670. local outfile=$1
  671. local data="$(for i in $CONFIG_VARS; do echo "$i='${!i}'"; done)"
  672. if [ "$outfile" ]; then echo "$data" > $outfile; else echo "$data"; fi
  673. }
  674. partition_sd_card() {
  675. local p1_end p2_start fdisk_cmds bname rname fstype
  676. p1_end=$((START_SECTOR+BOOT_SECTORS-1))
  677. p2_start=$((p1_end+1))
  678. fdisk_cmds="o\nn\np\n1\n$START_SECTOR\n$p1_end\nn\np\n2\n$p2_start\n\nw\n"
  679. set +e
  680. echo -e "$fdisk_cmds" | fdisk "/dev/$SDCARD_DEVNAME"
  681. set -e
  682. do_partprobe
  683. bname="$(lsblk --noheadings --list --output=NAME /dev/$BOOT_DEVNAME)"
  684. [ "$bname" == $BOOT_DEVNAME ] || die 'Partitioning failed!'
  685. rname="$(lsblk --noheadings --list --output=NAME /dev/$ROOT_DEVNAME)"
  686. [ "$rname" == $ROOT_DEVNAME ] || die 'Partitioning failed!'
  687. # filesystem is required by call to _add_state_file(), so we must create it here
  688. fstype=$(lsblk --noheadings --list --output=FSTYPE "/dev/$BOOT_DEVNAME")
  689. [ "$fstype" == 'ext4' -a "$ROOTENC_REUSE_FS" ] || mkfs.ext4 -F "/dev/$BOOT_DEVNAME"
  690. do_partprobe
  691. _add_state_file 'card_partitioned' 'mount'
  692. }
  693. _do_partition() {
  694. imsg "All data on /dev/$SDCARD_DEVNAME ($SD_INFO) will be destroyed!!!"
  695. _user_confirm 'Are you sure you want to continue?' 'no'
  696. if [ "$ERASE" ]; then
  697. erase_boot_sector_and_first_partition
  698. else
  699. create_partition_label
  700. fi
  701. copy_boot_loader
  702. partition_sd_card
  703. }
  704. copy_system_boot() {
  705. [ "$PARTITION_ONLY" ] && {
  706. _add_state_file 'bootpart_copied' 'mount'
  707. return
  708. }
  709. mount "/dev/$BOOT_DEVNAME" $BOOT_ROOT
  710. pu_msg "Copying files to boot partition:"
  711. _show_output
  712. rsync $RSYNC_VERBOSITY --archive $SRC_ROOT/boot/* $BOOT_ROOT
  713. _hide_output
  714. [ -e "$BOOT_ROOT/boot" ] || (cd $BOOT_ROOT && ln -s . 'boot')
  715. _add_state_file 'bootpart_copied'
  716. umount $BOOT_ROOT
  717. }
  718. create_bootpart_label() {
  719. e2label "/dev/$BOOT_DEVNAME" "$BOOTPART_LABEL"
  720. do_partprobe
  721. _add_state_file 'bootpart_label_created' 'mount'
  722. }
  723. copy_system_root() {
  724. if [ "$FORCE_REFORMAT_ROOT" ] || ! cryptsetup isLuks "/dev/$ROOT_DEVNAME"; then
  725. pu_msg "Formatting encrypted root partition:"
  726. echo -n $DISK_PASSWD | cryptsetup luksFormat "/dev/$ROOT_DEVNAME" '-'
  727. fi
  728. echo $DISK_PASSWD | cryptsetup luksOpen "/dev/$ROOT_DEVNAME" $ROOTFS_NAME
  729. local fstype=$(lsblk --noheadings --list --output=FSTYPE "/dev/mapper/$ROOTFS_NAME")
  730. [ "$fstype" == 'ext4' -a "$ROOTENC_REUSE_FS" ] || mkfs.ext4 -F "/dev/mapper/$ROOTFS_NAME"
  731. [ "$PARTITION_ONLY" ] || {
  732. mount "/dev/mapper/$ROOTFS_NAME" $TARGET_ROOT
  733. pu_msg "Copying system to encrypted root partition:"
  734. _show_output
  735. rsync $RSYNC_VERBOSITY --archive --exclude=boot $SRC_ROOT/* $TARGET_ROOT
  736. _hide_output
  737. sync
  738. mkdir -p "$TARGET_ROOT/boot"
  739. touch "$TARGET_ROOT/root/.no_rootfs_resize"
  740. umount $TARGET_ROOT
  741. }
  742. cryptsetup luksClose $ROOTFS_NAME
  743. do_partprobe
  744. _add_state_file 'rootpart_copied' 'mount'
  745. }
  746. mount_target() {
  747. echo $DISK_PASSWD | cryptsetup luksOpen "/dev/$ROOT_DEVNAME" $ROOTFS_NAME
  748. mount "/dev/mapper/$ROOTFS_NAME" $TARGET_ROOT
  749. mount "/dev/$BOOT_DEVNAME" "$TARGET_ROOT/boot"
  750. local src dest args
  751. while read src dest args; do
  752. mount $args $src $TARGET_ROOT/$dest
  753. done <<-EOF
  754. udev dev -t devtmpfs -o rw,relatime,nosuid,mode=0755
  755. devpts dev/pts -t devpts
  756. tmpfs dev/shm -t tmpfs -o rw,nosuid,nodev,relatime
  757. proc proc -t proc
  758. sys sys -t sysfs
  759. EOF
  760. }
  761. _copy_to_target() {
  762. local fn=$1
  763. if [ -e $fn ]; then
  764. echo "Copying '$fn'"
  765. cat $fn > $TARGET_ROOT/$fn
  766. else
  767. imsg "Unable to copy '$fn' to target (file does not exist)"
  768. false
  769. fi
  770. }
  771. create_etc_crypttab() {
  772. local root_uuid="$(lsblk --noheadings --list --nodeps --output=UUID /dev/$ROOT_DEVNAME)"
  773. echo "$ROOTFS_NAME UUID=$root_uuid none initramfs,luks" > "$TARGET_ROOT/etc/crypttab"
  774. _display_file "$TARGET_ROOT/etc/crypttab"
  775. }
  776. copy_etc_files() {
  777. _copy_to_target '/etc/resolv.conf'
  778. _copy_to_target '/etc/hosts'
  779. set +e
  780. _copy_to_target /etc/apt/apt.conf.d/*proxy
  781. set -e
  782. }
  783. _set_target_vars() {
  784. target_distro=$(chroot $TARGET_ROOT 'lsb_release' '--short' '--codename')
  785. target_kernel=$(chroot $TARGET_ROOT 'ls' '/boot' | egrep '^vmlinu[xz]')
  786. imsg "$(printf '%-8s %-28s %s' '' 'Host' 'Target')"
  787. imsg "$(printf '%-8s %-28s %s' '' '----' '------')"
  788. imsg "$(printf '%-8s %-28s %s' 'distro:' $host_distro $target_distro)"
  789. imsg "$(printf '%-8s %-28s %s' 'kernel:' $host_kernel $target_kernel)"
  790. }
  791. _distros_match() {
  792. [ $host_distro == $target_distro ]
  793. }
  794. _kernels_match() {
  795. [ ${host_kernel%.*} == ${target_kernel%.*} ] || return 1
  796. [ ${host_kernel##*-} == ${target_kernel##*-} ]
  797. }
  798. copy_etc_files_distro_specific() {
  799. local files='/etc/apt/sources.list /etc/apt/sources.list.d/armbian.list'
  800. if _distros_match; then
  801. for i in $files; do _copy_to_target $i; done
  802. else
  803. warn 'Warning: host and target distros do not match:'
  804. for i in $files; do imsg " not copying $i"; done
  805. fi
  806. }
  807. _display_file() {
  808. local name text reply
  809. if [ "$2" ]; then
  810. name="$1"
  811. text="$2"
  812. else
  813. name=${1#$TARGET_ROOT}
  814. text="$(cat $1)"
  815. fi
  816. hl='────────────────────────────────────────'
  817. hl="$hl$hl$hl"
  818. hls=${hl:0:${#name}+1}
  819. echo "┌─$hls─┐"
  820. echo "│ $name: │"
  821. echo "├─$hls─┘"
  822. echo "$text" | sed 's/^/│ /'
  823. }
  824. edit_armbianEnv() {
  825. local file text console_arg
  826. file="$TARGET_ROOT/boot/armbianEnv.txt"
  827. ed $file <<-'EOF'
  828. g/^\s*rootdev=/d
  829. g/^\s*console=/d
  830. g/^\s*bootlogo=/d
  831. wq
  832. EOF
  833. case $SERIAL_CONSOLE in
  834. 'yes') console_arg='serial' ;;
  835. *) console_arg='display' ;;
  836. esac
  837. text="rootdev=/dev/mapper/$ROOTFS_NAME
  838. console=$console_arg
  839. bootlogo=false"
  840. echo "$text" >> $file
  841. _display_file $file
  842. }
  843. # Add the following lines to '/etc/initramfs-tools/initramfs.conf'. If
  844. # your board’s IP address will be statically configured, substitute the
  845. # correct static IP address after 'IP='. If it will be configured via
  846. # DHCP, omit the IP line entirely:
  847. edit_initramfs_conf() {
  848. local file="$TARGET_ROOT/etc/initramfs-tools/initramfs.conf"
  849. ed $file <<-'EOF'
  850. g/^\s*IP=/s/^/# /
  851. g/^\s*DEVICE=/d
  852. wq
  853. EOF
  854. [ "$IP_ADDRESS" == 'dhcp' -o "$IP_ADDRESS" == 'none' ] || {
  855. echo "IP=$IP_ADDRESS:::$NETMASK::$dev:off" >> $file
  856. }
  857. [ "$IP_ADDRESS" == 'none' ] || echo "DEVICE=eth0" >> $file
  858. _display_file $file
  859. }
  860. edit_initramfs_modules() {
  861. local modlist file hdr
  862. [ "$ADD_ALL_MODS" -o "$ADD_MODS" ] && {
  863. if ! _kernels_match; then
  864. warn 'Host and target kernels do not match. Not adding modules to initramfs'
  865. elif ! _distros_match; then
  866. warn 'Host and target distros do not match. Not adding modules to initramfs'
  867. else
  868. local g_mods='libcomposite u_ether usb_f_rndis g_ether usb_f_eem'
  869. [ "$ADD_ALL_MODS" ] && modlist=$(lsmod | cut -d ' ' -f1 | tail -n+2)
  870. [ "$ADD_MODS" ] && modlist+=$(echo; for m in ${ADD_MODS//,/ }; do echo $m; done)
  871. fi
  872. }
  873. file="$TARGET_ROOT/etc/initramfs-tools/modules"
  874. hdr="# List of modules that you want to include in your initramfs.
  875. # They will be loaded at boot time in the order below.
  876. #
  877. # Syntax: module_name [args ...]
  878. #
  879. # You must run update-initramfs(8) to effect this change.
  880. #
  881. "
  882. echo "$hdr$modlist" > $file
  883. _display_file $file
  884. }
  885. copy_authorized_keys() {
  886. local dest="$TARGET_ROOT/etc/dropbear-initramfs"
  887. mkdir -p $dest
  888. /bin/cp 'authorized_keys' $dest
  889. _display_file "$dest/authorized_keys"
  890. }
  891. create_fstab() {
  892. local boot_uuid file text
  893. boot_uuid="$(lsblk --noheadings --list --output=UUID /dev/$BOOT_DEVNAME)"
  894. file="$TARGET_ROOT/etc/fstab"
  895. text="/dev/mapper/$ROOTFS_NAME / ext4 defaults,noatime,nodiratime,commit=600,errors=remount-ro 0 1
  896. UUID=$boot_uuid /boot ext4 defaults,noatime,nodiratime,commit=600,errors=remount-ro 0 2
  897. tmpfs /tmp tmpfs defaults,nosuid 0 0"
  898. echo "$text" > $file
  899. _display_file $file
  900. }
  901. edit_dropbear_cfg() {
  902. local dest file text
  903. dest="$TARGET_ROOT/etc/dropbear-initramfs"
  904. file="$dest/config"
  905. text='DROPBEAR_OPTIONS="-p 2222"
  906. DROPBEAR=y'
  907. if [ "$IP_ADDRESS" == 'none' ]; then
  908. [ -e $file ] && rm -v $file
  909. true
  910. else
  911. mkdir -p $dest
  912. [ -e $file ] && grep -q '^DROPBEAR_OPTIONS="-p 2222"' $file || echo "$text" >> $file
  913. _display_file $file
  914. fi
  915. }
  916. create_cryptroot_unlock_sh() {
  917. local dest file text
  918. dest="$TARGET_ROOT/etc/initramfs-tools/hooks"
  919. file="$dest/cryptroot-unlock.sh"
  920. text='#!/bin/sh
  921. if [ "$1" = "prereqs" ]; then echo "dropbear-initramfs"; exit 0; fi
  922. . /usr/share/initramfs-tools/hook-functions
  923. source="/tmp/cryptroot-unlock-profile"
  924. root_home=$(echo $DESTDIR/root-*)
  925. root_home=${root_home#$DESTDIR}
  926. echo "if [ \"\$SSH_CLIENT\" ]; then /usr/bin/cryptroot-unlock; fi" > $source
  927. copy_file ssh_login_profile $source $root_home/.profile
  928. exit 0'
  929. mkdir -p $dest
  930. echo "$text" > $file
  931. chmod 755 $file
  932. _display_file $file
  933. }
  934. # begin chroot functions:
  935. apt_install_target() {
  936. local pkgs=$(_print_pkgs_to_install 'target')
  937. [ "$pkgs" ] && {
  938. echo "target packages to install: $pkgs"
  939. local ls1 ls2
  940. _show_output
  941. ls1=$(ls -l /boot/initrd.img-*)
  942. # DEBUG:
  943. # dpkg-reconfigure $pkgs # doesn't work in chroot
  944. # apt --yes purge $pkgs
  945. # apt-get --yes --purge autoremove
  946. dpkg --configure --pending --force-confdef
  947. set +e
  948. apt --yes purge 'bash-completion'
  949. apt --yes purge 'command-not-found'
  950. set -e
  951. _apt_update
  952. echo 'force-confdef' > /root/.dpkg.cfg
  953. apt --yes install $pkgs
  954. rm /root/.dpkg.cfg
  955. apt --yes autoremove
  956. ls2=$(ls -l /boot/initrd.img-*)
  957. [ "$ls1" != "$ls2" ] && initramfs_updated='y'
  958. _hide_output
  959. }
  960. true
  961. }
  962. update_initramfs() {
  963. [ "$ROOTENC_TESTING" ] && return 0
  964. _show_output
  965. local ver=$(echo /boot/vmlinu?-* | sed 's/.boot.vmlinu.-//')
  966. update-initramfs -k $ver -u
  967. _hide_output
  968. }
  969. check_initramfs() {
  970. local text chk count
  971. text="$(lsinitramfs /boot/initrd.img*)"
  972. set +e
  973. chk=$(echo "$text" | grep 'cryptsetup')
  974. count=$(echo "$chk" | wc -l)
  975. [ "$count" -gt 5 ] || { echo "$text"; die 'Cryptsetup scripts missing in initramfs image'; }
  976. _display_file "lsinitramfs /boot/initrd.img* | grep 'cryptsetup'" "$chk"
  977. [ "$IP_ADDRESS" == 'none' ] || {
  978. chk=$(echo "$text" | grep 'dropbear')
  979. count=$(echo "$chk" | wc -l)
  980. [ "$count" -gt 5 ] || { echo "$text"; die 'Dropbear scripts missing in initramfs image'; }
  981. _display_file "lsinitramfs /boot/initrd.img* | grep 'dropbear'" "$chk"
  982. chk=$(echo "$text" | grep 'authorized_keys')
  983. count=$(echo "$chk" | wc -l)
  984. [ "$count" -eq 1 ] || { echo "$text"; die 'authorized_keys missing in initramfs image'; }
  985. _display_file "lsinitramfs /boot/initrd.img* | grep 'authorized_keys'" "$chk"
  986. }
  987. set -e
  988. }
  989. configure_target() {
  990. [ "$PARTITION_ONLY" ] && return
  991. mount_target
  992. _set_target_vars
  993. copy_etc_files
  994. copy_etc_files_distro_specific
  995. edit_initramfs_conf
  996. edit_initramfs_modules
  997. [ "$IP_ADDRESS" == 'none' ] || copy_authorized_keys
  998. create_etc_crypttab
  999. create_fstab
  1000. edit_dropbear_cfg
  1001. [ "$IP_ADDRESS" == 'none' ] || create_cryptroot_unlock_sh
  1002. edit_armbianEnv
  1003. _debug_pause
  1004. _show_output # this must be done before entering chroot
  1005. /bin/cp $0 $TARGET_ROOT
  1006. export 'ROOTFS_NAME' 'IP_ADDRESS' 'target_distro' 'ROOTENC_TESTING' 'ROOTENC_PAUSE' 'ROOTENC_IGNORE_APT_ERRORS' 'APT_UPGRADE'
  1007. chroot $TARGET_ROOT "./$PROGNAME" $ORIG_OPTS 'in_target'
  1008. /bin/cp -a '/etc/resolv.conf' "$TARGET_ROOT/etc" # this could be a symlink
  1009. /bin/rm "$TARGET_ROOT/$PROGNAME"
  1010. _add_state_file 'target_configured' 'target'
  1011. }
  1012. _set_env_vars() {
  1013. shopt -s extglob
  1014. local name val
  1015. while [ $# -gt 0 ]; do
  1016. name=${1%=?*} val=${1#+([A-Z_])=}
  1017. [ "$name" == "$1" -o "$val" == "$1" ] && die "$1: illegal argument (must be in format 'NAME=value')"
  1018. eval "$name=$val"
  1019. shift
  1020. done
  1021. shopt -u extglob
  1022. }
  1023. _mount_target_and_exit() {
  1024. setup_loopmount
  1025. mount_target
  1026. _set_target_vars
  1027. rmdir $BOOT_ROOT
  1028. exit
  1029. }
  1030. # begin execution
  1031. set -e
  1032. while getopts hCfFmo:MUpRsudvz OPT
  1033. do
  1034. case "$OPT" in
  1035. h) print_help; exit ;;
  1036. C) NO_CLEANUP='y' ;;
  1037. f) FORCE_RECONFIGURE='y' ;;
  1038. F) FORCE_REBUILD='y' ;;
  1039. m) ADD_ALL_MODS='y' ;;
  1040. o) ADD_MODS=$OPTARG ;;
  1041. M) MOUNT_TARGET_ONLY='y' ;;
  1042. U) UMOUNT_TARGET_ONLY='y' ;;
  1043. p) PARTITION_ONLY='y' ;;
  1044. R) FORCE_REFORMAT_ROOT='y' ;;
  1045. s) USE_LOCAL_AUTHORIZED_KEYS='y' ;;
  1046. u) APT_UPGRADE='y' ;;
  1047. d) DEBUG='y' ;&
  1048. v) VERBOSE='y' RSYNC_VERBOSITY='--verbose' ;;
  1049. z) ERASE='y' ;;
  1050. *) exit ;;
  1051. esac
  1052. ORIG_OPTS+="-$OPT $OPTARG "
  1053. done
  1054. shift $((OPTIND-1))
  1055. trap '_return_handler' RETURN
  1056. trap '_sigint_handler' INT
  1057. trap '_error_handler' ERR
  1058. set -o functrace
  1059. set -o errtrace
  1060. exec {stdout_dup}>&1
  1061. exec {stderr_dup}>&2
  1062. [ $UID == 0 -o $EUID == 0 ] || die 'This program must be run as root!'
  1063. export HOME='/root'
  1064. [ "$DEBUG" ] && set -x
  1065. ARG1=$1; shift
  1066. _set_env_vars $@
  1067. if [ "$ARG1" == 'in_target' ]; then
  1068. SCRIPT_DESC='Target script'
  1069. _hide_output
  1070. [ "$target_distro" == 'bionic' ] && {
  1071. echo 'export CRYPTSETUP=y' > '/etc/initramfs-tools/conf.d/cryptsetup'
  1072. }
  1073. apt_install_target
  1074. [ "$initramfs_updated" ] || update_initramfs
  1075. check_initramfs
  1076. else
  1077. SCRIPT_DESC='Host script'
  1078. _do_header
  1079. _set_host_vars
  1080. get_armbian_image
  1081. apt_install_host # _preclean requires cryptsetup
  1082. _preclean
  1083. [ "$UMOUNT_TARGET_ONLY" ] && exit
  1084. check_sdcard_name_and_params $ARG1
  1085. create_build_dir
  1086. [ "$MOUNT_TARGET_ONLY" ] && warn 'Mounting source and target and exiting at user request'
  1087. _get_user_vars
  1088. _test_sdcard_mounted
  1089. [ "$MOUNT_TARGET_ONLY" ] && _mount_target_and_exit
  1090. _warn_user_opts
  1091. _confirm_user_vars
  1092. [ "$IP_ADDRESS" == 'none' ] || get_authorized_keys
  1093. [ "$NO_CLEANUP" ] || trap '_clean' EXIT
  1094. setup_loopmount
  1095. _debug_pause
  1096. check_install_state
  1097. _hide_output
  1098. [ "$card_partitioned" == 'n' ] && _do_partition
  1099. _debug_pause
  1100. [ "$bootpart_copied" == 'n' ] && copy_system_boot
  1101. [ "$bootpart_label_created" == 'n' ] && create_bootpart_label
  1102. [ "$rootpart_copied" == 'n' ] && copy_system_root
  1103. [ "$target_configured" == 'n' ] && configure_target
  1104. sync
  1105. gmsg 'All done!'
  1106. if [ "$IP_ADDRESS" != 'none' ]; then
  1107. imsg "To unlock the target disk, execute the following from the unlocking host:"
  1108. imsg " ssh -p 2222 root@${IP_ADDRESS/dhcp/TARGET_IP}"
  1109. fi
  1110. fi