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