On Mon, Mar 21, 2011 at 02:58:22PM +0100, Jan Engelhardt wrote:
> On Monday 2011-03-21 12:26, Michael Olbrich wrote:
> 
> >Hi,
> >
> >I've been playing with systemd. So far I have been unable to get a login
> >prompt. The problem is this:
> >
> >[...]
> >systemd[1]: Job dev-ttyAMA0.device/start timed out.
> >systemd[1]: Job dev-ttyAMA0.device/start finished, result=timeout
> >systemd[1]: Job [email protected]/start finished, 
> >result=dependency
> >[...]
> 
> This is reminiscient of 
> https://bugzilla.novell.com/show_bug.cgi?id=679165
> 
> The default systemd files only start getties on a handful of ttys. Since 
> it seems impossible to distinguish between display ttys and e.g. modem 
> ttys, one can't just simply start gettys on all tty*, thus necessiting 
> manual addition for further ttys in the udev rules.

It is possible to distinguish between virtual displays and
e.g. modem ttys.  Just have a look at the attachment.
Here I've ported the most features of the mingetty (1.0.8s)
of openSUSE to the agetty of util-linux (2.19) simply to
test it out.

Now with the ioctl TIOCMGET on a modem/serial line the status
bits of a modem can be determined.  The ioctl TIOCMGET will
fail with EINVAL on virtual displays.


     Werner

-- 
  "Having a smoking section in a restaurant is like having
          a peeing section in a swimming pool." -- Edward Burr
--- agetty.c
+++ agetty.c	2011-03-18 15:05:19.767926623 +0000
@@ -18,6 +18,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <termios.h>
+#include <sys/ioctl.h>
 #include <signal.h>
 #include <errno.h>
 #include <sys/types.h>
@@ -31,6 +32,11 @@
 #include <sys/file.h>
 #include <sys/socket.h>
 #include <netdb.h>
+#include <sys/kd.h>
+#include <grp.h>
+#include <locale.h>
+#include <iconv.h>
+#include <wctype.h>
 
 #include "strutils.h"
 #include "nls.h"
@@ -40,6 +46,16 @@
 #ifdef __linux__
 #include <sys/param.h>
 #define USE_SYSLOG
+#ifndef IUTF8
+#  error IUTF8 input flag not defined
+#endif
+#endif
+
+#ifndef DEFAULT_VCTERM
+#define DEFAULT_VCTERM	"linux"
+#endif
+#ifndef DEFAULT_STERM
+#define DEFAULT_STERM	"vt100"
 #endif
 
  /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */
@@ -75,6 +91,7 @@
 #endif
 
 #define LOGIN " login: "		/* login prompt */
+#define ARRAY_SIZE_MAX	16
 
 /* Some shorthands for control characters. */
 
@@ -116,13 +133,20 @@
 struct options {
     int     flags;			/* toggle switches, see below */
     int     timeout;			/* time-out period */
+    char   *autolog;			/* login the user automatically */
+    char   *chdir;			/* Chdir before the login */
+    char   *chroot;			/* Chroot before the login */
     char   *login;			/* login program */
+    char   *logopt;			/* options for login program */
     char   *tty;			/* name of tty */
+    char   *vcline;			/* line of virtual console  */
+    char   *term;			/* terminal type  */
     char   *initstring;			/* modem init string */
     char   *issue;			/* alternative issue file */
+    int     delay;			/* Sleep seconds before prompt */
+    int     nice;			/* Run login with this priority */
     int     numspeed;			/* number of baud rates to try */
     int     speeds[MAX_SPEED];		/* baud rates to be tried */
-    int     eightbits;			/* assume 8bit-clean tty */
 };
 
 #define	F_PARSE		(1<<0)		/* process modem status messages */
