#!/bin/sh # -- defaults # install_dev=/dev/sdb arch=`uname -m` suse_version=tumbleweed o_name=suse-$suse_version cmd=install cwd=`pwd` ssh_auth_sock=/tmp/agent-host.sock env_ssh_auth_sock=$SSH_AUTH_SOCK initrd_modules=" via-rhine r8169 e1000 e1000e b44 myri10ge forcedeth 8139too sata_nv nfs nfs_acl auth_rpcgss nfsv3 nfsv4 fscache lockd sunrpc sis edd sisfb evdev shpchp sis_agp hwmon ahci libahci usbhid hid uhci_hcd ohci_hcd ehci_hcd usbcore hid-generic" config_script= #kernel_flavour=-desktop kernel_flavour= case $suse_version in 11.4) services=" ldap " base_pkgs=" sysvinit " payload_pkgs=" suspend java-1_6_0-sun.i586 java-1_6_0-sun-plugin.i586 grep mkinitrd insserv cpio sysconfig device-mapper sysvinit lsof dhcpcd kbd db-utils insserv unscd portmap xorg-x11-Xnest xorg-x11-fonts-scalable xntp xorg-x11-Xvfb xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-libxcb xorg-x11-sdk xorg-x11-server-glx xorg-x11-fonts-cyrillic xorg-x11-fonts-syriac xorg-x11-man jux-run jux-client-run feedfsd-run feedfs-local-run feedfs-net-run netscan-run snmp-manager-run ftp-server-run bprof-run " special_pkgs=" aufs aufs-kmp$kernel_flavour " #kernel_version=3.7.8~jng11 #rpm_kernel_version=3.7.8~jng11-1 #kernel_version=3.2.0 #rpm_kernel_version=3.2.0-1 ;; 12.2) services=" ldap " base_pkgs=" sysvinit " payload_pkgs=" suspend unscd db-utils insserv " special_pkgs=" aufs aufs-kmp$kernel_flavour " kernel_version=3.7.8-jng11$kernel_flavour rpm_kernel_version=3.7.8~jng11-1 ;; 12.3) services=" ldap " base_pkgs=" module-init-tools sysvinit " payload_pkgs=" suspend nscd usbutils db-utils insserv " special_pkgs=" jux-run jux-client-run feedfsd-run feedfs-local-run feedfs-net-run netscan-run snmp-manager-run ftp-server-run bprof-run aufs aufs-kmp$kernel_flavour " # currently unused variable devel_pkgs=" ant alsa-devel apache-commons-logging boost-devel createrepo curl-devel ffmpeg-devel fuse-devel glib2-devel glu-devel jakarta-commons-logging java-1_7_0-openjdk-devel libXv-devel libXxf86vm-devel libasound2 libcap-devel libffmpeg-devel libsamplerate-devel libsndfile-devel log4j nasm net-snmp openldap2-devel rpmbuild uuid-devel " kernel_version=3.9.9-jng20-1.1$kernel_flavour rpm_kernel_version=3.9.9~jng20-1.1 kernel_version=3.7.10-jng12$kernel_flavour rpm_kernel_version=3.7.10~jng12-1 #kernel_version=3.9.9-jng20-1.2$kernel_flavour kernel_version=3.9.9-jng20$kernel_flavour rpm_kernel_version=3.9.9~jng20-1.2 ;; 13.1) services=" ldap " base_pkgs=" aaa_base module-init-tools sysvinit " payload_pkgs=" suspend nscd sssd usbutils aufs-util db48-utils psmisc ntp acpid " special_pkgs=" jux-run-1.2.2-119p02 jux-client-run feedfsd-run feedfs-local-run feedfs-net-run netscan-run snmp-manager-run ftp-server-run bprof-run " # currently unused variable devel_pkgs=" ant alsa-devel apache-commons-logging boost-devel createrepo curl-devel ffmpeg-devel fuse-devel glib2-devel glu-devel jakarta-commons-logging java-1_7_0-openjdk-devel libXv-devel libXxf86vm-devel libasound2 libcap-devel libffmpeg-devel libsamplerate-devel libsndfile-devel log4j nasm net-snmp openldap2-devel rpmbuild uuid-devel " kernel_version=3.12.7-jux+ rpm_kernel_version=3.12.7-jux+ ;; tumbleweed) services=" slapd " base_pkgs=" aaa_base module-init-tools " payload_pkgs=" nscd sssd usbutils aufs-util db48-utils psmisc ntp acpid " special_pkgs=" jux-run jux-client-run feedfsd-run feedfs-local-run feedfs-net-run netscan-run snmp-manager-run ftp-server-run bprof-run " # currently unused variable devel_pkgs=" ant alsa-devel apache-commons-logging boost-devel createrepo curl-devel ffmpeg-devel fuse-devel glib2-devel glu-devel jakarta-commons-logging java-1_7_0-openjdk-devel libXv-devel libXxf86vm-devel libasound2 libcap-devel libffmpeg-devel libsamplerate-devel libsndfile-devel log4j nasm net-snmp openldap2-devel rpmbuild uuid-devel " kernel_version=4.8.0_rc6_jux+-1 rpm_kernel_version=4.8.0_rc6_jux+-1 ;; esac umask 0022 kernel_files_version=${kernel_version/_/-} unused_pkgs=" sysvinit-systemd kernel kernel-syms-$rpm_kernel_version kernel-source-$rpm_kernel_version aufs-kmp " base_pkgs=" $base_pkgs coreutils " update_pkgs=" $update_pkgs zypper " # -- pciutils # used by jux, should be done by grepping /proc-files special_pkgs=" $special_pkgs kernel$kernel_flavour-$rpm_kernel_version rpm " # -- sysvinit # systemd-sysvinit boots, but doesn't work cleanly as of now payload_pkgs=" $payload_pkgs kernel memtest86+ grub syslinux splashy mailx ppp sensors pam_ldap pam_apparmor gnome-keyring-pam proftpd proftpd-ldap bind dhcp-server rsyslog openldap2 courier-authlib-ldap xinetd apache2 apache2-mod_php5 nfs-kernel-server tftp nss_ldap fuse mdadm smartmontools nfs-client openldap2-client samba-client sudo glibc-locale openvpn xorg-x11 xorg-x11-driver-video xorg-x11-libs xorg-x11-server xorg-x11-Xvnc unclutter xterm man findutils-locate vim iputils strace wget lukemftp openssh parted tar zip unzip rsync gcc-c++ libtool autoconf automake make cvs patch pciutils ytools-run " # fetchmsttfonts # -- functions usage() { echo " usage: $myname -h $myname [options] [install|mount|unmount|mkinitrd|fs] options: -f -d target-device -n target-name -p file with root password -a architecture -k hook=command -r root directory -u user (for source-code-management login) known hooks are: o fs-ready o pkg-installed during hook execution the following variables are filled o \$hook : the name of the hook o \$root : path to the root directory of the distribution being integrated " [ "$1" ] && exit $1 } get_opts() { set -- `getopt 'hd:n:a:p:k:u:w:s:r:f' $*` while [ "$1" != -- ] ; do case "$1" in -h) usage 0 ;; -d) install_dev=$2 shift ;; -n) o_name=$2 shift ;; -r) o_root=$2 shift ;; -a) arch=$2 shift ;; -p) root_password_file=$2 shift ;; -k) hooks[${2%%=*}]="${2#*=}" shift ;; -u) scm_user=$2 shift ;; -w) cwd="$2" cd "$cwd" shift ;; -s) env_ssh_auth_sock=$2 shift ;; -f) o_force=1 ;; *) usage 1 ;; esac shift done shift [ "$1" ] && cmd="$1" } log() { echo $@ >&2 } fatal() { log $@ exit 1 } run() { local exit_on_error=0 while [[ "$1" =~ ^- ]]; do case $1 in -e) exit_on_error=1 esac shift done log =========== running $@ $@ local r=$? if [ "$exit_on_error" = 1 -a $r != 0 ]; then die "exiting after error during command \"$@\"" fi return $r } die() { log "$@" exit 1 } _cat() { cat | sed 's/^[ ]*|//' } check_var_set() { local var val local r=0 for var in $@; do eval val=\$$var [ "$val" ] || { log "$var is not set" r=1 } done return $r } _run_chroot() { local cmd set +x while read cmd; do cmd=`echo $cmd | sed 's/[ ]*#.*//'` [ "$cmd" ] || continue echo == running LANG=POSIX chroot $root $cmd LANG=POSIX chroot $root $cmd || { die "failed to run >$cmd<" } done set -x } run_chroot() { local cmd="LANG=POSIX SSH_AUTH_SOCK=$ssh_auth_sock chroot $root" echo == running $cmd "$@" #eval $cmd "$@" || { #LANG=POSIX SSH_AUTH_SOCK=$ssh_auth_sock chroot $root "$@" || { LANG=POSIX SSH_AUTH_SOCK=$ssh_auth_sock chroot $root "$@" || { die "failed to run >$@<, exiting" } } run_hook() { hook="$1" local cmd="${hooks[$hook]}" [ "$cmd" ] || return 0 ( cd $cwd echo "== running hook $hook ($cmd)" . $cmd ) } run_parted() { while read cmd; do run parted --script $install_dev $cmd done } create_partition_table() { assert_not_mounted local max=$(LANG=POSIX parted -s $install_dev print | sed '/Disk/ !d; s/.*: *//') # FIXME mkpart primary 40G 100% doesn't work on nbd _cat << EOT | run_parted |mklabel gpt |mkpart primary 1M 2M |name 1 grub |set 1 bios_grub |mkpart primary 2M 10G |name 2 boot |mkpart primary 10G 40G |name 3 swap |mkpart primary 40G $max |name 4 root EOT grub_boot_partition_num=0 grub_slash_boot_partition_num=1 } create_file_systems() { run mkfs -t ext4 $install_dev_boot run mkswap $install_dev_swap run mkfs -t ext4 $install_dev_root } canonicalize() { readlink -f $1 2>/dev/null } check_mount() { local canonicalized=`realpath $root/$1 2>/dev/null` #echo root="$root" #echo arg="$1" #echo canonicalized="$canonicalized" [ ! "$canonicalized" ] && return 1 grep -q " $canonicalized " /proc/mounts && return 0 canonicalized=`canonicalize $root/$1` [ ! "$canonicalized" ] && return 1 grep -q " $canonicalized " /proc/mounts && return 0 return 1 } assert_not_mounted() { if grep $install_dev /proc/mounts; then echo "$install_dev is mounted, exiting" exit 1 fi } setup_bind_mounts() { [ -d "$root" ] || { [ "$o_force" ] || { echo "directory \"$root\" does not exist" exit 1 } } local fs for fs in /dev /sys /proc /srv/ftp; do check_mount $fs || { run -e mkdir -p -m 755 $root$fs run -e mount -o bind $fs $root$fs } done if [ "$env_ssh_auth_sock" ]; then check_mount $ssh_auth_sock || { run -e mkdir -p `dirname $root/$ssh_auth_sock` run -e touch $root/$ssh_auth_sock run -e mount --bind $env_ssh_auth_sock $root/$ssh_auth_sock } fi } mount_devices() { check_mount / || { run -e install -d -m 755 $root run -e mount $install_dev_root $root } check_mount /boot || { run -e mkdir -p -m 755 $root/boot run -e mount $install_dev_boot $root/boot } } unmount_devices() { local devices="$ssh_auth_sock /srv/ftp /proc /sys /dev /boot /" [ "$1" ] && devices="$@" local d for d in $devices; do check_mount $d && run umount $root$d done rm -f $ssh_auth_sock } zypper_repos_base() { _cat << EOT | grep -v " #" |distro-oss 0 ftp://ftp/pub/mirror/suse/distribution/$suse_version/repo/oss EOT } zypper_repos_update() { _cat << EOT | grep -v "^ *#" |#distro-non-oss 0 ftp://ftp/pub/mirror/suse/distribution/$suse_version/repo/non-oss |#update 0 ftp://ftp/pub/mirror/suse/update/$suse_version EOT } zypper_repos_payload() { _cat << EOT | grep -v "^ *#" |#ftp.jannet.de 10 ftp://dspdev:123qweasd@ftp.jannet.de/pub/packages/linux/suse/$suse_version/inst-source |ftp.jannet.de 10 ftp://dspdev:123qweasd@ftp/pub/mirror/jannet/ftp/pub/packages/linux/suse/$suse_version/inst-source |ftp.priv.lcl 5 ftp://dspdev:123qweasd@ftp.priv.lcl/pub/local/packages/suse/$suse_version EOT } zypper_repos_special() { _cat << EOT | grep -v "^ *#" |#jengelh 0 ftp://ftp/pub/mirror/gwdg/pub/linux/misc/suser-jengelh/openSUSE_$suse_version |videolan 0 ftp://ftp/pub/mirror/videolan/pub/videolan/vlc/SuSE/$suse_version EOT } zypper_repos() { local repo for repo in $@; do zypper_repos_$repo done } setup_zypper_repos() { local name prio uri zypper_repos $@ | while read name prio uri; do run_chroot zypper $zypper_global_opts ar -p $prio $uri $name done } host_zypper() { local repos="$1" shift local distros=`zypper_repos $repos | awk '{print "--plus-repo " $3 }'` run -e zypper $zypper_global_opts --root $root $distros $@ } init_root_password_file() { [ "$root_password_file" ] || root_password_file=$HOME/.$myname/root-pass [ ! -f $root_password_file ] && { local dir=`dirname $root_password_file` mkdir -m 0755 $dir dd if=/dev/random of=/dev/stdout bs=1 count=4 2>/dev/null | hexdump | sed '/^0000000/ !d; s/0000000 //; s/ //g' > $root_password_file chmod 600 $root_password_file } } make_etc_fstab() { _cat< $p for e in $myname rpmnew; do h="$root/$f.$e" [ -e "$h" ] && { echo "== resetting host file from $f.$e" mv $h $root/$f break } done if [ -s $p ]; then echo "+ $f and host file differ:" cat $p else echo "+ no difference between $f and host file" fi rm -f $p done host_files="" set -e return 0 } setup_boot_loader_local() { check_var_set grub_boot_partition_num grub_slash_boot_partition_num || \ die "not setting up boot loader, exiting" local drivename=hd$install_grub_dev_num local boot="($drivename,$grub_boot_partition_num)" local slash_boot="($drivename,$grub_slash_boot_partition_num)" local map=/tmp/$myname-grub-device.map local mkinitrd_sh=/root/bin/mkinitrd-local.sh echo "($drivename) $install_dev" > $map #dd if=$root/boot/grub/stage1 of=$install_dev count=512 count=1 _cat << EOT | sed 's/#.*//' | run grub --batch --device-map=$map |root $slash_boot |setup ($drivename) |#setup --stage2=/boot/grub/stage2 --force-lba $boot $slash_boot |quit EOT make_sysconfig_bootloader > $root/etc/sysconfig/bootloader make_boot_menu > $root/boot/grub/menu.lst _cat << EOT > $root$mkinitrd_sh |#!/bin/sh |umask 0022 |/sbin/mkinitrd \\ | -B \\ | -k /boot/vmlinuz-$kernel_version \\ | -i /boot/initrd-local-$kernel_version \\ | -M /boot/System.map-$kernel_version \\ | -m "$initrd_modules" \\ | -d $install_dev_root |ln -sf initrd-local-$kernel_version /boot/initrd-local |chmod 644 /boot/initrd-local-$kernel_version EOT chmod 755 $root$mkinitrd_sh run_chroot $mkinitrd_sh } setup_boot_loader_net() { local mkinitrd_sh=/root/bin/mkinitrd-net.sh _cat << EOT > $root$mkinitrd_sh |#!/bin/sh |umask 0022 |/sbin/mkinitrd \\ | -B \\ | -k /boot/vmlinuz-$kernel_files_version \\ | -i /boot/initrd-netboot-$kernel_files_version \\ | -M /boot/System.map-$kernel_files_version \\ | -m "$initrd_modules" \\ | -d jan://blub/dings |ln -sf initrd-netboot-$kernel_files_version /boot/initrd-netboot |ln -sf memtest.bin /boot/memtest |chmod 644 /boot/initrd-netboot-$kernel_files_version EOT chmod 755 $root$mkinitrd_sh run_chroot $mkinitrd_sh } undo_rpmnew() { local n o # reset config files to rpm versions find $root -name '*.rpmnew' | while read n; do o=`echo $n | sed 's/\.rpmnew//'` log renaming $n to $o mv $n $o done } setup_root_directory() { if [ -w "$install_dev" ]; then if [ ! "$non_interactive" ]; then local answer echo -n "Continuing will delete ALL data on $install_dev. Are you sure [y|n]? " read answer if [ "$answer" != y ]; then exit 1 fi fi create_partition_table create_file_systems mount_devices else if [ ! "$non_interactive" ]; then if [ -e $root ]; then local answer echo -n "Continuing will delete ALL data below $root. Are you sure [y|n]? " read answer if [ "$answer" != y ]; then exit 1 fi fi fi if [ ! "$root" ]; then echo "cowardly not removing empty root directory" exit 1 fi if [[ "$root" =~ home ]]; then echo "cowardly not removing directory matching \"home\"" exit 1 fi set +e unmount_devices grep -q $root /proc/mounts && { echo "====== there are filesystems mounted below $root: >" grep $root /proc/mounts echo "====== unmount them manually" exit 1 } run rm -rf $root run install -d -m 755 $root fi } insserv_all() { local local_services=" boot.juxearly boot.bprof sshd jux $services " case $suse_version in 11.4) run_chroot /sbin/insserv -de $local_services ;; *) local s for s in $local_services; do run_chroot systemctl enable $s.service done ;; esac } set_sysconf_value() { local file="$1" local key="$2" local value="`echo $3 | sed 's%/%\\\\/%g'`" sed "s/^ *$key *=.*/$key=\"$value\"/" $root/$file > $root/$file.tmp mv $root/$file.tmp $root/$file } cmd_install() { local c set -e [ -r "$root_password_file" ] || { die "root password file doesn't exist, do echo -n my-secret > $root_password_file" } if true; then setup_root_directory setup_bind_mounts run_hook fs-ready host_zypper "base" install --auto-agree-with-licenses $base_pkgs host_zypper "base update" install --auto-agree-with-licenses $update_pkgs #undo_rpmnew use_host_files /etc/hosts /etc/resolv.conf /etc/sysconfig/security /etc/nsswitch.conf \ /etc/passwd /etc/group /etc/certs /etc/opt/bprof/bprof.secret run_chroot install -d -m 755 /etc/dhcpd.conf.d #make_etc_resolv_conf > $root/etc/resolv.conf #make_etc_fstab > $root/etc/fstab run setup_zypper_repos base update payload echo "=== refreshing >" run_chroot zypper $zypper_global_opts refresh echo "=== refreshing <" echo "=== installing payload packages >" run_chroot zypper $zypper_global_opts install --auto-agree-with-licenses $payload_pkgs echo "=== installing payload packages <" run setup_zypper_repos special run_chroot zypper $zypper_global_opts install --auto-agree-with-licenses $special_pkgs # run_chroot zypper $zypper_global_opts -t srcpackage --download-only $payload_pkgs #kernel_version=`readlink -f $root/boot/vmlinuz 2>/dev/null | xargs basename | sed 's/vmlinuz-//'` # TODO: should be done by package manager post install script run_chroot /opt/ytools/bin/jcs init make_etc_jcs_jcs_conf > $root/etc/jcs/jcs.conf run_chroot mkdir -p -m 700 /root/.ssh make_home_ssh_conf > $root/root/.ssh/config run_chroot chmod 600 /root/.ssh/config make_etc_sysconfig_network_ifcfg_eth0 > $root/etc/sysconfig/network/ifcfg-eth0 run_chroot chmod 644 /etc/sysconfig/network/ifcfg-eth0 run_hook pkg-installed fi # test export CVSROOT=:ext:$scm_user@cvs.jannet.de:/home/jannet/arc/cvs echo "=== running jcs fetch" run_chroot jcs fetch reset_host_files echo "=== running jcs setup" run_chroot jcs setup # seed the ldap database # FIXME: this does not work off a netboot distro slapcat | chroot $root /usr/sbin/slapadd -qw [ "$install_dev" ] && setup_boot_loader_local setup_boot_loader_net run_chroot ln -sf vmlinuz-$kernel_files_version /boot/vmlinuz insserv_all echo "=== using root password file $root_password_file" echo -n root: | cat - $root_password_file | chroot $root /usr/sbin/chpasswd case $suse_version in 11.4) run_chroot /sbin/SuSEconfig ;; *) ;; esac [ -x $root/opt/jux/bin/jux-init-namespace.sh ] && run_chroot /opt/jux/bin/jux-init-namespace.sh run_chroot /sbin/ldconfig run_chroot install -m 755 -d /srv/nfs/var run_chroot install -m 777 -d /srv/nfs/var/cores run_chroot install -m 777 -d /var/cores unmount_devices [ -w "$install_dev" ] || { install -d -m 755 $root/../rw /etc/init.d/nfsserver status >/dev/null 2>&1 && { /etc/init.d/nfsserver restart } } } # -- here we go myname=`basename $0` zypper_global_opts="--non-interactive --gpg-auto-import-keys --no-gpg-checks" date=`date` host_files="" declare -A hooks get_opts "$@" init_root_password_file case $arch in i386|i586|i686) case `uname -m` in i386|i586|i686);; x86_64) l32=linux32;; esac ;; armv7hl) ;; x86_64|"") ;; *) die "invalid architecture $arch specified";; esac if [ `whoami` != root -o "$l32" ]; then [ ! "$scm_user" ] && scm_user=`whoami` exe=$0 [ ${exe:0:1} = / ] || { exe=`/bin/pwd`/$exe exe=`readlink -f $exe` } # preferring "ssh -l root localhost" over "sudo" to keep ssh-agent environment #sudo -i $exe $@ -p $root_password_file [ "$env_ssh_auth_sock" ] && opt_ssh_auth_sock="-s $env_ssh_auth_sock" ssh -l root localhost $l32 $exe $@ -p $root_password_file -u $scm_user -w $cwd $opt_ssh_auth_sock exit $? fi [ ! "$scm_user" ] && scm_user=`whoami` if [ "$o_root" ]; then root=$o_root/ro else root=/srv/nfs/boot/distros/$o_name-$arch/ro fi # -- set up variables denoting devices if [ "$install_dev" ]; then tmp=`realpath "$install_dev"` [ -b "$tmp" ] || fatal "device \"$install_dev\" is not a link to a block device" install_dev="$tmp" case $install_dev in /dev/sd*) part_sep="";; /dev/loop*) part_sep="";; /dev/nbd*) part_sep="p";; esac install_dev_boot=$install_dev$part_sep""2 install_dev_swap=$install_dev$part_sep""3 install_dev_root=$install_dev$part_sep""4 case $install_dev in /dev/sda) install_grub_dev_num=0;; /dev/sdb) install_grub_dev_num=1;; /dev/sdc) install_grub_dev_num=2;; /dev/sdd) install_grub_dev_num=3;; /dev/sde) install_grub_dev_num=4;; /dev/nbd*) install_grub_dev_num=${d/\/dev\/nbd/};; /dev/loop*) install_grub_dev_num=0;; *) fatal "device \"$install_dev\" can't be mapped onto a grub device number, edit $myname to add support" ;; esac run_dev=/dev/sda run_dev_boot=$run_dev""2 run_dev_swap=$run_dev""3 run_dev_root=$run_dev""4 run_grub_dev_num=0 ## test > #grub_boot_partition_num=0 #grub_slash_boot_partition_num=1 #mount_devices ## test < fi case $cmd in install) cmd_install exit $? ;; fs) setup_root_directory setup_bind_mounts ;; mount) [ "$install_dev" ] && mount_devices setup_bind_mounts exit $? ;; unmount) unmount_devices exit $? ;; mkinitrd) grub_boot_partition_num=0 grub_slash_boot_partition_num=1 [ "$install_dev" ] && { mount_devices setup_boot_loader_local } setup_boot_loader_net exit $? ;; *) log "unknown command \"$cmd\"" usage 1 ;; esac exit 0