]> git.ipfire.org Git - thirdparty/util-linux.git/blame - term-utils/agetty.c
setterm: fix declarations shadowing variables in the global scope [oclint]
[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 */
c32270d4 13
6dbe3af9
KZ
14#include <stdio.h>
15#include <unistd.h>
16#include <stdlib.h>
2b6fc908 17#include <string.h>
1961482a 18#include <termios.h>
6dbe3af9
KZ
19#include <signal.h>
20#include <errno.h>
3aa6b68f 21#include <sys/ioctl.h>
6dbe3af9
KZ
22#include <sys/types.h>
23#include <sys/stat.h>
bb280f79 24#include <sys/wait.h>
6dbe3af9 25#include <fcntl.h>
22853e4a 26#include <stdarg.h>
6dbe3af9
KZ
27#include <ctype.h>
28#include <utmp.h>
29#include <getopt.h>
22853e4a 30#include <time.h>
6dbe3af9 31#include <sys/file.h>
e61e66bd 32#include <sys/socket.h>
729def03 33#include <langinfo.h>
3aa6b68f 34#include <grp.h>
2b945eda
KZ
35#include <arpa/inet.h>
36#include <netdb.h>
37#include <ifaddrs.h>
0f283438 38#include <net/if.h>
df73ad46 39
8abcf290 40#include "strutils.h"
e12c9866 41#include "all-io.h"
7eda085c 42#include "nls.h"
e6590f06 43#include "pathnames.h"
e5b17b31 44#include "c.h"
1683f6bf 45#include "widechar.h"
879a7ab4 46#include "ttyutils.h"
d689166b 47#include "color-names.h"
6dbe3af9 48
1eb16fd7
KZ
49#ifdef USE_PLYMOUTH_SUPPORT
50# include "plymouth-ctrl.h"
51#endif
52
4ebac79f
SK
53#ifdef HAVE_SYS_PARAM_H
54# include <sys/param.h>
55#endif
56
c32270d4
CE
57#if defined(__FreeBSD_kernel__)
58#include <pty.h>
c32270d4
CE
59#endif
60
fd6b7a7f 61#ifdef __linux__
0da025da 62# include <sys/kd.h>
53d55042 63# define USE_SYSLOG
729def03
WF
64# ifndef DEFAULT_VCTERM
65# define DEFAULT_VCTERM "linux"
66# endif
fba8a535 67# if defined (__s390__) || defined (__s390x__)
f2bcda51
WF
68# define DEFAULT_TTYS0 "dumb"
69# define DEFAULT_TTY32 "ibm327x"
fba8a535
WF
70# define DEFAULT_TTYS1 "vt220"
71# endif
729def03
WF
72# ifndef DEFAULT_STERM
73# define DEFAULT_STERM "vt102"
74# endif
4585ec0b
ST
75#elif defined(__GNU__)
76# define USE_SYSLOG
77# ifndef DEFAULT_VCTERM
78# define DEFAULT_VCTERM "hurd"
79# endif
80# ifndef DEFAULT_STERM
81# define DEFAULT_STERM "vt102"
82# endif
729def03
WF
83#else
84# ifndef DEFAULT_VCTERM
85# define DEFAULT_VCTERM "vt100"
86# endif
87# ifndef DEFAULT_STERM
88# define DEFAULT_STERM "vt100"
89# endif
6dbe3af9
KZ
90#endif
91
c32270d4
CE
92#ifdef __FreeBSD_kernel__
93#define USE_SYSLOG
94#endif
95
53d55042 96/* If USE_SYSLOG is undefined all diagnostics go to /dev/console. */
6dbe3af9 97#ifdef USE_SYSLOG
53d55042 98# include <syslog.h>
6dbe3af9
KZ
99#endif
100
b75c8134
KZ
101/*
102 * Some heuristics to find out what environment we are in: if it is not
53d55042
SK
103 * System V, assume it is SunOS 4. The LOGIN_PROCESS is defined in System V
104 * utmp.h, which will select System V style getty.
b75c8134 105 */
53d55042
SK
106#ifdef LOGIN_PROCESS
107# define SYSV_STYLE
6dbe3af9
KZ
108#endif
109
b75c8134
KZ
110/*
111 * Things you may want to modify.
112 *
113 * If ISSUE is not defined, agetty will never display the contents of the
114 * /etc/issue file. You will not want to spit out large "issue" files at the
115 * wrong baud rate. Relevant for System V only.
116 *
117 * You may disagree with the default line-editing etc. characters defined
118 * below. Note, however, that DEL cannot be used for interrupt generation
119 * and for line editing at the same time.
120 */
6dbe3af9 121
53d55042 122/* Displayed before the login prompt. */
6dbe3af9 123#ifdef SYSV_STYLE
96cc7b0b 124# define ISSUE _PATH_ISSUE
53d55042 125# include <sys/utsname.h>
6dbe3af9
KZ
126#endif
127
53d55042 128/* Login prompt. */
e85281a8 129#define LOGIN "login: "
9aeb66dc 130#define LOGIN_ARGV_MAX 16 /* Numbers of args for login */
6dbe3af9 131
6443dd43
SW
132/*
133 * agetty --reload
134 */
6443dd43
SW
135#ifdef AGETTY_RELOAD
136# include <sys/inotify.h>
e36deb64
SW
137# include <linux/netlink.h>
138# include <linux/rtnetlink.h>
6443dd43
SW
139# define AGETTY_RELOAD_FILENAME "/run/agetty.reload" /* trigger file */
140# define AGETTY_RELOAD_FDNONE -2 /* uninitialized fd */
141static int inotify_fd = AGETTY_RELOAD_FDNONE;
e36deb64 142static int netlink_fd = AGETTY_RELOAD_FDNONE;
6443dd43
SW
143#endif
144
b75c8134
KZ
145/*
146 * When multiple baud rates are specified on the command line, the first one
147 * we will try is the first one specified.
148 */
6dbe3af9
KZ
149#define FIRST_SPEED 0
150
151/* Storage for command-line options. */
53d55042 152#define MAX_SPEED 10 /* max. nr. of baud rates */
6dbe3af9
KZ
153
154struct options {
bde20e1b 155 int flags; /* toggle switches, see below */
cf582859 156 unsigned int timeout; /* time-out period */
eb8e1f9f 157 char *autolog; /* login the user automatically */
e85281a8
DWF
158 char *chdir; /* Chdir before the login */
159 char *chroot; /* Chroot before the login */
bde20e1b 160 char *login; /* login program */
eb8e1f9f 161 char *logopt; /* options for login program */
bde20e1b 162 char *tty; /* name of tty */
729def03
WF
163 char *vcline; /* line of virtual console */
164 char *term; /* terminal type */
bde20e1b
WF
165 char *initstring; /* modem init string */
166 char *issue; /* alternative issue file */
cb872ac9
KZ
167 char *erasechars; /* string with erase chars */
168 char *killchars; /* string with kill chars */
b34f097e 169 char *osrelease; /* /etc/os-release data */
cf582859 170 unsigned int delay; /* Sleep seconds before prompt */
e85281a8 171 int nice; /* Run login with this priority */
bde20e1b 172 int numspeed; /* number of baud rates to try */
ef264c83 173 int clocal; /* CLOCAL_MODE_* */
b9c73909 174 int kbmode; /* Keyboard mode if virtual console */
bde20e1b 175 speed_t speeds[MAX_SPEED]; /* baud rates to be tried */
6dbe3af9
KZ
176};
177
ef264c83
KZ
178enum {
179 CLOCAL_MODE_AUTO = 0,
180 CLOCAL_MODE_ALWAYS,
181 CLOCAL_MODE_NEVER
182};
183
53d55042
SK
184#define F_PARSE (1<<0) /* process modem status messages */
185#define F_ISSUE (1<<1) /* display /etc/issue */
186#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */
ef264c83 187
53d55042
SK
188#define F_INITSTRING (1<<4) /* initstring is set */
189#define F_WAITCRLF (1<<5) /* wait for CR or LF */
190#define F_CUSTISSUE (1<<6) /* give alternative issue file */
191#define F_NOPROMPT (1<<7) /* do not ask for login name! */
192#define F_LCUC (1<<8) /* support for *LCUC stty modes */
193#define F_KEEPSPEED (1<<9) /* follow baud rate from kernel */
194#define F_KEEPCFLAGS (1<<10) /* reuse c_cflags setup from kernel */
bde20e1b 195#define F_EIGHTBITS (1<<11) /* Assume 8bit-clean tty */
3aa6b68f
WF
196#define F_VCONSOLE (1<<12) /* This is a virtual console */
197#define F_HANGUP (1<<13) /* Do call vhangup(2) */
0da025da 198#define F_UTF8 (1<<14) /* We can do UTF8 */
eb8e1f9f 199#define F_LOGINPAUSE (1<<15) /* Wait for any key before dropping login prompt */
e85281a8
DWF
200#define F_NOCLEAR (1<<16) /* Do not clear the screen before prompting */
201#define F_NONL (1<<17) /* No newline before issue */
202#define F_NOHOSTNAME (1<<18) /* Do not show the hostname */
203#define F_LONGHNAME (1<<19) /* Show Full qualified hostname */
36601b23 204#define F_NOHINTS (1<<20) /* Don't print hints */
01095ae3 205#define F_REMOTE (1<<21) /* Add '-h fakehost' to login(1) command line */
0da025da
WF
206
207#define serial_tty_option(opt, flag) \
208 (((opt)->flags & (F_VCONSOLE|(flag))) == (flag))
6dbe3af9 209
fd6b7a7f 210struct Speedtab {
53d55042 211 long speed;
bde20e1b 212 speed_t code;
fd6b7a7f
KZ
213};
214
1a204cd2 215static const struct Speedtab speedtab[] = {
53d55042
SK
216 {50, B50},
217 {75, B75},
218 {110, B110},
219 {134, B134},
220 {150, B150},
221 {200, B200},
222 {300, B300},
223 {600, B600},
224 {1200, B1200},
225 {1800, B1800},
226 {2400, B2400},
227 {4800, B4800},
228 {9600, B9600},
09625c2e 229#ifdef B19200
53d55042 230 {19200, B19200},
09625c2e 231#elif defined(EXTA)
53d55042 232 {19200, EXTA},
fd6b7a7f 233#endif
09625c2e
JB
234#ifdef B38400
235 {38400, B38400},
236#elif defined(EXTB)
53d55042 237 {38400, EXTB},
fd6b7a7f
KZ
238#endif
239#ifdef B57600
53d55042 240 {57600, B57600},
fd6b7a7f
KZ
241#endif
242#ifdef B115200
53d55042 243 {115200, B115200},
fd6b7a7f
KZ
244#endif
245#ifdef B230400
53d55042 246 {230400, B230400},
f28dde8e
JB
247#endif
248#ifdef B460800
249 {460800, B460800},
250#endif
251#ifdef B500000
252 {500000, B500000},
253#endif
254#ifdef B576000
255 {576000, B576000},
256#endif
257#ifdef B921600
258 {921600, B921600},
259#endif
260#ifdef B1000000
261 {1000000, B1000000},
262#endif
263#ifdef B1152000
264 {1152000, B1152000},
265#endif
266#ifdef B1500000
267 {1500000, B1500000},
268#endif
269#ifdef B2000000
270 {2000000, B2000000},
271#endif
272#ifdef B2500000
273 {2500000, B2500000},
274#endif
275#ifdef B3000000
276 {3000000, B3000000},
277#endif
278#ifdef B3500000
279 {3500000, B3500000},
280#endif
281#ifdef B4000000
282 {4000000, B4000000},
fd6b7a7f 283#endif
53d55042 284 {0, 0},
6dbe3af9
KZ
285};
286
729def03 287static void init_special_char(char* arg, struct options *op);
53d55042
SK
288static void parse_args(int argc, char **argv, struct options *op);
289static void parse_speeds(struct options *op, char *arg);
729def03
WF
290static void update_utmp(struct options *op);
291static void open_tty(char *tty, struct termios *tp, struct options *op);
53d55042 292static void termio_init(struct options *op, struct termios *tp);
0da025da 293static void reset_vc (const struct options *op, struct termios *tp);
53d55042 294static void auto_baud(struct termios *tp);
2b945eda
KZ
295static void output_special_char (unsigned char c, struct options *op,
296 struct termios *tp, FILE *fp);
53d55042
SK
297static void do_prompt(struct options *op, struct termios *tp);
298static void next_speed(struct options *op, struct termios *tp);
299static char *get_logname(struct options *op,
300 struct termios *tp, struct chardata *cp);
301static void termio_final(struct options *op,
302 struct termios *tp, struct chardata *cp);
303static int caps_lock(char *s);
bde20e1b
WF
304static speed_t bcode(char *s);
305static void usage(FILE * out) __attribute__((__noreturn__));
763d7a87
KZ
306static void log_err(const char *, ...) __attribute__((__noreturn__))
307 __attribute__((__format__(printf, 1, 2)));
308static void log_warn (const char *, ...)
309 __attribute__((__format__(printf, 1, 2)));
a694957a 310static ssize_t append(char *dest, size_t len, const char *sep, const char *src);
9aeb66dc
KZ
311static void check_username (const char* nm);
312static void login_options_to_argv(char *argv[], int *argc, char *str, char *username);
6443dd43 313static void reload_agettys(void);
6dbe3af9 314
eb63b9b8 315/* Fake hostname for ut_host specified on command line. */
bde20e1b 316static char *fakehost;
eb63b9b8 317
6dbe3af9 318#ifdef DEBUGGING
bb280f79 319# include "closestream.h"
46568b00 320# ifndef DEBUG_OUTPUT
bb280f79 321# define DEBUG_OUTPUT "/dev/tty10"
46568b00
KZ
322# endif
323# define debug(s) do { fprintf(dbf,s); fflush(dbf); } while (0)
6dbe3af9
KZ
324FILE *dbf;
325#else
46568b00 326# define debug(s) do { ; } while (0)
efcf26f4 327#endif
6dbe3af9 328
53d55042 329int main(int argc, char **argv)
6dbe3af9 330{
9aeb66dc 331 char *username = NULL; /* login name, given to /bin/login */
eb8e1f9f
WF
332 struct chardata chardata; /* will be set by get_logname() */
333 struct termios termios; /* terminal mode bits */
1a204cd2 334 struct options options = {
eb8e1f9f
WF
335 .flags = F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
336 .login = _PATH_LOGIN, /* default login program */
eb8e1f9f 337 .tty = "tty1", /* default tty line */
5676f365 338 .issue = ISSUE /* default issue file */
70bedfa1 339 };
9aeb66dc
KZ
340 char *login_argv[LOGIN_ARGV_MAX + 1];
341 int login_argc = 0;
3aa6b68f
WF
342 struct sigaction sa, sa_hup, sa_quit, sa_int;
343 sigset_t set;
70bedfa1
KZ
344
345 setlocale(LC_ALL, "");
346 bindtextdomain(PACKAGE, LOCALEDIR);
347 textdomain(PACKAGE);
b75c8134 348
3aa6b68f
WF
349 /* In case vhangup(2) has to called */
350 sa.sa_handler = SIG_IGN;
351 sa.sa_flags = SA_RESTART;
352 sigemptyset (&sa.sa_mask);
353 sigaction(SIGHUP, &sa, &sa_hup);
354 sigaction(SIGQUIT, &sa, &sa_quit);
355 sigaction(SIGINT, &sa, &sa_int);
356
6dbe3af9 357#ifdef DEBUGGING
1aba8336 358 dbf = fopen(DEBUG_OUTPUT, "w");
bb280f79
WF
359 for (int i = 1; i < argc; i++) {
360 if (i > 1)
361 debug(" ");
53d55042 362 debug(argv[i]);
bb280f79
WF
363 }
364 debug("\n");
53d55042 365#endif /* DEBUGGING */
6dbe3af9 366
70bedfa1 367 /* Parse command-line arguments. */
70bedfa1 368 parse_args(argc, argv, &options);
6dbe3af9 369
9aeb66dc
KZ
370 login_argv[login_argc++] = options.login; /* set login program name */
371
70bedfa1 372 /* Update the utmp file. */
6dbe3af9 373#ifdef SYSV_STYLE
729def03 374 update_utmp(&options);
6dbe3af9 375#endif
e85281a8
DWF
376 if (options.delay)
377 sleep(options.delay);
378
70bedfa1 379 debug("calling open_tty\n");
53d55042 380
70bedfa1 381 /* Open the tty as standard { input, output, error }. */
729def03 382 open_tty(options.tty, &termios, &options);
70bedfa1 383
3aa6b68f
WF
384 /* Unmask SIGHUP if inherited */
385 sigemptyset(&set);
386 sigaddset(&set, SIGHUP);
387 sigprocmask(SIG_UNBLOCK, &set, NULL);
388 sigaction(SIGHUP, &sa_hup, NULL);
389
70bedfa1 390 tcsetpgrp(STDIN_FILENO, getpid());
8410cdd3 391
9e930041 392 /* Default is to follow the current line speed and then default to 9600 */
914047b4 393 if ((options.flags & F_VCONSOLE) == 0 && options.numspeed == 0) {
8410cdd3 394 options.speeds[options.numspeed++] = bcode("9600");
3c5ee57c 395 options.flags |= F_KEEPSPEED;
914047b4 396 }
8410cdd3 397
70bedfa1
KZ
398 /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
399 debug("calling termio_init\n");
53d55042 400 termio_init(&options, &termios);
70bedfa1 401
53d55042 402 /* Write the modem init string and DO NOT flush the buffers. */
eb8e1f9f
WF
403 if (serial_tty_option(&options, F_INITSTRING) &&
404 options.initstring && *options.initstring != '\0') {
70bedfa1 405 debug("writing init string\n");
729def03 406 write_all(STDOUT_FILENO, options.initstring,
3aa6b68f 407 strlen(options.initstring));
70bedfa1
KZ
408 }
409
714cff30
KZ
410 if (options.flags & F_VCONSOLE || options.clocal != CLOCAL_MODE_ALWAYS)
411 /* Go to blocking mode unless -L is specified, this change
412 * affects stdout, stdin and stderr as all the file descriptors
413 * are created by dup(). */
70bedfa1 414 fcntl(STDOUT_FILENO, F_SETFL,
53d55042 415 fcntl(STDOUT_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
70bedfa1
KZ
416
417 /* Optionally detect the baud rate from the modem status message. */
418 debug("before autobaud\n");
0da025da 419 if (serial_tty_option(&options, F_PARSE))
70bedfa1
KZ
420 auto_baud(&termios);
421
422 /* Set the optional timer. */
423 if (options.timeout)
cf582859 424 alarm(options.timeout);
70bedfa1 425
53d55042 426 /* Optionally wait for CR or LF before writing /etc/issue */
0da025da 427 if (serial_tty_option(&options, F_WAITCRLF)) {
70bedfa1
KZ
428 char ch;
429
430 debug("waiting for cr-lf\n");
53d55042
SK
431 while (read(STDIN_FILENO, &ch, 1) == 1) {
432 /* Strip "parity bit". */
433 ch &= 0x7f;
fd6b7a7f 434#ifdef DEBUGGING
70bedfa1 435 fprintf(dbf, "read %c\n", ch);
fd6b7a7f 436#endif
53d55042
SK
437 if (ch == '\n' || ch == '\r')
438 break;
70bedfa1 439 }
726f69e2 440 }
726f69e2 441
f5664477 442 INIT_CHARDATA(&chardata);
933956cb
KZ
443
444 if (options.autolog) {
445 debug("doing auto login\n");
446 username = options.autolog;
447 }
448
1683f6bf 449 if ((options.flags & F_NOPROMPT) == 0) {
eb8e1f9f 450 if (options.autolog) {
933956cb 451 /* Autologin prompt */
eb8e1f9f 452 do_prompt(&options, &termios);
8c219bf4 453 printf(_("%s%s (automatic login)\n"), LOGIN, options.autolog);
eb8e1f9f
WF
454 } else {
455 /* Read the login name. */
456 debug("reading login name\n");
9aeb66dc 457 while ((username =
f8bd089b 458 get_logname(&options, &termios, &chardata)) == NULL)
8410cdd3 459 if ((options.flags & F_VCONSOLE) == 0 && options.numspeed)
eb8e1f9f
WF
460 next_speed(&options, &termios);
461 }
70bedfa1 462 }
6dbe3af9 463
70bedfa1 464 /* Disable timer. */
70bedfa1 465 if (options.timeout)
53d55042 466 alarm(0);
6dbe3af9 467
0da025da
WF
468 if ((options.flags & F_VCONSOLE) == 0) {
469 /* Finalize the termios settings. */
470 termio_final(&options, &termios, &chardata);
6dbe3af9 471
0da025da
WF
472 /* Now the newline character should be properly written. */
473 write_all(STDOUT_FILENO, "\r\n", 2);
474 }
6dbe3af9 475
763d7a87
KZ
476 sigaction(SIGQUIT, &sa_quit, NULL);
477 sigaction(SIGINT, &sa_int, NULL);
3aa6b68f 478
9aeb66dc
KZ
479 if (username)
480 check_username(username);
481
482 if (options.logopt) {
483 /*
484 * The --login-options completely overwrites the default
485 * way how agetty composes login(1) command line.
486 */
487 login_options_to_argv(login_argv, &login_argc,
488 options.logopt, username);
01095ae3
KZ
489 } else {
490 if (fakehost && (options.flags & F_REMOTE)) {
491 login_argv[login_argc++] = "-h";
492 login_argv[login_argc++] = fakehost;
493 }
494 if (username) {
495 if (options.autolog)
496 login_argv[login_argc++] = "-f";
497 else
498 login_argv[login_argc++] = "--";
499 login_argv[login_argc++] = username;
500 }
9aeb66dc 501 }
eb8e1f9f 502
9aeb66dc 503 login_argv[login_argc] = NULL; /* last login argv */
eb8e1f9f 504
74ce680a
SK
505 if (options.chroot && chroot(options.chroot) < 0)
506 log_err(_("%s: can't change root directory %s: %m"),
507 options.tty, options.chroot);
508 if (options.chdir && chdir(options.chdir) < 0)
509 log_err(_("%s: can't change working directory %s: %m"),
510 options.tty, options.chdir);
511 if (options.nice && nice(options.nice) < 0)
512 log_warn(_("%s: can't change process priority: %m"),
513 options.tty);
514
f614b73c 515 free(options.osrelease);
1aba8336 516#ifdef DEBUGGING
1aba8336
SK
517 if (close_stream(dbf) != 0)
518 log_err("write failed: %s", DEBUG_OUTPUT);
519#endif
520
70bedfa1 521 /* Let the login program take care of password validation. */
9aeb66dc
KZ
522 execv(options.login, login_argv);
523 log_err(_("%s: can't exec %s: %m"), options.tty, login_argv[0]);
524}
525
526/*
527 * Returns : @str if \u not found
528 * : @username if @str equal to "\u"
529 * : newly allocated string if \u mixed with something other
530 */
531static char *replace_u(char *str, char *username)
532{
533 char *entry = NULL, *p = str;
534 size_t usz = username ? strlen(username) : 0;
535
536 while (*p) {
537 size_t sz;
538 char *tp, *old = entry;
539
540 if (memcmp(p, "\\u", 2)) {
541 p++;
542 continue; /* no \u */
543 }
544 sz = strlen(str);
545
3797cf25 546 if (p == str && sz == 2) {
9aeb66dc 547 /* 'str' contains only '\u' */
3797cf25 548 free(old);
9aeb66dc 549 return username;
3797cf25 550 }
9aeb66dc
KZ
551
552 tp = entry = malloc(sz + usz);
553 if (!tp)
554 log_err(_("failed to allocate memory: %m"));
555
556 if (p != str) {
9e930041 557 /* copy chars before \u */
9aeb66dc
KZ
558 memcpy(tp, str, p - str);
559 tp += p - str;
560 }
561 if (usz) {
562 /* copy username */
563 memcpy(tp, username, usz);
564 tp += usz;
565 }
566 if (*(p + 2))
567 /* copy chars after \u + \0 */
568 memcpy(tp, p + 2, sz - (p - str) - 1);
569 else
570 *tp = '\0';
571
572 p = tp;
573 str = entry;
574 free(old);
575 }
576
577 return entry ? entry : str;
578}
579
580static void login_options_to_argv(char *argv[], int *argc,
581 char *str, char *username)
582{
583 char *p;
584 int i = *argc;
585
586 while (str && isspace(*str))
587 str++;
588 p = str;
589
590 while (p && *p && i < LOGIN_ARGV_MAX) {
591 if (isspace(*p)) {
592 *p = '\0';
593 while (isspace(*++p))
594 ;
595 if (*p) {
596 argv[i++] = replace_u(str, username);
597 str = p;
598 }
599 } else
600 p++;
601 }
602 if (str && *str && i < LOGIN_ARGV_MAX)
603 argv[i++] = replace_u(str, username);
604 *argc = i;
6dbe3af9
KZ
605}
606
f2bcda51
WF
607#define is_speed(str) (strlen((str)) == strspn((str), "0123456789,"))
608
53d55042 609/* Parse command-line arguments. */
bde20e1b 610static void parse_args(int argc, char **argv, struct options *op)
6dbe3af9 611{
53d55042 612 int c;
70bedfa1
KZ
613
614 enum {
615 VERSION_OPTION = CHAR_MAX + 1,
36601b23 616 NOHINTS_OPTION,
e85281a8
DWF
617 NOHOSTNAME_OPTION,
618 LONGHOSTNAME_OPTION,
cb872ac9
KZ
619 HELP_OPTION,
620 ERASE_CHARS_OPTION,
621 KILL_CHARS_OPTION,
6443dd43 622 RELOAD_OPTION,
70bedfa1 623 };
3aa6b68f 624 const struct option longopts[] = {
70bedfa1 625 { "8bits", no_argument, 0, '8' },
eb8e1f9f 626 { "autologin", required_argument, 0, 'a' },
70bedfa1 627 { "noreset", no_argument, 0, 'c' },
e85281a8
DWF
628 { "chdir", required_argument, 0, 'C' },
629 { "delay", required_argument, 0, 'd' },
01095ae3 630 { "remote", no_argument, 0, 'E' },
70bedfa1
KZ
631 { "issue-file", required_argument, 0, 'f' },
632 { "flow-control", no_argument, 0, 'h' },
633 { "host", required_argument, 0, 'H' },
634 { "noissue", no_argument, 0, 'i' },
635 { "init-string", required_argument, 0, 'I' },
e85281a8 636 { "noclear", no_argument, 0, 'J' },
70bedfa1 637 { "login-program", required_argument, 0, 'l' },
ef264c83 638 { "local-line", optional_argument, 0, 'L' },
70bedfa1
KZ
639 { "extract-baud", no_argument, 0, 'm' },
640 { "skip-login", no_argument, 0, 'n' },
e85281a8 641 { "nonewline", no_argument, 0, 'N' },
eb8e1f9f 642 { "login-options", required_argument, 0, 'o' },
3fc62fd3 643 { "login-pause", no_argument, 0, 'p' },
e85281a8
DWF
644 { "nice", required_argument, 0, 'P' },
645 { "chroot", required_argument, 0, 'r' },
3aa6b68f 646 { "hangup", no_argument, 0, 'R' },
70bedfa1
KZ
647 { "keep-baud", no_argument, 0, 's' },
648 { "timeout", required_argument, 0, 't' },
649 { "detect-case", no_argument, 0, 'U' },
650 { "wait-cr", no_argument, 0, 'w' },
36601b23 651 { "nohints", no_argument, 0, NOHINTS_OPTION },
3fc62fd3 652 { "nohostname", no_argument, 0, NOHOSTNAME_OPTION },
e85281a8 653 { "long-hostname", no_argument, 0, LONGHOSTNAME_OPTION },
6443dd43 654 { "reload", no_argument, 0, RELOAD_OPTION },
70bedfa1
KZ
655 { "version", no_argument, 0, VERSION_OPTION },
656 { "help", no_argument, 0, HELP_OPTION },
cb872ac9
KZ
657 { "erase-chars", required_argument, 0, ERASE_CHARS_OPTION },
658 { "kill-chars", required_argument, 0, KILL_CHARS_OPTION },
70bedfa1
KZ
659 { NULL, 0, 0, 0 }
660 };
661
763d7a87 662 while ((c = getopt_long(argc, argv,
6f964206 663 "8a:cC:d:Ef:hH:iI:Jl:L::mnNo:pP:r:Rst:Uw", longopts,
53d55042 664 NULL)) != -1) {
70bedfa1
KZ
665 switch (c) {
666 case '8':
bde20e1b 667 op->flags |= F_EIGHTBITS;
70bedfa1 668 break;
eb8e1f9f
WF
669 case 'a':
670 op->autolog = optarg;
671 break;
70bedfa1
KZ
672 case 'c':
673 op->flags |= F_KEEPCFLAGS;
674 break;
e85281a8
DWF
675 case 'C':
676 op->chdir = optarg;
677 break;
678 case 'd':
cf582859 679 op->delay = strtou32_or_err(optarg, _("invalid delay argument"));
e85281a8 680 break;
01095ae3
KZ
681 case 'E':
682 op->flags |= F_REMOTE;
683 break;
53d55042 684 case 'f':
70bedfa1
KZ
685 op->flags |= F_CUSTISSUE;
686 op->issue = optarg;
687 break;
53d55042 688 case 'h':
70bedfa1
KZ
689 op->flags |= F_RTSCTS;
690 break;
53d55042 691 case 'H':
70bedfa1
KZ
692 fakehost = optarg;
693 break;
53d55042 694 case 'i':
70bedfa1
KZ
695 op->flags &= ~F_ISSUE;
696 break;
697 case 'I':
729def03
WF
698 init_special_char(optarg, op);
699 op->flags |= F_INITSTRING;
700 break;
e85281a8
DWF
701 case 'J':
702 op->flags |= F_NOCLEAR;
703 break;
70bedfa1 704 case 'l':
53d55042 705 op->login = optarg;
70bedfa1 706 break;
53d55042 707 case 'L':
ef264c83
KZ
708 /* -L and -L=always have the same meaning */
709 op->clocal = CLOCAL_MODE_ALWAYS;
710 if (optarg) {
711 if (strcmp(optarg, "=always") == 0)
712 op->clocal = CLOCAL_MODE_ALWAYS;
713 else if (strcmp(optarg, "=never") == 0)
714 op->clocal = CLOCAL_MODE_NEVER;
715 else if (strcmp(optarg, "=auto") == 0)
716 op->clocal = CLOCAL_MODE_AUTO;
717 else
38ae77d7 718 log_err(_("invalid argument of --local-line"));
ef264c83 719 }
70bedfa1 720 break;
53d55042 721 case 'm':
70bedfa1
KZ
722 op->flags |= F_PARSE;
723 break;
724 case 'n':
725 op->flags |= F_NOPROMPT;
726 break;
6c77c7fa
BR
727 case 'N':
728 op->flags |= F_NONL;
729 break;
eb8e1f9f
WF
730 case 'o':
731 op->logopt = optarg;
732 break;
733 case 'p':
734 op->flags |= F_LOGINPAUSE;
735 break;
e85281a8 736 case 'P':
cf582859 737 op->nice = strtos32_or_err(optarg, _("invalid nice argument"));
e85281a8
DWF
738 break;
739 case 'r':
740 op->chroot = optarg;
741 break;
3aa6b68f
WF
742 case 'R':
743 op->flags |= F_HANGUP;
744 break;
70bedfa1 745 case 's':
53d55042 746 op->flags |= F_KEEPSPEED;
70bedfa1 747 break;
53d55042 748 case 't':
cf582859 749 op->timeout = strtou32_or_err(optarg, _("invalid timeout argument"));
70bedfa1
KZ
750 break;
751 case 'U':
752 op->flags |= F_LCUC;
753 break;
754 case 'w':
755 op->flags |= F_WAITCRLF;
756 break;
36601b23
KZ
757 case NOHINTS_OPTION:
758 op->flags |= F_NOHINTS;
759 break;
e85281a8
DWF
760 case NOHOSTNAME_OPTION:
761 op->flags |= F_NOHOSTNAME;
762 break;
763 case LONGHOSTNAME_OPTION:
764 op->flags |= F_LONGHNAME;
765 break;
cb872ac9
KZ
766 case ERASE_CHARS_OPTION:
767 op->erasechars = optarg;
768 break;
769 case KILL_CHARS_OPTION:
770 op->killchars = optarg;
771 break;
6443dd43
SW
772 case RELOAD_OPTION:
773 reload_agettys();
774 exit(EXIT_SUCCESS);
70bedfa1 775 case VERSION_OPTION:
f6277500 776 printf(UTIL_LINUX_VERSION);
70bedfa1
KZ
777 exit(EXIT_SUCCESS);
778 case HELP_OPTION:
779 usage(stdout);
780 default:
781 usage(stderr);
726f69e2 782 }
6dbe3af9 783 }
70bedfa1
KZ
784
785 debug("after getopt loop\n");
786
729def03 787 if (argc < optind + 1) {
bde20e1b 788 log_warn(_("not enough arguments"));
70bedfa1
KZ
789 usage(stderr);
790 }
791
729def03 792 /* Accept "tty", "baudrate tty", and "tty baudrate". */
f2bcda51 793 if (is_speed(argv[optind])) {
53d55042
SK
794 /* Assume BSD style speed. */
795 parse_speeds(op, argv[optind++]);
729def03
WF
796 if (argc < optind + 1) {
797 warn(_("not enough arguments"));
798 usage(stderr);
799 }
800 op->tty = argv[optind++];
70bedfa1 801 } else {
53d55042 802 op->tty = argv[optind++];
729def03 803 if (argc > optind) {
14da9b1f
KZ
804 char *v = argv[optind];
805 if (is_speed(v)) {
729def03 806 parse_speeds(op, v);
14da9b1f
KZ
807 optind++;
808 }
729def03 809 }
70bedfa1
KZ
810 }
811
729def03
WF
812 /* On virtual console remember the line which is used for */
813 if (strncmp(op->tty, "tty", 3) == 0 &&
814 strspn(op->tty + 3, "0123456789") == strlen(op->tty+3))
815 op->vcline = op->tty+3;
816
70bedfa1 817 if (argc > optind && argv[optind])
729def03 818 op->term = argv[optind];
6dbe3af9 819
66ee8158 820#ifdef DO_DEVFS_FIDDLING
70bedfa1 821 /*
53d55042 822 * Some devfs junk, following Goswin Brederlow:
70bedfa1
KZ
823 * turn ttyS<n> into tts/<n>
824 * turn tty<n> into vc/<n>
53d55042 825 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=72241
70bedfa1
KZ
826 */
827 if (op->tty && strlen(op->tty) < 90) {
828 char dev_name[100];
829 struct stat st;
830
831 if (strncmp(op->tty, "ttyS", 4) == 0) {
832 strcpy(dev_name, "/dev/");
833 strcat(dev_name, op->tty);
834 if (stat(dev_name, &st) < 0) {
835 strcpy(dev_name, "/dev/tts/");
836 strcat(dev_name, op->tty + 4);
ca8e91a4
KZ
837 if (stat(dev_name, &st) == 0) {
838 op->tty = strdup(dev_name + 5);
839 if (!op->tty)
840 log_err(_("failed to allocate memory: %m"));
841 }
70bedfa1
KZ
842 }
843 } else if (strncmp(op->tty, "tty", 3) == 0) {
844 strcpy(dev_name, "/dev/");
845 strncat(dev_name, op->tty, 90);
846 if (stat(dev_name, &st) < 0) {
847 strcpy(dev_name, "/dev/vc/");
848 strcat(dev_name, op->tty + 3);
ca8e91a4
KZ
849 if (stat(dev_name, &st) == 0) {
850 op->tty = strdup(dev_name + 5);
851 if (!op->tty)
852 log_err(_("failed to allocate memory: %m"));
853 }
70bedfa1
KZ
854 }
855 }
856 }
53d55042 857#endif /* DO_DEVFS_FIDDLING */
66ee8158 858
70bedfa1 859 debug("exiting parseargs\n");
6dbe3af9
KZ
860}
861
53d55042 862/* Parse alternate baud rates. */
bde20e1b 863static void parse_speeds(struct options *op, char *arg)
6dbe3af9 864{
53d55042 865 char *cp;
3383b2f5 866 char *str = strdup(arg);
6dbe3af9 867
3383b2f5
KZ
868 if (!str)
869 log_err(_("failed to allocate memory: %m"));
870
871 debug("entered parse_speeds:\n");
872 for (cp = strtok(str, ","); cp != NULL; cp = strtok((char *)0, ",")) {
70bedfa1 873 if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
bde20e1b 874 log_err(_("bad speed: %s"), cp);
70bedfa1 875 if (op->numspeed >= MAX_SPEED)
bde20e1b 876 log_err(_("too many alternate speeds"));
70bedfa1
KZ
877 }
878 debug("exiting parsespeeds\n");
3383b2f5 879 free(str);
6dbe3af9
KZ
880}
881
882#ifdef SYSV_STYLE
883
53d55042 884/* Update our utmp entry. */
729def03 885static void update_utmp(struct options *op)
6dbe3af9 886{
53d55042
SK
887 struct utmp ut;
888 time_t t;
729def03
WF
889 pid_t pid = getpid();
890 pid_t sid = getsid(0);
891 char *vcline = op->vcline;
892 char *line = op->tty;
53d55042 893 struct utmp *utp;
70bedfa1
KZ
894
895 /*
896 * The utmp file holds miscellaneous information about things started by
897 * /sbin/init and other system-related events. Our purpose is to update
898 * the utmp entry for the current process, in particular the process type
899 * and the tty line we are listening to. Return successfully only if the
900 * utmp file can be opened for update, and if we are able to find our
901 * entry in the utmp file.
902 */
70bedfa1
KZ
903 utmpname(_PATH_UTMP);
904 setutent();
905
53d55042 906 /*
729def03 907 * Find my pid in utmp.
53d55042
SK
908 *
909 * FIXME: Earlier (when was that?) code here tested only utp->ut_type !=
910 * INIT_PROCESS, so maybe the >= here should be >.
911 *
912 * FIXME: The present code is taken from login.c, so if this is changed,
913 * maybe login has to be changed as well (is this true?).
914 */
70bedfa1 915 while ((utp = getutent()))
729def03 916 if (utp->ut_pid == pid
70bedfa1
KZ
917 && utp->ut_type >= INIT_PROCESS
918 && utp->ut_type <= DEAD_PROCESS)
919 break;
920
921 if (utp) {
922 memcpy(&ut, utp, sizeof(ut));
923 } else {
53d55042 924 /* Some inits do not initialize utmp. */
70bedfa1 925 memset(&ut, 0, sizeof(ut));
729def03
WF
926 if (vcline && *vcline)
927 /* Standard virtual console devices */
763d7a87 928 strncpy(ut.ut_id, vcline, sizeof(ut.ut_id));
729def03
WF
929 else {
930 size_t len = strlen(line);
931 char * ptr;
763d7a87
KZ
932 if (len >= sizeof(ut.ut_id))
933 ptr = line + len - sizeof(ut.ut_id);
729def03
WF
934 else
935 ptr = line;
763d7a87 936 strncpy(ut.ut_id, ptr, sizeof(ut.ut_id));
729def03 937 }
70bedfa1
KZ
938 }
939
940 strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
941 strncpy(ut.ut_line, line, sizeof(ut.ut_line));
942 if (fakehost)
943 strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
944 time(&t);
cfa7fe89
SK
945#if defined(_HAVE_UT_TV)
946 ut.ut_tv.tv_sec = t;
947#else
70bedfa1 948 ut.ut_time = t;
cfa7fe89 949#endif
70bedfa1 950 ut.ut_type = LOGIN_PROCESS;
729def03
WF
951 ut.ut_pid = pid;
952 ut.ut_session = sid;
70bedfa1
KZ
953
954 pututline(&ut);
955 endutent();
956
957 {
48d7b13a 958#ifdef HAVE_UPDWTMP
70bedfa1 959 updwtmp(_PATH_WTMP, &ut);
5c36a0eb 960#else
70bedfa1
KZ
961 int ut_fd;
962 int lf;
963
53d55042 964 if ((lf = open(_PATH_WTMPLOCK, O_CREAT | O_WRONLY, 0660)) >= 0) {
70bedfa1 965 flock(lf, LOCK_EX);
53d55042
SK
966 if ((ut_fd =
967 open(_PATH_WTMP, O_APPEND | O_WRONLY)) >= 0) {
729def03 968 write_all(ut_fd, &ut, sizeof(ut));
70bedfa1
KZ
969 close(ut_fd);
970 }
971 flock(lf, LOCK_UN);
972 close(lf);
973 }
53d55042 974#endif /* HAVE_UPDWTMP */
70bedfa1 975 }
6dbe3af9
KZ
976}
977
53d55042 978#endif /* SYSV_STYLE */
6dbe3af9 979
53d55042 980/* Set up tty as stdin, stdout & stderr. */
729def03 981static void open_tty(char *tty, struct termios *tp, struct options *op)
6dbe3af9 982{
3aa6b68f 983 const pid_t pid = getpid();
b9c73909 984 int closed = 0;
88e0f3df
ST
985#ifndef KDGKBMODE
986 int serial;
987#endif
6dbe3af9 988
3aa6b68f
WF
989 /* Set up new standard input, unless we are given an already opened port. */
990
991 if (strcmp(tty, "-") != 0) {
992 char buf[PATH_MAX+1];
993 struct group *gr = NULL;
70bedfa1 994 struct stat st;
3aa6b68f
WF
995 int fd, len;
996 pid_t tid;
997 gid_t gid = 0;
998
999 /* Use tty group if available */
1000 if ((gr = getgrnam("tty")))
1001 gid = gr->gr_gid;
1002
06fa5817
YK
1003 len = snprintf(buf, sizeof(buf), "/dev/%s", tty);
1004 if (len < 0 || (size_t)len >= sizeof(buf))
3aa6b68f
WF
1005 log_err(_("/dev/%s: cannot open as standard input: %m"), tty);
1006
c3a9f86f
SK
1007 /* Open the tty as standard input. */
1008 if ((fd = open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0)) < 0)
1009 log_err(_("/dev/%s: cannot open as standard input: %m"), tty);
1010
3aa6b68f
WF
1011 /*
1012 * There is always a race between this reset and the call to
1013 * vhangup() that s.o. can use to get access to your tty.
1014 * Linux login(1) will change tty permissions. Use root owner and group
1015 * with permission -rw------- for the period between getty and login.
1016 */
c3a9f86f 1017 if (fchown(fd, 0, gid) || fchmod(fd, (gid ? 0620 : 0600))) {
3aa6b68f
WF
1018 if (errno == EROFS)
1019 log_warn("%s: %m", buf);
1020 else
1021 log_err("%s: %m", buf);
1022 }
6dbe3af9 1023
3aa6b68f 1024 /* Sanity checks... */
3aa6b68f 1025 if (fstat(fd, &st) < 0)
763d7a87 1026 log_err("%s: %m", buf);
70bedfa1 1027 if ((st.st_mode & S_IFMT) != S_IFCHR)
bde20e1b 1028 log_err(_("/dev/%s: not a character device"), tty);
c6b35139
KZ
1029 if (!isatty(fd))
1030 log_err(_("/dev/%s: not a tty"), tty);
6dbe3af9 1031
3aa6b68f 1032 if (((tid = tcgetsid(fd)) < 0) || (pid != tid)) {
763d7a87 1033 if (ioctl(fd, TIOCSCTTY, 1) == -1)
8c219bf4 1034 log_warn(_("/dev/%s: cannot get controlling tty: %m"), tty);
3aa6b68f
WF
1035 }
1036
783b08fc
KZ
1037 close(STDIN_FILENO);
1038 errno = 0;
1039
3aa6b68f 1040 if (op->flags & F_HANGUP) {
783b08fc
KZ
1041
1042 if (ioctl(fd, TIOCNOTTY))
1043 debug("TIOCNOTTY ioctl failed\n");
1044
3aa6b68f 1045 /*
9e930041 1046 * Let's close all file descriptors before vhangup
783b08fc 1047 * https://lkml.org/lkml/2012/6/5/145
3aa6b68f 1048 */
783b08fc
KZ
1049 close(fd);
1050 close(STDOUT_FILENO);
1051 close(STDERR_FILENO);
1052 errno = 0;
1053 closed = 1;
1054
3aa6b68f 1055 if (vhangup())
8c219bf4 1056 log_err(_("/dev/%s: vhangup() failed: %m"), tty);
783b08fc
KZ
1057 } else
1058 close(fd);
6dbe3af9 1059
70bedfa1 1060 debug("open(2)\n");
3aa6b68f
WF
1061 if (open(buf, O_RDWR|O_NOCTTY|O_NONBLOCK, 0) != 0)
1062 log_err(_("/dev/%s: cannot open as standard input: %m"), tty);
783b08fc 1063
3aa6b68f 1064 if (((tid = tcgetsid(STDIN_FILENO)) < 0) || (pid != tid)) {
763d7a87 1065 if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) == -1)
8c219bf4 1066 log_warn(_("/dev/%s: cannot get controlling tty: %m"), tty);
3aa6b68f
WF
1067 }
1068
70bedfa1 1069 } else {
3aa6b68f 1070
70bedfa1
KZ
1071 /*
1072 * Standard input should already be connected to an open port. Make
1073 * sure it is open for read/write.
1074 */
3aa6b68f 1075
70bedfa1 1076 if ((fcntl(STDIN_FILENO, F_GETFL, 0) & O_RDWR) != O_RDWR)
bde20e1b 1077 log_err(_("%s: not open for read/write"), tty);
3aa6b68f 1078
70bedfa1
KZ
1079 }
1080
3aa6b68f 1081 if (tcsetpgrp(STDIN_FILENO, pid))
8c219bf4 1082 log_warn(_("/dev/%s: cannot set process group: %m"), tty);
3aa6b68f
WF
1083
1084 /* Get rid of the present outputs. */
783b08fc
KZ
1085 if (!closed) {
1086 close(STDOUT_FILENO);
1087 close(STDERR_FILENO);
1088 errno = 0;
1089 }
3aa6b68f 1090
70bedfa1
KZ
1091 /* Set up standard output and standard error file descriptors. */
1092 debug("duping\n");
53d55042 1093
70bedfa1
KZ
1094 /* set up stdout and stderr */
1095 if (dup(STDIN_FILENO) != 1 || dup(STDIN_FILENO) != 2)
bde20e1b 1096 log_err(_("%s: dup problem: %m"), tty);
6dbe3af9 1097
3aa6b68f
WF
1098 /* make stdio unbuffered for slow modem lines */
1099 setvbuf(stdout, NULL, _IONBF, 0);
1100
6dbe3af9 1101 /*
70bedfa1
KZ
1102 * The following ioctl will fail if stdin is not a tty, but also when
1103 * there is noise on the modem control lines. In the latter case, the
53d55042
SK
1104 * common course of action is (1) fix your cables (2) give the modem
1105 * more time to properly reset after hanging up.
1106 *
1107 * SunOS users can achieve (2) by patching the SunOS kernel variable
1108 * "zsadtrlow" to a larger value; 5 seconds seems to be a good value.
1109 * http://www.sunmanagers.org/archives/1993/0574.html
6dbe3af9 1110 */
bde20e1b 1111 memset(tp, 0, sizeof(struct termios));
70bedfa1 1112 if (tcgetattr(STDIN_FILENO, tp) < 0)
8c219bf4 1113 log_err(_("%s: failed to get terminal attributes: %m"), tty);
70bedfa1 1114
fba8a535
WF
1115#if defined (__s390__) || defined (__s390x__)
1116 if (!op->term) {
1117 /*
1118 * Special terminal on first serial line on a S/390(x) which
1119 * is due legacy reasons a block terminal of type 3270 or
1120 * higher. Whereas the second serial line on a S/390(x) is
1121 * a real character terminal which is compatible with VT220.
1122 */
f2bcda51 1123 if (strcmp(op->tty, "ttyS0") == 0) /* linux/drivers/s390/char/con3215.c */
fba8a535 1124 op->term = DEFAULT_TTYS0;
f2bcda51
WF
1125 else if (strncmp(op->tty, "3270/tty", 8) == 0) /* linux/drivers/s390/char/con3270.c */
1126 op->term = DEFAULT_TTY32;
1127 else if (strcmp(op->tty, "ttyS1") == 0) /* linux/drivers/s390/char/sclp_vt220.c */
fba8a535
WF
1128 op->term = DEFAULT_TTYS1;
1129 }
1130#endif
c32270d4
CE
1131
1132#if defined(__FreeBSD_kernel__)
1133 login_tty (0);
1134#endif
1135
70bedfa1 1136 /*
3aa6b68f 1137 * Detect if this is a virtual console or serial/modem line.
b9c73909
WF
1138 * In case of a virtual console the ioctl KDGKBMODE succeeds
1139 * whereas on other lines it will fails.
70bedfa1 1140 */
88e0f3df
ST
1141#ifdef KDGKBMODE
1142 if (ioctl(STDIN_FILENO, KDGKBMODE, &op->kbmode) == 0)
1143#else
1144 if (ioctl(STDIN_FILENO, TIOCMGET, &serial) < 0 && (errno == EINVAL))
1145#endif
1146 {
3aa6b68f
WF
1147 op->flags |= F_VCONSOLE;
1148 if (!op->term)
1149 op->term = DEFAULT_VCTERM;
b9c73909 1150 } else {
88e0f3df 1151#ifdef K_RAW
b9c73909 1152 op->kbmode = K_RAW;
88e0f3df 1153#endif
b9c73909
WF
1154 if (!op->term)
1155 op->term = DEFAULT_STERM;
1156 }
729def03
WF
1157
1158 setenv("TERM", op->term, 1);
6dbe3af9
KZ
1159}
1160
6443dd43
SW
1161/* Initialize termios settings. */
1162static void termio_clear(int fd)
1163{
1164 /*
1165 * Do not write a full reset (ESC c) because this destroys
1166 * the unicode mode again if the terminal was in unicode
1167 * mode. Also it clears the CONSOLE_MAGIC features which
1168 * are required for some languages/console-fonts.
1169 * Just put the cursor to the home position (ESC [ H),
1170 * erase everything below the cursor (ESC [ J), and set the
1171 * scrolling region to the full window (ESC [ r)
1172 */
1173 write_all(fd, "\033[r\033[H\033[J", 9);
1174}
1175
53d55042 1176/* Initialize termios settings. */
bde20e1b 1177static void termio_init(struct options *op, struct termios *tp)
6dbe3af9 1178{
70bedfa1 1179 speed_t ispeed, ospeed;
1683f6bf 1180 struct winsize ws;
1eb16fd7 1181#ifdef USE_PLYMOUTH_SUPPORT
bb280f79 1182 struct termios lock;
fe3f7e17
WF
1183 int i = (plymouth_command(MAGIC_PING) == 0) ? PLYMOUTH_TERMIOS_FLAGS_DELAY : 0;
1184 if (i)
1185 plymouth_command(MAGIC_QUIT);
bb280f79
WF
1186 while (i-- > 0) {
1187 /*
1188 * Even with TTYReset=no it seems with systemd or plymouth
1189 * the termios flags become changed from under the first
1190 * agetty on a serial system console as the flags are locked.
1191 */
1192 memset(&lock, 0, sizeof(struct termios));
1193 if (ioctl(STDIN_FILENO, TIOCGLCKTRMIOS, &lock) < 0)
1194 break;
1195 if (!lock.c_iflag && !lock.c_oflag && !lock.c_cflag && !lock.c_lflag)
1196 break;
1197 debug("termios locked\n");
bb280f79
WF
1198 sleep(1);
1199 }
1200 memset(&lock, 0, sizeof(struct termios));
1201 ioctl(STDIN_FILENO, TIOCSLCKTRMIOS, &lock);
88e0f3df 1202#endif
70bedfa1 1203
0da025da
WF
1204 if (op->flags & F_VCONSOLE) {
1205#if defined(IUTF8) && defined(KDGKBMODE)
b9c73909 1206 switch(op->kbmode) {
0da025da 1207 case K_UNICODE:
b0198a11 1208 setlocale(LC_CTYPE, "C.UTF-8");
0da025da
WF
1209 op->flags |= F_UTF8;
1210 break;
1211 case K_RAW:
1212 case K_MEDIUMRAW:
1213 case K_XLATE:
1214 default:
1215 setlocale(LC_CTYPE, "POSIX");
1216 op->flags &= ~F_UTF8;
1217 break;
1218 }
1219#else
1220 setlocale(LC_CTYPE, "POSIX");
1221 op->flags &= ~F_UTF8;
1222#endif
1223 reset_vc(op, tp);
1224
1225 if ((tp->c_cflag & (CS8|PARODD|PARENB)) == CS8)
1226 op->flags |= F_EIGHTBITS;
1227
6443dd43
SW
1228 if ((op->flags & F_NOCLEAR) == 0)
1229 termio_clear(STDOUT_FILENO);
0da025da
WF
1230 return;
1231 }
1232
8410cdd3
KZ
1233 /*
1234 * Serial line
1235 */
1236
1237 if (op->flags & F_KEEPSPEED || !op->numspeed) {
53d55042
SK
1238 /* Save the original setting. */
1239 ispeed = cfgetispeed(tp);
70bedfa1 1240 ospeed = cfgetospeed(tp);
1683f6bf
DWF
1241
1242 if (!ispeed) ispeed = TTYDEF_SPEED;
1243 if (!ospeed) ospeed = TTYDEF_SPEED;
1244
53d55042 1245 } else {
70bedfa1 1246 ospeed = ispeed = op->speeds[FIRST_SPEED];
53d55042 1247 }
70bedfa1
KZ
1248
1249 /*
1250 * Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
1251 * Special characters are set after we have read the login name; all
1252 * reads will be done in raw mode anyway. Errors will be dealt with
53d55042 1253 * later on.
70bedfa1 1254 */
53d55042 1255
8408647d
WF
1256#ifdef IUTF8
1257 tp->c_iflag = tp->c_iflag & IUTF8;
7478fce0
ST
1258 if (tp->c_iflag & IUTF8)
1259 op->flags |= F_UTF8;
8408647d
WF
1260#else
1261 tp->c_iflag = 0;
1262#endif
9c62a232
DJ
1263 tp->c_lflag = 0;
1264 tp->c_oflag &= OPOST | ONLCR;
70bedfa1 1265
1683f6bf 1266 if ((op->flags & F_KEEPCFLAGS) == 0)
70bedfa1
KZ
1267 tp->c_cflag = CS8 | HUPCL | CREAD | (tp->c_cflag & CLOCAL);
1268
53d55042
SK
1269 /*
1270 * Note that the speed is stored in the c_cflag termios field, so we have
9e930041 1271 * set the speed always when the cflag is reset.
70bedfa1
KZ
1272 */
1273 cfsetispeed(tp, ispeed);
1274 cfsetospeed(tp, ospeed);
1275
ef264c83
KZ
1276 /* The default is to follow setting from kernel, but it's possible
1277 * to explicitly remove/add CLOCAL flag by -L[=<mode>]*/
1278 switch (op->clocal) {
1279 case CLOCAL_MODE_ALWAYS:
1280 tp->c_cflag |= CLOCAL; /* -L or -L=always */
1281 break;
1282 case CLOCAL_MODE_NEVER:
1283 tp->c_cflag &= ~CLOCAL; /* -L=never */
1284 break;
1285 case CLOCAL_MODE_AUTO: /* -L=auto */
1286 break;
1287 }
1288
30e8b186 1289#ifdef HAVE_STRUCT_TERMIOS_C_LINE
70bedfa1 1290 tp->c_line = 0;
1961482a 1291#endif
70bedfa1
KZ
1292 tp->c_cc[VMIN] = 1;
1293 tp->c_cc[VTIME] = 0;
7eda085c 1294
1683f6bf
DWF
1295 /* Check for terminal size and if not found set default */
1296 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) {
1297 int set = 0;
1298 if (ws.ws_row == 0) {
1299 ws.ws_row = 24;
1300 set++;
1301 }
1302 if (ws.ws_col == 0) {
1303 ws.ws_col = 80;
1304 set++;
1305 }
efcf26f4
KZ
1306 if (ioctl(STDIN_FILENO, TIOCSWINSZ, &ws))
1307 debug("TIOCSWINSZ ioctl failed\n");
1683f6bf
DWF
1308 }
1309
53d55042 1310 /* Optionally enable hardware flow control. */
7eda085c 1311#ifdef CRTSCTS
70bedfa1
KZ
1312 if (op->flags & F_RTSCTS)
1313 tp->c_cflag |= CRTSCTS;
7eda085c 1314#endif
bb280f79
WF
1315 /* Flush input and output queues, important for modems! */
1316 tcflush(STDIN_FILENO, TCIOFLUSH);
7eda085c 1317
bb280f79
WF
1318 if (tcsetattr(STDIN_FILENO, TCSANOW, tp))
1319 log_warn(_("setting terminal attributes failed: %m"));
fd6b7a7f 1320
53d55042
SK
1321 /* Go to blocking input even in local mode. */
1322 fcntl(STDIN_FILENO, F_SETFL,
1323 fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
fd6b7a7f 1324
70bedfa1 1325 debug("term_io 2\n");
6dbe3af9
KZ
1326}
1327
0da025da
WF
1328/* Reset virtual console on stdin to its defaults */
1329static void reset_vc(const struct options *op, struct termios *tp)
1330{
879a7ab4
KZ
1331 int fl = 0;
1332
1333 fl |= (op->flags & F_KEEPCFLAGS) == 0 ? 0 : UL_TTY_KEEPCFLAGS;
1334 fl |= (op->flags & F_UTF8) == 0 ? 0 : UL_TTY_UTF8;
1335
1336 reset_virtual_console(tp, fl);
0da025da 1337
0da025da 1338 if (tcsetattr(STDIN_FILENO, TCSADRAIN, tp))
8c219bf4 1339 log_warn(_("setting terminal attributes failed: %m"));
bb280f79
WF
1340
1341 /* Go to blocking input even in local mode. */
1342 fcntl(STDIN_FILENO, F_SETFL,
1343 fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
0da025da
WF
1344}
1345
53d55042 1346/* Extract baud rate from modem status message. */
bde20e1b 1347static void auto_baud(struct termios *tp)
6dbe3af9 1348{
bde20e1b 1349 speed_t speed;
53d55042 1350 int vmin;
70bedfa1 1351 unsigned iflag;
53d55042
SK
1352 char buf[BUFSIZ];
1353 char *bp;
1354 int nread;
70bedfa1
KZ
1355
1356 /*
1357 * This works only if the modem produces its status code AFTER raising
1358 * the DCD line, and if the computer is fast enough to set the proper
1359 * baud rate before the message has gone by. We expect a message of the
1360 * following format:
1361 *
1362 * <junk><number><junk>
1363 *
1364 * The number is interpreted as the baud rate of the incoming call. If the
1365 * modem does not tell us the baud rate within one second, we will keep
1366 * using the current baud rate. It is advisable to enable BREAK
1367 * processing (comma-separated list of baud rates) if the processing of
1368 * modem status messages is enabled.
1369 */
1370
1371 /*
1372 * Use 7-bit characters, don't block if input queue is empty. Errors will
53d55042 1373 * be dealt with later on.
70bedfa1 1374 */
70bedfa1 1375 iflag = tp->c_iflag;
53d55042
SK
1376 /* Enable 8th-bit stripping. */
1377 tp->c_iflag |= ISTRIP;
70bedfa1 1378 vmin = tp->c_cc[VMIN];
53d55042
SK
1379 /* Do not block when queue is empty. */
1380 tp->c_cc[VMIN] = 0;
70bedfa1
KZ
1381 tcsetattr(STDIN_FILENO, TCSANOW, tp);
1382
1383 /*
1384 * Wait for a while, then read everything the modem has said so far and
1385 * try to extract the speed of the dial-in call.
1386 */
53d55042 1387 sleep(1);
70bedfa1
KZ
1388 if ((nread = read(STDIN_FILENO, buf, sizeof(buf) - 1)) > 0) {
1389 buf[nread] = '\0';
53d55042 1390 for (bp = buf; bp < buf + nread; bp++)
70bedfa1
KZ
1391 if (isascii(*bp) && isdigit(*bp)) {
1392 if ((speed = bcode(bp))) {
1393 cfsetispeed(tp, speed);
1394 cfsetospeed(tp, speed);
1395 }
1396 break;
1397 }
6dbe3af9 1398 }
6dbe3af9 1399
53d55042 1400 /* Restore terminal settings. Errors will be dealt with later on. */
70bedfa1
KZ
1401 tp->c_iflag = iflag;
1402 tp->c_cc[VMIN] = vmin;
53d55042 1403 tcsetattr(STDIN_FILENO, TCSANOW, tp);
6dbe3af9
KZ
1404}
1405
2448f336
KZ
1406static char *xgethostname(void)
1407{
1408 char *name;
1409 size_t sz = get_hostname_max() + 1;
1410
1411 name = malloc(sizeof(char) * sz);
1412 if (!name)
1413 log_err(_("failed to allocate memory: %m"));
1414
8362545b
KZ
1415 if (gethostname(name, sz) != 0) {
1416 free(name);
2448f336 1417 return NULL;
8362545b 1418 }
2448f336
KZ
1419 name[sz - 1] = '\0';
1420 return name;
1421}
1422
1423static char *xgetdomainname(void)
1424{
1425#ifdef HAVE_GETDOMAINNAME
1426 char *name;
1427 size_t sz = get_hostname_max() + 1;
1428
1429 name = malloc(sizeof(char) * sz);
1430 if (!name)
1431 log_err(_("failed to allocate memory: %m"));
1432
8362545b
KZ
1433 if (getdomainname(name, sz) != 0) {
1434 free(name);
2448f336 1435 return NULL;
8362545b 1436 }
2448f336
KZ
1437 name[sz - 1] = '\0';
1438 return name;
1439#endif
1440 return NULL;
1441}
1442
b34f097e
KZ
1443static char *read_os_release(struct options *op, const char *varname)
1444{
1445 int fd = -1;
1446 struct stat st;
1447 size_t varsz = strlen(varname);
1448 char *p, *buf = NULL, *ret = NULL;
1449
1450 /* read the file only once */
1451 if (!op->osrelease) {
b28842ae 1452 fd = open(_PATH_OS_RELEASE_ETC, O_RDONLY);
b34f097e 1453 if (fd == -1) {
b28842ae
KZ
1454 fd = open(_PATH_OS_RELEASE_USR, O_RDONLY);
1455 if (fd == -1) {
1456 log_warn(_("cannot open os-release file"));
1457 return NULL;
1458 }
b34f097e
KZ
1459 }
1460
1461 if (fstat(fd, &st) < 0 || st.st_size > 4 * 1024 * 1024)
1462 goto done;
1463
1464 op->osrelease = malloc(st.st_size + 1);
1465 if (!op->osrelease)
1466 log_err(_("failed to allocate memory: %m"));
1467 if (read_all(fd, op->osrelease, st.st_size) != (ssize_t) st.st_size) {
1468 free(op->osrelease);
1469 op->osrelease = NULL;
1470 goto done;
1471 }
1472 op->osrelease[st.st_size] = 0;
1473 }
1474 buf = strdup(op->osrelease);
1475 if (!buf)
1476 log_err(_("failed to allocate memory: %m"));
1477 p = buf;
1478
1479 for (;;) {
1480 char *eol, *eon;
1481
1482 p += strspn(p, "\n\r");
1483 p += strspn(p, " \t\n\r");
1484 if (!*p)
1485 break;
1486 if (strspn(p, "#;\n") != 0) {
1487 p += strcspn(p, "\n\r");
1488 continue;
1489 }
1490 if (strncmp(p, varname, varsz) != 0) {
1491 p += strcspn(p, "\n\r");
1492 continue;
1493 }
1494 p += varsz;
1495 p += strspn(p, " \t\n\r=\"");
1496 eol = p + strcspn(p, "\n\r");
1497 *eol = '\0';
1498 eon = eol-1;
1499 while (eon > p) {
1500 if (*eon == '\t' || *eon == ' ') {
1501 eon--;
1502 continue;
1503 }
1504 if (*eon == '"') {
1505 *eon = '\0';
1506 break;
1507 }
1508 break;
1509 }
f614b73c 1510 free(ret);
b34f097e
KZ
1511 ret = strdup(p);
1512 if (!ret)
1513 log_err(_("failed to allocate memory: %m"));
1514 p = eol + 1;
1515 }
1516done:
1517 free(buf);
1518 if (fd >= 0)
1519 close(fd);
1520 return ret;
1521}
1522
c2ef308b 1523#ifdef AGETTY_RELOAD
e36deb64
SW
1524static void open_netlink(void)
1525{
1526 struct sockaddr_nl addr = { 0, };
1527 int sock;
1528
1529 if (netlink_fd != AGETTY_RELOAD_FDNONE)
1530 return;
1531
1532 sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
1533 if (sock >= 0) {
1534 addr.nl_family = AF_NETLINK;
1535 addr.nl_pid = getpid();
1536 addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
1537 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
1538 close(sock);
1539 else
1540 netlink_fd = sock;
1541 }
1542}
1543
1544static int process_netlink_msg(int *changed)
1545{
1546 char buf[4096];
1547 struct sockaddr_nl snl;
1548 struct nlmsghdr *h;
1549 int rc;
1550
1551 struct iovec iov = {
1552 .iov_base = buf,
1553 .iov_len = sizeof(buf)
1554 };
1555 struct msghdr msg = {
1556 .msg_name = &snl,
1557 .msg_namelen = sizeof(snl),
1558 .msg_iov = &iov,
1559 .msg_iovlen = 1,
1560 .msg_control = NULL,
1561 .msg_controllen = 0,
1562 .msg_flags = 0
1563 };
1564
1565 rc = recvmsg(netlink_fd, &msg, MSG_DONTWAIT);
1566 if (rc < 0) {
1567 if (errno == EWOULDBLOCK || errno == EAGAIN)
1568 return 0;
1569
1570 /* Failure, just stop listening for changes */
1571 close(netlink_fd);
1572 netlink_fd = AGETTY_RELOAD_FDNONE;
1573 return 0;
1574 }
1575
1576 for (h = (struct nlmsghdr *)buf; NLMSG_OK(h, (unsigned int)rc); h = NLMSG_NEXT(h, rc)) {
1577 if (h->nlmsg_type == NLMSG_DONE ||
1578 h->nlmsg_type == NLMSG_ERROR) {
1579 close(netlink_fd);
1580 netlink_fd = AGETTY_RELOAD_FDNONE;
1581 return 0;
1582 }
1583
1584 *changed = 1;
1585 break;
1586 }
1587
1588 return 1;
1589}
1590
1591static int process_netlink(void)
1592{
1593 int changed = 0;
1594 while (process_netlink_msg(&changed));
1595 return changed;
1596}
1597
c2ef308b
KZ
1598static int wait_for_term_input(int fd)
1599{
1600 char buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
1601 struct termios orig, nonc;
1602 fd_set rfds;
1603 int count, i;
1604
1605 /* Our aim here is to fall through if something fails
1606 * and not be stuck waiting. On failure assume we have input */
1607
1608 if (tcgetattr(fd, &orig) != 0)
1609 return 1;
1610
1611 memcpy(&nonc, &orig, sizeof (nonc));
1612 nonc.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHOKE);
1613 nonc.c_cc[VMIN] = 1;
1614 nonc.c_cc[VTIME] = 0;
1615
1616 if (tcsetattr(fd, TCSANOW, &nonc) != 0)
1617 return 1;
1618
1619 if (inotify_fd == AGETTY_RELOAD_FDNONE) {
1620 /* make sure the reload trigger file exists */
1621 int reload_fd = open(AGETTY_RELOAD_FILENAME,
1622 O_CREAT|O_CLOEXEC|O_RDONLY,
1623 S_IRUSR|S_IWUSR);
1624
1625 /* initialize reload trigger inotify stuff */
1626 if (reload_fd >= 0) {
1627 inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1628 if (inotify_fd > 0)
1629 inotify_add_watch(inotify_fd, AGETTY_RELOAD_FILENAME,
1630 IN_ATTRIB | IN_MODIFY);
1631
1632 close(reload_fd);
1633 } else
1634 log_warn(_("failed to create reload file: %s: %m"),
1635 AGETTY_RELOAD_FILENAME);
1636 }
1637
e36deb64 1638 while (1) {
5184d936
KZ
1639 int nfds = fd;
1640
e36deb64
SW
1641 FD_ZERO(&rfds);
1642 FD_SET(fd, &rfds);
c2ef308b 1643
5184d936 1644 if (inotify_fd >= 0) {
e36deb64 1645 FD_SET(inotify_fd, &rfds);
5184d936
KZ
1646 nfds = max(nfds, inotify_fd);
1647 }
1648 if (netlink_fd >= 0) {
e36deb64 1649 FD_SET(netlink_fd, &rfds);
5184d936
KZ
1650 nfds = max(nfds, netlink_fd);
1651 }
c2ef308b 1652
e36deb64 1653 /* If waiting fails, just fall through, presumably reading input will fail */
5184d936 1654 if (select(nfds + 1, &rfds, NULL, NULL, NULL) < 0)
e36deb64 1655 return 1;
c2ef308b 1656
e36deb64
SW
1657 if (FD_ISSET(fd, &rfds)) {
1658 count = read(fd, buffer, sizeof (buffer));
c2ef308b 1659
e36deb64 1660 tcsetattr(fd, TCSANOW, &orig);
c2ef308b 1661
e36deb64
SW
1662 /* Reinject the bytes we read back into the buffer, usually just one byte */
1663 for (i = 0; i < count; i++)
1664 ioctl(fd, TIOCSTI, buffer + i);
c2ef308b 1665
e36deb64
SW
1666 /* Have terminal input */
1667 return 1;
c2ef308b 1668
e36deb64
SW
1669 } else if (netlink_fd >= 0 && FD_ISSET(netlink_fd, &rfds)) {
1670 if (!process_netlink())
1671 continue;
c2ef308b
KZ
1672
1673 /* Just drain the inotify buffer */
e36deb64
SW
1674 } else if (inotify_fd >= 0 && FD_ISSET(inotify_fd, &rfds)) {
1675 while (read(inotify_fd, buffer, sizeof (buffer)) > 0);
1676 }
1677
1678 tcsetattr(fd, TCSANOW, &orig);
c2ef308b
KZ
1679
1680 /* Need to reprompt */
1681 return 0;
1682 }
1683}
1684#endif /* AGETTY_RELOAD */
1685static void print_issue_file(struct options *op, struct termios *tp)
6dbe3af9
KZ
1686{
1687#ifdef ISSUE
53d55042 1688 FILE *fd;
c2ef308b 1689#endif
e85281a8
DWF
1690 if ((op->flags & F_NONL) == 0) {
1691 /* Issue not in use, start with a new line. */
1692 write_all(STDOUT_FILENO, "\r\n", 2);
1693 }
3aa6b68f 1694
53d55042 1695#ifdef ISSUE
70bedfa1 1696 if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) {
1683f6bf 1697 int c, oflag = tp->c_oflag; /* Save current setting. */
3aa6b68f 1698
1683f6bf
DWF
1699 if ((op->flags & F_VCONSOLE) == 0) {
1700 /* Map new line in output to carriage return & new line. */
1701 tp->c_oflag |= (ONLCR | OPOST);
1702 tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
1703 }
70bedfa1
KZ
1704
1705 while ((c = getc(fd)) != EOF) {
729def03 1706 if (c == '\\')
2b945eda 1707 output_special_char(getc(fd), op, tp, fd);
729def03 1708 else
763d7a87 1709 putchar(c);
70bedfa1
KZ
1710 }
1711 fflush(stdout);
6dbe3af9 1712
1683f6bf
DWF
1713 if ((op->flags & F_VCONSOLE) == 0) {
1714 /* Restore settings. */
1715 tp->c_oflag = oflag;
1716 /* Wait till output is gone. */
1717 tcsetattr(STDIN_FILENO, TCSADRAIN, tp);
1718 }
53d55042 1719 fclose(fd);
6dbe3af9 1720 }
eb8e1f9f 1721#endif /* ISSUE */
c2ef308b
KZ
1722}
1723
1724/* Show login prompt, optionally preceded by /etc/issue contents. */
1725static void do_prompt(struct options *op, struct termios *tp)
1726{
fe3f7e17 1727#ifdef AGETTY_RELOAD
c2ef308b 1728again:
fe3f7e17 1729#endif
c2ef308b
KZ
1730 print_issue_file(op, tp);
1731
eb8e1f9f 1732 if (op->flags & F_LOGINPAUSE) {
8c219bf4 1733 puts(_("[press ENTER to login]"));
c2ef308b
KZ
1734#ifdef AGETTY_RELOAD
1735 if (!wait_for_term_input(STDIN_FILENO)) {
1736 /* reload issue */
1737 if (op->flags & F_VCONSOLE)
1738 termio_clear(STDOUT_FILENO);
1739 goto again;
1740 }
1741#endif
eb8e1f9f
WF
1742 getc(stdin);
1743 }
1744#ifdef KDGKBLED
36601b23
KZ
1745 if (!(op->flags & F_NOHINTS) && !op->autolog &&
1746 (op->flags & F_VCONSOLE)) {
a694957a
KZ
1747 int kb = 0;
1748
eb8e1f9f 1749 if (ioctl(STDIN_FILENO, KDGKBLED, &kb) == 0) {
a694957a
KZ
1750 char hint[256] = { '\0' };
1751 int nl = 0;
a694957a 1752
01c5b787 1753 if (access(_PATH_NUMLOCK_ON, F_OK) == 0)
a694957a
KZ
1754 nl = 1;
1755
1756 if (nl && (kb & 0x02) == 0)
1757 append(hint, sizeof(hint), NULL, _("Num Lock off"));
1758
1759 else if (nl == 0 && (kb & 2) && (kb & 0x20) == 0)
1760 append(hint, sizeof(hint), NULL, _("Num Lock on"));
1761
1762 if ((kb & 0x04) && (kb & 0x40) == 0)
1763 append(hint, sizeof(hint), ", ", _("Caps Lock on"));
1764
1765 if ((kb & 0x01) && (kb & 0x10) == 0)
1766 append(hint, sizeof(hint), ", ", _("Scroll Lock on"));
1767
1768 if (*hint)
1769 printf(_("Hint: %s\n\n"), hint);
eb8e1f9f
WF
1770 }
1771 }
1772#endif /* KDGKBLED */
e85281a8 1773 if ((op->flags & F_NOHOSTNAME) == 0) {
2448f336
KZ
1774 char *hn = xgethostname();
1775
1776 if (hn) {
e85281a8 1777 char *dot = strchr(hn, '.');
74b3df85
SK
1778 char *cn = hn;
1779 struct addrinfo *res = NULL;
e85281a8 1780
e85281a8
DWF
1781 if ((op->flags & F_LONGHNAME) == 0) {
1782 if (dot)
1783 *dot = '\0';
74b3df85
SK
1784
1785 } else if (dot == NULL) {
1786 struct addrinfo hints;
1787
1788 memset(&hints, 0, sizeof(hints));
1789 hints.ai_flags = AI_CANONNAME;
1790
1791 if (!getaddrinfo(hn, NULL, &hints, &res)
1792 && res && res->ai_canonname)
1793 cn = res->ai_canonname;
1794 }
1795
1796 write_all(STDOUT_FILENO, cn, strlen(cn));
e85281a8 1797 write_all(STDOUT_FILENO, " ", 1);
74b3df85
SK
1798
1799 if (res)
1800 freeaddrinfo(res);
2448f336 1801 free(hn);
e85281a8 1802 }
70bedfa1 1803 }
933956cb 1804 if (!op->autolog) {
eb8e1f9f
WF
1805 /* Always show login prompt. */
1806 write_all(STDOUT_FILENO, LOGIN, sizeof(LOGIN) - 1);
1807 }
6dbe3af9
KZ
1808}
1809
53d55042 1810/* Select next baud rate. */
bde20e1b 1811static void next_speed(struct options *op, struct termios *tp)
6dbe3af9 1812{
70bedfa1
KZ
1813 static int baud_index = -1;
1814
1815 if (baud_index == -1)
1816 /*
53d55042 1817 * If the F_KEEPSPEED flags is set then the FIRST_SPEED is not
70bedfa1
KZ
1818 * tested yet (see termio_init()).
1819 */
53d55042
SK
1820 baud_index =
1821 (op->flags & F_KEEPSPEED) ? FIRST_SPEED : 1 % op->numspeed;
70bedfa1
KZ
1822 else
1823 baud_index = (baud_index + 1) % op->numspeed;
1824
1825 cfsetispeed(tp, op->speeds[baud_index]);
1826 cfsetospeed(tp, op->speeds[baud_index]);
53d55042 1827 tcsetattr(STDIN_FILENO, TCSANOW, tp);
6dbe3af9
KZ
1828}
1829
53d55042 1830/* Get user name, establish parity, speed, erase, kill & eol. */
bde20e1b 1831static char *get_logname(struct options *op, struct termios *tp, struct chardata *cp)
6dbe3af9 1832{
70bedfa1 1833 static char logname[BUFSIZ];
53d55042
SK
1834 char *bp;
1835 char c; /* input character, full eight bits */
1836 char ascval; /* low 7 bits of input character */
1683f6bf 1837 int eightbit;
53d55042
SK
1838 static char *erase[] = { /* backspace-space-backspace */
1839 "\010\040\010", /* space parity */
1840 "\010\040\010", /* odd parity */
1841 "\210\240\210", /* even parity */
1842 "\210\240\210", /* no parity */
70bedfa1
KZ
1843 };
1844
1845 /* Initialize kill, erase, parity etc. (also after switching speeds). */
f5664477 1846 INIT_CHARDATA(cp);
70bedfa1 1847
53d55042
SK
1848 /*
1849 * Flush pending input (especially important after parsing or switching
1850 * the baud rate).
1851 */
1683f6bf
DWF
1852 if ((op->flags & F_VCONSOLE) == 0)
1853 sleep(1);
53d55042 1854 tcflush(STDIN_FILENO, TCIFLUSH);
70bedfa1 1855
1683f6bf
DWF
1856 eightbit = (op->flags & F_EIGHTBITS);
1857 bp = logname;
1858 *bp = '\0';
1859
1860 while (*logname == '\0') {
1861
1862 /* Write issue file and prompt */
70bedfa1
KZ
1863 do_prompt(op, tp);
1864
6443dd43
SW
1865#ifdef AGETTY_RELOAD
1866 /* If asked to reprompt *before* terminal input arrives, then do so */
1867 if (!wait_for_term_input(STDIN_FILENO)) {
1868 if (op->flags & F_VCONSOLE)
1869 termio_clear(STDOUT_FILENO);
1870 continue;
1871 }
1872#endif
1683f6bf
DWF
1873 cp->eol = '\0';
1874
1875 /* Read name, watch for break and end-of-line. */
1876 while (cp->eol == '\0') {
1877
cb872ac9
KZ
1878 char key;
1879
763d7a87 1880 if (read(STDIN_FILENO, &c, 1) < 1) {
1683f6bf 1881
714cff30
KZ
1882 /* The terminal could be open with O_NONBLOCK when
1883 * -L (force CLOCAL) is specified... */
1683f6bf 1884 if (errno == EINTR || errno == EAGAIN) {
a5bd7939 1885 xusleep(250000);
1683f6bf
DWF
1886 continue;
1887 }
1888 switch (errno) {
1889 case 0:
1890 case EIO:
1891 case ESRCH:
1892 case EINVAL:
1893 case ENOENT:
1894 break;
1895 default:
1896 log_err(_("%s: read: %m"), op->tty);
1897 }
70bedfa1 1898 }
1683f6bf 1899
70bedfa1 1900 /* Do parity bit handling. */
1683f6bf 1901 if (eightbit)
70bedfa1 1902 ascval = c;
1683f6bf
DWF
1903 else if (c != (ascval = (c & 0177))) {
1904 uint32_t bits; /* # of "1" bits per character */
1905 uint32_t mask; /* mask with 1 bit up */
1906 for (bits = 1, mask = 1; mask & 0177; mask <<= 1) {
70bedfa1 1907 if (mask & ascval)
53d55042 1908 bits++;
1683f6bf 1909 }
70bedfa1
KZ
1910 cp->parity |= ((bits & 1) ? 1 : 2);
1911 }
1683f6bf 1912
cb872ac9
KZ
1913 if (op->killchars && strchr(op->killchars, ascval))
1914 key = CTL('U');
1915 else if (op->erasechars && strchr(op->erasechars, ascval))
1916 key = DEL;
1917 else
1918 key = ascval;
1919
70bedfa1 1920 /* Do erase, kill and end-of-line processing. */
cb872ac9 1921 switch (key) {
1683f6bf
DWF
1922 case 0:
1923 *bp = 0;
1924 if (op->numspeed > 1)
4a9b7543 1925 return NULL;
1683f6bf 1926 break;
70bedfa1
KZ
1927 case CR:
1928 case NL:
1683f6bf
DWF
1929 *bp = 0; /* terminate logname */
1930 cp->eol = ascval; /* set end-of-line char */
70bedfa1
KZ
1931 break;
1932 case BS:
1933 case DEL:
1683f6bf 1934 cp->erase = ascval; /* set erase character */
70bedfa1 1935 if (bp > logname) {
b9261127 1936 if ((tp->c_lflag & ECHO) == 0)
1683f6bf 1937 write_all(1, erase[cp->parity], 3);
70bedfa1
KZ
1938 bp--;
1939 }
1940 break;
1941 case CTL('U'):
1683f6bf 1942 cp->kill = ascval; /* set kill character */
70bedfa1 1943 while (bp > logname) {
b9261127 1944 if ((tp->c_lflag & ECHO) == 0)
1683f6bf 1945 write_all(1, erase[cp->parity], 3);
70bedfa1
KZ
1946 bp--;
1947 }
1948 break;
1949 case CTL('D'):
1950 exit(EXIT_SUCCESS);
1951 default:
1683f6bf
DWF
1952 if (!isascii(ascval) || !isprint(ascval))
1953 break;
1954 if ((size_t)(bp - logname) >= sizeof(logname) - 1)
bde20e1b 1955 log_err(_("%s: input overrun"), op->tty);
b9261127 1956 if ((tp->c_lflag & ECHO) == 0)
1683f6bf
DWF
1957 write_all(1, &c, 1); /* echo the character */
1958 *bp++ = ascval; /* and store it */
70bedfa1
KZ
1959 break;
1960 }
6dbe3af9 1961 }
6dbe3af9 1962 }
1683f6bf
DWF
1963#ifdef HAVE_WIDECHAR
1964 if ((op->flags & (F_EIGHTBITS|F_UTF8)) == (F_EIGHTBITS|F_UTF8)) {
1965 /* Check out UTF-8 multibyte characters */
1966 ssize_t len;
1967 wchar_t *wcs, *wcp;
1968
1969 len = mbstowcs((wchar_t *)0, logname, 0);
1970 if (len < 0)
8c219bf4 1971 log_err(_("%s: invalid character conversion for login name"), op->tty);
1683f6bf 1972
fea1cbf7 1973 wcs = malloc((len + 1) * sizeof(wchar_t));
ca8e91a4
KZ
1974 if (!wcs)
1975 log_err(_("failed to allocate memory: %m"));
1683f6bf
DWF
1976
1977 len = mbstowcs(wcs, logname, len + 1);
1978 if (len < 0)
8c219bf4 1979 log_err(_("%s: invalid character conversion for login name"), op->tty);
1683f6bf
DWF
1980
1981 wcp = wcs;
1982 while (*wcp) {
1983 const wint_t wc = *wcp++;
1984 if (!iswprint(wc))
8c219bf4 1985 log_err(_("%s: invalid character 0x%x in login name"), op->tty, wc);
1683f6bf
DWF
1986 }
1987 free(wcs);
1988 } else
1989#endif
1990 if ((op->flags & F_LCUC) && (cp->capslock = caps_lock(logname))) {
1991
1992 /* Handle names with upper case and no lower case. */
70bedfa1
KZ
1993 for (bp = logname; *bp; bp++)
1994 if (isupper(*bp))
1683f6bf
DWF
1995 *bp = tolower(*bp); /* map name to lower case */
1996 }
1997
70bedfa1 1998 return logname;
6dbe3af9
KZ
1999}
2000
53d55042 2001/* Set the final tty mode bits. */
bde20e1b 2002static void termio_final(struct options *op, struct termios *tp, struct chardata *cp)
6dbe3af9 2003{
70bedfa1 2004 /* General terminal-independent stuff. */
53d55042
SK
2005
2006 /* 2-way flow control */
2007 tp->c_iflag |= IXON | IXOFF;
2008 tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
2009 /* no longer| ECHOCTL | ECHOPRT */
70bedfa1
KZ
2010 tp->c_oflag |= OPOST;
2011 /* tp->c_cflag = 0; */
53d55042
SK
2012 tp->c_cc[VINTR] = DEF_INTR;
2013 tp->c_cc[VQUIT] = DEF_QUIT;
2014 tp->c_cc[VEOF] = DEF_EOF;
70bedfa1 2015 tp->c_cc[VEOL] = DEF_EOL;
fd6b7a7f 2016#ifdef __linux__
53d55042 2017 tp->c_cc[VSWTC] = DEF_SWITCH;
1961482a 2018#elif defined(VSWTCH)
53d55042
SK
2019 tp->c_cc[VSWTCH] = DEF_SWITCH;
2020#endif /* __linux__ */
6dbe3af9 2021
70bedfa1
KZ
2022 /* Account for special characters seen in input. */
2023 if (cp->eol == CR) {
53d55042
SK
2024 tp->c_iflag |= ICRNL;
2025 tp->c_oflag |= ONLCR;
70bedfa1 2026 }
53d55042
SK
2027 tp->c_cc[VERASE] = cp->erase;
2028 tp->c_cc[VKILL] = cp->kill;
70bedfa1
KZ
2029
2030 /* Account for the presence or absence of parity bits in input. */
2031 switch (cp->parity) {
53d55042
SK
2032 case 0:
2033 /* space (always 0) parity */
2034 break;
2035 case 1:
2036 /* odd parity */
2037 tp->c_cflag |= PARODD;
2038 /* do not break */
2039 case 2:
2040 /* even parity */
2041 tp->c_cflag |= PARENB;
2042 tp->c_iflag |= INPCK | ISTRIP;
2043 /* do not break */
2044 case (1 | 2):
2045 /* no parity bit */
2046 tp->c_cflag &= ~CSIZE;
2047 tp->c_cflag |= CS7;
2048 break;
70bedfa1
KZ
2049 }
2050 /* Account for upper case without lower case. */
2051 if (cp->capslock) {
ee519041 2052#ifdef IUCLC
70bedfa1 2053 tp->c_iflag |= IUCLC;
ee519041 2054#endif
b75c8134 2055#ifdef XCASE
70bedfa1 2056 tp->c_lflag |= XCASE;
1961482a 2057#endif
ee519041 2058#ifdef OLCUC
70bedfa1 2059 tp->c_oflag |= OLCUC;
ee519041 2060#endif
70bedfa1 2061 }
53d55042 2062 /* Optionally enable hardware flow control. */
6dbe3af9 2063#ifdef CRTSCTS
70bedfa1
KZ
2064 if (op->flags & F_RTSCTS)
2065 tp->c_cflag |= CRTSCTS;
6dbe3af9
KZ
2066#endif
2067
53d55042 2068 /* Finally, make the new settings effective. */
70bedfa1 2069 if (tcsetattr(STDIN_FILENO, TCSANOW, tp) < 0)
8c219bf4 2070 log_err(_("%s: failed to set terminal attributes: %m"), op->tty);
6dbe3af9
KZ
2071}
2072
53d55042
SK
2073/*
2074 * String contains upper case without lower case.
2075 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=52940
2076 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=156242
2077 */
bde20e1b 2078static int caps_lock(char *s)
6dbe3af9 2079{
53d55042 2080 int capslock;
70bedfa1
KZ
2081
2082 for (capslock = 0; *s; s++) {
2083 if (islower(*s))
2084 return EXIT_SUCCESS;
2085 if (capslock == 0)
2086 capslock = isupper(*s);
2087 }
2088 return capslock;
6dbe3af9
KZ
2089}
2090
53d55042 2091/* Convert speed string to speed code; return 0 on failure. */
bde20e1b 2092static speed_t bcode(char *s)
6dbe3af9 2093{
1a204cd2 2094 const struct Speedtab *sp;
53d55042 2095 long speed = atol(s);
6dbe3af9 2096
70bedfa1
KZ
2097 for (sp = speedtab; sp->speed; sp++)
2098 if (sp->speed == speed)
2099 return sp->code;
2100 return 0;
6dbe3af9
KZ
2101}
2102
7d368556 2103static void __attribute__ ((__noreturn__)) usage(FILE *out)
6dbe3af9 2104{
7d368556 2105 fputs(USAGE_HEADER, out);
3755d48b
BS
2106 fprintf(out, _(" %1$s [options] <line> [<baud_rate>,...] [<termtype>]\n"
2107 " %1$s [options] <baud_rate>,... <line> [<termtype>]\n"), program_invocation_short_name);
451dbcfa
BS
2108
2109 fputs(USAGE_SEPARATOR, out);
2110 fputs(_("Open a terminal and set its mode.\n"), out);
2111
7d368556
SK
2112 fputs(USAGE_OPTIONS, out);
2113 fputs(_(" -8, --8bits assume 8-bit tty\n"), out);
2114 fputs(_(" -a, --autologin <user> login the specified user automatically\n"), out);
2115 fputs(_(" -c, --noreset do not reset control mode\n"), out);
1505a0b2 2116 fputs(_(" -E, --remote use -r <hostname> for login(1)\n"), out);
7d368556
SK
2117 fputs(_(" -f, --issue-file <file> display issue file\n"), out);
2118 fputs(_(" -h, --flow-control enable hardware flow control\n"), out);
2119 fputs(_(" -H, --host <hostname> specify login host\n"), out);
2120 fputs(_(" -i, --noissue do not display issue file\n"), out);
2121 fputs(_(" -I, --init-string <string> set init string\n"), out);
584c0744 2122 fputs(_(" -J --noclear do not clear the screen before prompt\n"), out);
7d368556 2123 fputs(_(" -l, --login-program <file> specify login program\n"), out);
38ae77d7 2124 fputs(_(" -L, --local-line[=<mode>] control the local line flag\n"), out);
7d368556
SK
2125 fputs(_(" -m, --extract-baud extract baud rate during connect\n"), out);
2126 fputs(_(" -n, --skip-login do not prompt for login\n"), out);
584c0744 2127 fputs(_(" -N --nonewline do not print a newline before issue\n"), out);
7d368556 2128 fputs(_(" -o, --login-options <opts> options that are passed to login\n"), out);
a056cfe2 2129 fputs(_(" -p, --login-pause wait for any key before the login\n"), out);
4b856ee4 2130 fputs(_(" -r, --chroot <dir> change root to the directory\n"), out);
7d368556
SK
2131 fputs(_(" -R, --hangup do virtually hangup on the tty\n"), out);
2132 fputs(_(" -s, --keep-baud try to keep baud rate after break\n"), out);
2133 fputs(_(" -t, --timeout <number> login process timeout\n"), out);
2134 fputs(_(" -U, --detect-case detect uppercase terminal\n"), out);
2135 fputs(_(" -w, --wait-cr wait carriage-return\n"), out);
7d368556 2136 fputs(_(" --nohints do not print hints\n"), out);
a056cfe2 2137 fputs(_(" --nohostname no hostname at all will be shown\n"), out);
7d368556
SK
2138 fputs(_(" --long-hostname show full qualified hostname\n"), out);
2139 fputs(_(" --erase-chars <string> additional backspace chars\n"), out);
2140 fputs(_(" --kill-chars <string> additional kill chars\n"), out);
584c0744
BR
2141 fputs(_(" --chdir <directory> chdir before the login\n"), out);
2142 fputs(_(" --delay <number> sleep seconds before prompt\n"), out);
2143 fputs(_(" --nice <number> run login with this priority\n"), out);
6443dd43 2144 fputs(_(" --reload reload prompts on running agetty instances\n"), out);
86ad1432
SK
2145 fputs(_(" --help display this help and exit\n"), out);
2146 fputs(_(" --version output version information and exit\n"), out);
7d368556 2147 fprintf(out, USAGE_MAN_TAIL("agetty(8)"));
70bedfa1
KZ
2148
2149 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
6dbe3af9
KZ
2150}
2151
bde20e1b
WF
2152/*
2153 * Helper function reports errors to console or syslog.
2154 * Will be used by log_err() and log_warn() therefore
2155 * it takes a format as well as va_list.
2156 */
6dbe3af9
KZ
2157#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2)
2158
bde20e1b 2159static void dolog(int priority, const char *fmt, va_list ap)
53d55042 2160{
6dbe3af9 2161#ifndef USE_SYSLOG
53d55042 2162 int fd;
6dbe3af9 2163#endif
53d55042
SK
2164 char buf[BUFSIZ];
2165 char *bp;
6dbe3af9 2166
70bedfa1
KZ
2167 /*
2168 * If the diagnostic is reported via syslog(3), the process name is
2169 * automatically prepended to the message. If we write directly to
2170 * /dev/console, we must prepend the process name ourselves.
2171 */
6dbe3af9 2172#ifdef USE_SYSLOG
70bedfa1
KZ
2173 buf[0] = '\0';
2174 bp = buf;
6dbe3af9 2175#else
53d55042 2176 str2cpy(buf, program_invocation_short_name, ": ");
70bedfa1 2177 bp = buf + strlen(buf);
53d55042 2178#endif /* USE_SYSLOG */
763d7a87 2179 vsnprintf(bp, sizeof(buf)-strlen(buf), fmt, ap);
6dbe3af9 2180
70bedfa1
KZ
2181 /*
2182 * Write the diagnostic directly to /dev/console if we do not use the
2183 * syslog(3) facility.
2184 */
6dbe3af9 2185#ifdef USE_SYSLOG
53d55042 2186 openlog(program_invocation_short_name, LOG_PID, LOG_AUTHPRIV);
bde20e1b 2187 syslog(priority, "%s", buf);
70bedfa1 2188 closelog();
6dbe3af9 2189#else
70bedfa1 2190 /* Terminate with CR-LF since the console mode is unknown. */
53d55042 2191 strcat(bp, "\r\n");
70bedfa1 2192 if ((fd = open("/dev/console", 1)) >= 0) {
729def03 2193 write_all(fd, buf, strlen(buf));
53d55042 2194 close(fd);
70bedfa1 2195 }
53d55042 2196#endif /* USE_SYSLOG */
bde20e1b
WF
2197}
2198
2199static void log_err(const char *fmt, ...)
2200{
2201 va_list ap;
2202
2203 va_start(ap, fmt);
2204 dolog(LOG_ERR, fmt, ap);
2205 va_end(ap);
2206
53d55042
SK
2207 /* Be kind to init(8). */
2208 sleep(10);
70bedfa1 2209 exit(EXIT_FAILURE);
6dbe3af9 2210}
bde20e1b
WF
2211
2212static void log_warn(const char *fmt, ...)
2213{
2214 va_list ap;
2215
2216 va_start(ap, fmt);
2217 dolog(LOG_WARNING, fmt, ap);
2218 va_end(ap);
2219}
729def03 2220
0f283438 2221static void print_addr(sa_family_t family, void *addr)
2b945eda 2222{
0f283438
KZ
2223 char buff[INET6_ADDRSTRLEN + 1];
2224
2225 inet_ntop(family, addr, buff, sizeof(buff));
2226 printf("%s", buff);
2227}
2228
2229/*
2230 * Prints IP for the specified interface (@iface), if the interface is not
2231 * specified then prints the "best" one (UP, RUNNING, non-LOOPBACK). If not
2232 * found the "best" interface then prints at least host IP.
2233 */
2234static void output_iface_ip(struct ifaddrs *addrs,
2235 const char *iface,
2236 sa_family_t family)
2237{
2238 struct ifaddrs *p;
2239 struct addrinfo hints, *info = NULL;
2240 char *host = NULL;
2241 void *addr = NULL;
2242
2243 if (!addrs)
2b945eda
KZ
2244 return;
2245
0f283438 2246 for (p = addrs; p; p = p->ifa_next) {
2b945eda 2247
0f283438
KZ
2248 if (!p->ifa_name ||
2249 !p->ifa_addr ||
2250 p->ifa_addr->sa_family != family)
2251 continue;
2252
2253 if (iface) {
2254 /* Filter out by interface name */
2255 if (strcmp(p->ifa_name, iface) != 0)
2256 continue;
2257 } else {
2258 /* Select the "best" interface */
2259 if ((p->ifa_flags & IFF_LOOPBACK) ||
2260 !(p->ifa_flags & IFF_UP) ||
2261 !(p->ifa_flags & IFF_RUNNING))
2262 continue;
2263 }
2b945eda 2264
0f283438
KZ
2265 addr = NULL;
2266 switch (p->ifa_addr->sa_family) {
2b945eda 2267 case AF_INET:
0f283438 2268 addr = &((struct sockaddr_in *) p->ifa_addr)->sin_addr;
2b945eda
KZ
2269 break;
2270 case AF_INET6:
0f283438 2271 addr = &((struct sockaddr_in6 *) p->ifa_addr)->sin6_addr;
2b945eda
KZ
2272 break;
2273 }
0f283438 2274
2b945eda 2275 if (addr) {
0f283438
KZ
2276 print_addr(family, addr);
2277 return;
2b945eda 2278 }
0f283438 2279 }
2b945eda 2280
0f283438
KZ
2281 if (iface)
2282 return;
2b945eda 2283
0f283438 2284 /* Hmm.. not found the best interface, print host IP at least */
2b945eda
KZ
2285 memset(&hints, 0, sizeof(hints));
2286 hints.ai_family = family;
2287 if (family == AF_INET6)
2288 hints.ai_flags = AI_V4MAPPED;
2289
2448f336
KZ
2290 host = xgethostname();
2291 if (host && getaddrinfo(host, NULL, &hints, &info) == 0 && info) {
2b945eda
KZ
2292 switch (info->ai_family) {
2293 case AF_INET:
2294 addr = &((struct sockaddr_in *) info->ai_addr)->sin_addr;
2295 break;
2296 case AF_INET6:
2297 addr = &((struct sockaddr_in6 *) info->ai_addr)->sin6_addr;
2298 break;
2299 }
0f283438
KZ
2300 if (addr)
2301 print_addr(family, addr);
2b945eda
KZ
2302
2303 freeaddrinfo(info);
2304 }
2448f336 2305 free(host);
2b945eda
KZ
2306}
2307
2308/*
2309 * parses \x{argument}, if not argument specified then returns NULL, the @fd
2310 * has to point to one char after the sequence (it means '{').
2311 */
2312static char *get_escape_argument(FILE *fd, char *buf, size_t bufsz)
2313{
2314 size_t i = 0;
2315 int c = fgetc(fd);
2316
2317 if (c == EOF || (unsigned char) c != '{') {
2318 ungetc(c, fd);
2319 return NULL;
2320 }
2321
2322 do {
2323 c = fgetc(fd);
2324 if (c == EOF)
2325 return NULL;
2326 if ((unsigned char) c != '}' && i < bufsz - 1)
2327 buf[i++] = (unsigned char) c;
2328
2329 } while ((unsigned char) c != '}');
2330
2331 buf[i] = '\0';
2332 return buf;
2333}
2334
729def03 2335static void output_special_char(unsigned char c, struct options *op,
2b945eda 2336 struct termios *tp, FILE *fp)
729def03
WF
2337{
2338 struct utsname uts;
763d7a87 2339
729def03 2340 switch (c) {
583627ef 2341 case 'e':
d689166b
KZ
2342 {
2343 char escname[UL_COLORNAME_MAXSZ];
2344
2345 if (get_escape_argument(fp, escname, sizeof(escname))) {
2346 const char *esc = color_sequence_from_colorname(escname);
2347 if (esc)
2348 fputs(esc, stdout);
2349 } else
2350 fputs("\033", stdout);
583627ef 2351 break;
d689166b 2352 }
729def03 2353 case 's':
10e8d7a3 2354 uname(&uts);
763d7a87 2355 printf("%s", uts.sysname);
729def03
WF
2356 break;
2357 case 'n':
10e8d7a3 2358 uname(&uts);
763d7a87 2359 printf("%s", uts.nodename);
729def03
WF
2360 break;
2361 case 'r':
10e8d7a3 2362 uname(&uts);
763d7a87 2363 printf("%s", uts.release);
729def03
WF
2364 break;
2365 case 'v':
10e8d7a3 2366 uname(&uts);
763d7a87 2367 printf("%s", uts.version);
729def03
WF
2368 break;
2369 case 'm':
10e8d7a3 2370 uname(&uts);
763d7a87 2371 printf("%s", uts.machine);
729def03
WF
2372 break;
2373 case 'o':
2374 {
2448f336
KZ
2375 char *dom = xgetdomainname();
2376
2377 fputs(dom ? dom : "unknown_domain", stdout);
2378 free(dom);
729def03
WF
2379 break;
2380 }
2381 case 'O':
2382 {
2448f336
KZ
2383 char *dom = NULL;
2384 char *host = xgethostname();
729def03
WF
2385 struct addrinfo hints, *info = NULL;
2386
2387 memset(&hints, 0, sizeof(hints));
2388 hints.ai_flags = AI_CANONNAME;
2389
2448f336 2390 if (host && getaddrinfo(host, NULL, &hints, &info) == 0 && info) {
729def03 2391 char *canon;
2448f336 2392
729def03
WF
2393 if (info->ai_canonname &&
2394 (canon = strchr(info->ai_canonname, '.')))
2395 dom = canon + 1;
729def03 2396 }
2448f336
KZ
2397 fputs(dom ? dom : "unknown_domain", stdout);
2398 if (info)
2399 freeaddrinfo(info);
2400 free(host);
729def03
WF
2401 break;
2402 }
2403 case 'd':
2404 case 't':
2405 {
2406 time_t now;
2407 struct tm *tm;
2408
763d7a87 2409 time(&now);
729def03
WF
2410 tm = localtime(&now);
2411
d9201203
KZ
2412 if (!tm)
2413 break;
2414
729def03 2415 if (c == 'd') /* ISO 8601 */
763d7a87 2416 printf("%s %s %d %d",
729def03
WF
2417 nl_langinfo(ABDAY_1 + tm->tm_wday),
2418 nl_langinfo(ABMON_1 + tm->tm_mon),
2419 tm->tm_mday,
2420 tm->tm_year < 70 ? tm->tm_year + 2000 :
2421 tm->tm_year + 1900);
2422 else
763d7a87 2423 printf("%02d:%02d:%02d",
729def03
WF
2424 tm->tm_hour, tm->tm_min, tm->tm_sec);
2425 break;
2426 }
2427 case 'l':
763d7a87 2428 printf ("%s", op->tty);
729def03
WF
2429 break;
2430 case 'b':
2431 {
2432 const speed_t speed = cfgetispeed(tp);
2433 int i;
2434
2435 for (i = 0; speedtab[i].speed; i++) {
2436 if (speedtab[i].code == speed) {
2437 printf("%ld", speedtab[i].speed);
2438 break;
2439 }
2440 }
2441 break;
2442 }
b34f097e
KZ
2443 case 'S':
2444 {
2445 char *var = NULL, varname[64];
2446
1132e5aa
KZ
2447 /* \S{varname} */
2448 if (get_escape_argument(fp, varname, sizeof(varname))) {
b34f097e 2449 var = read_os_release(op, varname);
1132e5aa
KZ
2450 if (var) {
2451 if (strcmp(varname, "ANSI_COLOR") == 0)
2452 printf("\033[%sm", var);
2453 else
2454 fputs(var, stdout);
2455 }
2456 /* \S */
2457 } else if ((var = read_os_release(op, "PRETTY_NAME"))) {
2458 fputs(var, stdout);
2459
2460 /* \S and PRETTY_NAME not found */
2461 } else {
10e8d7a3 2462 uname(&uts);
1132e5aa 2463 fputs(uts.sysname, stdout);
b34f097e
KZ
2464 }
2465 break;
2466 }
729def03
WF
2467 case 'u':
2468 case 'U':
2469 {
2470 int users = 0;
2471 struct utmp *ut;
2472 setutent();
2473 while ((ut = getutent()))
2474 if (ut->ut_type == USER_PROCESS)
2475 users++;
2476 endutent();
729def03 2477 if (c == 'U')
9e531400
BS
2478 printf(P_("%d user", "%d users", users), users);
2479 else
2480 printf ("%d ", users);
729def03
WF
2481 break;
2482 }
2b945eda
KZ
2483 case '4':
2484 case '6':
2485 {
2486 sa_family_t family = c == '4' ? AF_INET : AF_INET6;
0f283438 2487 struct ifaddrs *addrs = NULL;
2b945eda
KZ
2488 char iface[128];
2489
e36deb64
SW
2490#ifdef AGETTY_RELOAD
2491 open_netlink();
2492#endif
2493
0f283438
KZ
2494 if (getifaddrs(&addrs))
2495 break;
2496
2497 if (get_escape_argument(fp, iface, sizeof(iface)))
2b945eda 2498 output_iface_ip(addrs, iface, family);
0f283438
KZ
2499 else
2500 output_iface_ip(addrs, NULL, family);
2501
2502 freeifaddrs(addrs);
2b945eda
KZ
2503 break;
2504 }
729def03 2505 default:
763d7a87 2506 putchar(c);
729def03
WF
2507 break;
2508 }
2509}
2510
2511static void init_special_char(char* arg, struct options *op)
2512{
2513 char ch, *p, *q;
2514 int i;
2515
ca8e91a4
KZ
2516 op->initstring = malloc(strlen(arg) + 1);
2517 if (!op->initstring)
2518 log_err(_("failed to allocate memory: %m"));
729def03
WF
2519
2520 /*
2521 * Copy optarg into op->initstring decoding \ddd octal
2522 * codes into chars.
2523 */
2524 q = op->initstring;
2525 p = arg;
2526 while (*p) {
2527 /* The \\ is converted to \ */
2528 if (*p == '\\') {
2529 p++;
2530 if (*p == '\\') {
2531 ch = '\\';
2532 p++;
2533 } else {
2534 /* Handle \000 - \177. */
2535 ch = 0;
2536 for (i = 1; i <= 3; i++) {
2537 if (*p >= '0' && *p <= '7') {
2538 ch <<= 3;
2539 ch += *p - '0';
2540 p++;
2541 } else {
2542 break;
2543 }
2544 }
2545 }
2546 *q++ = ch;
2547 } else
2548 *q++ = *p++;
2549 }
2550 *q = '\0';
2551}
eb8e1f9f 2552
a694957a
KZ
2553/*
2554 * Appends @str to @dest and if @dest is not empty then use use @sep as a
2555 * separator. The maximal final length of the @dest is @len.
2556 *
2557 * Returns the final @dest length or -1 in case of error.
2558 */
2559static ssize_t append(char *dest, size_t len, const char *sep, const char *src)
2560{
2561 size_t dsz = 0, ssz = 0, sz;
2562 char *p;
2563
2564 if (!dest || !len || !src)
2565 return -1;
2566
2567 if (*dest)
2568 dsz = strlen(dest);
2569 if (dsz && sep)
2570 ssz = strlen(sep);
2571 sz = strlen(src);
2572
2573 if (dsz + ssz + sz + 1 > len)
2574 return -1;
2575
2576 p = dest + dsz;
2577 if (ssz) {
2578 memcpy(p, sep, ssz);
2579 p += ssz;
2580 }
2581 memcpy(p, src, sz);
2582 *(p + sz) = '\0';
2583
2584 return dsz + ssz + sz;
2585}
9aeb66dc 2586
eb8e1f9f
WF
2587/*
2588 * Do not allow the user to pass an option as a user name
2589 * To be more safe: Use `--' to make sure the rest is
2590 * interpreted as non-options by the program, if it supports it.
2591 */
9aeb66dc 2592static void check_username(const char* nm)
eb8e1f9f
WF
2593{
2594 const char *p = nm;
2595 if (!nm)
2596 goto err;
763d7a87 2597 if (strlen(nm) > 42)
eb8e1f9f 2598 goto err;
763d7a87 2599 while (isspace(*p))
eb8e1f9f
WF
2600 p++;
2601 if (*p == '-')
2602 goto err;
2603 return;
2604err:
2605 errno = EPERM;
8c219bf4 2606 log_err(_("checkname failed: %m"));
eb8e1f9f
WF
2607}
2608
6443dd43
SW
2609static void reload_agettys(void)
2610{
2611#ifdef AGETTY_RELOAD
c9f5ec0f
KZ
2612 int fd = open(AGETTY_RELOAD_FILENAME, O_CREAT|O_CLOEXEC|O_WRONLY,
2613 S_IRUSR|S_IWUSR);
6443dd43 2614 if (fd < 0)
54fefa07 2615 err(EXIT_FAILURE, _("cannot open %s"), AGETTY_RELOAD_FILENAME);
6443dd43 2616
90d5285d 2617 if (futimens(fd, NULL) < 0 || close(fd) < 0)
54fefa07 2618 err(EXIT_FAILURE, _("cannot touch file %s"),
6443dd43
SW
2619 AGETTY_RELOAD_FILENAME);
2620#else
2621 /* very unusual */
1d231190 2622 errx(EXIT_FAILURE, _("--reload is unsupported on your system"));
6443dd43
SW
2623#endif
2624}