Merge branch 'master' of git.ganneff.de:misc
[misc.git] / bin / tm
diff --git a/bin/tm b/bin/tm
index 01c8631..b532c31 100755 (executable)
--- a/bin/tm
+++ b/bin/tm
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-# Copyright (C) 2011, 2012, 2013 Joerg Jaspert <joerg@debian.org>
+# Copyright (C) 2011, 2012, 2013, 2014, 2016 Joerg Jaspert <joerg@debian.org>
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -61,6 +61,17 @@ declare -r TMDIR=${TMDIR:-"${HOME}/.tmux.d"}
 # TMSESSHOST=false -> session name is host1_host2
 declare -r TMSESSHOST=${TMSESSHOST:-"true"}
 
+# Allow to globally define a custom ssh command line.
+TMSSHCMD=${TMSSHCMD:-"ssh"}
+
+# Debug output
+declare -r DEBUG=${DEBUG:-"false"}
+
+# Save the last argument, it may be used (traditional style) for
+# replacing
+args=$#
+TMREPARG=${!args}
+
 # Where does your tmux starts numbering its windows? Mine does at 1,
 # default for tmux is 0. We try to find it out, but if we fail, (as we
 # only check $HOME/.tmux.conf you can set this variable to whatever it
@@ -71,12 +82,16 @@ if [[ -f ${HOME}/.tmux.conf ]]; then
 else
     bindex=0
 fi
-TMWIN=${TMWIN:-$bindex}
+declare TMWIN=${TMWIN:-$bindex}
 unset bindex
 
 ########################################################################
 # Nothing below here to configure
 
+# Should we group the session to another? Set to true if -g on
+# commandline
+GROUPSESSION=false
+
 # Should we open another session, even if we already have one with
 # this name? (Ie. second multisession to the same set of hosts)
 # This is either set by the getopts option -n or by having -n
@@ -92,8 +107,8 @@ else
     DOUBLENAME=false
 fi
 
-# Store the commandline
-declare -r cmdline=${1:-""}
+# Store the first commandline parameter
+cmdline=${1:-""}
 
 # Get the tmux version and split it in major/minor
 TMUXVERS=$(tmux -V 2>/dev/null || echo "tmux 1.3")
