/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2010 Lennart Poettering
-***/
#include <errno.h>
#include <fcntl.h>
#include "io-util.h"
#include "log.h"
#include "macro.h"
-#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
}
int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
- struct termios old_termios, new_termios;
- char c, line[LINE_MAX];
+ _cleanup_free_ char *line = NULL;
+ struct termios old_termios;
+ int r;
assert(f);
assert(ret);
+ /* If this is a terminal, then switch canonical mode off, so that we can read a single character */
if (tcgetattr(fileno(f), &old_termios) >= 0) {
- new_termios = old_termios;
+ struct termios new_termios = old_termios;
new_termios.c_lflag &= ~ICANON;
new_termios.c_cc[VMIN] = 1;
new_termios.c_cc[VTIME] = 0;
if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
- size_t k;
+ int c;
if (t != USEC_INFINITY) {
if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
- tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+ (void) tcsetattr(fileno(f), TCSADRAIN, &old_termios);
return -ETIMEDOUT;
}
}
- k = fread(&c, 1, 1, f);
+ errno = 0;
+ c = fgetc(f);
+ if (c == EOF)
+ r = ferror(f) && errno > 0 ? -errno : -EIO;
+ else
+ r = 0;
- tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+ (void) tcsetattr(fileno(f), TCSADRAIN, &old_termios);
- if (k <= 0)
- return -EIO;
+ if (r < 0)
+ return r;
if (need_nl)
*need_nl = c != '\n';
return -ETIMEDOUT;
}
- errno = 0;
- if (!fgets(line, sizeof(line), f))
- return errno > 0 ? -errno : -EIO;
+ /* If this is not a terminal, then read a full line instead */
- truncate_nl(line);
+ r = read_line(f, 16, &line); /* longer than necessary, to eat up UTF-8 chars/vt100 key sequences */
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EIO;
if (strlen(line) != 1)
return -EBADMSG;
}
int ask_string(char **ret, const char *text, ...) {
+ int r;
+
assert(ret);
assert(text);
for (;;) {
- char line[LINE_MAX];
+ _cleanup_free_ char *line = NULL;
va_list ap;
if (colors_enabled())
fflush(stdout);
- errno = 0;
- if (!fgets(line, sizeof(line), stdin))
- return errno > 0 ? -errno : -EIO;
-
- if (!endswith(line, "\n"))
- putchar('\n');
- else {
- char *s;
-
- if (isempty(line))
- continue;
-
- truncate_nl(line);
- s = strdup(line);
- if (!s)
- return -ENOMEM;
+ r = read_line(stdin, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EIO;
- *ret = s;
+ if (!isempty(line)) {
+ *ret = TAKE_PTR(line);
return 0;
}
}
if (e)
(void) safe_atoi(e, &c);
- if (c <= 0)
+ if (c <= 0 || c > USHRT_MAX) {
c = fd_columns(STDOUT_FILENO);
-
- if (c <= 0)
- c = 80;
+ if (c <= 0)
+ c = 80;
+ }
cached_columns = c;
return cached_columns;
if (e)
(void) safe_atoi(e, &l);
- if (l <= 0)
+ if (l <= 0 || l > USHRT_MAX) {
l = fd_lines(STDOUT_FILENO);
-
- if (l <= 0)
- l = 24;
+ if (l <= 0)
+ l = 24;
+ }
cached_lines = l;
return cached_lines;
}
bool on_tty(void) {
+
+ /* We check both stdout and stderr, so that situations where pipes on the shell are used are reliably
+ * recognized, regardless if only the output or the errors are piped to some place. Since on_tty() is generally
+ * used to default to a safer, non-interactive, non-color mode of operation it's probably good to be defensive
+ * here, and check for both. Note that we don't check for STDIN_FILENO, because it should fine to use fancy
+ * terminal functionality when outputting stuff, even if the input is piped to us. */
+
if (cached_on_tty < 0)
- cached_on_tty = isatty(STDOUT_FILENO) > 0;
+ cached_on_tty =
+ isatty(STDOUT_FILENO) > 0 &&
+ isatty(STDERR_FILENO) > 0;
return cached_on_tty;
}
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- r = safe_fork("(sd-openpt)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ r = namespace_fork("(sd-openptns)", "(sd-openpt)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
+ pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child);
if (r < 0)
return r;
if (r == 0) {
pair[0] = safe_close(pair[0]);
- r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
master = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
if (master < 0)
_exit(EXIT_FAILURE);
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate_and_check("(sd-openpt)", child, 0);
+ r = wait_for_terminate_and_check("(sd-openptns)", child, 0);
if (r < 0)
return r;
if (r != EXIT_SUCCESS)
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- r = safe_fork("(sd-terminal)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ r = namespace_fork("(sd-terminalns)", "(sd-terminal)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG,
+ pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child);
if (r < 0)
return r;
if (r == 0) {
pair[0] = safe_close(pair[0]);
- r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
master = open_terminal(name, mode|O_NOCTTY|O_CLOEXEC);
if (master < 0)
_exit(EXIT_FAILURE);
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate_and_check("(sd-terminal)", child, 0);
+ r = wait_for_terminate_and_check("(sd-terminalns)", child, 0);
if (r < 0)
return r;
if (r != EXIT_SUCCESS)
return 0;
}
-static bool urlify_enabled(void) {
- static int cached_urlify_enabled = -1;
-
- /* Unfortunately 'less' doesn't support links like this yet ðŸ˜, hence let's disable this as long as there's a
- * pager in effect. Let's drop this check as soon as less got fixed a and enough time passed so that it's safe
- * to assume that a link-enabled 'less' version has hit most installations. */
-
- if (cached_urlify_enabled < 0) {
- int val;
-
- val = getenv_bool("SYSTEMD_URLIFY");
- if (val >= 0)
- cached_urlify_enabled = val;
- else
- cached_urlify_enabled = colors_enabled() && !pager_have();
- }
-
- return cached_urlify_enabled;
-}
-
-int terminal_urlify(const char *url, const char *text, char **ret) {
- char *n;
-
- assert(url);
-
- /* Takes an URL and a pretty string and formats it as clickable link for the terminal. See
- * https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda for details. */
-
- if (isempty(text))
- text = url;
-
- if (urlify_enabled())
- n = strjoin("\x1B]8;;", url, "\a", text, "\x1B]8;;\a");
- else
- n = strdup(text);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
-}
-
-int terminal_urlify_path(const char *path, const char *text, char **ret) {
- _cleanup_free_ char *absolute = NULL;
- struct utsname u;
- const char *url;
- int r;
-
- assert(path);
-
- /* Much like terminal_urlify() above, but takes a file system path as input
- * and turns it into a proper file:// URL first. */
-
- if (isempty(path))
- return -EINVAL;
-
- if (isempty(text))
- text = path;
-
- if (!urlify_enabled()) {
- char *n;
-
- n = strdup(text);
- if (!n)
- return -ENOMEM;
-
- *ret = n;
- return 0;
- }
-
- if (uname(&u) < 0)
- return -errno;
-
- if (!path_is_absolute(path)) {
- r = path_make_absolute_cwd(path, &absolute);
- if (r < 0)
- return r;
-
- path = absolute;
- }
-
- /* As suggested by https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda, let's include the local
- * hostname here. Note that we don't use gethostname_malloc() or gethostname_strict() since we are interested
- * in the raw string the kernel has set, whatever it may be, under the assumption that terminals are not overly
- * careful with validating the strings either. */
-
- url = strjoina("file://", u.nodename, path);
-
- return terminal_urlify(url, text, ret);
-}
-
-static int cat_file(const char *filename, bool newline) {
- _cleanup_fclose_ FILE *f = NULL;
- int r;
-
- f = fopen(filename, "re");
- if (!f)
- return -errno;
-
- printf("%s%s# %s%s\n",
- newline ? "\n" : "",
- ansi_highlight_blue(),
- filename,
- ansi_normal());
- fflush(stdout);
-
- for (;;) {
- _cleanup_free_ char *line = NULL;
+int vt_restore(int fd) {
+ static const struct vt_mode mode = {
+ .mode = VT_AUTO,
+ };
+ int r, q = 0;
- r = read_line(f, LONG_LINE_MAX, &line);
- if (r < 0)
- return log_error_errno(r, "Failed to read \"%s\": %m", filename);
- if (r == 0)
- break;
+ r = ioctl(fd, KDSETMODE, KD_TEXT);
+ if (r < 0)
+ q = log_debug_errno(errno, "Failed to set VT in text mode, ignoring: %m");
- puts(line);
+ r = vt_reset_keyboard(fd);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to reset keyboard mode, ignoring: %m");
+ if (q >= 0)
+ q = r;
}
- return 0;
-}
-
-int cat_files(const char *file, char **dropins, CatFlags flags) {
- char **path;
- int r;
-
- if (file) {
- r = cat_file(file, false);
- if (r == -ENOENT && (flags & CAT_FLAGS_MAIN_FILE_OPTIONAL))
- printf("%s# config file %s not found%s\n",
- ansi_highlight_magenta(),
- file,
- ansi_normal());
- else if (r < 0)
- return log_warning_errno(r, "Failed to cat %s: %m", file);
+ r = ioctl(fd, VT_SETMODE, &mode);
+ if (r < 0) {
+ log_debug_errno(errno, "Failed to set VT_AUTO mode, ignoring: %m");
+ if (q >= 0)
+ q = -errno;
}
- STRV_FOREACH(path, dropins) {
- r = cat_file(*path, file || path != dropins);
- if (r < 0)
- return log_warning_errno(r, "Failed to cat %s: %m", *path);
+ r = fchown(fd, 0, (gid_t) -1);
+ if (r < 0) {
+ log_debug_errno(errno, "Failed to chown VT, ignoring: %m");
+ if (q >= 0)
+ q = -errno;
}
- return 0;
-}
-
-void print_separator(void) {
-
- /* Outputs a separator line that resolves to whitespace when copied from the terminal. We do that by outputting
- * one line filled with spaces with ANSI underline set, followed by a second (empty) line. */
-
- if (underline_enabled()) {
- size_t i, c;
-
- c = columns();
-
- flockfile(stdout);
- fputs_unlocked(ANSI_UNDERLINE, stdout);
-
- for (i = 0; i < c; i++)
- fputc_unlocked(' ', stdout);
-
- fputs_unlocked(ANSI_NORMAL "\n\n", stdout);
- funlockfile(stdout);
- } else
- fputs("\n\n", stdout);
+ return q;
}