Nested tmux sessions on local and remote servers - nested

I'm trying to setup tmux to work like so:
Outer session (level 0) on localhost with prefix C-a
Inner session (level 1) on localhost with prefix C-b
Inner session (level 1) on remote host with prefix C-b
So each inner session is nested directly in the outer session.
If I setup my .tmux.conf like this:
unbind C-b
set -g prefix C-a
bind-key -n C-b send-prefix
the local inner session receives the C-b prefix properly, but remote inner session doesn't.
If I change my tmux.conf to this:
unbind C-b
set -g prefix C-a
then the remote inner session receives C-b, but the local session doesn't.
Is there a way I can configure tmux so that both the local and remote nested sessions receive the C-b prefix?

Inspired by this blog post on Tmux scripting, I was able to find a solution. I put this in my shell startup script (e.g. .bashrc) on my localhost:
tmux_outer() {
SESSION=$1
tmux -2 new-session -d -s $SESSION
tmux set prefix C-a
tmux -2 attach-session -t $SESSION
}
tmux_inner() {
SESSION=$1
env TMUX='' tmux -2 new-session -s $SESSION
}
and removed my .tmux.conf
The problem in my original .tmux.conf:
unbind C-b
set -g prefix C-a
bind-key -n C-b send-prefix
seems to be set -g prefix C-a, which globally sets the prefix to C-a. In my shell function, I create the outer session in a detached state, set the prefix to C-a only for that session, and then attach to that session.
When I start an inner session on my localhost or remote host, it has the default tmux prefix C-b. Since the outer session isn't trying to capture and send C-b, both inner sessions receive C-b with no problem.

tmux manual says, if I read it correctly, 1) prefix is server-level setting and 2) all sessions are managed by a single server. This means you can't get outer and inner session at the same host with different prefixes, unless you are starting multiple servers using corresponding options as non-default socket and another config for a second server.
Alternatively, it's easier to use GNU screen instead of tmux. It allows prefix specification in command line and unlimited server count in a easier way.
UPDATE[2017-07-05]: modern tmux has enough per-session configuration, including switch prefix, despite in somewhat cumbersome way.

Related

Reattached tmux session not importing ~/.bashrc

I am using iTerm2 integrated with tmux. My normal working pattern is to first open up the iTerm2 terminal on my Mac, and then ssh to my dev Virtual Machine.
My dev VM has tmux installed, so that I can re-attach the tmux sessions to my dev VM.
The problem is when the first time I create the tmux session it will source ~/.bashrc with no problem.
But if I clean detach tmux session, and later re-attach those tmux sessions, the ~/.bashrc will not be sourced.
I have included
if [ -f ~/.bashrc ]; then source ~/.bashrc; fi
in ~/.bash_profile, .profile, .bash_login.
And also included
set-option -g default-command "exec /bin/bash"
to ~/.tmux.conf
As is implied by the verb "re-attach", your tmux session (and with it the Bash shell that runs in it) is kept running on your dev machine when you disconnect, so that you can later reconnect to the very same session. (That's the main feature of tmux: Normally, a shell is directly connected to your terminal or SSH session, so when you close / disconnect it, the shell has nothing to read from and output to, so it will have to exit. tmux provides a virtual terminal in between, so the shell has something to hang onto (even if nobody sees the output and nobody currently inputs anything), and tmux handles the session management.)
Applications (like Bash and also Vim) usually only read their configuration on startup. As Bash is kept running (you can verify that via ps -o etime --pid $$), it won't notice that you're reconnecting via tmux, and has no reason to reload its config - everything should still be defined and preserved within the tmux session. If you need to reload a (changed) configuration, you have to do this explicitly (source ~/.bashrc), or open up a new shell.

tmux ctrl+d does not detach from session

I am running a server with Ubuntu 14.04 using nodejs with
npm start
command.
I start tmux session with
tmux
command, then do
npm start
and finally do
ctrl+d
to detach.
But ctrl+d would not work for me. Whatever I am using, it only detaches me from the session if I stop all the processes.
It is also same problem when I am trying to detach from session with mongod running.
Any ideas?
PS: running ctrl+a+d does not work either.
When issuing a tmux command, you need to first use the prefix key combination. By default, this is C-b (ctrl+b).
If the default prefix isn't working, it's possible that you changed it or, if you're using someone else's .tmux.conf, they may have changed it. You can run tmux list-keys | grep send-prefix from your shell to determine what the current prefix is.
So, in order to detach from a running session, you'd type C-b d.

Is there any way to run a script on any SSH connect/disconnect?

I'd like to change my terminal color depending on ssh connected HOSTNAME.
I know how to modify the terminal, but how can I instrument ssh to add hooks?
I could wrap the ssh command with a shell function, or replace the binary, but its used as a dependency by other apps, and I would rather not do that.
You can use LocalCommand feature of OpenSSH when connecting to a remote server:
LocalCommand
Specifies a command to execute on the local machine after successfully connecting to the server. The command string extends to the end of the line, and is executed
with the user's shell. The following escape character substitutions will be performed: ‘%d’ (local user's home directory), ‘%h’ (remote host name), ‘%l’ (local host
name), ‘%n’ (host name as provided on the command line), ‘%p’ (remote port), ‘%r’ (remote user name) or ‘%u’ (local user name).
The command is run synchronously and does not have access to the session of the ssh(1) that spawned it. It should not be used for interactive commands.
This directive is ignored unless PermitLocalCommand has been enabled.
There is probably no easy way to execute a command when ending a connection with a remote server though apart from writing a ssh wrapper.
A Wrapper around SSH may be your best bet, even though you have discounted it. However almost every program lets you specify the 'ssh' command they use.
In my I own case I have a 'r' command to replace 'ssh' which performs account lookups (username and host aliases and DNS expansion without relying on the 'DNS resolver domain list', amoungst other things). These Are my notes to get various programs to call 'r' instead of 'ssh'.
scp
No config or environment variables, you must use option "-Sr"
scp -Sr ...
rsync
set environment variable
RSYNC_RSH="r -x"
unison
In ".unison/default.prf"
sshcmd = r
sshargs = -q -x
cssh
In ".clusterssh/config"
ssh=r
ssh_args= -x -o ConnectTimeout=10
multixterm
Use multixterm -xc "r %n" hostname...
vim netrw (file explorer)
Have it use the "rsync" command (set above)...
vim rsync://hostname/
OR for vim scp://hostname/...
In ".vimrc" configuration, redefine scp command to use.
let g:netrw_list_cmd="r USEPORT HOSTNAME ls -Fa1"
let g:netrw_scp_cmd="scp -Sr -q"
My 'r' script decodes all SSH arguments, I have seen used, and even handles very OLD "rsync" commands that placed more options AFTER the hostname! (Yes, I have been using this script a long time)