@@ -104,12 +119,19 @@ declare -r TMUXMINOR=${TMUXVERS##*.}
 # Save IFS
 declare -r OLDIFS=${IFS}
 
+# To save session file data
+TMDATA=""
+
+# Freeform .cfg file or other session file?
+TMSESCFG=""
+
 ########################################################################
 function usage() {
     echo "tmux helper by Joerg Jaspert <joerg@ganneff.de>"
     echo "There are two ways to call it. Traditional and \"getopts\" style."
-    echo "Traditional call as: $0 CMD [host]..."
-    echo "Getopts call as: $0 [-s host] [-m hostlist] [-l] [-n] [-h] [-c config] [-e]"
+    echo "Traditional call as: $0 CMD [host]...[host]"
+    echo "Getopts call as: $0 [-s host] [-m hostlist] [-k name] [-l] [-n] [-h] [-c config] [-e]"
+    echo "Note that traditional and getopts can be mixed, sometimes."
     echo ""
     echo "Traditional:"
     echo "CMD is one of"
@@ -118,27 +140,64 @@ function usage() {
     echo " ms          Open multi ssh sessions to hosts, synchronizing input"
     echo "             To open a second session to the same set of hosts put a"
     echo "             -n in front of ms"
-    echo " \$anything  Either plain tmux session with name of \$anything or"
+    echo " k           Kill a session. Note that this needs the exact session name"
+    echo "             as shown by tm ls"
+    echo " \$anything   Either plain tmux session with name of \$anything or"
     echo "             session according to TMDIR file"
     echo ""
     echo "Getopts style:"
     echo "-l           List running sessions"
     echo "-s host      Open ssh session to host"
-    echo "-ms hostlist Open multi ssh sessions to hosts, synchronizing input"
+    echo "-m hostlist  Open multi ssh sessions to hosts, synchronizing input"
     echo "             Due to the way getopts works, hostlist must be enclosed in \"\""
     echo "-n           Open a second session to the same set of hosts"
+    echo "-g           Group session - attach to an existing session, but keep seperate"
+    echo "             window control"
+    echo "-k name      Kill a session. Note that this needs the exact session name"
+    echo "             as shown by tm ls"
     echo "-c config    Setup session according to TMDIR file"
     echo "-e SESSION   Use existion session named SESSION"
-    echo ""
+    echo "-r REPLACE   Value to use for replacing in session files"
     echo ""
     echo "TMDIR file:"
-    echo "Each file in \$TMDIR defines a tmux session."
-    echo "Filename is the commandline option \$anything"
-    echo "Content is defined as:"
+    echo "Each file in \$TMDIR defines a tmux session. There are two types of files,"
+    echo "those without an extension and those with the extension \".cfg\" (no \"\")."
+    echo "The filename corresponds to the commandline \$anything (or -c)."
+    echo ""
+    echo "Content of extensionless files is defined as:"
     echo "  First line: Session name"
-    echo "  Second line: extra tmux options"
+    echo "  Second line: extra tmux commandline options"
     echo "  Any following line: A hostname to open a shell with in the normal"
-    echo "  ssh syntax. (ie [user@]hostname"
+    echo "                      ssh syntax. (ie [user@]hostname)"
+    echo ""
+    echo "Content of .cfg files is defined as:"
+    echo "  First line: Session name"
+    echo "  Second line: extra tmux commandline options"
+    echo "  Third line: The new-session command to use. Place NONE here if you want plain"
+    echo "              defaults, though that may mean just a shell. Otherwise put the full"
+    echo "              new-session command with all options you want here."
+    echo "  Any following line: Any tmux command you can find in the tmux manpage."
+    echo "              You should ensure that commands arrive at the right tmux session / window."
+    echo "              To help you with this, there are some variables available which you"
+    echo "              can use, they are replaced with values right before commands are executed:"
+    echo "              SESSION - replaced with the session name"
+    echo "              TMWIN   - see below for explanation of TMWIN Environment variable"
+    echo ""
+    echo "NOTE: Both types of files accept external listings of hostnames."
+    echo "      That is, the output of any shell command given will be used as a list"
+    echo "      of hostnames to connect to (or a set of tmux commands to run)."
+    echo ""
+    echo "NOTE: Session files can include the Token ++TMREPLACETM++ at any point. This"
+    echo "      will be replaced by the value of the -r option (if you use getopts style) or"
+    echo "      by the LAST argument on the line if you use traditional calling."
+    echo "      Note that with traditional calling, the argument will also be tried as a hostname,"
+    echo "      so it may not make much sense there, unless using a session file that contains"
+    echo "      solely of LIST commands."
+    echo ""
+    echo "NOTE: Session files can include any existing environment variable at any point (but"
+    echo "      only one per line). Those get replaced during tm execution time with the actual"
+    echo "      value of the environment variable. Common usage is $HOME, but any existing var"
+    echo "      works fine."
     echo ""
     echo "Environment variables recognized by this script:"
     echo "TMPDIR     - Where tmux stores its session information"
@@ -159,6 +218,12 @@ function usage() {
     echo "             like mine do at 1, then you can set TMWIN to 1"
     echo "TMSESSHOST - Should the hostname appear in session names?"
     echo "             DEFAULT: true"
+    echo "TMSSHCMD   - Allow to globally define a custom ssh command line."
+    echo "             This can be just the command or any option one wishes to have"
+    echo "             everywhere."
+    echo "             DEFAULT: ssh"
+    echo "DEBUG      - Show debug output (remember to redirect it to a file)"
+    echo ""
     exit 42
 }
 
@@ -167,8 +232,10 @@ function usage() {
 function clean_session() {
     local toclean=${*:-""}
 
-    # Neither space nor dot are friends in the SESSION name
+    # Neither space nor dot nor : or " are friends in the SESSION name
     toclean=${toclean// /_}
+    toclean=${toclean//:/_}
+    toclean=${toclean//\"/}
     echo ${toclean//./_}
 }
 
@@ -179,7 +246,7 @@ function ssh_sessname() {
         local one=$1
         # get rid of first argument (s|ms), we don't want to sort this
         shift
-        local sess=$(for i in $*; do echo $i; done | sort | tr '\n' ' ')
+        local sess=$(for i in $@; do echo $i; done | sort | tr '\n' ' ')
         sess="${one} ${sess% *}"
     else
         # no sorting wanted
@@ -188,22 +255,118 @@ function ssh_sessname() {
     clean_session ${sess}
 }
 
+# Setup functions for all tmux commands
+function setup_command_aliases() {
+    local command
+    local SESNAME
+    SESNAME="tmlscm$$"
+    if [[ ${TMUXMAJOR} -lt 2 ]] || [[ ${TMUXMINOR} -lt 2 ]]; then
+        # Starting tmux 2.2, this is no longer needed
+        # Debian Bug #718777 - tmux needs a session to have lscm work
+        tmux new-session -d -s ${SESNAME} -n "check" "sleep 3"
+    fi
+    for command in $(tmux list-commands|awk '{print $1}'); do
+        eval "tm_$command() { tmux $command \"\$@\" >/dev/null; }"
+    done
+    if [[ ${TMUXMAJOR} -lt 2 ]] || [[ ${TMUXMINOR} -lt 2 ]]; then
+        tmux kill-session -t ${SESNAME} || true
+    fi
+}
+
+# Run a command (function) after replacing variables
+function do_cmd() {
+    local cmd=$*
+    cmd1=${cmd%% *}
+    if [[ ${cmd1} =~ ^# ]]; then
+        return
+    elif  [[ ${cmd1} =~ new-window ]]; then
+        TMWIN=$(( TMWIN + 1 ))
+    fi
+
+    cmd=${cmd//SESSION/$SESSION}
+    cmd=${cmd//TMWIN/$TMWIN}
+    cmd=${cmd/$cmd1 /}
+    debug $cmd1 $cmd
+    eval tm_$cmd1 $cmd
+}
+
 # Use a configuration file to setup the tmux parameters/session
 function own_config() {
+    if [[ ${1} =~ .cfg$ ]]; then
+        TMSESCFG="free"
+    fi
     # Set IFS to be NEWLINE only, not also space/tab, as our input files
-    # are \n seperated (one entry per line) and lines may well have spaces
-    IFS="
+    # are \n seperated (one entry per line) and lines may well have spaces.
+    local IFS="
 "
     # Fill an array with our config
-    TMDATA=( $(cat "${TMDIR}/$1") )
+    if [[ -n ${TMDATA[@]:-""} ]] && [[ ${#TMDATA[@]} -gt 0 ]]; then
+        olddata=("${TMDATA[@]}")
+    fi
+
+    TMDATA=( $(sed -e "s/++TMREPLACETM++/${TMREPARG}/g" "${TMDIR}/$1") )
     # Restore IFS
     IFS=${OLDIFS}
 
-    SESSION=$(clean_session ${TMDATA[0]})
+    SESSION=${SESSION:-$(clean_session ${TMDATA[0]})}
 
     if [ "${TMDATA[1]}" != "NONE" ]; then
         TMOPTS=${TMDATA[1]}
     fi
+
+    # Seperate the lines we work with
+    local IFS=""
+    local -a workdata=(${TMDATA[@]:2})
+    IFS=${OLDIFS}
+
+    # Lines (starting with line 3) may start with LIST, then we get
+    # the list of hosts from elsewhere. So if one does, we exec the
+    # command given, then append the output to TMDATA - while deleting
+    # the actual line with LIST in.
+    local TMPDATA=$(mktemp -u -p ${TMPDIR} .tmux_tm_XXXXXXXXXX)
+    trap "rm -f ${TMPDATA}" EXIT ERR HUP INT QUIT TERM
+    local index=0
+    while [[ ${index} -lt ${#workdata[@]} ]]; do
+        if [[ "${workdata[${index}]}" =~ ^LIST\ (.*)$ ]]; then
+            # printf -- 'workdata: %s\n' "${workdata[@]}"
+            local cmd=${BASH_REMATCH[1]}
+            if [[ ${cmd} =~ \$\{([0-9a-zA-Z_]+)\} ]]; then
+                repvar=${BASH_REMATCH[1]}
+                reptext=${!repvar}
+                cmd=${cmd//\$\{$repvar\}/$reptext}
+            fi
+            echo "Line ${index}: Fetching hostnames using provided shell command '${cmd}', please stand by..."
+
+            $( ${cmd} >| "${TMPDATA}" )
+            # Set IFS to be NEWLINE only, not also space/tab, the list may have ssh options
+            # and what not, so \n is our seperator, not more.
+            IFS="
+"
+            out=( $(tr -d '\r' < "${TMPDATA}" ) )
+
+            # Restore IFS
+            IFS=${OLDIFS}
+
+            workdata+=( "${out[@]}" )
+            unset workdata[${index}]
+            unset out
+            # printf -- 'workdata: %s\n' "${workdata[@]}"
+        elif [[ "${workdata[${index}]}" =~ ^SSHCMD\ (.*)$ ]]; then
+            TMSSHCMD=${BASH_REMATCH[1]}
+        fi
+        index=$(( index + 1 ))
+    done
+    rm -f "${TMPDATA}"
+    trap - EXIT ERR HUP INT QUIT TERM
+    debug "TMDATA: ${TMDATA[@]}"
+    debug "olddata: ${olddata[@]:-''}"
+    if [[ -n ${olddata[@]:-""} ]]; then
+        TMDATA=( "${olddata[@]}" "${workdata[@]}" )
+    else
+        TMDATA=( "${TMDATA[@]:0:2}" "${workdata[@]}"  )
+    fi
+    declare -r TMDATA=( "${TMDATA[@]}" )
+    debug "TMDATA now ${TMDATA[@]}"
 }
 
 # Simple overview of running sessions
@@ -216,44 +379,73 @@ function list_sessions() {
     fi
 }
 
+# We either have a debug function that shows output, or one that
+# plainly returns
+if [[ ${DEBUG} == true ]]; then
+        eval "debug() { >&2 echo \$* ; }"
+else
+        eval "debug() { return ; }"
+fi
+
+setup_command_aliases
+
 ########################################################################
 # MAIN work follows here
-
 # Check the first cmdline parameter, we might want to prepare something
 case ${cmdline} in
     ls)
         list_sessions
         exit 0
         ;;
-    s|ms)
-        # Yay, we want ssh to a remote host - or even a multi session setup
+    s|ms|k)
+        # Yay, we want ssh to a remote host - or even a multi session setup - or kill one
         # So we have to prepare our session name to fit in what tmux (and shell)
         # allow us to have. And so that we can reopen an existing session, if called
         # with the same hosts again.
         SESSION=$(ssh_sessname $@)
+        declare -r cmdline
+        shift
         ;;
     -*)
-        while getopts "lnhs:m:c:e:" OPTION; do
-            case $OPTION in
+        while getopts "lnhs:m:c:e:r:k:g:" OPTION; do
+            case ${OPTION} in
                 l) # ls
                     list_sessions
                     exit 0
                     ;;
                 s) # ssh
                     SESSION=$(ssh_sessname s ${OPTARG})
+                    declare -r cmdline=s
+                    shift
+                    ;;
+                k) # kill session
+                    SESSION=$(ssh_sessname s ${OPTARG})
+                    declare -r cmdline=k
+                    shift
                     ;;
                 m) # ms (needs hostnames in "")
                     SESSION=$(ssh_sessname ms ${OPTARG})
+                    declare -r cmdline=ms
+                    shift
                     ;;
                 c) # pre-defined config
                     own_config ${OPTARG}
                     ;;
                 e) # existing session name
                     SESSION=$(clean_session ${OPTARG})
+                    shift
                     ;;
                 n) # new session even if same name one already exists
                     DOUBLENAME=true
                     ;;
+                r) # replacement arg
+                    TMREPARG=${OPTARG}
+                    ;;
+                g) # Group session, not simple attach
+                    declare -r GROUPSESSION=true
+                    SESSION=$(clean_session ${OPTARG})
+                    shift
+                    ;;
                 h)
                     usage
                     ;;
@@ -274,62 +466,72 @@ case ${cmdline} in
             else
                 usage
             fi
-        elif [ -r "${TMDIR}/$1" ]; then
-            own_config $1
+        elif [ -r "${TMDIR}/${cmdline}" ]; then
+            for arg in "$@"; do
+                own_config ${arg}
+            done
         else
             # Not a config file, so just session name.
-            TMDATA=""
             SESSION=${cmdline}
         fi
         ;;
 esac
 
+havesession="false"
+if tmux has-session -t ${SESSION} 2>/dev/null; then
+    havesession="true"
+fi
+declare -r havesession
+
 # And now check if we would end up with a doubled session name.
 # If so add something "random" to the new name, like our pid.
-if [[ ${DOUBLENAME} == true ]] && tmux has-session -t ${SESSION} 2>/dev/null; then
+if [[ ${DOUBLENAME} == true ]] && [[ ${havesession} == true ]]; then
     # Session exist but we are asked to open another one,
     # so adjust our session name
-    if [[ ${SESSION} =~ ([ms]+)_(.*) ]]; then
+    if [[ ${#TMDATA} -eq 0 ]] && [[ ${SESSION} =~ ([ms]+)_(.*) ]]; then
         SESSION="${BASH_REMATCH[1]}_$$_${BASH_REMATCH[2]}"
+    else
+        SESSION="$$_${SESSION}"
     fi
 fi
 
 if [[ ${TMSESSHOST} = true ]]; then
-    declare -r SESSION="$(hostname -s)_${SESSION}"
+    declare -r SESSION="$(uname -n|cut -d. -f1)_${SESSION}"
 else
     declare -r SESSION
 fi
 
 # We only do special work if the SESSION does not already exist.
-if ! tmux has-session -t ${SESSION} 2>/dev/null; then
+if [[ ${cmdline} != k ]] && [[ ${havesession} == false ]]; then
     # In case we want some extra things...
     # Check stupid users
     if [ $# -lt 1 ]; then
         usage
     fi
+    tm_pane_error="create pane failed: pane too small"
     case ${cmdline} in
         s)
             # The user wants to open ssh to one or more hosts
-            tmux new-session -d -s ${SESSION} -n "${1}" "ssh ${1}"
+            do_cmd new-session -d -s ${SESSION} -n "${1}" "'${TMSSHCMD} ${1}'"
             # We disable any automated renaming, as that lets tmux set
             # the pane title to the process running in the pane. Which
             # means you can end up with tons of "bash". With this
             # disabled you will have panes named after the host.
-            tmux set-window-option -t ${SESSION} automatic-rename off >/dev/null
+            do_cmd set-window-option -t ${SESSION} automatic-rename off >/dev/null
             # If we have at least tmux 1.7, allow-rename works, such also disabling
             # any rename based on shell escape codes.
-            if [ ${TMUXMINOR} -ge 7 ] || [ ${TMUXMAJOR} -gt 1 ]; then
-                tmux set-window-option -t ${SESSION} allow-rename off >/dev/null
+            if [ ${TMUXMINOR//[!0-9]/} -ge 7 ] || [ ${TMUXMAJOR//[!0-9]/} -gt 1 ]; then
+                do_cmd set-window-option -t ${SESSION} allow-rename off >/dev/null
             fi
             shift
             count=2
             while [ $# -gt 0 ]; do
-                tmux new-window -d -t ${SESSION}:${count} -n "${1}" "ssh ${1}"
-                tmux set-window-option -t ${SESSION}:${count} automatic-rename off >/dev/null
+                do_cmd new-window -d -t ${SESSION}:${count} -n "${1}" "${TMSSHCMD} ${1}"
+                do_cmd set-window-option -t ${SESSION}:${count} automatic-rename off >/dev/null
                 # If we have at least tmux 1.7, allow-rename works, such also disabling
                 # any rename based on shell escape codes.
-                if [ ${TMUXMINOR} -ge 7 ] || [ ${TMUXMAJOR} -gt 1 ]; then
-                    tmux set-window-option -t ${SESSION}:${count} allow-rename off >/dev/null
+                if [ ${TMUXMINOR//[!0-9]/} -ge 7 ] || [ ${TMUXMAJOR//[!0-9]/} -gt 1 ]; then
+                    do_cmd set-window-option -t ${SESSION}:${count} allow-rename off >/dev/null
                 fi
                 count=$(( count + 1 ))
                 shift
@@ -339,54 +541,93 @@ if ! tmux has-session -t ${SESSION} 2>/dev/null; then
             # We open a multisession window. That is, we tile the window as many times
             # as we have hosts, display them all and have the user input send to all
             # of them at once.
-            tmux new-session -d -s ${SESSION} -n "Multisession" "ssh ${1}"
+            do_cmd new-session -d -s ${SESSION} -n "Multisession" "'${TMSSHCMD} ${1}'"
             shift
             while [ $# -gt 0 ]; do
-                tmux split-window -d -t ${SESSION}:${TMWIN} "ssh ${1}"
-                # Always have tmux redo the layout, so all windows are evenly sized.
-                # Otherwise you quickly end up with tmux telling you there is no
-                # more space available for tiling - and also very different amount
-                # of visible space per host.
-                tmux select-layout -t ${SESSION}:${TMWIN} main-horizontal >/dev/null
+                set +e
+                output=$(do_cmd split-window -d -t ${SESSION}:${TMWIN} "'${TMSSHCMD} ${1}'" 2>&1)
+                ret=$?
+                set -e
+                if [[ ${ret} -ne 0 ]] && [[ ${output} == ${tm_pane_error} ]]; then
+                    # No more space -> have tmux redo the
+                    # layout, so all windows are evenly sized.
+                    do_cmd select-layout -t ${SESSION}:${TMWIN} main-horizontal >/dev/null
+                    # And dont shift parameter away
+                    continue
+                fi
                 shift
             done
             # Now synchronize them
-            tmux set-window-option -t ${SESSION}:${TMWIN} synchronize-pane >/dev/null
+            do_cmd set-window-option -t ${SESSION}:${TMWIN} synchronize-pane >/dev/null
             # And set a final layout which ensures they all have about the same size
-            tmux select-layout -t ${SESSION}:${TMWIN} tiled >/dev/null
+            do_cmd select-layout -t ${SESSION}:${TMWIN} tiled >/dev/null
             ;;
         *)
             # Whatever string, so either a plain session or something from our tmux.d
             if [ -z "${TMDATA}" ]; then
                 # the easy case, just a plain session name
-                tmux new-session -d -s ${SESSION}
+                do_cmd new-session -d -s ${SESSION}
             else
                 # data in our data array, the user wants his own config
+                if [[ ${TMSESCFG} = free ]]; then
+                    if [[ ${TMDATA[2]} = NONE ]]; then
+                        # We have a free form config where we get the actual tmux commands
+                        # supplied by the user, so just issue them after creating the session.
+                        do_cmd new-session -d -s ${SESSION} -n "'${TMDATA[0]}'"
+                    else
+                        do_cmd ${TMDATA[2]}
+                    fi
+                    tmcount=${#TMDATA[@]}
+                    index=3
+                    while [ ${index} -lt ${tmcount} ]; do
+                        do_cmd ${TMDATA[$index]}
+                        (( index++ ))
+                    done
+                else
+                    # So lets start with the "first" line, before dropping into a loop
+                    do_cmd new-session -d -s ${SESSION} -n "${TMDATA[0]}" "'${TMSSHCMD} ${TMDATA[2]}'"
 
-                # So lets start with the "first" line, before dropping into a loop
-                tmux new-session -d -s ${SESSION} -n "${TMDATA[0]}" "ssh ${TMDATA[2]}"
-
-                tmcount=${#TMDATA[@]}
-                index=3
-                while [ ${index} -lt ${tmcount} ]; do
-                    tmux split-window -d -t ${SESSION}:${TMWIN} "ssh ${TMDATA[$index]}"
-                    # Always have tmux redo the layout, so all windows are evenly sized.
-                    # Otherwise you quickly end up with tmux telling you there is no
-                    # more space available for tiling - and also very different amount
-                    # of visible space per host.
-                    tmux select-layout -t ${SESSION}:${TMWIN} main-horizontal >/dev/null
-                    (( index++ ))
-                done
-                # Now synchronize them
-                tmux set-window-option -t ${SESSION}:${TMWIN} synchronize-pane >/dev/null
-                # And set a final layout which ensures they all have about the same size
-                tmux select-layout -t ${SESSION}:${TMWIN} tiled >/dev/null
+                    tmcount=${#TMDATA[@]}
+                    index=3
+                    while [ ${index} -lt ${tmcount} ]; do
+                        # List of hostnames, open a new connection per line
+                        set +e
+                        output=$(do_cmd split-window -d -t ${SESSION}:${TMWIN} "'${TMSSHCMD} ${TMDATA[$index]}'" 2>&1)
+                        set -e
+                        if [[ ${output} =~ ${tm_pane_error} ]]; then
+                            # No more space -> have tmux redo the
+                            # layout, so all windows are evenly sized.
+                            do_cmd select-layout -t ${SESSION}:${TMWIN} main-horizontal >/dev/null
+                            # And again, don't increase index
+                            continue
+                        fi
+                        (( index++ ))
+                    done
+                    # Now synchronize them
+                    do_cmd set-window-option -t ${SESSION}:${TMWIN} synchronize-pane >/dev/null
+                    # And set a final layout which ensures they all have about the same size
+                    do_cmd select-layout -t ${SESSION}:${TMWIN} tiled >/dev/null
+                fi
             fi
             ;;
     esac
     # Build up new session, ensure we start in the first window
-    tmux select-window -t ${SESSION}:${TMWIN}
+    do_cmd select-window -t ${SESSION}:${TMWIN}
+elif [[ ${cmdline} == k ]]; then
+    # So we are asked to kill a session
+    tokill=${SESSION//k_/}
+    do_cmd kill-session -t ${tokill}
+    exit 0
 fi
 
-# And last, but not least, attach to it
-tmux ${TMOPTS} attach -t ${SESSION}
+# If we should group our session or not
+if [[ ${GROUPSESSION} == true ]]; then
+    # Grouping means opening a new session, but sharing the sessions with
+    # another session (existing and new windows). But window control is separate.
+    sesname="$$_${SESSION}"
+    tmux ${TMOPTS} new-session -s ${sesname} -t ${SESSION}
+    tmux ${TMOPTS} kill-session -t ${sesname}
+else
+    # Do not group, just attach
+    tmux ${TMOPTS} attach -t ${SESSION}
+fi