Create or attach Byobu default windows - tmux

I run Byobu (backed by Tmux) on a remote machine. I have a basic window layout which is what I generally want, and I've configured Byobu to launch at login so when I ssh to the remote machine I resume my session and when I want to leave I F6 to detach and disconnect. This works well.
My problem is when I reboot the machine all my Byobu windows are erased. I'm not trying (yet?) to resume exactly where I left off, but just getting all the windows with their names would be a big help.
I can use .byobu/window.tmux to have a window layout that's executed when I connect. But I've tried this
new-session 'remote';
new-window -n irc;
new-window -n code;
new-window -n logs;
and that causes Byobu to create a new session each time I connect, so I can't resume anymore.
Then I tried adding the -A flag to attach:
new-session -A 'remote';
new-window -n irc;
new-window -n code;
new-window -n logs;
This goes back to resuming the old session, but now every time I connect I get an additional three windows added to the windows I had before.
Here's pseudo-code for what I want:
if session_exists('remote') {
attach('remote');
} else {
new-session 'remote';
new-window -n irc;
new-window -n code;
new-window -n logs;
}
Can I do this?

I found this article where the guy explains how to create one main, unattached session and then create a shared session from that one.
This results in the behavior you're aiming for, I believe.
Check out this article:
byobu-tmux tabbed windows

Related

Can tmux query physical terminal? (Maybe iTerm2 only)

