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