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