From ea152b4e9656945f17d18bf4e5ece91e3e57ebb1 Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Tue, 29 Jan 2019 11:57:55 +0100
Subject: [PATCH] agetty: backport RHEL-8 version
The code is identical to RHEL-8.0, except:
* disabled ISSUEDIR_SUPPORT
* disabled AGETTY_RELOAD
* removed \e and \e{name} feature
Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1664752
Signed-off-by: Karel Zak <kzak@redhat.com>
---
include/pathnames.h | 5 +-
term-utils/agetty.c | 1141 +++++++++++++++++++++++++++++++------------
2 files changed, 839 insertions(+), 307 deletions(-)
diff --git a/include/pathnames.h b/include/pathnames.h
index fa4bddbad..ec5eef918 100644
--- a/include/pathnames.h
+++ b/include/pathnames.h
@@ -64,9 +64,10 @@
/* used in term-utils/agetty.c */
#define _PATH_ISSUE "/etc/issue"
-#define _PATH_OS_RELEASE "/etc/os-release"
+#define _PATH_ISSUEDIR _PATH_ISSUE ".d"
+#define _PATH_OS_RELEASE_ETC "/etc/os-release"
+#define _PATH_OS_RELEASE_USR "/usr/lib/os-release"
#define _PATH_NUMLOCK_ON _PATH_LOCALSTATEDIR "/numlock-on"
-
#define _PATH_LOGINDEFS "/etc/login.defs"
/* used in misc-utils/look.c */
diff --git a/term-utils/agetty.c b/term-utils/agetty.c
index 948d77246..c3c7ab504 100644
--- a/term-utils/agetty.c
+++ b/term-utils/agetty.c
@@ -10,6 +10,7 @@
*
* This program is freely distributable.
*/
+
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
@@ -20,19 +21,21 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <fcntl.h>
#include <stdarg.h>
#include <ctype.h>
-#include <utmp.h>
+#include <utmpx.h>
#include <getopt.h>
#include <time.h>
-#include <sys/file.h>
#include <sys/socket.h>
#include <langinfo.h>
#include <grp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ifaddrs.h>
+#include <net/if.h>
+#include <sys/utsname.h>
#include "strutils.h"
#include "all-io.h"
@@ -41,14 +44,37 @@
#include "c.h"
#include "widechar.h"
#include "ttyutils.h"
+#include "env.h"
+
+#ifdef USE_PLYMOUTH_SUPPORT
+# include "plymouth-ctrl.h"
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+
+#if defined(__FreeBSD_kernel__)
+# include <pty.h>
+# ifdef HAVE_UTMP_H
+# include <utmp.h>
+# endif
+# ifdef HAVE_LIBUTIL_H
+# include <libutil.h>
+# endif
+#endif
#ifdef __linux__
# include <sys/kd.h>
-# include <sys/param.h>
# define USE_SYSLOG
# ifndef DEFAULT_VCTERM
# define DEFAULT_VCTERM "linux"
# endif
+# if defined (__s390__) || defined (__s390x__)
+# define DEFAULT_TTYS0 "dumb"
+# define DEFAULT_TTY32 "ibm327x"
+# define DEFAULT_TTYS1 "vt220"
+# endif
# ifndef DEFAULT_STERM
# define DEFAULT_STERM "vt102"
# endif
@@ -69,6 +95,10 @@
# endif
#endif
+#ifdef __FreeBSD_kernel__
+#define USE_SYSLOG
+#endif
+
/* If USE_SYSLOG is undefined all diagnostics go to /dev/console. */
#ifdef USE_SYSLOG
# include <syslog.h>
@@ -86,9 +116,9 @@
/*
* Things you may want to modify.
*
- * If ISSUE is not defined, agetty will never display the contents of the
- * /etc/issue file. You will not want to spit out large "issue" files at the
- * wrong baud rate. Relevant for System V only.
+ * If ISSUE_SUPPORT is not defined, agetty will never display the contents of
+ * the /etc/issue file. You will not want to spit out large "issue" files at
+ * the wrong baud rate. Relevant for System V only.
*
* You may disagree with the default line-editing etc. characters defined
* below. Note, however, that DEL cannot be used for interrupt generation
@@ -97,14 +127,26 @@
/* Displayed before the login prompt. */
#ifdef SYSV_STYLE
-# define ISSUE _PATH_ISSUE
-# include <sys/utsname.h>
+# define ISSUE_SUPPORT
#endif
/* Login prompt. */
#define LOGIN "login: "
#define LOGIN_ARGV_MAX 16 /* Numbers of args for login */
+/*
+ * agetty --reload
+ */
+#ifdef AGETTY_RELOAD
+# include <sys/inotify.h>
+# include <linux/netlink.h>
+# include <linux/rtnetlink.h>
+# define AGETTY_RELOAD_FILENAME "/run/agetty.reload" /* trigger file */
+# define AGETTY_RELOAD_FDNONE -2 /* uninitialized fd */
+static int inotify_fd = AGETTY_RELOAD_FDNONE;
+static int netlink_fd = AGETTY_RELOAD_FDNONE;
+#endif
+
/*
* When multiple baud rates are specified on the command line, the first one
* we will try is the first one specified.
@@ -116,7 +158,7 @@
struct options {
int flags; /* toggle switches, see below */
- int timeout; /* time-out period */
+ unsigned int timeout; /* time-out period */
char *autolog; /* login the user automatically */
char *chdir; /* Chdir before the login */
char *chroot; /* Chroot before the login */
@@ -126,11 +168,11 @@ struct options {
char *vcline; /* line of virtual console */
char *term; /* terminal type */
char *initstring; /* modem init string */
- char *issue; /* alternative issue file */
+ char *issue; /* alternative issue file or directory */
char *erasechars; /* string with erase chars */
char *killchars; /* string with kill chars */
char *osrelease; /* /etc/os-release data */
- int delay; /* Sleep seconds before prompt */
+ unsigned int delay; /* Sleep seconds before prompt */
int nice; /* Run login with this priority */
int numspeed; /* number of baud rates to try */
int clocal; /* CLOCAL_MODE_* */
@@ -145,12 +187,12 @@ enum {
};
#define F_PARSE (1<<0) /* process modem status messages */
-#define F_ISSUE (1<<1) /* display /etc/issue */
+#define F_ISSUE (1<<1) /* display /etc/issue or /etc/issue.d */
#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */
#define F_INITSTRING (1<<4) /* initstring is set */
#define F_WAITCRLF (1<<5) /* wait for CR or LF */
-#define F_CUSTISSUE (1<<6) /* give alternative issue file */
+
#define F_NOPROMPT (1<<7) /* do not ask for login name! */
#define F_LCUC (1<<8) /* support for *LCUC stty modes */
#define F_KEEPSPEED (1<<9) /* follow baud rate from kernel */
@@ -189,16 +231,14 @@ static const struct Speedtab speedtab[] = {
{2400, B2400},
{4800, B4800},
{9600, B9600},
-#ifdef B19200
+#ifdef B19200
{19200, B19200},
-#endif
-#ifdef B38400
- {38400, B38400},
-#endif
-#ifdef EXTA
+#elif defined(EXTA)
{19200, EXTA},
#endif
-#ifdef EXTB
+#ifdef B38400
+ {38400, B38400},
+#elif defined(EXTB)
{38400, EXTB},
#endif
#ifdef B57600
@@ -209,6 +249,42 @@ static const struct Speedtab speedtab[] = {
#endif
#ifdef B230400
{230400, B230400},
+#endif
+#ifdef B460800
+ {460800, B460800},
+#endif
+#ifdef B500000
+ {500000, B500000},
+#endif
+#ifdef B576000
+ {576000, B576000},
+#endif
+#ifdef B921600
+ {921600, B921600},
+#endif
+#ifdef B1000000
+ {1000000, B1000000},
+#endif
+#ifdef B1152000
+ {1152000, B1152000},
+#endif
+#ifdef B1500000
+ {1500000, B1500000},
+#endif
+#ifdef B2000000
+ {2000000, B2000000},
+#endif
+#ifdef B2500000
+ {2500000, B2500000},
+#endif
+#ifdef B3000000
+ {3000000, B3000000},
+#endif
+#ifdef B3500000
+ {3500000, B3500000},
+#endif
+#ifdef B4000000
+ {4000000, B4000000},
#endif
{0, 0},
};
@@ -221,6 +297,7 @@ static void open_tty(char *tty, struct termios *tp, struct options *op);
static void termio_init(struct options *op, struct termios *tp);
static void reset_vc (const struct options *op, struct termios *tp);
static void auto_baud(struct termios *tp);
+static void list_speeds(void);
static void output_special_char (unsigned char c, struct options *op,
struct termios *tp, FILE *fp);
static void do_prompt(struct options *op, struct termios *tp);
@@ -231,7 +308,8 @@ static void termio_final(struct options *op,
struct termios *tp, struct chardata *cp);
static int caps_lock(char *s);
static speed_t bcode(char *s);
-static void usage(FILE * out) __attribute__((__noreturn__));
+static void usage(void) __attribute__((__noreturn__));
+static void exit_slowly(int code) __attribute__((__noreturn__));
static void log_err(const char *, ...) __attribute__((__noreturn__))
__attribute__((__format__(printf, 1, 2)));
static void log_warn (const char *, ...)
@@ -239,13 +317,16 @@ static void log_warn (const char *, ...)
static ssize_t append(char *dest, size_t len, const char *sep, const char *src);
static void check_username (const char* nm);
static void login_options_to_argv(char *argv[], int *argc, char *str, char *username);
+static void reload_agettys(void);
+static void print_issue_file(struct options *op, struct termios *tp);
/* Fake hostname for ut_host specified on command line. */
static char *fakehost;
#ifdef DEBUGGING
+# include "closestream.h"
# ifndef DEBUG_OUTPUT
-# define DEBUG_OUTPUT "/dev/ttyp0"
+# define DEBUG_OUTPUT "/dev/tty10"
# endif
# define debug(s) do { fprintf(dbf,s); fflush(dbf); } while (0)
FILE *dbf;
@@ -261,8 +342,7 @@ int main(int argc, char **argv)
struct options options = {
.flags = F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
.login = _PATH_LOGIN, /* default login program */
- .tty = "tty1", /* default tty line */
- .issue = ISSUE /* default issue file */
+ .tty = "tty1" /* default tty line */
};
char *login_argv[LOGIN_ARGV_MAX + 1];
int login_argc = 0;
@@ -283,8 +363,12 @@ int main(int argc, char **argv)
#ifdef DEBUGGING
dbf = fopen(DEBUG_OUTPUT, "w");
- for (int i = 1; i < argc; i++)
+ for (int i = 1; i < argc; i++) {
+ if (i > 1)
+ debug(" ");
debug(argv[i]);
+ }
+ debug("\n");
#endif /* DEBUGGING */
/* Parse command-line arguments. */
@@ -311,6 +395,13 @@ int main(int argc, char **argv)
sigaction(SIGHUP, &sa_hup, NULL);
tcsetpgrp(STDIN_FILENO, getpid());
+
+ /* Default is to follow the current line speed and then default to 9600 */
+ if ((options.flags & F_VCONSOLE) == 0 && options.numspeed == 0) {
+ options.speeds[options.numspeed++] = bcode("9600");
+ options.flags |= F_KEEPSPEED;
+ }
+
/* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
debug("calling termio_init\n");
termio_init(&options, &termios);
@@ -337,7 +428,7 @@ int main(int argc, char **argv)
/* Set the optional timer. */
if (options.timeout)
- alarm((unsigned) options.timeout);
+ alarm(options.timeout);
/* Optionally wait for CR or LF before writing /etc/issue */
if (serial_tty_option(&options, F_WAITCRLF)) {
@@ -362,7 +453,9 @@ int main(int argc, char **argv)
username = options.autolog;
}
- if ((options.flags & F_NOPROMPT) == 0) {
+ if (options.flags & F_NOPROMPT) { /* --skip-login */
+ print_issue_file(&options, &termios);
+ } else { /* regular (auto)login */
if (options.autolog) {
/* Autologin prompt */
do_prompt(&options, &termios);
@@ -372,7 +465,7 @@ int main(int argc, char **argv)
debug("reading login name\n");
while ((username =
get_logname(&options, &termios, &chardata)) == NULL)
- if ((options.flags & F_VCONSOLE) == 0)
+ if ((options.flags & F_VCONSOLE) == 0 && options.numspeed)
next_speed(&options, &termios);
}
}
@@ -403,9 +496,12 @@ int main(int argc, char **argv)
login_options_to_argv(login_argv, &login_argc,
options.logopt, username);
} else {
- if (fakehost && (options.flags & F_REMOTE)) {
- login_argv[login_argc++] = "-h";
- login_argv[login_argc++] = fakehost;
+ if (options.flags & F_REMOTE) {
+ if (fakehost) {
+ login_argv[login_argc++] = "-h";
+ login_argv[login_argc++] = fakehost;
+ } else if (options.flags & F_NOHOSTNAME)
+ login_argv[login_argc++] = "-H";
}
if (username) {
if (options.autolog)
@@ -418,25 +514,18 @@ int main(int argc, char **argv)
login_argv[login_argc] = NULL; /* last login argv */
- if (options.chroot) {
- if (chroot(options.chroot) < 0)
- log_err(_("%s: can't change root directory %s: %m"),
- options.tty, options.chroot);
- }
- if (options.chdir) {
- if (chdir(options.chdir) < 0)
- log_err(_("%s: can't change working directory %s: %m"),
- options.tty, options.chdir);
- }
- if (options.nice) {
- if (nice(options.nice) < 0)
- log_warn(_("%s: can't change process priority: %m"),
- options.tty);
- }
- if (options.osrelease)
- free(options.osrelease);
+ if (options.chroot && chroot(options.chroot) < 0)
+ log_err(_("%s: can't change root directory %s: %m"),
+ options.tty, options.chroot);
+ if (options.chdir && chdir(options.chdir) < 0)
+ log_err(_("%s: can't change working directory %s: %m"),
+ options.tty, options.chdir);
+ if (options.nice && nice(options.nice) < 0)
+ log_warn(_("%s: can't change process priority: %m"),
+ options.tty);
+
+ free(options.osrelease);
#ifdef DEBUGGING
- fprintf(dbf, "read %c\n", ch);
if (close_stream(dbf) != 0)
log_err("write failed: %s", DEBUG_OUTPUT);
#endif
@@ -477,7 +566,7 @@ static char *replace_u(char *str, char *username)
log_err(_("failed to allocate memory: %m"));
if (p != str) {
- /* copy chars befor \u */
+ /* copy chars before \u */
memcpy(tp, str, p - str);
tp += p - str;
}
@@ -527,6 +616,55 @@ static void login_options_to_argv(char *argv[], int *argc,
*argc = i;
}
+static void output_version(void)
+{
+ static const char *features[] = {
+#ifdef DEBUGGING
+ "debug",
+#endif
+#ifdef CRTSCTS
+ "flow control",
+#endif
+#ifdef KDGKBLED
+ "hints",
+#endif
+#ifdef ISSUE_SUPPORT
+ "issue",
+#endif
+#ifdef ISSUEDIR_SUPPORT
+ "issue.d",
+#endif
+#ifdef KDGKBMODE
+ "keyboard mode",
+#endif
+#ifdef USE_PLYMOUTH_SUPPORT
+ "plymouth",
+#endif
+#ifdef AGETTY_RELOAD
+ "reload",
+#endif
+#ifdef USE_SYSLOG
+ "syslog",
+#endif
+#ifdef HAVE_WIDECHAR
+ "widechar",
+#endif
+ NULL
+ };
+ unsigned int i;
+
+ printf( _("%s from %s"), program_invocation_short_name, PACKAGE_STRING);
+ fputs(" (", stdout);
+ for (i = 0; features[i]; i++) {
+ if (0 < i)
+ fputs(", ", stdout);
+ printf("%s", features[i]);
+ }
+ fputs(")\n", stdout);
+}
+
+#define is_speed(str) (strlen((str)) == strspn((str), "0123456789,"))
+
/* Parse command-line arguments. */
static void parse_args(int argc, char **argv, struct options *op)
{
@@ -540,46 +678,50 @@ static void parse_args(int argc, char **argv, struct options *op)
HELP_OPTION,
ERASE_CHARS_OPTION,
KILL_CHARS_OPTION,
+ RELOAD_OPTION,
+ LIST_SPEEDS_OPTION,
};
const struct option longopts[] = {
- { "8bits", no_argument, 0, '8' },
- { "autologin", required_argument, 0, 'a' },
- { "noreset", no_argument, 0, 'c' },
- { "chdir", required_argument, 0, 'C' },
- { "delay", required_argument, 0, 'd' },
- { "remote", no_argument, 0, 'E' },
- { "issue-file", required_argument, 0, 'f' },
- { "flow-control", no_argument, 0, 'h' },
- { "host", required_argument, 0, 'H' },
- { "noissue", no_argument, 0, 'i' },
- { "init-string", required_argument, 0, 'I' },
- { "noclear", no_argument, 0, 'J' },
- { "login-program", required_argument, 0, 'l' },
- { "local-line", optional_argument, 0, 'L' },
- { "extract-baud", no_argument, 0, 'm' },
- { "skip-login", no_argument, 0, 'n' },
- { "nonewline", no_argument, 0, 'N' },
- { "login-options", required_argument, 0, 'o' },
- { "login-pause", no_argument, 0, 'p' },
- { "nice", required_argument, 0, 'P' },
- { "chroot", required_argument, 0, 'r' },
- { "hangup", no_argument, 0, 'R' },
- { "keep-baud", no_argument, 0, 's' },
- { "timeout", required_argument, 0, 't' },
- { "detect-case", no_argument, 0, 'U' },
- { "wait-cr", no_argument, 0, 'w' },
- { "nohints", no_argument, 0, NOHINTS_OPTION },
- { "nohostname", no_argument, 0, NOHOSTNAME_OPTION },
- { "long-hostname", no_argument, 0, LONGHOSTNAME_OPTION },
- { "version", no_argument, 0, VERSION_OPTION },
- { "help", no_argument, 0, HELP_OPTION },
- { "erase-chars", required_argument, 0, ERASE_CHARS_OPTION },
- { "kill-chars", required_argument, 0, KILL_CHARS_OPTION },
- { NULL, 0, 0, 0 }
+ { "8bits", no_argument, NULL, '8' },
+ { "autologin", required_argument, NULL, 'a' },
+ { "noreset", no_argument, NULL, 'c' },
+ { "chdir", required_argument, NULL, 'C' },
+ { "delay", required_argument, NULL, 'd' },
+ { "remote", no_argument, NULL, 'E' },
+ { "issue-file", required_argument, NULL, 'f' },
+ { "flow-control", no_argument, NULL, 'h' },
+ { "host", required_argument, NULL, 'H' },
+ { "noissue", no_argument, NULL, 'i' },
+ { "init-string", required_argument, NULL, 'I' },
+ { "noclear", no_argument, NULL, 'J' },
+ { "login-program", required_argument, NULL, 'l' },
+ { "local-line", optional_argument, NULL, 'L' },
+ { "extract-baud", no_argument, NULL, 'm' },
+ { "list-speeds", no_argument, NULL, LIST_SPEEDS_OPTION },
+ { "skip-login", no_argument, NULL, 'n' },
+ { "nonewline", no_argument, NULL, 'N' },
+ { "login-options", required_argument, NULL, 'o' },
+ { "login-pause", no_argument, NULL, 'p' },
+ { "nice", required_argument, NULL, 'P' },
+ { "chroot", required_argument, NULL, 'r' },
+ { "hangup", no_argument, NULL, 'R' },
+ { "keep-baud", no_argument, NULL, 's' },
+ { "timeout", required_argument, NULL, 't' },
+ { "detect-case", no_argument, NULL, 'U' },
+ { "wait-cr", no_argument, NULL, 'w' },
+ { "nohints", no_argument, NULL, NOHINTS_OPTION },
+ { "nohostname", no_argument, NULL, NOHOSTNAME_OPTION },
+ { "long-hostname", no_argument, NULL, LONGHOSTNAME_OPTION },
+ { "reload", no_argument, NULL, RELOAD_OPTION },
+ { "version", no_argument, NULL, VERSION_OPTION },
+ { "help", no_argument, NULL, HELP_OPTION },
+ { "erase-chars", required_argument, NULL, ERASE_CHARS_OPTION },
+ { "kill-chars", required_argument, NULL, KILL_CHARS_OPTION },
+ { NULL, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv,
- "8a:cC:d:Ef:hH:iI:Jl:LmnNo:pP:r:Rst:Uw", longopts,
+ "8a:cC:d:Ef:hH:iI:Jl:L::mnNo:pP:r:Rst:Uw", longopts,
NULL)) != -1) {
switch (c) {
case '8':
@@ -595,13 +737,12 @@ static void parse_args(int argc, char **argv, struct options *op)
op->chdir = optarg;
break;
case 'd':
- op->delay = atoi(optarg);
+ op->delay = strtou32_or_err(optarg, _("invalid delay argument"));
break;
case 'E':
op->flags |= F_REMOTE;
break;
case 'f':
- op->flags |= F_CUSTISSUE;
op->issue = optarg;
break;
case 'h':
@@ -634,7 +775,7 @@ static void parse_args(int argc, char **argv, struct options *op)
else if (strcmp(optarg, "=auto") == 0)
op->clocal = CLOCAL_MODE_AUTO;
else
- log_err(_("unssuported --local-line mode argument"));
+ log_err(_("invalid argument of --local-line"));
}
break;
case 'm':
@@ -643,6 +784,9 @@ static void parse_args(int argc, char **argv, struct options *op)
case 'n':
op->flags |= F_NOPROMPT;
break;
+ case 'N':
+ op->flags |= F_NONL;
+ break;
case 'o':
op->logopt = optarg;
break;
@@ -650,7 +794,7 @@ static void parse_args(int argc, char **argv, struct options *op)
op->flags |= F_LOGINPAUSE;
break;
case 'P':
- op->nice = atoi(optarg);
+ op->nice = strtos32_or_err(optarg, _("invalid nice argument"));
break;
case 'r':
op->chroot = optarg;
@@ -662,8 +806,7 @@ static void parse_args(int argc, char **argv, struct options *op)
op->flags |= F_KEEPSPEED;
break;
case 't':
- if ((op->timeout = atoi(optarg)) <= 0)
- log_err(_("bad timeout value: %s"), optarg);
+ op->timeout = strtou32_or_err(optarg, _("invalid timeout argument"));
break;
case 'U':
op->flags |= F_LCUC;
@@ -686,14 +829,19 @@ static void parse_args(int argc, char **argv, struct options *op)
case KILL_CHARS_OPTION:
op->killchars = optarg;
break;
+ case RELOAD_OPTION:
+ reload_agettys();
+ exit(EXIT_SUCCESS);
+ case LIST_SPEEDS_OPTION:
+ list_speeds();
+ exit(EXIT_SUCCESS);
case VERSION_OPTION:
- printf(_("%s from %s\n"), program_invocation_short_name,
- PACKAGE_STRING);
+ output_version();
exit(EXIT_SUCCESS);
case HELP_OPTION:
- usage(stdout);
+ usage();
default:
- usage(stderr);
+ errtryhelp(EXIT_FAILURE);
}
}
@@ -701,26 +849,26 @@ static void parse_args(int argc, char **argv, struct options *op)
if (argc < optind + 1) {
log_warn(_("not enough arguments"));
- usage(stderr);
+ errx(EXIT_FAILURE, _("not enough arguments"));
}
/* Accept "tty", "baudrate tty", and "tty baudrate". */
- if ('0' <= argv[optind][0] && argv[optind][0] <= '9') {
+ if (is_speed(argv[optind])) {
/* Assume BSD style speed. */
parse_speeds(op, argv[optind++]);
if (argc < optind + 1) {
- warn(_("not enough arguments"));
- usage(stderr);
+ log_warn(_("not enough arguments"));
+ errx(EXIT_FAILURE, _("not enough arguments"));
}
op->tty = argv[optind++];
} else {
op->tty = argv[optind++];
if (argc > optind) {
- char *v = argv[optind++];
- if ('0' <= *v && *v <= '9')
+ char *v = argv[optind];
+ if (is_speed(v)) {
parse_speeds(op, v);
- else
- op->speeds[op->numspeed++] = bcode("9600");
+ optind++;
+ }
}
}
@@ -732,45 +880,6 @@ static void parse_args(int argc, char **argv, struct options *op)
if (argc > optind && argv[optind])
op->term = argv[optind];
-#ifdef DO_DEVFS_FIDDLING
- /*
- * Some devfs junk, following Goswin Brederlow:
- * turn ttyS<n> into tts/<n>
- * turn tty<n> into vc/<n>
- * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=72241
- */
- if (op->tty && strlen(op->tty) < 90) {
- char dev_name[100];
- struct stat st;
-
- if (strncmp(op->tty, "ttyS", 4) == 0) {
- strcpy(dev_name, "/dev/");
- strcat(dev_name, op->tty);
- if (stat(dev_name, &st) < 0) {
- strcpy(dev_name, "/dev/tts/");
- strcat(dev_name, op->tty + 4);
- if (stat(dev_name, &st) == 0) {
- op->tty = strdup(dev_name + 5);
- if (!op->tty)
- log_err(_("failed to allocate memory: %m"));
- }
- }
- } else if (strncmp(op->tty, "tty", 3) == 0) {
- strcpy(dev_name, "/dev/");
- strncat(dev_name, op->tty, 90);
- if (stat(dev_name, &st) < 0) {
- strcpy(dev_name, "/dev/vc/");
- strcat(dev_name, op->tty + 3);
- if (stat(dev_name, &st) == 0) {
- op->tty = strdup(dev_name + 5);
- if (!op->tty)
- log_err(_("failed to allocate memory: %m"));
- }
- }
- }
- }
-#endif /* DO_DEVFS_FIDDLING */
-
debug("exiting parseargs\n");
}
@@ -778,15 +887,20 @@ static void parse_args(int argc, char **argv, struct options *op)
static void parse_speeds(struct options *op, char *arg)
{
char *cp;
+ char *str = strdup(arg);
+
+ if (!str)
+ log_err(_("failed to allocate memory: %m"));
- debug("entered parse_speeds\n");
- for (cp = strtok(arg, ","); cp != NULL; cp = strtok((char *)0, ",")) {
+ debug("entered parse_speeds:\n");
+ for (cp = strtok(str, ","); cp != NULL; cp = strtok((char *)0, ",")) {
if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
log_err(_("bad speed: %s"), cp);
if (op->numspeed >= MAX_SPEED)
log_err(_("too many alternate speeds"));
}
debug("exiting parsespeeds\n");
+ free(str);
}
#ifdef SYSV_STYLE
@@ -794,13 +908,13 @@ static void parse_speeds(struct options *op, char *arg)
/* Update our utmp entry. */
static void update_utmp(struct options *op)
{
- struct utmp ut;
+ struct utmpx ut;
time_t t;
pid_t pid = getpid();
pid_t sid = getsid(0);
char *vcline = op->vcline;
char *line = op->tty;
- struct utmp *utp;
+ struct utmpx *utp;
/*
* The utmp file holds miscellaneous information about things started by
@@ -810,8 +924,8 @@ static void update_utmp(struct options *op)
* utmp file can be opened for update, and if we are able to find our
* entry in the utmp file.
*/
- utmpname(_PATH_UTMP);
- setutent();
+ utmpxname(_PATH_UTMP);
+ setutxent();
/*
* Find my pid in utmp.
@@ -822,7 +936,7 @@ static void update_utmp(struct options *op)
* FIXME: The present code is taken from login.c, so if this is changed,
* maybe login has to be changed as well (is this true?).
*/
- while ((utp = getutent()))
+ while ((utp = getutxent()))
if (utp->ut_pid == pid
&& utp->ut_type >= INIT_PROCESS
&& utp->ut_type <= DEAD_PROCESS)
@@ -852,33 +966,15 @@ static void update_utmp(struct options *op)
if (fakehost)
strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
time(&t);
- ut.ut_time = t;
+ ut.ut_tv.tv_sec = t;
ut.ut_type = LOGIN_PROCESS;
ut.ut_pid = pid;
ut.ut_session = sid;
- pututline(&ut);
- endutent();
+ pututxline(&ut);
+ endutxent();
- {
-#ifdef HAVE_UPDWTMP
- updwtmp(_PATH_WTMP, &ut);
-#else
- int ut_fd;
- int lf;
-
- if ((lf = open(_PATH_WTMPLOCK, O_CREAT | O_WRONLY, 0660)) >= 0) {
- flock(lf, LOCK_EX);
- if ((ut_fd =
- open(_PATH_WTMP, O_APPEND | O_WRONLY)) >= 0) {
- write_all(ut_fd, &ut, sizeof(ut));
- close(ut_fd);
- }
- flock(lf, LOCK_UN);
- close(lf);
- }
-#endif /* HAVE_UPDWTMP */
- }
+ updwtmpx(_PATH_WTMP, &ut);
}
#endif /* SYSV_STYLE */
@@ -888,6 +984,9 @@ static void open_tty(char *tty, struct termios *tp, struct options *op)
{
const pid_t pid = getpid();
int closed = 0;
+#ifndef KDGKBMODE
+ int serial;
+#endif
/* Set up new standard input, unless we are given an already opened port. */
@@ -903,8 +1002,12 @@ static void open_tty(char *tty, struct termios *tp, struct options *op)
if ((gr = getgrnam("tty")))
gid = gr->gr_gid;
- if (((len = snprintf(buf, sizeof(buf), "/dev/%s", tty)) >=
- (int)sizeof(buf)) || (len < 0))
+ len = snprintf(buf, sizeof(buf), "/dev/%s", tty);
+ if (len < 0 || (size_t)len >= sizeof(buf))
+ log_err(_("/dev/%s: cannot open as standard input: %m"), tty);
+
+ /* Open the tty as standard input. */
+ if ((fd = open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0)) < 0)
log_err(_("/dev/%s: cannot open as standard input: %m"), tty);
/*
@@ -913,24 +1016,20 @@ static void open_tty(char *tty, struct termios *tp, struct options *op)
* Linux login(1) will change tty permissions. Use root owner and group
* with permission -rw------- for the period between getty and login.
*/
- if (chown(buf, 0, gid) || chmod(buf, (gid ? 0620 : 0600))) {
+ if (fchown(fd, 0, gid) || fchmod(fd, (gid ? 0620 : 0600))) {
if (errno == EROFS)
log_warn("%s: %m", buf);
else
log_err("%s: %m", buf);
}
- /* Open the tty as standard input. */
- if ((fd = open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0)) < 0)
- log_err(_("/dev/%s: cannot open as standard input: %m"), tty);
-
/* Sanity checks... */
- if (!isatty(fd))
- log_err(_("/dev/%s: not a character device"), tty);
if (fstat(fd, &st) < 0)
log_err("%s: %m", buf);
if ((st.st_mode & S_IFMT) != S_IFCHR)
log_err(_("/dev/%s: not a character device"), tty);
+ if (!isatty(fd))
+ log_err(_("/dev/%s: not a tty"), tty);
if (((tid = tcgetsid(fd)) < 0) || (pid != tid)) {
if (ioctl(fd, TIOCSCTTY, 1) == -1)
@@ -946,7 +1045,7 @@ static void open_tty(char *tty, struct termios *tp, struct options *op)
debug("TIOCNOTTY ioctl failed\n");
/*
- * Let's close all file decriptors before vhangup
+ * Let's close all file descriptors before vhangup
* https://lkml.org/lkml/2012/6/5/145
*/
close(fd);
@@ -1015,22 +1114,66 @@ static void open_tty(char *tty, struct termios *tp, struct options *op)
if (tcgetattr(STDIN_FILENO, tp) < 0)
log_err(_("%s: failed to get terminal attributes: %m"), tty);
+#if defined (__s390__) || defined (__s390x__)
+ if (!op->term) {
+ /*
+ * Special terminal on first serial line on a S/390(x) which
+ * is due legacy reasons a block terminal of type 3270 or
+ * higher. Whereas the second serial line on a S/390(x) is
+ * a real character terminal which is compatible with VT220.
+ */
+ if (strcmp(op->tty, "ttyS0") == 0) /* linux/drivers/s390/char/con3215.c */
+ op->term = DEFAULT_TTYS0;
+ else if (strncmp(op->tty, "3270/tty", 8) == 0) /* linux/drivers/s390/char/con3270.c */
+ op->term = DEFAULT_TTY32;
+ else if (strcmp(op->tty, "ttyS1") == 0) /* linux/drivers/s390/char/sclp_vt220.c */
+ op->term = DEFAULT_TTYS1;
+ }
+#endif
+
+#if defined(__FreeBSD_kernel__)
+ login_tty (0);
+#endif
+
/*
* Detect if this is a virtual console or serial/modem line.
* In case of a virtual console the ioctl KDGKBMODE succeeds
* whereas on other lines it will fails.
*/
- if (ioctl(STDIN_FILENO, KDGKBMODE, &op->kbmode) == 0) {
+#ifdef KDGKBMODE
+ if (ioctl(STDIN_FILENO, KDGKBMODE, &op->kbmode) == 0)
+#else
+ if (ioctl(STDIN_FILENO, TIOCMGET, &serial) < 0 && (errno == EINVAL))
+#endif
+ {
op->flags |= F_VCONSOLE;
if (!op->term)
op->term = DEFAULT_VCTERM;
} else {
+#ifdef K_RAW
op->kbmode = K_RAW;
+#endif
if (!op->term)
op->term = DEFAULT_STERM;
}
- setenv("TERM", op->term, 1);
+ if (setenv("TERM", op->term, 1) != 0)
+ log_err(_("failed to set the %s environment variable"), "TERM");
+}
+
+/* Initialize termios settings. */
+static void termio_clear(int fd)
+{
+ /*
+ * Do not write a full reset (ESC c) because this destroys
+ * the unicode mode again if the terminal was in unicode
+ * mode. Also it clears the CONSOLE_MAGIC features which
+ * are required for some languages/console-fonts.
+ * Just put the cursor to the home position (ESC [ H),
+ * erase everything below the cursor (ESC [ J), and set the
+ * scrolling region to the full window (ESC [ r)
+ */
+ write_all(fd, "\033[r\033[H\033[J", 9);
}
/* Initialize termios settings. */
@@ -1038,6 +1181,28 @@ static void termio_init(struct options *op, struct termios *tp)
{
speed_t ispeed, ospeed;
struct winsize ws;
+#ifdef USE_PLYMOUTH_SUPPORT
+ struct termios lock;
+ int i = (plymouth_command(MAGIC_PING) == 0) ? PLYMOUTH_TERMIOS_FLAGS_DELAY : 0;
+ if (i)
+ plymouth_command(MAGIC_QUIT);
+ while (i-- > 0) {
+ /*
+ * Even with TTYReset=no it seems with systemd or plymouth
+ * the termios flags become changed from under the first
+ * agetty on a serial system console as the flags are locked.
+ */
+ memset(&lock, 0, sizeof(struct termios));
+ if (ioctl(STDIN_FILENO, TIOCGLCKTRMIOS, &lock) < 0)
+ break;
+ if (!lock.c_iflag && !lock.c_oflag && !lock.c_cflag && !lock.c_lflag)
+ break;
+ debug("termios locked\n");
+ sleep(1);
+ }
+ memset(&lock, 0, sizeof(struct termios));
+ ioctl(STDIN_FILENO, TIOCSLCKTRMIOS, &lock);
+#endif
if (op->flags & F_VCONSOLE) {
#if defined(IUTF8) && defined(KDGKBMODE)
@@ -1063,22 +1228,16 @@ static void termio_init(struct options *op, struct termios *tp)
if ((tp->c_cflag & (CS8|PARODD|PARENB)) == CS8)
op->flags |= F_EIGHTBITS;
- if ((op->flags & F_NOCLEAR) == 0) {
- /*
- * Do not write a full reset (ESC c) because this destroys
- * the unicode mode again if the terminal was in unicode
- * mode. Also it clears the CONSOLE_MAGIC features which
- * are required for some languages/console-fonts.
- * Just put the cursor to the home position (ESC [ H),
- * erase everything below the cursor (ESC [ J), and set the
- * scrolling region to the full window (ESC [ r)
- */
- write_all(STDOUT_FILENO, "\033[r\033[H\033[J", 9);
- }
+ if ((op->flags & F_NOCLEAR) == 0)
+ termio_clear(STDOUT_FILENO);
return;
}
- if (op->flags & F_KEEPSPEED) {
+ /*
+ * Serial line
+ */
+
+ if (op->flags & F_KEEPSPEED || !op->numspeed) {
/* Save the original setting. */
ispeed = cfgetispeed(tp);
ospeed = cfgetospeed(tp);
@@ -1097,9 +1256,6 @@ static void termio_init(struct options *op, struct termios *tp)
* later on.
*/
- /* Flush input and output queues, important for modems! */
- tcflush(STDIN_FILENO, TCIOFLUSH);
-
/* The defaul is set c_iflag in termio_final() according to chardata.
* Unfortunately, the chardata are not set according to the serial line
* if --autolog is enabled. In this case we do not read from the line
@@ -1124,7 +1280,7 @@ static void termio_init(struct options *op, struct termios *tp)
/*
* Note that the speed is stored in the c_cflag termios field, so we have
- * set the speed always when the cflag se reseted.
+ * set the speed always when the cflag is reset.
*/
cfsetispeed(tp, ispeed);
cfsetospeed(tp, ospeed);
@@ -1150,15 +1306,10 @@ static void termio_init(struct options *op, struct termios *tp)
/* Check for terminal size and if not found set default */
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) {
- int set = 0;
- if (ws.ws_row == 0) {
+ if (ws.ws_row == 0)
ws.ws_row = 24;
- set++;
- }
- if (ws.ws_col == 0) {
+ if (ws.ws_col == 0)
ws.ws_col = 80;
- set++;
- }
if (ioctl(STDIN_FILENO, TIOCSWINSZ, &ws))
debug("TIOCSWINSZ ioctl failed\n");
}
@@ -1168,8 +1319,11 @@ static void termio_init(struct options *op, struct termios *tp)
if (op->flags & F_RTSCTS)
tp->c_cflag |= CRTSCTS;
#endif
+ /* Flush input and output queues, important for modems! */
+ tcflush(STDIN_FILENO, TCIOFLUSH);
- tcsetattr(STDIN_FILENO, TCSANOW, tp);
+ if (tcsetattr(STDIN_FILENO, TCSANOW, tp))
+ log_warn(_("setting terminal attributes failed: %m"));
/* Go to blocking input even in local mode. */
fcntl(STDIN_FILENO, F_SETFL,
@@ -1190,6 +1344,10 @@ static void reset_vc(const struct options *op, struct termios *tp)
if (tcsetattr(STDIN_FILENO, TCSADRAIN, tp))
log_warn(_("setting terminal attributes failed: %m"));
+
+ /* Go to blocking input even in local mode. */
+ fcntl(STDIN_FILENO, F_SETFL,
+ fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
}
/* Extract baud rate from modem status message. */
@@ -1273,7 +1431,7 @@ static char *xgetdomainname(void)
{
#ifdef HAVE_GETDOMAINNAME
char *name;
- size_t sz = get_hostname_max() + 1;
+ const size_t sz = get_hostname_max() + 1;
name = malloc(sizeof(char) * sz);
if (!name)
@@ -1285,8 +1443,9 @@ static char *xgetdomainname(void)
}
name[sz - 1] = '\0';
return name;
-#endif
+#else
return NULL;
+#endif
}
@@ -1299,10 +1458,13 @@ static char *read_os_release(struct options *op, const char *varname)
/* read the file only once */
if (!op->osrelease) {
- fd = open(_PATH_OS_RELEASE, O_RDONLY);
+ fd = open(_PATH_OS_RELEASE_ETC, O_RDONLY);
if (fd == -1) {
- log_warn(_("cannot open: %s: %m"), _PATH_OS_RELEASE);
- return NULL;
+ fd = open(_PATH_OS_RELEASE_USR, O_RDONLY);
+ if (fd == -1) {
+ log_warn(_("cannot open os-release file"));
+ return NULL;
+ }
}
if (fstat(fd, &st) < 0 || st.st_size > 4 * 1024 * 1024)
@@ -1359,8 +1521,7 @@ static char *read_os_release(struct options *op, const char *varname)
}
break;
}
- if (ret)
- free(ret);
+ free(ret);
ret = strdup(p);
if (!ret)
log_err(_("failed to allocate memory: %m"));
@@ -1373,20 +1534,251 @@ done:
return ret;
}
-/* Show login prompt, optionally preceded by /etc/issue contents. */
-static void do_prompt(struct options *op, struct termios *tp)
+#ifdef AGETTY_RELOAD
+static void open_netlink(void)
{
-#ifdef ISSUE
- FILE *fd;
-#endif /* ISSUE */
+ struct sockaddr_nl addr = { 0, };
+ int sock;
+ if (netlink_fd != AGETTY_RELOAD_FDNONE)
+ return;
+
+ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sock >= 0) {
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pid = getpid();
+ addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ close(sock);
+ else
+ netlink_fd = sock;
+ }
+}
+
+static int process_netlink_msg(int *changed)
+{
+ char buf[4096];
+ struct sockaddr_nl snl;
+ struct nlmsghdr *h;
+ int rc;
+
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf)
+ };
+ struct msghdr msg = {
+ .msg_name = &snl,
+ .msg_namelen = sizeof(snl),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0
+ };
+
+ rc = recvmsg(netlink_fd, &msg, MSG_DONTWAIT);
+ if (rc < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return 0;
+
+ /* Failure, just stop listening for changes */
+ close(netlink_fd);
+ netlink_fd = AGETTY_RELOAD_FDNONE;
+ return 0;
+ }
+
+ for (h = (struct nlmsghdr *)buf; NLMSG_OK(h, (unsigned int)rc); h = NLMSG_NEXT(h, rc)) {
+ if (h->nlmsg_type == NLMSG_DONE ||
+ h->nlmsg_type == NLMSG_ERROR) {
+ close(netlink_fd);
+ netlink_fd = AGETTY_RELOAD_FDNONE;
+ return 0;
+ }
+
+ *changed = 1;
+ break;
+ }
+
+ return 1;
+}
+
+static int process_netlink(void)
+{
+ int changed = 0;
+ while (process_netlink_msg(&changed));
+ return changed;
+}
+
+static int wait_for_term_input(int fd)
+{
+ char buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
+ fd_set rfds;
+
+ if (inotify_fd == AGETTY_RELOAD_FDNONE) {
+ /* make sure the reload trigger file exists */
+ int reload_fd = open(AGETTY_RELOAD_FILENAME,
+ O_CREAT|O_CLOEXEC|O_RDONLY,
+ S_IRUSR|S_IWUSR);
+
+ /* initialize reload trigger inotify stuff */
+ if (reload_fd >= 0) {
+ inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+ if (inotify_fd > 0)
+ inotify_add_watch(inotify_fd, AGETTY_RELOAD_FILENAME,
+ IN_ATTRIB | IN_MODIFY);
+
+ close(reload_fd);
+ } else
+ log_warn(_("failed to create reload file: %s: %m"),
+ AGETTY_RELOAD_FILENAME);
+ }
+
+ while (1) {
+ int nfds = fd;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ if (inotify_fd >= 0) {
+ FD_SET(inotify_fd, &rfds);
+ nfds = max(nfds, inotify_fd);
+ }
+ if (netlink_fd >= 0) {
+ FD_SET(netlink_fd, &rfds);
+ nfds = max(nfds, netlink_fd);
+ }
+
+ /* If waiting fails, just fall through, presumably reading input will fail */
+ if (select(nfds + 1, &rfds, NULL, NULL, NULL) < 0)
+ return 1;
+
+ if (FD_ISSET(fd, &rfds)) {
+ return 1;
+
+ } else if (netlink_fd >= 0 && FD_ISSET(netlink_fd, &rfds)) {
+ if (!process_netlink())
+ continue;
+
+ /* Just drain the inotify buffer */
+ } else if (inotify_fd >= 0 && FD_ISSET(inotify_fd, &rfds)) {
+ while (read(inotify_fd, buffer, sizeof (buffer)) > 0);
+ }
+
+ return 0;
+ }
+}
+#endif /* AGETTY_RELOAD */
+
+#ifdef ISSUEDIR_SUPPORT
+static int issuedir_filter(const struct dirent *d)
+{
+ size_t namesz;
+
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG &&
+ d->d_type != DT_LNK)
+ return 0;
+#endif
+ if (*d->d_name == '.')
+ return 0;
+
+ namesz = strlen(d->d_name);
+ if (!namesz || namesz < ISSUEDIR_EXTSIZ + 1 ||
+ strcmp(d->d_name + (namesz - ISSUEDIR_EXTSIZ), ISSUEDIR_EXT))
+ return 0;
+
+ /* Accept this */
+ return 1;
+}
+
+static FILE *issuedir_next_file(int dd, struct dirent **namelist, int nfiles, int *n)
+{
+ while (*n < nfiles) {
+ struct dirent *d = namelist[*n];
+ struct stat st;
+ FILE *f;
+
+ (*n)++;
+
+ if (fstatat(dd, d->d_name, &st, 0) ||
+ !S_ISREG(st.st_mode))
+ continue;
+
+ f = fopen_at(dd, d->d_name, O_RDONLY|O_CLOEXEC, "r" UL_CLOEXECSTR);
+ if (f)
+ return f;
+ }
+ return NULL;
+}
+
+#endif /* ISSUEDIR_SUPPORT */
+
+#ifndef ISSUE_SUPPORT
+static void print_issue_file(struct options *op, struct termios *tp __attribute__((__unused__)))
+{
+ if ((op->flags & F_NONL) == 0) {
+ /* Issue not in use, start with a new line. */
+ write_all(STDOUT_FILENO, "\r\n", 2);
+ }
+}
+#else /* ISSUE_SUPPORT */
+
+static void print_issue_file(struct options *op, struct termios *tp)
+{
+ const char *filename, *dirname = NULL;
+ FILE *f = NULL;
+#ifdef ISSUEDIR_SUPPORT
+ int dd = -1, nfiles = 0, i;
+ struct dirent **namelist = NULL;
+#endif
if ((op->flags & F_NONL) == 0) {
/* Issue not in use, start with a new line. */
write_all(STDOUT_FILENO, "\r\n", 2);
}
-#ifdef ISSUE
- if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) {
+ if (!(op->flags & F_ISSUE))
+ return;
+
+ /*
+ * The custom issue file or directory specified by: agetty -f <path>.
+ * Note that nothing is printed if the file/dir does not exist.
+ */
+ filename = op->issue;
+ if (filename) {
+ struct stat st;
+
+ if (stat(filename, &st) < 0)
+ return;
+ if (S_ISDIR(st.st_mode)) {
+ dirname = filename;
+ filename = NULL;
+ }
+ } else {
+ /* The default /etc/issue and optional /etc/issue.d directory
+ * as extension to the file. The /etc/issue.d directory is
+ * ignored if there is no /etc/issue file. The file may be
+ * empty or symlink.
+ */
+ if (access(_PATH_ISSUE, F_OK|R_OK) != 0)
+ return;
+ filename = _PATH_ISSUE;
+ dirname = _PATH_ISSUEDIR;
+ }
+
+#ifdef ISSUEDIR_SUPPORT
+ if (dirname) {
+ dd = open(dirname, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (dd >= 0)
+ nfiles = scandirat(dd, ".", &namelist, issuedir_filter, versionsort);
+ if (nfiles <= 0)
+ dirname = NULL;
+ }
+ i = 0;
+#endif
+ if (filename)
+ f = fopen(filename, "r");
+
+ if (f || dirname) {
int c, oflag = tp->c_oflag; /* Save current setting. */
if ((op->flags & F_VCONSOLE) == 0) {
@@ -1395,12 +1787,23 @@ static void do_prompt(struct options *op, struct termios *tp)
tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
}
- while ((c = getc(fd)) != EOF) {
- if (c == '\\')
- output_special_char(getc(fd), op, tp, fd);
- else
- putchar(c);
- }
+ do {
+#ifdef ISSUEDIR_SUPPORT
+ if (!f && i < nfiles)
+ f = issuedir_next_file(dd, namelist, nfiles, &i);
+#endif
+ if (!f)
+ break;
+ while ((c = getc(f)) != EOF) {
+ if (c == '\\')
+ output_special_char(getc(f), op, tp, f);
+ else
+ putchar(c);
+ }
+ fclose(f);
+ f = NULL;
+ } while (dirname);
+
fflush(stdout);
if ((op->flags & F_VCONSOLE) == 0) {
@@ -1409,11 +1812,36 @@ static void do_prompt(struct options *op, struct termios *tp)
/* Wait till output is gone. */
tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
}
- fclose(fd);
}
-#endif /* ISSUE */
+
+#ifdef ISSUEDIR_SUPPORT
+ for (i = 0; i < nfiles; i++)
+ free(namelist[i]);
+ free(namelist);
+ if (dd >= 0)
+ close(dd);
+#endif
+}
+#endif /* ISSUE_SUPPORT */
+
+/* Show login prompt, optionally preceded by /etc/issue contents. */
+static void do_prompt(struct options *op, struct termios *tp)
+{
+#ifdef AGETTY_RELOAD
+again:
+#endif
+ print_issue_file(op, tp);
+
if (op->flags & F_LOGINPAUSE) {
puts(_("[press ENTER to login]"));
+#ifdef AGETTY_RELOAD
+ if (!wait_for_term_input(STDIN_FILENO)) {
+ /* reload issue */
+ if (op->flags & F_VCONSOLE)
+ termio_clear(STDOUT_FILENO);
+ goto again;
+ }
+#endif
getc(stdin);
}
#ifdef KDGKBLED
@@ -1533,18 +1961,36 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata
*bp = '\0';
while (*logname == '\0') {
-
/* Write issue file and prompt */
do_prompt(op, tp);
+#ifdef AGETTY_RELOAD
+ if (!wait_for_term_input(STDIN_FILENO)) {
+ /* refresh prompt -- discard input data, clear terminal
+ * and call do_prompt() again
+ */
+ if ((op->flags & F_VCONSOLE) == 0)
+ sleep(1);
+ tcflush(STDIN_FILENO, TCIFLUSH);
+ if (op->flags & F_VCONSOLE)
+ termio_clear(STDOUT_FILENO);
+ bp = logname;
+ *bp = '\0';
+ continue;
+ }
+#endif
cp->eol = '\0';
/* Read name, watch for break and end-of-line. */
while (cp->eol == '\0') {
char key;
+ ssize_t readres;
- if (read(STDIN_FILENO, &c, 1) < 1) {
+ debug("read from FD\n");
+ readres = read(STDIN_FILENO, &c, 1);
+ if (readres < 0) {
+ debug("read failed\n");
/* The terminal could be open with O_NONBLOCK when
* -L (force CLOCAL) is specified... */
@@ -1558,12 +2004,15 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata
case ESRCH:
case EINVAL:
case ENOENT:
- break;
+ exit_slowly(EXIT_SUCCESS);
default:
log_err(_("%s: read: %m"), op->tty);
}
}
+ if (readres == 0)
+ c = 0;
+
/* Do parity bit handling. */
if (eightbit)
ascval = c;
@@ -1588,8 +2037,10 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata
switch (key) {
case 0:
*bp = 0;
- if (op->numspeed > 1)
+ if (op->numspeed > 1 && !(op->flags & F_VCONSOLE))
return NULL;
+ if (readres == 0)
+ exit_slowly(EXIT_SUCCESS);
break;
case CR:
case NL:
@@ -1627,6 +2078,7 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata
}
}
}
+
#ifdef HAVE_WIDECHAR
if ((op->flags & (F_EIGHTBITS|F_UTF8)) == (F_EIGHTBITS|F_UTF8)) {
/* Check out UTF-8 multibyte characters */
@@ -1637,7 +2089,7 @@ static char *get_logname(struct options *op, struct termios *tp, struct chardata
if (len < 0)
log_err(_("%s: invalid character conversion for login name"), op->tty);
- wcs = (wchar_t *) malloc((len + 1) * sizeof(wchar_t));
+ wcs = malloc((len + 1) * sizeof(wchar_t));
if (!wcs)
log_err(_("failed to allocate memory: %m"));
@@ -1702,12 +2154,12 @@ static void termio_final(struct options *op, struct termios *tp, struct chardata
case 1:
/* odd parity */
tp->c_cflag |= PARODD;
- /* do not break */
+ /* fallthrough */
case 2:
/* even parity */
tp->c_cflag |= PARENB;
tp->c_iflag |= INPCK | ISTRIP;
- /* do not break */
+ /* fallthrough */
case (1 | 2):
/* no parity bit */
tp->c_cflag &= ~CSIZE;
@@ -1767,11 +2219,17 @@ static speed_t bcode(char *s)
return 0;
}
-static void __attribute__ ((__noreturn__)) usage(FILE *out)
+static void __attribute__((__noreturn__)) usage(void)
{
+ FILE *out = stdout;
+
fputs(USAGE_HEADER, out);
- fprintf(out, _(" %1$s [options] line [baud_rate,...] [termtype]\n"
- " %1$s [options] baud_rate,... line [termtype]\n"), program_invocation_short_name);
+ fprintf(out, _(" %1$s [options] <line> [<baud_rate>,...] [<termtype>]\n"
+ " %1$s [options] <baud_rate>,... <line> [<termtype>]\n"), program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Open a terminal and set its mode.\n"), out);
+
fputs(USAGE_OPTIONS, out);
fputs(_(" -8, --8bits assume 8-bit tty\n"), out);
fputs(_(" -a, --autologin <user> login the specified user automatically\n"), out);
@@ -1782,10 +2240,12 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out)
fputs(_(" -H, --host <hostname> specify login host\n"), out);
fputs(_(" -i, --noissue do not display issue file\n"), out);
fputs(_(" -I, --init-string <string> set init string\n"), out);
+ fputs(_(" -J --noclear do not clear the screen before prompt\n"), out);
fputs(_(" -l, --login-program <file> specify login program\n"), out);
- fputs(_(" -L, --local-line[=<mode>] cotrol local line flag\n"), out);
+ fputs(_(" -L, --local-line[=<mode>] control the local line flag\n"), out);
fputs(_(" -m, --extract-baud extract baud rate during connect\n"), out);
fputs(_(" -n, --skip-login do not prompt for login\n"), out);
+ fputs(_(" -N --nonewline do not print a newline before issue\n"), out);
fputs(_(" -o, --login-options <opts> options that are passed to login\n"), out);
fputs(_(" -p, --login-pause wait for any key before the login\n"), out);
fputs(_(" -r, --chroot <dir> change root to the directory\n"), out);
@@ -1794,18 +2254,29 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out)
fputs(_(" -t, --timeout <number> login process timeout\n"), out);
fputs(_(" -U, --detect-case detect uppercase terminal\n"), out);
fputs(_(" -w, --wait-cr wait carriage-return\n"), out);
- fputs(_(" --noclear do not clear the screen before prompt\n"), out);
fputs(_(" --nohints do not print hints\n"), out);
- fputs(_(" --nonewline do not print a newline before issue\n"), out);
fputs(_(" --nohostname no hostname at all will be shown\n"), out);
fputs(_(" --long-hostname show full qualified hostname\n"), out);
fputs(_(" --erase-chars <string> additional backspace chars\n"), out);
fputs(_(" --kill-chars <string> additional kill chars\n"), out);
- fputs(_(" --help display this help and exit\n"), out);
- fputs(_(" --version output version information and exit\n"), out);
- fprintf(out, USAGE_MAN_TAIL("agetty(8)"));
+ fputs(_(" --chdir <directory> chdir before the login\n"), out);
+ fputs(_(" --delay <number> sleep seconds before prompt\n"), out);
+ fputs(_(" --nice <number> run login with this priority\n"), out);
+ fputs(_(" --reload reload prompts on running agetty instances\n"), out);
+ fputs(_(" --list-speeds display supported baud rates\n"), out);
+ printf( " --help %s\n", USAGE_OPTSTR_HELP);
+ printf( " --version %s\n", USAGE_OPTSTR_VERSION);
+ printf(USAGE_MAN_TAIL("agetty(8)"));
+
+ exit(EXIT_SUCCESS);
+}
+
+static void list_speeds(void)
+{
+ const struct Speedtab *sp;
- exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+ for (sp = speedtab; sp->speed; sp++)
+ printf("%10ld\n", sp->speed);
}
/*
@@ -1855,6 +2326,13 @@ static void dolog(int priority, const char *fmt, va_list ap)
#endif /* USE_SYSLOG */
}
+static void exit_slowly(int code)
+{
+ /* Be kind to init(8). */
+ sleep(10);
+ exit(code);
+}
+
static void log_err(const char *fmt, ...)
{
va_list ap;
@@ -1863,9 +2341,7 @@ static void log_err(const char *fmt, ...)
dolog(LOG_ERR, fmt, ap);
va_end(ap);
- /* Be kind to init(8). */
- sleep(10);
- exit(EXIT_FAILURE);
+ exit_slowly(EXIT_FAILURE);
}
static void log_warn(const char *fmt, ...)
@@ -1877,41 +2353,70 @@ static void log_warn(const char *fmt, ...)
va_end(ap);
}
-static void output_iface_ip(struct ifaddrs *addrs, const char *iface, sa_family_t family)
+static void print_addr(sa_family_t family, void *addr)
+{
+ char buff[INET6_ADDRSTRLEN + 1];
+
+ inet_ntop(family, addr, buff, sizeof(buff));
+ printf("%s", buff);
+}
+
+/*
+ * Prints IP for the specified interface (@iface), if the interface is not
+ * specified then prints the "best" one (UP, RUNNING, non-LOOPBACK). If not
+ * found the "best" interface then prints at least host IP.
+ */
+static void output_iface_ip(struct ifaddrs *addrs,
+ const char *iface,
+ sa_family_t family)
{
- if (!iface)
+ struct ifaddrs *p;
+ struct addrinfo hints, *info = NULL;
+ char *host = NULL;
+ void *addr = NULL;
+
+ if (!addrs)
return;
- if (addrs->ifa_name
- && strcmp(addrs->ifa_name, iface) == 0
- && addrs->ifa_addr
- && addrs->ifa_addr->sa_family == family) {
+ for (p = addrs; p; p = p->ifa_next) {
- void *addr = NULL;
- char buff[INET6_ADDRSTRLEN + 1];
+ if (!p->ifa_name ||
+ !p->ifa_addr ||
+ p->ifa_addr->sa_family != family)
+ continue;
+
+ if (iface) {
+ /* Filter out by interface name */
+ if (strcmp(p->ifa_name, iface) != 0)
+ continue;
+ } else {
+ /* Select the "best" interface */
+ if ((p->ifa_flags & IFF_LOOPBACK) ||
+ !(p->ifa_flags & IFF_UP) ||
+ !(p->ifa_flags & IFF_RUNNING))
+ continue;
+ }
- switch (addrs->ifa_addr->sa_family) {
+ addr = NULL;
+ switch (p->ifa_addr->sa_family) {
case AF_INET:
- addr = &((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
+ addr = &((struct sockaddr_in *) p->ifa_addr)->sin_addr;
break;
case AF_INET6:
- addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
+ addr = &((struct sockaddr_in6 *) p->ifa_addr)->sin6_addr;
break;
}
+
if (addr) {
- inet_ntop(addrs->ifa_addr->sa_family, addr, buff, sizeof(buff));
- printf("%s", buff);
+ print_addr(family, addr);
+ return;
}
+ }
- } else if (addrs->ifa_next)
- output_iface_ip(addrs->ifa_next, iface, family);
-}
-
-static void output_ip(sa_family_t family)
-{
- char *host;
- struct addrinfo hints, *info = NULL;
+ if (iface)
+ return;
+ /* Hmm.. not found the best interface, print host IP at least */
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
if (family == AF_INET6)
@@ -1919,9 +2424,6 @@ static void output_ip(sa_family_t family)
host = xgethostname();
if (host && getaddrinfo(host, NULL, &hints, &info) == 0 && info) {
-
- void *addr = NULL;
-
switch (info->ai_family) {
case AF_INET:
addr = &((struct sockaddr_in *) info->ai_addr)->sin_addr;
@@ -1930,12 +2432,8 @@ static void output_ip(sa_family_t family)
addr = &((struct sockaddr_in6 *) info->ai_addr)->sin6_addr;
break;
}
- if (addr) {
- char buff[INET6_ADDRSTRLEN + 1];
-
- inet_ntop(info->ai_family, (void *) addr, buff, sizeof(buff));
- printf("%s", buff);
- }
+ if (addr)
+ print_addr(family, addr);
freeaddrinfo(info);
}
@@ -1974,22 +2472,25 @@ static void output_special_char(unsigned char c, struct options *op,
{
struct utsname uts;
- uname(&uts);
-
switch (c) {
case 's':
+ uname(&uts);
printf("%s", uts.sysname);
break;
case 'n':
+ uname(&uts);
printf("%s", uts.nodename);
break;
case 'r':
+ uname(&uts);
printf("%s", uts.release);
break;
case 'v':
+ uname(&uts);
printf("%s", uts.version);
break;
case 'm':
+ uname(&uts);
printf("%s", uts.machine);
break;
case 'o':
@@ -2066,30 +2567,39 @@ static void output_special_char(unsigned char c, struct options *op,
{
char *var = NULL, varname[64];
- if (get_escape_argument(fp, varname, sizeof(varname)))
+ /* \S{varname} */
+ if (get_escape_argument(fp, varname, sizeof(varname))) {
var = read_os_release(op, varname);
- else if (!(var = read_os_release(op, "PRETTY_NAME")))
- var = uts.sysname;
- if (var) {
- if (strcmp(varname, "ANSI_COLOR") == 0)
- printf("\033[%sm", var);
- else
- printf("%s", var);
- if (var != uts.sysname)
- free(var);
+ if (var) {
+ if (strcmp(varname, "ANSI_COLOR") == 0)
+ printf("\033[%sm", var);
+ else
+ fputs(var, stdout);
+ }
+ /* \S */
+ } else if ((var = read_os_release(op, "PRETTY_NAME"))) {
+ fputs(var, stdout);
+
+ /* \S and PRETTY_NAME not found */
+ } else {
+ uname(&uts);
+ fputs(uts.sysname, stdout);
}
+
+ free(var);
+
break;
}
case 'u':
case 'U':
{
int users = 0;
- struct utmp *ut;
- setutent();
- while ((ut = getutent()))
+ struct utmpx *ut;
+ setutxent();
+ while ((ut = getutxent()))
if (ut->ut_type == USER_PROCESS)
users++;
- endutent();
+ endutxent();
if (c == 'U')
printf(P_("%d user", "%d users", users), users);
else
@@ -2100,17 +2610,22 @@ static void output_special_char(unsigned char c, struct options *op,
case '6':
{
sa_family_t family = c == '4' ? AF_INET : AF_INET6;
+ struct ifaddrs *addrs = NULL;
char iface[128];
- if (get_escape_argument(fp, iface, sizeof(iface))) { /* interface IP */
- struct ifaddrs *addrs;
- int status = getifaddrs(&addrs);
- if (status != 0)
- break;
+#ifdef AGETTY_RELOAD
+ open_netlink();
+#endif
+
+ if (getifaddrs(&addrs))
+ break;
+
+ if (get_escape_argument(fp, iface, sizeof(iface)))
output_iface_ip(addrs, iface, family);
- freeifaddrs(addrs);
- } else /* host IP */
- output_ip(family);
+ else
+ output_iface_ip(addrs, NULL, family);
+
+ freeifaddrs(addrs);
break;
}
default:
@@ -2162,7 +2677,7 @@ static void init_special_char(char* arg, struct options *op)
}
/*
- * Appends @str to @dest and if @dest is not empty then use use @sep as a
+ * Appends @str to @dest and if @dest is not empty then use @sep as a
* separator. The maximal final length of the @dest is @len.
*
* Returns the final @dest length or -1 in case of error.
@@ -2217,3 +2732,19 @@ err:
log_err(_("checkname failed: %m"));
}
+static void reload_agettys(void)
+{
+#ifdef AGETTY_RELOAD
+ int fd = open(AGETTY_RELOAD_FILENAME, O_CREAT|O_CLOEXEC|O_WRONLY,
+ S_IRUSR|S_IWUSR);
+ if (fd < 0)
+ err(EXIT_FAILURE, _("cannot open %s"), AGETTY_RELOAD_FILENAME);
+
+ if (futimens(fd, NULL) < 0 || close(fd) < 0)
+ err(EXIT_FAILURE, _("cannot touch file %s"),
+ AGETTY_RELOAD_FILENAME);
+#else
+ /* very unusual */
+ errx(EXIT_FAILURE, _("--reload is unsupported on your system"));
+#endif
+}
--
2.20.1