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