]> git.ipfire.org Git - thirdparty/util-linux.git/blame - term-utils/agetty.c
script: fix building with --disable-nls
[thirdparty/util-linux.git] / term-utils / agetty.c
CommitLineData
b75c8134 1/*
53d55042
SK
2 * Alternate Getty (agetty) 'agetty' is a versatile, portable, easy to use
3 * replacement for getty on SunOS 4.1.x or the SAC ttymon/ttyadm/sacadm/pmadm
4 * suite on Solaris and other SVR4 systems. 'agetty' was written by Wietse
5 * Venema, enhanced by John DiMarco, and further enhanced by Dennis Cronin.
b75c8134
KZ
6 *
7 * Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
1683f6bf
DWF
8 * Adopt the mingetty features for a better support
9 * of virtual consoles by Werner Fink <werner@suse.de>
b75c8134 10 *
53d55042 11 * This program is freely distributable.
b75c8134 12 */
6dbe3af9
KZ
13#include <stdio.h>
14#include <unistd.h>
15#include <stdlib.h>
2b6fc908 16#include <string.h>
1961482a 17#include <termios.h>
6dbe3af9
KZ
18#include <signal.h>
19#include <errno.h>
3aa6b68f 20#include <sys/ioctl.h>
6dbe3af9
KZ
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <fcntl.h>
22853e4a 24#include <stdarg.h>
6dbe3af9
KZ
25#include <ctype.h>
26#include <utmp.h>
27#include <getopt.h>
22853e4a 28#include <time.h>
6dbe3af9 29#include <sys/file.h>
e61e66bd
KZ
30#include <sys/socket.h>
31#include <netdb.h>
729def03 32#include <langinfo.h>
3aa6b68f 33#include <grp.h>
df73ad46 34
8abcf290 35#include "strutils.h"
729def03 36#include "writeall.h"
7eda085c 37#include "nls.h"
e6590f06 38#include "pathnames.h"
e5b17b31 39#include "c.h"
1683f6bf 40#include "widechar.h"
6dbe3af9 41
fd6b7a7f 42#ifdef __linux__
0da025da 43# include <sys/kd.h>
53d55042
SK
44# include <sys/param.h>
45# define USE_SYSLOG
729def03
WF
46# ifndef DEFAULT_VCTERM
47# define DEFAULT_VCTERM "linux"
48# endif
49# ifndef DEFAULT_STERM
50# define DEFAULT_STERM "vt102"
51# endif
4585ec0b
ST
52#elif defined(__GNU__)
53# define USE_SYSLOG
54# ifndef DEFAULT_VCTERM
55# define DEFAULT_VCTERM "hurd"
56# endif
57# ifndef DEFAULT_STERM
58# define DEFAULT_STERM "vt102"
59# endif
729def03
WF
60#else
61# ifndef DEFAULT_VCTERM
62# define DEFAULT_VCTERM "vt100"
63# endif
64# ifndef DEFAULT_STERM
65# define DEFAULT_STERM "vt100"
66# endif
6dbe3af9
KZ
67#endif
68
53d55042 69/* If USE_SYSLOG is undefined all diagnostics go to /dev/console. */
6dbe3af9 70#ifdef USE_SYSLOG
53d55042 71# include <syslog.h>
6dbe3af9
KZ
72#endif
73
b75c8134
KZ
74/*
75 * Some heuristics to find out what environment we are in: if it is not
53d55042
SK
76 * System V, assume it is SunOS 4. The LOGIN_PROCESS is defined in System V
77 * utmp.h, which will select System V style getty.
b75c8134 78 */
53d55042
SK
79#ifdef LOGIN_PROCESS
80# define SYSV_STYLE
6dbe3af9
KZ
81#endif
82
b75c8134
KZ
83/*
84 * Things you may want to modify.
85 *
86 * If ISSUE is not defined, agetty will never display the contents of the
87 * /etc/issue file. You will not want to spit out large "issue" files at the
88 * wrong baud rate. Relevant for System V only.
89 *
90 * You may disagree with the default line-editing etc. characters defined
91 * below. Note, however, that DEL cannot be used for interrupt generation
92 * and for line editing at the same time.
93 */
6dbe3af9 94
53d55042 95/* Displayed before the login prompt. */
6dbe3af9 96#ifdef SYSV_STYLE
96cc7b0b 97# define ISSUE _PATH_ISSUE
53d55042 98# include <sys/utsname.h>
6dbe3af9
KZ
99#endif
100
53d55042 101/* Login prompt. */
e85281a8 102#define LOGIN "login: "
9aeb66dc 103#define LOGIN_ARGV_MAX 16 /* Numbers of args for login */
6dbe3af9
KZ
104
105/* Some shorthands for control characters. */
6dbe3af9
KZ
106#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */
107#define CR CTL('M') /* carriage return */
108#define NL CTL('J') /* line feed */
109#define BS CTL('H') /* back space */
110#define DEL CTL('?') /* delete */
111
53d55042 112/* Defaults for line-editing etc. characters; you may want to change these. */
6dbe3af9
KZ
113#define DEF_ERASE DEL /* default erase character */
114#define DEF_INTR CTL('C') /* default interrupt character */
115#define DEF_QUIT CTL('\\') /* default quit char */
116#define DEF_KILL CTL('U') /* default kill char */
117#define DEF_EOF CTL('D') /* default EOF char */
118#define DEF_EOL 0
119#define DEF_SWITCH 0 /* default switch char */
120
77337bfb 121#ifndef MAXHOSTNAMELEN
53d55042
SK
122# ifdef HOST_NAME_MAX
123# define MAXHOSTNAMELEN HOST_NAME_MAX
124# else
125# define MAXHOSTNAMELEN 64
126# endif /* HOST_NAME_MAX */
127#endif /* MAXHOSTNAMELEN */
6dbe3af9 128
b75c8134
KZ
129/*
130 * When multiple baud rates are specified on the command line, the first one
131 * we will try is the first one specified.
132 */
6dbe3af9
KZ
133#define FIRST_SPEED 0
134
135/* Storage for command-line options. */
53d55042 136#define MAX_SPEED 10 /* max. nr. of baud rates */
6dbe3af9
KZ
137
138struct options {
bde20e1b
WF
139 int flags; /* toggle switches, see below */
140 int timeout; /* time-out period */
eb8e1f9f 141 char *autolog; /* login the user automatically */
e85281a8
DWF
142 char *chdir; /* Chdir before the login */
143 char *chroot; /* Chroot before the login */
bde20e1b 144 char *login; /* login program */
eb8e1f9f 145 char *logopt; /* options for login program */
bde20e1b 146 char *tty; /* name of tty */
729def03
WF
147 char *vcline; /* line of virtual console */
148 char *term; /* terminal type */
bde20e1b
WF
149 char *initstring; /* modem init string */
150 char *issue; /* alternative issue file */
e85281a8
DWF
151 int delay; /* Sleep seconds before prompt */
152 int nice; /* Run login with this priority */
bde20e1b
WF
153 int numspeed; /* number of baud rates to try */
154 speed_t speeds[MAX_SPEED]; /* baud rates to be tried */
6dbe3af9
KZ
155};
156
53d55042
SK
157#define F_PARSE (1<<0) /* process modem status messages */
158#define F_ISSUE (1<<1) /* display /etc/issue */
159#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */
160#define F_LOCAL (1<<3) /* force local */
161#define F_INITSTRING (1<<4) /* initstring is set */
162#define F_WAITCRLF (1<<5) /* wait for CR or LF */
163#define F_CUSTISSUE (1<<6) /* give alternative issue file */
164#define F_NOPROMPT (1<<7) /* do not ask for login name! */
165#define F_LCUC (1<<8) /* support for *LCUC stty modes */
166#define F_KEEPSPEED (1<<9) /* follow baud rate from kernel */
167#define F_KEEPCFLAGS (1<<10) /* reuse c_cflags setup from kernel */
bde20e1b 168#define F_EIGHTBITS (1<<11) /* Assume 8bit-clean tty */
3aa6b68f
WF
169#define F_VCONSOLE (1<<12) /* This is a virtual console */
170#define F_HANGUP (1<<13) /* Do call vhangup(2) */
0da025da 171#define F_UTF8 (1<<14) /* We can do UTF8 */
eb8e1f9f 172#define F_LOGINPAUSE (1<<15) /* Wait for any key before dropping login prompt */
e85281a8
DWF
173#define F_NOCLEAR (1<<16) /* Do not clear the screen before prompting */
174#define F_NONL (1<<17) /* No newline before issue */
175#define F_NOHOSTNAME (1<<18) /* Do not show the hostname */
176#define F_LONGHNAME (1<<19) /* Show Full qualified hostname */
0da025da
WF
177
178#define serial_tty_option(opt, flag) \
179 (((opt)->flags & (F_VCONSOLE|(flag))) == (flag))
6dbe3af9
KZ
180
181/* Storage for things detected while the login name was read. */
6dbe3af9 182struct chardata {
53d55042
SK
183 int erase; /* erase character */
184 int kill; /* kill character */
185 int eol; /* end-of-line character */
186 int parity; /* what parity did we see */
187 int capslock; /* upper case without lower case */
6dbe3af9
KZ
188};
189
190/* Initial values for the above. */
6dbe3af9 191struct chardata init_chardata = {
53d55042
SK
192 DEF_ERASE, /* default erase character */
193 DEF_KILL, /* default kill character */
194 13, /* default eol char */
195 0, /* space parity */
196 0, /* no capslock */
fd6b7a7f
KZ
197};
198
199struct Speedtab {
53d55042 200 long speed;
bde20e1b 201 speed_t code;
fd6b7a7f
KZ
202};
203
204static struct Speedtab speedtab[] = {
53d55042
SK
205 {50, B50},
206 {75, B75},
207 {110, B110},
208 {134, B134},
209 {150, B150},
210 {200, B200},
211 {300, B300},
212 {600, B600},
213 {1200, B1200},
214 {1800, B1800},
215 {2400, B2400},
216 {4800, B4800},
217 {9600, B9600},
fd6b7a7f 218#ifdef B19200
53d55042 219 {19200, B19200},
fd6b7a7f
KZ
220#endif
221#ifdef B38400
53d55042 222 {38400, B38400},
fd6b7a7f
KZ
223#endif
224#ifdef EXTA
53d55042 225 {19200, EXTA},
fd6b7a7f
KZ
226#endif
227#ifdef EXTB
53d55042 228 {38400, EXTB},
fd6b7a7f
KZ
229#endif
230#ifdef B57600
53d55042 231 {57600, B57600},
fd6b7a7f
KZ
232#endif
233#ifdef B115200
53d55042 234 {115200, B115200},
fd6b7a7f
KZ
235#endif
236#ifdef B230400
53d55042 237 {230400, B230400},
fd6b7a7f 238#endif
53d55042 239 {0, 0},
6dbe3af9
KZ
240};
241
729def03 242static void init_special_char(char* arg, struct options *op);
53d55042
SK
243static void parse_args(int argc, char **argv, struct options *op);
244static void parse_speeds(struct options *op, char *arg);
729def03
WF
245static void update_utmp(struct options *op);
246static void open_tty(char *tty, struct termios *tp, struct options *op);
53d55042 247static void termio_init(struct options *op, struct termios *tp);
0da025da 248static void reset_vc (const struct options *op, struct termios *tp);
53d55042 249static void auto_baud(struct termios *tp);
729def03 250static void output_special_char (unsigned char c, struct options *op, struct termios *tp);
53d55042
SK
251static void do_prompt(struct options *op, struct termios *tp);
252static void next_speed(struct options *op, struct termios *tp);
253static char *get_logname(struct options *op,
254 struct termios *tp, struct chardata *cp);
255static void termio_final(struct options *op,
256 struct termios *tp, struct chardata *cp);
257static int caps_lock(char *s);
bde20e1b
WF
258static speed_t bcode(char *s);
259static void usage(FILE * out) __attribute__((__noreturn__));
763d7a87
KZ
260static void log_err(const char *, ...) __attribute__((__noreturn__))
261 __attribute__((__format__(printf, 1, 2)));
262static void log_warn (const char *, ...)
263 __attribute__((__format__(printf, 1, 2)));
a694957a 264static ssize_t append(char *dest, size_t len, const char *sep, const char *src);
9aeb66dc
KZ
265static void check_username (const char* nm);
266static void login_options_to_argv(char *argv[], int *argc, char *str, char *username);
6dbe3af9 267
eb63b9b8 268/* Fake hostname for ut_host specified on command line. */
bde20e1b 269static char *fakehost;
eb63b9b8 270
6dbe3af9
KZ
271#ifdef DEBUGGING
272#define debug(s) fprintf(dbf,s); fflush(dbf)
273FILE *dbf;
274#else
53d55042
SK
275#define debug(s)
276#endif /* DEBUGGING */
6dbe3af9 277
53d55042 278int main(int argc, char **argv)
6dbe3af9 279{
9aeb66dc 280 char *username = NULL; /* login name, given to /bin/login */
eb8e1f9f
WF
281 struct chardata chardata; /* will be set by get_logname() */
282 struct termios termios; /* terminal mode bits */
70bedfa1 283 static struct options options = {
eb8e1f9f
WF
284 .flags = F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
285 .login = _PATH_LOGIN, /* default login program */
eb8e1f9f
WF
286 .tty = "tty1", /* default tty line */
287 .term = DEFAULT_VCTERM, /* terminal type */
288 .issue = ISSUE /* default issue file */
70bedfa1 289 };
9aeb66dc
KZ
290 char *login_argv[LOGIN_ARGV_MAX + 1];
291 int login_argc = 0;
3aa6b68f
WF
292 struct sigaction sa, sa_hup, sa_quit, sa_int;
293 sigset_t set;
70bedfa1
KZ
294
295 setlocale(LC_ALL, "");
296 bindtextdomain(PACKAGE, LOCALEDIR);
297 textdomain(PACKAGE);
b75c8134 298
3aa6b68f
WF
299 /* In case vhangup(2) has to called */
300 sa.sa_handler = SIG_IGN;
301 sa.sa_flags = SA_RESTART;
302 sigemptyset (&sa.sa_mask);
303 sigaction(SIGHUP, &sa, &sa_hup);
304 sigaction(SIGQUIT, &sa, &sa_quit);
305 sigaction(SIGINT, &sa, &sa_int);
306
6dbe3af9 307#ifdef DEBUGGING
fd6b7a7f 308 dbf = fopen("/dev/ttyp0", "w");
53d55042
SK
309 for (int i = 1; i < argc; i++)
310 debug(argv[i]);
311#endif /* DEBUGGING */
6dbe3af9 312
70bedfa1 313 /* Parse command-line arguments. */
70bedfa1 314 parse_args(argc, argv, &options);
6dbe3af9 315
9aeb66dc
KZ
316 login_argv[login_argc++] = options.login; /* set login program name */
317
70bedfa1 318 /* Update the utmp file. */
6dbe3af9 319#ifdef SYSV_STYLE
729def03 320 update_utmp(&options);
6dbe3af9 321#endif
e85281a8
DWF
322 if (options.delay)
323 sleep(options.delay);
324
70bedfa1 325 debug("calling open_tty\n");
53d55042 326
70bedfa1 327 /* Open the tty as standard { input, output, error }. */
729def03 328 open_tty(options.tty, &termios, &options);
70bedfa1 329
3aa6b68f
WF
330 /* Unmask SIGHUP if inherited */
331 sigemptyset(&set);
332 sigaddset(&set, SIGHUP);
333 sigprocmask(SIG_UNBLOCK, &set, NULL);
334 sigaction(SIGHUP, &sa_hup, NULL);
335
70bedfa1
KZ
336 tcsetpgrp(STDIN_FILENO, getpid());
337 /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
338 debug("calling termio_init\n");
53d55042 339 termio_init(&options, &termios);
70bedfa1 340
53d55042 341 /* Write the modem init string and DO NOT flush the buffers. */
eb8e1f9f
WF
342 if (serial_tty_option(&options, F_INITSTRING) &&
343 options.initstring && *options.initstring != '\0') {
70bedfa1 344 debug("writing init string\n");
729def03 345 write_all(STDOUT_FILENO, options.initstring,
3aa6b68f 346 strlen(options.initstring));
70bedfa1
KZ
347 }
348
0da025da 349 if (!serial_tty_option(&options, F_LOCAL))
53d55042 350 /* Go to blocking write mode unless -L is specified. */
70bedfa1 351 fcntl(STDOUT_FILENO, F_SETFL,
53d55042 352 fcntl(STDOUT_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
70bedfa1
KZ
353
354 /* Optionally detect the baud rate from the modem status message. */
355 debug("before autobaud\n");
0da025da 356 if (serial_tty_option(&options, F_PARSE))
70bedfa1
KZ
357 auto_baud(&termios);
358
359 /* Set the optional timer. */
360 if (options.timeout)
763d7a87 361 alarm((unsigned) options.timeout);
70bedfa1 362
53d55042 363 /* Optionally wait for CR or LF before writing /etc/issue */
0da025da 364 if (serial_tty_option(&options, F_WAITCRLF)) {
70bedfa1
KZ
365 char ch;
366
367 debug("waiting for cr-lf\n");
53d55042
SK
368 while (read(STDIN_FILENO, &ch, 1) == 1) {
369 /* Strip "parity bit". */
370 ch &= 0x7f;
fd6b7a7f 371#ifdef DEBUGGING
70bedfa1 372 fprintf(dbf, "read %c\n", ch);
fd6b7a7f 373#endif
53d55042
SK
374 if (ch == '\n' || ch == '\r')
375 break;
70bedfa1 376 }
726f69e2 377 }
726f69e2 378
70bedfa1 379 chardata = init_chardata;
1683f6bf 380 if ((options.flags & F_NOPROMPT) == 0) {
eb8e1f9f 381 if (options.autolog) {
9aeb66dc 382 /* Do the auto login. */
eb8e1f9f
WF
383 debug("doing auto login\n");
384 do_prompt(&options, &termios);
763d7a87 385 printf("%s%s (automatic login)\n", LOGIN, options.autolog);
9aeb66dc 386 username = options.autolog;
eb8e1f9f
WF
387 } else {
388 /* Read the login name. */
389 debug("reading login name\n");
9aeb66dc 390 while ((username =
eb8e1f9f
WF
391 get_logname(&options, &termios, &chardata)) == 0)
392 if ((options.flags & F_VCONSOLE) == 0)
393 next_speed(&options, &termios);
394 }
70bedfa1 395 }
6dbe3af9 396
70bedfa1 397 /* Disable timer. */
70bedfa1 398 if (options.timeout)
53d55042 399 alarm(0);
6dbe3af9 400
0da025da
WF
401 if ((options.flags & F_VCONSOLE) == 0) {
402 /* Finalize the termios settings. */
403 termio_final(&options, &termios, &chardata);
6dbe3af9 404
0da025da
WF
405 /* Now the newline character should be properly written. */
406 write_all(STDOUT_FILENO, "\r\n", 2);
407 }
6dbe3af9 408
763d7a87
KZ
409 sigaction(SIGQUIT, &sa_quit, NULL);
410 sigaction(SIGINT, &sa_int, NULL);
3aa6b68f 411
9aeb66dc
KZ
412 if (username)
413 check_username(username);
414
415 if (options.logopt) {
416 /*
417 * The --login-options completely overwrites the default
418 * way how agetty composes login(1) command line.
419 */
420 login_options_to_argv(login_argv, &login_argc,
421 options.logopt, username);
422 } else if (username) {
423 if (options.autolog)
424 login_argv[login_argc++] = "-f";
425 else
426 login_argv[login_argc++] = "--";
427 login_argv[login_argc++] = username;
428 }
eb8e1f9f 429
9aeb66dc 430 login_argv[login_argc] = NULL; /* last login argv */
eb8e1f9f 431
e85281a8
DWF
432 if (options.chroot) {
433 if (chroot(options.chroot) < 0)
434 log_err(_("%s: can't change root directory %s: %m"),
435 options.tty, options.chroot);
436 }
437 if (options.chdir) {
438 if (chdir(options.chdir) < 0)
439 log_err(_("%s: can't change working directory %s: %m"),
440 options.tty, options.chdir);
441 }
442 if (options.nice) {
443 if (nice(options.nice) < 0)
444 log_warn(_("%s: can't change process priority: %m"),
445 options.tty);
446 }
447
70bedfa1 448 /* Let the login program take care of password validation. */
9aeb66dc
KZ
449 execv(options.login, login_argv);
450 log_err(_("%s: can't exec %s: %m"), options.tty, login_argv[0]);
451}
452
453/*
454 * Returns : @str if \u not found
455 * : @username if @str equal to "\u"
456 * : newly allocated string if \u mixed with something other
457 */
458static char *replace_u(char *str, char *username)
459{
460 char *entry = NULL, *p = str;
461 size_t usz = username ? strlen(username) : 0;
462
463 while (*p) {
464 size_t sz;
465 char *tp, *old = entry;
466
467 if (memcmp(p, "\\u", 2)) {
468 p++;
469 continue; /* no \u */
470 }
471 sz = strlen(str);
472
473 if (p == str && sz == 2)
474 /* 'str' contains only '\u' */
475 return username;
476
477 tp = entry = malloc(sz + usz);
478 if (!tp)
479 log_err(_("failed to allocate memory: %m"));
480
481 if (p != str) {
482 /* copy chars befor \u */
483 memcpy(tp, str, p - str);
484 tp += p - str;
485 }
486 if (usz) {
487 /* copy username */
488 memcpy(tp, username, usz);
489 tp += usz;
490 }
491 if (*(p + 2))
492 /* copy chars after \u + \0 */
493 memcpy(tp, p + 2, sz - (p - str) - 1);
494 else
495 *tp = '\0';
496
497 p = tp;
498 str = entry;
499 free(old);
500 }
501
502 return entry ? entry : str;
503}
504
505static void login_options_to_argv(char *argv[], int *argc,
506 char *str, char *username)
507{
508 char *p;
509 int i = *argc;
510
511 while (str && isspace(*str))
512 str++;
513 p = str;
514
515 while (p && *p && i < LOGIN_ARGV_MAX) {
516 if (isspace(*p)) {
517 *p = '\0';
518 while (isspace(*++p))
519 ;
520 if (*p) {
521 argv[i++] = replace_u(str, username);
522 str = p;
523 }
524 } else
525 p++;
526 }
527 if (str && *str && i < LOGIN_ARGV_MAX)
528 argv[i++] = replace_u(str, username);
529 *argc = i;
6dbe3af9
KZ
530}
531
53d55042 532/* Parse command-line arguments. */
bde20e1b 533static void parse_args(int argc, char **argv, struct options *op)
6dbe3af9 534{
53d55042
SK
535 extern char *optarg;
536 extern int optind;
537 int c;
70bedfa1
KZ
538
539 enum {
540 VERSION_OPTION = CHAR_MAX + 1,
e85281a8
DWF
541 NOHOSTNAME_OPTION,
542 LONGHOSTNAME_OPTION,
70bedfa1
KZ
543 HELP_OPTION
544 };
3aa6b68f 545 const struct option longopts[] = {
70bedfa1 546 { "8bits", no_argument, 0, '8' },
eb8e1f9f 547 { "autologin", required_argument, 0, 'a' },
70bedfa1 548 { "noreset", no_argument, 0, 'c' },
e85281a8
DWF
549 { "chdir", required_argument, 0, 'C' },
550 { "delay", required_argument, 0, 'd' },
70bedfa1
KZ
551 { "issue-file", required_argument, 0, 'f' },
552 { "flow-control", no_argument, 0, 'h' },
553 { "host", required_argument, 0, 'H' },
554 { "noissue", no_argument, 0, 'i' },
555 { "init-string", required_argument, 0, 'I' },
e85281a8 556 { "noclear", no_argument, 0, 'J' },
70bedfa1
KZ
557 { "login-program", required_argument, 0, 'l' },
558 { "local-line", no_argument, 0, 'L' },
559 { "extract-baud", no_argument, 0, 'm' },
560 { "skip-login", no_argument, 0, 'n' },
e85281a8 561 { "nonewline", no_argument, 0, 'N' },
eb8e1f9f 562 { "login-options", required_argument, 0, 'o' },
3fc62fd3 563 { "login-pause", no_argument, 0, 'p' },
e85281a8
DWF
564 { "nice", required_argument, 0, 'P' },
565 { "chroot", required_argument, 0, 'r' },
3aa6b68f 566 { "hangup", no_argument, 0, 'R' },
70bedfa1
KZ
567 { "keep-baud", no_argument, 0, 's' },
568 { "timeout", required_argument, 0, 't' },
569 { "detect-case", no_argument, 0, 'U' },
570 { "wait-cr", no_argument, 0, 'w' },
3fc62fd3 571 { "nohostname", no_argument, 0, NOHOSTNAME_OPTION },
e85281a8 572 { "long-hostname", no_argument, 0, LONGHOSTNAME_OPTION },
70bedfa1
KZ
573 { "version", no_argument, 0, VERSION_OPTION },
574 { "help", no_argument, 0, HELP_OPTION },
575 { NULL, 0, 0, 0 }
576 };
577
763d7a87
KZ
578 while ((c = getopt_long(argc, argv,
579 "8a:cC:d:f:hH:iI:Jl:LmnNo:pP:r:Rst:Uw", longopts,
53d55042 580 NULL)) != -1) {
70bedfa1
KZ
581 switch (c) {
582 case '8':
bde20e1b 583 op->flags |= F_EIGHTBITS;
70bedfa1 584 break;
eb8e1f9f
WF
585 case 'a':
586 op->autolog = optarg;
587 break;
70bedfa1
KZ
588 case 'c':
589 op->flags |= F_KEEPCFLAGS;
590 break;
e85281a8
DWF
591 case 'C':
592 op->chdir = optarg;
593 break;
594 case 'd':
595 op->delay = atoi(optarg);
596 break;
53d55042 597 case 'f':
70bedfa1
KZ
598 op->flags |= F_CUSTISSUE;
599 op->issue = optarg;
600 break;
53d55042 601 case 'h':
70bedfa1
KZ
602 op->flags |= F_RTSCTS;
603 break;
53d55042 604 case 'H':
70bedfa1
KZ
605 fakehost = optarg;
606 break;
53d55042 607 case 'i':
70bedfa1
KZ
608 op->flags &= ~F_ISSUE;
609 break;
610 case 'I':
729def03
WF
611 init_special_char(optarg, op);
612 op->flags |= F_INITSTRING;
613 break;
e85281a8
DWF
614 case 'J':
615 op->flags |= F_NOCLEAR;
616 break;
70bedfa1 617 case 'l':
53d55042 618 op->login = optarg;
70bedfa1 619 break;
53d55042 620 case 'L':
70bedfa1
KZ
621 op->flags |= F_LOCAL;
622 break;
53d55042 623 case 'm':
70bedfa1
KZ
624 op->flags |= F_PARSE;
625 break;
626 case 'n':
627 op->flags |= F_NOPROMPT;
628 break;
eb8e1f9f
WF
629 case 'o':
630 op->logopt = optarg;
631 break;
632 case 'p':
633 op->flags |= F_LOGINPAUSE;
634 break;
e85281a8
DWF
635 case 'P':
636 op->nice = atoi(optarg);
637 break;
638 case 'r':
639 op->chroot = optarg;
640 break;
3aa6b68f
WF
641 case 'R':
642 op->flags |= F_HANGUP;
643 break;
70bedfa1 644 case 's':
53d55042 645 op->flags |= F_KEEPSPEED;
70bedfa1 646 break;
53d55042 647 case 't':
70bedfa1 648 if ((op->timeout = atoi(optarg)) <= 0)
bde20e1b 649 log_err(_("bad timeout value: %s"), optarg);
70bedfa1
KZ
650 break;
651 case 'U':
652 op->flags |= F_LCUC;
653 break;
654 case 'w':
655 op->flags |= F_WAITCRLF;
656 break;
e85281a8
DWF
657 case NOHOSTNAME_OPTION:
658 op->flags |= F_NOHOSTNAME;
659 break;
660 case LONGHOSTNAME_OPTION:
661 op->flags |= F_LONGHNAME;
662 break;
70bedfa1
KZ
663 case VERSION_OPTION:
664 printf(_("%s from %s\n"), program_invocation_short_name,
53d55042 665 PACKAGE_STRING);
70bedfa1
KZ
666 exit(EXIT_SUCCESS);
667 case HELP_OPTION:
668 usage(stdout);
669 default:
670 usage(stderr);
726f69e2 671 }
6dbe3af9 672 }
70bedfa1
KZ
673
674 debug("after getopt loop\n");
675
729def03 676 if (argc < optind + 1) {
bde20e1b 677 log_warn(_("not enough arguments"));
70bedfa1
KZ
678 usage(stderr);
679 }
680
729def03 681 /* Accept "tty", "baudrate tty", and "tty baudrate". */
53d55042
SK
682 if ('0' <= argv[optind][0] && argv[optind][0] <= '9') {
683 /* Assume BSD style speed. */
684 parse_speeds(op, argv[optind++]);
729def03
WF
685 if (argc < optind + 1) {
686 warn(_("not enough arguments"));
687 usage(stderr);
688 }
689 op->tty = argv[optind++];
70bedfa1 690 } else {
53d55042 691 op->tty = argv[optind++];
729def03
WF
692 if (argc > optind) {
693 char *v = argv[optind++];
694 if ('0' <= *v && *v <= '9')
695 parse_speeds(op, v);
696 else
697 op->speeds[op->numspeed++] = bcode("9600");
698 }
70bedfa1
KZ
699 }
700
729def03
WF
701 /* On virtual console remember the line which is used for */
702 if (strncmp(op->tty, "tty", 3) == 0 &&
703 strspn(op->tty + 3, "0123456789") == strlen(op->tty+3))
704 op->vcline = op->tty+3;
705
70bedfa1 706 if (argc > optind && argv[optind])
729def03 707 op->term = argv[optind];
6dbe3af9 708
66ee8158 709#ifdef DO_DEVFS_FIDDLING
70bedfa1 710 /*
53d55042 711 * Some devfs junk, following Goswin Brederlow:
70bedfa1
KZ
712 * turn ttyS<n> into tts/<n>
713 * turn tty<n> into vc/<n>
53d55042 714 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=72241
70bedfa1
KZ
715 */
716 if (op->tty && strlen(op->tty) < 90) {
717 char dev_name[100];
718 struct stat st;
719
720 if (strncmp(op->tty, "ttyS", 4) == 0) {
721 strcpy(dev_name, "/dev/");
722 strcat(dev_name, op->tty);
723 if (stat(dev_name, &st) < 0) {
724 strcpy(dev_name, "/dev/tts/");
725 strcat(dev_name, op->tty + 4);
ca8e91a4
KZ
726 if (stat(dev_name, &st) == 0) {
727 op->tty = strdup(dev_name + 5);
728 if (!op->tty)
729 log_err(_("failed to allocate memory: %m"));
730 }
70bedfa1
KZ
731 }
732 } else if (strncmp(op->tty, "tty", 3) == 0) {
733 strcpy(dev_name, "/dev/");
734 strncat(dev_name, op->tty, 90);
735 if (stat(dev_name, &st) < 0) {
736 strcpy(dev_name, "/dev/vc/");
737 strcat(dev_name, op->tty + 3);
ca8e91a4
KZ
738 if (stat(dev_name, &st) == 0) {
739 op->tty = strdup(dev_name + 5);
740 if (!op->tty)
741 log_err(_("failed to allocate memory: %m"));
742 }
70bedfa1
KZ
743 }
744 }
745 }
53d55042 746#endif /* DO_DEVFS_FIDDLING */
66ee8158 747
70bedfa1 748 debug("exiting parseargs\n");
6dbe3af9
KZ
749}
750
53d55042 751/* Parse alternate baud rates. */
bde20e1b 752static void parse_speeds(struct options *op, char *arg)
6dbe3af9 753{
53d55042 754 char *cp;
6dbe3af9 755
f021834e 756 debug("entered parse_speeds\n");
53d55042 757 for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *)0, ",")) {
70bedfa1 758 if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
bde20e1b 759 log_err(_("bad speed: %s"), cp);
70bedfa1 760 if (op->numspeed >= MAX_SPEED)
bde20e1b 761 log_err(_("too many alternate speeds"));
70bedfa1
KZ
762 }
763 debug("exiting parsespeeds\n");
6dbe3af9
KZ
764}
765
766#ifdef SYSV_STYLE
767
53d55042 768/* Update our utmp entry. */
729def03 769static void update_utmp(struct options *op)
6dbe3af9 770{
53d55042
SK
771 struct utmp ut;
772 time_t t;
729def03
WF
773 pid_t pid = getpid();
774 pid_t sid = getsid(0);
775 char *vcline = op->vcline;
776 char *line = op->tty;
53d55042 777 struct utmp *utp;
70bedfa1
KZ
778
779 /*
780 * The utmp file holds miscellaneous information about things started by
781 * /sbin/init and other system-related events. Our purpose is to update
782 * the utmp entry for the current process, in particular the process type
783 * and the tty line we are listening to. Return successfully only if the
784 * utmp file can be opened for update, and if we are able to find our
785 * entry in the utmp file.
786 */
70bedfa1
KZ
787 utmpname(_PATH_UTMP);
788 setutent();
789
53d55042 790 /*
729def03 791 * Find my pid in utmp.
53d55042
SK
792 *
793 * FIXME: Earlier (when was that?) code here tested only utp->ut_type !=
794 * INIT_PROCESS, so maybe the >= here should be >.
795 *
796 * FIXME: The present code is taken from login.c, so if this is changed,
797 * maybe login has to be changed as well (is this true?).
798 */
70bedfa1 799 while ((utp = getutent()))
729def03 800 if (utp->ut_pid == pid
70bedfa1
KZ
801 && utp->ut_type >= INIT_PROCESS
802 && utp->ut_type <= DEAD_PROCESS)
803 break;
804
805 if (utp) {
806 memcpy(&ut, utp, sizeof(ut));
807 } else {
53d55042 808 /* Some inits do not initialize utmp. */
70bedfa1 809 memset(&ut, 0, sizeof(ut));
729def03
WF
810 if (vcline && *vcline)
811 /* Standard virtual console devices */
763d7a87 812 strncpy(ut.ut_id, vcline, sizeof(ut.ut_id));
729def03
WF
813 else {
814 size_t len = strlen(line);
815 char * ptr;
763d7a87
KZ
816 if (len >= sizeof(ut.ut_id))
817 ptr = line + len - sizeof(ut.ut_id);
729def03
WF
818 else
819 ptr = line;
763d7a87 820 strncpy(ut.ut_id, ptr, sizeof(ut.ut_id));
729def03 821 }
70bedfa1
KZ
822 }
823
824 strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
825 strncpy(ut.ut_line, line, sizeof(ut.ut_line));
826 if (fakehost)
827 strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
828 time(&t);
829 ut.ut_time = t;
830 ut.ut_type = LOGIN_PROCESS;
729def03
WF
831 ut.ut_pid = pid;
832 ut.ut_session = sid;
70bedfa1
KZ
833
834 pututline(&ut);
835 endutent();
836
837 {
48d7b13a 838#ifdef HAVE_UPDWTMP
70bedfa1 839 updwtmp(_PATH_WTMP, &ut);
5c36a0eb 840#else
70bedfa1
KZ
841 int ut_fd;
842 int lf;
843
53d55042 844 if ((lf = open(_PATH_WTMPLOCK, O_CREAT | O_WRONLY, 0660)) >= 0) {
70bedfa1 845 flock(lf, LOCK_EX);
53d55042
SK
846 if ((ut_fd =
847 open(_PATH_WTMP, O_APPEND | O_WRONLY)) >= 0) {
729def03 848 write_all(ut_fd, &ut, sizeof(ut));
70bedfa1
KZ
849 close(ut_fd);
850 }
851 flock(lf, LOCK_UN);
852 close(lf);
853 }
53d55042 854#endif /* HAVE_UPDWTMP */
70bedfa1 855 }
6dbe3af9
KZ
856}
857
53d55042 858#endif /* SYSV_STYLE */
6dbe3af9 859
53d55042 860/* Set up tty as stdin, stdout & stderr. */
729def03 861static void open_tty(char *tty, struct termios *tp, struct options *op)
6dbe3af9 862{
3aa6b68f
WF
863 const pid_t pid = getpid();
864 int serial;
6dbe3af9 865
3aa6b68f
WF
866 /* Set up new standard input, unless we are given an already opened port. */
867
868 if (strcmp(tty, "-") != 0) {
869 char buf[PATH_MAX+1];
870 struct group *gr = NULL;
70bedfa1 871 struct stat st;
3aa6b68f
WF
872 int fd, len;
873 pid_t tid;
874 gid_t gid = 0;
875
876 /* Use tty group if available */
877 if ((gr = getgrnam("tty")))
878 gid = gr->gr_gid;
879
763d7a87
KZ
880 if (((len = snprintf(buf, sizeof(buf), "/dev/%s", tty)) >=
881 (int)sizeof(buf)) || (len < 0))
3aa6b68f
WF
882 log_err(_("/dev/%s: cannot open as standard input: %m"), tty);
883
884 /*
885 * There is always a race between this reset and the call to
886 * vhangup() that s.o. can use to get access to your tty.
887 * Linux login(1) will change tty permissions. Use root owner and group
888 * with permission -rw------- for the period between getty and login.
889 */
763d7a87 890 if (chown(buf, 0, gid) || chmod(buf, (gid ? 0660 : 0600))) {
3aa6b68f
WF
891 if (errno == EROFS)
892 log_warn("%s: %m", buf);
893 else
894 log_err("%s: %m", buf);
895 }
6dbe3af9 896
3aa6b68f
WF
897 /* Open the tty as standard input. */
898 if ((fd = open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0)) < 0)
899 log_err(_("/dev/%s: cannot open as standard input: %m"), tty);
900
901 /* Sanity checks... */
763d7a87 902 if (!isatty(fd))
3aa6b68f
WF
903 log_err(_("/dev/%s: not a character device"), tty);
904 if (fstat(fd, &st) < 0)
763d7a87 905 log_err("%s: %m", buf);
70bedfa1 906 if ((st.st_mode & S_IFMT) != S_IFCHR)
bde20e1b 907 log_err(_("/dev/%s: not a character device"), tty);
6dbe3af9 908
3aa6b68f 909 if (((tid = tcgetsid(fd)) < 0) || (pid != tid)) {
763d7a87 910 if (ioctl(fd, TIOCSCTTY, 1) == -1)
3aa6b68f
WF
911 log_err("/dev/%s: cannot get controlling tty: %m", tty);
912 }
913
914 if (op->flags & F_HANGUP) {
915 /*
916 * vhangup() will replace all open file descriptors in the kernel
917 * that point to our controlling tty by a dummy that will deny
918 * further reading/writing to our device. It will also reset the
919 * tty to sane defaults, so we don't have to modify the tty device
920 * for sane settings. We also get a SIGHUP/SIGCONT.
921 */
922 if (vhangup())
923 log_err("/dev/%s: vhangup() failed: %m", tty);
763d7a87 924 ioctl(fd, TIOCNOTTY);
3aa6b68f
WF
925 }
926
763d7a87 927 close(fd);
53d55042
SK
928 close(STDIN_FILENO);
929 errno = 0;
6dbe3af9 930
70bedfa1 931 debug("open(2)\n");
3aa6b68f
WF
932 if (open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0) != 0)
933 log_err(_("/dev/%s: cannot open as standard input: %m"), tty);
934 if (((tid = tcgetsid(STDIN_FILENO)) < 0) || (pid != tid)) {
763d7a87 935 if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) == -1)
3aa6b68f
WF
936 log_err("/dev/%s: cannot get controlling tty: %m", tty);
937 }
938
70bedfa1 939 } else {
3aa6b68f 940
70bedfa1
KZ
941 /*
942 * Standard input should already be connected to an open port. Make
943 * sure it is open for read/write.
944 */
3aa6b68f 945
70bedfa1 946 if ((fcntl(STDIN_FILENO, F_GETFL, 0) & O_RDWR) != O_RDWR)
bde20e1b 947 log_err(_("%s: not open for read/write"), tty);
3aa6b68f 948
70bedfa1
KZ
949 }
950
3aa6b68f
WF
951 if (tcsetpgrp(STDIN_FILENO, pid))
952 log_err("/dev/%s: cannot set process group: %m", tty);
953
954 /* Get rid of the present outputs. */
955 close(STDOUT_FILENO);
956 close(STDERR_FILENO);
957 errno = 0;
958
70bedfa1
KZ
959 /* Set up standard output and standard error file descriptors. */
960 debug("duping\n");
53d55042 961
70bedfa1
KZ
962 /* set up stdout and stderr */
963 if (dup(STDIN_FILENO) != 1 || dup(STDIN_FILENO) != 2)
bde20e1b 964 log_err(_("%s: dup problem: %m"), tty);
6dbe3af9 965
3aa6b68f
WF
966 /* make stdio unbuffered for slow modem lines */
967 setvbuf(stdout, NULL, _IONBF, 0);
968
6dbe3af9 969 /*
70bedfa1
KZ
970 * The following ioctl will fail if stdin is not a tty, but also when
971 * there is noise on the modem control lines. In the latter case, the
53d55042
SK
972 * common course of action is (1) fix your cables (2) give the modem
973 * more time to properly reset after hanging up.
974 *
975 * SunOS users can achieve (2) by patching the SunOS kernel variable
976 * "zsadtrlow" to a larger value; 5 seconds seems to be a good value.
977 * http://www.sunmanagers.org/archives/1993/0574.html
6dbe3af9 978 */
bde20e1b 979 memset(tp, 0, sizeof(struct termios));
70bedfa1 980 if (tcgetattr(STDIN_FILENO, tp) < 0)
bde20e1b 981 log_err("%s: tcgetattr: %m", tty);
70bedfa1
KZ
982
983 /*
3aa6b68f
WF
984 * Detect if this is a virtual console or serial/modem line.
985 * In case of a virtual console the ioctl TIOCMGET fails and
986 * the error number will be set to EINVAL.
70bedfa1 987 */
3aa6b68f
WF
988 if (ioctl(STDIN_FILENO, TIOCMGET, &serial) < 0 && (errno = EINVAL)) {
989 op->flags |= F_VCONSOLE;
990 if (!op->term)
991 op->term = DEFAULT_VCTERM;
992 } else if (!op->term)
993 op->term = DEFAULT_STERM;
729def03
WF
994
995 setenv("TERM", op->term, 1);
6dbe3af9
KZ
996}
997
53d55042 998/* Initialize termios settings. */
bde20e1b 999static void termio_init(struct options *op, struct termios *tp)
6dbe3af9 1000{
70bedfa1 1001 speed_t ispeed, ospeed;
1683f6bf 1002 struct winsize ws;
70bedfa1 1003
0da025da
WF
1004 if (op->flags & F_VCONSOLE) {
1005#if defined(IUTF8) && defined(KDGKBMODE)
1006 int mode;
1007
1008 /* Detect mode of current keyboard setup, e.g. for UTF-8 */
1009 if (ioctl(STDIN_FILENO, KDGKBMODE, &mode) < 0)
1010 mode = K_RAW;
1011 switch(mode) {
1012 case K_UNICODE:
b0198a11 1013 setlocale(LC_CTYPE, "C.UTF-8");
0da025da
WF
1014 op->flags |= F_UTF8;
1015 break;
1016 case K_RAW:
1017 case K_MEDIUMRAW:
1018 case K_XLATE:
1019 default:
1020 setlocale(LC_CTYPE, "POSIX");
1021 op->flags &= ~F_UTF8;
1022 break;
1023 }
1024#else
1025 setlocale(LC_CTYPE, "POSIX");
1026 op->flags &= ~F_UTF8;
1027#endif
1028 reset_vc(op, tp);
1029
1030 if ((tp->c_cflag & (CS8|PARODD|PARENB)) == CS8)
1031 op->flags |= F_EIGHTBITS;
1032
e85281a8
DWF
1033 if ((op->flags & F_NOCLEAR) == 0) {
1034 /*
1035 * Do not write a full reset (ESC c) because this destroys
1036 * the unicode mode again if the terminal was in unicode
1037 * mode. Also it clears the CONSOLE_MAGIC features which
1038 * are required for some languages/console-fonts.
1039 * Just put the cursor to the home position (ESC [ H),
1040 * erase everything below the cursor (ESC [ J), and set the
1041 * scrolling region to the full window (ESC [ r)
1042 */
1043 write_all(STDOUT_FILENO, "\033[r\033[H\033[J", 9);
1044 }
0da025da
WF
1045 return;
1046 }
1047
70bedfa1 1048 if (op->flags & F_KEEPSPEED) {
53d55042
SK
1049 /* Save the original setting. */
1050 ispeed = cfgetispeed(tp);
70bedfa1 1051 ospeed = cfgetospeed(tp);
1683f6bf
DWF
1052
1053 if (!ispeed) ispeed = TTYDEF_SPEED;
1054 if (!ospeed) ospeed = TTYDEF_SPEED;
1055
53d55042 1056 } else {
70bedfa1 1057 ospeed = ispeed = op->speeds[FIRST_SPEED];
53d55042 1058 }
70bedfa1
KZ
1059
1060 /*
1061 * Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
1062 * Special characters are set after we have read the login name; all
1063 * reads will be done in raw mode anyway. Errors will be dealt with
53d55042 1064 * later on.
70bedfa1 1065 */
53d55042
SK
1066
1067 /* Flush input and output queues, important for modems! */
1068 tcflush(STDIN_FILENO, TCIOFLUSH);
70bedfa1 1069
8408647d
WF
1070#ifdef IUTF8
1071 tp->c_iflag = tp->c_iflag & IUTF8;
7478fce0
ST
1072 if (tp->c_iflag & IUTF8)
1073 op->flags |= F_UTF8;
8408647d
WF
1074#else
1075 tp->c_iflag = 0;
1076#endif
1077 tp->c_lflag = tp->c_oflag = 0;
70bedfa1 1078
1683f6bf 1079 if ((op->flags & F_KEEPCFLAGS) == 0)
70bedfa1
KZ
1080 tp->c_cflag = CS8 | HUPCL | CREAD | (tp->c_cflag & CLOCAL);
1081
53d55042
SK
1082 /*
1083 * Note that the speed is stored in the c_cflag termios field, so we have
70bedfa1
KZ
1084 * set the speed always when the cflag se reseted.
1085 */
1086 cfsetispeed(tp, ispeed);
1087 cfsetospeed(tp, ospeed);
1088
53d55042 1089 if (op->flags & F_LOCAL)
70bedfa1 1090 tp->c_cflag |= CLOCAL;
30e8b186 1091#ifdef HAVE_STRUCT_TERMIOS_C_LINE
70bedfa1 1092 tp->c_line = 0;
1961482a 1093#endif
70bedfa1
KZ
1094 tp->c_cc[VMIN] = 1;
1095 tp->c_cc[VTIME] = 0;
7eda085c 1096
1683f6bf
DWF
1097 /* Check for terminal size and if not found set default */
1098 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) {
1099 int set = 0;
1100 if (ws.ws_row == 0) {
1101 ws.ws_row = 24;
1102 set++;
1103 }
1104 if (ws.ws_col == 0) {
1105 ws.ws_col = 80;
1106 set++;
1107 }
763d7a87 1108 ioctl(STDIN_FILENO, TIOCSWINSZ, &ws);
1683f6bf
DWF
1109 }
1110
53d55042 1111 /* Optionally enable hardware flow control. */
7eda085c 1112#ifdef CRTSCTS
70bedfa1
KZ
1113 if (op->flags & F_RTSCTS)
1114 tp->c_cflag |= CRTSCTS;
7eda085c
KZ
1115#endif
1116
53d55042 1117 tcsetattr(STDIN_FILENO, TCSANOW, tp);
fd6b7a7f 1118
53d55042
SK
1119 /* Go to blocking input even in local mode. */
1120 fcntl(STDIN_FILENO, F_SETFL,
1121 fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
fd6b7a7f 1122
70bedfa1 1123 debug("term_io 2\n");
6dbe3af9
KZ
1124}
1125
0da025da
WF
1126/* Reset virtual console on stdin to its defaults */
1127static void reset_vc(const struct options *op, struct termios *tp)
1128{
1129 /* Use defaults of <sys/ttydefaults.h> for base settings */
1130 tp->c_iflag |= TTYDEF_IFLAG;
1131 tp->c_oflag |= TTYDEF_OFLAG;
1132 tp->c_lflag |= TTYDEF_LFLAG;
1133
1134 if ((op->flags & F_KEEPCFLAGS) == 0) {
1135#ifdef CBAUD
1136 tp->c_lflag &= ~CBAUD;
1137#endif
1138 tp->c_cflag |= (B38400 | TTYDEF_CFLAG);
1139 }
1140
1141 /* Sane setting, allow eight bit characters, no carriage return delay
1142 * the same result as `stty sane cr0 pass8'
1143 */
1144 tp->c_iflag |= (BRKINT | ICRNL | IMAXBEL);
1145 tp->c_iflag &= ~(IGNBRK | INLCR | IGNCR | IXOFF | IUCLC | IXANY | ISTRIP);
1146 tp->c_oflag |= (OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0);
4585ec0b 1147 tp->c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | \
0da025da
WF
1148 NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
1149 tp->c_lflag |= (ISIG | ICANON | IEXTEN | ECHO|ECHOE|ECHOK|ECHOKE);
4585ec0b 1150 tp->c_lflag &= ~(ECHONL|ECHOCTL|ECHOPRT | NOFLSH | TOSTOP);
0da025da
WF
1151
1152 if ((op->flags & F_KEEPCFLAGS) == 0) {
1153 tp->c_cflag |= (CREAD | CS8 | HUPCL);
1154 tp->c_cflag &= ~(PARODD | PARENB);
1155 }
4585ec0b
ST
1156#ifdef OFDEL
1157 tp->c_oflag &= ~OFDEL;
1158#endif
1159#ifdef XCASE
1160 tp->c_lflag &= ~XCASE;
1161#endif
0da025da
WF
1162#ifdef IUTF8
1163 if (op->flags & F_UTF8)
1164 tp->c_iflag |= IUTF8; /* Set UTF-8 input flag */
1165 else
1166 tp->c_iflag &= ~IUTF8;
1167#endif
1168 /* VTIME and VMIN can overlap with VEOF and VEOL since they are
1169 * only used for non-canonical mode. We just set the at the
1170 * beginning, so nothing bad should happen.
1171 */
1172 tp->c_cc[VTIME] = 0;
1173 tp->c_cc[VMIN] = 1;
1174 tp->c_cc[VINTR] = CINTR;
1175 tp->c_cc[VQUIT] = CQUIT;
1176 tp->c_cc[VERASE] = CERASE; /* ASCII DEL (0177) */
1177 tp->c_cc[VKILL] = CKILL;
1178 tp->c_cc[VEOF] = CEOF;
1179#ifdef VSWTC
1180 tp->c_cc[VSWTC] = _POSIX_VDISABLE;
4585ec0b 1181#elif defined(VSWTCH)
0da025da
WF
1182 tp->c_cc[VSWTCH] = _POSIX_VDISABLE;
1183#endif
1184 tp->c_cc[VSTART] = CSTART;
1185 tp->c_cc[VSTOP] = CSTOP;
1186 tp->c_cc[VSUSP] = CSUSP;
1187 tp->c_cc[VEOL] = _POSIX_VDISABLE;
1188 tp->c_cc[VREPRINT] = CREPRINT;
1189 tp->c_cc[VDISCARD] = CDISCARD;
1190 tp->c_cc[VWERASE] = CWERASE;
1191 tp->c_cc[VLNEXT] = CLNEXT;
1192 tp->c_cc[VEOL2] = _POSIX_VDISABLE;
1193 if (tcsetattr(STDIN_FILENO, TCSADRAIN, tp))
1194 log_warn("tcsetattr problem: %m");
1195}
1196
53d55042 1197/* Extract baud rate from modem status message. */
bde20e1b 1198static void auto_baud(struct termios *tp)
6dbe3af9 1199{
bde20e1b 1200 speed_t speed;
53d55042 1201 int vmin;
70bedfa1 1202 unsigned iflag;
53d55042
SK
1203 char buf[BUFSIZ];
1204 char *bp;
1205 int nread;
70bedfa1
KZ
1206
1207 /*
1208 * This works only if the modem produces its status code AFTER raising
1209 * the DCD line, and if the computer is fast enough to set the proper
1210 * baud rate before the message has gone by. We expect a message of the
1211 * following format:
1212 *
1213 * <junk><number><junk>
1214 *
1215 * The number is interpreted as the baud rate of the incoming call. If the
1216 * modem does not tell us the baud rate within one second, we will keep
1217 * using the current baud rate. It is advisable to enable BREAK
1218 * processing (comma-separated list of baud rates) if the processing of
1219 * modem status messages is enabled.
1220 */
1221
1222 /*
1223 * Use 7-bit characters, don't block if input queue is empty. Errors will
53d55042 1224 * be dealt with later on.
70bedfa1 1225 */
70bedfa1 1226 iflag = tp->c_iflag;
53d55042
SK
1227 /* Enable 8th-bit stripping. */
1228 tp->c_iflag |= ISTRIP;
70bedfa1 1229 vmin = tp->c_cc[VMIN];
53d55042
SK
1230 /* Do not block when queue is empty. */
1231 tp->c_cc[VMIN] = 0;
70bedfa1
KZ
1232 tcsetattr(STDIN_FILENO, TCSANOW, tp);
1233
1234 /*
1235 * Wait for a while, then read everything the modem has said so far and
1236 * try to extract the speed of the dial-in call.
1237 */
53d55042 1238 sleep(1);
70bedfa1
KZ
1239 if ((nread = read(STDIN_FILENO, buf, sizeof(buf) - 1)) > 0) {
1240 buf[nread] = '\0';
53d55042 1241 for (bp = buf; bp < buf + nread; bp++)
70bedfa1
KZ
1242 if (isascii(*bp) && isdigit(*bp)) {
1243 if ((speed = bcode(bp))) {
1244 cfsetispeed(tp, speed);
1245 cfsetospeed(tp, speed);
1246 }
1247 break;
1248 }
6dbe3af9 1249 }
6dbe3af9 1250
53d55042 1251 /* Restore terminal settings. Errors will be dealt with later on. */
70bedfa1
KZ
1252 tp->c_iflag = iflag;
1253 tp->c_cc[VMIN] = vmin;
53d55042 1254 tcsetattr(STDIN_FILENO, TCSANOW, tp);
6dbe3af9
KZ
1255}
1256
53d55042 1257/* Show login prompt, optionally preceded by /etc/issue contents. */
bde20e1b 1258static void do_prompt(struct options *op, struct termios *tp)
6dbe3af9
KZ
1259{
1260#ifdef ISSUE
53d55042 1261 FILE *fd;
53d55042 1262#endif /* ISSUE */
6dbe3af9 1263
e85281a8
DWF
1264 if ((op->flags & F_NONL) == 0) {
1265 /* Issue not in use, start with a new line. */
1266 write_all(STDOUT_FILENO, "\r\n", 2);
1267 }
3aa6b68f 1268
53d55042 1269#ifdef ISSUE
70bedfa1 1270 if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) {
1683f6bf 1271 int c, oflag = tp->c_oflag; /* Save current setting. */
3aa6b68f 1272
1683f6bf
DWF
1273 if ((op->flags & F_VCONSOLE) == 0) {
1274 /* Map new line in output to carriage return & new line. */
1275 tp->c_oflag |= (ONLCR | OPOST);
1276 tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
1277 }
70bedfa1
KZ
1278
1279 while ((c = getc(fd)) != EOF) {
729def03
WF
1280 if (c == '\\')
1281 output_special_char(getc(fd), op, tp);
1282 else
763d7a87 1283 putchar(c);
70bedfa1
KZ
1284 }
1285 fflush(stdout);
6dbe3af9 1286
1683f6bf
DWF
1287 if ((op->flags & F_VCONSOLE) == 0) {
1288 /* Restore settings. */
1289 tp->c_oflag = oflag;
1290 /* Wait till output is gone. */
1291 tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
1292 }
53d55042 1293 fclose(fd);
6dbe3af9 1294 }
eb8e1f9f
WF
1295#endif /* ISSUE */
1296 if (op->flags & F_LOGINPAUSE) {
1297 puts("[press ENTER to login]");
1298 getc(stdin);
1299 }
1300#ifdef KDGKBLED
1301 if (op->autolog == (char*)0 && (op->flags & F_VCONSOLE)) {
a694957a
KZ
1302 int kb = 0;
1303
eb8e1f9f 1304 if (ioctl(STDIN_FILENO, KDGKBLED, &kb) == 0) {
a694957a
KZ
1305 char hint[256] = { '\0' };
1306 int nl = 0;
1307 struct stat st;
1308
1309 if (stat("/var/run/numlock-on", &st) == 0)
1310 nl = 1;
1311
1312 if (nl && (kb & 0x02) == 0)
1313 append(hint, sizeof(hint), NULL, _("Num Lock off"));
1314
1315 else if (nl == 0 && (kb & 2) && (kb & 0x20) == 0)
1316 append(hint, sizeof(hint), NULL, _("Num Lock on"));
1317
1318 if ((kb & 0x04) && (kb & 0x40) == 0)
1319 append(hint, sizeof(hint), ", ", _("Caps Lock on"));
1320
1321 if ((kb & 0x01) && (kb & 0x10) == 0)
1322 append(hint, sizeof(hint), ", ", _("Scroll Lock on"));
1323
1324 if (*hint)
1325 printf(_("Hint: %s\n\n"), hint);
eb8e1f9f
WF
1326 }
1327 }
1328#endif /* KDGKBLED */
e85281a8 1329 if ((op->flags & F_NOHOSTNAME) == 0) {
53d55042 1330 char hn[MAXHOSTNAMELEN + 1];
e85281a8
DWF
1331 if (gethostname(hn, sizeof(hn)) == 0) {
1332 struct hostent *ht;
1333 char *dot = strchr(hn, '.');
1334
1335 hn[MAXHOSTNAMELEN] = '\0';
1336 if ((op->flags & F_LONGHNAME) == 0) {
1337 if (dot)
1338 *dot = '\0';
1339 write_all(STDOUT_FILENO, hn, strlen(hn));
1340 } else if (dot == NULL && (ht = gethostbyname(hn)))
1341 write_all(STDOUT_FILENO, ht->h_name, strlen(ht->h_name));
1342 else
1343 write_all(STDOUT_FILENO, hn, strlen(hn));
1344 write_all(STDOUT_FILENO, " ", 1);
1345 }
70bedfa1 1346 }
eb8e1f9f
WF
1347 if (op->autolog == (char*)0) {
1348 /* Always show login prompt. */
1349 write_all(STDOUT_FILENO, LOGIN, sizeof(LOGIN) - 1);
1350 }
6dbe3af9
KZ
1351}
1352
53d55042 1353/* Select next baud rate. */
bde20e1b 1354static void next_speed(struct options *op, struct termios *tp)
6dbe3af9 1355{
70bedfa1
KZ
1356 static int baud_index = -1;
1357
1358 if (baud_index == -1)
1359 /*
53d55042 1360 * If the F_KEEPSPEED flags is set then the FIRST_SPEED is not
70bedfa1
KZ
1361 * tested yet (see termio_init()).
1362 */
53d55042
SK
1363 baud_index =
1364 (op->flags & F_KEEPSPEED) ? FIRST_SPEED : 1 % op->numspeed;
70bedfa1
KZ
1365 else
1366 baud_index = (baud_index + 1) % op->numspeed;
1367
1368 cfsetispeed(tp, op->speeds[baud_index]);
1369 cfsetospeed(tp, op->speeds[baud_index]);
53d55042 1370 tcsetattr(STDIN_FILENO, TCSANOW, tp);
6dbe3af9
KZ
1371}
1372
53d55042 1373/* Get user name, establish parity, speed, erase, kill & eol. */
bde20e1b 1374static char *get_logname(struct options *op, struct termios *tp, struct chardata *cp)
6dbe3af9 1375{
70bedfa1 1376 static char logname[BUFSIZ];
53d55042
SK
1377 char *bp;
1378 char c; /* input character, full eight bits */
1379 char ascval; /* low 7 bits of input character */
1683f6bf 1380 int eightbit;
53d55042
SK
1381 static char *erase[] = { /* backspace-space-backspace */
1382 "\010\040\010", /* space parity */
1383 "\010\040\010", /* odd parity */
1384 "\210\240\210", /* even parity */
1385 "\210\240\210", /* no parity */
70bedfa1
KZ
1386 };
1387
1388 /* Initialize kill, erase, parity etc. (also after switching speeds). */
70bedfa1
KZ
1389 *cp = init_chardata;
1390
53d55042
SK
1391 /*
1392 * Flush pending input (especially important after parsing or switching
1393 * the baud rate).
1394 */
1683f6bf
DWF
1395 if ((op->flags & F_VCONSOLE) == 0)
1396 sleep(1);
53d55042 1397 tcflush(STDIN_FILENO, TCIFLUSH);
70bedfa1 1398
1683f6bf
DWF
1399 eightbit = (op->flags & F_EIGHTBITS);
1400 bp = logname;
1401 *bp = '\0';
1402
1403 while (*logname == '\0') {
1404
1405 /* Write issue file and prompt */
70bedfa1
KZ
1406 do_prompt(op, tp);
1407
1683f6bf
DWF
1408 cp->eol = '\0';
1409
1410 /* Read name, watch for break and end-of-line. */
1411 while (cp->eol == '\0') {
1412
763d7a87 1413 if (read(STDIN_FILENO, &c, 1) < 1) {
1683f6bf
DWF
1414
1415 /* Do not report trivial like EINTR/EIO errors. */
1416 if (errno == EINTR || errno == EAGAIN) {
1417 usleep(1000);
1418 continue;
1419 }
1420 switch (errno) {
1421 case 0:
1422 case EIO:
1423 case ESRCH:
1424 case EINVAL:
1425 case ENOENT:
1426 break;
1427 default:
1428 log_err(_("%s: read: %m"), op->tty);
1429 }
70bedfa1 1430 }
1683f6bf 1431
70bedfa1 1432 /* Do parity bit handling. */
1683f6bf 1433 if (eightbit)
70bedfa1 1434 ascval = c;
1683f6bf
DWF
1435 else if (c != (ascval = (c & 0177))) {
1436 uint32_t bits; /* # of "1" bits per character */
1437 uint32_t mask; /* mask with 1 bit up */
1438 for (bits = 1, mask = 1; mask & 0177; mask <<= 1) {
70bedfa1 1439 if (mask & ascval)
53d55042 1440 bits++;
1683f6bf 1441 }
70bedfa1
KZ
1442 cp->parity |= ((bits & 1) ? 1 : 2);
1443 }
1683f6bf 1444
70bedfa1
KZ
1445 /* Do erase, kill and end-of-line processing. */
1446 switch (ascval) {
1683f6bf
DWF
1447 case 0:
1448 *bp = 0;
1449 if (op->numspeed > 1)
4a9b7543 1450 return NULL;
1683f6bf 1451 break;
70bedfa1
KZ
1452 case CR:
1453 case NL:
1683f6bf
DWF
1454 *bp = 0; /* terminate logname */
1455 cp->eol = ascval; /* set end-of-line char */
70bedfa1
KZ
1456 break;
1457 case BS:
1458 case DEL:
1459 case '#':
1683f6bf 1460 cp->erase = ascval; /* set erase character */
70bedfa1 1461 if (bp > logname) {
1683f6bf
DWF
1462 if ((tp->c_cflag & (ECHO)) == 0)
1463 write_all(1, erase[cp->parity], 3);
70bedfa1
KZ
1464 bp--;
1465 }
1466 break;
1467 case CTL('U'):
1468 case '@':
1683f6bf 1469 cp->kill = ascval; /* set kill character */
70bedfa1 1470 while (bp > logname) {
1683f6bf
DWF
1471 if ((tp->c_cflag & (ECHO)) == 0)
1472 write_all(1, erase[cp->parity], 3);
70bedfa1
KZ
1473 bp--;
1474 }
1475 break;
1476 case CTL('D'):
1477 exit(EXIT_SUCCESS);
1478 default:
1683f6bf
DWF
1479 if (!isascii(ascval) || !isprint(ascval))
1480 break;
1481 if ((size_t)(bp - logname) >= sizeof(logname) - 1)
bde20e1b 1482 log_err(_("%s: input overrun"), op->tty);
1683f6bf
DWF
1483 if ((tp->c_cflag & (ECHO)) == 0)
1484 write_all(1, &c, 1); /* echo the character */
1485 *bp++ = ascval; /* and store it */
70bedfa1
KZ
1486 break;
1487 }
6dbe3af9 1488 }
6dbe3af9 1489 }
1683f6bf
DWF
1490#ifdef HAVE_WIDECHAR
1491 if ((op->flags & (F_EIGHTBITS|F_UTF8)) == (F_EIGHTBITS|F_UTF8)) {
1492 /* Check out UTF-8 multibyte characters */
1493 ssize_t len;
1494 wchar_t *wcs, *wcp;
1495
1496 len = mbstowcs((wchar_t *)0, logname, 0);
1497 if (len < 0)
1498 log_err("%s: invalid character conversion for login name", op->tty);
1499
ca8e91a4
KZ
1500 wcs = (wchar_t *) malloc((len + 1) * sizeof(wchar_t));
1501 if (!wcs)
1502 log_err(_("failed to allocate memory: %m"));
1683f6bf
DWF
1503
1504 len = mbstowcs(wcs, logname, len + 1);
1505 if (len < 0)
1506 log_err("%s: invalid character conversion for login name", op->tty);
1507
1508 wcp = wcs;
1509 while (*wcp) {
1510 const wint_t wc = *wcp++;
1511 if (!iswprint(wc))
1512 log_err("%s: invalid character 0x%x in login name", op->tty, wc);
1513 }
1514 free(wcs);
1515 } else
1516#endif
1517 if ((op->flags & F_LCUC) && (cp->capslock = caps_lock(logname))) {
1518
1519 /* Handle names with upper case and no lower case. */
70bedfa1
KZ
1520 for (bp = logname; *bp; bp++)
1521 if (isupper(*bp))
1683f6bf
DWF
1522 *bp = tolower(*bp); /* map name to lower case */
1523 }
1524
70bedfa1 1525 return logname;
6dbe3af9
KZ
1526}
1527
53d55042 1528/* Set the final tty mode bits. */
bde20e1b 1529static void termio_final(struct options *op, struct termios *tp, struct chardata *cp)
6dbe3af9 1530{
70bedfa1 1531 /* General terminal-independent stuff. */
53d55042
SK
1532
1533 /* 2-way flow control */
1534 tp->c_iflag |= IXON | IXOFF;
1535 tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
1536 /* no longer| ECHOCTL | ECHOPRT */
70bedfa1
KZ
1537 tp->c_oflag |= OPOST;
1538 /* tp->c_cflag = 0; */
53d55042
SK
1539 tp->c_cc[VINTR] = DEF_INTR;
1540 tp->c_cc[VQUIT] = DEF_QUIT;
1541 tp->c_cc[VEOF] = DEF_EOF;
70bedfa1 1542 tp->c_cc[VEOL] = DEF_EOL;
fd6b7a7f 1543#ifdef __linux__
53d55042 1544 tp->c_cc[VSWTC] = DEF_SWITCH;
1961482a 1545#elif defined(VSWTCH)
53d55042
SK
1546 tp->c_cc[VSWTCH] = DEF_SWITCH;
1547#endif /* __linux__ */
6dbe3af9 1548
70bedfa1
KZ
1549 /* Account for special characters seen in input. */
1550 if (cp->eol == CR) {
53d55042
SK
1551 tp->c_iflag |= ICRNL;
1552 tp->c_oflag |= ONLCR;
70bedfa1 1553 }
53d55042
SK
1554 tp->c_cc[VERASE] = cp->erase;
1555 tp->c_cc[VKILL] = cp->kill;
70bedfa1
KZ
1556
1557 /* Account for the presence or absence of parity bits in input. */
1558 switch (cp->parity) {
53d55042
SK
1559 case 0:
1560 /* space (always 0) parity */
1561 break;
1562 case 1:
1563 /* odd parity */
1564 tp->c_cflag |= PARODD;
1565 /* do not break */
1566 case 2:
1567 /* even parity */
1568 tp->c_cflag |= PARENB;
1569 tp->c_iflag |= INPCK | ISTRIP;
1570 /* do not break */
1571 case (1 | 2):
1572 /* no parity bit */
1573 tp->c_cflag &= ~CSIZE;
1574 tp->c_cflag |= CS7;
1575 break;
70bedfa1
KZ
1576 }
1577 /* Account for upper case without lower case. */
1578 if (cp->capslock) {
ee519041 1579#ifdef IUCLC
70bedfa1 1580 tp->c_iflag |= IUCLC;
ee519041 1581#endif
b75c8134 1582#ifdef XCASE
70bedfa1 1583 tp->c_lflag |= XCASE;
1961482a 1584#endif
ee519041 1585#ifdef OLCUC
70bedfa1 1586 tp->c_oflag |= OLCUC;
ee519041 1587#endif
70bedfa1 1588 }
53d55042 1589 /* Optionally enable hardware flow control. */
6dbe3af9 1590#ifdef CRTSCTS
70bedfa1
KZ
1591 if (op->flags & F_RTSCTS)
1592 tp->c_cflag |= CRTSCTS;
6dbe3af9
KZ
1593#endif
1594
53d55042 1595 /* Finally, make the new settings effective. */
70bedfa1 1596 if (tcsetattr(STDIN_FILENO, TCSANOW, tp) < 0)
bde20e1b 1597 log_err("%s: tcsetattr: TCSANOW: %m", op->tty);
6dbe3af9
KZ
1598}
1599
53d55042
SK
1600/*
1601 * String contains upper case without lower case.
1602 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=52940
1603 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=156242
1604 */
bde20e1b 1605static int caps_lock(char *s)
6dbe3af9 1606{
53d55042 1607 int capslock;
70bedfa1
KZ
1608
1609 for (capslock = 0; *s; s++) {
1610 if (islower(*s))
1611 return EXIT_SUCCESS;
1612 if (capslock == 0)
1613 capslock = isupper(*s);
1614 }
1615 return capslock;
6dbe3af9
KZ
1616}
1617
53d55042 1618/* Convert speed string to speed code; return 0 on failure. */
bde20e1b 1619static speed_t bcode(char *s)
6dbe3af9 1620{
70bedfa1 1621 struct Speedtab *sp;
53d55042 1622 long speed = atol(s);
6dbe3af9 1623
70bedfa1
KZ
1624 for (sp = speedtab; sp->speed; sp++)
1625 if (sp->speed == speed)
1626 return sp->code;
1627 return 0;
6dbe3af9
KZ
1628}
1629
bde20e1b 1630static void __attribute__ ((__noreturn__)) usage(FILE * out)
6dbe3af9 1631{
53d55042 1632 fprintf(out, _("\nUsage:\n"
c0d38ecd
KZ
1633 " %1$s [options] line baud_rate,... [termtype]\n"
1634 " %1$s [options] baud_rate,... line [termtype]\n"),
70bedfa1
KZ
1635 program_invocation_short_name);
1636
53d55042
SK
1637 fprintf(out, _("\nOptions:\n"
1638 " -8, --8bits assume 8-bit tty\n"
c0d38ecd 1639 " -a, --autologin <user> login the specified user automatically\n"
53d55042 1640 " -c, --noreset do not reset control mode\n"
c0d38ecd 1641 " -f, --issue-file <file> display issue file\n"
53d55042 1642 " -h, --flow-control enable hardware flow control\n"
c0d38ecd 1643 " -H, --host <hostname> specify login host\n"
53d55042 1644 " -i, --noissue do not display issue file\n"
c0d38ecd
KZ
1645 " -I, --init-string <string> set init string\n"
1646 " -l, --login-program <file> specify login program\n"
53d55042
SK
1647 " -L, --local-line force local line\n"
1648 " -m, --extract-baud extract baud rate during connect\n"
1649 " -n, --skip-login do not prompt for login\n"
c0d38ecd 1650 " -o, --login-options <opts> options that are passed to login\n"
eb8e1f9f 1651 " -p, --loginpause wait for any key before the login\n"
3aa6b68f 1652 " -R, --hangup do virtually hangup on the tty\n"
53d55042 1653 " -s, --keep-baud try to keep baud rate after break\n"
c0d38ecd 1654 " -t, --timeout <number> login process timeout\n"
53d55042
SK
1655 " -U, --detect-case detect uppercase terminal\n"
1656 " -w, --wait-cr wait carriage-return\n"
e85281a8
DWF
1657 " --noclear do not clear the screen before prompt\n"
1658 " --nonewline do not print a newline before issue\n"
1659 " --no-hostname no hostname at all will be shown\n"
1660 " --long-hostname show full qualified hostname\n"
53d55042
SK
1661 " --version output version information and exit\n"
1662 " --help display this help and exit\n\n"));
70bedfa1
KZ
1663
1664 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
6dbe3af9
KZ
1665}
1666
bde20e1b
WF
1667/*
1668 * Helper function reports errors to console or syslog.
1669 * Will be used by log_err() and log_warn() therefore
1670 * it takes a format as well as va_list.
1671 */
6dbe3af9
KZ
1672#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2)
1673
bde20e1b 1674static void dolog(int priority, const char *fmt, va_list ap)
53d55042 1675{
6dbe3af9 1676#ifndef USE_SYSLOG
53d55042 1677 int fd;
6dbe3af9 1678#endif
53d55042
SK
1679 char buf[BUFSIZ];
1680 char *bp;
6dbe3af9 1681
70bedfa1
KZ
1682 /*
1683 * If the diagnostic is reported via syslog(3), the process name is
1684 * automatically prepended to the message. If we write directly to
1685 * /dev/console, we must prepend the process name ourselves.
1686 */
6dbe3af9 1687#ifdef USE_SYSLOG
70bedfa1
KZ
1688 buf[0] = '\0';
1689 bp = buf;
6dbe3af9 1690#else
53d55042 1691 str2cpy(buf, program_invocation_short_name, ": ");
70bedfa1 1692 bp = buf + strlen(buf);
53d55042 1693#endif /* USE_SYSLOG */
763d7a87 1694 vsnprintf(bp, sizeof(buf)-strlen(buf), fmt, ap);
6dbe3af9 1695
70bedfa1
KZ
1696 /*
1697 * Write the diagnostic directly to /dev/console if we do not use the
1698 * syslog(3) facility.
1699 */
6dbe3af9 1700#ifdef USE_SYSLOG
53d55042 1701 openlog(program_invocation_short_name, LOG_PID, LOG_AUTHPRIV);
bde20e1b 1702 syslog(priority, "%s", buf);
70bedfa1 1703 closelog();
6dbe3af9 1704#else
70bedfa1 1705 /* Terminate with CR-LF since the console mode is unknown. */
53d55042 1706 strcat(bp, "\r\n");
70bedfa1 1707 if ((fd = open("/dev/console", 1)) >= 0) {
729def03 1708 write_all(fd, buf, strlen(buf));
53d55042 1709 close(fd);
70bedfa1 1710 }
53d55042 1711#endif /* USE_SYSLOG */
bde20e1b
WF
1712}
1713
1714static void log_err(const char *fmt, ...)
1715{
1716 va_list ap;
1717
1718 va_start(ap, fmt);
1719 dolog(LOG_ERR, fmt, ap);
1720 va_end(ap);
1721
53d55042
SK
1722 /* Be kind to init(8). */
1723 sleep(10);
70bedfa1 1724 exit(EXIT_FAILURE);
6dbe3af9 1725}
bde20e1b
WF
1726
1727static void log_warn(const char *fmt, ...)
1728{
1729 va_list ap;
1730
1731 va_start(ap, fmt);
1732 dolog(LOG_WARNING, fmt, ap);
1733 va_end(ap);
1734}
729def03
WF
1735
1736static void output_special_char(unsigned char c, struct options *op,
1737 struct termios *tp)
1738{
1739 struct utsname uts;
763d7a87
KZ
1740
1741 uname(&uts);
3aa6b68f 1742
729def03
WF
1743 switch (c) {
1744 case 's':
763d7a87 1745 printf("%s", uts.sysname);
729def03
WF
1746 break;
1747 case 'n':
763d7a87 1748 printf("%s", uts.nodename);
729def03
WF
1749 break;
1750 case 'r':
763d7a87 1751 printf("%s", uts.release);
729def03
WF
1752 break;
1753 case 'v':
763d7a87 1754 printf("%s", uts.version);
729def03
WF
1755 break;
1756 case 'm':
763d7a87 1757 printf("%s", uts.machine);
729def03
WF
1758 break;
1759 case 'o':
1760 {
1761 char domainname[MAXHOSTNAMELEN+1];
1762#ifdef HAVE_GETDOMAINNAME
1763 if (getdomainname(domainname, sizeof(domainname)))
1764#endif
1765 strcpy(domainname, "unknown_domain");
1766 domainname[sizeof(domainname)-1] = '\0';
763d7a87 1767 printf("%s", domainname);
729def03
WF
1768 break;
1769 }
1770 case 'O':
1771 {
1772 char *dom = "unknown_domain";
1773 char host[MAXHOSTNAMELEN+1];
1774 struct addrinfo hints, *info = NULL;
1775
1776 memset(&hints, 0, sizeof(hints));
1777 hints.ai_flags = AI_CANONNAME;
1778
1779 if (gethostname(host, sizeof(host)) ||
1780 getaddrinfo(host, NULL, &hints, &info) ||
1781 info == NULL)
1782 fputs(dom, stdout);
1783 else {
1784 char *canon;
1785 if (info->ai_canonname &&
1786 (canon = strchr(info->ai_canonname, '.')))
1787 dom = canon + 1;
1788 fputs(dom, stdout);
1789 freeaddrinfo(info);
1790 }
1791 break;
1792 }
1793 case 'd':
1794 case 't':
1795 {
1796 time_t now;
1797 struct tm *tm;
1798
763d7a87 1799 time(&now);
729def03
WF
1800 tm = localtime(&now);
1801
1802 if (c == 'd') /* ISO 8601 */
763d7a87 1803 printf("%s %s %d %d",
729def03
WF
1804 nl_langinfo(ABDAY_1 + tm->tm_wday),
1805 nl_langinfo(ABMON_1 + tm->tm_mon),
1806 tm->tm_mday,
1807 tm->tm_year < 70 ? tm->tm_year + 2000 :
1808 tm->tm_year + 1900);
1809 else
763d7a87 1810 printf("%02d:%02d:%02d",
729def03
WF
1811 tm->tm_hour, tm->tm_min, tm->tm_sec);
1812 break;
1813 }
1814 case 'l':
763d7a87 1815 printf ("%s", op->tty);
729def03
WF
1816 break;
1817 case 'b':
1818 {
1819 const speed_t speed = cfgetispeed(tp);
1820 int i;
1821
1822 for (i = 0; speedtab[i].speed; i++) {
1823 if (speedtab[i].code == speed) {
1824 printf("%ld", speedtab[i].speed);
1825 break;
1826 }
1827 }
1828 break;
1829 }
1830 case 'u':
1831 case 'U':
1832 {
1833 int users = 0;
1834 struct utmp *ut;
1835 setutent();
1836 while ((ut = getutent()))
1837 if (ut->ut_type == USER_PROCESS)
1838 users++;
1839 endutent();
1840 printf ("%d ", users);
1841 if (c == 'U')
763d7a87 1842 printf((users == 1) ? _("user") : _("users"));
729def03
WF
1843 break;
1844 }
1845 default:
763d7a87 1846 putchar(c);
729def03
WF
1847 break;
1848 }
1849}
1850
1851static void init_special_char(char* arg, struct options *op)
1852{
1853 char ch, *p, *q;
1854 int i;
1855
ca8e91a4
KZ
1856 op->initstring = malloc(strlen(arg) + 1);
1857 if (!op->initstring)
1858 log_err(_("failed to allocate memory: %m"));
729def03
WF
1859
1860 /*
1861 * Copy optarg into op->initstring decoding \ddd octal
1862 * codes into chars.
1863 */
1864 q = op->initstring;
1865 p = arg;
1866 while (*p) {
1867 /* The \\ is converted to \ */
1868 if (*p == '\\') {
1869 p++;
1870 if (*p == '\\') {
1871 ch = '\\';
1872 p++;
1873 } else {
1874 /* Handle \000 - \177. */
1875 ch = 0;
1876 for (i = 1; i <= 3; i++) {
1877 if (*p >= '0' && *p <= '7') {
1878 ch <<= 3;
1879 ch += *p - '0';
1880 p++;
1881 } else {
1882 break;
1883 }
1884 }
1885 }
1886 *q++ = ch;
1887 } else
1888 *q++ = *p++;
1889 }
1890 *q = '\0';
1891}
eb8e1f9f 1892
a694957a
KZ
1893/*
1894 * Appends @str to @dest and if @dest is not empty then use use @sep as a
1895 * separator. The maximal final length of the @dest is @len.
1896 *
1897 * Returns the final @dest length or -1 in case of error.
1898 */
1899static ssize_t append(char *dest, size_t len, const char *sep, const char *src)
1900{
1901 size_t dsz = 0, ssz = 0, sz;
1902 char *p;
1903
1904 if (!dest || !len || !src)
1905 return -1;
1906
1907 if (*dest)
1908 dsz = strlen(dest);
1909 if (dsz && sep)
1910 ssz = strlen(sep);
1911 sz = strlen(src);
1912
1913 if (dsz + ssz + sz + 1 > len)
1914 return -1;
1915
1916 p = dest + dsz;
1917 if (ssz) {
1918 memcpy(p, sep, ssz);
1919 p += ssz;
1920 }
1921 memcpy(p, src, sz);
1922 *(p + sz) = '\0';
1923
1924 return dsz + ssz + sz;
1925}
9aeb66dc 1926
eb8e1f9f
WF
1927/*
1928 * Do not allow the user to pass an option as a user name
1929 * To be more safe: Use `--' to make sure the rest is
1930 * interpreted as non-options by the program, if it supports it.
1931 */
9aeb66dc 1932static void check_username(const char* nm)
eb8e1f9f
WF
1933{
1934 const char *p = nm;
1935 if (!nm)
1936 goto err;
763d7a87 1937 if (strlen(nm) > 42)
eb8e1f9f 1938 goto err;
763d7a87 1939 while (isspace(*p))
eb8e1f9f
WF
1940 p++;
1941 if (*p == '-')
1942 goto err;
1943 return;
1944err:
1945 errno = EPERM;
763d7a87 1946 log_err("checkname: %m");
eb8e1f9f
WF
1947}
1948