Tmux restore creates an empty session - shell

I'm using the tmux-continuum and tmux-resurrect plugins. If I kill tmux (restarting my machine for example) and then run the following commands:
$ tmux ls
$ failed to connect to server: No such file or directory
Then when I start tmux it automatically restores my saved sessions plus an unnamed session (usually 0)
$ tmux
$ tmux ls
0: 1 windows (created...)
saved_session_1: 1 windows (created...)
saved_session_2: 1 windows (created...)
...
My current workflow goes like this:
Start tmux
Detach from tmux
Attach to the unnamed session
Kill unnamed session
Attach to one of my saved sessions
I don't want to have to repeat this every time I restart tmux. How can I restore my saved tmux sessions without creating the unnamed session?

If I understand correctly your question this is a common issue with tmux-resurrect. The solution given here (currently the last comment in the Github discussion) has worked for me.
Add the following to your .tmux.conf and then do source ~.tmux.conf(if that is the path of your conf file):
set -g #resurrect-hook-pre-restore-pane-processes 'tmux switch-client -n && tmux kill-session -t=0'
This a hook for tmux-resurrect which tells it to kill session 0 before restoring the panels.
Note: since the name of the session (-t=0) is hardcoded it will work only for that session, hence only if you do restore when you start the tmux server the first time, if you restore from sessions after 0 nothing will happen (which is nice to avoid kill sessions accidentally).

Run the following command (preferably make an alias it):
$ tmux new-session -A -s [session-name]
Flag meaning:
-s refers to session name.
-A In case session-name exists, command will act like attach-session instead of new-session.
Refer to the man page for official documentation:
$ man tmux
new-session [-AdDEP] [-c start-directory] [-F format] [-n window-name] [-s session-name] [-t
target-session] [-x width] [-y height] [shell-command]
(alias: new)
Create a new session with name session-name.
The new session is attached to the current terminal unless -d is given. window-name and
shell-command are the name of and shell command to execute in the initial window. If -d
is used, -x and -y specify the size of the initial window (80 by 24 if not given).
If run from a terminal, any termios(4) special characters are saved and used for new win‐
dows in the new session.
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.
If -t is given, the new session is grouped with target-session. This means they share
the same set of windows - all windows from target-session are linked to the new session,
any new windows are linked to both sessions and any windows closed removed from both ses‐
sions. The current and previous window and any session options remain independent and
either session may be killed without affecting the other. -n and shell-command are
invalid if -t is used.
The -P option prints information about the new session after it has been created. By
default, it uses the format ‘#{session_name}:’ but a different format may be specified
with -F.
If -E is used, the update-environment option will not be applied.

You could simply launch tmux by tmux a (i.e. attach to "existing" sessions). This would trigger tmux-continuum to first restore all sessions and then you'll get attached to one of them.
Works fine for me. I'm running tmux 3.0a with tmux-resurrect and tmux-continuum plugins.

Related

running a server in tmux with ansible

trying to setup a staging server for an API which I'm building using Django - and so far I was cutting corners, starting the thing using python manage.py runserver. But now that the setup grew a bit more complex, I decided to build an ansible playbok. Everything worked fine until I got to launching gunicorn - because I want it to run inside a tmux session. Manual process doesn't seem to trivially translate to ansible. I've been manually creating tmux session:
tmux new-session -A -s api
and then running gunicorn inside this new "environment" (subshell?)
The thing is (as is probably obvious to ansible veterans), when I get to running the first step, my playbook just hangs, and never gets to the next step, which is where gunicorn is to be started. I suppose this is because I'm starting a new shell with tmux, and ansible is lost, not hearing back (because, my guess, it's still waiting for a response on the original shell? which will never come). Is there a right way to execute the "tmux" step, letting ansible use it as a context/environment for the next step, or should I just be content with ansible doing the setup, and do the tmux thing manually? I had a similar problem, when dealing with the fact that gunicorn is inside a virtualenv, but the workaround is to use a full path, which includes the virtualenv guts. Not sure if there's a similar workaround with tmux...
thanks y'all
tmux immediately attaches to the new session, and doesn't exit until you detach from the session or the last process in the session ends. Until tmux exits, the rest of your script hangs.
You can use the -D option to prevent attaching to the session, whether or not it needs to be created.
tmux new-session -AD -s api
The rest of your script can now proceed.
tmux new-session -AD -s api is a shortcut for
tmux has-session -t api || tmux new-session -d -s api

