#include <netdb.h>
#include <ifaddrs.h>
#include <net/if.h>
+#include <sys/utsname.h>
#include "strutils.h"
#include "all-io.h"
/*
* 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
/* Displayed before the login prompt. */
#ifdef SYSV_STYLE
-# define ISSUE _PATH_ISSUE
-# include <sys/utsname.h>
+# define ISSUE_SUPPORT
+# if defined(HAVE_SCANDIRAT) && defined(HAVE_OPENAT)
+# include <dirent.h>
+# include "fileutils.h"
+# define ISSUEDIR_SUPPORT
+# define ISSUEDIR_EXT ".issue"
+# define ISSUEDIR_EXTSIZ (sizeof(ISSUEDIR_EXT) - 1)
+# endif
#endif
/* Login prompt. */
# define AGETTY_RELOAD_FDNONE -2 /* uninitialized fd */
static int inotify_fd = AGETTY_RELOAD_FDNONE;
static int netlink_fd = AGETTY_RELOAD_FDNONE;
+static uint32_t netlink_groups;
+#endif
+
+struct issue {
+ FILE *output;
+ char *mem;
+ size_t mem_sz;
+
+#ifdef AGETTY_RELOAD
+ char *mem_old;
#endif
+ unsigned int do_tcsetattr : 1,
+ do_tcrestore : 1;
+};
/*
* When multiple baud rates are specified on the command line, the first one
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 */
};
#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 */
static void update_utmp(struct options *op);
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 reset_vc(const struct options *op, struct termios *tp, int canon);
static void auto_baud(struct termios *tp);
-static void output_special_char (unsigned char c, struct options *op,
+static void list_speeds(void);
+static void output_special_char (struct issue *ie, unsigned char c, struct options *op,
struct termios *tp, FILE *fp);
-static void do_prompt(struct options *op, struct termios *tp);
+static void do_prompt(struct issue *ie, struct options *op, struct termios *tp);
static void next_speed(struct options *op, struct termios *tp);
-static char *get_logname(struct options *op,
+static char *get_logname(struct issue *ie, struct options *op,
struct termios *tp, struct chardata *cp);
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 *, ...)
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 issue *ie, struct options *op, struct termios *tp);
+static void eval_issue_file(struct issue *ie, struct options *op, struct termios *tp);
/* Fake hostname for ut_host specified on command line. */
static char *fakehost;
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 */
+ };
+ struct issue issue = {
+ .mem = NULL,
};
char *login_argv[LOGIN_ARGV_MAX + 1];
int login_argc = 0;
username = options.autolog;
}
- if ((options.flags & F_NOPROMPT) == 0) {
+ if (options.flags & F_NOPROMPT) { /* --skip-login */
+ eval_issue_file(&issue, &options, &termios);
+ print_issue_file(&issue, &options, &termios);
+ } else { /* regular (auto)login */
if (options.autolog) {
/* Autologin prompt */
- do_prompt(&options, &termios);
+ eval_issue_file(&issue, &options, &termios);
+ do_prompt(&issue, &options, &termios);
printf(_("%s%s (automatic login)\n"), LOGIN, options.autolog);
} else {
/* Read the login name. */
debug("reading login name\n");
while ((username =
- get_logname(&options, &termios, &chardata)) == NULL)
+ get_logname(&issue, &options, &termios, &chardata)) == NULL)
if ((options.flags & F_VCONSOLE) == 0 && options.numspeed)
next_speed(&options, &termios);
}
if (options.timeout)
alarm(0);
- if ((options.flags & F_VCONSOLE) == 0) {
- /* Finalize the termios settings. */
+ /* Finalize the termios settings. */
+ if ((options.flags & F_VCONSOLE) == 0)
termio_final(&options, &termios, &chardata);
+ else
+ reset_vc(&options, &termios, 1);
- /* Now the newline character should be properly written. */
- write_all(STDOUT_FILENO, "\r\n", 2);
- }
+ /* Now the newline character should be properly written. */
+ write_all(STDOUT_FILENO, "\r\n", 2);
sigaction(SIGQUIT, &sa_quit, NULL);
sigaction(SIGINT, &sa_int, NULL);
*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. */
ERASE_CHARS_OPTION,
KILL_CHARS_OPTION,
RELOAD_OPTION,
+ LIST_SPEEDS_OPTION,
};
const struct option longopts[] = {
{ "8bits", no_argument, NULL, '8' },
{ "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' },
op->flags |= F_REMOTE;
break;
case 'f':
- op->flags |= F_CUSTISSUE;
op->issue = optarg;
break;
case 'h':
case RELOAD_OPTION:
reload_agettys();
exit(EXIT_SUCCESS);
+ case LIST_SPEEDS_OPTION:
+ list_speeds();
+ exit(EXIT_SUCCESS);
case VERSION_OPTION:
- printf(UTIL_LINUX_VERSION);
+ output_version();
exit(EXIT_SUCCESS);
case HELP_OPTION:
- usage(stdout);
+ usage();
default:
- usage(stderr);
+ errtryhelp(EXIT_FAILURE);
}
}
if (argc < optind + 1) {
log_warn(_("not enough arguments"));
- usage(stderr);
+ errx(EXIT_FAILURE, _("not enough arguments"));
}
/* Accept "tty", "baudrate tty", and "tty baudrate". */
/* 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 {
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");
}
memset(&ut, 0, sizeof(ut));
if (vcline && *vcline)
/* Standard virtual console devices */
- strncpy(ut.ut_id, vcline, sizeof(ut.ut_id));
+ str2memcpy(ut.ut_id, vcline, sizeof(ut.ut_id));
else {
size_t len = strlen(line);
char * ptr;
ptr = line + len - sizeof(ut.ut_id);
else
ptr = line;
- strncpy(ut.ut_id, ptr, sizeof(ut.ut_id));
+ str2memcpy(ut.ut_id, ptr, sizeof(ut.ut_id));
}
}
- strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
- strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+ str2memcpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
+ str2memcpy(ut.ut_line, line, sizeof(ut.ut_line));
if (fakehost)
- strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
+ str2memcpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
time(&t);
ut.ut_tv.tv_sec = t;
ut.ut_type = LOGIN_PROCESS;
setlocale(LC_CTYPE, "POSIX");
op->flags &= ~F_UTF8;
#endif
- reset_vc(op, tp);
+ reset_vc(op, tp, 0);
if ((tp->c_cflag & (CS8|PARODD|PARENB)) == CS8)
op->flags |= F_EIGHTBITS;
* later on.
*/
+ /* The default 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
+ * at all. The best what we can do in this case is to keep c_iflag
+ * unmodified for --autolog.
+ */
+ if (!op->autolog) {
#ifdef IUTF8
- tp->c_iflag = tp->c_iflag & IUTF8;
- if (tp->c_iflag & IUTF8)
- op->flags |= F_UTF8;
+ tp->c_iflag = tp->c_iflag & IUTF8;
+ if (tp->c_iflag & IUTF8)
+ op->flags |= F_UTF8;
#else
- tp->c_iflag = 0;
+ tp->c_iflag = 0;
#endif
+ }
+
tp->c_lflag = 0;
tp->c_oflag &= OPOST | ONLCR;
}
/* Reset virtual console on stdin to its defaults */
-static void reset_vc(const struct options *op, struct termios *tp)
+static void reset_vc(const struct options *op, struct termios *tp, int canon)
{
int fl = 0;
reset_virtual_console(tp, fl);
+#ifdef AGETTY_RELOAD
+ /*
+ * Discard all the flags that makes the line go canonical with echoing.
+ * We need to know when the user starts typing.
+ */
+ if (canon == 0)
+ tp->c_lflag = 0;
+#endif
+
if (tcsetattr(STDIN_FILENO, TCSADRAIN, tp))
log_warn(_("setting terminal attributes failed: %m"));
#endif
}
+
static char *read_os_release(struct options *op, const char *varname)
{
int fd = -1;
continue;
}
p += varsz;
+ p += strspn(p, " \t\n\r");
+
+ if (*p != '=')
+ continue;
+
p += strspn(p, " \t\n\r=\"");
eol = p + strcspn(p, "\n\r");
*eol = '\0';
if (sock >= 0) {
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
- addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
+ addr.nl_groups = netlink_groups;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
close(sock);
else
}
}
-static int process_netlink_msg(int *changed)
+static int process_netlink_msg(int *triggered)
{
char buf[4096];
struct sockaddr_nl snl;
return 0;
}
- *changed = 1;
+ *triggered = 1;
break;
}
static int process_netlink(void)
{
- int changed = 0;
- while (process_netlink_msg(&changed));
- return changed;
+ int triggered = 0;
+ while (process_netlink_msg(&triggered));
+ return triggered;
}
-static int wait_for_term_input(int fd, char *prebuf, size_t prebufsz, size_t *readsz)
+static int wait_for_term_input(int fd)
{
char buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
- struct termios orig, nonc;
fd_set rfds;
- int count;
-
- /* Our aim here is to fall through if something fails
- * and not be stuck waiting. On failure assume we have input */
-
- if (tcgetattr(fd, &orig) != 0)
- return 1;
-
- memcpy(&nonc, &orig, sizeof (nonc));
- nonc.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHOKE);
- nonc.c_cc[VMIN] = 1;
- nonc.c_cc[VTIME] = 0;
-
- if (tcsetattr(fd, TCSANOW, &nonc) != 0)
- return 1;
if (inotify_fd == AGETTY_RELOAD_FDNONE) {
/* make sure the reload trigger file exists */
return 1;
if (FD_ISSET(fd, &rfds)) {
- count = read(fd, buffer, sizeof (buffer));
-
- tcsetattr(fd, TCSANOW, &orig);
-
- if (count > 0 && prebuf && prebufsz) {
- memcpy(prebuf, buffer, min(sizeof(buffer), prebufsz));
- *readsz = count;
- }
-
return 1;
} else if (netlink_fd >= 0 && FD_ISSET(netlink_fd, &rfds)) {
while (read(inotify_fd, buffer, sizeof (buffer)) > 0);
}
- tcsetattr(fd, TCSANOW, &orig);
-
- /* Need to reprompt */
return 0;
}
}
#endif /* AGETTY_RELOAD */
-static void print_issue_file(struct options *op, struct termios *tp)
+
+#ifdef ISSUEDIR_SUPPORT
+static int issuedir_filter(const struct dirent *d)
{
-#ifdef ISSUE
- FILE *fd;
+ 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 issue *ie __attribute__((__unused__)),
+ 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);
}
+}
+
+static void eval_issue_file(struct issue *ie __attribute__((__unused__)),
+ struct options *op __attribute__((__unused__)),
+ struct termios *tp __attribute__((__unused__)))
+{
+}
+#else /* ISSUE_SUPPORT */
+
+#ifdef AGETTY_RELOAD
+static int issue_is_changed(struct issue *ie)
+{
+ if (ie->mem_old && ie->mem
+ && strcmp(ie->mem_old, ie->mem) == 0) {
+ free(ie->mem_old);
+ ie->mem_old = ie->mem;
+ ie->mem = NULL;
+ return 0;
+ }
+
+ return 1;
+}
+#endif
+
+static void print_issue_file(struct issue *ie,
+ struct options *op,
+ struct termios *tp)
+{
+ int oflag = tp->c_oflag; /* Save current setting. */
-#ifdef ISSUE
- if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) {
- int c, oflag = tp->c_oflag; /* Save current setting. */
+ if ((op->flags & F_NONL) == 0) {
+ /* Issue not in use, start with a new line. */
+ write_all(STDOUT_FILENO, "\r\n", 2);
+ }
+ if (ie->do_tcsetattr) {
if ((op->flags & F_VCONSOLE) == 0) {
/* Map new line in output to carriage return & new line. */
tp->c_oflag |= (ONLCR | OPOST);
tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
}
+ }
- while ((c = getc(fd)) != EOF) {
- if (c == '\\')
- output_special_char(getc(fd), op, tp, fd);
- else
- putchar(c);
+ if (ie->mem_sz)
+ write_all(STDOUT_FILENO, ie->mem, ie->mem_sz);
+
+ if (ie->do_tcrestore) {
+ /* Restore settings. */
+ tp->c_oflag = oflag;
+ /* Wait till output is gone. */
+ tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
+ }
+
+#ifdef AGETTY_RELOAD
+ free(ie->mem_old);
+ ie->mem_old = ie->mem;
+ ie->mem = NULL;
+ ie->mem_sz = 0;
+#else
+ free(ie->mem);
+ ie->mem = NULL;
+ ie->mem_sz = 0;
+#endif
+}
+
+static void eval_issue_file(struct issue *ie,
+ 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
+#ifdef AGETTY_RELOAD
+ netlink_groups = 0;
+#endif
+
+ 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;
+ }
+
+ ie->output = open_memstream(&ie->mem, &ie->mem_sz);
+#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;
+
+ ie->do_tcsetattr = 1;
+
+ 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(ie, getc(f), op, tp, f);
+ else
+ putc(c, ie->output);
+ }
+ fclose(f);
+ f = NULL;
+ } while (dirname);
+
fflush(stdout);
- if ((op->flags & F_VCONSOLE) == 0) {
- /* Restore settings. */
- tp->c_oflag = oflag;
- /* Wait till output is gone. */
- tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
- }
- fclose(fd);
+ if ((op->flags & F_VCONSOLE) == 0)
+ ie->do_tcrestore = 1;
}
-#endif /* ISSUE */
+
+#ifdef ISSUEDIR_SUPPORT
+ for (i = 0; i < nfiles; i++)
+ free(namelist[i]);
+ free(namelist);
+ if (dd >= 0)
+ close(dd);
+#endif
+#ifdef AGETTY_RELOAD
+ if (netlink_groups != 0)
+ open_netlink();
+#endif
+ fclose(ie->output);
}
+#endif /* ISSUE_SUPPORT */
/* Show login prompt, optionally preceded by /etc/issue contents. */
-static void do_prompt(struct options *op, struct termios *tp)
+static void do_prompt(struct issue *ie, struct options *op, struct termios *tp)
{
#ifdef AGETTY_RELOAD
again:
#endif
- print_issue_file(op, tp);
+ print_issue_file(ie, op, tp);
if (op->flags & F_LOGINPAUSE) {
puts(_("[press ENTER to login]"));
#ifdef AGETTY_RELOAD
- if (!wait_for_term_input(STDIN_FILENO, NULL, 0, NULL)) {
- /* reload issue */
- if (op->flags & F_VCONSOLE)
- termio_clear(STDOUT_FILENO);
- goto again;
+ /* reload issue */
+ if (!wait_for_term_input(STDIN_FILENO)) {
+ eval_issue_file(ie, op, tp);
+ if (issue_is_changed(ie)) {
+ if (op->flags & F_VCONSOLE)
+ termio_clear(STDOUT_FILENO);
+ goto again;
+ }
}
-#else
- getc(stdin);
#endif
+ getc(stdin);
}
#ifdef KDGKBLED
if (!(op->flags & F_NOHINTS) && !op->autolog &&
}
/* Get user name, establish parity, speed, erase, kill & eol. */
-static char *get_logname(struct options *op, struct termios *tp, struct chardata *cp)
+static char *get_logname(struct issue *ie, struct options *op, struct termios *tp, struct chardata *cp)
{
static char logname[BUFSIZ];
char *bp;
bp = logname;
*bp = '\0';
+ eval_issue_file(ie, op, tp);
while (*logname == '\0') {
- char prebuf[LOGIN_NAME_MAX] = { 0 };
- size_t presz = 0, precur = 0;
-
/* Write issue file and prompt */
- do_prompt(op, tp);
+ do_prompt(ie, op, tp);
+ no_reload:
#ifdef AGETTY_RELOAD
- /* If asked to reprompt *before* terminal input arrives, then do so.
- *
- * Note that wait_for_term_input() calls read() and the result
- * is stored to the 'prebuf'. We need this to avoid data lost
- * by terminal attributes reset (and return chars back to the
- * terminal by TIOCSTI is fragile (chars reorder)).
- *
- * The data from 'prebuf' are not printed to the terminal yet
- * (disabled ECHO in wait_for_term_input()).
- */
- if (!wait_for_term_input(STDIN_FILENO,
- prebuf, sizeof(prebuf), &presz)) {
-
+ 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);
+ eval_issue_file(ie, op, tp);
+ if (!issue_is_changed(ie))
+ goto no_reload;
+ tcflush(STDIN_FILENO, TCIFLUSH);
if (op->flags & F_VCONSOLE)
termio_clear(STDOUT_FILENO);
+ bp = logname;
+ *bp = '\0';
continue;
}
#endif
while (cp->eol == '\0') {
char key;
- int force_echo = 0;
+ ssize_t readres;
- /* use already read data from buffer */
- if (presz && precur < presz) {
- c = prebuf[precur++];
- force_echo = 1;
-
- /* read from terminal */
- } else 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... */
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;
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:
case CTL('D'):
exit(EXIT_SUCCESS);
default:
- if (!isascii(ascval) || !isprint(ascval))
- break;
if ((size_t)(bp - logname) >= sizeof(logname) - 1)
log_err(_("%s: input overrun"), op->tty);
- if ((tp->c_lflag & ECHO) == 0 || force_echo)
+ if ((tp->c_lflag & ECHO) == 0)
write_all(1, &c, 1); /* echo the character */
*bp++ = ascval; /* and store it */
break;
}
+ /* Everything was erased. */
+ if (bp == logname && cp->eol == '\0')
+ goto no_reload;
}
}
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;
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);
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(_(" --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(_(" --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);
}
/*
#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;
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, ...)
va_end(ap);
}
-static void print_addr(sa_family_t family, void *addr)
+static void print_addr(struct issue *ie, sa_family_t family, void *addr)
{
char buff[INET6_ADDRSTRLEN + 1];
inet_ntop(family, addr, buff, sizeof(buff));
- printf("%s", buff);
+ fprintf(ie->output, "%s", buff);
}
/*
* 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,
+static void output_iface_ip(struct issue *ie,
+ struct ifaddrs *addrs,
const char *iface,
sa_family_t family)
{
}
if (addr) {
- print_addr(family, addr);
+ print_addr(ie, family, addr);
return;
}
}
break;
}
if (addr)
- print_addr(family, addr);
+ print_addr(ie, family, addr);
freeaddrinfo(info);
}
return buf;
}
-static void output_special_char(unsigned char c, struct options *op,
- struct termios *tp, FILE *fp)
+static void output_special_char(struct issue *ie,
+ unsigned char c,
+ struct options *op,
+ struct termios *tp,
+ FILE *fp)
{
struct utsname uts;
if (get_escape_argument(fp, escname, sizeof(escname))) {
const char *esc = color_sequence_from_colorname(escname);
if (esc)
- fputs(esc, stdout);
+ fputs(esc, ie->output);
} else
- fputs("\033", stdout);
+ fputs("\033", ie->output);
break;
}
case 's':
uname(&uts);
- printf("%s", uts.sysname);
+ fprintf(ie->output, "%s", uts.sysname);
break;
case 'n':
uname(&uts);
- printf("%s", uts.nodename);
+ fprintf(ie->output, "%s", uts.nodename);
break;
case 'r':
uname(&uts);
- printf("%s", uts.release);
+ fprintf(ie->output, "%s", uts.release);
break;
case 'v':
uname(&uts);
- printf("%s", uts.version);
+ fprintf(ie->output, "%s", uts.version);
break;
case 'm':
uname(&uts);
- printf("%s", uts.machine);
+ fprintf(ie->output, "%s", uts.machine);
break;
case 'o':
{
char *dom = xgetdomainname();
- fputs(dom ? dom : "unknown_domain", stdout);
+ fputs(dom ? dom : "unknown_domain", ie->output);
free(dom);
break;
}
(canon = strchr(info->ai_canonname, '.')))
dom = canon + 1;
}
- fputs(dom ? dom : "unknown_domain", stdout);
+ fputs(dom ? dom : "unknown_domain", ie->output);
if (info)
freeaddrinfo(info);
free(host);
break;
if (c == 'd') /* ISO 8601 */
- printf("%s %s %d %d",
+ fprintf(ie->output, "%s %s %d %d",
nl_langinfo(ABDAY_1 + tm->tm_wday),
nl_langinfo(ABMON_1 + tm->tm_mon),
tm->tm_mday,
tm->tm_year < 70 ? tm->tm_year + 2000 :
tm->tm_year + 1900);
else
- printf("%02d:%02d:%02d",
+ fprintf(ie->output, "%02d:%02d:%02d",
tm->tm_hour, tm->tm_min, tm->tm_sec);
break;
}
case 'l':
- printf ("%s", op->tty);
+ fprintf (ie->output, "%s", op->tty);
break;
case 'b':
{
for (i = 0; speedtab[i].speed; i++) {
if (speedtab[i].code == speed) {
- printf("%ld", speedtab[i].speed);
+ fprintf(ie->output, "%ld", speedtab[i].speed);
break;
}
}
var = read_os_release(op, varname);
if (var) {
if (strcmp(varname, "ANSI_COLOR") == 0)
- printf("\033[%sm", var);
+ fprintf(ie->output, "\033[%sm", var);
else
- fputs(var, stdout);
+ fputs(var, ie->output);
}
/* \S */
} else if ((var = read_os_release(op, "PRETTY_NAME"))) {
- fputs(var, stdout);
+ fputs(var, ie->output);
/* \S and PRETTY_NAME not found */
} else {
uname(&uts);
- fputs(uts.sysname, stdout);
+ fputs(uts.sysname, ie->output);
}
free(var);
users++;
endutxent();
if (c == 'U')
- printf(P_("%d user", "%d users", users), users);
+ fprintf(ie->output, P_("%d user", "%d users", users), users);
else
- printf ("%d ", users);
+ fprintf (ie->output, "%d ", users);
break;
}
+#if defined(RTMGRP_IPV4_IFADDR) && defined(RTMGRP_IPV6_IFADDR)
case '4':
case '6':
{
struct ifaddrs *addrs = NULL;
char iface[128];
-#ifdef AGETTY_RELOAD
- open_netlink();
-#endif
-
if (getifaddrs(&addrs))
break;
if (get_escape_argument(fp, iface, sizeof(iface)))
- output_iface_ip(addrs, iface, family);
+ output_iface_ip(ie, addrs, iface, family);
else
- output_iface_ip(addrs, NULL, family);
+ output_iface_ip(ie, addrs, NULL, family);
freeifaddrs(addrs);
+
+ if (c == '4')
+ netlink_groups |= RTMGRP_IPV4_IFADDR;
+ else
+ netlink_groups |= RTMGRP_IPV6_IFADDR;
break;
}
+#endif
default:
- putchar(c);
+ putc(c, ie->output);
break;
}
}