Logo Search packages:      
Sourcecode: s390-tools version File versions  Download package

functions.c

/*
 * iucvtty / iucvconn - IUCV Terminal Applications
 *
 * Common functions
 *
 * Copyright IBM Corp. 2008, 2010
 * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
 */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include "af_iucv.h"
#include "iucvterm/config.h"
#include "iucvterm/gettext.h"
#include "iucvterm/functions.h"
#include "iucvterm/proto.h"


/* Global program component for iucv terminal tools */
#define PRG_COMPONENT         "iucvterm"


/**
 * __write() - Write data
 * @fd:           File descriptor
 * @buf:    Pointer to data buffer
 * @len:    Buffer length
 *
 * Write @len number of bytes from the buffer @buf to the file
 * descriptor @fd. The routines handles EINTR and partially writes.
 * Returns the error code from the underlying write(2) syscall.
 */
ssize_t __write(int fd, const void *buf, size_t len)
{
      ssize_t rc;
      size_t  written = 0;

      while (written < len) {
            rc = write(fd, buf + written, len - written);
            if (rc == -1 && errno == EINTR)
                  continue;
            if (rc <= 0)
                  return rc;
            written += rc;
      }
      return written;
}

#ifdef __DEBUG__
static void __dump_msg(int fd, const struct iucvtty_msg *m, char dir)
{
      write_session_info("%c: (fd=%d) MSG: ver=%02x type=%02x datalen=%u\n",
                     dir, fd, m->version, m->type, (uint16_t) m->datalen);
}
#endif


/**
 * iucvtty_socket() - Creates and return an IUCV socket
 * @sai:    AF_IUCV socket address structure
 * @host:   z/VM guest name
 * @service:      Terminal name passed as additional data to @host after connect
 *
 * This function sets up the struct sockaddr_iucv with the specified
 * VM guest virtual machine and terminal information.
 * Finally, it returns an AF_IUCV socket.
 */
int iucvtty_socket(struct sockaddr_iucv *sai,
               const char *host, const char *service)
{
      char temp[9];

      memset(sai, 0, sizeof(struct sockaddr_iucv));
      sai->siucv_family = AF_IUCV;

      if (host != NULL) {
            snprintf(temp, 9, "%-8s", host);
            memcpy(sai->siucv_user_id, temp, 8);
      } else
            memset(sai->siucv_user_id, ' ', 8);

      if (service != NULL) {
            snprintf(temp, 9, "%-8s", service);
            memcpy(sai->siucv_name, temp, 8);
      } else
            memset(sai->siucv_name, ' ', 8);

      return socket(PF_IUCV, SOCK_STREAM, 0);
}

/**
 * iucvtty_tx_termenv() - Send terminal environment variable
 * @dest:   File descriptor to output data
 * @dflt:   TERM environment string ('\0' terminated)
 *
 * Copy terminal environment variable to destination @dest.
 */
int iucvtty_tx_termenv(int dest, char *dflt)
{
      struct iucvtty_msg *msg;
      char *term = getenv("TERM");
      size_t len;
      int rc;

      if (term == NULL && dflt != NULL)
            term = dflt;

      len = 0;
      if (term != NULL)
            len = 1 + strlen(term);

      /* Note: The server console tool waits for terminal environment
       *       information: the message is sent even if it is empty */
      msg = msg_alloc(MSG_TYPE_TERMENV, len);
      if (msg == NULL)
            return -1;
      msg_cpy_from(msg, term, len);
      rc = iucvtty_write_msg(dest, msg);
      msg_free(msg);

      return rc;
}

/**
 * iucvtty_rx_termenv() - Receive terminal environment variable
 * @fd:           File descriptor to read data from
 * @buf:    Buffer to store the terminal environment variable
 * @len:    Size of buffer @buf
 */
int iucvtty_rx_termenv(int fd, void *buf, size_t len)
{
      int rc;
      size_t skip;
      struct iucvtty_msg *msg = msg_alloc(MSG_TYPE_TERMENV, len);

      if (msg == NULL)
            return -1;
      skip = 0;
      rc = iucvtty_read_msg(fd, msg, msg_size(msg), &skip);
      iucvtty_skip_msg_residual(fd, &skip);
      if (!rc) {
            if (msg->datalen == 0)
                  memset(buf, 0, min(1, len));
            else
                  msg_cpy_to(msg, buf, len);
      }
      msg_free(msg);
      return rc;
}

/**
 * iucvtty_tx_data() - Send terminal data
 * @from:   File descriptor to read data from
 * @msg:    Pointer to iucv tty message buffer
 * @len:    Size of message buffer
 *
 * This routine reads data from file descriptor @from and stores them in
 * a data array of the specified iucv tty message @msg. It reads up to
 * @len - MSG_DATA_OFFSET bytes from fd @from.
 */
