install.sh (8597B)
1 #!/bin/sh -e 2 3 #TODO: really, should generate a temp file from fstab, 4 # partitions, crypttab and boot config, that would 5 # be far easier to manipulate that those damned 6 # readlink calls... 7 8 MUST_DIE=0 9 die() 10 { 11 echo "\e[31m$@\e[0m" 12 exit 1 13 } 14 15 error() 16 { 17 echo "\e[31m$@\e[0m" 18 MUST_DIE=1 19 } 20 21 warn() 22 { 23 echo "\e[33m$@\e[0m" 24 } 25 26 info() 27 { 28 echo "\e[36m$@\e[0m" 29 } 30 31 success() 32 { 33 echo "\e[32m$@\e[0m" 34 } 35 36 inst_syslinux() 37 { 38 flavor="${1:?"flavor not provided"}" 39 shift 40 part_type="${1:?"part_type not provided"}" 41 shift 42 part="${1:?"partition not provided"}" 43 shift 44 fs="${1:?"fs not provided"}" 45 shift 46 disk="${1:?"disk not provided"}" 47 shift 48 src="${1:?"src folder not provided"}" 49 shift 50 rootfs="${1:?"rootfs folder not provided"}" 51 shift 52 53 ## common stuff 54 mkfs.$fs $part 55 case "$part_type" in 56 mbr) 57 mbr="mbr.bin" 58 ;; 59 gpt) 60 mbr="gptmbr.bin" 61 ;; 62 *) 63 error "unknow partition type: $part_type" 64 return 65 esac 66 dd if=/usr/lib/SYSLINUX/$mbr of="$disk" 67 mkdir /tmp/syslinux 68 mount $part /tmp/syslinux 69 DEST_DIR=/tmp/syslinux 70 71 case "$flavor" in 72 syslinux) 73 syslinux -i $part 74 ;; 75 76 efilinux) 77 if test "$part_type" != "gpt" 78 then 79 error "EFIlinux only supports gpt partition type" 80 return 81 fi 82 83 DEST_DIR="$DEST_DIR/EFI/BOOT" 84 mkdir -p "$DEST_DIR" 85 cp /usr/lib/SYSLINUX.EFI/efi64/syslinux.efi "$DEST_DIR"/bootx64.efi 86 ;; 87 *) 88 error "flavor $flavor not supported" 89 ;; 90 esac 91 92 cp -r --no-preserve=all "$src"/* "$DEST_DIR" 93 # this is probably a bad idea, since it prevent user's customisations 94 cp "$rootfs/vmlinuz" "$rootfs/initrd.img" "$DEST_DIR" 95 umount /tmp/syslinux 96 } 97 98 TARGET="${1:?"TARGET must be provided as 1st arg"}" 99 SOURCE="${2:?"SOURCE must be provided as 2nd arg"}" 100 MIRROR="${3}" 101 102 test -z "$MIRROR" && 103 warn "No mirror is set, you are going to waste everyone's resources (including your time)" 104 105 test -d "$TARGET" -n "$(find "$TARGET" -maxdepth 0 -empty)" && 106 error "using a folder as TARGET ($TARGET) is only allowed if empty (for security reasons)" 107 test -e "$TARGET" || mkdir -p "$TARGET" || 108 error "Unable to create folder $TARGET" 109 test -b "$TARGET" -o -f "$TARGET" -o -d "$TARGET" || 110 error "invalid TARGET ($TARGET) type" 111 test -d "$SOURCE" || 112 error "SOURCE ($SOURCE) should be a folder" 113 fstab="$SOURCE/root/etc/fstab" 114 test -f "$fstab" || 115 error "fstab ($fstab) must be a file" 116 test -e /mnt/target && 117 error "/mnt/target already exists" 118 test $MUST_DIE -ne 0 && 119 die "Errors where found, exiting." 120 121 ### disk preparation 122 ## target is a disk or a file: we need to prepare it 123 ## folder targets are for building chroots. 124 if test ! -d "$TARGET" 125 then 126 ## partitionning (and artificial delay for syncing) 127 part_file="$SOURCE"/partitions 128 test -f "$part_file" || 129 die "part_file ($part_file) must be a file" 130 info "start partitioning" 131 #TODO: sfdisk is able to *not* destroy a disk's partitions. 132 # Thus, this script *should* be able to only add a new OS 133 # instead of only working in current "erase and write" way. 134 wipefs -a "$TARGET" 135 sfdisk "$TARGET" < "$part_file" 136 success "partitioning done" 137 SYNCTIME=3 138 info "syncing disks... or udev, dunno... artificial sleep sorry. (${SYNCTIME}s to wait)" 139 sync 140 sleep $SYNCTIME 141 info "syncing done" 142 143 ## encryption 144 crypttab="$SOURCE/root/etc/crypttab" 145 if test -f "$crypttab" 146 then 147 info "encrypting partitions" 148 while read mapped_dev dev keyfile params 149 do 150 if test "$keyfile" = "none" 151 then 152 keyfile="$SOURCE/$mapped_dev.key" 153 test -f "$keyfile" || 154 die "keyfile ($keyfile) is not a file" 155 156 test $(wc -l "$keyfile"|cut -f1 -d' ') -eq 0 || 157 die "keyfile contains LF characters, bad idea" 158 else 159 keyfile="$SOURCE/root/$keyfile" 160 fi 161 OPTS="$(echo -n $params | sed -e 's/[a-z]*=/--&/g' -e 's/,/ /g')" 162 part="$(eval $(echo "$dev" | sed '/PARTLABEL=/ s!PARTLABEL=\(.*\)!readlink -f /dev/disk/by-partlabel/\1!g'))" 163 cryptsetup -v $OPTS luksFormat "$part" - < "$keyfile" 164 cryptsetup open "$part" "$mapped_dev" --type luks --key-file - < "$keyfile" 165 done < "$crypttab" 166 success "encryption done" 167 fi 168 169 ## formatting partitions" 170 info "formatting partitions" 171 eval $(awk < "$fstab" '$1 ~ /[\/=]/ { if( sub( "PARTLABEL=", "$(readlink -f /dev/disk/by-partlabel/", $1 ) ) $1 = $1 ")"; printf "yes | mkfs.%s %s ;\n", $3, $1; }') 172 success "system partitions formated" 173 fi 174 175 ### system installation 176 ## mounting partitions 177 info "mounting partitions" 178 eval $(awk < $fstab '$1 !~ /^[^ =\/]+ / { printf "mkdir /mnt/target/%s; mount -t %s %s /mnt/target/%s;\n", $2, $3, $1, $2 }') 179 success "mounting done" 180 181 ## template installation 182 cp_target="$TARGET" 183 info "installing template to $cp_target" 184 test -d "$TARGET" || cp_target="/mnt/target" 185 cp -ar "$SOURCE"/root/* "$cp_target" 186 success "template installation done" 187 188 ## bootstrap 189 PKG_LIST="$(find "$SOURCE/packages" -name '*.list' -exec cat {} \+ | tr -d '\n')" 190 info "preparing to install $PKG_LIST" 191 debootstrap --print-debs --keep-debootstrap-dir --variant=minbase "--include=$PKG_LIST" $(cat $SOURCE/distname) "$cp_target" "$MIRROR" > /tmp/guess.log 192 NB_PKG=$(wc -l < /tmp/guess.log) 193 info "$NB_PKG packages to process." 194 info "downloading..." 195 debootstrap --make-tarball=/tmp/packages.tar --keep-debootstrap-dir --variant=minbase "--include=$PKG_LIST" $(cat $SOURCE/distname) "$cp_target" "$MIRROR" | sed -u -n '/Validating/ p' | pv -l -s $NB_PKG > /tmp/debootstrap.dl.log 196 info "installing..." 197 debootstrap --no-merged-usr --unpack-tarball=/tmp/packages.tar $(cat $SOURCE/distname) "$cp_target" "$MIRROR" | stdbuf -i0 pv -l -s $(($NB_PKG * 3)) > /tmp/debootstrap.inst.log 198 success "install done" 199 200 info "basic configuration steps" 201 . ./$SOURCE/config 202 success "basic configuration done" 203 204 info "cleaning target's /var/cache/apt" 205 rm -r /"$cp_target"/var/cache/apt/archives/*.deb 206 success "cleaning done" 207 208 ## if using cryptsetup, then it's needed to update initramfs 209 ## since it's *not* done (correctly?) by debootstrap 210 if test -f "$crypttab" 211 then 212 for metafs in dev sys proc 213 do 214 mount --bind /${metafs} /"$cp_target"/${metafs} 215 done 216 chroot /"$cp_target" update-initramfs -u 217 for metafs in dev sys proc 218 do 219 umount /"$cp_target"/${metafs} 220 done 221 fi 222 223 info "users creation" 224 for user in $(cut -d: -f1 "$SOURCE/users") 225 do 226 if test -d "$SOURCE/users.d/$user" 227 then 228 SKEL_DIR="-k $SOURCE/users.d/$user" 229 fi 230 231 test "$user" = "root" || useradd -R "$cp_target" -m $SKEL_DIR $user 232 done 233 chpasswd -R "$cp_target" < "$SOURCE/users" 234 success "users creation done" 235 236 ### boot-loaders configuration 237 ## for now, only triggered if the script was asked to 238 ## handle the partitions. 239 ## for now, only syslinux is supported. 240 ## Requirements for boot-loaders integration are: 241 ## * they *MUST NOT* be managed by the installed system: 242 ## this is because the user might be using this script 243 ## for multi-boots, and automatic management of boot by 244 ## a system is more than likely to cause havoc. 245 NOBOOT="won't install boot loader" 246 if test -n "$part_file" -a -f "$part_file" 247 then 248 # bootloaders require to know the partitionning type 249 part_type="$(awk < "$part_file" '/label:/ {print $2}')" 250 # the partiton where to install it is important, too 251 part_boot="$(sed -n -e '/bootable/ s/.*\<name="\([^"]*\)".*/\1/g p' -e 's/.*\<name=\([^"][^ ,]*\).*/\1/g p' "$part_file")" 252 part_boot="$(readlink -f /dev/disk/by-partlabel/$part_boot)" 253 254 # for now, FS_BOOT and BOOTLOADER are sourced for 255 # simplicity 256 . "$SOURCE/boot" 257 # in future, I hope to guess as much as possible with 258 # various heuristics, like if fstab contains a /boot 259 # entry that can be linked to a $SOURCE/partitions's 260 # entry. 261 262 INSTALL_BOOT="yes" 263 if test $(echo "$part_type" | wc -w ) -ne 1 264 then 265 INSTALL_BOOT="no" 266 warn "only 1 label is supported for now, $NOBOOT" 267 fi 268 269 if test $(echo "$part_boot" | wc -w ) -ne 1 270 then 271 INSTALL_BOOT="no" 272 warn "only 1 bootable flag is supported for now, $NOBOOT" 273 fi 274 275 if test -z "$BOOTLOADER" 276 then 277 INSTALL_BOOT="no" 278 warn "variable BOOTLOADER was not set, $NOBOOT" 279 fi 280 281 if test "$INSTALL_BOOT" = "yes" 282 then 283 case "$BOOTLOADER" in 284 #TODO: pxe/iso/ext variants 285 efilinux|syslinux) 286 inst_syslinux $BOOTLOADER $part_type $part_boot $FS_BOOT $TARGET "$SOURCE/syslinux" "$cp_target" 287 ;; 288 *) 289 error "unsupported BOOTLOADER value ($BOOTLOADER)" 290 ;; 291 esac 292 fi 293 fi 294 295 ### cleaning up 296 ## unmount 297 info "unmounting partitions" 298 for mnt in $(findmnt -n -R -oTARGET -M $cp_target -l | tac) 299 do 300 umount $mnt 301 done 302 success "unmounting done" 303 304 ## closing luks 305 if test -f "$crypttab" 306 then 307 info "closing luks" 308 for name in $(awk < "$SOURCE/root/etc/crypttab" '{ print $1 }' ) 309 do 310 cryptsetup close $name 311 done 312 success "luks closed" 313 fi