mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
This commit is contained in:
@@ -0,0 +1,521 @@
|
||||
/*
|
||||
* iucvtty / iucvconn - IUCV Terminal Applications
|
||||
*
|
||||
* Common functions
|
||||
*
|
||||
* Copyright IBM Corp. 2008, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <regex.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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/functions.h"
|
||||
#include "iucvterm/gettext.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(1u, 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(®ex, re, REG_EXTENDED | REG_ICASE | REG_NOSUB);
|
||||
if (rc) {
|
||||
__regerror(rc, ®ex);
|
||||
rc = -1;
|
||||
}
|
||||
regfree(®ex);
|
||||
|
||||
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(®ex, re, REG_EXTENDED | REG_ICASE | REG_NOSUB);
|
||||
if (rc) {
|
||||
__regerror(rc, ®ex);
|
||||
regfree(®ex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = regexec(®ex, str, nmatch, pmatch, 0);
|
||||
if (rc == REG_NOMATCH)
|
||||
rc = 1;
|
||||
|
||||
regfree(®ex);
|
||||
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';
|
||||
}
|
||||
Reference in New Issue
Block a user