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.
Related
Does anyone know any way of doing this? The help would be greatly appreciated, I've been beating my head over this one for a while now, and I can't seem to find any way to get scripts to auto-start and display in the foreground as they would if I had manually started them.
what i want to do is basically just power on the vm, let it boot, then watch the script run and echo results to the console or whatever the script would normally display if ran manually
I've been able to use Cron and systemd to run the script I would like to run at startup, but I cannot figure out any way to get these scripts to run in the screen, as they would if I had typed ./startup_script.sh
i am currently testing everything in a headless Debian 11 vm. Auto login as root is already enabled. i just need to complete this last step but i don't know how to do.
You can fire up a tmux session and send keys it to start a script like this:
tsid="session_name"
my_script="/home/script.sh"
# create new tmux session
tmux new-session -d -s ${tsid}
# launch the script in the tmux session
echo "launching script in a tmux session called ${tsid}"
tmux send-keys -t ${tsid} "${$my_script}" 'C-m'
I do something similar to start a "quad-pane" to my raspberry pi devices. I can send commands to each pane via ssh without looking at the screen I am sending commands to. I can send commands to the panes from any ssh session without actually connecting to a gui, or being in the "session". This way, another machine can have that session up on a monitoring display and I can momentarily ssh over a command to the host and it will show over there without ever actually connecting to the window. The panes can run scripts and and have the output on the each respective screen/pane waiting for me to connect later and look at it. Every now and then I'll pull up my quad feed to see what's happening... then disconnect and leave it all running.
# function to send an arbitrary command to a tmux pane
# arg1: full id of pane, e.g. "pi4-host01:quad-feed.0"
# arg2+ command(s) to send to pane
tmux_send_command_to_pane_id() {
local tpid cmd
tpid="${1:-0}"
cmd="${#}"
echo "cmd to send to pane: ${tpid}: ${cmd}"
tmux send-keys -t "${tpid}" "${cmd}" 'C-m'
}
If you pair something like this with cron or systemd, i'd imagine you could get to your goal relatively quick. It does depend on installing tmux. I know others use screen, but I have become a fan of tmux for whatever reason.
A nice answer on systemd scripts here -> https://unix.stackexchange.com/a/47715/376937
Here is another question that may help as well:
Tmux command to run shell command on active pane?
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.
I am using my laptop via shell terminal to log on school’s server to run a
Matlab session. The session will take about 10 hours and I want to close my
laptop, go home, have dinner, and re-engage the shell terminal to check the
progress of my Matlab session.
From this link I know I should use nohup nohup to keep my terminal alive,
but I meet the following problem. Here is a screenshot of my shell after I start
running Matlab session:
where a = cv000_29590 is the respond from the Matlab. It should keep running
until cv999999 and take about 10 hours.
The problem is, this shell is not interactive anymore. I can’t enter anymore
commands, that is, I have no where to enter nohup commend to keep my SSH
session alive.
It's not really possible after you've already started a session. But for new sessions you can do the following:
Add the following to the top of your .bash_profile:
if [ -z "${PS1}" ] ; then
return
fi
if [ "${TERM}" != "screen" ] ; then
export HOSTNAME
exec screen -xRR
fi
function new {
u=${1:-$USER}
test ${u} = ${USER} && screen -t ${u}#${HOSTNAME} || screen -t ${u}#${HOSTNAME} su --login ${u}
}
Put the following content into .screenrc:
escape ^bb
shell -$SHELL
termcapinfo xterm ti#:te#
hardstatus lastline "%-Lw[%n%f %t]%+Lw%<"
screen -t ${USER}#${HOSTNAME}
These are mostly my own customizations of screen. The most important of which is that I set the screen escape character to CTRL-b instead of the default CTRL-a so I can still use CTRL-a in bash to go to the beginning of a line.
Use CTRL-b c to create shells in new windows (or just type new at the bash prompt to use the function). And use CTRL-b d to detach your session and leave it running. Next time you login, you'll be reattached to your session and everything will be as it was. Use CTRL-b n to cycle through the windows you've created. If you don't want to multiple windows, you don't have to, just use the ability to leave a session running and reattach later.
I think it's related to the parent process creating new subprocess and does not have tty. Can anyone explain the detail under the hood? i.e. the related working model of bash, process creation, etc?
It may be a very broad topic so pointers to posts are also very appreciated. I've Googled for a while, all the results are about very specific case and none is about the story behind the scene. To provide more context, below is the shell script resulting the 'bash: no job control in this shell'.
#! /bin/bash
while [ 1 ]; do
st=$(netstat -an |grep 7070 |grep LISTEN -o | uniq)
if [ -z $st ]; then
echo "need to start proxy #$(date)"
bash -i -c "ssh -D 7070 -N user#my-ssh.example.com > /dev/null"
else
echo "proxy OK #$(date)"
fi
sleep 3
done
This line:
bash -i -c "ssh -D 7070 -N user#my-ssh.example.com > /dev/null"
is where "bash:no job control in this shell” come from.
You may need to enable job control:
#! /bin/bash
set -m
Job control is a collection of features in the shell and the tty driver which allow the user to manage multiple jobs from a single interactive shell.
A job is a single command or a pipeline. If you run ls, that's a job. If you run ls|more, that's still just one job. If the command you run starts subprocesses of its own, then they will also belong to the same job unless they are intentionally detached.
Without job control, you have the ability to put a job in the background by adding & to the command line. And that's about all the control you have.
With job control, you can additionally:
Suspend a running foreground job with CtrlZ
Resume a suspended job in the foreground with fg
Resume a suspend job in the background with bg
Bring a running background job into the foreground with fg
The shell maintains a list of jobs which you can see by running the jobs command. Each one is assigned a job number (distinct from the PIDs of the process(es) that make up the job). You can use the job number, prefixed with %, as an argument to fg or bg to select a job to foreground or background. The %jobnumber notation is also acceptable to the shell's builtin kill command. This can be convenient because the job numbers are assigned starting from 1, so they're shorter than PIDs.
There are also shortcuts %+ for the most recently foregrounded job and %- for the previously foregrounded job, so you can switch back and forth rapidly between two jobs with CtrlZ followed by fg %- (suspend the current one, resume the other one) without having to remember the numbers. Or you can use the beginning of the command itself. If you have suspended an ffmpeg command, resuming it is as easy as fg %ff (assuming no other active jobs start with "ff"). And as one last shortcut, you don't have to type the fg. Just entering %- as a command foregrounds the previous job.
"But why do we need this?" I can hear you asking. "I can just start another shell if I want to run another command." True, there are many ways of multitasking. On a normal day I have login shells running on tty1 through tty10 (yes there are more than 6, you just have to activate them), one of which will be running a screen session with 4 screens in it, another might have an ssh running on it in which there is another screen session running on the remote machine, plus my X session with 3 or 4 xterms. And I still use job control.
If I'm in the middle of vi or less or aptitude or any other interactive thing, and I need to run a couple of other quick commands to decide how to proceed, CtrlZ, run the commands, and fg is natural and quick. (In lots of cases an interactive program has a ! keybinding to run an external command for you; I don't think that's as good because you don't get the benefit of your shell's history, command line editor, and completion system.) I find it sad whenever I see someone launch a secondary xterm/screen/whatever to run one command, look at it for two seconds, and then exit.
Now about this script of yours. In general it does not appear to be competently written. The line in question:
bash -i -c "ssh -D 7070 -N user#my-ssh.example.com > /dev/null"
is confusing. I can't figure out why the ssh command is being passed down to a separate shell instead of just being executed straight from the main script, let alone why someone added -i to it. The -i option tells the shell to run interactively, which activates job control (among other things). But it isn't actually being used interactively. Whatever the purpose was behind the separate shell and the -i, the warning about job control was a side effect. I'm guessing it was a hack to get around some undesirable feature of ssh. That's the kind of thing that when you do it, you should comment it.
One of the possible options would be not having access to the tty.
Under the hood:
bash checks whether the session is interactive, if not - no job
control.
if forced_interactive is set, then check that stderr is
attached to a tty is skipped and bash checks again, whether in can
open /dev/tty for read-write access.
then it checks whether new line discipline is used, if not, then job control is disabled too.
If (and only if) we just set our process group to our pid, thereby becoming a process group leader, and the terminal is not in the same process group as our (new) process group, then set the terminal's process group to our (new) process group. If that fails, set our process group back to what it was originally (so we can still read from the terminal) and turn off job control.
if all of the above has failed, you see the message.
I partially quoted the comments from bash source code.
As per additional request of the question author:
http://tiswww.case.edu/php/chet/bash/bashtop.html Here you can find bash itself.
If you can read the C code, get the source tarball, inside it you will find job.c - that one will explain you more "under the hood" stuff.
I ran into a problem on my own embedded system and I got rid of the "no job control" error by running the getty process with "setsid", which according to its manpage starts a process with a new session id.
Faced this problem only because I've miscopied a previously executed command together with % prefix into zsh, like % echo this instead of echo this. The error was very unclear to such a stupid typo
I have installed tmux from source on my localspace in Fedora. It was working nicely so far. But suddenly can not run it anymore, when run tmux, it just halts. Tried different command options like ls-sessions, none works. Killed all the processes of my user, deleted all the files of tmux and libevnet, and reinstalled them again from scratch. Still same, and tmux command in terminal just freezes without any actual error.
I had faced this problem for a long time and after a bit of searching I figured out that this was being caused because I accidently hit Ctrl+S (Ctrl+A+S is my shortcut for switching panes), and this turns off flow control in terminals and stops the terminal from accepting input. It can be reenabled by pressing Ctrl+Q.
Source: https://superuser.com/a/553349/137226
Had a similar issue, where I had a tmux session with two buffers. I didn't see anything I typed, but when I switched between buffers what I had typed previously would appear onscreen. stty sane didn't work.
I detached Ctrl-b+d, and noticed that there was still a client attached when I looked at tmux list-clients. tmux detach-client removed it, and then I could reattach and the everything worked again.
If it is ok to lose your sessions, try deleting the tmux-NNNNNNN directory, where NNNNNNN is a number, under your /tmp directory. According to the tmux manual, if the TMPDIR environment variable is set, the tmux-NNNNNNN will be put in the TMPDIR.
tmux stores the server socket in a directory under /tmp (or TMPDIR if set);
This solved my problem of not being able to run tmux commands that are related to sessions. I also tried the following, but they did not work:
killall -9 tmux
reinstall tmux
restart shell session
I could not easily restart the operating system, because it's a shared server managed by others.
tmux was halting right after I started it. Ctrl-Q and Ctrl-C didn't do anything.
Fixed with
killall -9 tmux
(May be a different problem, but this question showed up in Google.)
I had the same issue. The cause is that the tmux buffer is full, and it also may happens cause of multi clients to the tmux session.
To solve it you need to detach all the clients from the session, and reattach it.
The best way I found to solve it is to add to the ~/.bashrc file this functions:
check_params() {
if [[ $1 < $2 ]]; then
echo -e "Usage:\n${3}"
ok=0
else
ok=1
fi
}
# detach all the clients from this session, and attach to it.
reattach_client() {
check_params $# 1 "reattach_client <tmux_session_name>"
if [[ $ok == 1 ]]; then
tmux list-client | grep $1 | awk '{split($1, s, ":"); print s[1]}' | xargs tmux detach-client -t | true
tmux attach -t $1
fi
}
then run source ~/.bashrc to make these changes in the terminal.
Now to attach the session type:
reattach_client <session_name>
solved my issue.
Thanks to Alex Zelichenko for help me with this!
You should be able to narrow down your problem a bit with a few of these tests:
Give it a shot from outside X11: Ctrl+Alt+F2 (or use ssh from another computer)
Test if other terminal emulators work: script and screen
Try another complicated terminal application: htop and mc
Reset your TTY settings: stty sane
Check that your terminal identified: echo $TERM (it should be something like "xterm" or "linux")
Make that your terminal capabilities file exists: ls -lh /usr/share/terminfo/*/$TERM
Thanks.
I found the problem. The tmux process were in D state, and I had no choice but to reboot the system.
The problem came from kerberos ticket expiring after a while. And find a scripts that solves this problem:
https://iain.cx/src/ktmux/
A less drastic action (to try before killing the tmux process) is to ssh into the machine and run the following command.
kill -CONT `pidof tmux`
Source: https://github.com/tmux/tmux/issues/507#issuecomment-271502093
This happened to me because I accidentally tried to create two parallel tmux sessions with the same name.
What worked for me was to enter htop, checking pid's of the two running commands that created the sessions, and killing both by using kill -9 pid1 and kill -9 pid2