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.
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>
11 * This program is freely distributable.
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
31 #include <sys/socket.h>
35 #include <arpa/inet.h>
39 #include <sys/utsname.h>
44 #include "pathnames.h"
49 #include "color-names.h"
52 #include "logindefs.h"
54 #ifdef USE_PLYMOUTH_SUPPORT
55 # include "plymouth-ctrl.h"
58 #ifdef HAVE_SYS_PARAM_H
59 # include <sys/param.h>
66 #if defined(__FreeBSD_kernel__)
71 # ifdef HAVE_LIBUTIL_H
79 # ifndef DEFAULT_VCTERM
80 # define DEFAULT_VCTERM "linux"
82 # if defined (__s390__) || defined (__s390x__)
83 # define DEFAULT_TTYS0 "dumb"
84 # define DEFAULT_TTY32 "ibm327x"
85 # define DEFAULT_TTYS1 "vt220"
87 # ifndef DEFAULT_STERM
88 # define DEFAULT_STERM "vt102"
90 #elif defined(__GNU__)
92 # ifndef DEFAULT_VCTERM
93 # define DEFAULT_VCTERM "hurd"
95 # ifndef DEFAULT_STERM
96 # define DEFAULT_STERM "vt102"
99 # ifndef DEFAULT_VCTERM
100 # define DEFAULT_VCTERM "vt100"
102 # ifndef DEFAULT_STERM
103 # define DEFAULT_STERM "vt100"
107 #ifdef __FreeBSD_kernel__
111 /* If USE_SYSLOG is undefined all diagnostics go to /dev/console. */
117 * Some heuristics to find out what environment we are in: if it is not
118 * System V, assume it is SunOS 4. The LOGIN_PROCESS is defined in System V
119 * utmp.h, which will select System V style getty.
126 * Things you may want to modify.
128 * If ISSUE_SUPPORT is not defined, agetty will never display the contents of
129 * the /etc/issue file. You will not want to spit out large "issue" files at
130 * the wrong baud rate. Relevant for System V only.
132 * You may disagree with the default line-editing etc. characters defined
133 * below. Note, however, that DEL cannot be used for interrupt generation
134 * and for line editing at the same time.
137 /* Displayed before the login prompt. */
139 # define ISSUE_SUPPORT
140 # if defined(HAVE_SCANDIRAT) && defined(HAVE_OPENAT)
142 # include "fileutils.h"
143 # define ISSUEDIR_SUPPORT
144 # define ISSUEDIR_EXT ".issue"
145 # define ISSUEDIR_EXTSIZ (sizeof(ISSUEDIR_EXT) - 1)
150 #define LOGIN_PROMPT "login: "
152 /* Numbers of args for login(1) */
153 #define LOGIN_ARGV_MAX 16
159 # include <sys/inotify.h>
160 # include <linux/netlink.h>
161 # include <linux/rtnetlink.h>
162 # define AGETTY_RELOAD_FILENAME "/run/agetty.reload" /* trigger file */
163 # define AGETTY_RELOAD_FDNONE -2 /* uninitialized fd */
164 static int inotify_fd
= AGETTY_RELOAD_FDNONE
;
165 static int netlink_fd
= AGETTY_RELOAD_FDNONE
;
166 static uint32_t netlink_groups
;
177 unsigned int do_tcsetattr
: 1,
182 * When multiple baud rates are specified on the command line, the first one
183 * we will try is the first one specified.
185 #define FIRST_SPEED 0
187 /* Storage for command-line options. */
188 #define MAX_SPEED 10 /* max. nr. of baud rates */
191 int flags
; /* toggle switches, see below */
192 unsigned int timeout
; /* time-out period */
193 char *autolog
; /* login the user automatically */
194 char *chdir
; /* Chdir before the login */
195 char *chroot
; /* Chroot before the login */
196 char *login
; /* login program */
197 char *logopt
; /* options for login program */
198 const char *tty
; /* name of tty */
199 const char *vcline
; /* line of virtual console */
200 char *term
; /* terminal type */
201 char *initstring
; /* modem init string */
202 char *issue
; /* alternative issue file or directory */
203 char *erasechars
; /* string with erase chars */
204 char *killchars
; /* string with kill chars */
205 char *osrelease
; /* /etc/os-release data */
206 unsigned int delay
; /* Sleep seconds before prompt */
207 int nice
; /* Run login with this priority */
208 int numspeed
; /* number of baud rates to try */
209 int clocal
; /* CLOCAL_MODE_* */
210 int kbmode
; /* Keyboard mode if virtual console */
211 int tty_is_stdin
; /* is the tty the standard input stream */
212 speed_t speeds
[MAX_SPEED
]; /* baud rates to be tried */
216 CLOCAL_MODE_AUTO
= 0,
221 #define F_PARSE (1<<0) /* process modem status messages */
222 #define F_ISSUE (1<<1) /* display /etc/issue or /etc/issue.d */
223 #define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */
225 #define F_INITSTRING (1<<4) /* initstring is set */
226 #define F_WAITCRLF (1<<5) /* wait for CR or LF */
228 #define F_NOPROMPT (1<<7) /* do not ask for login name! */
229 #define F_LCUC (1<<8) /* support for *LCUC stty modes */
230 #define F_KEEPSPEED (1<<9) /* follow baud rate from kernel */
231 #define F_KEEPCFLAGS (1<<10) /* reuse c_cflags setup from kernel */
232 #define F_EIGHTBITS (1<<11) /* Assume 8bit-clean tty */
233 #define F_VCONSOLE (1<<12) /* This is a virtual console */
234 #define F_HANGUP (1<<13) /* Do call vhangup(2) */
235 #define F_UTF8 (1<<14) /* We can do UTF8 */
236 #define F_LOGINPAUSE (1<<15) /* Wait for any key before dropping login prompt */
237 #define F_NOCLEAR (1<<16) /* Do not clear the screen before prompting */
238 #define F_NONL (1<<17) /* No newline before issue */
239 #define F_NOHOSTNAME (1<<18) /* Do not show the hostname */
240 #define F_LONGHNAME (1<<19) /* Show Full qualified hostname */
241 #define F_NOHINTS (1<<20) /* Don't print hints */
242 #define F_REMOTE (1<<21) /* Add '-h fakehost' to login(1) command line */
244 #define serial_tty_option(opt, flag) \
245 (((opt)->flags & (F_VCONSOLE|(flag))) == (flag))
252 static const struct Speedtab speedtab
[] = {
324 static void init_special_char(char* arg
, struct options
*op
);
325 static void parse_args(int argc
, char **argv
, struct options
*op
);
326 static void parse_speeds(struct options
*op
, char *arg
);
327 static void update_utmp(struct options
*op
);
328 static void open_tty(const char *tty
, struct termios
*tp
, struct options
*op
);
329 static void termio_init(struct options
*op
, struct termios
*tp
);
330 static void reset_vc(const struct options
*op
, struct termios
*tp
, int canon
);
331 static void auto_baud(struct termios
*tp
);
332 static void list_speeds(void);
333 static void output_special_char (struct issue
*ie
, unsigned char c
, struct options
*op
,
334 struct termios
*tp
, FILE *fp
);
335 static void do_prompt(struct issue
*ie
, struct options
*op
, struct termios
*tp
);
336 static void next_speed(struct options
*op
, struct termios
*tp
);
337 static char *get_logname(struct issue
*ie
, struct options
*op
,
338 struct termios
*tp
, struct chardata
*cp
);
339 static void termio_final(struct options
*op
,
340 struct termios
*tp
, struct chardata
*cp
);
341 static int caps_lock(char *s
);
342 static speed_t
bcode(char *s
);
343 static void usage(void) __attribute__((__noreturn__
));
344 static void exit_slowly(int code
) __attribute__((__noreturn__
));
345 static void log_err(const char *, ...) __attribute__((__noreturn__
))
346 __attribute__((__format__(printf
, 1, 2)));
347 static void log_warn (const char *, ...)
348 __attribute__((__format__(printf
, 1, 2)));
349 static ssize_t
append(char *dest
, size_t len
, const char *sep
, const char *src
);
350 static void check_username (const char* nm
);
351 static void login_options_to_argv(char *argv
[], int *argc
, char *str
, char *username
);
352 static void reload_agettys(void);
353 static void print_issue_file(struct issue
*ie
, struct options
*op
, struct termios
*tp
);
354 static void eval_issue_file(struct issue
*ie
, struct options
*op
, struct termios
*tp
);
355 static void show_issue(struct options
*op
);
358 /* Fake hostname for ut_host specified on command line. */
359 static char *fakehost
;
362 # include "closestream.h"
363 # ifndef DEBUG_OUTPUT
364 # define DEBUG_OUTPUT "/dev/tty10"
366 # define debug(s) do { fprintf(dbf,s); fflush(dbf); } while (0)
369 # define debug(s) do { ; } while (0)
372 int main(int argc
, char **argv
)
374 char *username
= NULL
; /* login name, given to /bin/login */
375 struct chardata chardata
; /* will be set by get_logname() */
376 struct termios termios
; /* terminal mode bits */
377 struct options options
= {
378 .flags
= F_ISSUE
, /* show /etc/issue (SYSV_STYLE) */
379 .login
= _PATH_LOGIN
, /* default login program */
380 .tty
= "tty1" /* default tty line */
382 struct issue issue
= {
385 char *login_argv
[LOGIN_ARGV_MAX
+ 1];
387 struct sigaction sa
, sa_hup
, sa_quit
, sa_int
;
390 setlocale(LC_ALL
, "");
391 bindtextdomain(PACKAGE
, LOCALEDIR
);
394 /* In case vhangup(2) has to called */
395 sa
.sa_handler
= SIG_IGN
;
396 sa
.sa_flags
= SA_RESTART
;
397 sigemptyset (&sa
.sa_mask
);
398 sigaction(SIGHUP
, &sa
, &sa_hup
);
399 sigaction(SIGQUIT
, &sa
, &sa_quit
);
400 sigaction(SIGINT
, &sa
, &sa_int
);
406 dbf
= fopen(DEBUG_OUTPUT
, "w");
407 for (i
= 1; i
< argc
; i
++) {
414 #endif /* DEBUGGING */
416 /* Parse command-line arguments. */
417 parse_args(argc
, argv
, &options
);
419 login_argv
[login_argc
++] = options
.login
; /* set login program name */
421 /* Update the utmp file. */
423 update_utmp(&options
);
426 sleep(options
.delay
);
428 debug("calling open_tty\n");
430 /* Open the tty as standard { input, output, error }. */
431 open_tty(options
.tty
, &termios
, &options
);
433 /* Unmask SIGHUP if inherited */
435 sigaddset(&set
, SIGHUP
);
436 sigprocmask(SIG_UNBLOCK
, &set
, NULL
);
437 sigaction(SIGHUP
, &sa_hup
, NULL
);
439 tcsetpgrp(STDIN_FILENO
, getpid());
441 /* Default is to follow the current line speed and then default to 9600 */
442 if ((options
.flags
& F_VCONSOLE
) == 0 && options
.numspeed
== 0) {
443 options
.speeds
[options
.numspeed
++] = bcode("9600");
444 options
.flags
|= F_KEEPSPEED
;
447 /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
448 debug("calling termio_init\n");
449 termio_init(&options
, &termios
);
451 /* Write the modem init string and DO NOT flush the buffers. */
452 if (options
.flags
& F_INITSTRING
&&
453 options
.initstring
&& *options
.initstring
!= '\0') {
454 debug("writing init string\n");
455 write_all(STDOUT_FILENO
, options
.initstring
,
456 strlen(options
.initstring
));
459 if (options
.flags
& F_VCONSOLE
|| options
.clocal
!= CLOCAL_MODE_ALWAYS
)
460 /* Go to blocking mode unless -L is specified, this change
461 * affects stdout, stdin and stderr as all the file descriptors
462 * are created by dup(). */
463 fcntl(STDOUT_FILENO
, F_SETFL
,
464 fcntl(STDOUT_FILENO
, F_GETFL
, 0) & ~O_NONBLOCK
);
466 /* Optionally detect the baud rate from the modem status message. */
467 debug("before autobaud\n");
468 if (serial_tty_option(&options
, F_PARSE
))
471 /* Set the optional timer. */
473 alarm(options
.timeout
);
475 /* Optionally wait for CR or LF before writing /etc/issue */
476 if (serial_tty_option(&options
, F_WAITCRLF
)) {
479 debug("waiting for cr-lf\n");
480 while (read(STDIN_FILENO
, &ch
, 1) == 1) {
481 /* Strip "parity bit". */
484 fprintf(dbf
, "read %c\n", ch
);
486 if (ch
== '\n' || ch
== '\r')
491 INIT_CHARDATA(&chardata
);
493 if (options
.autolog
) {
494 debug("doing auto login\n");
495 username
= options
.autolog
;
498 if (options
.flags
& F_NOPROMPT
) { /* --skip-login */
499 eval_issue_file(&issue
, &options
, &termios
);
500 print_issue_file(&issue
, &options
, &termios
);
502 } else { /* regular (auto)login */
503 if ((options
.flags
& F_NOHOSTNAME
) == 0 &&
504 getlogindefs_bool("LOGIN_PLAIN_PROMPT", 0) == 1)
505 /* /etc/login.defs enbles --nohostname too */
506 options
.flags
|= F_NOHOSTNAME
;
508 if (options
.autolog
) {
509 /* Autologin prompt */
510 eval_issue_file(&issue
, &options
, &termios
);
511 do_prompt(&issue
, &options
, &termios
);
512 printf(_("%s%s (automatic login)\n"), LOGIN_PROMPT
,
515 /* Read the login name. */
516 debug("reading login name\n");
518 get_logname(&issue
, &options
, &termios
, &chardata
)) == NULL
)
519 if ((options
.flags
& F_VCONSOLE
) == 0 && options
.numspeed
)
520 next_speed(&options
, &termios
);
528 /* Finalize the termios settings. */
529 if ((options
.flags
& F_VCONSOLE
) == 0)
530 termio_final(&options
, &termios
, &chardata
);
532 reset_vc(&options
, &termios
, 1);
534 /* Now the newline character should be properly written. */
535 write_all(STDOUT_FILENO
, "\r\n", 2);
537 sigaction(SIGQUIT
, &sa_quit
, NULL
);
538 sigaction(SIGINT
, &sa_int
, NULL
);
541 check_username(username
);
543 if (options
.logopt
) {
545 * The --login-options completely overwrites the default
546 * way how agetty composes login(1) command line.
548 login_options_to_argv(login_argv
, &login_argc
,
549 options
.logopt
, username
);
551 if (options
.flags
& F_REMOTE
) {
553 login_argv
[login_argc
++] = "-h";
554 login_argv
[login_argc
++] = fakehost
;
555 } else if (options
.flags
& F_NOHOSTNAME
)
556 login_argv
[login_argc
++] = "-H";
560 login_argv
[login_argc
++] = "-f";
561 login_argv
[login_argc
++] = "--";
562 login_argv
[login_argc
++] = username
;
566 login_argv
[login_argc
] = NULL
; /* last login argv */
568 if (options
.chroot
&& chroot(options
.chroot
) < 0)
569 log_err(_("%s: can't change root directory %s: %m"),
570 options
.tty
, options
.chroot
);
571 if (options
.chdir
&& chdir(options
.chdir
) < 0)
572 log_err(_("%s: can't change working directory %s: %m"),
573 options
.tty
, options
.chdir
);
574 if (options
.nice
&& nice(options
.nice
) < 0)
575 log_warn(_("%s: can't change process priority: %m"),
578 free(options
.osrelease
);
580 if (close_stream(dbf
) != 0)
581 log_err("write failed: %s", DEBUG_OUTPUT
);
584 /* Let the login program take care of password validation. */
585 execv(options
.login
, login_argv
);
586 log_err(_("%s: can't exec %s: %m"), options
.tty
, login_argv
[0]);
590 * Returns : @str if \u not found
591 * : @username if @str equal to "\u"
592 * : newly allocated string if \u mixed with something other
594 static char *replace_u(char *str
, char *username
)
596 char *entry
= NULL
, *p
= str
;
597 size_t usz
= username
? strlen(username
) : 0;
601 char *tp
, *old
= entry
;
603 if (memcmp(p
, "\\u", 2) != 0) {
605 continue; /* no \u */
609 if (p
== str
&& sz
== 2) {
610 /* 'str' contains only '\u' */
615 tp
= entry
= malloc(sz
+ usz
);
617 log_err(_("failed to allocate memory: %m"));
620 /* copy chars before \u */
621 tp
= mempcpy(tp
, str
, p
- str
);
624 tp
= mempcpy(tp
, username
, usz
);
627 /* copy chars after \u + \0 */
628 memcpy(tp
, p
+ 2, sz
- (p
- str
) - 1);
637 return entry
? entry
: str
;
640 static void login_options_to_argv(char *argv
[], int *argc
,
641 char *str
, char *username
)
646 while (str
&& isspace(*str
))
650 while (p
&& *p
&& i
< LOGIN_ARGV_MAX
) {
653 while (isspace(*++p
))
656 argv
[i
++] = replace_u(str
, username
);
662 if (str
&& *str
&& i
< LOGIN_ARGV_MAX
)
663 argv
[i
++] = replace_u(str
, username
);
667 static void output_version(void)
669 static const char *features
[] = {
682 #ifdef ISSUEDIR_SUPPORT
688 #ifdef USE_PLYMOUTH_SUPPORT
704 printf( _("%s from %s"), program_invocation_short_name
, PACKAGE_STRING
);
706 for (i
= 0; features
[i
]; i
++) {
709 printf("%s", features
[i
]);
711 fputs(")\n", stdout
);
714 #define is_speed(str) (strlen((str)) == strspn((str), "0123456789,"))
716 /* Parse command-line arguments. */
717 static void parse_args(int argc
, char **argv
, struct options
*op
)
720 int opt_show_issue
= 0;
723 VERSION_OPTION
= CHAR_MAX
+ 1,
734 const struct option longopts
[] = {
735 { "8bits", no_argument
, NULL
, '8' },
736 { "autologin", required_argument
, NULL
, 'a' },
737 { "noreset", no_argument
, NULL
, 'c' },
738 { "chdir", required_argument
, NULL
, 'C' },
739 { "delay", required_argument
, NULL
, 'd' },
740 { "remote", no_argument
, NULL
, 'E' },
741 { "issue-file", required_argument
, NULL
, 'f' },
742 { "show-issue", no_argument
, NULL
, ISSUE_SHOW_OPTION
},
743 { "flow-control", no_argument
, NULL
, 'h' },
744 { "host", required_argument
, NULL
, 'H' },
745 { "noissue", no_argument
, NULL
, 'i' },
746 { "init-string", required_argument
, NULL
, 'I' },
747 { "noclear", no_argument
, NULL
, 'J' },
748 { "login-program", required_argument
, NULL
, 'l' },
749 { "local-line", optional_argument
, NULL
, 'L' },
750 { "extract-baud", no_argument
, NULL
, 'm' },
751 { "list-speeds", no_argument
, NULL
, LIST_SPEEDS_OPTION
},
752 { "skip-login", no_argument
, NULL
, 'n' },
753 { "nonewline", no_argument
, NULL
, 'N' },
754 { "login-options", required_argument
, NULL
, 'o' },
755 { "login-pause", no_argument
, NULL
, 'p' },
756 { "nice", required_argument
, NULL
, 'P' },
757 { "chroot", required_argument
, NULL
, 'r' },
758 { "hangup", no_argument
, NULL
, 'R' },
759 { "keep-baud", no_argument
, NULL
, 's' },
760 { "timeout", required_argument
, NULL
, 't' },
761 { "detect-case", no_argument
, NULL
, 'U' },
762 { "wait-cr", no_argument
, NULL
, 'w' },
763 { "nohints", no_argument
, NULL
, NOHINTS_OPTION
},
764 { "nohostname", no_argument
, NULL
, NOHOSTNAME_OPTION
},
765 { "long-hostname", no_argument
, NULL
, LONGHOSTNAME_OPTION
},
766 { "reload", no_argument
, NULL
, RELOAD_OPTION
},
767 { "version", no_argument
, NULL
, VERSION_OPTION
},
768 { "help", no_argument
, NULL
, HELP_OPTION
},
769 { "erase-chars", required_argument
, NULL
, ERASE_CHARS_OPTION
},
770 { "kill-chars", required_argument
, NULL
, KILL_CHARS_OPTION
},
774 while ((c
= getopt_long(argc
, argv
,
775 "8a:cC:d:Ef:hH:iI:Jl:L::mnNo:pP:r:Rst:Uw", longopts
,
779 op
->flags
|= F_EIGHTBITS
;
782 op
->autolog
= optarg
;
785 op
->flags
|= F_KEEPCFLAGS
;
791 op
->delay
= strtou32_or_err(optarg
, _("invalid delay argument"));
794 op
->flags
|= F_REMOTE
;
800 op
->flags
|= F_RTSCTS
;
806 op
->flags
&= ~F_ISSUE
;
809 init_special_char(optarg
, op
);
810 op
->flags
|= F_INITSTRING
;
813 op
->flags
|= F_NOCLEAR
;
819 /* -L and -L=always have the same meaning */
820 op
->clocal
= CLOCAL_MODE_ALWAYS
;
822 if (strcmp(optarg
, "=always") == 0)
823 op
->clocal
= CLOCAL_MODE_ALWAYS
;
824 else if (strcmp(optarg
, "=never") == 0)
825 op
->clocal
= CLOCAL_MODE_NEVER
;
826 else if (strcmp(optarg
, "=auto") == 0)
827 op
->clocal
= CLOCAL_MODE_AUTO
;
829 log_err(_("invalid argument of --local-line"));
833 op
->flags
|= F_PARSE
;
836 op
->flags
|= F_NOPROMPT
;
845 op
->flags
|= F_LOGINPAUSE
;
848 op
->nice
= strtos32_or_err(optarg
, _("invalid nice argument"));
854 op
->flags
|= F_HANGUP
;
857 op
->flags
|= F_KEEPSPEED
;
860 op
->timeout
= strtou32_or_err(optarg
, _("invalid timeout argument"));
866 op
->flags
|= F_WAITCRLF
;
869 op
->flags
|= F_NOHINTS
;
871 case NOHOSTNAME_OPTION
:
872 op
->flags
|= F_NOHOSTNAME
;
874 case LONGHOSTNAME_OPTION
:
875 op
->flags
|= F_LONGHNAME
;
877 case ERASE_CHARS_OPTION
:
878 op
->erasechars
= optarg
;
880 case KILL_CHARS_OPTION
:
881 op
->killchars
= optarg
;
886 case LIST_SPEEDS_OPTION
:
889 case ISSUE_SHOW_OPTION
:
898 errtryhelp(EXIT_FAILURE
);
902 if (opt_show_issue
) {
907 debug("after getopt loop\n");
909 if (argc
< optind
+ 1) {
910 log_warn(_("not enough arguments"));
911 errx(EXIT_FAILURE
, _("not enough arguments"));
914 /* Accept "tty", "baudrate tty", and "tty baudrate". */
915 if (is_speed(argv
[optind
])) {
916 /* Assume BSD style speed. */
917 parse_speeds(op
, argv
[optind
++]);
918 if (argc
< optind
+ 1) {
919 log_warn(_("not enough arguments"));
920 errx(EXIT_FAILURE
, _("not enough arguments"));
922 op
->tty
= argv
[optind
++];
924 op
->tty
= argv
[optind
++];
926 char *v
= argv
[optind
];
934 /* resolve the tty path in case it was provided as stdin */
935 if (strcmp(op
->tty
, "-") == 0) {
936 op
->tty_is_stdin
= 1;
937 int fd
= get_terminal_name(NULL
, &op
->tty
, NULL
);
939 log_warn(_("could not get terminal name: %d"), fd
);
943 /* On virtual console remember the line which is used for */
944 if (strncmp(op
->tty
, "tty", 3) == 0 &&
945 strspn(op
->tty
+ 3, "0123456789") == strlen(op
->tty
+3))
946 op
->vcline
= op
->tty
+3;
948 if (argc
> optind
&& argv
[optind
])
949 op
->term
= argv
[optind
];
951 debug("exiting parseargs\n");
954 /* Parse alternate baud rates. */
955 static void parse_speeds(struct options
*op
, char *arg
)
958 char *str
= strdup(arg
);
961 log_err(_("failed to allocate memory: %m"));
963 debug("entered parse_speeds:\n");
964 for (cp
= strtok(str
, ","); cp
!= NULL
; cp
= strtok((char *)0, ",")) {
965 if ((op
->speeds
[op
->numspeed
++] = bcode(cp
)) <= 0)
966 log_err(_("bad speed: %s"), cp
);
967 if (op
->numspeed
>= MAX_SPEED
)
968 log_err(_("too many alternate speeds"));
970 debug("exiting parsespeeds\n");
976 /* Update our utmp entry. */
977 static void update_utmp(struct options
*op
)
981 pid_t pid
= getpid();
982 pid_t sid
= getsid(0);
983 const char *vcline
= op
->vcline
;
984 const char *line
= op
->tty
;
988 * The utmp file holds miscellaneous information about things started by
989 * /sbin/init and other system-related events. Our purpose is to update
990 * the utmp entry for the current process, in particular the process type
991 * and the tty line we are listening to. Return successfully only if the
992 * utmp file can be opened for update, and if we are able to find our
993 * entry in the utmp file.
995 utmpxname(_PATH_UTMP
);
999 * Find my pid in utmp.
1001 * FIXME: Earlier (when was that?) code here tested only utp->ut_type !=
1002 * INIT_PROCESS, so maybe the >= here should be >.
1004 * FIXME: The present code is taken from login.c, so if this is changed,
1005 * maybe login has to be changed as well (is this true?).
1007 while ((utp
= getutxent()))
1008 if (utp
->ut_pid
== pid
1009 && utp
->ut_type
>= INIT_PROCESS
1010 && utp
->ut_type
<= DEAD_PROCESS
)
1014 memcpy(&ut
, utp
, sizeof(ut
));
1016 /* Some inits do not initialize utmp. */
1017 memset(&ut
, 0, sizeof(ut
));
1018 if (vcline
&& *vcline
)
1019 /* Standard virtual console devices */
1020 str2memcpy(ut
.ut_id
, vcline
, sizeof(ut
.ut_id
));
1022 size_t len
= strlen(line
);
1024 if (len
>= sizeof(ut
.ut_id
))
1025 ptr
= line
+ len
- sizeof(ut
.ut_id
);
1028 str2memcpy(ut
.ut_id
, ptr
, sizeof(ut
.ut_id
));
1032 str2memcpy(ut
.ut_user
, "LOGIN", sizeof(ut
.ut_user
));
1033 str2memcpy(ut
.ut_line
, line
, sizeof(ut
.ut_line
));
1035 str2memcpy(ut
.ut_host
, fakehost
, sizeof(ut
.ut_host
));
1037 ut
.ut_tv
.tv_sec
= t
;
1038 ut
.ut_type
= LOGIN_PROCESS
;
1040 ut
.ut_session
= sid
;
1045 updwtmpx(_PATH_WTMP
, &ut
);
1048 #endif /* SYSV_STYLE */
1050 /* Set up tty as stdin, stdout & stderr. */
1051 static void open_tty(const char *tty
, struct termios
*tp
, struct options
*op
)
1053 const pid_t pid
= getpid();
1059 /* Set up new standard input, unless we are given an already opened port. */
1061 if (!op
->tty_is_stdin
) {
1062 char buf
[PATH_MAX
+1];
1063 struct group
*gr
= NULL
;
1069 /* Use tty group if available */
1070 if ((gr
= getgrnam("tty")))
1073 len
= snprintf(buf
, sizeof(buf
), "/dev/%s", tty
);
1074 if (len
< 0 || (size_t)len
>= sizeof(buf
))
1075 log_err(_("/dev/%s: cannot open as standard input: %m"), tty
);
1077 /* Open the tty as standard input. */
1078 if ((fd
= open(buf
, O_RDWR
|O_NOCTTY
|O_NONBLOCK
, 0)) < 0)
1079 log_err(_("/dev/%s: cannot open as standard input: %m"), tty
);
1082 * There is always a race between this reset and the call to
1083 * vhangup() that s.o. can use to get access to your tty.
1084 * Linux login(1) will change tty permissions. Use root owner and group
1085 * with permission -rw------- for the period between getty and login.
1087 if (fchown(fd
, 0, gid
) || fchmod(fd
, (gid
? 0620 : 0600))) {
1089 log_warn("%s: %m", buf
);
1091 log_err("%s: %m", buf
);
1094 /* Sanity checks... */
1095 if (fstat(fd
, &st
) < 0)
1096 log_err("%s: %m", buf
);
1097 if ((st
.st_mode
& S_IFMT
) != S_IFCHR
)
1098 log_err(_("/dev/%s: not a character device"), tty
);
1100 log_err(_("/dev/%s: not a tty"), tty
);
1102 if (((tid
= tcgetsid(fd
)) < 0) || (pid
!= tid
)) {
1103 if (ioctl(fd
, TIOCSCTTY
, 1) == -1)
1104 log_warn(_("/dev/%s: cannot get controlling tty: %m"), tty
);
1107 close(STDIN_FILENO
);
1110 if (op
->flags
& F_HANGUP
) {
1112 if (ioctl(fd
, TIOCNOTTY
))
1113 debug("TIOCNOTTY ioctl failed\n");
1116 * Let's close all file descriptors before vhangup
1117 * https://lkml.org/lkml/2012/6/5/145
1120 close(STDOUT_FILENO
);
1121 close(STDERR_FILENO
);
1126 log_err(_("/dev/%s: vhangup() failed: %m"), tty
);
1131 if (open(buf
, O_RDWR
|O_NOCTTY
|O_NONBLOCK
, 0) != 0)
1132 log_err(_("/dev/%s: cannot open as standard input: %m"), tty
);
1134 if (((tid
= tcgetsid(STDIN_FILENO
)) < 0) || (pid
!= tid
)) {
1135 if (ioctl(STDIN_FILENO
, TIOCSCTTY
, 1) == -1)
1136 log_warn(_("/dev/%s: cannot get controlling tty: %m"), tty
);
1142 * Standard input should already be connected to an open port. Make
1143 * sure it is open for read/write.
1146 if ((fcntl(STDIN_FILENO
, F_GETFL
, 0) & O_RDWR
) != O_RDWR
)
1147 log_err(_("%s: not open for read/write"), tty
);
1151 if (tcsetpgrp(STDIN_FILENO
, pid
))
1152 log_warn(_("/dev/%s: cannot set process group: %m"), tty
);
1154 /* Get rid of the present outputs. */
1156 close(STDOUT_FILENO
);
1157 close(STDERR_FILENO
);
1161 /* Set up standard output and standard error file descriptors. */
1164 /* set up stdout and stderr */
1165 if (dup(STDIN_FILENO
) != 1 || dup(STDIN_FILENO
) != 2)
1166 log_err(_("%s: dup problem: %m"), tty
);
1168 /* make stdio unbuffered for slow modem lines */
1169 setvbuf(stdout
, NULL
, _IONBF
, 0);
1172 * The following ioctl will fail if stdin is not a tty, but also when
1173 * there is noise on the modem control lines. In the latter case, the
1174 * common course of action is (1) fix your cables (2) give the modem
1175 * more time to properly reset after hanging up.
1177 * SunOS users can achieve (2) by patching the SunOS kernel variable
1178 * "zsadtrlow" to a larger value; 5 seconds seems to be a good value.
1179 * http://www.sunmanagers.org/archives/1993/0574.html
1181 memset(tp
, 0, sizeof(struct termios
));
1182 if (tcgetattr(STDIN_FILENO
, tp
) < 0)
1183 log_err(_("%s: failed to get terminal attributes: %m"), tty
);
1185 #ifdef HAVE_GETTTYNAM
1187 struct ttyent
*ent
= getttynam(tty
);
1188 /* a bit nasty as it's never freed */
1189 if (ent
&& ent
->ty_type
) {
1190 op
->term
= strdup(ent
->ty_type
);
1192 log_err(_("failed to allocate memory: %m"));
1197 #if defined (__s390__) || defined (__s390x__)
1200 * Special terminal on first serial line on a S/390(x) which
1201 * is due legacy reasons a block terminal of type 3270 or
1202 * higher. Whereas the second serial line on a S/390(x) is
1203 * a real character terminal which is compatible with VT220.
1205 if (strcmp(op
->tty
, "ttyS0") == 0) /* linux/drivers/s390/char/con3215.c */
1206 op
->term
= DEFAULT_TTYS0
;
1207 else if (strncmp(op
->tty
, "3270/tty", 8) == 0) /* linux/drivers/s390/char/con3270.c */
1208 op
->term
= DEFAULT_TTY32
;
1209 else if (strcmp(op
->tty
, "ttyS1") == 0) /* linux/drivers/s390/char/sclp_vt220.c */
1210 op
->term
= DEFAULT_TTYS1
;
1214 #if defined(__FreeBSD_kernel__)
1219 * Detect if this is a virtual console or serial/modem line.
1220 * In case of a virtual console the ioctl KDGKBMODE succeeds
1221 * whereas on other lines it will fails.
1224 if (ioctl(STDIN_FILENO
, KDGKBMODE
, &op
->kbmode
) == 0)
1226 if (ioctl(STDIN_FILENO
, TIOCMGET
, &serial
) < 0 && (errno
== EINVAL
))
1229 op
->flags
|= F_VCONSOLE
;
1231 op
->term
= DEFAULT_VCTERM
;
1237 op
->term
= DEFAULT_STERM
;
1240 if (setenv("TERM", op
->term
, 1) != 0)
1241 log_err(_("failed to set the %s environment variable"), "TERM");
1244 /* Initialize termios settings. */
1245 static void termio_clear(int fd
)
1248 * Do not write a full reset (ESC c) because this destroys
1249 * the unicode mode again if the terminal was in unicode
1250 * mode. Also it clears the CONSOLE_MAGIC features which
1251 * are required for some languages/console-fonts.
1252 * Just put the cursor to the home position (ESC [ H),
1253 * erase everything below the cursor (ESC [ J), and set the
1254 * scrolling region to the full window (ESC [ r)
1256 write_all(fd
, "\033[r\033[H\033[J", 9);
1259 /* Initialize termios settings. */
1260 static void termio_init(struct options
*op
, struct termios
*tp
)
1262 speed_t ispeed
, ospeed
;
1264 #ifdef USE_PLYMOUTH_SUPPORT
1265 struct termios lock
;
1266 int i
= (plymouth_command(MAGIC_PING
) == 0) ? PLYMOUTH_TERMIOS_FLAGS_DELAY
: 0;
1268 plymouth_command(MAGIC_QUIT
);
1271 * Even with TTYReset=no it seems with systemd or plymouth
1272 * the termios flags become changed from under the first
1273 * agetty on a serial system console as the flags are locked.
1275 memset(&lock
, 0, sizeof(struct termios
));
1276 if (ioctl(STDIN_FILENO
, TIOCGLCKTRMIOS
, &lock
) < 0)
1278 if (!lock
.c_iflag
&& !lock
.c_oflag
&& !lock
.c_cflag
&& !lock
.c_lflag
)
1280 debug("termios locked\n");
1283 memset(&lock
, 0, sizeof(struct termios
));
1284 ioctl(STDIN_FILENO
, TIOCSLCKTRMIOS
, &lock
);
1287 if (op
->flags
& F_VCONSOLE
) {
1288 #if defined(IUTF8) && defined(KDGKBMODE)
1289 switch(op
->kbmode
) {
1291 setlocale(LC_CTYPE
, "C.UTF-8");
1292 op
->flags
|= F_UTF8
;
1298 setlocale(LC_CTYPE
, "POSIX");
1299 op
->flags
&= ~F_UTF8
;
1303 setlocale(LC_CTYPE
, "POSIX");
1304 op
->flags
&= ~F_UTF8
;
1306 reset_vc(op
, tp
, 0);
1308 if ((tp
->c_cflag
& (CS8
|PARODD
|PARENB
)) == CS8
)
1309 op
->flags
|= F_EIGHTBITS
;
1311 if ((op
->flags
& F_NOCLEAR
) == 0)
1312 termio_clear(STDOUT_FILENO
);
1320 if (op
->flags
& F_KEEPSPEED
|| !op
->numspeed
) {
1321 /* Save the original setting. */
1322 ispeed
= cfgetispeed(tp
);
1323 ospeed
= cfgetospeed(tp
);
1325 /* Save also the original speed to array of the speeds to make
1326 * it possible to return the original after unexpected BREAKs.
1329 op
->speeds
[op
->numspeed
++] = ispeed
? ispeed
:
1333 ispeed
= TTYDEF_SPEED
;
1335 ospeed
= TTYDEF_SPEED
;
1337 ospeed
= ispeed
= op
->speeds
[FIRST_SPEED
];
1341 * Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
1342 * Special characters are set after we have read the login name; all
1343 * reads will be done in raw mode anyway. Errors will be dealt with
1347 /* The default is set c_iflag in termio_final() according to chardata.
1348 * Unfortunately, the chardata are not set according to the serial line
1349 * if --autolog is enabled. In this case we do not read from the line
1350 * at all. The best what we can do in this case is to keep c_iflag
1351 * unmodified for --autolog.
1355 tp
->c_iflag
= tp
->c_iflag
& IUTF8
;
1356 if (tp
->c_iflag
& IUTF8
)
1357 op
->flags
|= F_UTF8
;
1364 tp
->c_oflag
&= OPOST
| ONLCR
;
1366 if ((op
->flags
& F_KEEPCFLAGS
) == 0)
1367 tp
->c_cflag
= CS8
| HUPCL
| CREAD
| (tp
->c_cflag
& CLOCAL
);
1370 * Note that the speed is stored in the c_cflag termios field, so we have
1371 * set the speed always when the cflag is reset.
1373 cfsetispeed(tp
, ispeed
);
1374 cfsetospeed(tp
, ospeed
);
1376 /* The default is to follow setting from kernel, but it's possible
1377 * to explicitly remove/add CLOCAL flag by -L[=<mode>]*/
1378 switch (op
->clocal
) {
1379 case CLOCAL_MODE_ALWAYS
:
1380 tp
->c_cflag
|= CLOCAL
; /* -L or -L=always */
1382 case CLOCAL_MODE_NEVER
:
1383 tp
->c_cflag
&= ~CLOCAL
; /* -L=never */
1385 case CLOCAL_MODE_AUTO
: /* -L=auto */
1389 #ifdef HAVE_STRUCT_TERMIOS_C_LINE
1393 tp
->c_cc
[VTIME
] = 0;
1395 /* Check for terminal size and if not found set default */
1396 if (ioctl(STDIN_FILENO
, TIOCGWINSZ
, &ws
) == 0) {
1401 if (ioctl(STDIN_FILENO
, TIOCSWINSZ
, &ws
))
1402 debug("TIOCSWINSZ ioctl failed\n");
1405 /* Optionally enable hardware flow control. */
1407 if (op
->flags
& F_RTSCTS
)
1408 tp
->c_cflag
|= CRTSCTS
;
1410 /* Flush input and output queues, important for modems! */
1411 tcflush(STDIN_FILENO
, TCIOFLUSH
);
1413 if (tcsetattr(STDIN_FILENO
, TCSANOW
, tp
))
1414 log_warn(_("setting terminal attributes failed: %m"));
1416 /* Go to blocking input even in local mode. */
1417 fcntl(STDIN_FILENO
, F_SETFL
,
1418 fcntl(STDIN_FILENO
, F_GETFL
, 0) & ~O_NONBLOCK
);
1420 debug("term_io 2\n");
1423 /* Reset virtual console on stdin to its defaults */
1424 static void reset_vc(const struct options
*op
, struct termios
*tp
, int canon
)
1428 fl
|= (op
->flags
& F_KEEPCFLAGS
) == 0 ? 0 : UL_TTY_KEEPCFLAGS
;
1429 fl
|= (op
->flags
& F_UTF8
) == 0 ? 0 : UL_TTY_UTF8
;
1431 reset_virtual_console(tp
, fl
);
1433 #ifdef AGETTY_RELOAD
1435 * Discard all the flags that makes the line go canonical with echoing.
1436 * We need to know when the user starts typing.
1442 if (tcsetattr(STDIN_FILENO
, TCSADRAIN
, tp
))
1443 log_warn(_("setting terminal attributes failed: %m"));
1445 /* Go to blocking input even in local mode. */
1446 fcntl(STDIN_FILENO
, F_SETFL
,
1447 fcntl(STDIN_FILENO
, F_GETFL
, 0) & ~O_NONBLOCK
);
1450 /* Extract baud rate from modem status message. */
1451 static void auto_baud(struct termios
*tp
)
1461 * This works only if the modem produces its status code AFTER raising
1462 * the DCD line, and if the computer is fast enough to set the proper
1463 * baud rate before the message has gone by. We expect a message of the
1466 * <junk><number><junk>
1468 * The number is interpreted as the baud rate of the incoming call. If the
1469 * modem does not tell us the baud rate within one second, we will keep
1470 * using the current baud rate. It is advisable to enable BREAK
1471 * processing (comma-separated list of baud rates) if the processing of
1472 * modem status messages is enabled.
1476 * Use 7-bit characters, don't block if input queue is empty. Errors will
1477 * be dealt with later on.
1479 iflag
= tp
->c_iflag
;
1480 /* Enable 8th-bit stripping. */
1481 tp
->c_iflag
|= ISTRIP
;
1482 vmin
= tp
->c_cc
[VMIN
];
1483 /* Do not block when queue is empty. */
1485 tcsetattr(STDIN_FILENO
, TCSANOW
, tp
);
1488 * Wait for a while, then read everything the modem has said so far and
1489 * try to extract the speed of the dial-in call.
1492 if ((nread
= read(STDIN_FILENO
, buf
, sizeof(buf
) - 1)) > 0) {
1494 for (bp
= buf
; bp
< buf
+ nread
; bp
++)
1495 if (c_isascii(*bp
) && isdigit(*bp
)) {
1496 if ((speed
= bcode(bp
))) {
1497 cfsetispeed(tp
, speed
);
1498 cfsetospeed(tp
, speed
);
1504 /* Restore terminal settings. Errors will be dealt with later on. */
1505 tp
->c_iflag
= iflag
;
1506 tp
->c_cc
[VMIN
] = vmin
;
1507 tcsetattr(STDIN_FILENO
, TCSANOW
, tp
);
1510 static char *xgethostname(void)
1513 size_t sz
= get_hostname_max() + 1;
1515 name
= malloc(sizeof(char) * sz
);
1517 log_err(_("failed to allocate memory: %m"));
1519 if (gethostname(name
, sz
) != 0) {
1523 name
[sz
- 1] = '\0';
1527 static char *xgetdomainname(void)
1529 #ifdef HAVE_GETDOMAINNAME
1531 const size_t sz
= get_hostname_max() + 1;
1533 name
= malloc(sizeof(char) * sz
);
1535 log_err(_("failed to allocate memory: %m"));
1537 if (getdomainname(name
, sz
) != 0) {
1541 name
[sz
- 1] = '\0';
1549 static char *read_os_release(struct options
*op
, const char *varname
)
1553 size_t varsz
= strlen(varname
);
1554 char *p
, *buf
= NULL
, *ret
= NULL
;
1556 /* read the file only once */
1557 if (!op
->osrelease
) {
1558 fd
= open(_PATH_OS_RELEASE_ETC
, O_RDONLY
);
1560 fd
= open(_PATH_OS_RELEASE_USR
, O_RDONLY
);
1562 log_warn(_("cannot open os-release file"));
1567 if (fstat(fd
, &st
) < 0 || st
.st_size
> 4 * 1024 * 1024)
1570 op
->osrelease
= malloc(st
.st_size
+ 1);
1572 log_err(_("failed to allocate memory: %m"));
1573 if (read_all(fd
, op
->osrelease
, st
.st_size
) != (ssize_t
) st
.st_size
) {
1574 free(op
->osrelease
);
1575 op
->osrelease
= NULL
;
1578 op
->osrelease
[st
.st_size
] = 0;
1580 buf
= strdup(op
->osrelease
);
1582 log_err(_("failed to allocate memory: %m"));
1588 p
+= strspn(p
, "\n\r");
1589 p
+= strspn(p
, " \t\n\r");
1592 if (strspn(p
, "#;\n") != 0) {
1593 p
+= strcspn(p
, "\n\r");
1596 if (strncmp(p
, varname
, varsz
) != 0) {
1597 p
+= strcspn(p
, "\n\r");
1601 p
+= strspn(p
, " \t\n\r");
1606 p
+= strspn(p
, " \t\n\r=\"");
1607 eol
= p
+ strcspn(p
, "\n\r");
1611 if (*eon
== '\t' || *eon
== ' ') {
1624 log_err(_("failed to allocate memory: %m"));
1634 #ifdef AGETTY_RELOAD
1635 static void open_netlink(void)
1637 struct sockaddr_nl addr
= { 0, };
1640 if (netlink_fd
!= AGETTY_RELOAD_FDNONE
)
1643 sock
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
1645 addr
.nl_family
= AF_NETLINK
;
1646 addr
.nl_pid
= getpid();
1647 addr
.nl_groups
= netlink_groups
;
1648 if (bind(sock
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0)
1655 static int process_netlink_msg(int *triggered
)
1658 struct sockaddr_nl snl
;
1662 struct iovec iov
= {
1664 .iov_len
= sizeof(buf
)
1666 struct msghdr msg
= {
1668 .msg_namelen
= sizeof(snl
),
1671 .msg_control
= NULL
,
1672 .msg_controllen
= 0,
1676 rc
= recvmsg(netlink_fd
, &msg
, MSG_DONTWAIT
);
1678 if (errno
== EWOULDBLOCK
|| errno
== EAGAIN
)
1681 /* Failure, just stop listening for changes */
1683 netlink_fd
= AGETTY_RELOAD_FDNONE
;
1687 for (h
= (struct nlmsghdr
*)buf
; NLMSG_OK(h
, (unsigned int)rc
); h
= NLMSG_NEXT(h
, rc
)) {
1688 if (h
->nlmsg_type
== NLMSG_DONE
||
1689 h
->nlmsg_type
== NLMSG_ERROR
) {
1691 netlink_fd
= AGETTY_RELOAD_FDNONE
;
1702 static int process_netlink(void)
1705 while (process_netlink_msg(&triggered
));
1709 static int wait_for_term_input(int fd
)
1711 char buffer
[sizeof(struct inotify_event
) + NAME_MAX
+ 1];
1714 if (inotify_fd
== AGETTY_RELOAD_FDNONE
) {
1715 /* make sure the reload trigger file exists */
1716 int reload_fd
= open(AGETTY_RELOAD_FILENAME
,
1717 O_CREAT
|O_CLOEXEC
|O_RDONLY
,
1720 /* initialize reload trigger inotify stuff */
1721 if (reload_fd
>= 0) {
1722 inotify_fd
= inotify_init1(IN_NONBLOCK
| IN_CLOEXEC
);
1724 inotify_add_watch(inotify_fd
, AGETTY_RELOAD_FILENAME
,
1725 IN_ATTRIB
| IN_MODIFY
);
1729 log_warn(_("failed to create reload file: %s: %m"),
1730 AGETTY_RELOAD_FILENAME
);
1739 if (inotify_fd
>= 0) {
1740 FD_SET(inotify_fd
, &rfds
);
1741 nfds
= max(nfds
, inotify_fd
);
1743 if (netlink_fd
>= 0) {
1744 FD_SET(netlink_fd
, &rfds
);
1745 nfds
= max(nfds
, netlink_fd
);
1748 /* If waiting fails, just fall through, presumably reading input will fail */
1749 if (select(nfds
+ 1, &rfds
, NULL
, NULL
, NULL
) < 0)
1752 if (FD_ISSET(fd
, &rfds
)) {
1757 if (netlink_fd
>= 0 && FD_ISSET(netlink_fd
, &rfds
)) {
1758 if (!process_netlink())
1761 /* Just drain the inotify buffer */
1762 } else if (inotify_fd
>= 0 && FD_ISSET(inotify_fd
, &rfds
)) {
1763 while (read(inotify_fd
, buffer
, sizeof (buffer
)) > 0);
1769 #endif /* AGETTY_RELOAD */
1771 #ifdef ISSUEDIR_SUPPORT
1772 static int issuedir_filter(const struct dirent
*d
)
1776 #ifdef _DIRENT_HAVE_D_TYPE
1777 if (d
->d_type
!= DT_UNKNOWN
&& d
->d_type
!= DT_REG
&&
1778 d
->d_type
!= DT_LNK
)
1781 if (*d
->d_name
== '.')
1784 namesz
= strlen(d
->d_name
);
1785 if (!namesz
|| namesz
< ISSUEDIR_EXTSIZ
+ 1 ||
1786 strcmp(d
->d_name
+ (namesz
- ISSUEDIR_EXTSIZ
), ISSUEDIR_EXT
) != 0)
1794 static int issuefile_read_stream(struct issue
*ie
, FILE *f
, struct options
*op
, struct termios
*tp
);
1796 /* returns: 0 on success, 1 cannot open, <0 on error
1798 static int issuedir_read(struct issue
*ie
, const char *dirname
,
1799 struct options
*op
, struct termios
*tp
)
1802 struct dirent
**namelist
= NULL
;
1804 dd
= open(dirname
, O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
1808 nfiles
= scandirat(dd
, ".", &namelist
, issuedir_filter
, versionsort
);
1812 ie
->do_tcsetattr
= 1;
1814 for (i
= 0; i
< nfiles
; i
++) {
1815 struct dirent
*d
= namelist
[i
];
1818 f
= fopen_at(dd
, d
->d_name
, O_RDONLY
|O_CLOEXEC
, "r" UL_CLOEXECSTR
);
1820 issuefile_read_stream(ie
, f
, op
, tp
);
1825 for (i
= 0; i
< nfiles
; i
++)
1833 #else /* !ISSUEDIR_SUPPORT */
1834 static int issuedir_read(struct issue
*ie
__attribute__((__unused__
)),
1835 const char *dirname
__attribute__((__unused__
)),
1836 struct options
*op
__attribute__((__unused__
)),
1837 struct termios
*tp
__attribute__((__unused__
)))
1841 #endif /* ISSUEDIR_SUPPORT */
1843 #ifndef ISSUE_SUPPORT
1844 static void print_issue_file(struct issue
*ie
__attribute__((__unused__
)),
1846 struct termios
*tp
__attribute__((__unused__
)))
1848 if ((op
->flags
& F_NONL
) == 0) {
1849 /* Issue not in use, start with a new line. */
1850 write_all(STDOUT_FILENO
, "\r\n", 2);
1854 static void eval_issue_file(struct issue
*ie
__attribute__((__unused__
)),
1855 struct options
*op
__attribute__((__unused__
)),
1856 struct termios
*tp
__attribute__((__unused__
)))
1860 static void show_issue(struct options
*op
__attribute__((__unused__
)))
1864 #else /* ISSUE_SUPPORT */
1866 static int issuefile_read_stream(
1867 struct issue
*ie
, FILE *f
,
1868 struct options
*op
, struct termios
*tp
)
1873 if (fstat(fileno(f
), &st
) || !S_ISREG(st
.st_mode
))
1880 ie
->output
= open_memstream(&ie
->mem
, &ie
->mem_sz
);
1883 while ((c
= getc(f
)) != EOF
) {
1885 output_special_char(ie
, getc(f
), op
, tp
, f
);
1887 putc(c
, ie
->output
);
1893 static int issuefile_read(
1894 struct issue
*ie
, const char *filename
,
1895 struct options
*op
, struct termios
*tp
)
1897 FILE *f
= fopen(filename
, "r" UL_CLOEXECSTR
);
1901 rc
= issuefile_read_stream(ie
, f
, op
, tp
);
1908 #ifdef AGETTY_RELOAD
1909 static int issue_is_changed(struct issue
*ie
)
1911 if (ie
->mem_old
&& ie
->mem
1912 && strcmp(ie
->mem_old
, ie
->mem
) == 0) {
1914 ie
->mem_old
= ie
->mem
;
1923 static void print_issue_file(struct issue
*ie
,
1927 int oflag
= tp
->c_oflag
; /* Save current setting. */
1929 if ((op
->flags
& F_NONL
) == 0) {
1930 /* Issue not in use, start with a new line. */
1931 write_all(STDOUT_FILENO
, "\r\n", 2);
1934 if (ie
->do_tcsetattr
) {
1935 if ((op
->flags
& F_VCONSOLE
) == 0) {
1936 /* Map new line in output to carriage return & new line. */
1937 tp
->c_oflag
|= (ONLCR
| OPOST
);
1938 tcsetattr(STDIN_FILENO
, TCSADRAIN
, tp
);
1943 write_all(STDOUT_FILENO
, ie
->mem
, ie
->mem_sz
);
1945 if (ie
->do_tcrestore
) {
1946 /* Restore settings. */
1947 tp
->c_oflag
= oflag
;
1948 /* Wait till output is gone. */
1949 tcsetattr(STDIN_FILENO
, TCSADRAIN
, tp
);
1952 #ifdef AGETTY_RELOAD
1954 ie
->mem_old
= ie
->mem
;
1964 static void eval_issue_file(struct issue
*ie
,
1970 #ifdef AGETTY_RELOAD
1973 if (!(op
->flags
& F_ISSUE
))
1976 * The custom issue file or directory list specified by:
1977 * agetty --isue-file <path[:path]...>
1978 * Note that nothing is printed if the file/dir does not exist.
1981 char *list
= strdup(op
->issue
);
1985 log_err(_("failed to allocate memory: %m"));
1987 for (file
= strtok(list
, ":"); file
; file
= strtok(NULL
, ":")) {
1990 if (stat(file
, &st
) < 0)
1992 if (S_ISDIR(st
.st_mode
))
1993 issuedir_read(ie
, file
, op
, tp
);
1995 issuefile_read(ie
, file
, op
, tp
);
2001 /* The default /etc/issue and optional /etc/issue.d directory as
2002 * extension to the file. The /etc/issue.d directory is ignored if
2003 * there is no /etc/issue file. The file may be empty or symlink.
2005 if (access(_PATH_ISSUE
, F_OK
|R_OK
) == 0) {
2006 issuefile_read(ie
, _PATH_ISSUE
, op
, tp
);
2007 issuedir_read(ie
, _PATH_ISSUEDIR
, op
, tp
);
2011 /* Fallback @runstatedir (usually /run) -- the file is not required to
2014 if (issuefile_read(ie
, _PATH_RUNSTATEDIR
"/" _PATH_ISSUE_FILENAME
, op
, tp
) == 0)
2016 if (issuedir_read(ie
, _PATH_RUNSTATEDIR
"/" _PATH_ISSUE_DIRNAME
, op
, tp
) == 0)
2021 /* Fallback @sysconfstaticdir (usually /usr/lib) -- the file is not
2022 * required to read the dir
2024 issuefile_read(ie
, _PATH_SYSCONFSTATICDIR
"/" _PATH_ISSUE_FILENAME
, op
, tp
);
2025 issuedir_read(ie
, _PATH_SYSCONFSTATICDIR
"/" _PATH_ISSUE_DIRNAME
, op
, tp
);
2029 #ifdef AGETTY_RELOAD
2030 if (netlink_groups
!= 0)
2039 /* This is --show-issue backend, executed by normal user on the current
2042 static void show_issue(struct options
*op
)
2044 struct issue ie
= { .output
= NULL
};
2047 memset(&tp
, 0, sizeof(struct termios
));
2048 if (tcgetattr(STDIN_FILENO
, &tp
) < 0)
2049 err(EXIT_FAILURE
, _("failed to get terminal attributes: %m"));
2051 eval_issue_file(&ie
, op
, &tp
);
2054 write_all(STDOUT_FILENO
, ie
.mem
, ie
.mem_sz
);
2060 #endif /* ISSUE_SUPPORT */
2062 /* Show login prompt, optionally preceded by /etc/issue contents. */
2063 static void do_prompt(struct issue
*ie
, struct options
*op
, struct termios
*tp
)
2065 #ifdef AGETTY_RELOAD
2068 print_issue_file(ie
, op
, tp
);
2070 if (op
->flags
& F_LOGINPAUSE
) {
2071 puts(_("[press ENTER to login]"));
2072 #ifdef AGETTY_RELOAD
2074 if (!wait_for_term_input(STDIN_FILENO
)) {
2075 eval_issue_file(ie
, op
, tp
);
2076 if (issue_is_changed(ie
)) {
2077 if (op
->flags
& F_VCONSOLE
)
2078 termio_clear(STDOUT_FILENO
);
2086 if (!(op
->flags
& F_NOHINTS
) && !op
->autolog
&&
2087 (op
->flags
& F_VCONSOLE
)) {
2090 if (ioctl(STDIN_FILENO
, KDGKBLED
, &kb
) == 0) {
2091 char hint
[256] = { '\0' };
2094 if (access(_PATH_NUMLOCK_ON
, F_OK
) == 0)
2097 if (nl
&& (kb
& 0x02) == 0)
2098 append(hint
, sizeof(hint
), NULL
, _("Num Lock off"));
2100 else if (nl
== 0 && (kb
& 2) && (kb
& 0x20) == 0)
2101 append(hint
, sizeof(hint
), NULL
, _("Num Lock on"));
2103 if ((kb
& 0x04) && (kb
& 0x40) == 0)
2104 append(hint
, sizeof(hint
), ", ", _("Caps Lock on"));
2106 if ((kb
& 0x01) && (kb
& 0x10) == 0)
2107 append(hint
, sizeof(hint
), ", ", _("Scroll Lock on"));
2110 printf(_("Hint: %s\n\n"), hint
);
2113 #endif /* KDGKBLED */
2114 if ((op
->flags
& F_NOHOSTNAME
) == 0) {
2115 char *hn
= xgethostname();
2118 char *dot
= strchr(hn
, '.');
2120 struct addrinfo
*res
= NULL
;
2122 if ((op
->flags
& F_LONGHNAME
) == 0) {
2126 } else if (dot
== NULL
) {
2127 struct addrinfo hints
;
2129 memset(&hints
, 0, sizeof(hints
));
2130 hints
.ai_flags
= AI_CANONNAME
;
2132 if (!getaddrinfo(hn
, NULL
, &hints
, &res
)
2133 && res
&& res
->ai_canonname
)
2134 cn
= res
->ai_canonname
;
2137 write_all(STDOUT_FILENO
, cn
, strlen(cn
));
2138 write_all(STDOUT_FILENO
, " ", 1);
2146 /* Always show login prompt. */
2147 write_all(STDOUT_FILENO
, LOGIN_PROMPT
,
2148 sizeof(LOGIN_PROMPT
) - 1);
2152 /* Select next baud rate. */
2153 static void next_speed(struct options
*op
, struct termios
*tp
)
2155 static int baud_index
= -1;
2157 if (baud_index
== -1)
2159 * If the F_KEEPSPEED flags is set then the FIRST_SPEED is not
2160 * tested yet (see termio_init()).
2163 (op
->flags
& F_KEEPSPEED
) ? FIRST_SPEED
: 1 % op
->numspeed
;
2165 baud_index
= (baud_index
+ 1) % op
->numspeed
;
2167 cfsetispeed(tp
, op
->speeds
[baud_index
]);
2168 cfsetospeed(tp
, op
->speeds
[baud_index
]);
2169 tcsetattr(STDIN_FILENO
, TCSANOW
, tp
);
2172 /* Get user name, establish parity, speed, erase, kill & eol. */
2173 static char *get_logname(struct issue
*ie
, struct options
*op
, struct termios
*tp
, struct chardata
*cp
)
2175 static char logname
[BUFSIZ
];
2177 char c
; /* input character, full eight bits */
2178 char ascval
; /* low 7 bits of input character */
2180 static char *erase
[] = { /* backspace-space-backspace */
2181 "\010\040\010", /* space parity */
2182 "\010\040\010", /* odd parity */
2183 "\210\240\210", /* even parity */
2184 "\210\240\210", /* no parity */
2187 /* Initialize kill, erase, parity etc. (also after switching speeds). */
2191 * Flush pending input (especially important after parsing or switching
2194 if ((op
->flags
& F_VCONSOLE
) == 0)
2196 tcflush(STDIN_FILENO
, TCIFLUSH
);
2198 eightbit
= (op
->flags
& (F_EIGHTBITS
|F_UTF8
));
2202 eval_issue_file(ie
, op
, tp
);
2203 while (*logname
== '\0') {
2204 /* Write issue file and prompt */
2205 do_prompt(ie
, op
, tp
);
2208 #ifdef AGETTY_RELOAD
2209 if (!wait_for_term_input(STDIN_FILENO
)) {
2210 /* refresh prompt -- discard input data, clear terminal
2211 * and call do_prompt() again
2213 if ((op
->flags
& F_VCONSOLE
) == 0)
2215 eval_issue_file(ie
, op
, tp
);
2216 if (!issue_is_changed(ie
))
2218 tcflush(STDIN_FILENO
, TCIFLUSH
);
2219 if (op
->flags
& F_VCONSOLE
)
2220 termio_clear(STDOUT_FILENO
);
2228 /* Read name, watch for break and end-of-line. */
2229 while (cp
->eol
== '\0') {
2234 debug("read from FD\n");
2235 readres
= read(STDIN_FILENO
, &c
, 1);
2237 debug("read failed\n");
2239 /* The terminal could be open with O_NONBLOCK when
2240 * -L (force CLOCAL) is specified... */
2241 if (errno
== EINTR
|| errno
== EAGAIN
) {
2251 exit_slowly(EXIT_SUCCESS
);
2253 log_err(_("%s: read: %m"), op
->tty
);
2260 /* Do parity bit handling. */
2263 else if (c
!= (ascval
= (c
& 0177))) {
2264 uint32_t bits
; /* # of "1" bits per character */
2265 uint32_t mask
; /* mask with 1 bit up */
2266 for (bits
= 1, mask
= 1; mask
& 0177; mask
<<= 1) {
2270 cp
->parity
|= ((bits
& 1) ? 1 : 2);
2273 if (op
->killchars
&& strchr(op
->killchars
, ascval
))
2275 else if (op
->erasechars
&& strchr(op
->erasechars
, ascval
))
2280 /* Do erase, kill and end-of-line processing. */
2284 if (op
->numspeed
> 1 && !(op
->flags
& F_VCONSOLE
))
2287 exit_slowly(EXIT_SUCCESS
);
2291 *bp
= 0; /* terminate logname */
2292 cp
->eol
= ascval
; /* set end-of-line char */
2296 cp
->erase
= ascval
; /* set erase character */
2298 if ((tp
->c_lflag
& ECHO
) == 0)
2299 write_all(1, erase
[cp
->parity
], 3);
2304 cp
->kill
= ascval
; /* set kill character */
2307 if (key
== CTL('C') && !(op
->flags
& F_VCONSOLE
))
2308 /* Ignore CTRL+C on serial line */
2310 while (bp
> logname
) {
2311 if ((tp
->c_lflag
& ECHO
) == 0)
2312 write_all(1, erase
[cp
->parity
], 3);
2319 if ((size_t)(bp
- logname
) >= sizeof(logname
) - 1)
2320 log_err(_("%s: input overrun"), op
->tty
);
2321 if ((tp
->c_lflag
& ECHO
) == 0)
2322 write_all(1, &c
, 1); /* echo the character */
2323 *bp
++ = ascval
; /* and store it */
2326 /* Everything was erased. */
2327 if (bp
== logname
&& cp
->eol
== '\0')
2332 #ifdef HAVE_WIDECHAR
2333 if ((op
->flags
& (F_EIGHTBITS
|F_UTF8
)) == (F_EIGHTBITS
|F_UTF8
)) {
2334 /* Check out UTF-8 multibyte characters */
2338 len
= mbstowcs((wchar_t *)0, logname
, 0);
2340 log_err(_("%s: invalid character conversion for login name"), op
->tty
);
2342 wcs
= malloc((len
+ 1) * sizeof(wchar_t));
2344 log_err(_("failed to allocate memory: %m"));
2346 len
= mbstowcs(wcs
, logname
, len
+ 1);
2348 log_err(_("%s: invalid character conversion for login name"), op
->tty
);
2352 const wint_t wc
= *wcp
++;
2354 log_err(_("%s: invalid character 0x%x in login name"), op
->tty
, wc
);
2359 if ((op
->flags
& F_LCUC
) && (cp
->capslock
= caps_lock(logname
))) {
2361 /* Handle names with upper case and no lower case. */
2362 for (bp
= logname
; *bp
; bp
++)
2364 *bp
= tolower(*bp
); /* map name to lower case */
2370 /* Set the final tty mode bits. */
2371 static void termio_final(struct options
*op
, struct termios
*tp
, struct chardata
*cp
)
2373 /* General terminal-independent stuff. */
2375 /* 2-way flow control */
2376 tp
->c_iflag
|= IXON
| IXOFF
;
2377 tp
->c_lflag
|= ICANON
| ISIG
| ECHO
| ECHOE
| ECHOK
| ECHOKE
;
2378 /* no longer| ECHOCTL | ECHOPRT */
2379 tp
->c_oflag
|= OPOST
;
2380 /* tp->c_cflag = 0; */
2381 tp
->c_cc
[VINTR
] = DEF_INTR
;
2382 tp
->c_cc
[VQUIT
] = DEF_QUIT
;
2383 tp
->c_cc
[VEOF
] = DEF_EOF
;
2384 tp
->c_cc
[VEOL
] = DEF_EOL
;
2386 tp
->c_cc
[VSWTC
] = DEF_SWITCH
;
2387 #elif defined(VSWTCH)
2388 tp
->c_cc
[VSWTCH
] = DEF_SWITCH
;
2389 #endif /* __linux__ */
2391 /* Account for special characters seen in input. */
2392 if (cp
->eol
== CR
) {
2393 tp
->c_iflag
|= ICRNL
;
2394 tp
->c_oflag
|= ONLCR
;
2396 tp
->c_cc
[VERASE
] = cp
->erase
;
2397 tp
->c_cc
[VKILL
] = cp
->kill
;
2399 /* Account for the presence or absence of parity bits in input. */
2400 switch (cp
->parity
) {
2402 /* space (always 0) parity */
2406 tp
->c_cflag
|= PARODD
;
2410 tp
->c_cflag
|= PARENB
;
2411 tp
->c_iflag
|= INPCK
| ISTRIP
;
2415 tp
->c_cflag
&= ~CSIZE
;
2419 /* Account for upper case without lower case. */
2422 tp
->c_iflag
|= IUCLC
;
2425 tp
->c_lflag
|= XCASE
;
2428 tp
->c_oflag
|= OLCUC
;
2431 /* Optionally enable hardware flow control. */
2433 if (op
->flags
& F_RTSCTS
)
2434 tp
->c_cflag
|= CRTSCTS
;
2437 /* Finally, make the new settings effective. */
2438 if (tcsetattr(STDIN_FILENO
, TCSANOW
, tp
) < 0)
2439 log_err(_("%s: failed to set terminal attributes: %m"), op
->tty
);
2443 * String contains upper case without lower case.
2444 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=52940
2445 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=156242
2447 static int caps_lock(char *s
)
2451 for (capslock
= 0; *s
; s
++) {
2453 return EXIT_SUCCESS
;
2455 capslock
= isupper(*s
);
2460 /* Convert speed string to speed code; return 0 on failure. */
2461 static speed_t
bcode(char *s
)
2463 const struct Speedtab
*sp
;
2468 speed
= strtol(s
, &end
, 10);
2470 if (errno
|| !end
|| end
== s
)
2473 for (sp
= speedtab
; sp
->speed
; sp
++)
2474 if (sp
->speed
== speed
)
2479 static void __attribute__((__noreturn__
)) usage(void)
2483 fputs(USAGE_HEADER
, out
);
2484 fprintf(out
, _(" %1$s [options] <line> [<baud_rate>,...] [<termtype>]\n"
2485 " %1$s [options] <baud_rate>,... <line> [<termtype>]\n"), program_invocation_short_name
);
2487 fputs(USAGE_SEPARATOR
, out
);
2488 fputs(_("Open a terminal and set its mode.\n"), out
);
2490 fputs(USAGE_OPTIONS
, out
);
2491 fputs(_(" -8, --8bits assume 8-bit tty\n"), out
);
2492 fputs(_(" -a, --autologin <user> login the specified user automatically\n"), out
);
2493 fputs(_(" -c, --noreset do not reset control mode\n"), out
);
2494 fputs(_(" -E, --remote use -r <hostname> for login(1)\n"), out
);
2495 fputs(_(" -f, --issue-file <list> display issue files or directories\n"), out
);
2496 fputs(_(" --show-issue display issue file and exit\n"), out
);
2497 fputs(_(" -h, --flow-control enable hardware flow control\n"), out
);
2498 fputs(_(" -H, --host <hostname> specify login host\n"), out
);
2499 fputs(_(" -i, --noissue do not display issue file\n"), out
);
2500 fputs(_(" -I, --init-string <string> set init string\n"), out
);
2501 fputs(_(" -J --noclear do not clear the screen before prompt\n"), out
);
2502 fputs(_(" -l, --login-program <file> specify login program\n"), out
);
2503 fputs(_(" -L, --local-line[=<mode>] control the local line flag\n"), out
);
2504 fputs(_(" -m, --extract-baud extract baud rate during connect\n"), out
);
2505 fputs(_(" -n, --skip-login do not prompt for login\n"), out
);
2506 fputs(_(" -N --nonewline do not print a newline before issue\n"), out
);
2507 fputs(_(" -o, --login-options <opts> options that are passed to login\n"), out
);
2508 fputs(_(" -p, --login-pause wait for any key before the login\n"), out
);
2509 fputs(_(" -r, --chroot <dir> change root to the directory\n"), out
);
2510 fputs(_(" -R, --hangup do virtually hangup on the tty\n"), out
);
2511 fputs(_(" -s, --keep-baud try to keep baud rate after break\n"), out
);
2512 fputs(_(" -t, --timeout <number> login process timeout\n"), out
);
2513 fputs(_(" -U, --detect-case detect uppercase terminal\n"), out
);
2514 fputs(_(" -w, --wait-cr wait carriage-return\n"), out
);
2515 fputs(_(" --nohints do not print hints\n"), out
);
2516 fputs(_(" --nohostname no hostname at all will be shown\n"), out
);
2517 fputs(_(" --long-hostname show full qualified hostname\n"), out
);
2518 fputs(_(" --erase-chars <string> additional backspace chars\n"), out
);
2519 fputs(_(" --kill-chars <string> additional kill chars\n"), out
);
2520 fputs(_(" --chdir <directory> chdir before the login\n"), out
);
2521 fputs(_(" --delay <number> sleep seconds before prompt\n"), out
);
2522 fputs(_(" --nice <number> run login with this priority\n"), out
);
2523 fputs(_(" --reload reload prompts on running agetty instances\n"), out
);
2524 fputs(_(" --list-speeds display supported baud rates\n"), out
);
2525 printf( " --help %s\n", USAGE_OPTSTR_HELP
);
2526 printf( " --version %s\n", USAGE_OPTSTR_VERSION
);
2527 printf(USAGE_MAN_TAIL("agetty(8)"));
2532 static void list_speeds(void)
2534 const struct Speedtab
*sp
;
2536 for (sp
= speedtab
; sp
->speed
; sp
++)
2537 printf("%10ld\n", sp
->speed
);
2541 * Helper function reports errors to console or syslog.
2542 * Will be used by log_err() and log_warn() therefore
2543 * it takes a format as well as va_list.
2545 static void dolog(int priority
2547 __attribute__((__unused__
))
2549 , const char *fmt
, va_list ap
)
2553 * If the diagnostic is reported via syslog(3), the process name is
2554 * automatically prepended to the message. If we write directly to
2555 * /dev/console, we must prepend the process name ourselves.
2557 openlog(program_invocation_short_name
, LOG_PID
, LOG_AUTHPRIV
);
2558 vsyslog(priority
, fmt
, ap
);
2562 * Write the diagnostic directly to /dev/console if we do not use
2563 * the syslog(3) facility.
2566 char new_fmt
[BUFSIZ
];
2569 snprintf(new_fmt
, sizeof(new_fmt
), "%s: %s\r\n",
2570 program_invocation_short_name
, fmt
);
2571 /* Terminate with CR-LF since the console mode is unknown. */
2572 vsnprintf(buf
, sizeof(buf
), new_fmt
, ap
);
2574 if ((fd
= open("/dev/console", 1)) >= 0) {
2575 write_all(fd
, buf
, strlen(buf
));
2578 #endif /* USE_SYSLOG */
2581 static void exit_slowly(int code
)
2583 /* Be kind to init(8). */
2588 static void log_err(const char *fmt
, ...)
2593 dolog(LOG_ERR
, fmt
, ap
);
2596 exit_slowly(EXIT_FAILURE
);
2599 static void log_warn(const char *fmt
, ...)
2604 dolog(LOG_WARNING
, fmt
, ap
);
2608 static void print_addr(struct issue
*ie
, sa_family_t family
, void *addr
)
2610 char buff
[INET6_ADDRSTRLEN
+ 1];
2612 inet_ntop(family
, addr
, buff
, sizeof(buff
));
2613 fprintf(ie
->output
, "%s", buff
);
2617 * Prints IP for the specified interface (@iface), if the interface is not
2618 * specified then prints the "best" one (UP, RUNNING, non-LOOPBACK). If not
2619 * found the "best" interface then prints at least host IP.
2621 static void output_iface_ip(struct issue
*ie
,
2622 struct ifaddrs
*addrs
,
2627 struct addrinfo hints
, *info
= NULL
;
2634 for (p
= addrs
; p
; p
= p
->ifa_next
) {
2638 p
->ifa_addr
->sa_family
!= family
)
2642 /* Filter out by interface name */
2643 if (strcmp(p
->ifa_name
, iface
) != 0)
2646 /* Select the "best" interface */
2647 if ((p
->ifa_flags
& IFF_LOOPBACK
) ||
2648 !(p
->ifa_flags
& IFF_UP
) ||
2649 !(p
->ifa_flags
& IFF_RUNNING
))
2654 switch (p
->ifa_addr
->sa_family
) {
2656 addr
= &((struct sockaddr_in
*) p
->ifa_addr
)->sin_addr
;
2659 addr
= &((struct sockaddr_in6
*) p
->ifa_addr
)->sin6_addr
;
2664 print_addr(ie
, family
, addr
);
2672 /* Hmm.. not found the best interface, print host IP at least */
2673 memset(&hints
, 0, sizeof(hints
));
2674 hints
.ai_family
= family
;
2675 if (family
== AF_INET6
)
2676 hints
.ai_flags
= AI_V4MAPPED
;
2678 host
= xgethostname();
2679 if (host
&& getaddrinfo(host
, NULL
, &hints
, &info
) == 0 && info
) {
2680 switch (info
->ai_family
) {
2682 addr
= &((struct sockaddr_in
*) info
->ai_addr
)->sin_addr
;
2685 addr
= &((struct sockaddr_in6
*) info
->ai_addr
)->sin6_addr
;
2689 print_addr(ie
, family
, addr
);
2697 * parses \x{argument}, if not argument specified then returns NULL, the @fd
2698 * has to point to one char after the sequence (it means '{').
2700 static char *get_escape_argument(FILE *fd
, char *buf
, size_t bufsz
)
2705 if (c
== EOF
|| (unsigned char) c
!= '{') {
2714 if ((unsigned char) c
!= '}' && i
< bufsz
- 1)
2715 buf
[i
++] = (unsigned char) c
;
2717 } while ((unsigned char) c
!= '}');
2723 static void output_special_char(struct issue
*ie
,
2734 char escname
[UL_COLORNAME_MAXSZ
];
2736 if (get_escape_argument(fp
, escname
, sizeof(escname
))) {
2737 const char *esc
= color_sequence_from_colorname(escname
);
2739 fputs(esc
, ie
->output
);
2741 fputs("\033", ie
->output
);
2746 fprintf(ie
->output
, "%s", uts
.sysname
);
2750 fprintf(ie
->output
, "%s", uts
.nodename
);
2754 fprintf(ie
->output
, "%s", uts
.release
);
2758 fprintf(ie
->output
, "%s", uts
.version
);
2762 fprintf(ie
->output
, "%s", uts
.machine
);
2766 char *dom
= xgetdomainname();
2768 fputs(dom
? dom
: "unknown_domain", ie
->output
);
2775 char *host
= xgethostname();
2776 struct addrinfo hints
, *info
= NULL
;
2778 memset(&hints
, 0, sizeof(hints
));
2779 hints
.ai_flags
= AI_CANONNAME
;
2781 if (host
&& getaddrinfo(host
, NULL
, &hints
, &info
) == 0 && info
) {
2784 if (info
->ai_canonname
&&
2785 (canon
= strchr(info
->ai_canonname
, '.')))
2788 fputs(dom
? dom
: "unknown_domain", ie
->output
);
2801 localtime_r(&now
, &tm
);
2803 if (c
== 'd') /* ISO 8601 */
2804 fprintf(ie
->output
, "%s %s %2d %d",
2805 nl_langinfo(ABDAY_1
+ tm
.tm_wday
),
2806 nl_langinfo(ABMON_1
+ tm
.tm_mon
),
2808 tm
.tm_year
< 70 ? tm
.tm_year
+ 2000 :
2811 fprintf(ie
->output
, "%02d:%02d:%02d",
2812 tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
2816 fprintf (ie
->output
, "%s", op
->tty
);
2820 const speed_t speed
= cfgetispeed(tp
);
2823 for (i
= 0; speedtab
[i
].speed
; i
++) {
2824 if (speedtab
[i
].code
== speed
) {
2825 fprintf(ie
->output
, "%ld", speedtab
[i
].speed
);
2833 char *var
= NULL
, varname
[64];
2836 if (get_escape_argument(fp
, varname
, sizeof(varname
))) {
2837 var
= read_os_release(op
, varname
);
2839 if (strcmp(varname
, "ANSI_COLOR") == 0)
2840 fprintf(ie
->output
, "\033[%sm", var
);
2842 fputs(var
, ie
->output
);
2845 } else if ((var
= read_os_release(op
, "PRETTY_NAME"))) {
2846 fputs(var
, ie
->output
);
2848 /* \S and PRETTY_NAME not found */
2851 fputs(uts
.sysname
, ie
->output
);
2864 while ((ut
= getutxent()))
2865 if (ut
->ut_type
== USER_PROCESS
)
2869 fprintf(ie
->output
, P_("%d user", "%d users", users
), users
);
2871 fprintf (ie
->output
, "%d ", users
);
2874 #if defined(RTMGRP_IPV4_IFADDR) && defined(RTMGRP_IPV6_IFADDR)
2878 sa_family_t family
= c
== '4' ? AF_INET
: AF_INET6
;
2879 struct ifaddrs
*addrs
= NULL
;
2882 if (getifaddrs(&addrs
))
2885 if (get_escape_argument(fp
, iface
, sizeof(iface
)))
2886 output_iface_ip(ie
, addrs
, iface
, family
);
2888 output_iface_ip(ie
, addrs
, NULL
, family
);
2893 netlink_groups
|= RTMGRP_IPV4_IFADDR
;
2895 netlink_groups
|= RTMGRP_IPV6_IFADDR
;
2900 putc(c
, ie
->output
);
2905 static void init_special_char(char* arg
, struct options
*op
)
2910 op
->initstring
= malloc(strlen(arg
) + 1);
2911 if (!op
->initstring
)
2912 log_err(_("failed to allocate memory: %m"));
2915 * Copy optarg into op->initstring decoding \ddd octal
2921 /* The \\ is converted to \ */
2928 /* Handle \000 - \177. */
2930 for (i
= 1; i
<= 3; i
++) {
2931 if (*p
>= '0' && *p
<= '7') {
2948 * Appends @str to @dest and if @dest is not empty then use @sep as a
2949 * separator. The maximal final length of the @dest is @len.
2951 * Returns the final @dest length or -1 in case of error.
2953 static ssize_t
append(char *dest
, size_t len
, const char *sep
, const char *src
)
2955 size_t dsz
= 0, ssz
= 0, sz
;
2958 if (!dest
|| !len
|| !src
)
2967 if (dsz
+ ssz
+ sz
+ 1 > len
)
2972 p
= mempcpy(p
, sep
, ssz
);
2977 return dsz
+ ssz
+ sz
;
2981 * Do not allow the user to pass an option as a user name
2982 * To be more safe: Use `--' to make sure the rest is
2983 * interpreted as non-options by the program, if it supports it.
2985 static void check_username(const char* nm
)
2990 if (strlen(nm
) > 42)
2999 log_err(_("checkname failed: %m"));
3002 static void reload_agettys(void)
3004 #ifdef AGETTY_RELOAD
3005 int fd
= open(AGETTY_RELOAD_FILENAME
, O_CREAT
|O_CLOEXEC
|O_WRONLY
,
3008 err(EXIT_FAILURE
, _("cannot open %s"), AGETTY_RELOAD_FILENAME
);
3010 if (futimens(fd
, NULL
) < 0 || close(fd
) < 0)
3011 err(EXIT_FAILURE
, _("cannot touch file %s"),
3012 AGETTY_RELOAD_FILENAME
);
3015 errx(EXIT_FAILURE
, _("--reload is unsupported on your system"));