# Shared bash functions for cereal
#
# The cereal scripts were written by
# Jameson Rollins <jrollins@fifthhorseman.net>
# and
# Daniel Kahn Gillmor <dkg-debian.org@fifthhorseman.net>.
#
# They are Copyright 2007, and are all released under the GPL, version 3
# or later.

##################################################
# managed directories
export ETC="/etc/cereal"
export SESSIONDIR="/var/lib/cereal/sessions"
export SERVICEDIR="/var/service"
export SERVICE="$SERVICEDIR/cereal"
export ERR=0
##################################################

error() {
    echo "$1" >&2
    ERR=${2:-'1'}
}
export -f error

failure() {
    echo "$1" >&2
    exit ${2:-'2'}
}
export -f failure

# check if TTY is valid tty
# check_is_tty TTY
check_is_tty() {
    [ -c "$1" ] || failure "'$1' is not a valid tty."
}
export -f check_is_tty

# check is tty is already being used in another session
# check_is_session_tty TTY
check_is_session_tty() {
    TTY="$1"
    local SESSION
    for SESSION in $(ls "$SESSIONDIR") ; do
	if grep -q "^$TTY$" "$SESSIONDIR/$SESSION/env/TTY" ; then
	    failure "TTY '$TTY' is already being monitored by session '$SESSION'."
	fi
    done
}	
export -f check_is_session_tty

# check if USER is valid
# check_user USER
check_user() {
    getent passwd "$1" > /dev/null || failure "'$1' is not a valid user."
}
export -f check_user

# check if GROUP is valid
# check_group GROUP
check_group() {
    getent group "$1" > /dev/null || failure "'$1' is not a valid group."
}
export -f check_group

# check if the user can read/write to a TTY
# check_tty_rw USER GROUP TTY
check_tty_rw() {
    chpst -u "$1:$2" bash -c "test -r $3 && test -w $3" || failure "User '$1' does not have read/write access to tty '$3', tty is not g+rw, or you do not have permission to change user."
}
export -f check_tty_rw

# check if session exists
# is_session SESSION
is_session() {
    test -d "$SESSIONDIR/$1"
}
export -f is_session

# is_linked SESSION
is_linked() {
    test -L "$SERVICE.$1"
}
export -f is_linked

# is_running SESSION
is_running() {
    local SD="$SERVICE.$1"
    # if session is linked in service dir...
    if [ -L "$SD" ] ; then
	# return 2 if supervise/stat says that the service is *not* running
	if [ -r "$SD/supervise/stat" ] && ! grep -q run "$SD/supervise/stat" ; then
	    return 2
	fi
	# otherwise return 0 since either it is running, or we can't tell
	# whether the service is actually running or not, so assume that it is
	# if it's linked.
	return 0
    else
	# return 1 if it's not linked
	return 1
    fi
}
export -f is_running

# check if user can control session
# is_controllable SESSION
is_controllable() {
    test -w "$SERVICE.$1/supervise/control"
}
export -f is_controllable

# can_attach SESSION [USER]
can_attach() {
    local USER=${2:-"$USER"}
    [ "$USER" = $(cat "$SESSIONDIR/$1/env/USER") ]
}
export -f can_attach

# in_group USER GROUP
in_group() {
    groups "$1" | cut -d ':' -f 2 | tr ' ' '\n' | grep -q "^$2$"
}

# can_follow SESSION [USER]
can_follow() {
    local LOGUSER=$(cat "$SESSIONDIR/$1/env/LOGUSER")
    local LOGGROUP=$(cat "$SESSIONDIR/$1/env/LOGGROUP")
    local USER=${2:-"$USER"}
    [ "$USER" = "$LOGUSER" ] || in_group "$USER" "$LOGGROUP" || [ $(id -u "$USER") = '0' ]
}
export -f can_follow

# write to the log of a session
#log_write SESSION STATEMENT
log_write() {
    echo -e "\ncereal: $2" >> "$SERVICE.$1/socket"
}
export -f log_write

# display_session SESSION [USER]
display_session() {
    local SESSION SFLAG AFLAG FFLAG

    SESSION="$1"
    USER=${2:-"$USER"}

    # set state flag ('+' linked (0), '-' stopped (1), '!' linked but stopped(2))
    # last flag works only for users that can read supervise/stat
    is_running "$SESSION"
    case $? in
	0)
	    SFLAG='+'
	    ;;
	1)
	    SFLAG='-'
	    ;;
	2)
	    SFLAG='!'
	    ;;
    esac
    # set attach flag
    if can_attach "$SESSION" "$USER" ; then
	AFLAG='a'
    else
	AFLAG='-'
    fi    
    # set follow flag

    if can_follow "$SESSION" "$USER" ; then
	FFLAG='f'
    else
	FFLAG='-'
    fi

    cd "$SESSIONDIR/$SESSION/env"
    echo "${SFLAG}${AFLAG}${FFLAG} $SESSION $(cat TTY) $(cat BAUD) $(cat USER) $(cat LOGGROUP)"
}
export -f display_session

# list [-n] SESSION [SESSION...]
list() {
    local SESSION SESSIONS
    local DISP=0

    # flag to just output session names (otherwise display full info)
    if [ "$1" = '--names' -o "$1" = '-n' ] ; then
	unset DISP
	shift 1
    fi

    # list of session to display
    if [ "$1" ] ; then
	SESSIONS="$@"
    else
	SESSIONS=$(ls -1 "$SESSIONDIR" 2> /dev/null)
	[ "$SESSIONS" ] || return 1
    fi

    for SESSION in $SESSIONS ; do
	if ! is_session "$SESSION" ; then
	    error "Session '$SESSION' not found." 1
	elif [ "$DISP" ] ; then
	    display_session "$SESSION"
	else
	    echo "$SESSION"
	fi
    done
}
export -f list
