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