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