armbian_rootenc_setup.sh 34 KB

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