autoinst

scripts to make automated installations of debian easy
git clone git://deadbeef.fr/autoinst.git
Log | Files | Refs | README | LICENSE

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