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