Blame doc/shell.dox

Packit Service 31306d
/**
Packit Service 31306d
@page libssh_tutor_shell Chapter 3: Opening a remote shell
Packit Service 31306d
@section opening_shell Opening a remote shell
Packit Service 31306d
Packit Service 31306d
We already mentioned that a single SSH connection can be shared
Packit Service 31306d
between several "channels". Channels can be used for different purposes.
Packit Service 31306d
Packit Service 31306d
This chapter shows how to open one of these channels, and how to use it to
Packit Service 31306d
start a command interpreter on a remote computer.
Packit Service 31306d
Packit Service 31306d
Packit Service 31306d
@subsection open_channel Opening and closing a channel
Packit Service 31306d
Packit Service 31306d
The ssh_channel_new() function creates a channel. It returns the channel as
Packit Service 31306d
a variable of type ssh_channel.
Packit Service 31306d
Packit Service 31306d
Once you have this channel, you open a SSH session that uses it with
Packit Service 31306d
ssh_channel_open_session().
Packit Service 31306d
Packit Service 31306d
Once you don't need the channel anymore, you can send an end-of-file
Packit Service 31306d
to it with ssh_channel_close(). At this point, you can destroy the channel
Packit Service 31306d
with ssh_channel_free().
Packit Service 31306d
Packit Service 31306d
The code sample below achieves these tasks:
Packit Service 31306d
Packit Service 31306d
@code
Packit Service 31306d
int shell_session(ssh_session session)
Packit Service 31306d
{
Packit Service 31306d
  ssh_channel channel;
Packit Service 31306d
  int rc;
Packit Service 31306d
Packit Service 31306d
  channel = ssh_channel_new(session);
Packit Service 31306d
  if (channel == NULL)
Packit Service 31306d
    return SSH_ERROR;
Packit Service 31306d
Packit Service 31306d
  rc = ssh_channel_open_session(channel);
Packit Service 31306d
  if (rc != SSH_OK)
Packit Service 31306d
  {
Packit Service 31306d
    ssh_channel_free(channel);
Packit Service 31306d
    return rc;
Packit Service 31306d
  }
Packit Service 31306d
Packit Service 31306d
  ...
Packit Service 31306d
Packit Service 31306d
  ssh_channel_close(channel);
Packit Service 31306d
  ssh_channel_send_eof(channel);
Packit Service 31306d
  ssh_channel_free(channel);
Packit Service 31306d
Packit Service 31306d
  return SSH_OK;
Packit Service 31306d
}
Packit Service 31306d
@endcode
Packit Service 31306d
Packit Service 31306d
Packit Service 31306d
@subsection interactive Interactive and non-interactive sessions
Packit Service 31306d
Packit Service 31306d
A "shell" is a command interpreter. It is said to be "interactive"
Packit Service 31306d
if there is a human user typing the commands, one after the
Packit Service 31306d
other. The contrary, a non-interactive shell, is similar to
Packit Service 31306d
the execution of commands in the background: there is no attached
Packit Service 31306d
terminal.
Packit Service 31306d
Packit Service 31306d
If you plan using an interactive shell, you need to create a
Packit Service 31306d
pseud-terminal on the remote side. A remote terminal is usually referred
Packit Service 31306d
to as a "pty", for "pseudo-teletype". The remote processes won't see the
Packit Service 31306d
difference with a real text-oriented terminal.
Packit Service 31306d
Packit Service 31306d
If needed, you request the pty with the function ssh_channel_request_pty().
Packit Service 31306d
Then you define its dimensions (number of rows and columns)
Packit Service 31306d
with ssh_channel_change_pty_size().
Packit Service 31306d
Packit Service 31306d
Be your session interactive or not, the next step is to request a
Packit Service 31306d
shell with ssh_channel_request_shell().
Packit Service 31306d
Packit Service 31306d
@code
Packit Service 31306d
int interactive_shell_session(ssh_channel channel)
Packit Service 31306d
{
Packit Service 31306d
  int rc;
Packit Service 31306d
Packit Service 31306d
  rc = ssh_channel_request_pty(channel);
Packit Service 31306d
  if (rc != SSH_OK) return rc;
Packit Service 31306d
Packit Service 31306d
  rc = ssh_channel_change_pty_size(channel, 80, 24);
Packit Service 31306d
  if (rc != SSH_OK) return rc;
Packit Service 31306d
Packit Service 31306d
  rc = ssh_channel_request_shell(channel);
Packit Service 31306d
  if (rc != SSH_OK) return rc;
Packit Service 31306d
Packit Service 31306d
  ...
Packit Service 31306d
Packit Service 31306d
  return rc;
Packit Service 31306d
}
Packit Service 31306d
@endcode
Packit Service 31306d
Packit Service 31306d
Packit Service 31306d
@subsection read_data Displaying the data sent by the remote computer
Packit Service 31306d
Packit Service 31306d
In your program, you will usually need to receive all the data "displayed"
Packit Service 31306d
into the remote pty. You will usually analyse, log, or display this data.
Packit Service 31306d
Packit Service 31306d
ssh_channel_read() and ssh_channel_read_nonblocking() are the simplest
Packit Service 31306d
way to read data from a channel. If you only need to read from a single
Packit Service 31306d
channel, they should be enough.
Packit Service 31306d
Packit Service 31306d
The example below shows how to wait for remote data using ssh_channel_read():
Packit Service 31306d
Packit Service 31306d
@code
Packit Service 31306d
int interactive_shell_session(ssh_channel channel)
Packit Service 31306d
{
Packit Service 31306d
  int rc;
Packit Service 31306d
  char buffer[256];
Packit Service 31306d
  int nbytes;
Packit Service 31306d
Packit Service 31306d
  rc = ssh_channel_request_pty(channel);
Packit Service 31306d
  if (rc != SSH_OK) return rc;
Packit Service 31306d
Packit Service 31306d
  rc = ssh_channel_change_pty_size(channel, 80, 24);
Packit Service 31306d
  if (rc != SSH_OK) return rc;
Packit Service 31306d
Packit Service 31306d
  rc = ssh_channel_request_shell(channel);
Packit Service 31306d
  if (rc != SSH_OK) return rc;
Packit Service 31306d
Packit Service 31306d
  while (ssh_channel_is_open(channel) &&
Packit Service 31306d
         !ssh_channel_is_eof(channel))
Packit Service 31306d
  {
Packit Service 31306d
    nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
Packit Service 31306d
    if (nbytes < 0)
Packit Service 31306d
      return SSH_ERROR;
Packit Service 31306d
Packit Service 31306d
    if (nbytes > 0)
Packit Service 31306d
      write(1, buffer, nbytes);
Packit Service 31306d
  }
Packit Service 31306d
Packit Service 31306d
  return rc;
Packit Service 31306d
}
Packit Service 31306d
@endcode
Packit Service 31306d
Packit Service 31306d
Unlike ssh_channel_read(), ssh_channel_read_nonblocking() never waits for
Packit Service 31306d
remote data to be ready. It returns immediately.
Packit Service 31306d
Packit Service 31306d
If you plan to use ssh_channel_read_nonblocking() repeatedly in a loop,
Packit Service 31306d
you should use a "passive wait" function like usleep(3) in the same
Packit Service 31306d
loop. Otherwise, your program will consume all the CPU time, and your
Packit Service 31306d
computer might become unresponsive.
Packit Service 31306d
Packit Service 31306d
Packit Service 31306d
@subsection write_data Sending user input to the remote computer
Packit Service 31306d
Packit Service 31306d
User's input is sent to the remote site with ssh_channel_write().
Packit Service 31306d
Packit Service 31306d
The following example shows how to combine a nonblocking read from a SSH
Packit Service 31306d
channel with a nonblocking read from the keyboard. The local input is then
Packit Service 31306d
sent to the remote computer:
Packit Service 31306d
Packit Service 31306d
@code
Packit Service 31306d
/* Under Linux, this function determines whether a key has been pressed.
Packit Service 31306d
   Under Windows, it is a standard function, so you need not redefine it.
Packit Service 31306d
*/
Packit Service 31306d
int kbhit()
Packit Service 31306d
{
Packit Service 31306d
    struct timeval tv = { 0L, 0L };
Packit Service 31306d
    fd_set fds;
Packit Service 31306d
Packit Service 31306d
    FD_ZERO(&fds);
Packit Service 31306d
    FD_SET(0, &fds);
Packit Service 31306d
Packit Service 31306d
    return select(1, &fds, NULL, NULL, &tv;;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
/* A very simple terminal emulator:
Packit Service 31306d
   - print data received from the remote computer
Packit Service 31306d
   - send keyboard input to the remote computer
Packit Service 31306d
*/
Packit Service 31306d
int interactive_shell_session(ssh_channel channel)
Packit Service 31306d
{
Packit Service 31306d
  /* Session and terminal initialization skipped */
Packit Service 31306d
  ...
Packit Service 31306d
Packit Service 31306d
  char buffer[256];
Packit Service 31306d
  int nbytes, nwritten;
Packit Service 31306d
Packit Service 31306d
  while (ssh_channel_is_open(channel) &&
Packit Service 31306d
         !ssh_channel_is_eof(channel))
Packit Service 31306d
  {
Packit Service 31306d
    nbytes = ssh_channel_read_nonblocking(channel, buffer, sizeof(buffer), 0);
Packit Service 31306d
    if (nbytes < 0) return SSH_ERROR;
Packit Service 31306d
    if (nbytes > 0)
Packit Service 31306d
    {
Packit Service 31306d
      nwritten = write(1, buffer, nbytes);
Packit Service 31306d
      if (nwritten != nbytes) return SSH_ERROR;
Packit Service 31306d
Packit Service 31306d
    if (!kbhit())
Packit Service 31306d
    {
Packit Service 31306d
      usleep(50000L); // 0.05 second
Packit Service 31306d
      continue;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    nbytes = read(0, buffer, sizeof(buffer));
Packit Service 31306d
    if (nbytes < 0) return SSH_ERROR;
Packit Service 31306d
    if (nbytes > 0)
Packit Service 31306d
    {
Packit Service 31306d
      nwritten = ssh_channel_write(channel, buffer, nbytes);
Packit Service 31306d
      if (nwritten != nbytes) return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
  }
Packit Service 31306d
Packit Service 31306d
  return rc;
Packit Service 31306d
}
Packit Service 31306d
@endcode
Packit Service 31306d
Packit Service 31306d
Of course, this is a poor terminal emulator, since the echo from the keys
Packit Service 31306d
pressed should not be done locally, but should be done by the remote side.
Packit Service 31306d
Also, user's input should not be sent once "Enter" key is pressed, but
Packit Service 31306d
immediately after each key is pressed. This can be accomplished
Packit Service 31306d
by setting the local terminal to "raw" mode with the cfmakeraw(3) function.
Packit Service 31306d
cfmakeraw() is a standard function under Linux, on other systems you can
Packit Service 31306d
recode it with:
Packit Service 31306d
Packit Service 31306d
@code
Packit Service 31306d
static void cfmakeraw(struct termios *termios_p)
Packit Service 31306d
{
Packit Service 31306d
    termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
Packit Service 31306d
    termios_p->c_oflag &= ~OPOST;
Packit Service 31306d
    termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
Packit Service 31306d
    termios_p->c_cflag &= ~(CSIZE|PARENB);
Packit Service 31306d
    termios_p->c_cflag |= CS8;
Packit Service 31306d
}
Packit Service 31306d
@endcode
Packit Service 31306d
Packit Service 31306d
If you are not using a local terminal, but some kind of graphical
Packit Service 31306d
environment, the solution to this kind of "echo" problems will be different.
Packit Service 31306d
Packit Service 31306d
Packit Service 31306d
@subsection select_loop A more elaborate way to get the remote data
Packit Service 31306d
Packit Service 31306d
*** Warning: ssh_select() and ssh_channel_select() are not relevant anymore,
Packit Service 31306d
    since libssh is about to provide an easier system for asynchronous
Packit Service 31306d
    communications. This subsection should be removed then. ***
Packit Service 31306d
Packit Service 31306d
ssh_channel_read() and ssh_channel_read_nonblocking() functions are simple,
Packit Service 31306d
but they are not adapted when you expect data from more than one SSH channel,
Packit Service 31306d
or from other file descriptors. Last example showed how getting data from
Packit Service 31306d
the standard input (the keyboard) at the same time as data from the SSH
Packit Service 31306d
channel was complicated. The functions ssh_select() and ssh_channel_select()
Packit Service 31306d
provide a more elegant way to wait for data coming from many sources.
Packit Service 31306d
Packit Service 31306d
The functions ssh_select() and ssh_channel_select() remind of the standard
Packit Service 31306d
UNIX select(2) function. The idea is to wait for "something" to happen:
Packit Service 31306d
incoming data to be read, outgoing data to block, or an exception to
Packit Service 31306d
occur. Both these functions do a "passive wait", i.e. you can safely use
Packit Service 31306d
them repeatedly in a loop, it will not consume exaggerate processor time
Packit Service 31306d
and make your computer unresponsive. It is quite common to use these
Packit Service 31306d
functions in your application's main loop.
Packit Service 31306d
Packit Service 31306d
The difference between ssh_select() and ssh_channel_select() is that
Packit Service 31306d
ssh_channel_select() is simpler, but allows you only to watch SSH channels.
Packit Service 31306d
ssh_select() is more complete and enables watching regular file descriptors
Packit Service 31306d
as well, in the same function call.
Packit Service 31306d
Packit Service 31306d
Below is an example of a function that waits both for remote SSH data to come,
Packit Service 31306d
as well as standard input from the keyboard:
Packit Service 31306d
Packit Service 31306d
@code
Packit Service 31306d
int interactive_shell_session(ssh_session session, ssh_channel channel)
Packit Service 31306d
{
Packit Service 31306d
  /* Session and terminal initialization skipped */
Packit Service 31306d
  ...
Packit Service 31306d
Packit Service 31306d
  char buffer[256];
Packit Service 31306d
  int nbytes, nwritten;
Packit Service 31306d
Packit Service 31306d
  while (ssh_channel_is_open(channel) &&
Packit Service 31306d
         !ssh_channel_is_eof(channel))
Packit Service 31306d
  {
Packit Service 31306d
    struct timeval timeout;
Packit Service 31306d
    ssh_channel in_channels[2], out_channels[2];
Packit Service 31306d
    fd_set fds;
Packit Service 31306d
    int maxfd;
Packit Service 31306d
Packit Service 31306d
    timeout.tv_sec = 30;
Packit Service 31306d
    timeout.tv_usec = 0;
Packit Service 31306d
    in_channels[0] = channel;
Packit Service 31306d
    in_channels[1] = NULL;
Packit Service 31306d
    FD_ZERO(&fds);
Packit Service 31306d
    FD_SET(0, &fds);
Packit Service 31306d
    FD_SET(ssh_get_fd(session), &fds);
Packit Service 31306d
    maxfd = ssh_get_fd(session) + 1;
Packit Service 31306d
Packit Service 31306d
    ssh_select(in_channels, out_channels, maxfd, &fds, &timeout);
Packit Service 31306d
Packit Service 31306d
    if (out_channels[0] != NULL)
Packit Service 31306d
    {
Packit Service 31306d
      nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
Packit Service 31306d
      if (nbytes < 0) return SSH_ERROR;
Packit Service 31306d
      if (nbytes > 0)
Packit Service 31306d
      {
Packit Service 31306d
        nwritten = write(1, buffer, nbytes);
Packit Service 31306d
        if (nwritten != nbytes) return SSH_ERROR;
Packit Service 31306d
      }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (FD_ISSET(0, &fds))
Packit Service 31306d
    {
Packit Service 31306d
      nbytes = read(0, buffer, sizeof(buffer));
Packit Service 31306d
      if (nbytes < 0) return SSH_ERROR;
Packit Service 31306d
      if (nbytes > 0)
Packit Service 31306d
      {
Packit Service 31306d
        nwritten = ssh_channel_write(channel, buffer, nbytes);
Packit Service 31306d
        if (nbytes != nwritten) return SSH_ERROR;
Packit Service 31306d
      }
Packit Service 31306d
    }
Packit Service 31306d
  }
Packit Service 31306d
Packit Service 31306d
  return rc;
Packit Service 31306d
}
Packit Service 31306d
@endcode
Packit Service 31306d
Packit Service 31306d
Packit Service 31306d
@subsection x11 Using graphical applications on the remote side
Packit Service 31306d
Packit Service 31306d
If your remote application is graphical, you can forward the X11 protocol to
Packit Service 31306d
your local computer.
Packit Service 31306d
Packit Service 31306d
To do that, you first declare that you accept X11 connections with
Packit Service 31306d
ssh_channel_accept_x11(). Then you create the forwarding tunnel for
Packit Service 31306d
the X11 protocol with ssh_channel_request_x11().
Packit Service 31306d
Packit Service 31306d
The following code performs channel initialization and shell session
Packit Service 31306d
opening, and handles a parallel X11 connection:
Packit Service 31306d
Packit Service 31306d
@code
Packit Service 31306d
int interactive_shell_session(ssh_channel channel)
Packit Service 31306d
{
Packit Service 31306d
  int rc;
Packit Service 31306d
  ssh_channel x11channel;
Packit Service 31306d
Packit Service 31306d
  rc = ssh_channel_request_pty(channel);
Packit Service 31306d
  if (rc != SSH_OK) return rc;
Packit Service 31306d
Packit Service 31306d
  rc = ssh_channel_change_pty_size(channel, 80, 24);
Packit Service 31306d
  if (rc != SSH_OK) return rc;
Packit Service 31306d
Packit Service 31306d
  rc = ssh_channel_request_x11(channel, 0, NULL, NULL, 0);
Packit Service 31306d
  if (rc != SSH_OK) return rc;
Packit Service 31306d
Packit Service 31306d
  rc = ssh_channel_request_shell(channel);
Packit Service 31306d
  if (rc != SSH_OK) return rc;
Packit Service 31306d
Packit Service 31306d
  /* Read the data sent by the remote computer here */
Packit Service 31306d
  ...
Packit Service 31306d
}
Packit Service 31306d
@endcode
Packit Service 31306d
Packit Service 31306d
Don't forget to set the $DISPLAY environment variable on the remote
Packit Service 31306d
side, or the remote applications won't try using the X11 tunnel:
Packit Service 31306d
Packit Service 31306d
@code
Packit Service 31306d
$ export DISPLAY=:0
Packit Service 31306d
$ xclock &
Packit Service 31306d
@endcode
Packit Service 31306d
Packit Service 31306d
*/