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