int iucvtty_read_data(int from, struct iucvtty_msg *msg, size_t len)
{
      ssize_t r;

      r = read(from, msg->data, len - MSG_DATA_OFFSET);
      if (r == -1 && errno == EINTR)      /* REVIEW: loop if EINTR ? */
            r = read(from, msg->data, len - MSG_DATA_OFFSET);
      if (r <= 0)
            return -1;
      msg->version = MSG_VERSION;
      msg->type    = MSG_TYPE_DATA;
      msg->datalen = (uint16_t) r;

      return 0;
}

/**
 * iucvtty_tx_data() - Send terminal data
 * @dest:   File descriptor to send data to
 * @from:   File descriptor to read data from
 * @msg:    Pointer to iucv tty message buffer
 * @len:    Size of message buffer
 *
 * This routine reads data from file descriptor @from and stores them in
 * a data array of the specified iucv tty message @msg. It reads up to
 * @len - MSG_DATA_OFFSET bytes from fd @from.
 * Finally, the iucv tty message written to file descriptor @dest.
 */
int iucvtty_tx_data(int dest, int from, struct iucvtty_msg *msg, size_t len)
{
      if (iucvtty_read_data(from, msg, len))
            return -1;
      if (iucvtty_write_msg(dest, msg))
            return -1;
      return 0;
}

/**
 * iucvtty_tx_winsize() - Send terminal window size information.
 * @dest:   Destination
 * @from:   Terminal file descriptor to request winsize
 *
 * Sends the terminal window size from terminal file descriptor
 * @from to the destination @dest.
 * If the window size is not retrieved, the routine will not fail.
 * The routine fails if there is a problem sending the window size
 * to @dest. The return codes are specified by iucvtty_write_msg().
 */
int iucvtty_tx_winsize(int dest, int from)
{
      int rc;
      struct iucvtty_msg *msg = msg_alloc(MSG_TYPE_WINSIZE,
                                    sizeof(struct winsize));
      if (msg == NULL)
            return -1;
      rc = 0;
      if (ioctl(from, TIOCGWINSZ, msg->data) > -1)
            rc = iucvtty_write_msg(dest, msg);
      msg_free(msg);

      return rc;
}

/**
 * iucvtty_tx_error() - Send an error code
 * @dest:   Destination
 * @errCode:      Error code
 */
int iucvtty_tx_error(int dest, uint32_t errCode)
{
      struct iucvtty_msg *msg;
      int rc;

      msg = msg_alloc(MSG_TYPE_ERROR, sizeof(errCode));
      if (msg == NULL)
            return -1;
      msg_cpy_from(msg, &errCode, sizeof(errCode));
      rc = iucvtty_write_msg(dest, msg);
      msg_free(msg);
      return rc;
}

/**
 * iucvtty_copy_data() - Copy IUCV message data
 * @dest:   Destination to copy data to
 * @msg:    IUCV message
 */
int iucvtty_copy_data(int dest, struct iucvtty_msg *msg)
{
      if (__write(dest, msg->data, msg->datalen) <= 0)
            return -1;
      return 0;
}

/**
 * iucvtty_skip_msg_residual() - Skip (receive & forget) count number of bytes
 * @fd:           File descriptor
 * @residual:     Residual of an iucv tty message received by iucvtty_read_msg()
 *
 * See iucvtty_read_msg() for an explanation when to use this routine.
 * Note: The @residual parameter shall not be NULL.
 */
void iucvtty_skip_msg_residual(int fd, size_t *residual)
{
      char b;
      size_t  i;

      if (*residual <= 0)
            return;
      for (i = 0; i < *residual; i++)
            if (read(fd, &b, 1) <= 0)
                  break;
      *residual = 0;
}

/**
 * iucvtty_read_msg() - Read/Receive an IUCV message
 * @fd:           File descriptor to read from
 * @msg:    Pointer to IUCV message buffer
 * @len:    IUCV message data len
 * @residual:     Status to be used by next call
 *
 * The function reads up to @len bytes from file descriptor @fd.
 * If the received message is larger than @len bytes, the @residual value
 * is set to the number of bytes remaining.
 * The function shall then be re-called to create a new message and receive
 * the next chunk of size @residual; or the remaining characters must be
 * skipped using the iucvtty_skip_msg() routine.
 * Note: The @len parameter shall be greater than MSG_DATA_OFFSET.
 *       The @residual parameter shall not be NULL.
 */
int iucvtty_read_msg(int fd, struct iucvtty_msg *msg,
                 size_t len, size_t *residual)
{
      int rc;
      ssize_t r;        /* number of bytes read from fd */

      if (*residual)
            len = min(len - MSG_DATA_OFFSET, *residual);

      while (1) {
            if (*residual) {
                  r = read(fd, msg->data, len);
                  if (r > 0)
                        msg->datalen = r;
            } else
                  r = read(fd, msg, len);

            if (r == -1 && errno == EINTR)
                  continue;
            if (r <= 0) {
                  rc = -1;
                  goto out_read_error;
            }

            break;      /* exit loop for a successful read */
      }
#ifdef __DEBUG__
      if (!*residual)
            __dump_msg(fd, msg, 'R');
#endif

      /* (re)calculate next chunk */
      if (*residual)
            *residual -= msg->datalen;
      else
            if (msg->datalen > (r - MSG_DATA_OFFSET)) {
                  /* calculate pending msg data and update datalen */
                  *residual = msg->datalen - (r - MSG_DATA_OFFSET);
                  msg->datalen = r - MSG_DATA_OFFSET;
            }

      /* check for a sane message */
      if (msg->version != MSG_VERSION) {
            fprintf(stderr, _("%s: %s\n"),
                  PRG_COMPONENT, _("The version of the received data "
                               "message is not supported\n"));
            rc = -2;
            goto out_read_error;
      }

      rc = 0;
out_read_error:
      return rc;
}

