Logo Search packages:      
Sourcecode: sbrsh version File versions  Download package

client.c

/*
 * Copyright (c) 2003, 2004, 2005 Nokia
 * Author: Timo Savola <tsavola@movial.fi>
 *
 * This program is licensed under GPL (see COPYING for details)
 */

#include "client.h"
#include "common.h"
#include "protocol.h"
#include "buffer.h"
#include "config.h"
#include "mount.h"
#include "version.h"

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#include <termios.h>
#include <signal.h>
#include <getopt.h>
#include <time.h>
#include <pwd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>


#define REQUIRED_VERSION 6

extern char **environ;

/** The name of this program. */
static char *progname;

/** Path of the config file (command line option). */
static char *configpath = NULL;

/** The command line arguments. */
static char *target = NULL;
static char *remote_user = NULL;
static char *cwd = NULL;
static char **args = NULL;
static int action = PTYPE_COMMAND;

/** The socket fd. */
static int sd = -1;

/** Buffers used to copy data around. */
static buffer_t *buf_out;
static buffer_t *buf_err;
static char *tmp_buf;

/** Is the tty in raw mode? */
static bool_t rawmode = FALSE;

/** The original mode of the tty. */
static struct termios oldtio;

/** How much IN DATA has the server requested? */
static size_t inreq = 0;

/** Have we requested some data? */
static bool_t outwait = FALSE;
static bool_t errwait = FALSE;

/** The protocol version of the daemon. */
static int daemon_version = 0;

#ifdef DEBUG
static void debug(const char *msg, ...)
{
      va_list arg;

      fprintf(stderr, "%s (%d) debug: ", progname, getpid());

      va_start(arg, msg);
      vfprintf(stderr, msg, arg);
      va_end(arg);

      fprintf(stderr, "\n");
      fflush(stderr);
}
#else
# define debug(msg, ...)
#endif

/*
 * Prints progname, message and errno description to stderr.
 */
void error(const char *msg, ...)
{
      char *desc = NULL;
      va_list arg;

      if (errno > 0)
            desc = strerror(errno);

#ifdef DEBUG
      fprintf(stderr, "%s (%d): ", progname, getpid());
#else
      fprintf(stderr, "%s: ", progname);
#endif

      va_start(arg, msg);
      vfprintf(stderr, msg, arg);
      va_end(arg);

      if (desc)
            fprintf(stderr, " (%s)", desc);

      fprintf(stderr, "\n");
      fflush(stderr);
}

/**
 * Writes (some of) buffer to a file.
 * @param buf the buffer
 * @param fd the target file descriptor
 * @param wait is set to false when the buffer is empty
 * @return 0 on success, -1 on error
 */
static int write_buffer(buffer_t *buf, int fd, bool_t *wait)
{
      /* buf is never at EOF so fd never becomes -1. */
      if (buf_write_out(buf, &fd) < 0) {
            error(fd == STDOUT_FILENO ?
                  "Can't write buffer to stdout" :
                  "Can't write buffer to stderr");
            return -1;
      }

      if (buf_is_empty(buf))
            *wait = FALSE;

      return 0;
}

/**
 * Sends an IN DATA packet.
 * @return 0 usually, 1 if stdin reached EOF, -1 on error
 */
static int send_data(void)
{
      bool_t ok = FALSE;
      ssize_t len = BUFFER_SIZE;

      /*
       * Get the amount of data we can read from a terminal.
       */
      if (isatty(STDIN_FILENO)) {
            if (ioctl(STDIN_FILENO, FIONREAD, &len) < 0) {
                  error("Can't check tty for available data");
                  return -1;
            }

            if (len < 0) {
                  error("ioctl(tty) gave invalid read length");
                  return -1;
            }

            if (len == 0)
                  return 0;

            if (len > BUFFER_SIZE)
                  len = BUFFER_SIZE;
      }

      len = read(STDIN_FILENO, tmp_buf, len);
      if (len < 0) {
            error("Can't read from stdin");
            return -1;
      }

      if (len == 0) {
            debug("Stdin hit EOF");
            ok = 1;
            inreq = 0;
      }

      if (write_buf_packet(sd, PTYPE_IN_DATA, len, tmp_buf) < 0) {
            error("Can't write IN DATA packet to socket");
            return -1;
      }

      /* At EOF: 0-0=0 */
      inreq -= len;

      return ok;
}

