A "shell" is a command interpreter. It is said to be "interactive" if there is a human user typing the commands, one after the other. The contrary, a non-interactive shell, is similar to the execution of commands in the background: there is no attached terminal.
If you plan using an interactive shell, you need to create a pseud-terminal on the remote side. A remote terminal is usually referred to as a "pty", for "pseudo-teletype". The remote processes won't see the difference with a real text-oriented terminal.
int
interactive_shell_session(ssh_channel channel)
int
rc;
if
(rc != SSH_OK)
return
rc;
if
(rc != SSH_OK)
return
rc;
if
(rc != SSH_OK)
return
rc;
return
rc;
LIBSSH_API int ssh_channel_request_pty(ssh_channel channel)
Request a PTY.
Definition
channels.c:1999
LIBSSH_API int ssh_channel_request_shell(ssh_channel channel)
Request a shell.
Definition
channels.c:2059
LIBSSH_API int ssh_channel_change_pty_size(ssh_channel channel, int cols, int rows)
Change the size of the terminal associated to a channel.
Definition
channels.c:2019
Displaying the data sent by the remote computer
In your program, you will usually need to receive all the data "displayed" into the remote pty. You will usually analyse, log, or display this data.
ssh_channel_read()
and
ssh_channel_read_nonblocking()
are the simplest way to read data from a channel. If you only need to read from a single channel, they should be enough.
The example below shows how to wait for remote data using
ssh_channel_read()
:
int
interactive_shell_session(ssh_channel channel)
int
rc;
char
buffer[256];
int
nbytes;
if
(rc != SSH_OK)
return
rc;
if
(rc != SSH_OK)
return
rc;
if
(rc != SSH_OK)
return
rc;
if
(nbytes < 0)
return
SSH_ERROR;
if
(nbytes > 0)
write(1, buffer, nbytes);
return
rc;
LIBSSH_API int ssh_channel_is_open(ssh_channel channel)
Check if the channel is open or not.
Definition
channels.c:1688
LIBSSH_API int ssh_channel_is_eof(ssh_channel channel)
Check if remote has sent an EOF.
Definition
channels.c:1720
LIBSSH_API int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr)
Reads data from a channel.
Definition
channels.c:3017
Unlike
ssh_channel_read()
,
ssh_channel_read_nonblocking()
never waits for remote data to be ready. It returns immediately.
If you plan to use
ssh_channel_read_nonblocking()
repeatedly in a loop, you should use a "passive wait" function like usleep(3) in the same loop. Otherwise, your program will consume all the CPU time, and your computer might become unresponsive.
Sending user input to the remote computer
User's input is sent to the remote site with
ssh_channel_write()
.
The following example shows how to combine a nonblocking read from a SSH channel with a nonblocking read from the keyboard. The local input is then sent to the remote computer:
int
kbhit()
struct
timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return
select(1, &fds, NULL, NULL, &tv);
int
interactive_shell_session(ssh_channel channel)
char
buffer[256];
int
nbytes, nwritten;
if
(nbytes < 0)
return
SSH_ERROR;
if
(nbytes > 0)
nwritten = write(1, buffer, nbytes);
if
(nwritten != nbytes)
return
SSH_ERROR;
if
(!kbhit())
usleep(50000L);
continue
;
nbytes = read(0, buffer,
sizeof
(buffer));
if
(nbytes < 0)
return
SSH_ERROR;
if
(nbytes > 0)
if
(nwritten != nbytes)
return
SSH_ERROR;
return
rc;
LIBSSH_API int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len)
Blocking write on a channel.
Definition
channels.c:1674
LIBSSH_API int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, int is_stderr)
Do a nonblocking read on the channel.
Definition
channels.c:3158
Of course, this is a poor terminal emulator, since the echo from the keys pressed should not be done locally, but should be done by the remote side. Also, user's input should not be sent once "Enter" key is pressed, but immediately after each key is pressed. This can be accomplished by setting the local terminal to "raw" mode with the cfmakeraw(3) function. cfmakeraw() is a standard function under Linux, on other systems you can recode it with:
static
void
cfmakeraw(
struct
termios *termios_p)
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
termios_p->c_cflag &= ~(CSIZE|PARENB);
termios_p->c_cflag |= CS8;
If you are not using a local terminal, but some kind of graphical environment, the solution to this kind of "echo" problems will be different.
A more elaborate way to get the remote data
Warning: ssh_select() and ssh_channel_select() are not relevant anymore,
since libssh is about to provide an easier system for asynchronous
communications. This subsection should be removed then. ***
ssh_channel_read()
and
ssh_channel_read_nonblocking()
functions are simple, but they are not adapted when you expect data from more than one SSH channel, or from other file descriptors. Last example showed how getting data from the standard input (the keyboard) at the same time as data from the SSH channel was complicated. The functions
ssh_select()
and
ssh_channel_select()
provide a more elegant way to wait for data coming from many sources.
The functions
ssh_select()
and
ssh_channel_select()
remind of the standard UNIX select(2) function. The idea is to wait for "something" to happen: incoming data to be read, outgoing data to block, or an exception to occur. Both these functions do a "passive wait", i.e. you can safely use them repeatedly in a loop, it will not consume exaggerate processor time and make your computer unresponsive. It is quite common to use these functions in your application's main loop.
The difference between
ssh_select()
and
ssh_channel_select()
is that
ssh_channel_select()
is simpler, but allows you only to watch SSH channels.
ssh_select()
is more complete and enables watching regular file descriptors as well, in the same function call.
Below is an example of a function that waits both for remote SSH data to come, as well as standard input from the keyboard:
int
interactive_shell_session(ssh_session session, ssh_channel channel)
char
buffer[256];
int
nbytes, nwritten;
struct
timeval timeout;
ssh_channel in_channels[2], out_channels[2];
fd_set fds;
int
maxfd;
timeout.tv_sec = 30;
timeout.tv_usec = 0;
in_channels[0] = channel;
in_channels[1] = NULL;
FD_ZERO(&fds);
FD_SET(0, &fds);
ssh_select
(in_channels, out_channels, maxfd, &fds, &timeout);
if
(out_channels[0] != NULL)
if
(nbytes < 0)
return
SSH_ERROR;
if
(nbytes > 0)
nwritten = write(1, buffer, nbytes);
if
(nwritten != nbytes)
return
SSH_ERROR;
if
(FD_ISSET(0, &fds))
nbytes = read(0, buffer,
sizeof
(buffer));
if
(nbytes < 0)
return
SSH_ERROR;
if
(nbytes > 0)
if
(nbytes != nwritten)
return
SSH_ERROR;
return
rc;
LIBSSH_API int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, fd_set *readfds, struct timeval *timeout)
A wrapper for the select syscall.
Definition
connect.c:334
LIBSSH_API socket_t ssh_get_fd(ssh_session session)
Get the fd of a connection.
Definition
session.c:606
Using graphical applications on the remote side
If your remote application is graphical, you can forward the X11 protocol to your local computer.
To do that, you first declare a callback to manage channel_open_request_x11_function. Then you create the forwarding tunnel for the X11 protocol with
ssh_channel_request_x11()
.
The following code performs channel initialization and shell session opening, and handles a parallel X11 connection:
#include <libssh/callbacks.h>
ssh_channel x11channel = NULL;
ssh_channel x11_open_request_callback(ssh_session session,
const
char
*shost,
int
sport,
void
*userdata)
return
x11channel;
int
interactive_shell_session(ssh_channel channel)
int
rc;
.userdata = NULL
if
(rc != SSH_OK)
return
rc;
if
(rc != SSH_OK)
return
rc;
if
(rc != SSH_OK)
return
rc;
if
(rc != SSH_OK)
return
rc;
if
(rc != SSH_OK)
return
rc;
LIBSSH_API int ssh_set_callbacks(ssh_session session, ssh_callbacks cb)
Set the session callback functions.
Definition
callbacks.c:48
#define ssh_callbacks_init(p)
Initializes an ssh_callbacks_struct A call to this macro is mandatory when you have set a new ssh_cal...
Definition
callbacks.h:451
LIBSSH_API int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol, const char *cookie, int screen_number)
Sends the "x11-req" channel request over an existing session channel.
Definition
channels.c:2186
Definition
callbacks.h:165
ssh_channel_open_request_x11_callback channel_open_request_x11_function
Definition
callbacks.h:191
Don't forget to check the $DISPLAY environment variable on the remote side, or the remote applications won't try using the X11 tunnel:
$ echo $DISPLAY
localhost:10.0
$ xclock &
See an implementation example at
https://gitlab.com/libssh/libssh-mirror/-/tree/master/examples/ssh_X11_client.c
for details.