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,37 @@
|
||||
#! /usr/bin/make -f
|
||||
|
||||
include ../../common.mak
|
||||
|
||||
ifndef GETTEXT_TEXTDOMAIN
|
||||
GETTEXT_TEXTDOMAIN = iucvterm
|
||||
endif
|
||||
|
||||
ALL_CPPFLAGS += -I../include
|
||||
ALL_CPPFLAGS += -DUSE_NLS -DGETTEXT_TEXTDOMAIN=\"$(GETTEXT_TEXTDOMAIN)\"
|
||||
#ALL_CPPFLAGS += -D__DEBUG__
|
||||
|
||||
PROGRAMS = iucvconn iucvtty
|
||||
SYSTOOLS = ttyrun
|
||||
|
||||
all: $(PROGRAMS) $(SYSTOOLS)
|
||||
check:
|
||||
install:
|
||||
for prg in $(PROGRAMS); do \
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(DESTDIR)$(USRBINDIR) ; \
|
||||
done
|
||||
for prg in $(SYSTOOLS); do \
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(DESTDIR)$(BINDIR) ; \
|
||||
done
|
||||
|
||||
clean:
|
||||
-rm -f *.o $(PROGRAMS) $(SYSTOOLS)
|
||||
|
||||
iucvconn: iucvconn.o getopt.o auditlog.o functions.o
|
||||
|
||||
iucvtty: LDLIBS = -lutil
|
||||
iucvtty: iucvtty.o getopt.o auditlog.o functions.o
|
||||
|
||||
ttyrun: GETTEXT_TEXTDOMAIN = ttyrun
|
||||
ttyrun: ttyrun.o
|
||||
|
||||
.PHONY: install clean
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* iucvtty / iucvconn - IUCV Terminal Applications
|
||||
*
|
||||
* Functions for session logging/auditing.
|
||||
* The session log and timing data files adhere to the format
|
||||
* described in script(1).
|
||||
*
|
||||
* 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 <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "iucvterm/functions.h"
|
||||
|
||||
#define OPEN_FILEMODE (O_WRONLY | O_CREAT | O_EXCL)
|
||||
#define OPEN_FILEMASK (S_IRUSR | S_IWUSR | S_IRGRP)
|
||||
|
||||
static int script_fd = -1; /* fd of typescript file */
|
||||
static int timing_fd = -1; /* fd of timing data file */
|
||||
static FILE *info_file = NULL; /* FILE of info file */
|
||||
static struct timeval last_tv; /* tv to calculate timing */
|
||||
|
||||
|
||||
/**
|
||||
* print_on_time() - Append formatted time string to a message.
|
||||
* @prefix: message
|
||||
*
|
||||
* Returns a new buffer starting with @prefix and appends
|
||||
* a formatted string representation of the current time.
|
||||
* Returns NULL if memory allocation has failed.
|
||||
*
|
||||
* The caller must free the returned buffer after use.
|
||||
*/
|
||||
static char *print_on_time(const char *prefix)
|
||||
{
|
||||
char *buf = malloc(64 + strlen(prefix) + 1);
|
||||
time_t t = time(NULL);
|
||||
|
||||
if (buf != NULL) {
|
||||
if (t == (time_t) -1)
|
||||
sprintf(buf, "%s\n", prefix);
|
||||
else {
|
||||
sprintf(buf, "%s on ", prefix);
|
||||
ctime_r(&t, buf + strlen(prefix) + 4);
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* write_session_info() - Write data to the session info file
|
||||
* @format: Format string, shall not be NULL
|
||||
*
|
||||
* Writes informational messages to the session info file.
|
||||
* The info message is prefixed with a timestamp as returned by the
|
||||
* time(2) syscall.
|
||||
*/
|
||||
void write_session_info(const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (info_file == NULL)
|
||||
return;
|
||||
|
||||
fprintf(info_file, "%lu ", time(NULL));
|
||||
va_start(ap, format);
|
||||
vfprintf(info_file, format, ap);
|
||||
va_end(ap);
|
||||
if (strrchr(format, '\n') == NULL)
|
||||
fprintf(info_file, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* write_session_log() - Write session data to the session log file
|
||||
* @buf: Pointer to a buffer with data to log
|
||||
* @len: Copy up to @len bytes from @buf
|
||||
*
|
||||
* The routines writes up to @len bytes of data from buffer @buf to
|
||||
* the session transcript; write appropriate timing data to the timing
|
||||
* file.
|
||||
*/
|
||||
ssize_t write_session_log(const void* buf, size_t len)
|
||||
{
|
||||
ssize_t rc;
|
||||
int count;
|
||||
char data[64] = "";
|
||||
struct timeval curr_tv;
|
||||
long time_diff;
|
||||
|
||||
/* immediately return if there is no fd to write to */
|
||||
if (script_fd == -1)
|
||||
return -1;
|
||||
|
||||
rc = __write(script_fd, buf, len);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* calculate delay and write timing info */
|
||||
if (gettimeofday(&curr_tv, NULL))
|
||||
time_diff = 1000000; /* one second (in usecs) */
|
||||
else {
|
||||
time_diff = (curr_tv.tv_sec - last_tv.tv_sec) * 1000000 +
|
||||
curr_tv.tv_usec - last_tv.tv_usec;
|
||||
last_tv = curr_tv; /* reset last timeval */
|
||||
}
|
||||
|
||||
count = sprintf(data, "%.6f %zu\n",
|
||||
(double) time_diff / (double) 1000000, len);
|
||||
rc = __write(timing_fd, data, (count < 0) ? 0 : count );
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* close_session_log() - Close session logging
|
||||
*
|
||||
* The routine writes a trailer to the session log file and
|
||||
* closes the session log, timing and info file descriptor.
|
||||
*/
|
||||
void close_session_log(void)
|
||||
{
|
||||
char *trailer;
|
||||
|
||||
if (script_fd > 0) {
|
||||
trailer = print_on_time("Script done");
|
||||
if (trailer != NULL) {
|
||||
__write(script_fd, trailer, strlen(trailer));
|
||||
write_session_info(trailer);
|
||||
free(trailer);
|
||||
}
|
||||
close(script_fd);
|
||||
}
|
||||
if (timing_fd > 0)
|
||||
close(timing_fd);
|
||||
if (info_file != NULL)
|
||||
fclose(info_file);
|
||||
}
|
||||
|
||||
/**
|
||||
* open_session_log() - Open session logging
|
||||
* @filepath: File path to the session transcript
|
||||
*
|
||||
* Opens the session, timing and info log file.
|
||||
* If the session specified by @filepath already exists; or one of the
|
||||
* files cannot be opened successfully, return an error.
|
||||
*/
|
||||
int open_session_log(const char *filepath)
|
||||
{
|
||||
char *buf;
|
||||
int old_errno;
|
||||
int info_fd;
|
||||
|
||||
buf = calloc(11 + strlen(filepath), sizeof(char));
|
||||
if (buf == NULL)
|
||||
goto out_no_mem;
|
||||
|
||||
script_fd = open(filepath, OPEN_FILEMODE, OPEN_FILEMASK);
|
||||
if (script_fd == -1)
|
||||
goto out_error_open;
|
||||
|
||||
sprintf(buf, "%s.timing", filepath);
|
||||
timing_fd = open(buf, OPEN_FILEMODE, OPEN_FILEMASK);
|
||||
if (timing_fd == -1)
|
||||
goto out_error_open;
|
||||
|
||||
sprintf(buf, "%s.info", filepath);
|
||||
info_fd = open(buf, OPEN_FILEMODE, OPEN_FILEMASK);
|
||||
if (info_fd == -1)
|
||||
goto out_error_open;
|
||||
info_file = fdopen(info_fd, "w");
|
||||
if (info_file == NULL)
|
||||
goto out_error_open;
|
||||
|
||||
if(gettimeofday(&last_tv, NULL))
|
||||
goto out_error_open;
|
||||
|
||||
free(buf);
|
||||
|
||||
buf = print_on_time("Script started");
|
||||
if (buf != NULL) {
|
||||
__write(script_fd, buf, strlen(buf));
|
||||
write_session_info(buf);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_error_open:
|
||||
old_errno = errno; /* preserve errno */
|
||||
free(buf);
|
||||
close_session_log();
|
||||
errno = old_errno; /* restore errno from failed open call */
|
||||
out_no_mem:
|
||||
return -1;
|
||||
}
|
||||
@@ -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';
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* iucvtty / iucvconn - IUCV Terminal Applications
|
||||
*
|
||||
* Processing of command line arguments
|
||||
*
|
||||
* 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 <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "iucvterm/config.h"
|
||||
#include "iucvterm/functions.h"
|
||||
#include "iucvterm/gettext.h"
|
||||
|
||||
static const char iucvtty_usage[] = N_(
|
||||
"Usage: %s [-h|--help] [-v|--version]\n"
|
||||
" %s [-a <regex>] <terminal id> [-- <login program> [<args>]]\n\n"
|
||||
"Options:\n"
|
||||
" -h, --help Print this help, then exit.\n"
|
||||
" -v, --version Print version information, then exit.\n"
|
||||
" -a, --allow-from Permit connections from particular z/VM guests only.\n"
|
||||
" A z/VM guest is permitted if regex matches its name.\n"
|
||||
);
|
||||
|
||||
static const char iucvconn_usage[] = N_(
|
||||
"Usage: %s [-h|--help] [-v|--version]\n"
|
||||
" %s [-e esc] [-s <file>] <vm guest> <terminal id>\n\n"
|
||||
"Options:\n"
|
||||
" -h, --help Print this help, then exit.\n"
|
||||
" -v, --version Print version information, then exit.\n"
|
||||
" -s, --sessionlog Write terminal session to file.\n"
|
||||
" -e, --escape-char Escape character (can be one of: A-Y, ], ^ or _)\n"
|
||||
" Characters C, D, Q, S, Z and [ are not allowed.\n"
|
||||
);
|
||||
|
||||
static const struct option iucvterm_long_opts[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, 'v' },
|
||||
{ "allow-from", required_argument, NULL, 'a' },
|
||||
{ "sessionlog", required_argument, NULL, 's' },
|
||||
{ "escape-char", required_argument, NULL, 'e' },
|
||||
{ NULL, no_argument, NULL, 0 }
|
||||
};
|
||||
|
||||
struct tool_info {
|
||||
char name[10];
|
||||
char optstring[10];
|
||||
const char *usage;
|
||||
unsigned char reqNonOpts;
|
||||
};
|
||||
|
||||
/* program specific command line settings */
|
||||
static const struct tool_info iucv_tool[2] = {
|
||||
{ .name = "iucvtty",
|
||||
.optstring = "-hva:",
|
||||
.usage = iucvtty_usage,
|
||||
.reqNonOpts = 1,
|
||||
},
|
||||
{ .name = "iucvconn",
|
||||
.optstring = "-hvs:e:",
|
||||
.usage = iucvconn_usage,
|
||||
.reqNonOpts = 2,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void usage_exit(const struct tool_info *prg, int is_error,
|
||||
const char *msg)
|
||||
{
|
||||
if (msg != NULL)
|
||||
fprintf(stderr, _("%s: %s\n"), prg->name, msg);
|
||||
fprintf(stderr, _(prg->usage), prg->name, prg->name);
|
||||
exit(is_error ? 1 : 0); /* rc=1 .. invalid args */
|
||||
}
|
||||
|
||||
static void version_exit(const struct tool_info *prg)
|
||||
{
|
||||
printf(_("%s: IUCV Terminal Applications, version %s\n"),
|
||||
prg->name, RELEASE_STRING);
|
||||
printf(_("Copyright IBM Corp. 2008, 2017\n"));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void cpy_or_exit(char *dest, const char *src, size_t size,
|
||||
const struct tool_info *prg, const char *param)
|
||||
{
|
||||
if (strlen(src) >= size){
|
||||
fprintf(stderr,
|
||||
_("%s: %s exceeds the maximum of %zu characters\n"),
|
||||
prg->name, param, size - 1);
|
||||
exit(1);
|
||||
}
|
||||
strncpy(dest, src, size);
|
||||
dest[size - 1] = 0;
|
||||
}
|
||||
|
||||
static void set_esc_or_exit(const struct tool_info *prg,
|
||||
const char val, unsigned char *esc)
|
||||
{
|
||||
unsigned char upval = toupper(val);
|
||||
|
||||
/* range of valid escape keys: A-Z [ \ ] ^ _ */
|
||||
if (upval < 'A' || upval > '_')
|
||||
usage_exit(prg, 1, _("The specified character is not a "
|
||||
"valid escape character"));
|
||||
|
||||
switch (upval) {
|
||||
case 'C': /* interrupt (ISIG) */
|
||||
case 'D': /* EoF / EoT */
|
||||
case 'Q': /* XON */
|
||||
case 'S': /* XOFF */
|
||||
case 'Z': /* suspend (shell) */
|
||||
case '[': /* ESC */
|
||||
usage_exit(prg, 1, _("The specified character is not a "
|
||||
"valid escape character"));
|
||||
default:
|
||||
*esc = upval ^ 0100; /* see ascii(7) */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void parse_options(enum iucvterm_prg prg, struct iucvterm_cfg *config,
|
||||
int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
int index;
|
||||
int nonOpts = 0;
|
||||
|
||||
config->cmd_parms = NULL;
|
||||
config->sessionlog = NULL;
|
||||
config->esc_char = '_' ^ 0100; /* Ctrl-_ (0x1f) */
|
||||
config->flags = 0;
|
||||
|
||||
while (1) {
|
||||
index = -1;
|
||||
c = getopt_long(argc, argv, iucv_tool[prg].optstring,
|
||||
iucvterm_long_opts, &index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 1:
|
||||
if (nonOpts >= iucv_tool[prg].reqNonOpts) {
|
||||
usage_exit(&iucv_tool[prg], 1, NULL);
|
||||
break;
|
||||
}
|
||||
switch (nonOpts) {
|
||||
case 0:
|
||||
if (prg == PROG_IUCV_CONN)
|
||||
cpy_or_exit(config->host, optarg,
|
||||
sizeof(config->host),
|
||||
&iucv_tool[prg],
|
||||
_("<vm guest>"));
|
||||
else
|
||||
cpy_or_exit(config->service, optarg,
|
||||
sizeof(config->service),
|
||||
&iucv_tool[prg],
|
||||
_("<terminal id>"));
|
||||
break;
|
||||
case 1:
|
||||
cpy_or_exit(config->service, optarg,
|
||||
sizeof(config->service),
|
||||
&iucv_tool[prg],
|
||||
_("<terminal id>"));
|
||||
break;
|
||||
default:
|
||||
usage_exit(&iucv_tool[prg], 1, NULL);
|
||||
break;
|
||||
}
|
||||
++nonOpts;
|
||||
break;
|
||||
case 'a':/* max 80 */
|
||||
cpy_or_exit(config->client_re, optarg,
|
||||
sizeof(config->client_re),
|
||||
&iucv_tool[prg], _("<regex>"));
|
||||
if (is_regex_valid(config->client_re))
|
||||
exit(1);
|
||||
config->flags |= CFG_F_CHKCLNT;
|
||||
break;
|
||||
case 'e':
|
||||
switch (strlen(optarg)) {
|
||||
case 1:
|
||||
set_esc_or_exit(&iucv_tool[prg], optarg[0],
|
||||
&config->esc_char);
|
||||
break;
|
||||
case 4:
|
||||
if (memcmp(optarg, "none", 4) == 0) {
|
||||
config->esc_char = 0;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
usage_exit(&iucv_tool[prg], 1,
|
||||
_("The escape character must be a "
|
||||
"single character or 'none'"));
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
config->sessionlog = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
usage_exit(&iucv_tool[prg], 0, NULL);
|
||||
case 'v':
|
||||
version_exit(&iucv_tool[prg]);
|
||||
case '?':
|
||||
printf(_("Try '%s --help' for more information.\n"),
|
||||
iucv_tool[prg].name);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) /* save additional parameters */
|
||||
config->cmd_parms = argv + optind;
|
||||
|
||||
if (nonOpts < iucv_tool[prg].reqNonOpts) /* not enough args */
|
||||
usage_exit(&iucv_tool[prg], 1,
|
||||
_("The command does not have enough arguments"));
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* iucvconn - Application that establishes a terminal connection over IUCV
|
||||
*
|
||||
* Core application
|
||||
*
|
||||
* 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 <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <syslog.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
|
||||
#include "iucvterm/config.h"
|
||||
#include "iucvterm/functions.h"
|
||||
#include "iucvterm/gettext.h"
|
||||
|
||||
#define SYSLOG_IDENT "iucvconn"
|
||||
#define PRG_COMPONENT SYSLOG_IDENT
|
||||
#define DEFAULT_TERM "linux"
|
||||
|
||||
#define AUDIT(f, ...) do { \
|
||||
syslog(LOG_INFO, (f), __VA_ARGS__); \
|
||||
write_session_info((f), __VA_ARGS__); \
|
||||
} while (0);
|
||||
|
||||
/* escape mode actions */
|
||||
enum esc_action_t {
|
||||
DISCONNECT, /* Disconnect from terminal */
|
||||
RESIZE, /* Force terminal resizing */
|
||||
SEND, /* Send data (default action) */
|
||||
IGNORE, /* Ignore escape character */
|
||||
};
|
||||
|
||||
|
||||
static volatile sig_atomic_t resize_tty;
|
||||
static struct termios ios_orig; /* store original termio settings */
|
||||
|
||||
|
||||
/**
|
||||
* sig_handler() - Signal handler
|
||||
* @sig: Signal number
|
||||
*/
|
||||
static void sig_handler(int sig)
|
||||
{
|
||||
switch (sig) {
|
||||
case SIGWINCH:
|
||||
resize_tty = sig;
|
||||
break;
|
||||
|
||||
case SIGTERM:
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &ios_orig);
|
||||
close_session_log();
|
||||
_exit(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get_msg_char() - Returns single character from message
|
||||
* @msg: The IUCV terminal message
|
||||
*
|
||||
* Returns the (single) character of an IUCV terminal message
|
||||
* if @msg is of type MSG_TYPE_DATA and contains a single character
|
||||
* (datalen == 1). Otherwise the routine returns zero.
|
||||
*/
|
||||
static unsigned char get_msg_char(const struct iucvtty_msg *msg)
|
||||
{
|
||||
if (msg->type != MSG_TYPE_DATA || msg->datalen != 1)
|
||||
return 0;
|
||||
|
||||
return msg->data[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* is_esc_char() - Check message for escape character
|
||||
* @msg: The IUCV terminal message
|
||||
* @esc: The escape character
|
||||
*
|
||||
* Returns 1 if @msg contains the escape character @esc only; otherwise the
|
||||
* function returns 0.
|
||||
*/
|
||||
static int is_esc_char(const struct iucvtty_msg *msg, unsigned char esc)
|
||||
{
|
||||
if (!esc)
|
||||
return 0;
|
||||
|
||||
return (get_msg_char(msg) == esc) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_action() - Returns the action from an escaped character
|
||||
* @msg: The IUCV terminal message
|
||||
* @esc: The escape character
|
||||
*
|
||||
* Returns the appropriate action of the escaped character.
|
||||
* The action is derived from the single character stored in the IUCV terminal
|
||||
* message @msg. If the escape character is recognized, SEND is returned for
|
||||
* sending the "escaped" escape character to the terminal.
|
||||
*
|
||||
* If it contains multiple characters, the default SEND action is used to
|
||||
* indicate that the escape mode is done and to force sending the complete data
|
||||
* characters (e.g. entered by copy & paste).
|
||||
*
|
||||
* NOTE: This routine must be called in "escape mode".
|
||||
*/
|
||||
static enum esc_action_t get_action(const struct iucvtty_msg *msg,
|
||||
unsigned char esc)
|
||||
{
|
||||
if (is_esc_char(msg, esc))
|
||||
return SEND;
|
||||
|
||||
switch (get_msg_char(msg)) {
|
||||
case 0:
|
||||
return SEND;
|
||||
case '.':
|
||||
case 'd':
|
||||
return DISCONNECT;
|
||||
case 'r':
|
||||
return RESIZE;
|
||||
default:
|
||||
return IGNORE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* iucvtty_worker() - Handle server connection
|
||||
* @terminal: IUCV TTY server file descriptor
|
||||
*/
|
||||
static int iucvtty_worker(int terminal, const struct iucvterm_cfg *cfg)
|
||||
{
|
||||
struct iucvtty_msg *msg;
|
||||
fd_set set;
|
||||
size_t chunk;
|
||||
int in_esc_mode;
|
||||
enum esc_action_t action;
|
||||
|
||||
/* setup buffers */
|
||||
msg = malloc(MSG_BUFFER_SIZE);
|
||||
if (msg == NULL) {
|
||||
print_error("Allocating memory for the data buffer failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* multiplex i/o between login program and socket */
|
||||
chunk = 0;
|
||||
in_esc_mode = 0; /* escape mode state */
|
||||
action = SEND;
|
||||
while (1) {
|
||||
if (resize_tty) {
|
||||
iucvtty_tx_winsize(terminal, STDIN_FILENO);
|
||||
resize_tty = 0; /* clear signal flag */
|
||||
}
|
||||
|
||||
FD_ZERO(&set);
|
||||
FD_SET(terminal, &set);
|
||||
FD_SET(STDIN_FILENO, &set);
|
||||
|
||||
if (select(MAX(STDIN_FILENO, terminal) + 1, &set,
|
||||
NULL, NULL, NULL) == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
if (FD_ISSET(terminal, &set)) {
|
||||
if (iucvtty_read_msg(terminal, msg,
|
||||
MSG_BUFFER_SIZE, &chunk))
|
||||
break;
|
||||
|
||||
switch (msg->type) {
|
||||
case MSG_TYPE_DATA:
|
||||
iucvtty_copy_data(STDOUT_FILENO, msg);
|
||||
write_session_log(msg->data, msg->datalen);
|
||||
break;
|
||||
case MSG_TYPE_ERROR:
|
||||
iucvtty_error(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(STDIN_FILENO, &set)) {
|
||||
if (iucvtty_read_data(STDIN_FILENO, msg,
|
||||
MSG_BUFFER_SIZE))
|
||||
break;
|
||||
|
||||
if (in_esc_mode) {
|
||||
in_esc_mode = 0; /* reset */
|
||||
action = get_action(msg, cfg->esc_char);
|
||||
} else {
|
||||
if (is_esc_char(msg, cfg->esc_char)) {
|
||||
in_esc_mode = 1;
|
||||
action = IGNORE;
|
||||
} else
|
||||
action = SEND;
|
||||
}
|
||||
|
||||
/* handle escape mode */
|
||||
switch (action) {
|
||||
case SEND: /* non-escape mode (default) */
|
||||
if (iucvtty_write_msg(terminal, msg))
|
||||
goto out_worker_loop;
|
||||
break;
|
||||
|
||||
case DISCONNECT:/* disconnect */
|
||||
goto out_worker_loop;
|
||||
|
||||
case RESIZE: /* force terminal resize */
|
||||
iucvtty_tx_winsize(terminal, STDIN_FILENO);
|
||||
break;
|
||||
|
||||
case IGNORE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out_worker_loop:
|
||||
free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* main() - IUCV CONN program startup
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc;
|
||||
int server;
|
||||
struct sockaddr_iucv addr;
|
||||
struct termios ios;
|
||||
struct sigaction sigact;
|
||||
struct passwd *passwd;
|
||||
struct iucvterm_cfg conf;
|
||||
|
||||
|
||||
/* gettext initialization */
|
||||
gettext_init();
|
||||
|
||||
/* parse command line options */
|
||||
parse_options(PROG_IUCV_CONN, &conf, argc, argv);
|
||||
|
||||
/* open session audit log */
|
||||
if (conf.sessionlog != NULL)
|
||||
if (open_session_log(conf.sessionlog)) {
|
||||
print_error("Creating the terminal session "
|
||||
"log files failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* open socket and connect to server */
|
||||
server = iucvtty_socket(&addr, conf.host, conf.service);
|
||||
if (server == -1) {
|
||||
print_error((errno == EAFNOSUPPORT)
|
||||
? N_("The AF_IUCV address family is not available")
|
||||
: N_("Creating the AF_IUCV socket failed"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* syslog */
|
||||
openlog(SYSLOG_IDENT, LOG_PID, LOG_AUTHPRIV);
|
||||
|
||||
/* get user information for syslog */
|
||||
passwd = getpwuid(geteuid());
|
||||
|
||||
if (connect(server, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
print_error("The new connection would exceed the "
|
||||
"maximum number of IUCV connections");
|
||||
break;
|
||||
case ENETUNREACH:
|
||||
print_error("The target z/VM guest virtual machine "
|
||||
"is not logged on");
|
||||
break;
|
||||
case EACCES:
|
||||
print_error("The IUCV authorizations do not permit "
|
||||
"connecting to the target z/VM guest");
|
||||
break;
|
||||
default:
|
||||
print_error("Connecting to the z/VM guest virtual "
|
||||
"machine failed");
|
||||
break;
|
||||
}
|
||||
AUDIT("Connection to %s/%s failed for user %s (uid=%i)",
|
||||
conf.host, conf.service,
|
||||
(passwd != NULL) ? passwd->pw_name : "n/a", geteuid());
|
||||
rc = 2;
|
||||
goto return_on_error;
|
||||
}
|
||||
AUDIT("Established connection to %s/%s for user %s (uid=%i)",
|
||||
conf.host, conf.service,
|
||||
(passwd != NULL) ? passwd->pw_name : "n/a", geteuid());
|
||||
|
||||
/* send client parameters */
|
||||
iucvtty_tx_termenv(server, DEFAULT_TERM);
|
||||
iucvtty_tx_winsize(server, STDIN_FILENO);
|
||||
|
||||
/* register signal handler */
|
||||
sigemptyset(&sigact.sa_mask);
|
||||
sigact.sa_flags = SA_RESTART;
|
||||
sigact.sa_handler = sig_handler;
|
||||
sigaction(SIGWINCH, &sigact, NULL);
|
||||
sigaction(SIGTERM, &sigact, NULL);
|
||||
|
||||
/* modify terminal settings */
|
||||
if (tcgetattr(STDIN_FILENO, &ios_orig)) {
|
||||
print_error("Getting the terminal I/O settings failed");
|
||||
rc = 3;
|
||||
goto return_on_error;
|
||||
}
|
||||
memcpy(&ios, &ios_orig, sizeof(ios));
|
||||
|
||||
/* put terminal into raw mode */
|
||||
cfmakeraw(&ios);
|
||||
/* NOTE: If the TTY driver (ldisc) runs in TTY_DRIVER_REAL_RAW,
|
||||
* we need to do the input character processing here;
|
||||
* that means to translate CR into CR + NL (ICRNL).
|
||||
* Define TTY_REAL_RAW in for that case. */
|
||||
#ifdef TTY_REAL_RAW
|
||||
ios.c_iflag |= ICRNL; /* | IGNPAR | IGNBRK; */
|
||||
#endif
|
||||
tcflush(STDIN_FILENO, TCIOFLUSH);
|
||||
if (tcsetattr(STDIN_FILENO, TCSANOW, &ios)) {
|
||||
print_error("Modifying the terminal I/O settings failed");
|
||||
rc = 4;
|
||||
goto return_on_error;
|
||||
}
|
||||
|
||||
iucvtty_worker(server, &conf);
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &ios_orig);
|
||||
|
||||
rc = 0;
|
||||
return_on_error:
|
||||
close(server);
|
||||
closelog();
|
||||
close_session_log();
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* iucvtty - Application that provides a full-screen terminal for iucvconn
|
||||
*
|
||||
* Core application
|
||||
*
|
||||
* 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 <fcntl.h>
|
||||
#include <pty.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <utmp.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
|
||||
#include "af_iucv.h"
|
||||
#include "iucvterm/config.h"
|
||||
#include "iucvterm/functions.h"
|
||||
#include "iucvterm/gettext.h"
|
||||
|
||||
#define SYSLOG_IDENT "iucvtty"
|
||||
#define PRG_COMPONENT SYSLOG_IDENT
|
||||
#define TERM_BUFSIZE 256
|
||||
#define TERM_DEFAULT "linux"
|
||||
|
||||
|
||||
static volatile sig_atomic_t sig_shutdown;
|
||||
|
||||
|
||||
/**
|
||||
* sig_handler() - Signal handler
|
||||
* @sig: Signal number.
|
||||
*/
|
||||
static void sig_handler(int sig)
|
||||
{
|
||||
sig_shutdown = sig;
|
||||
}
|
||||
|
||||
/**
|
||||
* exec_login_prog() - execute a login program
|
||||
* @cmd: Path to the (login) program executable
|
||||
*/
|
||||
static int exec_login_prog(char *cmd[])
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (cmd != NULL)
|
||||
rc = execv(cmd[0], cmd);
|
||||
else
|
||||
rc = execl("/bin/login", "/bin/login", (char *) NULL);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* iucvtty_worker() - Handle an incoming client connection
|
||||
* @client: Client file descriptor
|
||||
* @master: PTY master file descriptor
|
||||
* @slave: PTY slave file descriptor
|
||||
* @cfg: IUCV TTY configuration structure.
|
||||
*/
|
||||
static int iucvtty_worker(int client, int master, int slave,
|
||||
const struct iucvterm_cfg *cfg)
|
||||
{
|
||||
int rc;
|
||||
struct iucvtty_msg *msg;
|
||||
pid_t child;
|
||||
fd_set set;
|
||||
size_t chunk;
|
||||
char term_env[TERM_BUFSIZE];
|
||||
|
||||
|
||||
/* flush pending terminal data */
|
||||
tcflush(master, TCIOFLUSH);
|
||||
|
||||
/* read terminal parameters from client */
|
||||
if (iucvtty_rx_termenv(client, term_env, TERM_BUFSIZE))
|
||||
sprintf(term_env, TERM_DEFAULT);
|
||||
|
||||
/* start login program */
|
||||
child = fork();
|
||||
if (child == -1) {
|
||||
print_error("Creating a new process to run the "
|
||||
"login program failed");
|
||||
iucvtty_tx_error(client, ERR_FORK);
|
||||
return 1; /* return from worker */
|
||||
}
|
||||
if (child == 0) { /* child process */
|
||||
closelog(); /* close syslog */
|
||||
|
||||
/* setup terminal */
|
||||
if (login_tty(slave)) {
|
||||
print_error("Setting up a terminal for user login failed");
|
||||
iucvtty_tx_error(client, ERR_SETUP_LOGIN_TTY);
|
||||
exit(2);
|
||||
}
|
||||
setenv("TERM", term_env, 1);
|
||||
if (exec_login_prog(cfg->cmd_parms)) {
|
||||
print_error("Running the login program failed");
|
||||
iucvtty_tx_error(client, ERR_CANNOT_EXEC_LOGIN);
|
||||
}
|
||||
exit(3); /* we only reach here if exec has failed */
|
||||
}
|
||||
|
||||
/* setup buffers */
|
||||
msg = malloc(MSG_BUFFER_SIZE);
|
||||
if (msg == NULL) {
|
||||
print_error("Allocating memory for the data buffer failed");
|
||||
rc = 2;
|
||||
goto out_kill_login;
|
||||
}
|
||||
|
||||
/* multiplex i/o between login program and socket. */
|
||||
rc = 0;
|
||||
chunk = 0;
|
||||
while (!sig_shutdown) {
|
||||
FD_ZERO(&set);
|
||||
FD_SET(client, &set);
|
||||
FD_SET(master, &set);
|
||||
|
||||
if (select(MAX(master, client) + 1, &set,
|
||||
NULL, NULL, NULL) == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
if (FD_ISSET(client, &set)) {
|
||||
if (iucvtty_read_msg(client, msg,
|
||||
MSG_BUFFER_SIZE, &chunk))
|
||||
break;
|
||||
switch (msg->type) {
|
||||
case MSG_TYPE_DATA:
|
||||
iucvtty_copy_data(master, msg);
|
||||
break;
|
||||
case MSG_TYPE_WINSIZE:
|
||||
if (msg->datalen != sizeof(struct winsize))
|
||||
break;
|
||||
if (ioctl(master, TIOCSWINSZ,
|
||||
(struct winsize *) msg->data))
|
||||
print_error("Resizing the terminal "
|
||||
"window failed");
|
||||
break;
|
||||
case MSG_TYPE_TERMIOS: /* ignored */
|
||||
break;
|
||||
case MSG_TYPE_ERROR:
|
||||
iucvtty_error(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(master, &set))
|
||||
if (iucvtty_tx_data(client, master,
|
||||
msg, MSG_BUFFER_SIZE))
|
||||
break;
|
||||
}
|
||||
free(msg);
|
||||
|
||||
out_kill_login:
|
||||
/* ensure the chld is terminated before calling waitpid:
|
||||
* - in case a sigterm has been received,
|
||||
* - or a sigchld from other than the chld
|
||||
*/
|
||||
kill(child, SIGKILL); /* cause a sigchld */
|
||||
waitpid(child, NULL, 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* main() - IUCV TTY program startup
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct iucvterm_cfg conf; /* program configuration */
|
||||
struct sockaddr_iucv saddr, caddr; /* IUCV socket address info */
|
||||
char client_host[9]; /* client guest name */
|
||||
int server, client; /* socket file descriptors */
|
||||
int master, slave; /* pre-allocated PTY fds */
|
||||
struct sigaction sigact; /* signal handler */
|
||||
int rc;
|
||||
socklen_t len;
|
||||
|
||||
|
||||
/* gettext initialization */
|
||||
gettext_init();
|
||||
|
||||
/* parse command line arguments */
|
||||
parse_options(PROG_IUCV_TTY, &conf, argc, argv);
|
||||
|
||||
/* create server socket... */
|
||||
server = iucvtty_socket(&saddr, NULL, conf.service);
|
||||
if (server == -1) {
|
||||
print_error((errno == EAFNOSUPPORT)
|
||||
? N_("The AF_IUCV address family is not available")
|
||||
: N_("Creating the AF_IUCV socket failed"));
|
||||
return 1;
|
||||
}
|
||||
if (bind(server, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) {
|
||||
print_error("Binding the AF_IUCV socket failed");
|
||||
close(server);
|
||||
return 1;
|
||||
}
|
||||
if (listen(server, 1) == -1) {
|
||||
print_error("Listening for incoming connections failed");
|
||||
close(server);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* pre-allocate PTY master/slave file descriptors */
|
||||
if (openpty(&master, &slave, NULL, NULL, NULL)) {
|
||||
print_error("Opening a new PTY master/slave device pair failed");
|
||||
close(server);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* set close-on-exec for file descriptors */
|
||||
fcntl(master, F_SETFD, FD_CLOEXEC);
|
||||
fcntl(server, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
/* syslog */
|
||||
openlog(SYSLOG_IDENT, LOG_PID, LOG_AUTHPRIV);
|
||||
syslog(LOG_INFO, "Listening on terminal ID: %s, using pts device: %s",
|
||||
conf.service, ttyname(slave));
|
||||
|
||||
rc = 0;
|
||||
len = sizeof(struct sockaddr_iucv);
|
||||
/* accept a new client connection */
|
||||
client = accept(server, (struct sockaddr *) &caddr, &len);
|
||||
if (client == -1) {
|
||||
print_error("An incoming connection could not be accepted");
|
||||
rc = 2;
|
||||
goto exit_on_error;
|
||||
}
|
||||
|
||||
/* check if client is allowed to connect */
|
||||
userid_cpy(client_host, caddr.siucv_user_id);
|
||||
if (is_client_allowed(client_host, &conf)) {
|
||||
iucvtty_tx_error(client, ERR_NOT_AUTHORIZED);
|
||||
syslog(LOG_WARNING, "Rejected client connection from %s; "
|
||||
"Client is not allowed to connect.",
|
||||
client_host);
|
||||
rc = 3;
|
||||
|
||||
} else { /* client is allowed to connect */
|
||||
syslog(LOG_INFO, "Accepted client connection from %s",
|
||||
client_host);
|
||||
/* set close-on-exec for client socket */
|
||||
fcntl(client, F_SETFD, FD_CLOEXEC);
|
||||
/* close server socket */
|
||||
close(server);
|
||||
|
||||
/* setup signal handler to notify shutdown signal */
|
||||
sigemptyset(&sigact.sa_mask);
|
||||
sigact.sa_flags = SA_RESTART;
|
||||
sigact.sa_handler = sig_handler;
|
||||
if (sigaction(SIGCHLD, &sigact, NULL)
|
||||
|| sigaction(SIGTERM, &sigact, NULL)
|
||||
|| sigaction(SIGINT, &sigact, NULL)
|
||||
|| sigaction(SIGPIPE, &sigact, NULL)) {
|
||||
print_error("Registering a signal handler failed");
|
||||
rc = 4;
|
||||
goto exit_on_error;
|
||||
}
|
||||
|
||||
/* handle client terminal connection */
|
||||
rc = iucvtty_worker(client, master, slave, &conf);
|
||||
}
|
||||
|
||||
close(client);
|
||||
|
||||
exit_on_error:
|
||||
close(slave);
|
||||
close(master);
|
||||
closelog();
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* ttyrun - Start a program if a specified terminal device is available
|
||||
*
|
||||
*
|
||||
* ttyrun is typically used to prevent a respawn through the init(8)
|
||||
* program when a terminal is not available.
|
||||
* ttyrun runs the specific program if the specified terminal device
|
||||
* can be opened successfully. Otherwise the program enters a sleep or
|
||||
* exits with a specified return value.
|
||||
*
|
||||
* Example: To start /sbin/agetty on terminal device hvc1, use:
|
||||
*
|
||||
* h1:2345:respawn:/sbin/ttyrun hvc1 /sbin/agetty -L 9600 %t linux
|
||||
*
|
||||
* Note: %t is resolved to the terminal device "hvc1" before /sbin/agetty
|
||||
* is started.
|
||||
*
|
||||
* Return values:
|
||||
* 1 - invalid argument or parameter is missing
|
||||
* 2 - terminal does not resolve to a terminal device
|
||||
* 3 - starting the specified program failed
|
||||
* 1..255 - terminal is not available and the return code is
|
||||
* specified with the -e option
|
||||
*
|
||||
* Copyright 2017 IBM Corp.
|
||||
*
|
||||
* 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 <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
|
||||
#define TTY_ESCAPE_STR "%t"
|
||||
|
||||
#define EXIT_INVALID_ARG 1
|
||||
#define EXIT_NO_TERMINAL 2
|
||||
#define EXIT_EXEC_FAILED 3
|
||||
|
||||
|
||||
static const char usage[] =
|
||||
"Usage: %s [-e status] <term> <program> [<program_options>]\n"
|
||||
" %s [-h|--help] [-v|--version]\n"
|
||||
"\n"
|
||||
"Start the program if the specified terminal device is available.\n"
|
||||
"If the terminal device cannot be opened, sleep until a signal is received\n"
|
||||
"that causes an exit or exit with the return value specified with status.\n"
|
||||
"\n"
|
||||
"-e, --exitstatus Specifies an exit status in the range 1 to 255.\n"
|
||||
"-V, --verbose Displays syslog messages.\n"
|
||||
"-h, --help Displays this help, then exits.\n"
|
||||
"-v, --version Displays version information, then exits.\n";
|
||||
|
||||
static void help_exit(const char *prg)
|
||||
{
|
||||
printf(usage, prg, prg);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void version_exit(const char *prg)
|
||||
{
|
||||
printf("%s: Start a program if a terminal device is available, "
|
||||
"version %s\n", prg, RELEASE_STRING);
|
||||
printf("Copyright IBM Corp. 2010, 2017\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void err_exit(const char *prg, const char *msg)
|
||||
{
|
||||
fprintf(stderr, "%s: %s\n", prg, msg);
|
||||
exit(EXIT_INVALID_ARG);
|
||||
}
|
||||
|
||||
static void wait_and_exit(void)
|
||||
{
|
||||
/* sleep until a signal is received, then exit */
|
||||
pause();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static const struct option prog_opts[] = {
|
||||
{ "help", no_argument, NULL, 'h'},
|
||||
{ "version", no_argument, NULL, 'v'},
|
||||
{ "exitstatus", required_argument, NULL, 'e'},
|
||||
{ "verbose", no_argument, NULL, 'V'},
|
||||
{ NULL, no_argument, NULL, 0 },
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc, tty, i, c, index, done, term_index, verbose;
|
||||
char terminal[PATH_MAX] = "";
|
||||
unsigned long exitstatus;
|
||||
|
||||
|
||||
/* parse command options */
|
||||
if (argc == 1)
|
||||
err_exit(argv[0], "One or more options are required but missing");
|
||||
|
||||
exitstatus = done = term_index = verbose = 0;
|
||||
while (!done) {
|
||||
c = getopt_long(argc, argv, "-hve:V", prog_opts, NULL);
|
||||
switch (c) {
|
||||
case -1:
|
||||
done = 1;
|
||||
break;
|
||||
case 1:
|
||||
/* the first non-optional argument must be the
|
||||
* terminal device */
|
||||
if (!strncmp(optarg, "/", 1))
|
||||
strncpy(terminal, optarg, PATH_MAX - 1);
|
||||
else
|
||||
snprintf(terminal, PATH_MAX, "/dev/%s", optarg);
|
||||
terminal[PATH_MAX - 1] = 0;
|
||||
term_index = optind - 1;
|
||||
done = 1;
|
||||
break;
|
||||
case 'e':
|
||||
errno = 0;
|
||||
exitstatus = strtoul(optarg, (char **) NULL, 10);
|
||||
if (errno == ERANGE)
|
||||
err_exit(argv[0], "The exit status must be "
|
||||
"an integer in the range 1 to 255");
|
||||
|
||||
if (!exitstatus || exitstatus > 255)
|
||||
err_exit(argv[0], "The exit status must be "
|
||||
"in the range 1 to 255");
|
||||
break;
|
||||
case 'V':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'h':
|
||||
help_exit(argv[0]);
|
||||
case 'v':
|
||||
version_exit(argv[0]);
|
||||
case '?':
|
||||
fprintf(stderr, "Try %s --help for more information\n",
|
||||
argv[0]);
|
||||
exit(EXIT_INVALID_ARG);
|
||||
}
|
||||
}
|
||||
index = optind;
|
||||
|
||||
/* check terminal */
|
||||
if (!strlen(terminal))
|
||||
err_exit(argv[0], "You must specify the name of "
|
||||
"a terminal device");
|
||||
|
||||
/* any program to start? */
|
||||
if (index == argc)
|
||||
err_exit(argv[0], "You must specify a program to start");
|
||||
|
||||
/* open and check terminal device */
|
||||
tty = open(terminal, O_NOCTTY | O_RDONLY | O_NONBLOCK);
|
||||
if (tty == -1) {
|
||||
if (verbose) {
|
||||
openlog(argv[0], LOG_PID, LOG_DAEMON);
|
||||
syslog(LOG_INFO, "Could not open tty %s (%s)",
|
||||
terminal, strerror(errno));
|
||||
closelog();
|
||||
}
|
||||
|
||||
/* enter wait or exit */
|
||||
if (exitstatus)
|
||||
exit(exitstatus);
|
||||
wait_and_exit();
|
||||
}
|
||||
rc = !isatty(tty);
|
||||
close(tty);
|
||||
if (rc)
|
||||
exit(EXIT_NO_TERMINAL);
|
||||
|
||||
/* start getty program */
|
||||
for (i = index; i < argc; i++)
|
||||
if (!strcmp(argv[i], TTY_ESCAPE_STR) && term_index)
|
||||
argv[i] = argv[term_index];
|
||||
if (execv(argv[index], argv + index))
|
||||
exit(EXIT_EXEC_FAILED);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
Reference in New Issue
Block a user