/**
 * Reads bytes from socket to a buffer.
 * @param buf the target buffer
 * @return 0 on success, -1 on error
 */
static int receive_stream(buffer_t *buf)
{
      uint32_t len;
      if (read_uint32(sd, &len) < 0) {
            error("Unable to read data packet length");
            return -1;
      }

      if (len == 0) {
            errno = 0;
            error("Received empty data packet (EOF)");
            return -1;
      }

      if (buf_read_in(buf, sd, len) < 0) {
            error("Can't append data packet to buffer");
            return -1;
      }

      return 0;
}

/**
 * Reads message from socket and prints it.
 * @param type "error" or "warning"
 * @return 0 on success, -1 on error
 */
static int receive_message(ptype_t type)
{
      uint32_t len;
      if (read_uint32(sd, &len) < 0) {
            error("Unable to read message packet length");
            return -1;
      }

      if (read_buf(sd, tmp_buf, len) < 0) {
            error("Can't read message packet");
            return -1;
      }

      if (len == BUFFER_SIZE)
            len = BUFFER_SIZE - 1;

      tmp_buf[len] = '\0';

#ifdef DEBUG
      if (type == PTYPE_ERROR)
            fprintf(stderr, "%s (%d) server: %s\n", progname, getpid(), tmp_buf);
      else
            fprintf(stderr, "%s (%d): %s\n", progname, getpid(), tmp_buf);
#else
      if (type == PTYPE_ERROR)
            fprintf(stderr, "%s server: %s\n", progname, tmp_buf);
      else
            fprintf(stderr, "%s: %s\n", progname, tmp_buf);
#endif

      return 0;
}

/**
 * Reads a packet from the socket (sd) and does something about it.
 * The value of an RC packet is stored in the global rc variable.
 * @return 1 on RC, 0 on other valid packet type, -1 on error
 */
static int receive_packet(uint16_t *rc)
{
      ptype_t type = read_enum(sd);
      switch (type) {
      case -1:
            error("Can't read packet type from socket");
            return -1;

      case PTYPE_IN_REQ:
            inreq = BUFFER_SIZE;
            return 0;

      case PTYPE_OUT_DATA:
            return receive_stream(buf_out);

      case PTYPE_ERR_DATA:
            return receive_stream(buf_err);

      case PTYPE_RC:
            debug("Receiving RC packet");
            if (read_uint16(sd, rc) < 0) {
                  error("Can't read RC packet from socket");
                  return -1;
            }
            return 1;

      case PTYPE_MESSAGE:
            return receive_message(PTYPE_MESSAGE);

      case PTYPE_ERROR:
            return receive_message(PTYPE_ERROR);

      default:
            errno = 0;
            error("Received packet has unexpected type (0x%02x)", type);
            return -1;
      }
}

/**
 * Sends a request for more data.
 * @param ptype PTYPE_OUT_REQ or PTYPE_ERR_REQ
 * @param wait is set to true
 * @return 0 on success, -1 on error
 */
static int send_request(ptype_t ptype, bool_t *wait)
{
      if (write_enum(sd, ptype) < 0) {
            error("Can't write packet to socket");
            return -1;
      }

      *wait = TRUE;

      return 0;
}

/**
 * Manages stdin/stdout/stderr and waits for the return code.
 * @return -1 on error, return code on success
 */