Attach and run script inside an existing tmux session

I want to have more control over a time consuming cron job running on a server. That is, be able to see progress, stop running code if necessary and etc..
I thought that this would be possible using a tmux session, but I can not figure out how.
I know that you can start new tmux sessions and run code inside it like this:
tmux new-session -d -s session_name "some code"
I've tried the obvious solution like this:
tmux new-session -s session_name
**exit session**
tmux a -t session_name "some code"
Is this even possible? Any advice is appreciated.
You could send the key to the tmux session:
tmux send -t session_name ls ENTER
if you need to send to a specific panel:
tmux send -t session_name.(panelnumber) ls ENTER
example:
tmux send -t session_name.0 ls ENTER

Run ssh and immediately execute command [duplicate]

This question already has answers here:
Can I ssh somewhere, run some commands, and then leave myself a prompt?
(5 answers)
Closed 7 years ago.
I'm trying to find UNIX or bash command to run a command after connecting to an ssh server. For example:
ssh name#ip "tmux list-sessions"
The above code works, it lists the sessions, but it then immediately disconnects. Putting it in the sshrc on the server side works, but I need to be able to type it in client side. I want to be able to run a command, it logs in, opens up the window, then runs the command I've set. Ive tried
[command] | ssh name#ip
ssh name#ip [command]
ssh name#ip "[command]"
ssh -t name#ip [command]
ssh -t 'command; bash -l'
will execute the command and then start up a login shell when it completes. For example:
ssh -t user#domain.example 'cd /some/path; bash -l'
This isn't quite what you're looking for, but I've found it useful in similar circumstances.
I recently added the following to my $HOME/.bashrc (something similar should be possible with shells other than bash):
if [ -f $HOME/.add-screen-to-history ] ; then
history -s 'screen -dr'
fi
I keep a screen session running on one particular machine, and I've had problems with ssh connections to that machine being dropped, requiring me to re-run screen -dr every time I reconnect.
With that addition, and after creating that (empty) file in my home directory, I automatically have the screen -dr command in my history when my shell starts. After reconnecting, I can just type Control-P Enter and I'm back in my screen session -- or I can ignore it. It's flexible, but not quite automatic, and in your case it's easier than typing tmux list-sessions.
You might want to make the history -s command unconditional.
This does require updating your $HOME/.bashrc on each of the target systems, which might or might not make it unsuitable for your purposes.
You can use the LocalCommand command-line option if the PermitLocalCommand option is enabled:
ssh username#hostname -o LocalCommand="tmux list-sessions"
For more details about the available options, see the ssh_config man page.

Running ssh sudo asynchronously

I'm trying to run a command with sudo on a remote machine. When I do it directly with
ssh -t -t -t myserver -q "sudo otheruser<<EOF
remotescript.sh
EOF"
it works fine, but if I add & at the end of the last line then it doesn't work. Why? How can I make it work?
I fact I'm running several such commands (to different servers) from a local script and save each output in a different file and would like them to run asynchronously.
Note: running ssh with otheruser#myserver is not an option. I really need to run sudo after I logged in.
Remove requiretty from sudo config (/etc/sudoers) on the remote machine.
Also add the -f option to ssh which puts the command in background (man: "must be used when ssh is run in the background").
The "&" should not be needed when using -f.
E.g:
ssh -f -t -t -t myserver -q "sudo otheruser<<EOF
remotescript.sh
EOF"
Use expect to control your ssh. It could be used to give automated response to the remote shell. Most processes when ran asynchronously suspends itself or becomes suspended when it tries to read input from terminal since another foreground process (the main shell) is using it.
There's a post about ssh and expect lately here: https://superuser.com/questions/509545/how-to-run-a-local-script-in-remote-server-using-expect-and-bash-script
Also try to disown your process after placing it on the background with disown to keep it from job control. e.g.
whole_command &
disown
Changing its input to /dev/null might also help but it could hang forever if it really needs input from user.
whole_command <&- < /dev/null &
disown

