From bc6e654149018090b7954e6667d3c7e7654625f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Hor=C3=A1k?= Date: Fri, 28 Jan 2011 14:18:39 +0100 Subject: [PATCH 53/61] ttyrun: run a program if a terminal device is available Summary: ttyrun: run a program if a terminal device is available Description: Depending on your setup, Linux on System z might or might not provide a particular terminal or console. ttyrun safely starts getty programs and prevents respawns through the init program if a terminal is not available. --- iucvterm/doc/Makefile | 2 +- iucvterm/doc/ttyrun.8 | 146 +++++++++++++++++++++++++++++++++++++++ iucvterm/src/Makefile | 11 +++- iucvterm/src/ttyrun.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 339 insertions(+), 3 deletions(-) create mode 100644 iucvterm/doc/ttyrun.8 create mode 100644 iucvterm/src/ttyrun.c diff --git a/iucvterm/doc/Makefile b/iucvterm/doc/Makefile index 5815f21..a646765 100644 --- a/iucvterm/doc/Makefile +++ b/iucvterm/doc/Makefile @@ -2,7 +2,7 @@ include ../../common.mak -MANS = iucvconn.1 iucvtty.1 ts-shell.1 hvc_iucv.9 chiucvallow.8 +MANS = iucvconn.1 iucvtty.1 ts-shell.1 hvc_iucv.9 chiucvallow.8 ttyrun.8 all: diff --git a/iucvterm/doc/ttyrun.8 b/iucvterm/doc/ttyrun.8 new file mode 100644 index 0000000..fc7a16f --- /dev/null +++ b/iucvterm/doc/ttyrun.8 @@ -0,0 +1,146 @@ +.\" ttyrun.8 +.\" +.\" +.\" Copyright IBM Corp. 2010 +.\" Author(s): Hendrik Brueckner +.\" ------------------------------------------------------------------------- +.TH "ttyrun" "8" "April 2010" "s390-tools" "System Management Commands" +.LO 8 +. +.ds s ttyrun +. +. +.SH NAME +ttyrun \- Start a program if a specified terminal device is available +. +. +. +.SH SYNOPSIS +.B \*s +.RB [ \-e | \-\-exitstatus +.IR status ] +.I term +.I program +.RI [ "program_options" ] +.br +.B \*s +.RB [ \-h | \-\-help ] +.br +.B \*s +.RB [ \-v | \-\-version ] +. +. +. +.SH DESCRIPTION +\fB\*s\fP is typically started during system initialization and is used +to prevent a respawn through the +.BR init (8) +program when a terminal is not available. + +\fIterm\fP is the name of the terminal device and is a path relative to +the \f(CW/dev\fP directory, for example, specify \f(CWhvc0\fP for +\f(CW/dev/hvc0\fP. +.br +If the specified terminal device can be opened, \fB\*s\fP starts the +specified program. + +If the terminal device cannot be opened, the behavior of \fB\*s\fP +depends on the \fB\-e\fP option: +. +.RS 2 +.IP "\(bu" 2 +If the \fB\-e\fP option has been specified, \fB\*s\fP exits with the +specified return value, or +.IP "\(bu" 2 +If the \fB\-e\fP option has not been specified, \fB\*s\fP sleeps until +it receives a signal that causes an exit. +.RE +.PP +\fIprogram\fP is an absolute path to the program to be started by +\fB\*s\fP and \fIprogram_options\fP specify additional arguments. +Depending on the program, arguments might be required. The variable +\f(CW%t\fP in the \fIprogram_options\fP is resolved to the terminal +device specified with \fIterm\fP. +. +. +. +.SH OPTIONS +.TP +.BR \-e ", " \-\-exitstatus\~\fIstatus\fP +Specifies an exit status that is returned when the terminal device +is not available. \fIstatus\fP must be an integer in the range 1 to 255. + +You can use this status value in an upstart job file to prevent +respawning. +. +.TP +.BR \-h ", " \-\-help +Displays a short help text, then exits. +. +.TP +.BR \-v ", " \-\-version +Displays the version number of \fB\*s\fP, then exits. +. +. +. +.SH "RETURN VALUES" +\fB\*s\fP exits with one of the following return values to report an +error condition: +.TP +.B 1 +\fB\*s\fP has been started with an argument that is not valid or +required but missing. +.TP +.B 2 +\fB\*s\fP could open the file specified for \fIterm\fP but the +file is not a terminal device. +.TP +.B 3 +\fB\*s\fP could not start the specified program. +.PP +The return values 1 to 3 might also be returned when the \fB\-e\fP +option is used and the terminal device is not available. +.TP +.B 4 \- 255 +The terminal device is not available and the \fB\-e\fP option +specifies an exit status in this range. +. +. +. +.SH "EXAMPLES" +.SS inittab +To start \fB/sbin/agetty\fP on terminal device "hvc1", specify: +.PP +.ft CW +.in +0.25in +.nf +h1:2345:respawn:/sbin/\*s hvc1 /sbin/agetty -L 9600 %t linux +.fi +.in -0.25in +.ft +. +.SS upstart job/event files +To start \fB/sbin/agetty\fP on terminal device "hvc1", add the following +settings to the job file: +.PP +.ft CW +.in +0.25in +.nf +respawn +normal exit 42 +exec /sbin/\*s -e 42 hvc1 /sbin/agetty -L 9600 %t linux +.fi +.in -0.25in +.ft +.PP +With the normal exit statement, you specify an exit status that will +prevent upstart from respawning the program. To prevent respawning with +\fB\*s\fP, you must specify the same value for the \fB\-e\fP option. +. +. +. +.SH "SEE ALSO" +.BR agetty (8), +.BR mingetty (8), +.BR inittab (5), +.BR events (5) diff --git a/iucvterm/src/Makefile b/iucvterm/src/Makefile index f1f8f7c..369c887 100644 --- a/iucvterm/src/Makefile +++ b/iucvterm/src/Makefile @@ -11,20 +11,27 @@ CPPFLAGS += -DUSE_NLS -DGETTEXT_TEXTDOMAIN=\"$(GETTEXT_TEXTDOMAIN)\" #CPPFLAGS += -D__DEBUG__ PROGRAMS = iucvconn iucvtty +SYSTOOLS = ttyrun -all: $(PROGRAMS) +all: $(PROGRAMS) $(SYSTOOLS) check: install: for prg in $(PROGRAMS); do \ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(USRBINDIR) ; \ done + for prg in $(SYSTOOLS); do \ + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 $$prg $(BINDIR) ; \ + done clean: - -rm -f *.o $(PROGRAMS) + -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 diff --git a/iucvterm/src/ttyrun.c b/iucvterm/src/ttyrun.c new file mode 100644 index 0000000..55c2bc2 --- /dev/null +++ b/iucvterm/src/ttyrun.c @@ -0,0 +1,183 @@ +/* + * 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 IBM Corp. 2010 + * Author(s): Hendrik Brueckner + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "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] []\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" +"-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\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'}, + { NULL, no_argument, NULL, 0 }, +}; + +int main(int argc, char *argv[]) +{ + int rc, tty, i, c, index, done, term_index; + 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 = 0; + while (!done) { + c = getopt_long(argc, argv, "-hve:", 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 '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) { + 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); +} -- 1.7.3.5