From ea152b4e9656945f17d18bf4e5ece91e3e57ebb1 Mon Sep 17 00:00:00 2001 From: Karel Zak 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 --- 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 #include #include @@ -20,19 +21,21 @@ #include #include #include +#include #include #include #include -#include +#include #include #include -#include #include #include #include #include #include #include +#include +#include #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 +#endif + +#if defined(__FreeBSD_kernel__) +# include +# ifdef HAVE_UTMP_H +# include +# endif +# ifdef HAVE_LIBUTIL_H +# include +# endif +#endif #ifdef __linux__ # include -# include # 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 @@ -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 +# 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 +# include +# include +# 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 into tts/ - * turn tty into vc/ - * 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 . + * 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] [,...] []\n" + " %1$s [options] ,... []\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 login the specified user automatically\n"), out); @@ -1782,10 +2240,12 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out) fputs(_(" -H, --host specify login host\n"), out); fputs(_(" -i, --noissue do not display issue file\n"), out); fputs(_(" -I, --init-string set init string\n"), out); + fputs(_(" -J --noclear do not clear the screen before prompt\n"), out); fputs(_(" -l, --login-program specify login program\n"), out); - fputs(_(" -L, --local-line[=] cotrol local line flag\n"), out); + fputs(_(" -L, --local-line[=] 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 options that are passed to login\n"), out); fputs(_(" -p, --login-pause wait for any key before the login\n"), out); fputs(_(" -r, --chroot change root to the directory\n"), out); @@ -1794,18 +2254,29 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out) fputs(_(" -t, --timeout 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 additional backspace chars\n"), out); fputs(_(" --kill-chars 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 chdir before the login\n"), out); + fputs(_(" --delay sleep seconds before prompt\n"), out); + fputs(_(" --nice 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