#!/usr/bin/bash # Note that this script requires "jq" to be installed and a version # of loginctl that accepts the "-j" option. while [ "$#" -gt 0 ]; do case "$1" in --path) shift; ttyPath="$1";; --path=*) IFS='=' read -r option value <<< "$1"; ttyPath="$value";; esac shift done ttyPath="${ttyPath:-/dev/virtio-ports/org.jdrupes.vmop_agent.0}" if [ ! -w "$ttyPath" ]; then echo >&2 "Device $ttyPath not writable" exit 1 fi # Create fd for the tty in variable con if ! exec {con}<>"$ttyPath"; then echo >&2 "Cannot open device $ttyPath" exit 1 fi # Temporary file for logging error messages, clear tty and signal ready temperr=$(mktemp) clear >/dev/tty1 echo >&${con} "220 Hello" # This script uses the (shared) home directory as "dictonary" for # synchronizing the username and the uid between hosts. # # Every user has a directory with his username. The directory is # owned by root to prevent changes of access rights by the user. # The uid and gid of the directory are equal. Thus the name of the # directory and the id from the group ownership also provide the # association between the username and the uid. # Add the user with name $1 to the host's "user database". This # may not be invoked concurrently. createUser() { local missing=$1 local uid local userHome="/home/$missing" local createOpts="" # Retrieve or create the uid for the username if [ -d "$userHome" ]; then # If a home directory exists, use the id from the group ownership as uid uid=$(ls -ldn "$userHome" | head -n 1 | awk '{print $4}') createOpts="--no-create-home" else # Else get the maximum of all ids from the group ownership +1 uid=$(ls -ln "/home" | tail -n +2 | awk '{print $4}' | sort | tail -1) uid=$(( $uid + 1 )) if [ $uid -lt 1100 ]; then uid=1100 fi createOpts="--create-home" fi groupadd -g $uid $missing useradd $missing -u $uid -g $uid $createOpts } # Login the user, i.e. create a desktopn for the user. doLogin() { user=$1 if [ "$user" = "root" ]; then echo >&${con} "504 Won't log in root" return fi # Check if this user is already logged in on tty2 curUser=$(loginctl -j | jq -r '.[] | select(.tty=="tty2") | .user') if [ "$curUser" = "$user" ]; then echo >&${con} "201 User already logged in" return fi # Terminate a running desktop (fail safe) attemptLogout # Check if username is known on this host. If not, create user uid=$(id -u ${user} 2>/dev/null) if [ $? != 0 ]; then ( flock 200 createUser ${user} ) 200>/home/.gen-uid-lock # This should now work, else something went wrong uid=$(id -u ${user} 2>/dev/null) if [ $? != 0 ]; then echo >&${con} "451 Cannot determine uid" return fi fi # Configure user as auto login user sed -i '/AutomaticLogin/d' /etc/gdm/custom.conf sed -i '/\[daemon\]/a AutomaticLoginEnable=true\nAutomaticLogin='$user \ /etc/gdm/custom.conf # Activate user systemctl restart gdm if [ $? -eq 0 ]; then echo >&${con} "201 User logged in successfully" else echo >&${con} "451 $(tr '\n' ' ' <${temperr})" fi } # Attempt to log out a user currently using tty1. This is an intermediate # operation that can be invoked from other operations attemptLogout() { sed -i '/AutomaticLogin/d' /etc/gdm/custom.conf systemctl stop gdm echo >&${con} "102 Desktop stopped" } # Log out any user currently using tty1. This is invoked when executing # the logout command and therefore sends back a 2xx return code. # Also try to restart gdm, if it is not running. doLogout() { attemptLogout systemctl restart gdm echo >&${con} "202 User logged out" } while read line <&${con}; do case $line in "login "*) IFS=' ' read -ra args <<< "$line"; doLogin ${args[1]};; "logout") doLogout;; esac done onExit() { doLogout if [ -n "$temperr" ]; then rm -f $temperr fi echo >&${con} "240 Quit" } trap onExit EXIT