static int manage(bool_t is_tty)
{
      fd_set readfds, writefds;
      bool_t inopen;
      int ok;

      /* Check if stdin is really available */
      if (is_tty || read(STDIN_FILENO,NULL,0)!=-1) {
            inopen = TRUE;
      } else {
            inopen = FALSE;
      }

      while (1) {
            FD_ZERO(&readfds);
            FD_ZERO(&writefds);

            FD_SET(sd, &readfds);

            if (inopen && inreq)
                  FD_SET(STDIN_FILENO, &readfds);

            if (!buf_is_empty(buf_out) || !outwait)
                  FD_SET(STDOUT_FILENO, &writefds);

            if (!is_tty && (!buf_is_empty(buf_err) || !errwait))
                  FD_SET(STDERR_FILENO, &writefds);

            if (select(sd + 1, &readfds, &writefds, NULL, NULL) <= 0) {
                  error("Can't select");
                  return -1;
            }

            /* read packet from socket */
            if (FD_ISSET(sd, &readfds)) {
                  uint16_t rc;

                  ok = receive_packet(&rc);
                  if (ok < 0)
                        return -1;

                  if (ok > 0)
                        return rc;
            }

            /* send data from stdin if requested */
            if (inopen && inreq && FD_ISSET(STDIN_FILENO, &readfds)) {
                  ok = send_data();
                  if (ok < 0)
                        return -1;
                  if (ok > 0)
                        inopen = FALSE;
            }

            /* flush buf_out to stdout or request more data */
            if (FD_ISSET(STDOUT_FILENO, &writefds)) {
                  if (buf_is_empty(buf_out))
                        ok = outwait || send_request(PTYPE_OUT_REQ, &outwait) >= 0;
                  else
                        ok = write_buffer(buf_out, STDOUT_FILENO, &outwait) >= 0;

                  if (!ok)
                        return -1;
            }

            /* flush buf_err to stderr or request more data */
            if (FD_ISSET(STDERR_FILENO, &writefds)) {
                  if (buf_is_empty(buf_err))
                        ok = errwait || send_request(PTYPE_ERR_REQ, &errwait) >= 0;
                  else
                        ok = write_buffer(buf_err, STDERR_FILENO, &errwait) >= 0;

                  if (!ok)
                        return -1;
            }
      }

      /* Not reached. */
      return -1;
}

/**
 * Sets stdin to raw mode.
 */
static int set_raw_mode(void)
{
      struct termios tio;

      if (tcgetattr(STDIN_FILENO, &tio) < 0) {
            error("Can't get termios");
            return -1;
      }

      oldtio = tio;

      tio.c_iflag |= IGNPAR;
      tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY);
      tio.c_lflag &= ~(IXOFF | ISIG | ICANON | ECHO | ECHOE | ECHOK);
      tio.c_lflag &= ~(ECHONL | IEXTEN | OPOST);
      tio.c_cc[VMIN] = 1;
      tio.c_cc[VTIME] = 0;

      debug("Entering raw mode");

      if (tcsetattr(STDIN_FILENO, TCSADRAIN, &tio) < 0) {
            error("Can't change termios");
            return -1;
      }

      rawmode = TRUE;

      return 0;
}

/**
 * Sets stdin in its original mode.
 */
static void set_old_mode(void)
{
      if (!rawmode) {
            debug("Not in raw mode");
            return;
      }

      debug("Leaving raw mode");

      if (tcsetattr(STDIN_FILENO, TCSADRAIN, &oldtio) < 0)
            error("Can't restore original termios");
}

static void usage(void)
{
      fprintf(stderr, "Usage: %s [-t|--target <target>]"
                         " [-c|--config <path>]"
                         " [-d|--directory <dir>]"
                         " [-r|--remote <uid>,<gid>,<username>]"
                         " [<command>]"
                         " [<args>]\n"
                  "       %s [-t|--target <target>]"
                         " [-c|--config <path>]"
                         " --mount\n"
                  "       %s [-t|--target <target>]"
                         " [-c|--config <path>]"
                         " --umount\n"
                  "       %s -v|--version\n"
                  "       %s -h|--help\n",
            progname, progname, progname, progname, progname);
}

static bool_t should_skip_parameter(char *arg, struct option *longopts)
{
      bool_t is_long;
      struct option *opt;

      is_long = strlen(arg) > 2;

      /* check if arg also has its value */
      if (is_long && strchr(arg, '=') != NULL)
            return FALSE;

      for (opt = longopts; opt->name; ++opt) {
            if (is_long) {
                  if (strcmp(&arg[2], opt->name) != 0)
                        continue;
            } else {
                  if (arg[1] != opt->val)
                        continue;
            }

            return opt->has_arg == required_argument;
      }

      return FALSE;
}