I'm trying to detect the presence of iTerm2 and if I run the following in iTerm2 (echo -n $'\e[5n'; read -s -t 0.1 line; printf '%q\n' "$line") the terminal responds with $'\033'\[ITERM2\ 3.2.1n$'\033'\[0n
However, if I am running a tmux session in the terminal, then tmux responds and gives me nothing.
Any idea how I can ask tmux to query the physical terminal to report its status?
Footnotes
Here is a description of [5n in the tmux source: https://github.com/tmux/tmux/blob/486ce9b09855ae30a2bf5e576cb6f7ad37792699/tools/ansicode.txt#L577
This might be iTerm2 only, since I haven't seen a response on any other terminal
According to ft in freenode's #tmux (and as seen in this Super User answer), you can use:
'\ePtmux;\e" STUFF_FOR_THE_TERMINAL_HERE "\e\\'
So, it would be something like:
echo -n $'\ePtmux;\e\e[5n\e\\'

Byobu unable to attach to running tmux

I´m a happy user of byobu, but recently I noted that I cannot attach anymore to open sessions.
With ps aux | grep tmux I can clearly see many tmux processes, but unfortunatly, when I try to attach with:
tmux attach
byobu attach
I get no results but a no session error. Moreover, with byobu-select-session I got a failed to connect to server instead.
There is a commant to connect tmux to a given socket, which I found using
lsof -U | grep '^tmux'. But still no session attached. My session files are in /tmp/user/tmux-1000/default, but I can see some sockets being used.
From ps aux I can see that byobu launches tmux with: tmux -2 -f /usr/share/byobu/profiles/tmuxrc new-session -n - /usr/bin/byobu-shell
Unfortunately, either with byobu -S path or byobu -L socketname I am not able to attach to previously open session, and byobu simply start a new session.
I run into a similar situation caused by accidentally removing the tmux socket in /tmp. The method described here solved the problem for me (either killall -SIGUSR1 tmux or kill -USR1 $PID_FOR_RUNNING_TMUX).

Different starting directory per window?

I daily use tmux (2.5) on my laptop to work, and my tmux sessions have a starting directory which is the working directory I started the tmux session from. Every pane/window I open start with this starting directory as working directory.
I can change this starting directory, and this change would apply to the whole session.
But if I want to work on a different project with several panes, I could start a new window, but every pane I would open in it would start with the session's starting directory : I would have to cd to the new location for each pane which isn't practical.
If I need to work on several project/directories simultaneously, I can start a new terminal session, then cd to the relevant directory/project and start a new tmux session. That's not complicated.
But if I want to do the same thing on a server through ssh, I'd need to either :
open a new ssh session.
either embed my remote tmux sessions in an other tmux session.
Neither sounds practical to me, I'd prefer a single tmux session on the remote machine.
I think it would be more convenient to being able to start new window with its own starting directory location that would apply to any new pane opened in it. Is there a way to achieve this?
Edit :
I already tried the -c parameter of tmux new-window command.
But it doesn't assign its starting directory to the window created this way, it only applies this custom starting directory to the first pane created.
Any new pane opened in this window then uses the session's starting directory as default working dir (and not the path passed to tmux new-window).
This question is very similar to: https://unix.stackexchange.com/questions/12032/create-new-window-with-current-directory-in-tmux
It depends on your tmux version but the -c parameter does do the trick but it does not remember the setting. There used to be a default-path setting but that has been removed in version 1.9 unfortunately.
For newer versions you will need to pass along the -c in all cases (you can use an alias if you manually execute that command) or if you use key bindings you need to rebind the split/new window keys.
bind '"' split-window -c "#{pane_current_path}"
bind % split-window -h -c "#{pane_current_path}"
bind c new-window -c "#{pane_current_path}"
To use a custom path instead of the current pane path, execute this command:
tmux setenv custom_path /home/whatever/some/path
Put this in your config:
bind '"' split-window -c "#{custom_path}"
bind % split-window -h -c "#{custom_path}"
bind c new-window -c "#{custom_path}"
Yes, it turns out the -c option to the new-window command is what you are looking for: https://unix.stackexchange.com/questions/12032/create-new-window-with-current-directory-in-tmux Also, this: https://unix.stackexchange.com/questions/101949/new-tmux-panes-go-to-the-same-directory-as-the-current-pane-new-tmux-windows-go
So either of tmux new-window -c $(pwd) or tmux new-window -c /path/to/dir inside your tmux session should do it.

How to auto-update SSH agent environment variables when attaching to existing tmux sessions?

I am trying to find a nice way to restore the SSH agent when I reconnect a disconnected tmux session.
The cause seems to be that the SSH agent session changes but the environment variable from the tmux session is not updated.
How can I automate this, before attaching the session itself? Because the session I am attaching to does not always have a bash prompt, so I cannot afford to type something inside it. It has to be something to run before creating or attaching the tmux session.
An example of the code I'm running is at https://gist.github.com/ssbarnea/8646491 -- a small ssh wrapper that is using tmux to create persistem ssh connections. This works quite well, but sometimes the ssh agent stops working so I am no longer able to use it to connect to other hosts.
There's an excellent gist by Martijn Vermaat, which addresses your problem in great depth, although it is intended for screen users, so I'm adjusting it for tmux here.
To summarize:
create ~/.ssh/rc if it doesn't exist yet, and add the following content:
#!/bin/bash
# Fix SSH auth socket location so agent forwarding works with tmux.
if test "$SSH_AUTH_SOCK" ; then
ln -sf $SSH_AUTH_SOCK ~/.ssh/ssh_auth_sock
fi
Make it work in tmux, add this to your ~/.tmux.conf:
# fix ssh agent when tmux is detached
setenv -g SSH_AUTH_SOCK $HOME/.ssh/ssh_auth_sock
Extra work is required if you want to enable X11 forwarding, see the gist.
While tmux updates SSH variables by default, there is no need to
change/add socket path
change the SSH_AUTH_SOCKET variable
I like the solution by Chris Down which I changed to add function
fixssh() {
eval $(tmux show-env \
|sed -n 's/^\(SSH_[^=]*\)=\(.*\)/export \1="\2"/p')
}
into ~/.bashrc. Call fixssh after attaching session or before ssh/scp/rsync.
Newer versions of tmux support -s option for show-env, so only
eval $(tmux show-env -s |grep '^SSH_')
is possible.
Here's what I use for updating SSH_AUTH_SOCK inside a tmux window (based on Hans Ginzel's script):
alias fixssh='eval $(tmux showenv -s SSH_AUTH_SOCK)'
Or for tmux that does not have showenv -s:
alias fixssh='export $(tmux showenv SSH_AUTH_SOCK)'
Here is my solution which includes both approaches, and does not require extra typing when I reconnect to tmux session
alias ssh='[ -n "$TMUX" ] && eval $(tmux showenv -s SSH_AUTH_SOCK); /usr/bin/ssh'
There are lots of good answers here. But there are cases where tmux show-environment doesn't see SSH_AUTH_SOCK. In that case you can use find to locate it explicitly.
export SSH_AUTH_SOCK=$(find /tmp -path '*/ssh-*' -name 'agent*' -uid $(id -u) 2>/dev/null | tail -n1)
That's long and complicated, so I'll break it down...
01 export SSH_AUTH_SOCK=$(
02 find /tmp \
03 -path '*/ssh-*'
04 -name 'agent*'
05 -uid $(id -u)
06 2>/dev/null
07 | tail -n1
08 )
export the SSH_AUTH_SOCK environment variable set to the output of the $() command substitution
find files starting in /tmp
limit results to only those with /ssh- in the path
limit results to only those whose name begins with agent
limit results to only those with a user id matching the current user
silence all (permissions, etc.) errors
take only the last result if there are multiple
You may be able to leave off 6 & 7 if you know that there will only be 1 result and you don't care about stderr garbage.
I use a variation of the previous answers:
eval "export $(tmux show-environment -g SSH_AUTH_SOCK)"
assuming that you did the ssh agent started from the outer environment. Same goes for other environment variables such as DISPLAY.
I prefer to avoid configuring TMUX (etc) and keep everything purely in ~/.ssh/. On the remote system:
Create ~/.ssh/rc:
#!/bin/bash
# Fix SSH auth socket location so agent forwarding works within tmux
if test "$SSH_AUTH_SOCK" ; then
ln -sf $SSH_AUTH_SOCK ~/.ssh/ssh_auth_sock
fi
Add following to ~/.ssh/config so it no longer relies on $SSH_AUTH_SOCK, which goes stale in detached terminals:
Host *
IdentityAgent ~/.ssh/ssh_auth_sock
Known limitations
ssh-add doesn't use ~/.ssh/config and so cannot communicate with ssh-agent. Commands like ssh-add -l produce errors, even though ssh user#host works fine, as does updating git remotes which are accessed via SSH.
I may have worked out a solution that is fully encapsulated in the ~/.tmux.conf configuration file. It is a different approach than modifying the ~/.bash_profile and ~/.ssh/rc.
Solution only using ~/.tmux.conf
Just cut and paste the following code into your ~/.tmux.conf
# ~/.tmux.conf
# SSH agent forwarding
#
# Ensure that SSH-Agent forwarding will work when re-attaching to the tmux
# session from a different SSH connection (after a dropped connection).
# This code will run upon tmux create, tmux attach, or config reload.
#
# If there is an SSH_AUTH_SOCK originally defined:
# 1) Remove all SSH related env var names from update-environment.
# Without this, setenv cannot override variables such as SSH_AUTH_SOCK.
# Verify update-environment with: tmux show-option -g update-environment
# 2) Force-set SSH_AUTH_SOCK to be a known location
# /tmp/ssh_auth_sock_tmux
# 3) Force-create a link of the first found ssh-agent socket at the known location
if-shell '[ -n $SSH_AUTH_SOCK ]' " \
set-option -sg update-environment \"DISPLAY WINDOWID XAUTHORITY\"; \
setenv -g SSH_AUTH_SOCK /tmp/ssh_auth_sock_tmux; \
run-shell \"ln -sf $(find /tmp/ssh-* -type s -readable | head -n 1) /tmp/ssh_auth_sock_tmux\" \
"
Caveat
The above solution along with the other solutions are susceptible to a race condition when initiating multiple connections to the same machine. Consider this:
Client 1 Connect: SSH to machineX, start/attach tmux (writes ssh_auth_sock link)
Client 2 Connect: SSH to machineX, start/attach tmux (overwrites ssh_auth_sock link)
Client 2 Disconnect: Client 1 is left with a stale ssh_auth_sock link, thus breaking ssh-agent
However, this solution is slightly more resilient because it only overwrites the ssh_auth_sock link upon tmux start/attach, instead of upon initialization of a bash shell ~/.bash_profile or ssh connection ~/.ssh/rc
To cover this last race condition, one may add a key binding to reload the tmux configuration with a (Ctrl-b r) key sequence.
# ~/.tmux.conf
# reload config file
bind r source-file ~/.tmux.conf
From within an active tmux session, executing this sequence when the ssh_auth_sock link goes stale will refresh the ssh-agent connection.
In case other fish shell users are wondering how to deal with this when using fish (as well as for my future self!). In my fish_prompt I added a call to the following function:
function _update_tmux_ssh
if set -q TMUX
eval (tmux show-environment SSH_AUTH_SOCK | sed 's/\=/ /' | sed 's/^/set /')
end
end
I suppose that more advanced *nix users would know how to replace sed with something better, but this works (tmux 3.0, fish 3.1).
Following up on #pymkin's answer above, add the following, which worked with tmux 3.2a on macOS 11.5.3:
To ~/.tmux.conf:
# first, unset update-environment[SSH_AUTH_SOCK] (idx 3), to prevent
# the client overriding the global value
set-option -g -u update-environment[3]
# And set the global value to our static symlink'd path:
set-environment -g SSH_AUTH_SOCK $HOME/.ssh/ssh_auth_sock
To ~/.ssh/rc:
#!/bin/sh
# On SSH connection, create stable auth socket path for Tmux usage
if test "$SSH_AUTH_SOCK"; then
ln -sf "$SSH_AUTH_SOCK" ~/.ssh/ssh_auth_sock
fi
What's going on? Tmux has the semi-helpful update-environment variable/feature to pick up certain environment variables when a client connects. I.e. when you do tmux new or tmux attach, it'll update the tmux environment from when you ran those commands. That's nice for new shells or commands you run inside tmux afterwards, but it doesn't help those shells you've started prior to the latest attach. To solve this, you could use some of the other answers here to have existing shells pick up this updated environment, but that's not the route I chose.
Instead, we're setting a static value for SSH_AUTH_SOCK inside tmux, which will be ~/.ssh/ssh_auth_sock. All shells inside tmux would pick that up, and never have to be updated later. Then, we configure ssh so that, upon connection, it updates that static path with a symlink to the latest real socket that ssh knows.
The missing piece from #pymkin's answer is that Tmux will have the session value override the global value, so doing set-environment -g isn't sufficient; it gets squashed whenever you re-attach. You also have to also tell tmux not to update SSH_AUTH_SOCK in the session environment, so that the global value can make it through. That's what the set-option -g -u is about.
After coming across so many suggestions, I finally figured out a solution that enables TMUX update the stale ssh agent after being attached. Basically, both the zshrc files on the local and remote machines need to be modified.
Insert the following codes into the local zshrc, which is based on this reference.
export SSH_AUTH_SOCK=~/.ssh/ssh-agent.$(hostname).sock
ssh-add -l 2>/dev/null >/dev/null
# The error of executing ssh-add command denotes a valid agent does not
# exist.
if [ $? -ge 1 ]; then
# remove the socket if it exists
if [ -S "${SSH_AUTH_SOCK}" ]; then
rm "${SSH_AUTH_SOCK}"
fi
ssh-agent -a "${SSH_AUTH_SOCK}" >/dev/null
# one week life time
ssh-add -t 1W path-to-private-rsa-file
fi
Insert the following code into the remote zshrc, where the tmux session will be attached.
alias fixssh='eval $(tmux showenv -s SSH_AUTH_SOCK)'
Then ssh into the remote machine. The -A option is necessary.
ssh -A username#hostname
Attach the TMUX session. Check the TMUX evironment variables
# run this command in the shell
tmux showenv -s
# or run this command after prefix CTRL+A or CTRL+B
:show-environment
Run fixssh in the previously existed panes to update the ssh agent. If a new pane is created, it will automatically get the new ssh-agent.
Here's another simple Bash solution, using PROMPT_COMMAND to update the SSH_* vars inside tmux before each prompt is generated. The downside to this solution is that it doesn't take effect in existing shells until a new prompt is generated, because PROMPT_COMMAND is only run before creating new prompts.
Just add this to your ~/.bashrc:
update_tmux_env () {
# Only run for shells inside a tmux session.
if [[ -n "$TMUX" ]]; then
eval $(tmux show-env -s | grep '^SSH_')
fi
}
export PROMPT_COMMAND=update_tmux_env
Here's a new fix to an old problem: I think it's simpler than the other fixes and there's no need to make a static socket or mess with the shell prompt or make a separate command you have to remember to run.
I added this code added to my .bashrc file:
if [[ -n $TMUX ]]; then
_fix_ssh_agent_in_tmux () { if [[ ! -S $SSH_AUTH_SOCK ]]; then eval export $(tmux show-env | grep SSH_AUTH_SOCK); fi }
ssh () { _fix_ssh_agent_in_tmux; command ssh $#; }
scp () { _fix_ssh_agent_in_tmux; command scp $#; }
git () { _fix_ssh_agent_in_tmux; command git $#; }
rsync () { _fix_ssh_agent_in_tmux; command rsync $#; }
fi
If the shell is running within tmux, it redefines 'ssh' and its ilk to bash functions which test and fix SSH_AUTH_SOCK before actually running the real commands.
Note that tmux show-env -g also returns a value for SSH_AUTH_SOCK but that one is stale, I assume it's from whenever the tmux server started. The command above queries the current tmux session's environment which seems to be correct.
I'm using tmux 2.6 (ships with with Ubuntu 18.04) and it seems to work well.

How to create new tmux session if none exists

I am trying to figure out how to attach to a tmux session if a named tmux session exists, if not I want to create a new one with the given name.
Currently, I know of a few tmux commands which can partly achieve what I am looking for, but its not clear how to combine them together to get what I am looking for:
tmux attach attaches to an automatically existing session - but errors out if no session exists
tmux new creates a new session - but it does so every time, so I can't leave it in my .tmux.conf
tmux has-session tests whether a session exists - but I don't know how to stitch it together with the other commands
Thus, I would like to create a tmux script, so that this happens automatically, instead of having to manually create it everytime I need to log into a sessions.
How can I write a automatic script so as to create a new tmux session (if a given session name doesnt exist) or attach to a session name (if it exists)?
I figured it out (and had it pointed out to me).
tmux attach || tmux new
Alternately, you can add
new-session
to your .tmux.conf - that will create a default session on server start.
Then tmux attach will either attach to the current session (running server, that is), or create a new session (start the server, read the config file, issue the new-session command) and attach to that.
As pointed out in comments from Petr Viktorin, jkoelker and pjincz, you can use the following command to attach to mySession if it exists, and to create it if it doesn't:
tmux new -A -s mySession
From man tmux:
new-session[-AdDEP] [-cstart-directory] [-Fformat] [-nwindow-name] [-ssession-name] [-tgroup-name] [-xwidth] [-yheight] [shell-command]
(alias: new)
Create a new session with name session-name.
[...]
The -A flag makes new-session behave like attach-session if session-name already exists; in this case, -D behaves like -d to attach-session.
new-session has supported -A since tmux-1.8.
Adapting Alex's suggestion to include project based configuration upon startup, I started using the following:
# ~/bin/tmux-myproject shell script
# The Project name is also used as a session name (usually shorter)
PROJECT_NAME="myproject"
PROJECT_DIR="~/myproject"
tmux has-session -t $PROJECT_NAME 2>/dev/null
if [ "$?" -eq 1 ] ; then
echo "No Session found. Creating and configuring."
pushd $PROJECT_DIR
tmux new-session -d -s $PROJECT_NAME
tmux source-file ~/bin/tmux-${PROJECT_NAME}.conf
popd
else
echo "Session found. Connecting."
fi
tmux attach-session -t $PROJECT_NAME
where tmux-myproject.conf is my startup series of tmux commands to create my windows and panes, as well as start my editors.
Although I find rampion's answer is sufficient for using 1 session, this script lets you setup multiple sessions:
SESSIONS="work play"
function has-session {
tmux has-session -t $1 2>/dev/null
}
function except
{
if [ "$?" -eq 1 ] ; then
$1
fi
}
# Configure your sessions here
function session-work
{
tmux new-session -d -s work
tmux neww -k -t work:1
}
function session-play
{
tmux new-session -d -s play
tmux neww -k -t play:1
}
#
#MAIN
for x in $SESSIONS
do
echo $x
has-session $x
except session-$x
done
NOTE:
-k --> new-window will not be created if already exists
-d --> start session or window, but don't attach to it yet
-s --> name the session
-t --> specify a target location in the form session:window.pane
I use an alias to create a new session if needed, and attach to my default session if it already exists:
alias tmuxre='tmux new-session -t default || tmux new-session -s default'
I added this to my .login on my server.
The reason I do it this way is because I don't want to attach to the same actual session, I want a new session which uses the same group of windows.
This is also similar to running screen -xRR.
For those who want to do the same thing in fish:
tmux attach -t mysesh; or tmux new -s mysesh
(edit: A solution considering named sessions is mentioned at the end of this answer)
I came across this question when I was looking for a particular use-case, but couldn't find any solutions to it, so I'll add mine here:
Upon terminal-launch tmux should:
check whether there are any unattached sessions and use the first it can find (each session will be attached only once)
if there are no unattached sessions create a new one
After reading through the tmux man-pages and looking up arrays in bash I was able to come up with the following one-liner:
tmux attach -t ${$(tmux list-sessions -F '#{session_name}' -f '#{==:#{session_attached},0}')[1]} || tmux new
Explanation:
tmux attach -t $A:
attach to session with content of variable A (in our case the return value of the list-session command + array-index call)
tmux new:
create new session
together -> tmux attach -t $A || tmux new:
if tmux attach fails, create a new session
The next part (our $A) is finding an unattached session:
A = ${$B[1]}: return the second element in the list B (first one seems to always be an empty string)
B = tmux list-sessions -F '#{session_name}' -f '#{==:#{session_attached},0}'
tmux list-sessions: list all sessions
tmux list-sessions -F '#{session_name}: -F stands for format and -F '#{session_name}' tells tmux to only show the name of the session and nothing else when it outputs the list
tmux list-sessions -f '#{==:#{session_attached},0}': -f stands for filter and -f '#{==:#{session_attached},0}'tells tmux to show only those list-elements, where the session_attached-value is equal to 0
tmux list-sessions -F '#{session_name}' -f '#{==:#{session_attached},0}': both flags in combination will output only the session name and only for those elements of the list, where the session_attached-value is equal to 0 (=unattached sessions)
Example:
My application was for WSL, so I added it to the launch of my Ubuntu profile inside the settings.json of Windows Terminal:
"commandline": "C:\\Windows\\system32\\wsl.exe -d Ubuntu-22.04 tmux attach -t ${$(tmux list-sessions -F '#{session_name}' -f '#{==:#{session_attached},0}')[1]} || tmux new",
Edit:
If you have ([a-zA-Z]) named sessions you can sort the list to put those at the beginning:
tmux attach -t ${$(sort -n <<<"${$(tmux list-sessions -F '#{session_name}' -f '#{==:#{session_attached},0}')[*]}")[1]} || tmux new
If you add tmux new -s "primary" || at the beginning of the previous command it will try to create the session with the name "primary" and if it already exists it will only attach to it if it is still unattached, otherwise it will take another unattached session (prioritizing named over unnamed) or just create a new unnamed session if there are no unattached sessions left.
Caveat: each time you run this command and "primary" already exists it will output an error-message that it wasn't able to create a session called "primary" (only visible for a split second)
edit edit:
you can redirect those messages by using &> /dev/null:
(tmux new -s primary || tmux new -s secondary || tmux new -s tertiary) &> /dev/null || tmux attach -t ${$(sort -n <<<"${$(tmux list-sessions -F '#{session_name}' -f '#{==:#{session_attached},0}')[*]}")[1]} || tmux new"

Resources