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:
Michael Holzheu
2017-08-07 16:13:17 +02:00
commit b627b8d8e1
647 changed files with 168974 additions and 0 deletions
+37
View File
@@ -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
+209
View File
@@ -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;
}
+521
View File
@@ -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(&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';
}
+227
View File
@@ -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"));
}
+352
View File
@@ -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;
}
+289
View File
@@ -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;
}
+192
View File
@@ -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);
}