Initial commit
This commit is contained in:
commit
295a111cec
|
@ -0,0 +1,6 @@
|
|||
config/downloads/
|
||||
config/simplefiles/CONTESTANT/opt/
|
||||
|
||||
tools/output/
|
||||
tools/backups-*
|
||||
tools/certs/
|
|
@ -0,0 +1,21 @@
|
|||
#! /bin/bash
|
||||
|
||||
# do only execute if this is called on the client being installed. Do not use during fai-diskimage
|
||||
if [ X$FAI_ACTION = Xinstall -a $do_init_tasks -eq 0 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Echo architecture and OS name in uppercase. Do NOT remove these two lines.
|
||||
uname -s | tr '[:lower:]' '[:upper:]'
|
||||
command -v dpkg >&/dev/null && dpkg --print-architecture | tr a-z A-Z
|
||||
|
||||
# determin if we are a DHCP client or not
|
||||
# count the : chars in the argument of ip=
|
||||
n="${ip//[^:]}"
|
||||
if [[ $ip =~ ^(on|any|dhcp)$ ]]; then
|
||||
echo DHCPC
|
||||
elif [ ${#n} -lt 6 ]; then
|
||||
echo DHCPC
|
||||
fi
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,36 @@
|
|||
#! /bin/bash
|
||||
|
||||
# (c) Thomas Lange, 2002-2013, lange@informatik.uni-koeln.de
|
||||
|
||||
# NOTE: Files named *.sh will be evaluated, but their output ignored.
|
||||
|
||||
[ $do_init_tasks -eq 1 ] || return 0 # Do only execute when doing install
|
||||
|
||||
echo 0 > /proc/sys/kernel/printk
|
||||
|
||||
#kernelmodules=
|
||||
# here, you can load modules depending on the kernel version
|
||||
case $(uname -r) in
|
||||
2.6*) kernelmodules="$kernelmodules mptspi dm-mod md-mod aes dm-crypt" ;;
|
||||
[3456]*) kernelmodules="$kernelmodules mptspi dm-mod md-mod aes dm-crypt" ;;
|
||||
esac
|
||||
|
||||
for mod in $kernelmodules; do
|
||||
[ X$verbose = X1 ] && echo Loading kernel module $mod
|
||||
modprobe -a $mod 1>/dev/null 2>&1
|
||||
done
|
||||
|
||||
# show the basic information about the network interface
|
||||
ip -br li show up|egrep -v ^lo; ip -br a show up|egrep -v ^lo
|
||||
|
||||
echo $printk > /proc/sys/kernel/printk
|
||||
|
||||
odisklist=$disklist
|
||||
set_disk_info # recalculate list of available disks
|
||||
if [ "$disklist" != "$odisklist" ]; then
|
||||
echo New disklist: $disklist
|
||||
echo disklist=\"$disklist\" >> $LOGDIR/additional.var
|
||||
fi
|
||||
|
||||
save_dmesg # save new boot messages (from loading modules)
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#! /bin/bash
|
||||
|
||||
if [ X$FAI_ACTION = Xinstall -o X$FAI_ACTION = X ]; then
|
||||
:
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
if [ X$action = Xdirinstall ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
grep -q INSTALL $LOGDIR/FAI_CLASSES || return 0
|
||||
[ "$flag_menu" ] || return 0
|
||||
|
||||
out=$(tty)
|
||||
red=$(mktemp)
|
||||
echo 'screen_color = (CYAN,RED,ON)' > $red
|
||||
|
||||
DIALOGRC=$red dialog --colors --clear --aspect 6 --title "FAI - Fully Automatic Installation" --trim \
|
||||
--msgbox "\n\n If you continue, \n all your data on the disk \n \n|\Zr\Z1 WILL BE DESTROYED \Z0\Zn|\n\n" 0 0 1>$out
|
||||
|
||||
# stop on any error, or if ESC was hit
|
||||
if [ $? -ne 0 ]; then
|
||||
task_error 999
|
||||
fi
|
||||
|
||||
rm $red
|
||||
unset red
|
||||
|
||||
# MENU DISABLED
|
||||
unset flag_menu
|
||||
FAI_ACTION=install
|
|
@ -0,0 +1,8 @@
|
|||
#! /bin/bash
|
||||
|
||||
# assign classes to hosts based on their hostname
|
||||
|
||||
# do not use this if a menu will be presented
|
||||
[ "$flag_menu" ] && exit 0
|
||||
|
||||
echo "FAIBASE DEBIAN XORG GNOME PARTICIPANT CONTESTANT"
|
|
@ -0,0 +1,10 @@
|
|||
#! /bin/bash
|
||||
|
||||
[ "$cmdlineclasses" ] || exit 0
|
||||
|
||||
# define the classes given on the cmdline (using -c)
|
||||
# Note: In the FAI example config, this script has very low priority, but for
|
||||
# me it make more sense to make it higher priority than the host classes.
|
||||
echo $cmdlineclasses
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,11 @@
|
|||
#! /bin/bash
|
||||
|
||||
ifclass -o CENTOS SLC && exit 0
|
||||
|
||||
ifclass -o GRUB_PC GRUB_EFI && exit 0
|
||||
|
||||
if [ -d /sys/firmware/efi ]; then
|
||||
echo GRUB_EFI
|
||||
elif ifclass -o I386 AMD64 ; then
|
||||
echo GRUB_PC
|
||||
fi
|
|
@ -0,0 +1,14 @@
|
|||
#! /bin/bash
|
||||
|
||||
# define classes for disk_config in an EFI enironment
|
||||
|
||||
if [ ! -d /sys/firmware/efi ] || ifclass GRUB_PC; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for c in LVM FAIBASE; do
|
||||
if ifclass $c; then
|
||||
echo ${c}_EFI
|
||||
break
|
||||
fi
|
||||
done
|
|
@ -0,0 +1 @@
|
|||
PARTICIPANT_USER_NAME=contestant
|
|
@ -0,0 +1,26 @@
|
|||
release=bullseye
|
||||
apt_cdn=http://deb.debian.org
|
||||
security_cdn=http://security.debian.org
|
||||
|
||||
# since bullseye Debian changed the suite name for security
|
||||
if [ $release = buster ]; then
|
||||
secsuite=buster/updates
|
||||
else
|
||||
secsuite=$release-security
|
||||
fi
|
||||
|
||||
CONSOLEFONT=
|
||||
KEYMAP=us-latin1
|
||||
|
||||
# MODULESLIST contains modules that will be loaded by the new system,
|
||||
# not during installation these modules will be written to /etc/modules
|
||||
# If you need a module during installation, add it to $kernelmodules
|
||||
# in 20-hwdetect.sh.
|
||||
MODULESLIST="usbhid psmouse"
|
||||
|
||||
# if you have enough RAM (>2GB) you may want to enable this line. It
|
||||
# also puts /var/cache into a ramdisk.
|
||||
#FAI_RAMDISKS="$target/var/lib/dpkg $target/var/cache"
|
||||
|
||||
# if you want to use the faiserver as APT proxy
|
||||
APTPROXY=http://10.0.0.9:3142
|
|
@ -0,0 +1,24 @@
|
|||
# default values for installation. You can override them in your *.var files
|
||||
|
||||
# disallow installation of packages from unsigned repositories
|
||||
FAI_ALLOW_UNSIGNED=0
|
||||
|
||||
# Set UTC=yes if your system clock is set to UTC (GMT), and UTC=no if not.
|
||||
UTC=yes
|
||||
TIMEZONE=Europe/Zurich
|
||||
|
||||
# the hash of the root password for the new installed linux system
|
||||
ROOTPW=''
|
||||
|
||||
# errors in tasks greater than this value will cause the installation to stop
|
||||
STOP_ON_ERROR=700
|
||||
|
||||
# set parameter for install_packages(8)
|
||||
MAXPACKAGES=800
|
||||
|
||||
PARTICIPANT_USER_NAME=soi
|
||||
PARTICIPANT_USER_PW='$y$j9T$h5VhMd4KkdmbxdZD1gO0N/$1hvwZgO8pQw13Xd6jaNXbtkbqVOC4W/ia/KXOcCGYvB'
|
||||
|
||||
if [ $FAI_ACTION = "install" ]; then
|
||||
LOGUSER=fai
|
||||
fi
|
|
@ -0,0 +1,3 @@
|
|||
SUPER_USER_NAME=superstofl
|
||||
SUPER_USER_DISPLAYNAME="Admin"
|
||||
SUPER_USER_PW=''
|
|
@ -0,0 +1,10 @@
|
|||
exim4-config exim4/dc_eximconfig_configtype select local delivery only; not on a network
|
||||
locales locales/default_environment_locale select en_US.UTF-8
|
||||
locales locales/locales_to_be_generated multiselect en_US.UTF-8 UTF-8
|
||||
keyboard-configuration keyboard-configuration/xkb-keymap select
|
||||
keyboard-configuration keyboard-configuration/model select Generic 105-key PC
|
||||
keyboard-configuration keyboard-configuration/modelcode string pc105
|
||||
keyboard-configuration keyboard-configuration/layoutcode string ch
|
||||
keyboard-configuration keyboard-configuration/layout select Switzerland
|
||||
keyboard-configuration keyboard-configuration/variant select Switzerland
|
||||
keyboard-configuration keyboard-configuration/optionscode string
|
|
@ -0,0 +1,2 @@
|
|||
locales locales/default_environment_locale select en_US.UTF-8
|
||||
locales locales/locales_to_be_generated multiselect en_US.UTF-8 UTF-8, de_CH.UTF-8 UTF-8, fr_CH.UTF-8 UTF-8, it_CH.UTF-8 UTF-8
|
|
@ -0,0 +1,9 @@
|
|||
# example of new config file for setup-storage
|
||||
#
|
||||
# <type> <mountpoint> <size> <fs type> <mount options> <misc options>
|
||||
|
||||
disk_config disk1 disklabel:msdos bootable:1 fstabkey:uuid
|
||||
|
||||
primary / 2G-50G ext4 rw,noatime,errors=remount-ro
|
||||
logical swap 200-10G swap sw
|
||||
logical /home 100- ext4 rw,noatime,nosuid,nodev createopts="-L home -m 1" tuneopts="-c 0 -i 0"
|
|
@ -0,0 +1,10 @@
|
|||
# example of new config file for setup-storage
|
||||
#
|
||||
# <type> <mountpoint> <size> <fs type> <mount options> <misc options>
|
||||
|
||||
disk_config disk1 disklabel:gpt bootable:1 fstabkey:uuid
|
||||
|
||||
primary /boot/efi 512M vfat rw
|
||||
primary / 2G-50G ext4 rw,noatime,errors=remount-ro
|
||||
primary swap 200-10G swap sw
|
||||
primary /home 100- ext4 rw,noatime,nosuid,nodev createopts="-L home -m 1" tuneopts="-c 0 -i 0"
|
|
@ -0,0 +1,15 @@
|
|||
# <type> <mountpoint> <size> <fs type> <mount options> <misc options>
|
||||
|
||||
# entire disk with LVM, separate /home
|
||||
|
||||
disk_config disk1 fstabkey:uuid align-at:1M
|
||||
|
||||
primary /boot 200 ext2 rw,noatime
|
||||
primary - 4G- - -
|
||||
|
||||
disk_config lvm
|
||||
|
||||
vg vg1 disk1.2
|
||||
vg1-root / 3G-50G ext4 noatime,rw
|
||||
vg1-swap swap 200-4G swap sw
|
||||
vg1-home /home 600- ext4 noatime,nosuid,nodev,rw
|
|
@ -0,0 +1,16 @@
|
|||
# <type> <mountpoint> <size> <fs type> <mount options> <misc options>
|
||||
|
||||
# entire disk with LVM, separate /home
|
||||
|
||||
disk_config disk1 disklabel:gpt fstabkey:uuid align-at:1M
|
||||
|
||||
primary /boot/efi 512M vfat rw
|
||||
primary /boot 200 ext2 rw,noatime
|
||||
primary - 4G- - -
|
||||
|
||||
disk_config lvm
|
||||
|
||||
vg vg1 disk1.3
|
||||
vg1-root / 3G-50G ext4 noatime,rw
|
||||
vg1-swap swap 200-4G swap sw
|
||||
vg1-home /home 600- ext4 noatime,nosuid,nodev,rw
|
|
@ -0,0 +1,5 @@
|
|||
DPkg {
|
||||
Options {
|
||||
"--force-confdef";
|
||||
}
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
deb {%apt_cdn%}/debian {%release%} main contrib non-free
|
||||
deb {%security_cdn%}/debian-security {%secsuite%} main contrib non-free
|
||||
deb {%apt_cdn%}/debian {%release%}-updates main contrib non-free
|
|
@ -0,0 +1,45 @@
|
|||
#! /bin/bash
|
||||
|
||||
# hook for installing a file system image (tar file)
|
||||
# this works for Ubuntu 14.04
|
||||
#
|
||||
# Copyright (C) 2015 Thomas Lange, lange@informatik.uni-koeln.de
|
||||
|
||||
|
||||
# I use this tar command to create the image of an already running and configured machine
|
||||
# tar -cf /tmp/IMAGE.tar --exclude /tmp/\* --exclude /run/\* --exclude /proc/\* --exclude /sys/\* --exclude /dev/\* /
|
||||
# add --xattrs --selinux --acls if needed (for CentOS 7)
|
||||
# Then copy this image to /srv/fai/config/basefiles/IMAGE.tar and make sure your client belongs to the class IMAGE
|
||||
|
||||
skiptask extrbase debconf repository updatebase instsoft
|
||||
skiptask configure # do not run the usual configure scripts
|
||||
|
||||
# we assume, that the new host will get its hostname and IP via DHCP
|
||||
# remove old hostname
|
||||
fgrep -v 127.0.1.1 $target/etc/hosts >> /tmp/fai/hosts
|
||||
mv /tmp/fai/hosts $target/etc/hosts
|
||||
rm $target/etc/hostname
|
||||
|
||||
#install grub
|
||||
mount -t proc proc $FAI_ROOT/proc
|
||||
mount -t sysfs sysfs $FAI_ROOT/sys
|
||||
mount --bind /dev $FAI_ROOT/dev
|
||||
|
||||
if [ -f $target/etc/debian_version ]; then
|
||||
$ROOTCMD grub-install $BOOT_DEVICE
|
||||
$ROOTCMD update-grub
|
||||
fi
|
||||
if [ -f $target/etc/centos-release ]; then
|
||||
rm $target/etc/grub2/device.map
|
||||
$FAI/scripts/CENTOS/40-install-grub
|
||||
$FAI/scripts/CENTOS/30-mkinitrd
|
||||
$ROOTCMD fixfiles onboot # this fixes the SELinux security contexts during the first boot
|
||||
fi
|
||||
|
||||
|
||||
# things that may be adjusted:
|
||||
#
|
||||
# MAC address ?? (not needed for Ubuntu, it uses iftab(5)
|
||||
# /etc/hosts may contain the IP and name of the original host
|
||||
# /etc/hostname (for Ubuntu just remove it)
|
||||
# /var/lib/NetworkManager/dhclient-eth0.conf?
|
|
@ -0,0 +1,25 @@
|
|||
#! /bin/bash
|
||||
|
||||
# if package locales will be installed, then install it early, before
|
||||
# other packages
|
||||
if [ $FAI_ACTION != "install" -a $FAI_ACTION != "dirinstall" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
fcopy -Bi /etc/apt/apt.conf.d/force_confdef
|
||||
ainsl -a /etc/ucf.conf "^conf_force_conffold=YES"
|
||||
|
||||
# in case the locales are already included inside the base file (Ubuntu)
|
||||
if [ -f $target/usr/sbin/locale-gen ]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
# if we want to install locales, install them now
|
||||
install_packages -l 2>/dev/null | egrep -q ' locales|locales '
|
||||
if [ $? -eq 0 ]; then
|
||||
if [ X$verbose = X1 ]; then
|
||||
$ROOTCMD apt-get -y install locales
|
||||
else
|
||||
$ROOTCMD apt-get -y install locales > /dev/null
|
||||
fi
|
||||
fi
|
|
@ -0,0 +1,222 @@
|
|||
#! /bin/bash
|
||||
|
||||
# parse all log files for error messages
|
||||
# print errors and warnings found to error.log
|
||||
# WARNING: This will only work with english error messages!
|
||||
|
||||
errfile=$LOGDIR/error.log
|
||||
|
||||
# Define grep patterns. Do not start or end with an empty line!
|
||||
globalerrorpatterns="error
|
||||
fail
|
||||
warn
|
||||
bad
|
||||
bad
|
||||
no space
|
||||
Couldn't stat
|
||||
Cannot access
|
||||
conflict
|
||||
is bigger than the limit
|
||||
did not exist
|
||||
non existent
|
||||
not found
|
||||
couldn't
|
||||
can't
|
||||
E: Sorry, broken packages
|
||||
^E:
|
||||
operator expected
|
||||
ambiguous redirect
|
||||
No previous regular expression
|
||||
No such
|
||||
Device or resource busy
|
||||
unknown option
|
||||
[a-z]\+\.log:E:
|
||||
No candidate version found
|
||||
segfault
|
||||
Couldn't find any package whose name or description matched
|
||||
cannot create
|
||||
The following packages have unmet dependencies"
|
||||
|
||||
globalignorepatterns="[a-z]\+\.log:#
|
||||
Error: Driver 'pcspkr' is already registered, aborting
|
||||
: bytes packets errors dropped
|
||||
:+ error=0
|
||||
:+ trap error=
|
||||
task_error_func=
|
||||
STOP_ON_ERROR=
|
||||
courier-webadmin
|
||||
plugins-bad
|
||||
Enabling conf localized-error-pages
|
||||
ibwebadmin
|
||||
kernel-patch-badram
|
||||
kolab-webadmin
|
||||
kolabadmin
|
||||
gstreamer.\+-plugins-really-bad
|
||||
liberrors.so
|
||||
liberrors-samba
|
||||
libsamba-errors
|
||||
gsambad
|
||||
libad
|
||||
libtest-nowarnings-perl
|
||||
libtest-warn-perl
|
||||
libclass-errorhandler-perl
|
||||
zope-ploneerrorreporting
|
||||
libroxen-errormessage
|
||||
liberror-perl
|
||||
perl-Error
|
||||
libgpg-error-dev
|
||||
libgpg-error0
|
||||
Opts:.\+errors=remount
|
||||
[RT]X packets:
|
||||
WARNING: unexpected IO-APIC
|
||||
warned about = ( )
|
||||
daemon.warn
|
||||
kern.warn
|
||||
rw,errors=
|
||||
Expect some cache
|
||||
no error
|
||||
failmsg
|
||||
RPC call returned error 101
|
||||
deverror.out
|
||||
(floppy), sector 0
|
||||
mount version older than kernel
|
||||
Can't locate module
|
||||
Warning only .\+MB will be used.
|
||||
hostname: Host name lookup failure
|
||||
I can't tell the difference.
|
||||
warning, not much extra random data, consider using the -rand option
|
||||
confC._FILE
|
||||
Warning: 3 database(s) sources
|
||||
were not found, (but were created)
|
||||
removing exim
|
||||
The home dir you specified already exists.
|
||||
No Rule for /usr/lib/ispell/default.hash.
|
||||
/usr/sbin/update-fonts-.\+: warning: absolute path
|
||||
hostname: Unknown server error
|
||||
EXT2-fs warning: checktime reached
|
||||
RPC: sendmsg returned error 101
|
||||
can't print them to stdout. Define these classes
|
||||
warning: downgrading
|
||||
suppress emacs errors
|
||||
echo Error:
|
||||
Can't open dependencies file
|
||||
documents in /usr/doc are no longer supported
|
||||
if you have both a SCSI and an IDE CD-ROM
|
||||
Warning: /proc/ide/hd?/settings interface is obsolete, and will be removed soon
|
||||
Monitoring disabled
|
||||
Error: only one processor found.
|
||||
Error Recovery Strategy:
|
||||
sector 0 does not have an
|
||||
syslogin_perform_logout: logout() returned an error
|
||||
grub is not in an XFS filesystem.
|
||||
grub-install: line 374:
|
||||
grub-probe: error: Cannot open \`/boot/grub/device.map'
|
||||
is harmless
|
||||
not updating .\+ font directory data.
|
||||
register_serial(): autoconfig failed
|
||||
Fontconfig error: Cannot load default config file
|
||||
asking for cache data failed
|
||||
However, I can not read the target:
|
||||
Warning: The partition table looks like it was made
|
||||
task_error=0
|
||||
task_local_error=0
|
||||
^info: Trying to set
|
||||
warning: /usr/lib/X11/fonts
|
||||
can't read /etc/udev/rules.d/z25_persistent-net.rules
|
||||
/cow': No such file or directory
|
||||
Dummy start-stop-daemon called
|
||||
X: bytes packets errors
|
||||
ACPI Error
|
||||
ACPI Warning
|
||||
AE_NOT_FOUND
|
||||
conflicts with ACPI region
|
||||
cannot stat \`/etc/modprobe.d/\*.conf'
|
||||
cdrom: open failed.
|
||||
libgpg-error
|
||||
process \`kudzu' used the deprecated sysctl system call
|
||||
PM: Resume from disk failed
|
||||
JBD: barrier-based sync failed
|
||||
aufs: module is from the staging directory, the quality is unknown
|
||||
warning: linuxlogo stop runlevel arguments (none) do not match
|
||||
insserv: warning: script .\+ missing LSB tags and overrides
|
||||
live-premount.\+ If this fails
|
||||
cannot read table of mounted file systems
|
||||
error: no alternatives for
|
||||
ERST: Error Record Serialization Table (ERST) support is initialized
|
||||
ERST: Table is not found
|
||||
HEST: Table not found
|
||||
failed to stat /dev/pts
|
||||
Failed to connect to socket /var/run/dbus/system_bus_socket
|
||||
fail to add MMCONFIG information
|
||||
can't initialize iptables table
|
||||
can't initialize ip6tables table
|
||||
Authentication warning overridden
|
||||
41-warning.sh
|
||||
PCCT header not found
|
||||
Download is performed unsandboxed as root as file
|
||||
update-alternatives: warning: skip creation of
|
||||
loop: module verification failed: signature
|
||||
Warning: apt-key output should not be parsed
|
||||
WARNING: Failed to connect to lvmetad. Falling back to device scanning
|
||||
Warning: The home dir /var/lib/usbmux you specified
|
||||
diff: /var/lib/apparmor/profiles/.apparmor.md5sums: No such file or directory
|
||||
error reporting disabled
|
||||
Enabling Firmware First mode for corrected errors
|
||||
errors: 0
|
||||
0 errors
|
||||
Memory Error Correction:
|
||||
Memory Controller 0 - Channel . Error
|
||||
IIO RAS/Control Status/Global Errors
|
||||
RAS: Correctable Errors collector initialized
|
||||
__stack_chk_fail
|
||||
grub.cfg.new: Directory nonexistent
|
||||
can't derive routing for PCI INT A
|
||||
failed to load isci/isci_firmware.bin
|
||||
Direct firmware load for isci/isci_firmware.bin failed with error
|
||||
Loading user firmware failed, using default values
|
||||
stunnel4 you specified can't be accessed: No such file or directory
|
||||
install-docs --verbose --check file_name' may give more details about the above errors
|
||||
cannot open '/etc/ssl/certs/java/cacerts' for reading: No such file or directory
|
||||
can't claim BAR
|
||||
disabling ASPM
|
||||
data block query control method not found
|
||||
subprocess.py.\+RuntimeWarning: line buffering
|
||||
Resource conflict.\+ found
|
||||
update-rc.d: warning: start and stop actions are no longer supported"
|
||||
|
||||
# add pattern on some conditions
|
||||
if [ -n $FAI_ALLOW_UNSIGNED ] ; then
|
||||
globalignorepatterns="$globalignorepatterns
|
||||
WARNING: untrusted versions
|
||||
WARNING: The following packages cannot be authenticated
|
||||
Ignoring these trust violations"
|
||||
fi
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# Here you can define your own patterns. Put one pattern in a line,
|
||||
# do not create empty lines.
|
||||
myerrorpatterns="X_X-X_XX"
|
||||
myignorepatterns="X_X-X_XX"
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# The main routine
|
||||
errorpatterns="$globalerrorpatterns
|
||||
$myerrorpatterns"
|
||||
ignorepatterns="$globalignorepatterns
|
||||
$myignorepatterns"
|
||||
|
||||
cd $LOGDIR || exit 3
|
||||
if [ -s $errfile ]; then
|
||||
echo "Errorfile already exists. Aborting." >&2
|
||||
exit
|
||||
fi
|
||||
|
||||
grep -i "$errorpatterns" *.log | grep -vi "$ignorepatterns" > $errfile
|
||||
if [ X$verbose = X1 ]; then
|
||||
egrep -v '^software.log:' $errfile > $LOGDIR/tempfile
|
||||
mv $LOGDIR/tempfile $errfile
|
||||
fi
|
||||
|
||||
if [ -s $errfile ]; then
|
||||
echo "ERRORS found in log files. See $errfile" >&2
|
||||
else
|
||||
echo "Congratulations! No errors found in log files."
|
||||
fi
|
|
@ -0,0 +1,7 @@
|
|||
#! /bin/bash
|
||||
|
||||
# use short hostname instead of FQDN
|
||||
export HOSTNAME=${HOSTNAME%%.*}
|
||||
if [ $do_init_tasks -eq 1 ]; then
|
||||
echo $HOSTNAME > /proc/sys/kernel/hostname
|
||||
fi
|
|
@ -0,0 +1,16 @@
|
|||
#! /bin/bash
|
||||
|
||||
if [ -n "$APTPROXY" ]; then
|
||||
echo "Acquire::http::Proxy \"$APTPROXY\";" > $target/etc/apt/apt.conf.d/02proxy
|
||||
else
|
||||
rm -f $target/etc/apt/apt.conf.d/02proxy
|
||||
fi
|
||||
|
||||
echo force-unsafe-io > $target/etc/dpkg/dpkg.cfg.d/unsafe-io
|
||||
|
||||
fcopy -iSM /etc/apt/sources.list
|
||||
|
||||
# you may want to add i386 arch to amd64 hosts
|
||||
# if ifclass AMD64; then
|
||||
# $ROOTCMD dpkg --add-architecture i386
|
||||
# fi
|
|
@ -0,0 +1,4 @@
|
|||
#! /bin/bash
|
||||
|
||||
cp -RT $FAI/simplefiles/PARTICIPANT/usr/share/keyrings $target/usr/share/keyrings
|
||||
cp -RT $FAI/simplefiles/PARTICIPANT/etc/apt/sources.list.d $target/etc/apt/sources.list.d
|
|
@ -0,0 +1,7 @@
|
|||
PACKAGES install-norec
|
||||
# For fai softupdate
|
||||
nfs-common netcat fai-client
|
||||
# For importing client certificate
|
||||
libnss3-tools
|
||||
# For screen recording
|
||||
ffmpeg
|
|
@ -0,0 +1,54 @@
|
|||
PACKAGES install-norec
|
||||
apt-transport-https # is only needed for stretch
|
||||
debconf-utils
|
||||
file
|
||||
less
|
||||
linuxlogo
|
||||
rsync
|
||||
openssh-client openssh-server
|
||||
time
|
||||
procinfo
|
||||
nullmailer
|
||||
eject
|
||||
locales
|
||||
console-setup kbd
|
||||
pciutils usbutils
|
||||
systemd-timesyncd
|
||||
#unattended-upgrades
|
||||
|
||||
PACKAGES install NONFREE
|
||||
# you may want these non-free kernel drivers
|
||||
firmware-bnx2 firmware-bnx2x firmware-realtek
|
||||
firmware-linux-nonfree
|
||||
# a list of firmware for wifi/wireless
|
||||
atmel-firmware firmware-atheros firmware-brcm80211
|
||||
firmware-iwlwifi firmware-libertas firmware-ralink firmware-zd1211
|
||||
firmware-brcm80211 firmware-ti-connectivity
|
||||
firmware-netronome firmware-netxen firmware-realtek
|
||||
firmware-cavium
|
||||
# firmware-ipw2x00 # needs a debconf question
|
||||
|
||||
PACKAGES install I386
|
||||
linux-image-686-pae
|
||||
memtest86+
|
||||
|
||||
PACKAGES install CHROOT
|
||||
linux-image-686-pae-
|
||||
linux-image-amd64-
|
||||
|
||||
PACKAGES install AMD64
|
||||
linux-image-amd64
|
||||
memtest86+
|
||||
|
||||
PACKAGES install ARM64
|
||||
grub-efi-arm64
|
||||
linux-image-arm64
|
||||
|
||||
PACKAGES install GRUB_PC
|
||||
grub-pc
|
||||
|
||||
PACKAGES install GRUB_EFI
|
||||
grub-efi
|
||||
|
||||
PACKAGES install LVM
|
||||
lvm2
|
Binary file not shown.
|
@ -0,0 +1,8 @@
|
|||
PACKAGES install-norec
|
||||
|
||||
firefox-esr
|
||||
#thunderbird
|
||||
menu gdm3
|
||||
gnome-core
|
||||
gnome-screensaver gnome-system-monitor gnome-system-tools
|
||||
network-manager-gnome
|
|
@ -0,0 +1,15 @@
|
|||
PACKAGES install
|
||||
# firmware
|
||||
firmware-linux-nonfree
|
||||
firmware-iwlwifi firmware-brcm80211 firmware-realtek
|
||||
# shell utilities
|
||||
netcat htop
|
||||
# software for participants
|
||||
chromium-l10n
|
||||
codeblocks emacs geany gedit joe kate kdevelop nano vim vim-gtk3
|
||||
gcc g++ gdb ddd valgrind python3 pypy
|
||||
evince gnome-terminal konsole byobu
|
||||
# from third-party repositories
|
||||
atom sublime-text code
|
||||
# requested by particants (gnome-tweaks can be used e.g. to change the function of Caps Lock key)
|
||||
gnome-tweaks fonts-firacode
|
|
@ -0,0 +1,37 @@
|
|||
PACKAGES install-norec
|
||||
|
||||
# the list of standard packages, without any libs and a few packages removed
|
||||
apt-listchanges
|
||||
bash-completion
|
||||
bind9-host
|
||||
dbus
|
||||
dnsutils
|
||||
doc-debian
|
||||
file
|
||||
gettext-base
|
||||
groff-base
|
||||
hdparm
|
||||
less
|
||||
locales
|
||||
lsof
|
||||
man-db
|
||||
manpages
|
||||
mime-support
|
||||
ncurses-term
|
||||
netcat-traditional
|
||||
openssh-client
|
||||
pciutils
|
||||
perl
|
||||
reportbug
|
||||
telnet
|
||||
traceroute
|
||||
ucf
|
||||
xz-utils
|
||||
|
||||
PACKAGES install-norec STRETCH BUSTER
|
||||
python
|
||||
python-minimal
|
||||
|
||||
PACKAGES install-norec BULLSEYE
|
||||
python3
|
||||
python3-minimal
|
|
@ -0,0 +1,2 @@
|
|||
PACKAGES install-norec
|
||||
sudo
|
|
@ -0,0 +1,12 @@
|
|||
PACKAGES install-norec DEBIAN
|
||||
xorg xserver-xorg-video-all xserver-xorg-input-all
|
||||
fonts-freefont-ttf
|
||||
xscreensaver
|
||||
xscreensaver-gl
|
||||
xterm
|
||||
desktop-base
|
||||
|
||||
PACKAGES install UBUNTU
|
||||
ubuntu-server-
|
||||
ubuntu-standard
|
||||
ubuntu-desktop
|
|
@ -0,0 +1,30 @@
|
|||
#! /bin/bash
|
||||
|
||||
error=0; trap 'error=$(($?>$error?$?:$error))' ERR # save maximum error code
|
||||
|
||||
cp -RT $FAI/simplefiles/CONTESTANT $target
|
||||
|
||||
$ROOTCMD dconf update
|
||||
|
||||
# Enable firewall
|
||||
$ROOTCMD systemctl enable nftables.service
|
||||
|
||||
# Disable Bluetooth
|
||||
$ROOTCMD systemctl disable bluetooth.service
|
||||
|
||||
# Disable sleep
|
||||
$ROOTCMD systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
|
||||
|
||||
sed -i 's|"homepage": ".*"|"homepage": "https://finals.soi.ch/"|' $target/etc/chromium/master_preferences
|
||||
|
||||
# Disable panels in gnome-control-center
|
||||
DISABLE_DESKTOP="$ROOTCMD dpkg-statoverride --force-statoverride-add --update --add root root 640"
|
||||
$DISABLE_DESKTOP /usr/share/applications/gnome-bluetooth-panel.desktop
|
||||
$DISABLE_DESKTOP /usr/share/applications/gnome-online-accounts-panel.desktop
|
||||
$DISABLE_DESKTOP /usr/share/applications/gnome-sharing-panel.desktop
|
||||
|
||||
# Auto login
|
||||
sed -i 's/# AutomaticLoginEnable = true/AutomaticLoginEnable = true/g' $target/etc/gdm3/daemon.conf
|
||||
sed -i 's/# AutomaticLogin = user1/AutomaticLogin = contestant/g' $target/etc/gdm3/daemon.conf
|
||||
|
||||
exit $error
|
|
@ -0,0 +1,12 @@
|
|||
#! /bin/bash
|
||||
|
||||
error=0; trap 'error=$(($?>$error?$?:$error))' ERR # save maximum error code
|
||||
|
||||
# set root password
|
||||
if [ -n "$ROOTPW" ]; then
|
||||
$ROOTCMD chpasswd --encrypted <<< "root:${ROOTPW}"
|
||||
else
|
||||
$ROOTCMD usermod -L root
|
||||
fi
|
||||
|
||||
exit $error
|
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Capabilities get lost when creating the fai base.tar.xz image.
|
||||
# Restore them here.
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
if [ ! -x $target/sbin/setcap ] ; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for FILE in /bin/ping /bin/ping6 /usr/bin/fping /usr/bin/fping6; do
|
||||
if [ -x $target/$FILE -a ! -h $target/$FILE ] ; then
|
||||
if $ROOTCMD /sbin/setcap cap_net_raw+ep $FILE; then
|
||||
echo "Setcap worked! $FILE is not suid!"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [ -x $target/usr/bin/systemd-detect-virt ] ; then
|
||||
$ROOTCMD /sbin/setcap cap_dac_override,cap_sys_ptrace+ep /usr/bin/systemd-detect-virt
|
||||
fi
|
|
@ -0,0 +1,118 @@
|
|||
#! /bin/bash
|
||||
|
||||
netplan_yaml() {
|
||||
# network configuration using ubuntu's netplan.io
|
||||
local IFNAME="$1"
|
||||
local METHOD="$2"
|
||||
echo "Generating netplan configuration for $IFNAME ($METHOD)" >&2
|
||||
echo "# generated by FAI"
|
||||
echo "network:"
|
||||
echo " version: 2"
|
||||
echo " renderer: $RENDERER"
|
||||
case "$RENDERER" in
|
||||
networkd)
|
||||
echo " ethernets:"
|
||||
echo " $IFNAME:"
|
||||
case "$METHOD" in
|
||||
dhcp)
|
||||
echo " dhcp4: true"
|
||||
;;
|
||||
static)
|
||||
echo " addresses: [$CIDR]"
|
||||
echo " gateway4: $GATEWAYS_1"
|
||||
echo " nameservers:"
|
||||
echo " search: [$DOMAIN]"
|
||||
echo " addresses: [${DNSSRVS// /, }]"
|
||||
;;
|
||||
esac
|
||||
esac
|
||||
}
|
||||
|
||||
iface_stanza() {
|
||||
# classic network configuration using /etc/network/interfaces
|
||||
local IFNAME="$1"
|
||||
local METHOD="$2"
|
||||
echo "Generating interface configuration for $IFNAME ($METHOD)" >&2
|
||||
echo "# generated by FAI"
|
||||
echo "auto $IFNAME"
|
||||
echo "iface $IFNAME inet $METHOD"
|
||||
case "$METHOD" in
|
||||
static)
|
||||
echo " address $CIDR"
|
||||
echo " gateway $GATEWAYS"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
newnicnames() {
|
||||
|
||||
# determine predictable network names only for stretch and above
|
||||
local name
|
||||
|
||||
[ $do_init_tasks -eq 0 ] && return
|
||||
[ -z "$NIC1" ] && return
|
||||
|
||||
fields="ID_NET_NAME_FROM_DATABASE ID_NET_NAME_ONBOARD ID_NET_NAME_SLOT ID_NET_NAME_PATH"
|
||||
for field in $fields; do
|
||||
name=$(udevadm info /sys/class/net/$NIC1 | sed -rn "s/^E: $field=(.+)/\1/p")
|
||||
if [[ $name ]]; then
|
||||
NIC1=$name
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
# try to get altname net dev
|
||||
name=$(ip link show $NIC1 | awk '/altname / { print $2 }')
|
||||
if [[ $name ]]; then
|
||||
NIC1=$name
|
||||
return
|
||||
else
|
||||
echo "$0: error: could not find systemd predictable network name. Using $NIC1."
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -z "$NIC1" ]; then
|
||||
echo "WARNING: \$NIC1 is not defined. Cannot add ethernet to /etc/network/interfaces."
|
||||
fi
|
||||
CIDR=$(ip --br ad sh $NIC1|awk '{print $3}')
|
||||
newnicnames
|
||||
|
||||
case "$FAI_ACTION" in
|
||||
install|dirinstall)
|
||||
ifclass DHCPC && METHOD=dhcp || METHOD=static
|
||||
ifclass XORG && RENDERER=NetworkManager || RENDERER=networkd
|
||||
|
||||
if [ -d $target/etc/netplan ]; then
|
||||
# Ubuntu >= 17.10 with netplan.io
|
||||
if [ -n "$NIC1" ]; then
|
||||
netplan_yaml $NIC1 $METHOD > $target/etc/netplan/01-${NIC1}.yaml
|
||||
fi
|
||||
elif [ -d $target/etc/network/interfaces.d ]; then
|
||||
# ifupdown >= 0.7.41 (Debian >= 8, Ubuntu >= 14.04)
|
||||
iface_stanza lo loopback > $target/etc/network/interfaces.d/lo
|
||||
|
||||
if [ -n "$NIC1" -a ! -f $target/etc/NetworkManager/NetworkManager.conf ]; then
|
||||
iface_stanza $NIC1 $METHOD > $target/etc/network/interfaces.d/$NIC1
|
||||
fi
|
||||
else
|
||||
(
|
||||
iface_stanza lo loopback
|
||||
iface_stanza $NIC1 $METHOD
|
||||
) > $target/etc/network/interfaces
|
||||
fi
|
||||
|
||||
if ! ifclass DHCPC ; then
|
||||
[ -n "$NETWORK" ] && echo "localnet $NETWORK" > $target/etc/networks
|
||||
if [ ! -L $target/etc/resolv.conf -a -e /etc/resolv.conf ]; then
|
||||
cp -p /etc/resolv.conf $target/etc
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# here fcopy is mostly used, when installing a client for running in a
|
||||
# different subnet than during the installation
|
||||
fcopy -iM /etc/resolv.conf
|
||||
fcopy -iM /etc/network/interfaces /etc/networks
|
||||
|
||||
exit $error
|
|
@ -0,0 +1,51 @@
|
|||
#! /bin/bash
|
||||
|
||||
# (c) Thomas Lange, 2001-2016, lange@debian.org
|
||||
# (c) Michael Goetze, 2010-2011, mgoetze@mgoetze.net
|
||||
|
||||
error=0; trap 'error=$(($?>$error?$?:$error))' ERR # save maximum error code
|
||||
|
||||
# a list of modules which are loaded at boot time
|
||||
for module in $MODULESLIST; do
|
||||
ainsl -a /etc/modules "^$module$"
|
||||
done
|
||||
|
||||
fcopy -Mv /etc/hostname || echo $HOSTNAME > $target/etc/hostname
|
||||
ainsl -a /etc/mailname ${HOSTNAME}
|
||||
if [ ! -e $target/etc/adjtime ]; then
|
||||
printf "0.0 0 0.0\n0\nUTC\n" > $target/etc/adjtime
|
||||
fi
|
||||
if [ "$UTC" = "yes" ]; then
|
||||
sed -i -e 's:^LOCAL$:UTC:' $target/etc/adjtime
|
||||
else
|
||||
sed -i -e 's:^UTC$:LOCAL:' $target/etc/adjtime
|
||||
fi
|
||||
|
||||
# enable linuxlogo
|
||||
if [ -f $target/etc/inittab ]; then
|
||||
sed -i -e 's#/sbin/getty 38400#/sbin/getty -f /etc/issue.linuxlogo 38400#' ${target}/etc/inittab
|
||||
elif [ -f $target/lib/systemd/system/getty@.service ]; then
|
||||
sed -i -e 's#sbin/agetty --noclear#sbin/agetty -f /etc/issue.linuxlogo --noclear#' $target/lib/systemd/system/getty@.service
|
||||
fi
|
||||
|
||||
# make sure a machine-id exists
|
||||
if [ ! -f $target/etc/machine-id ]; then
|
||||
> $target/etc/machine-id
|
||||
fi
|
||||
# recreate machine-id if the file is empty
|
||||
if [ X"$(stat -c '%s' $target/etc/machine-id 2>/dev/null)" = X0 -a -f $target/bin/systemd-machine-id-setup ]; then
|
||||
$ROOTCMD systemd-machine-id-setup
|
||||
fi
|
||||
|
||||
ln -fs /proc/mounts $target/etc/mtab
|
||||
|
||||
rm -f $target/etc/dpkg/dpkg.cfg.d/fai $target/etc/dpkg/dpkg.cfg.d/unsafe-io
|
||||
|
||||
if [ -d /etc/fai ]; then
|
||||
if ! fcopy -Mv /etc/fai/fai.conf; then
|
||||
ainsl -a /etc/fai/fai.conf "FAI_CONFIG_SRC=$FAI_CONFIG_SRC"
|
||||
fi
|
||||
fi
|
||||
fcopy -iv /etc/rc.local
|
||||
|
||||
exit $error
|
|
@ -0,0 +1,38 @@
|
|||
#! /bin/bash
|
||||
|
||||
# (c) Thomas Lange, 2001-2012, lange@debian.org
|
||||
|
||||
error=0; trap 'error=$(($?>$error?$?:$error))' ERR # save maximum error code
|
||||
|
||||
echo $TIMEZONE > $target/etc/timezone
|
||||
if [ -L $target/etc/localtime ]; then
|
||||
ln -sf /usr/share/zoneinfo/${TIMEZONE} $target/etc/localtime
|
||||
else
|
||||
cp -f /usr/share/zoneinfo/${TIMEZONE} $target/etc/localtime
|
||||
fi
|
||||
|
||||
if [ -f $target/etc/hosts.orig ]; then
|
||||
mv $target/etc/hosts.orig $target/etc/hosts
|
||||
fi
|
||||
if [ -n "$IPADDR" ]; then
|
||||
# ifclass DHCPC ||
|
||||
ainsl -s /etc/hosts "$IPADDR $HOSTNAME.$DOMAIN $HOSTNAME"
|
||||
else
|
||||
ifclass DHCPC && ainsl -s /etc/hosts "127.0.0.1 $HOSTNAME"
|
||||
fi
|
||||
fcopy -iM /etc/hosts /etc/motd
|
||||
|
||||
# make /root accessible only by root
|
||||
chmod -c 0700 $target/root
|
||||
chown -c root:root $target/root
|
||||
# copy default dotfiles for root account
|
||||
fcopy -ir /root
|
||||
|
||||
# use tmpfs for /tmp if not defined in disk_config
|
||||
if ! grep -Pq '\s/tmp\s' $target/etc/fstab; then
|
||||
ainsl /etc/fstab "tmpfs /tmp tmpfs nodev,nosuid,size=50%,mode=1777 0 0"
|
||||
fi
|
||||
chmod -c 1777 ${target}/tmp
|
||||
chown -c 0:0 ${target}/tmp
|
||||
|
||||
exit $error
|
|
@ -0,0 +1,25 @@
|
|||
#! /bin/bash
|
||||
|
||||
# (c) Thomas Lange, 2006,2009, lange@debian.org
|
||||
# create entries for removable media in fstab and directories in /media
|
||||
|
||||
cdromlist() {
|
||||
[ -f /proc/sys/dev/cdrom/info ] || return
|
||||
devs=$(grep 'drive name:' /proc/sys/dev/cdrom/info | cut -d ":" -f 2)
|
||||
for d in $devs; do
|
||||
echo $d
|
||||
done
|
||||
}
|
||||
|
||||
fstabline () {
|
||||
line=$(printf "%-15s %-15s %-7s %-15s %-7s %s\n" "$1" "$2" "$3" "$4" "$5" "$6")
|
||||
ainsl /etc/fstab "$line"
|
||||
}
|
||||
|
||||
i=0
|
||||
for cdrom in $(cdromlist | tac); do
|
||||
[ $i -eq 0 ] && ln -sfn cdrom0 $target/media/cdrom
|
||||
mkdir -p $target/media/cdrom$i
|
||||
fstabline /dev/$cdrom /media/cdrom$i udf,iso9660 ro,user,noauto 0 0
|
||||
i=$((i + 1))
|
||||
done
|
|
@ -0,0 +1,68 @@
|
|||
#! /bin/bash
|
||||
# support for GRUB version 2
|
||||
|
||||
error=0; trap 'error=$(($?>$error?$?:$error))' ERR # save maximum error code
|
||||
|
||||
# This script assumes that the disk has a GPT partition table and
|
||||
# that the extended system partition (ESP) is mounted on /boot/efi.
|
||||
# When building a disk image, we don't change the NVRAM to point at
|
||||
# the boot image we made available, because the disk image is likely
|
||||
# not installed on the current system. As a result, we force
|
||||
# installation into the removable media paths as well as the standard
|
||||
# debian path.
|
||||
|
||||
set -a
|
||||
|
||||
# do not set up grub during dirinstall
|
||||
if [ "$FAI_ACTION" = "dirinstall" ] ; then
|
||||
exit 0
|
||||
fi
|
||||
# during softupdate use this file
|
||||
[ -r $LOGDIR/disk_var.sh ] && . $LOGDIR/disk_var.sh
|
||||
|
||||
if [ -z "$BOOT_DEVICE" ]; then
|
||||
exit 189
|
||||
fi
|
||||
|
||||
# disable os-prober because of #802717
|
||||
ainsl /etc/default/grub 'GRUB_DISABLE_OS_PROBER=true'
|
||||
|
||||
# skip the rest, if not an initial installation
|
||||
if [ $FAI_ACTION != "install" ]; then
|
||||
$ROOTCMD update-grub
|
||||
exit $error
|
||||
fi
|
||||
|
||||
GROOT=$($ROOTCMD grub-probe -tdrive -d $BOOT_DEVICE)
|
||||
|
||||
# handle /boot in lvm-on-md
|
||||
_bdev=$(readlink -f $BOOT_DEVICE)
|
||||
if [ "${_bdev%%-*}" = "/dev/dm" ]; then
|
||||
BOOT_DEVICE=$( lvs --noheadings -o devices $BOOT_DEVICE | sed -e 's/^*\([^(]*\)(.*$/\1/' )
|
||||
fi
|
||||
|
||||
# Check if RAID is used for the boot device
|
||||
if [[ $BOOT_DEVICE =~ '/dev/md' ]]; then
|
||||
raiddev=${BOOT_DEVICE#/dev/}
|
||||
# install grub on all members of RAID
|
||||
for device in $(LC_ALL=C perl -ne 'if(/^'$raiddev'\s.+raid\d+\s(.+)/){ $_=$1; s/\d+\[\d+\]//g; print }' /proc/mdstat); do
|
||||
echo Install grub on /dev/$device
|
||||
$ROOTCMD grub-install --no-floppy --force-extra-removable "/dev/$device"
|
||||
done
|
||||
|
||||
elif [[ $BOOT_DEVICE =~ '/dev/loop' ]]; then
|
||||
# do not update vmram when using a loop device
|
||||
$ROOTCMD grub-install --no-floppy --force-extra-removable --modules=part_gpt --no-nvram $BOOT_DEVICE
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Grub installed on hostdisk $BOOT_DEVICE"
|
||||
fi
|
||||
|
||||
else
|
||||
$ROOTCMD grub-install --no-floppy --modules=part_gpt "$GROOT"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Grub installed on $BOOT_DEVICE = $GROOT"
|
||||
fi
|
||||
fi
|
||||
$ROOTCMD update-grub
|
||||
|
||||
exit $error
|
|
@ -0,0 +1,84 @@
|
|||
#! /bin/bash
|
||||
# support for GRUB version 2
|
||||
|
||||
error=0; trap 'error=$(($?>$error?$?:$error))' ERR # save maximum error code
|
||||
|
||||
set -a
|
||||
|
||||
# do not set up grub during dirinstall
|
||||
if [ "$FAI_ACTION" = "dirinstall" ] ; then
|
||||
exit 0
|
||||
fi
|
||||
# during softupdate use this file
|
||||
[ -r $LOGDIR/disk_var.sh ] && . $LOGDIR/disk_var.sh
|
||||
|
||||
if [ -z "$BOOT_DEVICE" ]; then
|
||||
exit 189
|
||||
fi
|
||||
|
||||
# disable os-prober because of #802717
|
||||
ainsl /etc/default/grub 'GRUB_DISABLE_OS_PROBER=true'
|
||||
|
||||
# skip the rest, if not an initial installation
|
||||
if [ $FAI_ACTION != "install" ]; then
|
||||
$ROOTCMD update-grub
|
||||
exit $error
|
||||
fi
|
||||
|
||||
get_stable_devname() {
|
||||
|
||||
local _DEV="$1"
|
||||
local i
|
||||
declare -a _RES
|
||||
|
||||
# prefer SCSI over ATA over WWN over path
|
||||
# do not use by-path
|
||||
|
||||
for i in $($ROOTCMD udevadm info -r --query=symlink "$_DEV"); do
|
||||
if [[ "$i" =~ /by-id/scsi ]]; then
|
||||
_RES[10]="$i"
|
||||
elif [[ "$i" =~ /by-id/ata ]]; then
|
||||
_RES[20]="$i"
|
||||
elif [[ "$i" =~ /by-id/wwn ]]; then
|
||||
_RES[99]="$i"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${_RES[@]::1}"
|
||||
}
|
||||
|
||||
# handle /boot in lvm-on-md
|
||||
_bdev=$(readlink -f $BOOT_DEVICE)
|
||||
if [ "${_bdev%%-*}" = "/dev/dm" ]; then
|
||||
BOOT_DEVICE=$( lvs --noheadings -o devices $BOOT_DEVICE | sed -e 's/^*\([^(]*\)(.*$/\1/' )
|
||||
fi
|
||||
|
||||
# Check if RAID is used for the boot device
|
||||
if [[ $BOOT_DEVICE =~ '/dev/md' ]]; then
|
||||
raiddev=${BOOT_DEVICE#/dev/}
|
||||
# install grub on all members of RAID
|
||||
for device in $(LC_ALL=C perl -ne 'if(/^'$raiddev'\s.+raid\d+\s(.+)/){ $_=$1; s/\d+\[\d+\]//g; s/(nvme.+?)p/$1/g; print }' /proc/mdstat); do
|
||||
pdevice=$(get_stable_devname /dev/$device)
|
||||
if [ -z "$pdevice" ]; then
|
||||
# if we cannot find a persistent name (for e.g. in a VM) use old name
|
||||
pdevice="/dev/$device"
|
||||
fi
|
||||
mbrdevices+="$pdevice, "
|
||||
echo Installing grub on /dev/$device = $pdevice
|
||||
$ROOTCMD grub-install --no-floppy "/dev/$device"
|
||||
done
|
||||
# remove last ,
|
||||
mbrdevices=${mbrdevices%, }
|
||||
else
|
||||
mbrdevices=$(get_stable_devname $BOOT_DEVICE)
|
||||
if [ -z "$mbrdevices" ]; then
|
||||
# if we cannot find a persistent name (for e.g. in a VM) use old name
|
||||
mbrdevices=$BOOT_DEVICE
|
||||
fi
|
||||
echo "Installing grub on $BOOT_DEVICE = $mbrdevices"
|
||||
$ROOTCMD grub-install --no-floppy "$mbrdevices"
|
||||
fi
|
||||
|
||||
echo "grub-pc grub-pc/install_devices multiselect $mbrdevices" | $ROOTCMD debconf-set-selections
|
||||
$ROOTCMD dpkg-reconfigure grub-pc
|
||||
exit $error
|
|
@ -0,0 +1,103 @@
|
|||
#! /bin/bash
|
||||
|
||||
# copyright Thomas Lange 2001-2016, lange@debian.org
|
||||
|
||||
error=0; trap 'error=$(($?>$error?$?:$error))' ERR # save maximum error code
|
||||
|
||||
if [ "$FAI_ACTION" = "dirinstall" -o $do_init_tasks -eq 0 ] ; then
|
||||
:
|
||||
else
|
||||
# check if mdadm has been forgotten
|
||||
if grep -q active /proc/mdstat 2>/dev/null; then
|
||||
if [ ! -d $target/etc/mdadm ]; then
|
||||
echo ERROR: Found Software RAID, but the mdadm package was not installed
|
||||
error=1
|
||||
fi
|
||||
fi
|
||||
|
||||
usedm=$(dmsetup ls 2>/dev/null | egrep -v '^live-rw|^live-base|^No devices found' | wc -l)
|
||||
if [ $usedm -ne 0 ]; then
|
||||
if [ ! -d $target/etc/lvm ]; then
|
||||
echo ERROR: Found lvm devices, but the lvm2 package was not installed
|
||||
error=1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# remove backup files from cfengine, but only if cfengine is installed
|
||||
if [ -x /usr/sbin/cfagent ] || [ -x $target/usr/sbin/cfagent ] ; then
|
||||
dirs="root etc var"
|
||||
for path in $dirs; do
|
||||
find $target/$path -maxdepth 20 -name \*.cfedited -o -name \*.cfsaved | xargs -r rm
|
||||
done
|
||||
fi
|
||||
|
||||
[ "$FAI_DEBMIRROR" ] &&
|
||||
ainsl /etc/fstab "#$FAI_DEBMIRROR $MNTPOINT nfs ro 0 0"
|
||||
|
||||
# set bios clock
|
||||
if [ $do_init_tasks -eq 1 ] ; then
|
||||
case "$UTC" in
|
||||
no|"") hwopt="--localtime" ;;
|
||||
yes) hwopt="--utc" ;;
|
||||
esac
|
||||
hwclock $hwopt --systohc || true
|
||||
fi
|
||||
|
||||
# Make sure everything is configured properly
|
||||
if ifclass DEBIAN ; then
|
||||
$ROOTCMD apt-get -f install -y
|
||||
fi
|
||||
|
||||
if [ $FAI_ACTION = "install" ]; then
|
||||
lskernels=$(echo $target/boot/vmlinu*)
|
||||
if [ ! -f ${lskernels%% *} ]; then
|
||||
echo "ERROR: No kernel was installed. Have a look at shell.log" >&2
|
||||
error=1
|
||||
fi
|
||||
fi
|
||||
|
||||
# copy sources.list
|
||||
fcopy -iSM /etc/apt/sources.list
|
||||
|
||||
|
||||
setrel() {
|
||||
|
||||
# if release is not set, try to determine it
|
||||
|
||||
if [ -n "$release" ]; then
|
||||
return
|
||||
fi
|
||||
if [ ! -f $target/etc/os-release ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
dists="jessie stretch buster bullseye bookworm trixie focal bionic xenial trusty"
|
||||
for d in $dists; do
|
||||
if grep -iq $d $target/etc/os-release; then
|
||||
release=$d
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# if installation was done from CD, replace useless sources.list
|
||||
setrel
|
||||
if [ -f $target/etc/apt/sources.list -a -n "$release" ]; then
|
||||
grep -q 'file generated by fai-cd' $target/etc/apt/sources.list && cat <<EOF > $target/etc/apt/sources.list
|
||||
deb $apt_cdn/debian $release main contrib non-free
|
||||
deb $security_cdn/debian-security ${secsuite} main contrib non-free
|
||||
#deb [trusted=yes] http://fai-project.org/download $release koeln
|
||||
EOF
|
||||
# if the package fai-server was installed, enable the project's repository
|
||||
if dpkg-query --admindir=$target/var/lib/dpkg -W fai-server >/dev/null 2>&1; then
|
||||
sed -i -e '/fai-project.org/s/^#//' $target/etc/apt/sources.list
|
||||
fi
|
||||
fi
|
||||
|
||||
# for ARM architecture, we may need the kernel and initrd to boot or flash the device
|
||||
if ifclass ARM64; then
|
||||
cp -pv $target/boot/vmlinuz* $target/boot/initrd* $FAI_RUNDIR
|
||||
fi
|
||||
|
||||
exit $error
|
|
@ -0,0 +1,56 @@
|
|||
#! /bin/bash
|
||||
|
||||
error=0; trap 'error=$(($?>$error?$?:$error))' ERR # save maximum error code
|
||||
|
||||
cp -RT $FAI/simplefiles/PARTICIPANT $target
|
||||
|
||||
$ROOTCMD dconf update
|
||||
|
||||
# Uncomment this to reinstall extensions
|
||||
# (disabled by default to speed up softupdate):
|
||||
# rm -rf $target/etc/skel/.vscode
|
||||
|
||||
# VS Code extensions
|
||||
if [ ! -d $target/etc/skel/.vscode ]; then
|
||||
# To avoid running VS Code as root, run it as nobody instead.
|
||||
mkdir $target/etc/skel/.vscode
|
||||
chown nobody:nogroup $target/etc/skel/.vscode
|
||||
shopt -s nullglob
|
||||
for ext in $FAI/downloads/*.vsix; do
|
||||
# We can't access the config space inside ROOTCMD, so we copy the file to the target.
|
||||
cp "$ext" $target/tmp/ext.vsix
|
||||
$ROOTCMD runuser -u nobody -- code --user-data-dir=/tmp/vsc.tmp \
|
||||
--extensions-dir=/etc/skel/.vscode/extensions \
|
||||
--install-extension=/tmp/ext.vsix
|
||||
done
|
||||
chown -R root:root $target/etc/skel/.vscode
|
||||
rm -rf $target/tmp/vsc.tmp $target/tmp/ext.vsix
|
||||
fi
|
||||
|
||||
# Install soi header
|
||||
tar --overwrite -xf $FAI/downloads/soi-header.tar.gz -C $target/usr/local/include --strip-components=2 soi-header/include/
|
||||
|
||||
# Install codeblocks template
|
||||
$target/bin/unzip -o $FAI/downloads/soi_template_codeblocks_ubuntu_RzdvSho.zip -d $target/usr/share/codeblocks/templates/wizard/
|
||||
if ! grep -q '_T("soi")' $target/usr/share/codeblocks/templates/wizard/config.script ; then
|
||||
sed -i 's|// project wizards|RegisterWizard(wizProject, _T("soi"), _T("A SOI task"), _T("Console"));|' $target/usr/share/codeblocks/templates/wizard/config.script
|
||||
fi
|
||||
|
||||
# add super user account
|
||||
if [ -n "$SUPER_USER_NAME" ]; then
|
||||
if ! $ROOTCMD getent passwd $SUPER_USER_NAME ; then
|
||||
$ROOTCMD adduser --disabled-login --gecos "$SUPER_USER_DISPLAYNAME" $SUPER_USER_NAME
|
||||
$ROOTCMD usermod -p "$SUPER_USER_PW" $SUPER_USER_NAME
|
||||
$ROOTCMD adduser $SUPER_USER_NAME sudo
|
||||
fi
|
||||
fi
|
||||
|
||||
# add participant account
|
||||
if [ -n "$PARTICIPANT_USER_NAME" ]; then
|
||||
if ! $ROOTCMD getent passwd $PARTICIPANT_USER_NAME ; then
|
||||
$ROOTCMD adduser --disabled-login --gecos "$PARTICIPANT_USER_NAME" $PARTICIPANT_USER_NAME
|
||||
$ROOTCMD usermod -p "$PARTICIPANT_USER_PW" $PARTICIPANT_USER_NAME
|
||||
fi
|
||||
fi
|
||||
|
||||
exit $error
|
|
@ -0,0 +1,8 @@
|
|||
#! /bin/bash
|
||||
|
||||
error=0; trap 'error=$(($?>$error?$?:$error))' ERR # save maximum error code
|
||||
|
||||
# Remove APT proxy
|
||||
rm -f $target/etc/apt/apt.conf.d/02proxy
|
||||
|
||||
exit $error
|
|
@ -0,0 +1,41 @@
|
|||
#! /bin/bash
|
||||
|
||||
error=0; trap 'error=$(($?>$error?$?:$error))' ERR # save maximum error code
|
||||
|
||||
FAI=/var/lib/bernconfig
|
||||
target=/
|
||||
|
||||
apt-get install libnss3-tools gnome-tweaks fonts-firacode
|
||||
|
||||
rsync --archive \
|
||||
--exclude /etc/apt \
|
||||
--exclude /usr/share/keyrings \
|
||||
--exclude /etc/ssh \
|
||||
$FAI/simplefiles/PARTICIPANT/ $target
|
||||
|
||||
rsync --archive \
|
||||
--exclude /etc/systemd/timesyncd.conf.d \
|
||||
--exclude /etc/hosts \
|
||||
--exclude /etc/nftables.conf \
|
||||
--exclude /root \
|
||||
$FAI/simplefiles/CONTESTANT/ $target
|
||||
|
||||
sed -i 's|firefox-esr|firefox_firefox|' $target/etc/dconf/db/local.d/00-favorite-apps
|
||||
|
||||
$ROOTCMD dconf update
|
||||
|
||||
# Install soi header
|
||||
tar --overwrite -xf $FAI/downloads/soi-header.tar.gz -C $target/usr/local/include --strip-components=2 soi-header/include/
|
||||
|
||||
|
||||
# Disable panels in gnome-control-center
|
||||
DISABLE_DESKTOP="$ROOTCMD dpkg-statoverride --force-statoverride-add --update --add root root 640"
|
||||
$DISABLE_DESKTOP /usr/share/applications/gnome-bluetooth-panel.desktop
|
||||
$DISABLE_DESKTOP /usr/share/applications/gnome-online-accounts-panel.desktop
|
||||
$DISABLE_DESKTOP /usr/share/applications/gnome-sharing-panel.desktop
|
||||
|
||||
# Auto login
|
||||
sed -i 's/# AutomaticLoginEnable = true/AutomaticLoginEnable = true/g' $target/etc/gdm3/custom.conf
|
||||
sed -i 's/# AutomaticLogin = user1/AutomaticLogin = contestant/g' $target/etc/gdm3/custom.conf
|
||||
|
||||
exit $error
|
|
@ -0,0 +1,3 @@
|
|||
# Disable blank screen
|
||||
[org/gnome/desktop/session]
|
||||
idle-delay=uint32 0
|
|
@ -0,0 +1,3 @@
|
|||
# Disable lock on blank screen
|
||||
[org/gnome/desktop/screensaver]
|
||||
lock-enabled = false
|
|
@ -0,0 +1,4 @@
|
|||
# Disable suspend when inactive
|
||||
[org/gnome/settings-daemon/plugins/power]
|
||||
sleep-inactive-ac-type = 'nothing'
|
||||
sleep-inactive-battery-type = 'nothing'
|
|
@ -0,0 +1,3 @@
|
|||
# This prevents "Updates available" notifications
|
||||
[org/gnome/software]
|
||||
allow-updates = false
|
|
@ -0,0 +1,2 @@
|
|||
[org/gnome/shell]
|
||||
enabled-extensions = ['contest-lock@soi.ch', 'user-indicator@soi.ch']
|
|
@ -0,0 +1,3 @@
|
|||
# Disable locking the screen
|
||||
[org/gnome/desktop/lockdown]
|
||||
disable-lock-screen=true
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"policies": {
|
||||
"OverrideFirstRunPage": "",
|
||||
"NoDefaultBookmarks": true,
|
||||
"DisableProfileImport": true,
|
||||
"Preferences": {
|
||||
"datareporting.policy.dataSubmissionPolicyBypassNotification": true,
|
||||
"security.default_personal_cert": "Select Automatically"
|
||||
},
|
||||
"Homepage": {
|
||||
"URL": "https://finals.soi.ch/",
|
||||
"StartPage": "homepage"
|
||||
},
|
||||
"DisplayBookmarksToolbar": true,
|
||||
"Bookmarks": [
|
||||
{
|
||||
"Title": "Grader",
|
||||
"URL": "https://finals.soi.ch/",
|
||||
"Placement": "toolbar"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
127.0.0.1 localhost
|
||||
|
||||
# The following lines are desirable for IPv6 capable hosts
|
||||
::1 ip6-localhost ip6-loopback
|
||||
fe00::0 ip6-localnet
|
||||
ff00::0 ip6-mcastprefix
|
||||
ff02::1 ip6-allnodes
|
||||
ff02::2 ip6-allrouters
|
||||
|
||||
10.0.0.8 finals.soi.ch
|
||||
10.0.0.9 contestserver
|
||||
|
||||
10.0.0.10 contestant10
|
||||
10.0.0.11 contestant11
|
||||
10.0.0.12 contestant12
|
||||
10.0.0.13 contestant13
|
||||
10.0.0.14 contestant14
|
||||
10.0.0.15 contestant15
|
||||
10.0.0.16 contestant16
|
||||
10.0.0.17 contestant17
|
||||
10.0.0.18 contestant18
|
||||
10.0.0.19 contestant19
|
||||
10.0.0.20 contestant20
|
||||
10.0.0.21 contestant21
|
||||
10.0.0.22 contestant22
|
||||
10.0.0.23 contestant23
|
||||
10.0.0.24 contestant24
|
||||
10.0.0.25 contestant25
|
||||
10.0.0.26 contestant26
|
||||
10.0.0.27 contestant27
|
||||
10.0.0.28 contestant28
|
||||
|
||||
10.0.0.40 test40
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/sbin/nft -f
|
||||
|
||||
flush ruleset
|
||||
|
||||
table inet filter {
|
||||
chain input {
|
||||
type filter hook input priority 0;
|
||||
|
||||
ct state invalid drop
|
||||
ct state { established, related } accept
|
||||
|
||||
# Accept loopback
|
||||
iif lo accept
|
||||
|
||||
# Accept ICMP
|
||||
ip protocol icmp accept
|
||||
ip6 nexthdr icmpv6 accept
|
||||
|
||||
# Accept incoming connections to these ports
|
||||
tcp dport { ssh } accept
|
||||
|
||||
reject
|
||||
}
|
||||
chain forward {
|
||||
type filter hook forward priority 0;
|
||||
reject
|
||||
}
|
||||
chain output {
|
||||
type filter hook output priority 0;
|
||||
|
||||
ct state invalid drop
|
||||
ct state { established, related } accept
|
||||
|
||||
# Accept loopback
|
||||
oif lo accept
|
||||
|
||||
# Accept outgoing connections to these addresses
|
||||
ip daddr { 10.0.0.1-10.0.0.9 } accept
|
||||
|
||||
# Accept any connections by root user
|
||||
#meta skuid root accept
|
||||
|
||||
reject
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
# Show a root password prompt for these actions:
|
||||
# - change network settings
|
||||
# - hibernate
|
||||
# - change package download proxy
|
||||
# - mount removable storage, perform other disk operations
|
||||
[Disallow actions]
|
||||
Identity=unix-user:*
|
||||
Action=org.freedesktop.ModemManager1.*;org.freedesktop.NetworkManager.*;org.freedesktop.login1.hibernate;org.freedesktop.packagekit.system-network-proxy-configure;org.freedesktop.udisks2.*
|
||||
ResultActive=auth_admin
|
||||
ResultInactive=auth_admin
|
||||
ResultAny=auth_admin
|
|
@ -0,0 +1,3 @@
|
|||
[Time]
|
||||
NTP=finals.soi.ch
|
||||
FallbackNTP=10.0.0.9
|
|
@ -0,0 +1,49 @@
|
|||
#!/bin/bash
|
||||
|
||||
# This tool installs the client certificate in Firefox and Chromium.
|
||||
|
||||
username="$1"
|
||||
|
||||
userhome="/home/$username"
|
||||
certificate="$userhome/.config/clientcert.p12"
|
||||
|
||||
runuser -u "$username" -- mkdir -p "$userhome/.config"
|
||||
mv "$userhome/clientcert.p12" "$certificate"
|
||||
chown "$username:$username" "$certificate"
|
||||
|
||||
# Delete all Firefox data
|
||||
rm -rf "$userhome/.mozilla/"
|
||||
|
||||
# Create an empty profile
|
||||
runuser -u "$username" -- mkdir -p "$userhome/.mozilla/firefox/main"
|
||||
|
||||
# Tell Firefox to user this profile
|
||||
cat <<EOF >"$userhome/.mozilla/firefox/profiles.ini"
|
||||
[Profile0]
|
||||
Name=main
|
||||
IsRelative=1
|
||||
Path=main
|
||||
|
||||
[General]
|
||||
StartWithLastProfile=1
|
||||
Version=2
|
||||
|
||||
[Install3B6073811A6ABF12]
|
||||
Default=main
|
||||
Locked=1
|
||||
|
||||
EOF
|
||||
|
||||
chown "$username:$username" "$userhome/.mozilla/firefox/profiles.ini"
|
||||
|
||||
# Create a certificate database
|
||||
runuser -u "$username" -- certutil -d "sql:$userhome/.mozilla/firefox/main/" -N --empty-password
|
||||
|
||||
# Import the client certificate
|
||||
runuser -u "$username" -- pk12util -d "sql:$userhome/.mozilla/firefox/main/" -i "$certificate" -K "" -W ""
|
||||
|
||||
# Do the same for the NSS shared certificate database, used by Chromium
|
||||
rm -rf "$userhome/.pki/"
|
||||
runuser -u "$username" -- mkdir -p "$userhome/.pki/nssdb"
|
||||
runuser -u "$username" -- certutil -d "sql:$userhome/.pki/nssdb/" -N --empty-password
|
||||
runuser -u "$username" -- pk12util -d "sql:$userhome/.pki/nssdb/" -i "$certificate" -K "" -W ""
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE NETSCAPE-Bookmark-file-1>
|
||||
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
|
||||
<TITLE>Bookmarks</TITLE>
|
||||
<H1>Bookmarks</H1>
|
||||
<DL><p>
|
||||
<DT><H3 PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Bar</H3>
|
||||
<DL><p>
|
||||
<DT><A HREF="https://finals.soi.ch/" ICON="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAC6FBMVEUAAAAYY/8YYv8YYv8XY/8AVf8XYf8YY/8YY/8YYv8uLi4vLy8xMTEwMDAvLy8crPQagvoYY/9AQEAvLy8wMDAwMDAvLy8dk8QerfYerfUYaf4YYv8aYf8vLy8wMDAwMDAwMDAwMDAwMDAfpekerfUervUXXf8YY/8XYf8gYP9AQEAvLy8wMDAwMDAvLy8rKysoaIYhmdQvOD1VVVUwMDAwMDAyMjIYYf8YY/8ddvoerPQerfUfpuosRlUvLy8vLy8AAAAXYP8YYv8YZP8erfUerPUwMDAwMDAxMTExMTEXYv8Zd/scqvEwMDAxMTEkJCQwMDAXYv8XY/8xMTEwMDAxMTEwMDAzMzMZY/8dk/kgn/8vLy8wMDAwMDAwMDAwMDAXYf8YY/8ckvgfrfUvLy8wMDAxMTEXov8frfQvLy8wMDAsLCwvLy8wMDAbYP8YYf8erfQckvkWYv8zMzMxMTExMTEwMDAXY/8YYv8ZYv8etPAckPkwMDAwMDAadv0erfUZdvwYYf8wMDAwMDAad/0erfUadv0YYv8wMDAvLy8wMDAVYP8YZP4dlPgYqvMbrvIclPkYYv8WX/8wMDAxMTEwMDAdrfUaffsYYv8ckvgfrfQdsfUfrfUerfYbkfgYYv8ZZP8uLi4wMDAxMTEerfUerfYAgP8ervUervYAqv8wMDAxMTEwMDAqVWofrfUA//8ckfcZZf8ckvgxMTEvLy8vLy8jhLQfrfQAv/8ckfgdkvgvLy8xMTEsSVYgn98rgKoXZP8wMDAwMDAgndwXYv8wMDAerPMerfUgouMYYv8eaf8bjvkfq/QxMTEwMDAtLS0yMjIxMTEwMDAZbP4Ybf8wMDAvLy8vLy8vLy8wMDAwMDAYYf8xMTEwMDAXYf8aY/8zMzMxMTE2NjYAAP8ZY/8YYv8YYv8XYv8YYv8wMDAeqPYwMTEpYn8erfUeqe8nb5MdoPcYY/8dofcerPMqW3QvMTIeqvX///8CtdVeAAAA6HRSTlMAd93fhANZbKC0IWJ9lGEu4pYIctDPcRqi/vHjHVfh8c6x8v3eTAtVVxAEkfugRgZn6JMDs7QkKok0kfvDwJKHAi32/uJNiY5YiJrWEopUB9+kpbjcbfkjSMsIJvpqy5lMt9O3nMg+C8BB/h1c8DCdwcsvBVl4zCz4UxHddpD67vyJj7X43fiCtox1JfzfFRPc8yPRc1o17J+52RoZ17qeKRb9Q7FUAqDYA/wVya/RAebD5Z3HbP1aBMHAK2nh9QakwdnGmUsr/e71EchbssAtLsKw+RWhR6JW21WyY79kUBRTEwF86eVtPJJs4AAAAAFiS0dE96vcevcAAAAJcEhZcwAAB2IAAAdiATh6mdsAAAAHdElNRQfjCQEXJDO++fPdAAACbklEQVQ4y32TVUBUQRSGR0Ul1VWxANnFwl1kDZC118QOLOwubFHBRDHXDkxUEJXVNTGxu7tFRcW8vzoq1rMzc2fZ5QHPy/3P+b9758zcM4TkGfnyF3ApmLdNCiksCv8HcOWAW+6au4enl5dnEXeuixbjgMbJLV6iJGR4lypdpuzncori4+vw/cozx1+r02kD8OVrBVqxUuUqgVVzbL0BCKoWbOTaWL0G/fa9Zi3nxfUhQGhtJsJMpjp169H6DQCdM9EQaGRmz8ZNFKXpj2bNibkFEO7wWwKthGjNO2/Tlst2QHu736EjQs1CdeJAhJDmzuhilEBXdOuuqkjmZ/foKXSv3ugjgb7oZ/9Yf7cBA+kglRiMIfL8gKFODQ8bLokRiBopKqMwWk/GaMZG2olxdLyrZgKJ9sdEUZiEySQmlq0+RRJTp7EkNoxoMV3kMxBCTLz9mXEyZvFsNglFvADCMYe48NLceTLm82wBWQiLABZBSxYvYftbukxdYvmKbEVZuYqsRoLI17Am1/5ct34D3ZjI802b6ZYkTTJv0k8AW1OwbTvdQVKtgkjcSa27eD0Ytt3qJ/dgL923n3DiQFraQWpNFeVDOCy3dQRH6TEu0o/zoz6RLqong3BKAsbTv86cFSqCt39OHZHzuBBtP92L9LfF8bsvyc1HXbb7V65e+wOLXg7M9Rj+fjgbIWnfCLz599Zt4M5ddeSS2ePefeCBHDlfH0V5+IgPre2xHNonT1OAePtIatRjJc8y5Nh7BzCR8Tzn97/gQBIT0S8zbeq9sWV66B3z8YoDr1X9JivBYEjIepvrPr57/+HjJ5J3/AP+PjzFCYiVWAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOS0wOS0wMVQyMzozNjo1MSswMjowMCj/Yj0AAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTktMDktMDFUMjM6MzY6NTErMDI6MDBZotqBAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAFd6VFh0UmF3IHByb2ZpbGUgdHlwZSBpcHRjAAB4nOPyDAhxVigoyk/LzEnlUgADIwsuYwsTIxNLkxQDEyBEgDTDZAMjs1Qgy9jUyMTMxBzEB8uASKBKLgDqFxF08kI1lQAAAABJRU5ErkJggg==">Grader</A>
|
||||
</DL><p>
|
||||
</DL><p>
|
|
@ -0,0 +1,424 @@
|
|||
// Portions of this file are taken from GNOME Shell and adapted.
|
||||
// Because of that, this gnome extension is distributed under
|
||||
// the terms of the GNU General Public License, version 2.
|
||||
|
||||
|
||||
const {
|
||||
AccountsService, Atk, Clutter, Gio,
|
||||
GLib, Graphene, Meta, Shell, St,
|
||||
} = imports.gi;
|
||||
|
||||
const Background = imports.ui.background;
|
||||
const Layout = imports.ui.layout;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
// half of the time for which a frame is displayed
|
||||
const HALF_FRAME_TIME_MS = 8;
|
||||
|
||||
const BLUR_BRIGHTNESS = 0.55;
|
||||
const BLUR_SIGMA = 60;
|
||||
|
||||
const POINTER_HIDE_TIMEOUT = 10 * GLib.USEC_PER_SEC;
|
||||
|
||||
let actor;
|
||||
let lockDialog;
|
||||
let labelCountdown;
|
||||
let labelTitle;
|
||||
let labelMessage;
|
||||
let labelUser;
|
||||
|
||||
const bgManagers = [];
|
||||
let backgroundGroup;
|
||||
|
||||
let cursorTracker;
|
||||
let motionId = 0;
|
||||
let lastMotionTime = 0;
|
||||
let pointerHidden = false;
|
||||
let pointerHideId = 0;
|
||||
|
||||
let user;
|
||||
|
||||
let grab;
|
||||
let countdownTimeoutId = 0;
|
||||
|
||||
let configFile;
|
||||
let configMonitor;
|
||||
let config;
|
||||
let startTime;
|
||||
|
||||
let isExtensionEnabled = false;
|
||||
let isActive = false;
|
||||
let isShellReady = false;
|
||||
let isActiveChanging = false;
|
||||
|
||||
function extLog (msg) {
|
||||
log(`[contest-lock] ${msg}`)
|
||||
}
|
||||
|
||||
function extLogError (msg) {
|
||||
printerr(`[contest-lock] Error: ${msg}`);
|
||||
}
|
||||
|
||||
function loadConfig () {
|
||||
configFile.load_contents_async(null, (obj, res) => {
|
||||
// If there is a poblem with the config file, log an error and keep
|
||||
// using the old config.
|
||||
let newConfig;
|
||||
try {
|
||||
const [ok, bytes] = configFile.load_contents_finish(res);
|
||||
// TextDecoder is used in upstream gnome-shell, but not yet
|
||||
// supported in current Debian.
|
||||
const contentStr = imports.byteArray.toString(bytes);
|
||||
//const contentStr = new TextDecoder().decode(bytes);
|
||||
newConfig = JSON.parse(contentStr);
|
||||
} catch (err) {
|
||||
logError(err, '[contest-lock] config file');
|
||||
return;
|
||||
}
|
||||
if (!(typeof newConfig === 'object' && newConfig != null)) {
|
||||
extLogError('config file: invalid format');
|
||||
return;
|
||||
}
|
||||
if (typeof newConfig.title !== 'string') {
|
||||
extLogError('config file: "title" must be a string');
|
||||
return;
|
||||
}
|
||||
if (typeof newConfig.message !== 'string') {
|
||||
extLogError('config file: "message" must be a string');
|
||||
return;
|
||||
}
|
||||
if (
|
||||
typeof newConfig.startTime !== 'string' ||
|
||||
!/^\d{4,}-\d\d-\d\dT\d\d:\d\d:\d\d\+\d\d:\d\d$/.test(newConfig.startTime)
|
||||
) {
|
||||
extLogError('config file: "startTime" must be a string with format 0000-00-00T00:00:00+00:00');
|
||||
return;
|
||||
}
|
||||
|
||||
extLog('Loaded new config.')
|
||||
config = newConfig;
|
||||
startTime = (new Date(newConfig.startTime)).getTime();
|
||||
|
||||
updateConfig();
|
||||
syncActive();
|
||||
});
|
||||
}
|
||||
|
||||
function syncActive () {
|
||||
if (isActiveChanging) return;
|
||||
let beforeStart = false;
|
||||
if (startTime != null) {
|
||||
const now = new Date();
|
||||
const timeToStart = startTime - now.getTime() - HALF_FRAME_TIME_MS;
|
||||
beforeStart = timeToStart > 0;
|
||||
}
|
||||
// ignore disable event when active
|
||||
if (beforeStart && isShellReady && (isExtensionEnabled || isActive)) {
|
||||
activate();
|
||||
} else {
|
||||
deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
function updateConfig () {
|
||||
if (labelTitle != null) {
|
||||
labelTitle.text = config.title;
|
||||
}
|
||||
if (labelMessage != null) {
|
||||
labelMessage.text = config.message;
|
||||
}
|
||||
}
|
||||
|
||||
function updateUser () {
|
||||
if (labelUser != null) {
|
||||
const realName = user.get_real_name();
|
||||
if (realName != null) labelUser.text = realName;
|
||||
}
|
||||
}
|
||||
|
||||
function updateCountdown () {
|
||||
countdownTimeoutId = 0;
|
||||
const now = new Date();
|
||||
const nowTime = now.getTime() + HALF_FRAME_TIME_MS;
|
||||
const timeToStart = startTime - nowTime;
|
||||
const beforeStart = timeToStart > 0;
|
||||
if (!beforeStart) {
|
||||
deactivate();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
const allSecondsToStart = Math.floor(timeToStart / 1000);
|
||||
const secondsToStart = allSecondsToStart % 60
|
||||
const allMinutesToStart = Math.floor(allSecondsToStart / 60);
|
||||
const minutesToStart = allMinutesToStart % 60;
|
||||
const hoursToStart = Math.floor(allMinutesToStart / 60);
|
||||
|
||||
let hoursString = '';
|
||||
if (hoursToStart !== 0) hoursString = `${hoursToStart}∶`;
|
||||
labelCountdown.text = hoursString +
|
||||
minutesToStart.toString().padStart(2, '0') + '∶' +
|
||||
secondsToStart.toString().padStart(2, '0');
|
||||
|
||||
const nextUpdateTime = 1000 - nowTime % 1000
|
||||
countdownTimeoutId = GLib.timeout_add(
|
||||
GLib.PRIORITY_HIGH,
|
||||
nextUpdateTime,
|
||||
updateCountdown
|
||||
);
|
||||
GLib.Source.set_name_by_id(countdownTimeoutId, '[contest-lock] updateCountdown');
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
function updateBackgrounds () {
|
||||
if (!isActive) return;
|
||||
while (bgManagers.length) bgManagers.pop().destroy();
|
||||
backgroundGroup.destroy_all_children();
|
||||
|
||||
for (let monitorIndex = 0; monitorIndex < Main.layoutManager.monitors.length; monitorIndex++) {
|
||||
const monitor = Main.layoutManager.monitors[monitorIndex];
|
||||
const widget = new St.Widget({
|
||||
style_class: 'screen-shield-background',
|
||||
x: monitor.x,
|
||||
y: monitor.y,
|
||||
width: monitor.width,
|
||||
height: monitor.height,
|
||||
effect: new Shell.BlurEffect({
|
||||
name: 'blur',
|
||||
brightness: BLUR_BRIGHTNESS,
|
||||
sigma: BLUR_SIGMA,
|
||||
}),
|
||||
});
|
||||
|
||||
const bgManager = new Background.BackgroundManager({
|
||||
container: widget,
|
||||
monitorIndex,
|
||||
controlPosition: false,
|
||||
});
|
||||
|
||||
bgManagers.push(bgManager);
|
||||
|
||||
backgroundGroup.add_child(widget);
|
||||
}
|
||||
}
|
||||
|
||||
function pointerHideTimer () {
|
||||
if (pointerHideId !== 0) {
|
||||
GLib.source_remove(pointerHideId);
|
||||
pointerHideId = 0;
|
||||
}
|
||||
if (!isActive) return GLib.SOURCE_REMOVE;
|
||||
|
||||
const timeToHide = lastMotionTime + POINTER_HIDE_TIMEOUT - GLib.get_monotonic_time();
|
||||
if (timeToHide <= 0) {
|
||||
cursorTracker.set_pointer_visible(false);
|
||||
pointerHidden = true;
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
pointerHideId = GLib.timeout_add(
|
||||
GLib.PRIORITY_HIGH,
|
||||
timeToHide / 1000 + 20,
|
||||
pointerHideTimer
|
||||
);
|
||||
GLib.Source.set_name_by_id(pointerHideId, '[contest-lock] pointerHide');
|
||||
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
function activate () {
|
||||
if (isActive) return;
|
||||
isActiveChanging = true;
|
||||
isActive = true;
|
||||
|
||||
grab = Main.pushModal(Main.uiGroup, { actionMode: Shell.ActionMode.LOCK_SCREEN });
|
||||
if (typeof grab === 'boolean') { // gnome 38
|
||||
if (!grab) {
|
||||
grab = Main.pushModal(Main.uiGroup, {
|
||||
options: Meta.ModalOptions.POINTER_ALREADY_GRABBED,
|
||||
actionMode: Shell.ActionMode.LOCK_SCREEN
|
||||
});
|
||||
}
|
||||
if (!grab) {
|
||||
extLogError('Failed to activate: Could not obtain keyboard grab.');
|
||||
return;
|
||||
}
|
||||
grab = Main.uiGroup;
|
||||
} else if ((grab.get_seat_state() & Clutter.GrabState.KEYBOARD) === 0) {
|
||||
Main.popModal(grab);
|
||||
grab = null;
|
||||
extLogError('Failed to activate: Could not obtain keyboard grab.');
|
||||
return;
|
||||
}
|
||||
|
||||
actor.show();
|
||||
|
||||
Main.sessionMode.pushMode('unlock-dialog');
|
||||
|
||||
backgroundGroup = new Clutter.Actor();
|
||||
|
||||
motionId = global.stage.connect('captured-event', (stage, event) => {
|
||||
if (event.type() === Clutter.EventType.MOTION) {
|
||||
lastMotionTime = GLib.get_monotonic_time();
|
||||
if (pointerHidden) {
|
||||
cursorTracker.set_pointer_visible(true);
|
||||
pointerHidden = false;
|
||||
pointerHideTimer();
|
||||
}
|
||||
}
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
});
|
||||
cursorTracker.set_pointer_visible(false);
|
||||
pointerHidden = true;
|
||||
|
||||
labelCountdown = new St.Label({
|
||||
style_class: 'contest-lock-countdown',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
labelTitle = new St.Label({
|
||||
style_class: 'contest-lock-title',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
labelMessage = new St.Label({
|
||||
style_class: 'contest-lock-message',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
labelUser = new St.Label({
|
||||
style_class: 'contest-lock-user',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
|
||||
const stack = new St.BoxLayout({
|
||||
style_class: 'contest-lock-stack',
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
})
|
||||
stack.add_child(labelUser);
|
||||
stack.add_child(labelCountdown);
|
||||
stack.add_child(labelTitle);
|
||||
stack.add_child(labelMessage);
|
||||
|
||||
const mainBox = new St.BoxLayout();
|
||||
mainBox.add_constraint(new Layout.MonitorConstraint({ primary: true }));
|
||||
mainBox.add_child(stack);
|
||||
|
||||
lockDialog = new St.Widget({
|
||||
name: 'contestLockDialog',
|
||||
accessible_role: Atk.Role.WINDOW,
|
||||
visible: false,
|
||||
reactive: true,
|
||||
can_focus: true,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
pivot_point: new Graphene.Point({ x: 0.5, y: 0.5 }),
|
||||
});
|
||||
lockDialog.add_child(backgroundGroup);
|
||||
lockDialog.add_child(mainBox);
|
||||
|
||||
updateConfig();
|
||||
updateUser();
|
||||
updateCountdown();
|
||||
updateBackgrounds();
|
||||
|
||||
// countdown may have just expired before we called updateCountdown
|
||||
if (!isActive) return;
|
||||
|
||||
actor.add_child(lockDialog);
|
||||
lockDialog.show();
|
||||
|
||||
extLog('Activated.')
|
||||
isActiveChanging = false;
|
||||
}
|
||||
|
||||
function deactivate () {
|
||||
if (!isActive) return;
|
||||
isActiveChanging = true;
|
||||
isActive = false;
|
||||
|
||||
if (Main.sessionMode.currentMode === 'unlock-dialog') {
|
||||
Main.sessionMode.popMode('unlock-dialog');
|
||||
}
|
||||
|
||||
Main.popModal(grab);
|
||||
grab = null;
|
||||
|
||||
if (countdownTimeoutId !== 0) {
|
||||
GLib.source_remove(countdownTimeoutId);
|
||||
countdownTimeoutId = 0;
|
||||
}
|
||||
|
||||
actor.hide();
|
||||
labelCountdown = null;
|
||||
labelTitle = null;
|
||||
labelMessage = null;
|
||||
labelUser = null;
|
||||
while (bgManagers.length) bgManagers.pop().destroy();
|
||||
lockDialog.destroy();
|
||||
lockDialog = null;
|
||||
|
||||
if (motionId) {
|
||||
global.stage.disconnect(motionId);
|
||||
motionId = 0;
|
||||
}
|
||||
cursorTracker.set_pointer_visible(true);
|
||||
|
||||
extLog('Deactivated.');
|
||||
isActiveChanging = false;
|
||||
}
|
||||
|
||||
function init (extension) {
|
||||
actor = Main.layoutManager.screenShieldGroup;
|
||||
|
||||
const userName = GLib.get_user_name();
|
||||
user = AccountsService.UserManager.get_default().get_user(userName);
|
||||
if (!user) return;
|
||||
|
||||
user.connect('notify::is-loaded', updateUser);
|
||||
user.connect('changed', updateUser);
|
||||
updateUser();
|
||||
|
||||
Main.layoutManager.connect('monitors-changed', updateBackgrounds);
|
||||
|
||||
cursorTracker = Meta.CursorTracker.get_for_display(global.display);
|
||||
|
||||
if (!Main.layoutManager._startingUp) {
|
||||
isShellReady = true;
|
||||
} else {
|
||||
Main.layoutManager.connect('startup-complete', () => {
|
||||
isShellReady = true;
|
||||
syncActive();
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: When we drop compatibility with gnome <42, remove this code,
|
||||
// rename the stylesheet back to stylesheet.css (so that it is loaded
|
||||
// by the extension system) and add a session-modes property which
|
||||
// includes unlock-dialog to metadata.json.
|
||||
const theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
|
||||
const stylesheetFile = extension.dir.get_child('stylesheet-always.css');
|
||||
theme.load_stylesheet(stylesheetFile);
|
||||
|
||||
// TODO: When we drop compatibility with gnome <42, remove this code.
|
||||
// gnome 38 has a bug that causes extensions to break when running
|
||||
// `dconf update` while the screen is locked.
|
||||
Main.extensionManager.reloadExtension = function () {};
|
||||
|
||||
configFile = Gio.File.new_for_path('/etc/contest-lock.json');
|
||||
configMonitor = configFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
||||
configMonitor.set_rate_limit(1000);
|
||||
configMonitor.connect('changed', loadConfig);
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
function enable () {
|
||||
isExtensionEnabled = true;
|
||||
syncActive();
|
||||
}
|
||||
|
||||
function disable () {
|
||||
isExtensionEnabled = false;
|
||||
syncActive();
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extension-id": "contest-lock",
|
||||
"uuid": "contest-lock@soi.ch",
|
||||
"name": "Contest lock screen",
|
||||
"description": "A custom lock screen for contests.",
|
||||
"shell-version": [ "3.38", "42" ],
|
||||
"url": ""
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
.contest-lock-stack {
|
||||
color: white;
|
||||
text-align: center;
|
||||
spacing: 24px;
|
||||
}
|
||||
|
||||
.contest-lock-countdown {
|
||||
font-size: 64pt;
|
||||
font-weight: 300;
|
||||
font-feature-settings: "tnum"; /* tabular figures */
|
||||
}
|
||||
|
||||
.contest-lock-title {
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
.contest-lock-user {
|
||||
font-size: 20pt;
|
||||
}
|
||||
|
||||
.contest-lock-message {
|
||||
padding-top: 24px;
|
||||
font-size: 16pt;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
const { St, Clutter, GLib, Gio, AccountsService } = imports.gi;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
let panelBin;
|
||||
let userLabel;
|
||||
|
||||
let user;
|
||||
|
||||
function updateUser () {
|
||||
const realName = user.get_real_name();
|
||||
if (realName != null) userLabel.text = realName;
|
||||
}
|
||||
|
||||
function init () {
|
||||
panelBin = new St.Bin({
|
||||
style_class: 'panel-bin',
|
||||
});
|
||||
userLabel = new St.Label({
|
||||
text: 'No user',
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
});
|
||||
panelBin.set_child(userLabel);
|
||||
|
||||
const userName = GLib.get_user_name();
|
||||
user = AccountsService.UserManager.get_default().get_user(userName);
|
||||
if (!user) return;
|
||||
|
||||
user.connect('notify::is-loaded', updateUser);
|
||||
user.connect('changed', updateUser);
|
||||
updateUser();
|
||||
}
|
||||
|
||||
function enable () {
|
||||
Main.panel._rightBox.insert_child_at_index(panelBin, 0);
|
||||
}
|
||||
|
||||
function disable () {
|
||||
Main.panel._rightBox.remove_child(panelBin);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extension-id": "user-indicator",
|
||||
"uuid": "user-indicator@soi.ch",
|
||||
"name": "User indicator",
|
||||
"description": "Shows the user's real name in the top bar.",
|
||||
"shell-version": [ "3.38", "42" ],
|
||||
"url": ""
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
.panel-bin {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px; }
|
|
@ -0,0 +1 @@
|
|||
deb [arch=amd64 signed-by=/usr/share/keyrings/atom-archive-keyring.gpg] http://packagecloud.io/AtomEditor/atom/any/ any main
|
|
@ -0,0 +1 @@
|
|||
deb [arch=amd64 signed-by=/usr/share/keyrings/sublimehq-archive-keyring.gpg] http://download.sublimetext.com/ apt/stable/
|
|
@ -0,0 +1 @@
|
|||
deb [arch=amd64,arm64,armhf signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] http://packages.microsoft.com/repos/code stable main
|
|
@ -0,0 +1,5 @@
|
|||
[org/gnome/desktop/background]
|
||||
picture-uri = 'file:///usr/local/share/backgrounds/wallpaper-soi.svg'
|
||||
picture-options = 'scaled'
|
||||
primary-color = '#e6e6e6'
|
||||
secondary-color = '#555555'
|
|
@ -0,0 +1,2 @@
|
|||
[org/gnome/shell]
|
||||
favorite-apps = ['firefox-esr.desktop', 'gnome-terminal.desktop', 'nautilus.desktop']
|
|
@ -0,0 +1,5 @@
|
|||
# Configure the default keyboard layouts shown in the switcher
|
||||
# According to Wikipedia, the Italian-speaking part of Switzerland also uses the ch+fr layout.
|
||||
# The first option is the default.
|
||||
[org/gnome/desktop/input-sources]
|
||||
sources = [('xkb', 'ch'), ('xkb', 'ch+fr'), ('xkb', 'us')]
|
|
@ -0,0 +1,2 @@
|
|||
user-db:user
|
||||
system-db:local
|
|
@ -0,0 +1,2 @@
|
|||
PasswordAuthentication no
|
||||
AllowUsers root
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="-20 -20 153.6 86.4" width="1920" height="1080" version="1.1" id="svg22" sodipodi:docname="wallpaper.svg" inkscape:version="0.92.3 (2405546, 2018-03-11)">
|
||||
<metadata id="metadata28">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||
<dc:title>Swiss Olympiad in Informatics</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs id="defs26"/>
|
||||
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="1669" inkscape:window-height="913" id="namedview24" showgrid="false" inkscape:zoom="0.472" inkscape:cx="811.36486" inkscape:cy="502.72794" inkscape:window-x="67" inkscape:window-y="27" inkscape:window-maximized="0" inkscape:current-layer="svg22"/>
|
||||
<title id="title2">Swiss Olympiad in Informatics</title>
|
||||
<rect style="fill:#e6e6e6;fill-opacity:1;stroke:#f9fdff;stroke-width:0.60472441;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect837" width="160" height="97.627121" x="-23.050846" y="-24.955931"/>
|
||||
<circle r="18.903814" id="circle4" cx="57.553818" cy="24.70454" style="fill:none;stroke:#303030;stroke-width:3.43705702"/>
|
||||
<path d="M 68.019104,24.704538 57.553815,14.239249 75.801426,3.0788495 M 47.088526,24.704538 H 68.019104 L 57.553815,35.169826 47.088526,24.704538 37.798573,43.321151" id="path6" inkscape:connector-curvature="0" style="fill:none;stroke:#1eadf5;stroke-width:2.74964571"/>
|
||||
<g id="g20" style="fill:#1862ff" transform="matrix(1.3748228,0,0,1.3748228,57.553815,24.704538)">
|
||||
<circle r="3" cy="7.6121001" id="circle8" cx="0"/>
|
||||
<circle r="3" cx="7.6121001" id="circle10" cy="0"/>
|
||||
<circle r="3" cy="-7.6121001" id="circle12" cx="0"/>
|
||||
<circle r="3" cx="-7.6121001" id="circle14" cy="0"/>
|
||||
<circle r="3" cx="13.2727" cy="-15.7298" id="circle16"/>
|
||||
<circle r="3" cx="-14.3693" cy="13.5411" id="circle18"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,629 @@
|
|||
# soifai
|
||||
|
||||
This repository contains the configuration for setting up computers for participants, either for contests or for training.
|
||||
The installation is done with FAI (Fully Automatic Installation), read the [FAI guide] to learn about it.
|
||||
|
||||
[FAI guide]: https://fai-project.org/fai-guide/
|
||||
|
||||
The way it works is that you have a FAI server, which is just a computer running a lot of different servers.
|
||||
The computers to be installed are then network booted, and the installation happens automatically, which takes about 10 minutes.
|
||||
Finally, the computers can be rebooted into the installed operating system.
|
||||
|
||||
When the installed computers are running, you can edit the configuration and perform a soft update.
|
||||
This applies the entire configuration without reinstalling the machines, and it only takes a few seconds.
|
||||
|
||||
Here is what each server on the FAI server does:
|
||||
|
||||
- DHCP: This assigns IP addresses to machines, and also the TFTP server and filename for network booting.
|
||||
- TFTP: This serves the bootloader and kernel which runs during the installation.
|
||||
It also has a config file for each machine, which tells it what to do after network booting.
|
||||
- NFS: Short for Network File System. It is read-only for clients, and contains two things:
|
||||
- The NFS root. This is the root file system of the OS which runs during the installation.
|
||||
- The config space. This is stored in this repository, and it defines how the machines are configured.
|
||||
- SSH: When the installation is complete, the installer uploads log files via ssh, and also disables installation for the just installed machine.
|
||||
- NTP: This serves the time, important for contests.
|
||||
- apt-cacher-ng: This is a proxy for APT, which caches all downloaded packages. That way, the machines don't need to be connected to the internet for the installation, and you only download everything once.
|
||||
- FAI monitor: This is a nice GUI which shows you the installation progress of all machines. It is optional.
|
||||
|
||||
|
||||
## Setting up the FAI server
|
||||
|
||||
This part is unfortunately not automatic, but here is how to do it.
|
||||
I don't think it's a good idea to install this on your personal laptop, so instead I made a fresh install of Ubuntu on an external SSD and installed FAI server there.
|
||||
You could consider using Debian instead of Ubuntu, since Ubuntu needs a few extra steps.
|
||||
|
||||
When installing the system, set the hostname to `contestserver`.
|
||||
Don't use `fai` as your username, because that is reserved for the log upload.
|
||||
|
||||
|
||||
### Installing Ubuntu on an external disk
|
||||
|
||||
This works almost like a normal install: Download the Ubuntu installer ISO, flash it to a USB drive, and boot from it.
|
||||
However, there is one issue, which is that the EFI bootloader needs to be installed in a different way on external disks, and the Ubuntu installer does not support that.
|
||||
Here are some guides:
|
||||
|
||||
- https://wiki.ubuntuusers.de/EFI_Externer-Datentr%C3%A4ger/ (it seems this doesn't work, but it explains some things)
|
||||
- https://www.58bits.com/blog/2020/02/28/how-create-truly-portable-ubuntu-installation-external-usb-hdd-or-ssd
|
||||
|
||||
You can do it like this: (TODO: test these exact steps)
|
||||
- Start the installer from the terminal with bootloader installation disabled: `ubiquity -b &`
|
||||
- When partitioning, create an EFI partition and a root filesystem partition
|
||||
- Boot the system via the installer drive:
|
||||
- boot from installer drive
|
||||
- enter grub command line
|
||||
- run `configfile (hd1,gpt2)/boot/grub/grub.cfg` (the `hd1` part depends, it should be the external disk)
|
||||
- unmount `/boot/efi`, edit `/etc/fstab` and set the UUID for the EFI partition from the external disk, then mount it.
|
||||
- Run `grub-install -d /usr/lib/grub/x86_64-efi --efi-directory=/boot/efi/ --removable /dev/sdX` (where sdX is the external disk)
|
||||
- Alternatively you can do the chroot approach described in the guide
|
||||
|
||||
|
||||
### Setup
|
||||
|
||||
Connect the Ethernet port to a switch or another laptop, so that the link is up.
|
||||
Set the IP address of the interface to static `10.0.0.9`, netmask `255.255.255.0`, no gateway.
|
||||
|
||||
Connect to the internet over a second network interface, e.g. Wifi or USB tethering with your phone.
|
||||
You need an internet connection while installing the machines, for apt-cacher-ng.
|
||||
|
||||
|
||||
Wireshark (optional, useful for debugging network problems):
|
||||
|
||||
```
|
||||
sudo apt install wireshark
|
||||
sudo usermod -a -G wireshark $USER
|
||||
```
|
||||
|
||||
You need to reboot before you can use wireshark.
|
||||
|
||||
|
||||
Time server:
|
||||
```
|
||||
sudo apt install ntp
|
||||
```
|
||||
You should also install ntp on the grader.
|
||||
That way, the grader and the computers have exactly the same time.
|
||||
The NTP server on the FAI server is used as a fallback.
|
||||
|
||||
|
||||
Parallel ssh:
|
||||
```
|
||||
sudo apt install pssh
|
||||
```
|
||||
|
||||
|
||||
Only on Ubuntu:
|
||||
|
||||
```
|
||||
sudo apt install debian-archive-keyring
|
||||
```
|
||||
|
||||
Install FAI:
|
||||
|
||||
```
|
||||
sudo apt install perl-tk fai-quickstart
|
||||
```
|
||||
|
||||
|
||||
Set content of `/etc/dhcp/dhcpd.conf`:
|
||||
|
||||
```
|
||||
authoritative;
|
||||
#deny unknown-clients;
|
||||
option dhcp-max-message-size 2048;
|
||||
use-host-decl-names on;
|
||||
#always-reply-rfc1048 on;
|
||||
|
||||
subnet 10.0.0.0 netmask 255.255.255.0 {
|
||||
range 10.0.0.100 10.0.0.150;
|
||||
option routers 10.0.0.9;
|
||||
option domain-name "contest";
|
||||
option domain-name-servers 10.0.0.9;
|
||||
option time-servers 10.0.0.9;
|
||||
option ntp-servers 10.0.0.9;
|
||||
server-name contestserver;
|
||||
next-server 10.0.0.9;
|
||||
if substring(option vendor-class-identifier, 0, 20) = "PXEClient:Arch:00000" {
|
||||
filename "fai/pxelinux.0";
|
||||
}
|
||||
if substring(option vendor-class-identifier, 0, 20) = "PXEClient:Arch:00007" {
|
||||
filename "fai/syslinux.efi";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reload: `sudo systemctl restart isc-dhcp-server`
|
||||
|
||||
|
||||
Package download cache:
|
||||
|
||||
```
|
||||
sudo apt install apt-cacher-ng
|
||||
```
|
||||
|
||||
Set a password for actions the web interface in `/etc/apt-cacher-ng/security.conf`.
|
||||
|
||||
Cache report at http://localhost:3142/acng-report.html
|
||||
|
||||
|
||||
Only on Ubuntu:
|
||||
Ubuntu does not have the sysvinit-core package, so we can't use it for the NFS root. Use Debian instead:
|
||||
- Replace content of `/etc/fai/apt/sources.list` with https://github.com/faiproject/fai/blob/master/conf/sources.list
|
||||
- Replace content of `/etc/fai/nfsroot.conf` with https://github.com/faiproject/fai/blob/master/conf/nfsroot.conf
|
||||
- Replace content of `/etc/fai/NFSROOT` with https://github.com/faiproject/fai/blob/master/conf/NFSROOT
|
||||
|
||||
Edit `/etc/fai/nfsroot.conf` and change these variables:
|
||||
```
|
||||
FAI_DEBOOTSTRAP_OPTS="--force-check-gpg"
|
||||
FAI_CONFIGDIR=/srv/soifai/config
|
||||
```
|
||||
|
||||
Edit `/etc/fai/fai.conf` and uncomment `LOGUSER=fai`.
|
||||
|
||||
Clone this repository and move it to `/srv` (move with sudo).
|
||||
If the fai server is on an external SSD, you can shut it down, mount the SSD on your personal laptop and copy the repo onto it, that way you don't need your private ssh key on the fai server.
|
||||
Make sure that the execute bits on the scripts are also copied.
|
||||
|
||||
|
||||
Set up FAI:
|
||||
|
||||
```
|
||||
sudo fai-setup -v
|
||||
```
|
||||
|
||||
Now you need to download some files:
|
||||
```
|
||||
cd /srv/soifai/config
|
||||
mkdir downloads
|
||||
```
|
||||
|
||||
Download VS code extensions and put them into the `/srv/soifai/config/downloads` folder.
|
||||
- https://marketplace.visualstudio.com/items?itemName=swissolyinfo.soicode
|
||||
- https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
|
||||
- https://marketplace.visualstudio.com/items?itemName=ms-python.python
|
||||
|
||||
Note: Some extensions (e.g. cpptools and python) may have pre-release versions, which should be avoided. But the marketplace website does not show which versions are pre-release, and if you just click "Download Extension", you will get a pre-release if there is one. To see which version is the most recent non-prerelease, open the extension in the marketplace in VS Code and look for the version tag. Then download this version in the "Version History" tab on the marketplace website.
|
||||
|
||||
Download the [SOI Code::Blocks template] for Ubuntu and put it in `/srv/soifai/config/downloads`.
|
||||
|
||||
[SOI Code::Blocks template]: https://soi.ch/wiki/soi-codeblocks/#install-the-soi-project-template
|
||||
|
||||
Download the SOI header:
|
||||
```
|
||||
wget -qO - https://git.soi.ch/SOI/soi-header/archive/master.tar.gz > downloads/soi-header.tar.gz
|
||||
```
|
||||
|
||||
|
||||
Create an ssh key and add the public key to `/srv/soifai/config/simplefiles/CONTESTANT/root/.ssh/authorized_keys`.
|
||||
```
|
||||
ssh-keygen -t ed25519
|
||||
```
|
||||
|
||||
|
||||
Invent a password for root on the machines.
|
||||
Create a password hash for it:
|
||||
```
|
||||
sudo apt install whois
|
||||
mkpasswd
|
||||
```
|
||||
Take the hash and put it in the `ROOTPW` variable in `class/FAIBASE.var`.
|
||||
|
||||
|
||||
## Installing the machines
|
||||
|
||||
Add the required number of hosts to `simplefiles/CONTESTANT/etc/hosts`, and install that file locally:
|
||||
```
|
||||
sudo cp simplefiles/CONTESTANT/etc/hosts /etc/hosts
|
||||
```
|
||||
|
||||
|
||||
Collect MAC addresses of clients ([guide](https://fai-project.org/fai-guide/#_a_id_mac_a_collecting_ethernet_addresses_for_multiple_hosts)).
|
||||
Start this command:
|
||||
```
|
||||
tcpdump -qtel broadcast and port bootpc | tee /tmp/mac.list
|
||||
```
|
||||
Now boot all the machines, then press Ctrl+C.
|
||||
Get a list of all MAC addresses with:
|
||||
```
|
||||
perl -ane 'print "\U$F[0]\n"' /tmp/mac.list|sort|uniq
|
||||
```
|
||||
|
||||
|
||||
For each machine, run this with the correct hostname and MAC address.
|
||||
This assigns the hostname and enables installation:
|
||||
```
|
||||
sudo dhcp-edit contestant10 3c:97:0e:1a:09:05
|
||||
sudo fai-chboot -IFv -u nfs://10.0.0.9/srv/soifai/config contestant10
|
||||
```
|
||||
|
||||
`dhcp-edit` adds a line to `/etc/dhcp/dhcpd.conf`.
|
||||
If a you want to change the MAC address associated with a hostname later, you need to edit that file.
|
||||
|
||||
Note: Network booting is disabled after installation completes. To reenable:
|
||||
```
|
||||
sudo fai-chboot -e contestant10
|
||||
```
|
||||
|
||||
|
||||
Run FAI monitor to monitor installations:
|
||||
```
|
||||
fai-monitor | fai-monitor-gui -
|
||||
```
|
||||
|
||||
Now you are ready to install the machines.
|
||||
For this, you need to interrupt the boot process and select network booting as booting method.
|
||||
For network booting to work, Secure Boot needs to be disabled on the machines.
|
||||
|
||||
|
||||
Check `/etc/exports` if NFS doesn't work. The IP address range must be correct.
|
||||
To reload nfs config: `sudo exportfs -ra`.
|
||||
|
||||
Logs are stored in `/var/log/fai/remote-logs`.
|
||||
Run this so you can read them:
|
||||
```
|
||||
sudo chmod o+rx /var/log/fai/remote-logs
|
||||
```
|
||||
|
||||
|
||||
In case network booting does not work, you can also boot with a USB stick (FAI-CD):
|
||||
```
|
||||
sudo fai-cd -A autodiscover.iso
|
||||
sudo dd if=autodiscover.iso of=/dev/sdx bs=1M conv=fsync
|
||||
```
|
||||
|
||||
For this to work, `fai-monitor` needs to be running.
|
||||
The IP address in `/var/log/fai/variables` needs to be correct.
|
||||
|
||||
|
||||
## Administrating running machines
|
||||
|
||||
For administrating all machines at the same time, use `parallel-ssh`.
|
||||
For this, you need a file containing a list of hosts; see `tools/hostlist` for an example.
|
||||
|
||||
Add host keys to your `known_hosts`:
|
||||
```
|
||||
parallel-ssh -h hostlist -O StrictHostKeyChecking=accept-new true
|
||||
```
|
||||
|
||||
Disable wifi:
|
||||
```
|
||||
parallel-ssh -h hostlist nmcli radio wifi off
|
||||
```
|
||||
(Comment: Wifi devices are turned on by default, and if you turn it off (through the UI, or with nmcli or rfkill), systemd-rfkill will create a file called e.g. `/var/lib/systemd/rfkill/pci-0000\:02\:00.0-bcma-1\:wlan` with content 1, to remember that *this* device should be turned off. I have not found a way to have *all* wifi devices turned off by default.)
|
||||
|
||||
Test time sync:
|
||||
```
|
||||
parallel-ssh -h hostlist -i date
|
||||
```
|
||||
|
||||
|
||||
### Performing a soft update
|
||||
|
||||
Do this to apply configuration after you have changed it.
|
||||
|
||||
Open FAI monitor to see progress:
|
||||
```
|
||||
fai-monitor | fai-monitor-gui -
|
||||
```
|
||||
Then:
|
||||
```
|
||||
parallel-ssh -h hostlist -o output --timeout 0 fai -v softupdate
|
||||
```
|
||||
The output of each machine is stored in the folder `output`.
|
||||
|
||||
|
||||
While working on the config and testing on a single machine, you can also run a single script, which is faster:
|
||||
```
|
||||
mount -t nfs 10.0.0.9:/srv/soifai/config /var/lib/fai/config
|
||||
FAI=/var/lib/fai/config target=/ /var/lib/fai/config/scripts/CONTESTANT/10-config
|
||||
```
|
||||
|
||||
|
||||
### Preparing client certificates
|
||||
|
||||
Prepare the `usernames.csv`.
|
||||
Each line should contain a username and real name.
|
||||
|
||||
Then run:
|
||||
|
||||
```
|
||||
sudo apt install golang-cfssl
|
||||
./create-certs.sh
|
||||
```
|
||||
|
||||
You will need `certs/ca.pem` on the grader.
|
||||
|
||||
|
||||
To test the client certificates, you can set up a test server:
|
||||
```
|
||||
sudo apt install nginx
|
||||
```
|
||||
|
||||
Edit `/etc/nginx/sites-enabled/default`:
|
||||
|
||||
```
|
||||
server {
|
||||
listen 443 ssl default_server;
|
||||
listen [::]:443 ssl default_server;
|
||||
|
||||
include snippets/snakeoil.conf;
|
||||
ssl_client_certificate /srv/soifai/tools/certs/ca.pem;
|
||||
ssl_verify_client on;
|
||||
|
||||
root /var/www/html;
|
||||
index index.html index.htm index.nginx-debian.html;
|
||||
server_name _;
|
||||
}
|
||||
```
|
||||
|
||||
Then: `sudo systemctl restart nginx.service`
|
||||
|
||||
With this configuration, you should now get an error if you access the server without a valid client certificate.
|
||||
|
||||
|
||||
### Starting the contest
|
||||
|
||||
You should stop apt-cacher-ng during contests: `sudo systemctl stop apt-cacher-ng.service`
|
||||
|
||||
Run these commands to prepare the contest
|
||||
```sh
|
||||
# Kill user session
|
||||
parallel-ssh -h hostlist loginctl kill-session 1
|
||||
parallel-ssh -h hostlist pkill -KILL -u contestant
|
||||
# Delete user
|
||||
parallel-ssh -h hostlist userdel -r contestant
|
||||
# Recreate user
|
||||
parallel-ssh -h hostlist adduser --disabled-password --gecos "\"SOI Finals\"" contestant
|
||||
# Assign users to machines
|
||||
./assign-user.sh contestant10 stofl
|
||||
./assign-user.sh contestant11 binna1
|
||||
...
|
||||
|
||||
# Adjust the contest lock screen configuration and copy it to all machines
|
||||
parallel-scp -h hostlist ./contest-lock.json /etc/contest-lock.json
|
||||
|
||||
# Reboot to trigger autologin and clear `/tmp`
|
||||
parallel-ssh -h hostlist reboot
|
||||
```
|
||||
|
||||
|
||||
Start backups:
|
||||
```
|
||||
./backup-create.sh timer
|
||||
```
|
||||
|
||||
|
||||
### If backup machine needed
|
||||
|
||||
Replace `contestant25` with the backup machine name.
|
||||
Replace `contestantxx` with the old machine (prepare by keeping the assignment of users to machines close by).
|
||||
|
||||
```
|
||||
./assign-user.sh contestant25 username
|
||||
rsync -av --chown contestant:contestant backups/contestantxx/xxxx/ root@contestant25:/home/contestant/
|
||||
```
|
||||
|
||||
|
||||
## Replacement exam setup
|
||||
|
||||
We used the laptops owned by SOI for the replacement exams.
|
||||
|
||||
For this, we manually set a static IP address (same as the DHCP assigned one) in the settings on each laptop (it will prompt you for the root password).
|
||||
That way, it works without the FAI server.
|
||||
|
||||
All the commands in the section "Starting the contest" can then be run from the grader.
|
||||
You just need to install: `sudo apt install ntp pssh`
|
||||
|
||||
|
||||
## Contest setup at Uni Bern
|
||||
|
||||
At Uni Bern, we don't set up the machines from scratch.
|
||||
Most of the software is already installed for us.
|
||||
|
||||
Copy this repository to the server, and run admin commands from there.
|
||||
We don't use FAI, instead we just run a script.
|
||||
|
||||
Test on one machine:
|
||||
```
|
||||
rsync --archive --chown root:root --delete --verbose config/ root@chagall.soi:/var/lib/bernconfig/
|
||||
ssh root@chagall.soi /var/lib/bernconfig/setup-bern.sh
|
||||
```
|
||||
|
||||
Deploy to all machines:
|
||||
```
|
||||
parallel-rsync -h hostlist -x "--archive --chown root:root --delete" config/ /var/lib/bernconfig/
|
||||
parallel-ssh -h hostlist -o output /var/lib/bernconfig/setup-bern.sh
|
||||
```
|
||||
|
||||
We need to add finals.soi.ch to the hosts file, so copy the file from any machine, add the entry and then deploy it:
|
||||
```
|
||||
parallel-rsync -h hostlist -x "--archive --chown root:root" hosts /etc/hosts
|
||||
```
|
||||
|
||||
Otherwise, contest administration works the same.
|
||||
Run the backup script on the server inside a tmux, so that it continues when your ssh connection breaks.
|
||||
|
||||
|
||||
## Training setup
|
||||
|
||||
We also use FAI to set up the SOI laptops for non-contest use.
|
||||
|
||||
In `config/class/50-host-classes`, replace `CONTESTANT` with `TRAINING`.
|
||||
|
||||
You need to set the password for the admin account.
|
||||
You can find the password in the internal wiki.
|
||||
If you reinstall all laptops, you can choose a new password and update it in the wiki.
|
||||
Hash the password with `mkpasswd`, and put the hash in `SUPER_USER_PW` in the file `config/class/TRAINING.var`.
|
||||
This repo is public, so don't commit the hash.
|
||||
|
||||
Then, install the laptops as in the section "Installing the machines".
|
||||
|
||||
|
||||
## Problems and solutions
|
||||
|
||||
This is a list of problems that we had and how we solved them.
|
||||
|
||||
**cpptools VS code extension crashes.**
|
||||
Fixed by using an older of cpptools, which you can download in the "Version History" tab on the marketplace website.
|
||||
This happened because we were unknowingly using a pre-release.
|
||||
|
||||
**Network booting fails.**
|
||||
Fixed by disabling Secure Boot in the system settings.
|
||||
|
||||
**Installed system does not boot.**
|
||||
Fixed by changing boot mode from legacy/BIOS to UEFI.
|
||||
|
||||
**The first time you boot the installed system, everything is fine, but after a reboot, the screen just shows a blinking cursor.**
|
||||
Linux is actually running and you can ssh into the machine, but for some unknown reason gdm failed to start.
|
||||
We don't know why this happens yet, but we have a workaround: Just run `systemctl start gdm` via ssh.
|
||||
```
|
||||
parallel-ssh -h hostlist systemctl start gdm
|
||||
```
|
||||
|
||||
**dhcp server not running.**
|
||||
This happens if the network cable was not plugged in when booting.
|
||||
```
|
||||
systemctl status isc-dhcp-server.service
|
||||
systemctl restart isc-dhcp-server.service
|
||||
```
|
||||
|
||||
**User indicator does not appear**
|
||||
Fixed by adding the gnome shell version from `gnome-shell --version` to the list of supported versions: `shell-version` in `simplefiles/CONTESTANT/usr/share/gnome-shell/extensions/user-indicator@soi.ch/metadata.json`.
|
||||
The same applies for the contest-lock extension.
|
||||
|
||||
|
||||
## Config space
|
||||
|
||||
The config space defines how the machines are setup.
|
||||
It is mounted with nfs on the target machine.
|
||||
|
||||
FAI has the concept of *classes*.
|
||||
Classes are defined by a list of strings.
|
||||
Order matters, classes are applied in the order in which they are defined.
|
||||
|
||||
We have defined these classes:
|
||||
|
||||
- `PARTICIPANT`: This sets up things that are used for both contest and training.
|
||||
- various code editors and other tools
|
||||
- VS Code extensions
|
||||
- soi header
|
||||
- Code::Blocks template
|
||||
- wallpaper
|
||||
- default favorite apps
|
||||
- default list of keyboard layouts
|
||||
- only allow ssh login as root, and disable password auth
|
||||
- `CONTESTANT`:
|
||||
- firewall (configured in `simplefiles/CONTESTANT/etc/nftables.conf`)
|
||||
- disable bluetooth
|
||||
- disable sleep
|
||||
- disable lock on blank screen
|
||||
- disable software update notifications
|
||||
- disable some panels in gnome-control-center
|
||||
- add polkit rules which block changing network settings and mounting storage devices (it prompts for the root password)
|
||||
- configure NTP server
|
||||
- set `authorized_keys` for root
|
||||
- enable automatic login
|
||||
- set browser homepage and bookmarks to https://finals.soi.ch
|
||||
- add a gnome shell extension which displays the user name in the top bar
|
||||
- add contest lock gnome shell extension
|
||||
- add some management scripts to be run via ssh
|
||||
- add some packages
|
||||
- `TRAINING`:
|
||||
- add admin user with sudo rights
|
||||
- remove APT proxy after packages are installed
|
||||
|
||||
Here is an overview of the file structure:
|
||||
|
||||
- `class`:
|
||||
First, all scripts in this folder which start with two digits are run.
|
||||
Of these files, the ones which do not have a `.sh` suffix define the classes.
|
||||
|
||||
FAI then goes through the list of classes and reads variables from the `$class.var` file (if it exists).
|
||||
- `script`:
|
||||
This contains a folder of scripts for each class.
|
||||
In these scripts, we can use these variables:
|
||||
- `$FAI`: Path to the config space.
|
||||
- `$target`: The root of the system being installed.
|
||||
For soft update, this is `/`.
|
||||
- `$ROOTCMD`: `chroot $target` during install.
|
||||
For soft update, this is empty.
|
||||
- `simplefiles`:
|
||||
This contains a bunch of files for each class that are copied over the root of the target.
|
||||
(The FAI example config only has `files`, but that is annoying because classes are at the end of the path.)
|
||||
- `package_config`:
|
||||
This defines which packages are installed.
|
||||
|
||||
|
||||
During the warmup, some participants requested additional VS code extensions.
|
||||
Because we didn't want them to be enabled for everyone, we put the .vsix files into `simplefiles/CONTESTANT/opt/` and did a soft update, so that they could manually install it from `/opt`.
|
||||
|
||||
|
||||
Whenever you add a new script, don't forget to make it executable with `chmod +x script.sh`, otherwise it will silently not be executed.
|
||||
|
||||
|
||||
Third-party apt repos are already setup. This is how it was done:
|
||||
```
|
||||
wget -qO - https://packagecloud.io/AtomEditor/atom/gpgkey | gpg --dearmor > simplefiles/PARTICIPANT/usr/share/keyrings/atom-archive-keyring.gpg
|
||||
wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | gpg --dearmor > simplefiles/PARTICIPANT/usr/share/keyrings/sublimehq-archive-keyring.gpg
|
||||
wget -qO - https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > simplefiles/PARTICIPANT/usr/share/keyrings/microsoft-archive-keyring.gpg
|
||||
|
||||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/atom-archive-keyring.gpg] http://packagecloud.io/AtomEditor/atom/any/ any main" > simplefiles/PARTICIPANT/etc/apt/sources.list.d/atom.list
|
||||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/sublimehq-archive-keyring.gpg] http://download.sublimetext.com/ apt/stable/" > simplefiles/PARTICIPANT/etc/apt/sources.list.d/sublime-text.list
|
||||
echo "deb [arch=amd64,arm64,armhf signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] http://packages.microsoft.com/repos/code stable main" > simplefiles/PARTICIPANT/etc/apt/sources.list.d/vscode.list
|
||||
```
|
||||
|
||||
|
||||
### Contest lock screen
|
||||
|
||||
The contest lock screen is a gnome extension which can lock the screen and show a countdown until the contest starts.
|
||||
The screen is unlocked when the contest starts.
|
||||
The lock screen also displays the user name and a title.
|
||||
It is configured in the file `/etc/contest-lock.json`.
|
||||
It watches this file, and when it changes the new configuration is instantly applied.
|
||||
|
||||
If there is an error in the config file, it will continue to use the old config and print a message.
|
||||
To see the logs, run this on a contestant machine:
|
||||
```
|
||||
journalctl -f -o cat /usr/bin/gnome-shell
|
||||
```
|
||||
|
||||
An additional text can be shown with the `message` field. It can contain newlines (`\n`).
|
||||
|
||||
|
||||
In case there is a problem with the contest lock screen and you can't fix it, the backup solution is to turn off `AutomaticLoginEnable` and set a password instead, that you announce when the contest starts:
|
||||
```
|
||||
parallel-ssh -h hostlist 'chpasswd <<< contestant:stofl'
|
||||
```
|
||||
|
||||
|
||||
**Development notes**
|
||||
|
||||
Links:
|
||||
- https://www.codeproject.com/Articles/5271677/How-to-Create-A-GNOME-Extension
|
||||
- https://gjs.guide/
|
||||
|
||||
Regular lock screen (contest-lock is based on this):
|
||||
- https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/screenShield.js
|
||||
- https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/unlockDialog.js
|
||||
|
||||
Developer commands:
|
||||
- Restart gnome-shell: Press Alt+F2, enter `r`. Only works if you log in with Xorg.
|
||||
- Open the gnome-shell developer tools: Press Alt+F2, enter `lg`.
|
||||
|
||||
|
||||
## TODO
|
||||
|
||||
- It would be useful to have something like lineinfile from ansible.
|
||||
FAI has ainsl, but it's not powerful enough.
|
||||
We could just copy the ansible code and make a cli out of it:
|
||||
https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/lineinfile.py
|
||||
- Allow locale change.
|
||||
The problem is that this requires logging out and back in.
|
||||
Maybe this could be a feature of contest-lock.
|
||||
You may also want to install task-german, firefox-esr-l10n-de
|
||||
(and same for other languages).
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This project is distributed under the terms of the GNU General
|
||||
Public License, version 2.
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/bash
|
||||
|
||||
machinename="$1"
|
||||
username="$2"
|
||||
|
||||
machineusername=contestant
|
||||
|
||||
userline=$(grep "^$username;" usernames.csv)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "User $username not found"
|
||||
exit 1
|
||||
fi
|
||||
fullname=$(echo "$userline" | cut "-d;" -f2)
|
||||
|
||||
# Set real name of machine user
|
||||
ssh root@"$machinename" chfn --full-name "\"$fullname\"" $machineusername
|
||||
|
||||
# Install client certificate
|
||||
scp "certs/$username.p12" "root@$machinename:/home/$machineusername/clientcert.p12"
|
||||
ssh root@"$machinename" install-client-cert $machineusername
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/bash
|
||||
|
||||
sleep_secs=120
|
||||
|
||||
do_backup() {
|
||||
for hostlogin in $(cat hostlist); do
|
||||
host=$(echo $hostlogin | cut "-d@" -f2)
|
||||
echo $host
|
||||
target=backups/$host/$(date --iso-8601=seconds)
|
||||
mkdir -p $target
|
||||
rsync --archive --verbose --prune-empty-dirs --exclude ".*" --exclude "/snap" --exclude "Screenshot from *" --max-size 200K $hostlogin:/home/contestant/ $target
|
||||
chmod o+rx $target
|
||||
done
|
||||
}
|
||||
|
||||
if [ $1 == timer ]; then
|
||||
while true; do
|
||||
do_backup
|
||||
echo
|
||||
echo "Finished, next backup in $sleep_secs seconds."
|
||||
echo
|
||||
echo
|
||||
sleep $sleep_secs
|
||||
done
|
||||
else
|
||||
do_backup
|
||||
fi
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"title": "SOI Finals 2023 · Day 1",
|
||||
"message": "",
|
||||
"startTime": "2023-05-05T12:00:00+02:00"
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env bash
|
||||
# install cfssl
|
||||
|
||||
set -e
|
||||
|
||||
usernames=$(cat usernames.csv | cut "-d;" -f1)
|
||||
|
||||
mkdir -p certs
|
||||
cd certs
|
||||
|
||||
cat <<EOF > ca.json
|
||||
{
|
||||
"CN": "SOI Finals Root CA",
|
||||
"key": {
|
||||
"algo": "rsa",
|
||||
"size": 2048
|
||||
}
|
||||
}
|
||||
|
||||
EOF
|
||||
|
||||
cfssl gencert -initca ca.json | cfssljson -bare ca
|
||||
|
||||
cat <<EOF >client-config.json
|
||||
{
|
||||
"signing": {
|
||||
"default": {
|
||||
"expiry": "438000h"
|
||||
},
|
||||
"profiles": {
|
||||
"client": {
|
||||
"usages": ["signing", "key encipherment", "digital signature", "client auth"],
|
||||
"expiry": "438000h"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EOF
|
||||
|
||||
for username in $usernames; do
|
||||
|
||||
cat <<EOF >client-csr-$username.json
|
||||
{
|
||||
"CN": "$username",
|
||||
"key": {
|
||||
"algo": "rsa",
|
||||
"size": 2048
|
||||
}
|
||||
}
|
||||
|
||||
EOF
|
||||
|
||||
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=client-config.json -profile=client client-csr-$username.json | cfssljson --bare $username-cert
|
||||
|
||||
openssl pkcs12 -export -in $username-cert.pem -inkey $username-cert-key.pem -out $username.p12 -passout pass:
|
||||
|
||||
done
|
|
@ -0,0 +1,19 @@
|
|||
root@contestant10
|
||||
root@contestant11
|
||||
root@contestant12
|
||||
root@contestant13
|
||||
root@contestant14
|
||||
root@contestant15
|
||||
root@contestant16
|
||||
root@contestant17
|
||||
root@contestant18
|
||||
root@contestant19
|
||||
root@contestant20
|
||||
root@contestant21
|
||||
root@contestant22
|
||||
root@contestant23
|
||||
root@contestant24
|
||||
root@contestant25
|
||||
root@contestant26
|
||||
root@contestant27
|
||||
root@contestant28
|
|
@ -0,0 +1,21 @@
|
|||
root@amiet.soi
|
||||
root@cezanne.soi
|
||||
root@muenter.soi
|
||||
root@yoshitoshi.soi
|
||||
root@bruegel.soi
|
||||
root@haring.soi
|
||||
root@giger.soi
|
||||
root@constable.soi
|
||||
root@elgreco.soi
|
||||
root@helnwein.soi
|
||||
root@rubens.soi
|
||||
root@escher.soi
|
||||
|
||||
root@tizian.soi
|
||||
root@courbet.soi
|
||||
root@rembrandt.soi
|
||||
root@duerer.soi
|
||||
|
||||
root@macke.soi
|
||||
root@chagall.soi
|
||||
root@renoir.soi
|
|
@ -0,0 +1 @@
|
|||
root@test40
|
|
@ -0,0 +1,2 @@
|
|||
stofl;Mouse Stofl
|
||||
binna1;Mouse Binna
|
|
Reference in New Issue