static char **modify_args(int old_argc, char **old_argv, int *new_argc, struct option *longopts)
{
      char **new_argv;
      int i = 0;

      new_argv = calloc(old_argc + 2, sizeof (char *));
      if (!new_argv) {
            oom_error();
            exit(1);
      }

      /* sbrsh's progname */
      new_argv[i++] = *old_argv++;

      if (old_argc > 1) {
            /* sbrsh's options and -- */
            while (i < old_argc) {
                  char *arg = *old_argv++;

                  if (arg[0] != '-') {
                        new_argv[i++] = "--";
                        new_argv[i++] = arg;
                        break;
                  }

                  new_argv[i++] = arg;

                  if (should_skip_parameter(arg, longopts) && i < old_argc)
                        new_argv[i++] = *old_argv++;
            }

            /* command and its arguments */
            while (*old_argv)
                  new_argv[i++] = *old_argv++;
      }

      *new_argc = i;
      return new_argv;
}

/**
 * Fills in the options from argv. Prints usage and exits on error.
 * @return TRUE if we should --umount-all and not run a command
 */
static void read_args(int orig_argc, char **orig_argv)
{
      char **argv;
      int argc;
      int skip_args=0;

      struct option longopts[] = {
            { "help",       no_argument,       0, 'h' },
            { "version",    no_argument,       0, 'v' },
            { "sbox-call",    no_argument,     &skip_args, 1 },
            { "target",     required_argument, 0, 't' },
            { "config",     required_argument, 0, 'c' },
            { "directory",  required_argument, 0, 'd' },
            { "remote",     required_argument, 0, 'r' },
            { "mount",      no_argument,       &action, PTYPE_MOUNT  },
            { "umount",     no_argument,       &action, PTYPE_UMOUNT },
            { 0 }
      };

      argv = modify_args(orig_argc, orig_argv, &argc, longopts);

      while (1) {
            int c;

            c = getopt_long(argc, argv, "hvt:c:d:r:", longopts, NULL);
            if (c < 0) {
                  break;
            }

            switch (c) {
            case 't':
                  target = optarg;
                  break;

            case 'c':
                  configpath = optarg;
                  break;

            case 'd':
                  cwd = optarg;
                  break;

            case 'r':
                  remote_user = optarg;
                  break;

            case 0:
                  /* --mount or --umount */
                  break;

            case 'v':
                  fprintf(stderr, "Scratchbox Remote Shell client %d%s\n",
                        PROTOCOL_VERSION, REVISION);
                  exit(0);

            case 'h':
                  usage();
                  exit(0);

            default:
                  usage();
                  exit(1);
            }
      }

      if (argv[optind] && argv[optind+skip_args])
            args = &argv[optind+skip_args];
}

/**
 * Called when the process exits.
 */
static void cleanup(void)
{
      /* see that stdout and stderr buffers are flushed */

      if (!buf_is_empty(buf_out)) {
            set_nonblocking(STDOUT_FILENO, FALSE);
            write_buffer(buf_out, STDOUT_FILENO, &outwait);
      }

      if (!buf_is_empty(buf_err)) {
            set_nonblocking(STDERR_FILENO, FALSE);
            write_buffer(buf_err, STDERR_FILENO, &errwait);
      }

      set_old_mode();

      debug("sbrsh exiting");
}