How do I change the starting directory of a tmux session?

The directory where a tmux session is started in will be the directory that new windows start at. How do I change this starting directory without closing the tmux session?
The way to do this is to detach from the session (^b d with the default keybindings) and then specify a different directory when you reattach to it. When attaching to a session, use the -c flag to specify the working directory. Here's an example:
$ tmux list-sessions
tmuxwtfbbq: 3 windows (created Tue Apr 5 14:25:48 2016) [190x49]
$ tmux attach-session -t tmuxwtfbbq -c /home/chuck/new_default_directory
This setting will be persisted - after you've reset the working directory, you won't need to keep specifying it every time you reattach to the session.
For the record, I'm on tmux version 2.0 (though I don't think it matters - I couldn't find anything about adding a -c option to the attach-session command in the change logs so I assume it's been there for quite a while).
Chucksmash's answer is a good one, but it can also be achieved without using the session if you like. The command attach-session is also available in the tmux command prompt; and the target session can be specified as the "current" session using a dot.
attach-session -t . -c /path/to/new/directory
Here's how you can change the tmux session's working directory without detaching the session, and without needing use to the <prefix> keystrokes:
(Option 1) Enter the directory at tmux command prompt:
tmux command-prompt "attach -c %1"
...will open a command prompt, then you type the working directory you want ~/my/dir and press ENTER
(Option 2) Provide the directory on the in-pane command line:
# Execute this in one of the shell panes of within your tmux session:
tmux command-prompt -I $PWD -P "New session dir:" "attach -c %1"
With this approach, the prompt for new-directory is pre-populated with the current dir of the pane which launched the command. Of course you can substitute anything else for $PWD if you please.
Want a shell function?
I have added this to my shell initialization:
# Change the current directory for a tmux session, which determines
# the starting dir for new windows/panes:
function tmux-cwd {
tmux command-prompt -I $PWD -P "New session dir:" "attach -c %1"
}
With all of these options, any future new windows will start in the given dir.
Note: attach, attach-session, and a are all aliases for each other. The tmux command-prompt has many other powers, it's worth reading the man page
use attach-session -c from command prompt or in a binding.
I have bindings to use the current directory automatically, using the provided pane_current_path var.
I have this bound to M-c, to update the current path.
# tmux.conf
# set default directory for new windows in this session to current directory:
bind M-c attach-session -c "#{pane_current_path}"
Similarly, when I just want another window in the current directory without changing the default, I have a binding on C:
# tmux.conf
# open a new window in the current directory
bind C new-window -c "#{pane_current_path}"
My bindings:
c: open a new window in session default directory [default binding]
C: open a new window in current directory
M-c: set session default directory to current directory
Happy Tmuxing!
The default working directory can be changed from the command line like this:
TMUX= tmux -C attach -c directory -t session </dev/null >/dev/null
Compared to command-prompt, for example, this has the advantage that no interactive prompts or confirmations are produced, so this can be run in a script. Unsetting TMUX allows attaching from inside a session, the option -C turns on control mode for non-interactive use and redirecting from /dev/null causes the client to detach immediately, while still updating the default directory. Redirecting the output is not necessary, but it suppresses messages.

