armbian_rootenc_setup.sh 36 KB

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