static int determine_user_info(uid_t *uidp, gid_t *gidp, char *username)
{
      uid_t uid;
      gid_t gid;

      if (remote_user != NULL) {
            /*
             * Parse the list for uid, gid, and username (string placed at
             * end for simplicity)
             */
            if (sscanf(remote_user, "%d,%d,%s", &uid, &gid, username) != 3) {
                  error("Invalid format for -r argument: %s", remote_user);
                  return 1;
            }

      } else {
            char *str;

            uid = geteuid();
            gid = getegid();

            struct passwd *userstruct = getpwuid(uid);
            if (!userstruct) {
                  error("Can't get user information about uid %d", uid);
                  return 1;
            }

            strcpy(username, userstruct->pw_name);

            /*
             * Check for environment overrides
             */
            str = getenv("_SBOX_NONFAKE_USER");
            if (str) {
                  strcpy(username, str);
            }

            str = getenv("_SBOX_NONFAKE_UID");
            if (str && (uid = atoi(str)) <= 0) {
                  error("Invalid _SBOX_NONFAKE_UID: %d", uid);
                  return 1;
            }

            str = getenv("_SBOX_NONFAKE_GID");
            if (str && (gid = atoi(str)) <= 0) {
                  error("Invalid _SBOX_NONFAKE_GID: %d", gid);
                  return 1;
            }
      }

      *uidp = uid;
      *gidp = gid;

      return 0;
}

/*
 * Reads options, resolves and connects to host, talks with it, returns rc.
 */
