libssh
0.11.0
The SSH library
|
We already mentioned that a single SSH connection can be shared between several "channels". Channels can be used for different purposes.
This chapter shows how to open one of these channels, and how to use it to start a command interpreter on a remote computer.
The ssh_channel_new() function creates a channel. It returns the channel as a variable of type ssh_channel.
Once you have this channel, you open a SSH session that uses it with ssh_channel_open_session().
Once you don't need the channel anymore, you can send an end-of-file to it with ssh_channel_close(). At this point, you can destroy the channel with ssh_channel_free().
The code sample below achieves these tasks:
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.
If needed, you request the pty with the function ssh_channel_request_pty(). If you want define its dimensions (number of rows and columns), call ssh_channel_request_pty_size() instead. It's also possible to change the dimensions after creating the pty with ssh_channel_change_pty_size().
These two functions configure the pty using the same terminal modes that stdin has. If stdin isn't a TTY, they use default modes that configure the pty with in canonical mode and e.g. preserving CR and LF characters. If you want to change the terminal modes used by the pty (e.g. to change CRLF handling), use ssh_channel_request_pty_size_modes(). This function accepts an additional "modes" buffer that is expected to contain encoded terminal modes according to RFC 4254 section 8.
Be your session interactive or not, the next step is to request a shell with ssh_channel_request_shell().
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():
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.
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:
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:
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.
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:
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:
Don't forget to check the $DISPLAY environment variable on the remote side, or the remote applications won't try using the X11 tunnel:
See an implementation example at https://gitlab.com/libssh/libssh-mirror/-/tree/master/examples/ssh_X11_client.c for details.