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