Can I start tmux as a child of gnome-session?

I'm running Ubuntu and use gnome-terminal and would like to run tmux in such a way that it can use all of the gnome-session environment in the same way opening a new terminal window would. E.g. using passphrase-less ssh.
The issue seems to be the process hierarchy...
In a new window of gnome-terminal:
$ pstree -ps $$
init(1)───lightdm(1825)───lightdm(18486)───gnome-session(18552)───gnome-terminal(18626)
Once I enter a new tmux session (even in the above terminal window:
$ pstree -ps $$
init(1)───tmux(15798)───bash(21770)
tmux appears to be a direct child of init and not in the session's process hierarchy. Is there a way to get it to be created as a child of gnome-session?
EDIT: Great answer below (the accepted one)! However I thought I'd include a function I wrote since receiving the answer to update all child bash processes of tmux to the latest environment:
#!/bin/bash
tmup ()
{
echo -n "Updating to latest tmux environment...";
export IFS=",";
for line in $(tmux showenv -t $(tmux display -p "#S") | tr "\n" ",");
do
if [[ $line == -* ]]; then
unset $(echo $line | cut -c2-);
else
export $line;
fi;
done;
unset IFS;
echo "Done"
}
The tmux server calls daemon(3) to detach itself from the process that started it (i.e. the initial tmux client). This is not optional so the server will always be reparented to PID 1 (e.g. init) after the double-fork-and-middle-exit done by daemon(3).
In general, it should not be important that the tmux server is no longer directly “connected” to gnome-session though the parentage of (surviving) processes.
In the case of ssh, the ability to use a key without having to retype its passphrase relies on access to an ssh-agent process. Instances of ssh look for the SSH_AUTH_SOCK environment variable to know where to contact an ssh-agent that can supply keys for it. gnome-session probably arranges to start an ssh-agent and populate its environment with the appropriate SSH_AUTH_SOCK value. This environment is inherited from parent to child as your various processes are started. In this way, the tmux server will also inherit the SSH_AUTH_SOCK value (from the initial tmux client, which got it from a shell, which got it from gnome-terminal, which got it from gnome-session).
A problem occurs, however, when you attach to a tmux session that was started from a different environment. Consider the following scenario that is hinted at by the fact that the PID of your tmux server is lower than that of your gnome-session:
Log in to a GUI session.
gnome-session starts an ssh-agent and configures SSH_AUTH_SOCK=foo in its environment; this value will be inherited by all of its future children.
You start a tmux server (via a shell and gnome-terminal).
The tmux server inherits SSH_AUTH_SOCK=foo; it will be passed on to its children (e.g. a shell running in a tmux session).
You disconnect from your tmux session and log out of your GUI session.
The tmux server and its children still have SSH_AUTH_SOCK=foo, but that value is probably no longer valid (when gnome-session is winding down, it probably killed the ssh-agent that it started).
Later, you log back in to a GUI session.
This time gnome-session sets SSH_AUTH_SOCK=bar and passes it along to its children.
You reconnect to your tmux session.
At this point, you have SSH_AUTH_SOCK=bar “outside” tmux and SSH_AUTH_SOCK=foo “inside” the session. This is the probably where you run into problems.
Effectively, since the tmux server has outlived the original GUI session, any environment variables it initially inherited that were specific to that session are potentially invalid (unless they happen to use exactly the same values the next time you log into a GUI session).
Luckily, tmux has a feature for handling this scenario. The update-environment session option specifies a list of environment variables that are copied into (or removed from) the “session environment” when a client creates or attaches to a session. SSH_AUTH_SOCK is a part of the default value of this option, so it is updated when you reattach. But, tmux is only able to update its “session environment” (which will be inherited by any new children of that session).
Unfortunately, tmux has no way to update any existing processes that are a part of that session (indeed, this is impossible, short of debugging tools that can tinker with the internals of already running processes). So, any existing shells running in windows/panes after the above scenario will probably be using an invalid SSH_AUTH_SOCK. ssh will not complain about the invalid value, it will just prompt you for the appropriate key’s passphrase.
What you might try doing is extracting the value of SSH_AUTH_SOCK from the session environment and incorporating it into the pre-existing shells from your old session with a command like this:
SSH_AUTH_SOCK=$(tmux show-environment | awk '/^SSH_AUTH_SOCK=/ { sub(/.*=/,""); print }')
If you are having issues related to other environment variables, then you may need to add them to update-environment (e.g. set-option -ga update-environment ' FROBNIZ' in your ~/.tmux.conf) and do something similar to copy the values into any existing shells after you reattach from a different context.

Resources