/**
 * iucvtty_write_msg() - Write/Send IUCV message
 * @fd:           File descriptor
 * @msg:    Pointer to IUCV message
 */
int iucvtty_write_msg(int fd, struct iucvtty_msg *msg)
{
      msg->version = MSG_VERSION;

      if (__write(fd, msg, msg_size(msg)) <= 0)
            return -1;

#ifdef __DEBUG__
      __dump_msg(fd, msg, 'S');
#endif
      return 0;
}


/**
 * iucv_msg_error() - Reports an IUCV message error
 * @comp:   Program component
 * @errnum: IUCV message error code
 */
void iucv_msg_error(const char *comp, uint32_t errnum)
{
      const char *translated;

      switch (errnum) {
      case ERR_FORK:
            translated = _("Creating a new process to run the "
                        "login program failed");
            break;
      case ERR_CANNOT_EXEC_LOGIN:
            translated = _("Running the login program failed");
            break;
      case ERR_SETUP_LOGIN_TTY:
            translated = _("Setting up a terminal for user login failed");
            break;
      case ERR_NOT_AUTHORIZED:
            translated = _("The z/VM guest virtual machine is not "
                        "permitted to connect");
            break;
      default:
            translated = _("The specified error code is not known");
            break;
      }
      fprintf(stderr, "%s: %s (%s=%" PRIu32 ")\r\n",
            comp, translated, _("error code"), errnum);
}

/**
 * program_error() - Report an program/syscall error.
 * @comp:   Program component name
 * @d:            Error message, subject to gettext translation
 */
void program_error(const char *comp, const char *d)
{
      fprintf(stderr, _("%s: %s: %s\n"), comp, _(d), strerror(errno));
}


/**
 * __regerror - Report an error from a previous regex api call
 * @error:  Error code
 * @re:           Reference to the used regular expression
 */
static inline void __regerror(int error, const regex_t *re)
{
      char errbuf[81];

      regerror(error, re, errbuf, 81);
      fprintf(stderr, _("The regular expression has an error: %s\n"), errbuf);
      return;
}

/**
 * is_regex_valid() - Check if the specified regex is syntactically correct.
 * @re:           String representation of the regular expression
 *
 * Returns zero on success, otherwise -1.
 */
int is_regex_valid(const char *re)
{
      regex_t regex;
      int rc;

      if (re == NULL)
            return -1;
      rc = regcomp(&regex, re, REG_EXTENDED | REG_ICASE | REG_NOSUB);
      if (rc) {
            __regerror(rc, &regex);
            rc = -1;
      }
      regfree(&regex);

      return rc;
}

/**
 * strmatch() - Match a string using a regular expression
 * @str:    String to match
 * @re:           Regular expression
 *
 * Returns zero on success, -1 on error or if @str is NULL; and
 * 1 if the regular expression did not match the string.
 */
int strmatch(const char *str, const char *re)
{
      regex_t regex;
      regmatch_t pmatch[1];
      size_t nmatch = 0;
      int rc;

      if (re == NULL)
            return -1;

      rc = regcomp(&regex, re, REG_EXTENDED | REG_ICASE | REG_NOSUB);
      if (rc) {
            __regerror(rc, &regex);
            regfree(&regex);
            return -1;
      }

      rc = regexec(&regex, str, nmatch, pmatch, 0);
      if (rc == REG_NOMATCH)
            rc = 1;

      regfree(&regex);
      return rc;
}

/**
 * is_client_allowed() - Check if the client is allowed to connect.
 * @client: Client name
 * @cfg:    Pointer to the IUCV terminal configuration structure
 *
 * The return code is identical to strmatch().  If client checking is
 * disabled, the function returns zero.
 */
int is_client_allowed(const char *client, const struct iucvterm_cfg *cfg)
{
      if (!CFG_CHKCLNT(cfg))
            return 0;

      return strmatch(client, cfg->client_re);
}

/**
 * userid_cpy() - Copy z/VM user ID and skip trailing spaces.
 * @dest:   Destination buffer
 * @userid: z/VM user ID
 */
void userid_cpy(char dest[9], const char userid[8])
{
      ssize_t pos;

      /* find pos of last character (pos 0..7) or -1 if user ID is empty */
      for (pos = 7; pos >= 0; pos--)
            if (userid[pos] != ' ')
                  break;

      if (pos >= 0)
            memcpy(dest, userid, pos + 1);
      dest[pos + 1] = '\0';
}

Generated by  Doxygen 1.6.0   Back to index