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