How can I ssh directly to a particular directory?

I often have to login to one of several servers and go to one of several directories on those machines. Currently I do something of this sort:
localhost ~]$ ssh somehost
Welcome to somehost!
somehost ~]$ cd /some/directory/somewhere/named/Foo
somehost Foo]$
I have scripts that can determine which host and which directory I need to get into but I cannot figure out a way to do this:
localhost ~]$ go_to_dir Foo
Welcome to somehost!
somehost Foo]$
Is there an easy, clever or any way to do this?
You can do the following:
ssh -t xxx.xxx.xxx.xxx "cd /directory_wanted ; bash --login"
This way, you will get a login shell right on the directory_wanted.
Explanation
-t Force pseudo-terminal allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services.
Multiple -t options force tty allocation, even if ssh has no local tty.
If you don't use -t then no prompt will appear.
If you don't add ; bash then the connection will get closed and return control to your local machine
If you don't add bash --login then it will not use your configs because its not a login shell
You could add
cd /some/directory/somewhere/named/Foo
to your .bashrc file (or .profile or whatever you call it) at the other host. That way, no matter what you do or where you ssh from, whenever you log onto that server, it will cd to the proper directory for you, and all you have to do is use ssh like normal.
Of curse, rogeriopvl's solution works too, but it's a tad bit more verbose, and you have to remember to do it every time (unless you make an alias) so it seems a bit less "fun".
My preferred approach is using the SSH config file (described below), but there are a few possible solutions depending on your usages.
Command Line Arguments
I think the best answer for this approach is christianbundy's reply to the accepted answer:
ssh -t example.com "cd /foo/bar; exec \$SHELL -l"
Using double quotes will allow you to use variables from your local machine, unless they are escaped (as $SHELL is here). Alternatively, you can use single quotes, and all of the variables you use will be the ones from the target machine:
ssh -t example.com 'cd /foo/bar; exec $SHELL -l'
Bash Function
You can simplify the command by wrapping it in a bash function. Let's say you just want to type this:
sshcd example.com /foo/bar
You can make this work by adding this to your ~/.bashrc:
sshcd () { ssh -t "$1" "cd \"$2\"; exec \$SHELL -l"; }
If you are using a variable that exists on the remote machine for the directory, be sure to escape it or put it in single quotes. For example, this will cd to the directory that is stored in the JBOSS_HOME variable on the remote machine:
sshcd example.com \$JBOSS_HOME
SSH Config File
If you'd like to see this behavior all the time for specific (or any) hosts with the normal ssh command without having to use extra command line arguments, you can set the RequestTTY and RemoteCommand options in your ssh config file.
For example, I'd like to type only this command:
ssh qaapps18
but want it to always behave like this command:
ssh -t qaapps18 'cd $JBOSS_HOME; exec $SHELL'
So I added this to my ~/.ssh/config file:
Host *apps*
RequestTTY yes
RemoteCommand cd $JBOSS_HOME; exec $SHELL
Now this rule applies to any host with "apps" in its hostname.
For more information, see http://man7.org/linux/man-pages/man5/ssh_config.5.html
I've created a tool to SSH and CD into a server consecutively – aptly named sshcd. For the example you've given, you'd simply use:
sshcd somehost:/some/directory/somewhere/named/Foo
Let me know if you have any questions or problems!
Based on additions to #rogeriopvl's answer, I suggest the following:
ssh -t xxx.xxx.xxx.xxx "cd /directory_wanted && bash"
Chaining commands by && will make the next command run only when the previous one was successful (as opposed to using ;, which executes commands sequentially). This is particularly useful when needing to cd to a directory performing the command.
Imagine doing the following:
/home/me$ cd /usr/share/teminal; rm -R *
The directory teminal doesn't exist, which causes you to stay in the home directory and remove all the files in there with the following command.
If you use &&:
/home/me$ cd /usr/share/teminal && rm -R *
The command will fail after not finding the directory.
In my very specific case, I just wanted to execute a command in a remote host, inside a specific directory from a Jenkins slave machine:
ssh myuser#mydomain
cd /home/myuser/somedir
./commandThatMustBeRunInside_somedir
exit
But my machine couldn't perform the ssh (it couldn't allocate a pseudo-tty I suppose) and kept me giving the following error:
Pseudo-terminal will not be allocated because stdin is not a terminal
I could get around this issue passing "cd to dir + my command" as a parameter of the ssh command (to not have to allocate a Pseudo-terminal) and by passing the option -T to explicitly tell to the ssh command that I didn't need pseudo-terminal allocation.
ssh -T myuser#mydomain "cd /home/myuser/somedir; ./commandThatMustBeRunInside_somedir"
I use the environment variable CDPATH
going one step further with the -t idea. I keep a set of scripts calling the one below to go to specific places in my frequently visited hosts. I keep them all in ~/bin and keep that directory in my path.
#!/bin/bash
# does ssh session switching to particular directory
# $1, hostname from config file
# $2, directory to move to after login
# can save this as say 'con' then
# make another script calling this one, e.g.
# con myhost repos/i2c
ssh -t $1 "cd $2; exec \$SHELL --login"
My answer may differ from what you really want, but I write here as may be useful for some people. In my solution you have to enter into the directory once and then every new ssh session goes to the same dir (after the first logout).
How to ssh to the same directory you have been in your last login.
(I assume you use bash on the remote node.)
Add this line to your ~/.bash_logout on the remote node(!):
echo $PWD > ~/.bash_lastpwd
and these lines to the ~/.bashrc file (still on the remote node!)
if [ -f ~/.bash_lastpwd ]; then
cd $(cat ~/.bash_lastpwd)
fi
This way you save your current path on every logout and .bashrc put you into that directory after login.
ps: You can tweak it further like using the SSH_CLIENT variable to decide to go into that directory or not, so you can differentiate between local logins and ssh or even between different ssh clients.
Another way of going to directly after logging in is create "Alias". When you login into your system just type that alias and you will be in that directory.
Example : Alias = myfolder '/var/www/Folder'
After you log in to your system type that alias (this works from any part of the system)
this command if not in bashrc will work for current session. So you can also add this alias to bashrc to use that in future
$ myfolder => takes you to that folder
I know this has been answered ages ago but I found the question while trying to incorporate an ssh login in a bash script and once logged in run a few commands and log back out and continue with the bash script. The simplest way I found which hasnt been mentioned elsewhere because it is so trivial is to do this.
#!/bin/bash
sshpass -p "password" ssh user#server 'cd /path/to/dir;somecommand;someothercommand;exit;'
Connect With User
In case if you don't know this, you can use this to connect by specifying both user and host
ssh -t <user>#<Host domain / IP> "cd /path/to/directory; bash --login"
Example: ssh -t admin#test.com "cd public_html; bash --login"
You can also append the commands to be executed on every login by appending it in the double quotes with a ; before each command
Unfortunately, the suggested solution (of #rogeriopvl) doesn't work when you use multiple hops, so I found another one.
On remote machine add into ~/.bashrc the following:
[ "x$CDTO" != "x" ] && cd $CDTO
This allows you to specify the desired target directory on command line in this way:
ssh -t host1 ssh -t host2 "CDTO=/desired_directory exec bash --login"
Sure, this way can be used for a single hop too.
This solution can be combined with the usefull tip of #redseven for greater flexibilty (if no $CDTO, go to saved directory, if exists).
SSH itself provides a means of communication, it does not know anything about directories. Since you can specify which remote command to execute (this is - by default - your shell), I'd start there.
simply modify your home with the command:
usermod -d /newhome username

Resources