Blob Blame History Raw
From bc6e654149018090b7954e6667d3c7e7654625f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
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 <brueckner@linux.vnet.ibm.com>
+.\" -------------------------------------------------------------------------
+.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 <brueckner@linux.vnet.ibm.com>
+ */
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#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] <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"
+"-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