int main(int argc, char **argv)
{
      config_t *cfg;
      mount_info_t **mounts;
      int16_t rc;
      uid_t uid = 0;
      gid_t gid = 0;
      char user[NAME_MAX];

      progname = get_progname(argv[0]);

      debug("sbrsh version %d%s", PROTOCOL_VERSION, REVISION);

      /*
       * Allocate buffers
       */

      if (!(cfg     = config_alloc()) ||
          !(buf_out = buf_alloc())    ||
          !(buf_err = buf_alloc())    ||
          !(tmp_buf = malloc(BUFFER_SIZE))) {
            oom_error();
            return 1;
      }

      /*
       * Clean exit
       */
      {
            struct sigaction act;

            act.sa_handler = exit;
            sigemptyset(&act.sa_mask);
            act.sa_flags = SA_ONESHOT;

            sigaction(SIGINT, &act, NULL);
            sigaction(SIGHUP, &act, NULL);
            sigaction(SIGTERM, &act, NULL);

            atexit(cleanup);
      }

      /*
       * Parse arguments
       */

      read_args(argc, argv);

      /*
       * Read configuration file
       */
      {
            char *home, *path = NULL;

            if (!configpath) {
                  home = getenv("HOME");
                  path = malloc(strlen(home) + strlen("/" CONFIG_NAME) + 1);
                  if (!path) {
                        oom_error();
                        return 1;
                  }
                  strcpy(path, home);
                  strcat(path, "/" CONFIG_NAME);

                  configpath = path;
            }

            if (!config_read(cfg, configpath, target)) {
                  if (target)
                        error("Target %s not found in %s", target, configpath);
                  else
                        error("No targets found in %s", configpath);
                  return 1;
            }

            if (path) {
                  free(path);
                  configpath = NULL;
            }
      }

      /*
       * Parse and do stuff with mount entries
       */
      {
            size_t len, i;

            len = calc_vec_len((void **) cfg->opts);

            mounts = calloc(len + 1, sizeof (mount_info_t *));
            if (!mounts) {
                  oom_error();
                  return 1;
            }

            for (i = 0; i < len; ++i) {
                  mount_info_t *mi = mntinfo_parse(cfg->opts[i]);
                  if (!mi)
                        return 1;

                  debug("type=%d device=%s point=%s opts=%s",
                        mi->type, mi->device, mi->point, mi->opts);

                  if (action == PTYPE_COMMAND && (MTYPE_NFS == mi->type ||
                                                  MTYPE_SSH == mi->type)) {
                        if (mntinfo_stat_device(mi) < 0)
                              return 1;

                        debug("device_dev=%lld", mi->device_dev);
                  }

                  mounts[i] = mi;
            }
      }

      /*
       * Connect to server
       */
      {
            struct addrinfo *ai, *i, hints = { 0 };
            char *port = DEFAULT_PORT;
            int ret;

            hints.ai_flags = AI_ADDRCONFIG;
            hints.ai_socktype = SOCK_STREAM;

            if (cfg->port)
                  port = cfg->port;

            ret = getaddrinfo(cfg->host, port, &hints, &ai);
            if (ret < 0) {
                  error("Can't resolve host: %s", gai_strerror(ret));
                  return 1;
            }

            for (i = ai; i; i = i->ai_next) {
                  sd = socket(i->ai_family, i->ai_socktype,
                              i->ai_protocol);
                  if (sd < 0)
                        continue;

                  if (connect(sd, i->ai_addr, i->ai_addrlen) == 0)
                        break;

                  close(sd);
                  sd = -1;
            }

            if (sd < 0) {
                  error("Can't connect");
                  return 1;
            }

            freeaddrinfo(ai);
      }

      /*
       * Version
       */

      if (send_version(sd) < 0) {
            error("Can't write protocol version packet to socket");
            return 1;
      }

      daemon_version = get_version(sd);
      if (daemon_version < 0) {
            error("Can't read protocol version packet from socket");
            return 1;
      }

      if (daemon_version < REQUIRED_VERSION) {
            errno = 0;
            error("Server version %d is too old (version %d required)",
                  daemon_version, REQUIRED_VERSION);
            return 1;
      }

      /*
       * Find out remote user ID, group ID, and username
       */
      if (determine_user_info(&uid, &gid, user)) {
            return 1;
      }

      /*
       * Send user name
       */
      {

            debug("Sending USER packet: %s", user);
            if (write_str_packet(sd, PTYPE_USER, user) < 0) {
                  error("Can't send USER packet");
                  return 1;
            }
      }

      /*
       * Read authentication reply or error messages
       */
      while (1) {
            uint16_t val;
            ptype_t type = read_enum(sd);
            switch (type) {
                  uint16_t auth;

            case -1:
                  error("Can't read packet type from socket");
                  return 1;

            case PTYPE_AUTH:
                  debug("Receiving AUTH packet");
                  if (read_uint16(sd, &auth) < 0) {
                        error("Can't read AUTH packet from socket");
                        return 1;
                  }
                  if (auth) {
                        debug("Authentication ok");
                        break;
                  } else {
                        errno = 0;
                        error("Authentication failed");
                        return 1;
                  }

            case PTYPE_RC:
                  debug("Receiving RC packet");
                  if (read_uint16(sd, &val) < 0) {
                        error("Can't read RC packet from socket");
                        return 1;
                  }
                  rc = val;
                  break;

            case PTYPE_MESSAGE:
                  receive_message(PTYPE_MESSAGE);
                  continue;

            case PTYPE_ERROR:
                  receive_message(PTYPE_ERROR);
                  continue;

            default:
                  errno = 0;
                  error("Received packet has unexpected type (0x%02x)", type);
                  return 1;
            }

            break;
      }

      /*
       * Send target name
       */
      debug("Sending TARGET packet: %s", cfg->target);
      if (write_str_packet(sd, PTYPE_TARGET, cfg->target) < 0) {
            error("Can't send TARGET packet");
            return 1;
      }

      config_free(cfg);

      /*
       * Send mounts
       */
      debug("Sending MOUNTS packet");
      if (write_enum(sd, PTYPE_MOUNTS) < 0 || write_mountv(sd, mounts) < 0) {
            error("Can't send MOUNTS packet");
            return 1;
      }

      /*
       * Action paths
       */

      if (action == PTYPE_COMMAND) {
            bool_t is_tty;

            /*
             * Send arguments
             */
            if (args) {
                  debug("Sending ARGS packet");
                  if (write_enum(sd, PTYPE_ARGS) < 0 || write_strv(sd, args) < 0) {
                        error("Can't send ARGS packet");
                        return 1;
                  }
            }

            /*
             * Send current working directory
             */
            if (cwd) {
                  debug("Sending CWD packet");
                  if (write_str_packet(sd, PTYPE_CWD, cwd) < 0) {
                        error("Can't send CWD packet");
                        return 1;
                  }
            }

            /*
             * Send environment
             */
            {
                  debug("Sending ENVIRON packet");
                  if (write_enum(sd, PTYPE_ENVIRON) < 0 || write_strv(sd, environ) < 0) {
                        error("Can't send ENVIRON packet");
                        return 1;
                  }
            }

            /*
             * Send effective user and group ID.  If the user was not
             * faked, then send supplementary group IDs; otherwise
             * discover these on the remote side.
             */
            {
                  gid_t gids[NGROUPS_MAX];
                  int num, i;
                  uint16v_t *ids;

                  if (!remote_user) {
                        num = getgroups(NGROUPS_MAX, gids);
                        if (num < 0) {
                              error("Can't get supplementary group IDs");
                              return 1;
                        }
                  } else {
                        num = 0;
                  }

                  ids = uint16v_alloc(2 + num);
                  if (!ids) {
                        oom_error();
                        return 1;
                  }

                  ids->vec[0] = uid;
                  ids->vec[1] = gid;

                  for (i = 0; i < num; ++i)
                        ids->vec[i + 2] = gids[i];

                  debug("Sending IDS packet");
                  if (write_enum(sd, PTYPE_IDS) < 0 || write_uint16v(sd, ids) < 0) {
                        error("Can't send IDS packet");
                        return 1;
                  }

                  uint16v_free(ids);
            }

            /*
             * Send umask
             */
            {
                  uint16_t mask = umask(0);
                  umask(mask);

                  debug("Sending UMASK packet");
                  if (write_uint16_packet(sd, PTYPE_UMASK, mask) < 0) {
                        error("Can't send UMASK packet");
                        return 1;
                  }
            }

            /*
             * Send terminal size
             */

            is_tty = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO);
            debug(is_tty ? "Terminal emulation enabled" : "Terminal emulation disabled");

            if (is_tty) {
                  struct winsize ws;
                  ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);

                  debug("Sending WINSIZE packet");
                  if (write_enum(sd, PTYPE_WINSIZE) < 0 || write_winsize(sd, &ws) < 0) {
                        error("Can't send WINSIZE packet");
                        return 1;
                  }
            }

            /*
             * Non-blocking I/O
             */

            if (set_nonblocking(STDOUT_FILENO, TRUE) < 0) {
                  error("Can't make stdout non-blocking");
                  return 1;
            }

            if (is_tty) {
                  if (set_nonblocking(STDERR_FILENO, TRUE) < 0) {
                        error("Can't make stderr non-blocking");
                        return 1;
                  }

                  if (set_raw_mode() < 0)
                        return 1;
            }

            /*
             * Send command action
             */
            debug("Sending COMMAND packet");
            if (write_enum(sd, PTYPE_COMMAND) < 0) {
                  error("Can't send COMMAND packet");
                  return 1;
            }

            /*
             * Main loop
             */
            rc = manage(is_tty);
            if (rc < 0)
                  return 1;

      } else {

            /*
             * Send (un)mount action
             */
            if (action == PTYPE_MOUNT) {
                  debug("Sending MOUNT packet");
                  if (write_enum(sd, PTYPE_MOUNT) < 0) {
                        error("Can't send MOUNT packet");
                        return 1;
                  }
            } else {
                  debug("Sending UMOUNT packet");
                  if (write_enum(sd, PTYPE_UMOUNT) < 0) {
                        error("Can't send UMOUNT packet");
                        return 1;
                  }
            }

            /*
             * Wait until the server has (un)mounted
             */
            while (1) {
                  uint16_t val;
                  ptype_t type = read_enum(sd);
                  switch (type) {
                  case -1:
                        error("Can't read packet type from socket");
                        return 1;

                  case PTYPE_RC:
                        debug("Receiving RC packet");
                        if (read_uint16(sd, &val) < 0) {
                              error("Can't read RC packet from socket");
                              return 1;
                        }
                        rc = val;
                        break;

                  case PTYPE_MESSAGE:
                        receive_message(PTYPE_MESSAGE);
                        continue;

                  case PTYPE_ERROR:
                        receive_message(PTYPE_ERROR);
                        continue;

                  default:
                        errno = 0;
                        error("Received packet has unexpected type (0x%02x)", type);
                        return 1;
                  }

                  break;
            }
      }

      /*
       * Exit with correct code
       */

      if (rc == INTERNAL_ERROR_CODE) {
            debug("Internal server error");
            return 1;
      } else {
            debug("Return code: %d", rc);
            return rc;
      }
}

Generated by  Doxygen 1.6.0   Back to index