@@ -136,6 +160,16 @@ struct options {
 #define F_LCUC		(1<<8)		/* Support for *LCUC stty modes */
 #define F_KEEPSPEED	(1<<9)		/* Follow baud rate from kernel */
 #define F_KEEPCFLAGS	(1<<10)		/* Reuse c_cflags setup from kernel */
+#define F_NOHANGUP	(1<<11)		/* No not call vhangup(2) */
+#define F_VCONSOLE	(1<<12)		/* This is a virtual console */
+#define F_UTF8		(1<<13)		/* We can do UTF8 */
+#define F_LOGINPAUSE	(1<<14)		/* Wait for any key before dropping login prompt */
+#define F_NOCLEAR	(1<<15)		/* Do not clear the screen before prompting */
+#define F_NONL		(1<<16)		/* No newline before issue */
+#define F_NORESET	(1<<17)		/* No reset on virtual consoles */
+#define F_NOHOSTNAME	(1<<18)		/* Do not show the hostname */
+#define F_LONGHNAME	(1<<19)		/* Show Full qualified hostname */
+#define F_EIGHTBITS	(1<<20)		/* Assume 8bit-clean tty */
 
 /* Storage for things detected while the login name was read. */
 
@@ -159,7 +193,7 @@ struct chardata init_chardata = {
 
 struct Speedtab {
     long    speed;
-    int     code;
+    speed_t  code;
 };
 
 static struct Speedtab speedtab[] = {
@@ -202,28 +236,35 @@ static struct Speedtab speedtab[] = {
 
 #define P_(s) s
 int main P_((int argc, char **argv));
-void parse_args P_((int argc, char **argv, struct options *op));
-void parse_speeds P_((struct options *op, char *arg));
-void update_utmp P_((char *line));
-void open_tty P_((char *tty, struct termios *tp, int local));
-void termio_init P_((struct termios *tp, struct options *op));
-void auto_baud P_((struct termios *tp));
-void do_prompt P_((struct options *op, struct termios *tp));
-void next_speed P_((struct termios *tp, struct options *op));
-char *get_logname P_((struct options *op, struct chardata *cp, struct termios *tp));
-void termio_final P_((struct options *op, struct termios *tp, struct chardata *cp));
-int caps_lock P_((char *s));
-int bcode P_((char *s));
-void usage P_((void));
-void error P_((const char *, ...));
+static void parse_args P_((int argc, char **argv, struct options *op));
+static void parse_speeds P_((struct options *op, char *arg));
+static void update_utmp P_((struct options *op));
+static void open_tty P_((char *tty, struct termios *tp, struct options *op));
+static void termio_init P_((struct termios *tp, struct options *op));
+static void auto_baud P_((struct termios *tp));
+static void output_special_char P_((unsigned char c, struct options *op, struct termios *tp));
+static void do_prompt P_((struct options *op, struct termios *tp));
+static void next_speed P_((struct termios *tp, struct options *op));
+static char *get_logname P_((struct options *op, struct chardata *cp, struct termios *tp));
+static void termio_final P_((struct options *op, struct termios *tp, struct chardata *cp));
+static int caps_lock P_((char *s));
+static int bcode P_((char *s));
+static void usage P_((void));
+static void error P_((const char *, ...)) __attribute__((__noreturn__)) __attribute__((__format__(printf, 1, 2)));
+static void warn P_((const char *, ...)) __attribute__((__format__(printf, 1, 2)));
+static void reset_vc P_((const int mode));
+static ssize_t safe_write P_((int fd, const char *buffer, size_t count));
+static void checkname P_((const char* nm));
+static void replacename P_((char** arr, const char* nm));
+static void mkarray P_((char** arr, char* str));
 #undef P_
 
 /* The following is used for understandable diagnostics. */
 
-char *progname;
+static char *progname;
 
 /* Fake hostname for ut_host specified on command line. */
-char *fakehost = NULL;
+static char *fakehost = NULL;
 
 /* ... */
 #ifdef DEBUGGING
@@ -234,88 +275,92 @@ FILE *dbf;
 #endif
 
 int
-main(argc, argv)
-     int     argc;
-     char  **argv;
+main(int argc, char *argv[])
 {
+    static struct options options;
     char   *logname = NULL;		/* login name, given to /bin/login */
+    char    logcmd[NAME_MAX+1];
+    char   *logarr[ARRAY_SIZE_MAX];
     struct chardata chardata;		/* set by get_logname() */
     struct termios termios;		/* terminal mode bits */
-    static struct options options = {
-	F_ISSUE,			/* show /etc/issue (SYSV_STYLE) */
-	0,				/* no timeout */
-	_PATH_LOGIN,			/* default login program */
-	"tty1",				/* default tty line */
-	"",				/* modem init string */
-	ISSUE,				/* default issue file */
-	0,				/* no baud rates known yet */
-    };
+    struct sigaction sa, sa_hup, sa_quit, sa_int;
+    sigset_t set;
 
-       setlocale(LC_ALL, "");
-       bindtextdomain(PACKAGE, LOCALEDIR);
-       textdomain(PACKAGE);
+    options.flags  = F_ISSUE;		/* show /etc/issue (SYSV_STYLE) */
+    options.login  = _PATH_LOGIN;	/* default login program */
+    options.logopt = "-- \\u";		/* escape for user name */
+    options.tty    = "tty1";		/* default tty line */
+    options.term   = DEFAULT_VCTERM;	/* default terminal type */
+    options.issue  = ISSUE;		/* default issue file */
+
+    sa.sa_handler = SIG_IGN;
+    sa.sa_flags = SA_RESTART;
+    sigemptyset (&sa.sa_mask);
+    sigaction(SIGHUP, &sa, &sa_hup);
+    sigaction(SIGQUIT, &sa, &sa_quit);
+    sigaction(SIGINT, &sa, &sa_int);
+
+    setlocale(LC_ALL, "");
+    bindtextdomain(PACKAGE, LOCALEDIR);
+    textdomain(PACKAGE);
     
     /* The BSD-style init command passes us a useless process name. */
 
 #ifdef	SYSV_STYLE
-       {
-	       char *ptr;
-	       progname = argv[0];
-	       if ((ptr = strrchr(argv[0], '/')))
-		       progname = ++ptr;
-       }
+    {
+# ifdef __GLIBC__
+	progname = program_invocation_short_name;
+# else
+	char *ptr;
+	progname = argv[0];
+	if ((ptr = strrchr(argv[0], '/')))
+	    progname = ++ptr;
+# endif
+    }
 #else
-       progname = "agetty";
-#endif
-
-#ifdef DEBUGGING
-	dbf = fopen("/dev/ttyp0", "w");
-
-	{	int i;
-	
-		for(i = 1; i < argc; i++) {
-			debug(argv[i]);
-		}
-	}
+    progname = "agetty";
 #endif
 
     /* Parse command-line arguments. */
 
     parse_args(argc, argv, &options);
 
-#ifdef __linux__
-	setsid();
-#endif
-	
-    /* Update the utmp file. */
-
 #ifdef	SYSV_STYLE
-    update_utmp(options.tty);
+    /* Update the utmp file. */
+    update_utmp(&options);
 #endif
 
+    if (options.delay)
+	sleep(options.delay);
+
     debug("calling open_tty\n");
     /* Open the tty as standard { input, output, error }. */
-    open_tty(options.tty, &termios, options.flags & F_LOCAL);
+    open_tty(options.tty, &termios, &options);
+
+    /* unmask SIGHUP if inherited */
+    sigemptyset (&set);
+    sigaddset (&set, SIGHUP);
+    sigprocmask (SIG_UNBLOCK, &set, NULL);
+    sigaction (SIGHUP, &sa_hup, NULL);
 
-    tcsetpgrp(0, getpid());
     /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
     debug("calling termio_init\n");
     termio_init(&termios, &options);
 
     /* write the modem init string and DON'T flush the buffers */
-    if (options.flags & F_INITSTRING) {
+    if ((options.flags & (F_VCONSOLE|F_INITSTRING)) == F_INITSTRING) {
 	debug("writing init string\n");
-	ignore_result( write(1, options.initstring, strlen(options.initstring)) );
+	safe_write(1, options.initstring, strlen(options.initstring));
     }
 
-    if (!(options.flags & F_LOCAL)) {
+    if ((options.flags & F_LOCAL) == 0 || (options.flags & F_VCONSOLE)) {
 	/* go to blocking write mode unless -L is specified */
 	fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK);
     }
 
     /* Optionally detect the baud rate from the modem status message. */
     debug("before autobaud\n");
-    if (options.flags & F_PARSE)
+    if ((options.flags & (F_VCONSOLE|F_PARSE)) == F_PARSE)
 	auto_baud(&termios);
 
     /* Set the optional timer. */
@@ -323,7 +368,7 @@ main(argc, argv)
 	(void) alarm((unsigned) options.timeout);
 
     /* optionally wait for CR or LF before writing /etc/issue */
-    if (options.flags & F_WAITCRLF) {
+    if ((options.flags & (F_VCONSOLE|F_WAITCRLF)) == F_WAITCRLF) {
 	char ch;
 
 	debug("waiting for cr-lf\n");
@@ -337,11 +382,24 @@ main(argc, argv)
     }
 
     chardata = init_chardata;
-    if (!(options.flags & F_NOPROMPT)) {
-	/* Read the login name. */
-	debug("reading login name\n");
-	while ((logname = get_logname(&options, &chardata, &termios)) == 0)
-	  next_speed(&termios, &options);
+    if ((options.flags & F_NOPROMPT) == 0) {
+	if (options.autolog) {
+	    debug("doing auto login\n");
+	    do_prompt(&options, &termios);
+	    printf ("%s%s (automatic login)\n", LOGIN, options.autolog);
+	    logname = options.autolog;
+	    options.logopt = "-f \\u";
+	} else {
+	    /* Read the login name. */
+	    debug("reading login name\n");
+	    while ((logname = get_logname(&options, &chardata, &termios)) == 0) {
+		if ((options.flags & F_VCONSOLE) == 0) {
+		    next_speed(&termios, &options);
+		    /* Initialize kill, erase, parity etc. after switching speed. */
+		    chardata = init_chardata;
+		}
+	    }
+	}
     }
 
     /* Disable timer. */
@@ -349,39 +407,91 @@ main(argc, argv)
     if (options.timeout)
 	(void) alarm(0);
 
-    /* Finalize the termios settings. */
-
-    termio_final(&options, &termios, &chardata);
 
-    /* Now the newline character should be properly written. */
-
-    ignore_result( write(1, "\n", 1) );
+    if ((options.flags & F_VCONSOLE) == 0) {
+	/* Finalize the termios settings. */
+	termio_final(&options, &termios, &chardata);
+
+	/* Now the newline character should be properly written. */
+	safe_write(1, "\n", 1);
+    }
+
+    sigaction (SIGQUIT, &sa_quit, NULL);
+    sigaction (SIGINT, &sa_int, NULL);
+
+    strncpy (logcmd, options.login, NAME_MAX);
+    strncat (logcmd, " ", NAME_MAX - strlen(logcmd));
+    strncat (logcmd, options.logopt, NAME_MAX - strlen(logcmd));
+
+    checkname(logname);
+    mkarray(logarr, logcmd);
+    replacename(logarr, logname);
+
+    if (options.chroot)
+	chroot(options.chroot);
+    if (options.chdir)
+	chdir(options.chdir);
+    if (options.nice)
+	nice(options.nice);
 
     /* Let the login program take care of password validation. */
 
-    (void) execl(options.login, options.login, "--", logname, NULL);
-    error(_("%s: can't exec %s: %m"), options.tty, options.login);
+    (void) execv(options.login, logarr);
+    warn(_("%s: can't exec %s: %m"), options.tty, options.login);
+    sleep(5);
+    exit(1);
 }
 
 /* parse-args - parse command-line arguments */
 
-void
-parse_args(argc, argv, op)
-     int     argc;
-     char  **argv;
-     struct options *op;
+static void
+parse_args(int argc, char *argv[], struct options *op)
 {
     extern char *optarg;		/* getopt */
     extern int optind;			/* getopt */
-    int     c;
+    int c, nipple, index;
+    struct option const long_options[] = {
+	{ "loginpause",	   no_argument, &nipple,  F_LOGINPAUSE	},
+	{ "long-hostname", no_argument, &nipple,  F_LONGHNAME	},
+	{ "noclear",	   no_argument, &nipple,  F_NOCLEAR	},
+	{ "nonewline",	   no_argument, &nipple,  F_NONL	},
+	{ "nohangup",	   no_argument, &nipple,  F_NOHANGUP	},
+	{ "noreset",	   no_argument, &nipple,  F_NORESET	},
+	{ "no-hostname",   no_argument, &nipple,  F_NOHOSTNAME	},
+	{ "nohostname",	   no_argument, &nipple,  F_NOHOSTNAME	},  /* compat option */
+	{ "autologin",	   required_argument,	    NULL,   'a' },
+	{ "chdir",	   required_argument,	    NULL,   'C' },
+	{ "chroot",	   required_argument,	    NULL,   'r' },
+	{ "delay",	   required_argument,	    NULL,   'd' },
+	{ "login",	   required_argument,	    NULL,   'l' },
+	{ "loginprog",	   required_argument,	    NULL,   'l' },  /* compat option */
+	{ "loginopts",	   required_argument,	    NULL,   'o' },
+	{ "logopts",	   required_argument,	    NULL,   'o' },
+	{ "nice",	   required_argument,	    NULL,   'N' },
+	{ "noissue",	   no_argument,		    NULL,   'i' },
+	{ "cs8",	   no_argument,		    NULL,   '8' },
+	{ 0, 0, 0, 0 }
+    };
 
-    while (isascii(c = getopt(argc, argv, "8cI:LH:f:hil:mst:wUn"))) {
+    while (isascii(c = getopt_long(argc, argv, "8a:C:cd:f:H:hI:iLl:mnN:o:r:st:Uw", long_options, &index))) {
 	switch (c) {
+	case 0:
+	    op->flags |= nipple;
+	    break;
 	case 'c':
 	    op->flags |= F_KEEPCFLAGS;
 	    break;
 	case '8':
-	    op->eightbits = 1;
+	    op->flags |= F_EIGHTBITS;
+	    break;
+	case 'a':
+	    op->autolog = optarg;
+	    break;
+	case 'C':
+	    op->chdir = optarg;
+	    break;
+	case 'd':
+	    op->delay = atoi(optarg);
 	    break;
 	case 'I':
 	    if (!(op->initstring = malloc(strlen(optarg)+1))) {
@@ -445,9 +555,18 @@ parse_args(argc, argv, op)
 	case 'm':				/* parse modem status message */
 	    op->flags |= F_PARSE;
 	    break;
+	case 'N':
+	    op->nice = atoi(optarg);
+	    break;
 	case 'n':
 	    op->flags |= F_NOPROMPT;
 	    break;
+	case 'o':
+	    op->logopt = optarg;
+	    break;
+	case 'r':
+	    op->chroot = optarg;
+	    break;
 	case 's':
 	    op->flags |= F_KEEPSPEED;		/* keep kernel defined speed */
 	    break;
@@ -465,23 +584,36 @@ parse_args(argc, argv, op)
 	    usage();
 	}
     }
-	debug("after getopt loop\n");
-    if (argc < optind + 2)			/* check parameter count */
+
+    debug("after getopt loop\n");
+    if (argc < optind + 1) 			/* check parameter count */
 	usage();
 
     /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
     if('0' <= argv[optind][0] && argv[optind][0] <= '9') {
 	/* a number first, assume it's a speed (BSD style) */
 	parse_speeds(op, argv[optind++]);	/* baud rate(s) */
-	op->tty = argv[optind];			/* tty name */
+	if (argc < optind + 1)
+	    usage();
+	op->tty = argv[optind++];		/* tty name */
     } else {
 	op->tty = argv[optind++];		/* tty name */
-	parse_speeds(op, argv[optind]);		/* baud rate(s) */
+	if (argc > optind) {
+	    char *v = argv[optind++];
+	    if ('0' <= *v && *v <= '9')
+		parse_speeds(op, v);		/* baud rate(s) */
+	    else
+		op->speeds[op->numspeed++] = bcode("9600");
+	}
     }
 
-    optind++;
+    /* On virtual console remember the line which is used for */
+    if (strncmp(op->tty, "tty", 3) == 0 &&
+	strspn(op->tty + 3, "0123456789") == strlen(op->tty+3))
+	op->vcline = op->tty+3;
+
     if (argc > optind && argv[optind])
-	setenv ("TERM", argv[optind], 1);
+	op->term = argv[optind];
 
 #ifdef DO_DEVFS_FIDDLING
     /*
@@ -520,14 +652,12 @@ parse_args(argc, argv, op)
 
 /* parse_speeds - parse alternate baud rates */
 
-void
-parse_speeds(op, arg)
-     struct options *op;
-     char   *arg;
+static void
+parse_speeds(struct options *op, char *arg)
 {
     char   *cp;
 
-	debug("entered parse_speeds\n");
+    debug("entered parse_speeds\n");
     for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
 	if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
 	    error(_("bad speed: %s"), cp);
@@ -540,13 +670,15 @@ parse_speeds(op, arg)
 #ifdef	SYSV_STYLE
 
 /* update_utmp - update our utmp entry */
-void
-update_utmp(line)
-     char   *line;
+static void
+update_utmp(struct options *op)
 {
     struct  utmp ut;
     time_t  t;
-    int     mypid = getpid();
+    pid_t   pid = getpid();
+    pid_t   sid = getsid(0);
+    char    *vcline = op->vcline;
+    char    *line   = op->tty;
     struct  utmp *utp;
 
     /*
@@ -561,12 +693,12 @@ update_utmp(line)
     utmpname(_PATH_UTMP);
     setutent();
 
-    /* Find mypid in utmp. Earlier code here tested only
+    /* Find pid in utmp. Earlier code here tested only
        utp->ut_type != INIT_PROCESS, so maybe the >= here should be >.
        The present code is taken from login.c, so if this is changed,
        maybe login has to be changed as well. */
     while ((utp = getutent()))
-	    if (utp->ut_pid == mypid
+	    if (utp->ut_pid == pid
 		&& utp->ut_type >= INIT_PROCESS
 		&& utp->ut_type <= DEAD_PROCESS)
 		    break;
@@ -576,17 +708,30 @@ update_utmp(line)
     } else {
 	/* some inits don't initialize utmp... */
 	memset(&ut, 0, sizeof(ut));
-	strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
+	if (vcline)
+	    /* Standard virtual console devices */
+	    strncpy (ut.ut_id, vcline, sizeof (ut.ut_id));
+	else {
+	    size_t len = strlen(line);
+	    char * ptr;
+	    if (len >= sizeof (ut.ut_id))
+		ptr = line + len - sizeof (ut.ut_id);
+	    else
+		ptr = line;
+	    strncpy (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));
     if (fakehost)
 	strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
+
     time(&t);
     ut.ut_time = t;
     ut.ut_type = LOGIN_PROCESS;
-    ut.ut_pid = mypid;
+    ut.ut_pid = pid;
+    ut.ut_session = sid;
 
     pututline(&ut);
     endutent();
@@ -601,7 +746,7 @@ update_utmp(line)
 	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) {
-		ignore_result( write(ut_fd, &ut, sizeof(ut)) );
+		safe_write(ut_fd, &ut, sizeof(ut));
 		close(ut_fd);
 	    }
 	    flock(lf, LOCK_UN);
@@ -614,40 +759,81 @@ update_utmp(line)
 #endif
 
 /* open_tty - set up tty as standard { input, output, error } */
-void
-open_tty(tty, tp, local)
-     char   *tty;
-     struct termios *tp;
-     int    local;
+static void
+open_tty(char *tty, struct termios *tp, struct options *op)
 {
-    /* Get rid of the present standard { output, error} if any. */
+    const pid_t pid = getpid();
+    int serial;
 
-    (void) close(1);
-    (void) close(2);
-    errno = 0;					/* ignore above errors */
+    /* Get rid of the present standard { output, error} if any. */
 
     /* Set up new standard input, unless we are given an already opened port. */
 
-    if (strcmp(tty, "-")) {
+    if (strcmp(tty, "-") != 0) {
+	char buf[PATH_MAX+1];
+	struct group *gr = NULL;
 	struct stat st;
+	int fd, len;
+	pid_t tid;
+	gid_t gid = 0;
+
+	/* Use tty group if available */
+	if ((gr = getgrnam("tty")))
+	    gid = gr->gr_gid;
 
-	/* Sanity checks... */
+	if (((len = snprintf(buf, sizeof(buf), "/dev/%s", tty)) >= (int)sizeof(buf)) || (len < 0))
+	    error(_("/dev/%s: cannot open as standard input: %m"), tty);
+
+	/* There is always a race between this reset and the call to
+	   vhangup() that s.o. can use to get access to your tty. */
+	if (chown (buf, 0, gid) || chmod (buf, (gid ? 0660 : 0600))) {
+	    if (errno == EROFS)
+		warn("%s: %m", buf);
+	    else
+		error("%s: %m", buf);
+	}
 
-	if (chdir("/dev"))
-	    error(_("/dev: chdir() failed: %m"));
-	if (stat(tty, &st) < 0)
-	    error("/dev/%s: %m", tty);
+	/* Open the tty as standard input. */
+	if ((fd = open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0)) < 0)
+	    error(_("/dev/%s: cannot open as standard input: %m"), tty);
+
+	/* Sanity checks... */
+	if (!isatty (fd))
+	    error(_("/dev/%s: not a character device"), tty);
+	if (fstat(fd, &st) < 0)
+	    error ("%s: %m", buf);
 	if ((st.st_mode & S_IFMT) != S_IFCHR)
 	    error(_("/dev/%s: not a character device"), tty);
 
-	/* Open the tty as standard input. */
+	if (((tid = tcgetsid(fd)) < 0) || (pid != tid)) {
+	    if (ioctl (fd, TIOCSCTTY, 1) == -1)
+		error("/dev/%s: cannot get controlling tty: %m", tty);
+	}
 
+	if ((op->flags & F_NOHANGUP) == 0) {
+	    /* vhangup() will replace all open file descriptors in the kernel
+	       that point to our controlling tty by a dummy that will deny
+	       further reading/writing to our device. It will also reset the
+	       tty to sane defaults, so we don't have to modify the tty device
+	       for sane settings. We also get a SIGHUP/SIGCONT.
+	     */
+	    if (vhangup())
+		error("%s: vhangup() failed: %m", tty);
+	    (void)ioctl(fd, TIOCNOTTY);
+	}
+
+
+	(void) close(fd);
 	(void) close(0);
 	errno = 0;				/* ignore close(2) errors */
 
 	debug("open(2)\n");
-	if (open(tty, O_RDWR|O_NONBLOCK, 0) != 0)
+	if (open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0) != 0)
 	    error(_("/dev/%s: cannot open as standard input: %m"), tty);
+	if (((tid = tcgetsid(0)) < 0) || (pid != tid)) {
+	    if (ioctl (0, TIOCSCTTY, 1) == -1)
+		error("/dev/%s: cannot get controlling tty: %m", tty);
+	}
 
     } else {
 
@@ -660,6 +846,13 @@ open_tty(tty, tp, local)
 	    error(_("%s: not open for read/write"), tty);
     }
 
+    if (tcsetpgrp(0, pid))
+	error("/dev/%s: cannot set process group: %m", tty);
+
+    (void) close(1);
+    (void) close(2);
+    errno = 0;					/* ignore above errors */
+
     /* Set up standard output and standard error file descriptors. */
     debug("duping\n");
     if (dup(0) != 1 || dup(0) != 2)		/* set up stdout and stderr */
@@ -678,34 +871,66 @@ open_tty(tty, tp, local)
 	error("%s: tcgetattr: %m", tty);
 
     /*
-     * It seems to be a terminal. Set proper protections and ownership. Mode
-     * 0622 is suitable for SYSV <4 because /bin/login does not change
-     * protections. SunOS 4 login will change the protections to 0620 (write
-     * access for group tty) after the login has succeeded.
-     *
-     * Linux login(1) will change tty permissions.
+     * Detect if this is a virtual console or serial/modem line. IN
+     * case of a virtual console the ioctl TIOCMGET fails and the
+     * error number will be set to EINVAL.
      */
+    if (ioctl(0, TIOCMGET, &serial) < 0 && (errno = EINVAL)) {
+	op->flags |= F_VCONSOLE;
+	if (!op->term)
+	    op->term = DEFAULT_VCTERM;
+    } else if (!op->term)
+	op->term = DEFAULT_STERM;
 
-    /*
-     * Let us use 0600 for Linux for the period between getty and login
-     */
-    ignore_result( chown(tty, 0, 0) );		/* root, sys */
-    ignore_result( chmod(tty, 0600) );		/* 0622: crw--w--w- */
-    errno = 0;					/* ignore above errors */
+    setenv("TERM", op->term, 1);
 }
 
 /* termio_init - initialize termios settings */
 
-char gbuf[1024];
-char area[1024];
-
-void
-termio_init(tp, op)
-     struct termios *tp;
-     struct options *op;
+static void
+termio_init(struct termios *tp, struct options *op)
 {
     speed_t ispeed, ospeed;
 
+    if (op->flags & F_VCONSOLE) {		/* a vc is not a modem */
+	int mode = 0;
+#ifdef IUTF8
+	/* Detect mode of current keyboard setup, e.g. for UTF-8 */
+	if (ioctl(0, KDGKBMODE, &mode) < 0)
+	    mode = K_RAW;
+	else
+	    switch(mode) {
+	    case K_UNICODE:
+		setlocale(LC_CTYPE, "en_US.UTF-8");
+		op->flags |= F_UTF8;
+		break;
+	    case K_RAW:
+	    case K_MEDIUMRAW:
+	    case K_XLATE:
+	    default:
+		setlocale(LC_CTYPE, "POSIX");
+		op->flags &= ~F_UTF8;
+		break;
+	    }
+#endif
+	reset_vc(mode);
+
+	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)
+	     */
+	    safe_write(0, "\033[r\033[H\033[J", 9);
+
+	op->flags |= F_EIGHTBITS;
+	return;
+    }
+
     if (op->flags & F_KEEPSPEED) {
 	ispeed = cfgetispeed(tp);		/* save the original setting */
 	ospeed = cfgetospeed(tp);
@@ -758,9 +983,8 @@ termio_init(tp, op)
 }
 
 /* auto_baud - extract baud rate from modem status message */
-void
-auto_baud(tp)
-     struct termios *tp;
+static void
+auto_baud(struct termios *tp)
 {
     int     speed;
     int     vmin;
@@ -821,173 +1045,105 @@ auto_baud(tp)
 }
 
 /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
-void
-do_prompt(op, tp)
-     struct options *op;
-     struct termios *tp;
+static void
+do_prompt(struct options *op, struct termios *tp)
 {
 #ifdef	ISSUE
     FILE    *fd;
-    int     oflag;
-    int     c;
-    struct utsname uts;
-
-    (void) uname(&uts);
 #endif
 
-    ignore_result( write(1, "\r\n", 2) );			/* start a new line */
+    if ((op->flags & F_NONL) == 0) {
+	if (op->flags & F_VCONSOLE)
+	    putchar ('\n');
+	else
+	    safe_write(1, "\r\n", 2);		/* start a new line */
+    }
 #ifdef	ISSUE					/* optional: show /etc/issue */
     if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) {
-	oflag = tp->c_oflag;			/* save current setting */
-	tp->c_oflag |= (ONLCR | OPOST);		/* map NL in output to CR-NL */
-	(void) tcsetattr(0, TCSADRAIN, tp);
-
-
-	while ((c = getc(fd)) != EOF)
-	{
+	int c, oflag = tp->c_oflag;
+	if ((op->flags & F_VCONSOLE) == 0) {
+	    tp->c_oflag |= (ONLCR | OPOST);		/* map NL in output to CR-NL */
+	    (void) tcsetattr(0, TCSADRAIN, tp);
+	}
+	while ((c = getc(fd)) != EOF) {
 	    if (c == '\\')
-	      {
-		c = getc(fd);
-		
-		switch (c)
-		  {
-		  case 's':
-		    (void) printf ("%s", uts.sysname);
-		    break;
-		    
-		  case 'n':
-		    (void) printf ("%s", uts.nodename);
-		    break;
-		    
-		  case 'r':
-		    (void) printf ("%s", uts.release);
-		    break;
-		    
-		  case 'v':
-		    (void) printf ("%s", uts.version);
-		    break;
-		    
-		  case 'm':
-		    (void) printf ("%s", uts.machine);
-		    break;
-
-		  case 'o':
-		   {
-		     char domainname[MAXHOSTNAMELEN+1];
-#ifdef HAVE_GETDOMAINNAME
-		     if (getdomainname(domainname, sizeof(domainname)))
-#endif
-			 strcpy(domainname, "unknown_domain");
-		     domainname[sizeof(domainname)-1] = '\0';
-		     printf ("%s", domainname);
-		   }
-		  break;
-
-		  case 'O':
-		   {
-			char *dom = "unknown_domain";
-			char host[MAXHOSTNAMELEN+1];
-			struct addrinfo hints, *info = NULL;
-
-			memset(&hints, 0, sizeof(hints));
-			hints.ai_flags = AI_CANONNAME;
-
-			if (gethostname(host, sizeof(host)) ||
-			    getaddrinfo(host, NULL, &hints, &info) ||
-			    info == NULL)
-				fputs(dom, stdout);
-			else {
-				char *canon;
-
-				if (info->ai_canonname &&
-				    (canon = strchr(info->ai_canonname, '.')))
-					dom = canon + 1;
-				fputs(dom, stdout);
-				freeaddrinfo(info);
-			}
-		   }
-		  break;
-
-		  case 'd':
-		  case 't':
-		    {
-		      time_t now;
-		      struct tm *tm;
-
-		      (void) time (&now);
-		      tm = localtime(&now);
-
-		      if (c == 'd')
-			(void) printf ("%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
-			(void) printf ("%02d:%02d:%02d",
-				tm->tm_hour, tm->tm_min, tm->tm_sec);
-		      break;
-		    }
-
-		  case 'l':
-		      (void) printf ("%s", op->tty);
-		      break;
-
-		  case 'b':
-		    {
-			int i;
-
-			for (i = 0; speedtab[i].speed; i++) {
-			    if (speedtab[i].code == cfgetispeed(tp)) {
-				printf("%ld", speedtab[i].speed);
-				break;
-			    }
-			}
-			break;
-		    }
-		  case 'u':
-		  case 'U':
-		    {
-		      int users = 0;
-		      struct utmp *ut;
-		      setutent();
-		      while ((ut = getutent()))
-		        if (ut->ut_type == USER_PROCESS)
-			  users++;
-		      endutent();
-		      printf ("%d ", users);
-		      if (c == 'U')
-		        printf ((users == 1) ? _("user") : _("users"));
-		      break;
-		    }
-		  default:
-		    (void) putchar(c);
-		  }
-	      }
+		output_special_char(getc(fd), op, tp);
 	    else
-	      (void) putchar(c);
+		(void) putchar(c);
 	}
 	fflush(stdout);
 
-	tp->c_oflag = oflag;			/* restore settings */
-	(void) tcsetattr(0, TCSADRAIN, tp);	/* wait till output is gone */
+	if ((op->flags & F_VCONSOLE) == 0) {
+	    tp->c_oflag = oflag;			/* restore settings */
+	    (void) tcsetattr(0, TCSADRAIN, tp);		/* wait till output is gone */
+	}
 	(void) fclose(fd);
     }
 #endif
+    if (op->flags & F_LOGINPAUSE) {
+	puts("[press ENTER to login]");
+	getc(stdin);
+    }
+    if (op->autolog == (char*)0 && (op->flags & F_VCONSOLE)) {
+	int kb = 0, nl = 0;
+	struct stat st;
+	if (stat("/var/run/numlock-on", &st) == 0)
+	    nl = 1;
+	if (ioctl(0, KDGKBLED, &kb) == 0) {
+	    char warn[128];
+	    off_t len = 0;
+
+	    if (nl && (kb & 0x02) == 0) {
+		strcpy(&warn[0], "Num Lock off");
+		len += 12;
+	    } else if (nl == 0 && (kb & 2) && (kb & 0x20) == 0) {
+		strcpy(&warn[0], "Num Lock on");
+		len += 11;
+	    }
+
+	    if ((kb & 0x04) && (kb & 0x40) == 0) {
+		if (len) {
+		    strcpy(&warn[len], ", ");
+		    len += 2;
+		}
+		strcpy(&warn[len], "Caps Lock on");
+		len += 12;
+	    }
+
+	    if ((kb & 0x01) && (kb & 0x10) == 0) {
+		if (len) {
+		    strcpy(&warn[len], ", ");
+		    len += 2;
+		}
+		strcpy(&warn[len], "Scroll Lock on");
+		len += 14;
+	    }
+
+	    if (len)
+		printf ("Hint: %s\n\n", warn);
+	}
+    }
+    if ((op->flags & F_NOHOSTNAME) == 0)
     {
 	char hn[MAXHOSTNAMELEN+1];
-	if (gethostname(hn, sizeof(hn)) == 0)
-	    ignore_result( write(1, hn, strlen(hn)) );
+
+	if (gethostname(hn, sizeof(hn)) == 0) {
+	    char *dot;
+	    hn[MAXHOSTNAMELEN] = '\0';
+
+	    if ((op->flags & F_LONGHNAME) && (dot = strchr(hn, '.')))
+		*dot = '\0';
+	    safe_write(1, hn, strlen(hn));
+	}
     }
-    ignore_result( write(1, LOGIN, sizeof(LOGIN) - 1) );	/* always show login prompt */
+    if (op->autolog == (char*)0)
+	safe_write(1, LOGIN, sizeof(LOGIN) - 1);	/* always show login prompt */
+    fflush(stdout);
 }
 
 /* next_speed - select next baud rate */
-void
-next_speed(tp, op)
-     struct termios *tp;
-     struct options *op;
+static void
+next_speed( struct termios *tp, struct options *op)
 {
     static int baud_index = -1;
 
@@ -1008,12 +1164,10 @@ next_speed(tp, op)
 
 /* get_logname - get user name, establish parity, speed, erase, kill, eol */
 
-char   *get_logname(op, cp, tp)
-     struct options *op;
-     struct chardata *cp;
-     struct termios *tp;
+static char
+*get_logname(struct options *op, struct chardata *cp, struct termios *tp)
 {
-    static char logname[BUFSIZ];
+    static char logname[4*UT_NAMESIZE];
     char   *bp;
     char    c;				/* input character, full eight bits */
     char    ascval;			/* low 7 bits of input character */
@@ -1026,16 +1180,77 @@ char   *get_logname(op, cp, tp)
 	"\210\240\210",			/* no parity */
     };
 
-    /* Initialize kill, erase, parity etc. (also after switching speeds). */
-
-    *cp = init_chardata;
-
     /* Flush pending input (esp. after parsing or switching the baud rate). */
 
-    (void) sleep(1);
+    if ((op->flags & F_VCONSOLE) == 0)
+	(void) sleep(1);
     (void) tcflush(0, TCIFLUSH);
 
-    /* Prompt for and read a login name. */
+    /* Prompt for and read a login name on a virtual console. */
+
+    if (op->flags & F_VCONSOLE) {
+	iconv_t ic;
+
+	for (*logname = 0; *logname == 0; /* void */ ) {
+
+	    /* Write issue file and prompt */
+
+	    do_prompt(op, tp);
+
+	    /* Read name, watch for break and end-of-line. */
+
+	    for (bp = logname;;) {
+
+		if (read (0, &c, 1) < 1) {
+		    if (errno == EINTR || errno == EAGAIN) {
+			usleep(1000);
+			continue;
+		    }
+		    if (errno == EIO || errno == ENOENT)
+			exit (EXIT_SUCCESS);
+		    error(_("%s: read: %m"), op->tty);
+		}
+
+		if (c == '\n' || c == '\r') {
+		    *bp = 0;
+		    break;
+		}
+
+		if ((op->flags & F_UTF8) == 0 && !isprint(c))
+		    error ("%s: invalid character 0x%x in login name", op->tty, c);
+
+		if ((size_t)(bp - logname) >= sizeof (logname) - 1)
+		    error(_("%s: input overrun"), op->tty);
+
+		*bp++ = c;
+	    }
+	}
+
+	if ((op->flags & F_UTF8)  && (ic = iconv_open("WCHAR_T", "UTF-8"))) {
+	    char tmpbuf[4*sizeof(logname)], *optr, *lptr;
+	    size_t len = bp - logname;
+	    size_t out = sizeof(tmpbuf) - 1;
+	    size_t wcl;
+	    wint_t *wcp;
+
+	    optr = tmpbuf;
+	    lptr = logname;
+	    if ((wcl = iconv(ic , &lptr, &len, &optr, &out)) == (size_t)-1)
+		error ("%s: invalid character conversion for login name", op->tty);
+	    iconv_close(ic);
+
+	    wcp = (wint_t*)tmpbuf;
+	    wcp[wcl] = (wint_t)0;
+	    while (*wcp) {
+		const wint_t wc = *wcp++;
+		if (!iswprint(wc))
+		    error ("%s: invalid character 0x%x in login name", op->tty, wc);
+	    }
+	}
+	return logname;
+    }
+
+    /* Prompt for and read a login name on a modem line. */
 
     for (*logname = 0; *logname == 0; /* void */ ) {
 
@@ -1050,7 +1265,11 @@ char   *get_logname(op, cp, tp)
 	    /* Do not report trivial EINTR/EIO errors. */
 
 	    if (read(0, &c, 1) < 1) {
-		if (errno == EINTR || errno == EIO)
+		if (errno == EINTR || errno == EAGAIN) {
+		    usleep(1000);
+		    continue;
+		}
+		if (errno == ENOENT || errno == EIO)
 		    exit(EXIT_SUCCESS);
 		error(_("%s: read: %m"), op->tty);
 	    }
@@ -1060,7 +1279,7 @@ char   *get_logname(op, cp, tp)
 		return EXIT_SUCCESS;
 	    /* Do parity bit handling. */
 
-	    if (op->eightbits) {
+	    if (op->flags & F_EIGHTBITS) {
 		ascval = c;
 	    } else if (c != (ascval = (c & 0177))) {	/* "parity" bit on */
 		for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
@@ -1081,7 +1300,7 @@ char   *get_logname(op, cp, tp)
 	    case '#':
 		cp->erase = ascval;		/* set erase character */
 		if (bp > logname) {
-		    ignore_result( write(1, erase[cp->parity], 3) );
+		    safe_write(1, erase[cp->parity], 3);
 		    bp--;
 		}
 		break;
@@ -1089,7 +1308,7 @@ char   *get_logname(op, cp, tp)
 	    case '@':
 		cp->kill = ascval;		/* set kill character */
 		while (bp > logname) {
-		    ignore_result( write(1, erase[cp->parity], 3) );
+		    safe_write(1, erase[cp->parity], 3);
 		    bp--;
 		}
 		break;
@@ -1098,10 +1317,10 @@ char   *get_logname(op, cp, tp)
 	    default:
 		if (!isascii(ascval) || !isprint(ascval)) {
 		     /* ignore garbage characters */ ;
-		} else if (bp - logname >= sizeof(logname) - 1) {
+		} else if ((size_t)(bp - logname) >= sizeof(logname) - 1) {
 		    error(_("%s: input overrun"), op->tty);
 		} else {
-		    ignore_result( write(1, &c, 1) );	/* echo the character */
+		    safe_write(1, &c, 1);	/* echo the character */
 		    *bp++ = ascval;		/* and store it */
 		}
 		break;
@@ -1118,11 +1337,8 @@ char   *get_logname(op, cp, tp)
 }
 
 /* termio_final - set the final tty mode bits */
-void
-termio_final(op, tp, cp)
-     struct options *op;
-     struct termios *tp;
-     struct chardata *cp;
+static void
+termio_final(struct options *op, struct termios *tp, struct chardata *cp)
 {
     /* General terminal-independent stuff. */
 
@@ -1194,9 +1410,8 @@ termio_final(op, tp, cp)
 }
 
 /* caps_lock - string contains upper case without lower case */
-int
-caps_lock(s)
-     char   *s;
+static int
+caps_lock(char *s)
 {
     int     capslock;
 
@@ -1210,9 +1425,8 @@ caps_lock(s)
 }
 
 /* bcode - convert speed string to speed code; return 0 on failure */
-int
-bcode(s)
-     char   *s;
+static int
+bcode(char *s)
 {
     struct Speedtab *sp;
     long    speed = atol(s);
@@ -1225,9 +1439,9 @@ bcode(s)
 
 /* usage - explain */
 
-void __attribute__((__noreturn__)) usage(void)
+static void __attribute__((__noreturn__)) usage(void)
 {
-    fprintf(stderr, _("Usage: %s [-8hiLmsUw] [-l login_program] [-t timeout] [-I initstring] [-H login_host] baud_rate,... line [termtype]\nor\t[-hiLmw] [-l login_program] [-t timeout] [-I initstring] [-H login_host] line baud_rate,... [termtype]\n"), progname);
+    fprintf(stderr, _("Usage: %s [-8hiLmsUw] [-l login_program] [-t timeout] [-I initstring] [-H login_host] [baud_rate,...] line [termtype]\nor\t[-hiLmw] [-l login_program] [-t timeout] [-I initstring] [-H login_host] line [baud_rate,...] [termtype]\n"), progname);
     exit(EXIT_FAILURE);
 }
 
@@ -1235,9 +1449,9 @@ void __attribute__((__noreturn__)) usage
 
 #define	str2cpy(b,s1,s2)	strcat(strcpy(b,s1),s2)
 
-void
-error(const char *fmt, ...) {
-    va_list ap;
+static void
+dolog(int priority, const char * fmt, va_list ap)
+{
 #ifndef	USE_SYSLOG
     int     fd;
 #endif
@@ -1267,7 +1481,9 @@ error(const char *fmt, ...) {
      * vsprintf() like interface.
      */
 
-    va_start(ap, fmt);
+#ifdef __linux__
+    vsnprintf (bp, sizeof(buf)-strlen(buf), fmt, ap);
+#else
     while (*fmt && bp < &buf[BUFSIZ-1]) {
 	if (strncmp(fmt, "%s", 2) == 0) {
 	    xstrncpy(bp, va_arg(ap, char *), &buf[BUFSIZ-1] - bp);
@@ -1282,7 +1498,7 @@ error(const char *fmt, ...) {
 	}
     }
     *bp = 0;
-    va_end(ap);
+#endif
 
     /*
      * Write the diagnostic directly to /dev/console if we do not use the
@@ -1291,16 +1507,308 @@ error(const char *fmt, ...) {
 
 #ifdef	USE_SYSLOG
     (void) openlog(progname, LOG_PID, LOG_AUTHPRIV);
-    (void) syslog(LOG_ERR, "%s", buf);
+    (void) syslog(priority, "%s", buf);
     closelog();
 #else
     /* Terminate with CR-LF since the console mode is unknown. */
     (void) strcat(bp, "\r\n");
     if ((fd = open("/dev/console", 1)) >= 0) {
-	ignore_result( write(fd, buf, strlen(buf)) );
+	safe_write(fd, buf, strlen(buf));
 	(void) close(fd);
     }
 #endif
+}
+
+static void
+error(const char *fmt, ...) {
+    va_list ap;
+
+    va_start(ap, fmt);
+    dolog(LOG_ERR, fmt, ap);
+    va_end(ap);
+
     (void) sleep((unsigned) 10);		/* be kind to init(8) */
     exit(1);
 }
+
+static void
+warn(const char *fmt, ...) {
+    va_list ap;
+
+    va_start(ap, fmt);
+    dolog(LOG_WARNING, fmt, ap);
+    va_end(ap);
+}
+
+/*
+ * Reset virtual console on stdin to its defaults
+ */
+static void
+reset_vc(const int mode)
+{
+    struct termios termios;
+    /* The above reset only puts the output things into a sane state.
+     * The input state is not reset.
+     */
+    memset (&termios, 0, sizeof termios);
+    if (tcgetattr (0, &termios))
+	warn ("tcgetattr problem: %m");
+
+    /* Use defaults of <sys/ttydefaults.h> for base settings */
+    termios.c_iflag |= TTYDEF_IFLAG;
+    termios.c_oflag |= TTYDEF_OFLAG;
+    termios.c_lflag |= TTYDEF_LFLAG;
+    termios.c_cflag |= (TTYDEF_SPEED | TTYDEF_CFLAG);
+
+    /* Sane setting, allow eight bit characters, no carriage return delay
+     * the same result as `stty sane cr0 pass8'
+     */
+    termios.c_iflag |=  (BRKINT | ICRNL | IMAXBEL);
+    termios.c_iflag &= ~(IGNBRK | INLCR | IGNCR | IXOFF | IUCLC | IXANY | ISTRIP);
+    termios.c_oflag |=  (OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0);
+    termios.c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | OFDEL |\
+			NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
+    termios.c_lflag |=  (ISIG | ICANON | IEXTEN | ECHO|ECHOE|ECHOK|ECHOKE);
+    termios.c_lflag &= ~(ECHONL | NOFLSH | XCASE | TOSTOP | ECHOCTL|ECHOPRT);
+    termios.c_cflag |=  (CREAD | CS8 | B38400);
+    termios.c_cflag &= ~(PARENB);
+#ifdef IUTF8
+    /* Set UTF-8 input flag */
+    switch(mode) {
+    case K_UNICODE:
+	termios.c_iflag |= IUTF8;
+	break;
+    case K_RAW:
+    case K_MEDIUMRAW:
+    case K_XLATE:
+    default:
+	termios.c_iflag &= ~IUTF8;
+	break;
+    }
+#endif
+    /* VTIME and VMIN can overlap with VEOF and VEOL since they are
+     * only used for non-canonical mode. We just set the at the
+     * beginning, so nothing bad should happen.
+     */
+    termios.c_cc[VTIME]    = 0;
+    termios.c_cc[VMIN]     = 1;
+    termios.c_cc[VINTR]    = CINTR;
+    termios.c_cc[VQUIT]    = CQUIT;
+    termios.c_cc[VERASE]   = CERASE; /* ASCII DEL (0177) */
+    termios.c_cc[VKILL]    = CKILL;
+    termios.c_cc[VEOF]     = CEOF;
+    termios.c_cc[VSWTC]    = _POSIX_VDISABLE;
+    termios.c_cc[VSTART]   = CSTART;
+    termios.c_cc[VSTOP]    = CSTOP;
+    termios.c_cc[VSUSP]    = CSUSP;
+    termios.c_cc[VEOL]     = _POSIX_VDISABLE;
+    termios.c_cc[VREPRINT] = CREPRINT;
+    termios.c_cc[VDISCARD] = CDISCARD;
+    termios.c_cc[VWERASE]  = CWERASE;
+    termios.c_cc[VLNEXT]   = CLNEXT;
+    termios.c_cc[VEOL2]    = _POSIX_VDISABLE;
+    if (tcsetattr (0, TCSADRAIN, &termios))
+	warn ("tcsetattr problem: %m");
+}
+
+static void
+output_special_char(unsigned char c, struct options *op, struct termios *tp)
+{
+    struct utsname uts;
+    (void) uname(&uts);
+    switch (c) {
+    case 's':
+	(void) printf ("%s", uts.sysname);
+	break;
+    case 'n':
+	(void) printf ("%s", uts.nodename);
+	break;
+    case 'r':
+	(void) printf ("%s", uts.release);
+	break;
+    case 'v':
+	(void) printf ("%s", uts.version);
+	break;
+    case 'm':
+	(void) printf ("%s", uts.machine);
+	break;
+    case 'o':
+	{
+	    char domainname[MAXHOSTNAMELEN+1];
+#ifdef HAVE_GETDOMAINNAME
+	    if (getdomainname(domainname, sizeof(domainname)))
+#endif
+		strcpy(domainname, "unknown_domain");
+	    domainname[sizeof(domainname)-1] = '\0';
+	    printf ("%s", domainname);
+	    break;
+	}
+    case 'O':
+	{
+	    char *dom = "unknown_domain";
+	    char host[MAXHOSTNAMELEN+1];
+	    struct addrinfo hints, *info = NULL;
+
+	    memset(&hints, 0, sizeof(hints));
+	    hints.ai_flags = AI_CANONNAME;
+
+	    if (gethostname(host, sizeof(host)) ||
+		getaddrinfo(host, NULL, &hints, &info) ||
+		info == NULL)
+		fputs(dom, stdout);
+	    else {
+		char *canon;
+		if (info->ai_canonname &&
+		    (canon = strchr(info->ai_canonname, '.')))
+		    dom = canon + 1;
+		fputs(dom, stdout);
+		freeaddrinfo(info);
+	    }
+	    break;
+	}
+    case 'd':
+    case 't':
+	{
+	    time_t now;
+	    struct tm *tm;
+
+	    (void) time (&now);
+	    tm = localtime(&now);
+
+	    if (c == 'd') /* ISO 8601 */
+		(void) printf ("%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
+		(void) printf ("%02d:%02d:%02d",
+				tm->tm_hour, tm->tm_min, tm->tm_sec);
+	    break;
+	}
+    case 'l':
+	(void) printf ("%s", op->tty);
+	break;
+    case 'b':
+	{
+	    const speed_t speed = cfgetispeed(tp);
+	    int i;
+
+	    for (i = 0; speedtab[i].speed; i++) {
+		if (speedtab[i].code == speed) {
+		    printf("%ld", speedtab[i].speed);
+		    break;
+		}
+	    }
+	    break;
+	}
+    case 'u':
+    case 'U':
+	{
+	    int users = 0;
+	    struct utmp *ut;
+	    setutent();
+	    while ((ut = getutent()))
+	    if (ut->ut_type == USER_PROCESS)
+		users++;
+	    endutent();
+	    printf ("%d ", users);
+	    if (c == 'U')
+		printf ((users == 1) ? _("user") : _("users"));
+	    break;
+	}
+    default:
+	(void) putchar(c);
+	break;
+    }
+}
+
+static ssize_t
+safe_write(int fd, const char *buffer, size_t count)
+{
+    ssize_t offset = 0;
+
+    while (count > 0) {
+	ssize_t block = write(fd, &buffer[offset], count);
+
+	if (block < 0 && errno == EINTR)
+	    continue;
+	if (block <= 0)
+	    return offset ? offset : block;
+	offset += block;
+	count -= block;
+    }
+    return offset;
+}
+
+/*
+ * Don't allow the user to pass an option as a user name
+ * To be more safe: Use -- to make sure the rest is interpreted
+ * as non-options by the program, if it supports it
+ */
+static void
+checkname(const char* nm)
+{
+    const char *p = nm;
+    if (!nm)
+	goto err;
+    if (strlen (nm) > 42)
+	goto err;
+    while (isspace (*p))
+	p++;
+    if (*p == '-')
+	goto err;
+    return;
+err:
+    errno = EPERM;
+    error ("checkname: %m");
+}
+
+static void
+replacename(char** arr, const char* nm)
+{
+    const char *p;
+    char *tmp;
+    while ((p = *arr)) {
+	const char *p1 = p;
+	while (*p1) {
+	    if (memcmp (p1, "\\u", 2) == 0) {
+	        tmp = malloc (strlen (p) + strlen (nm));
+	        if (!tmp)
+	            error ("replacename: %m");
+	        if (p1 != p)
+	            memcpy (tmp, p, (p1-p));
+	        *(tmp + (p1-p)) = 0;
+	        strcat (tmp, nm);
+	        strcat (tmp, p1+2);
+	        *arr = tmp;
+	    }
+	    p1++;
+	}
+	arr++;
+}
+}
+
+static void
+mkarray(char** arr, char* str)
+{
+    char* p = str;
+    char* start = p;
+    int i = 0;
+
+    while (*p && i < (ARRAY_SIZE_MAX - 2)) {
+	if (isspace (*p)) {
+	    *p = 0;
+	    while (isspace (*++p))
+	        ;
+	    if (*p) {
+	        arr[i++] = start;
+	        start = p;
+	    }
+	} else
+	    p++;
+    }
+    arr[i++] = start;
+    arr[i++] = (char*)0;
+}
_______________________________________________
systemd-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/systemd-devel

Reply via email to