#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
-#include <sys/socket.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/types.h>
int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
_cleanup_free_ char *line = NULL;
struct termios old_termios;
- int r;
+ int r, fd;
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) {
+ /* If this is a terminal, then switch canonical mode off, so that we can read a single
+ * character. (Note that fmemopen() streams do not have an fd associated with them, let's handle that
+ * nicely.) */
+ fd = fileno(f);
+ if (fd >= 0 && tcgetattr(fd, &old_termios) >= 0) {
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) {
+ if (tcsetattr(fd, TCSADRAIN, &new_termios) >= 0) {
char c;
if (t != USEC_INFINITY) {
- if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
- (void) tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+ if (fd_wait_for_event(fd, POLLIN, t) <= 0) {
+ (void) tcsetattr(fd, TCSADRAIN, &old_termios);
return -ETIMEDOUT;
}
}
r = safe_fgetc(f, &c);
- (void) tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+ (void) tcsetattr(fd, TCSADRAIN, &old_termios);
if (r < 0)
return r;
if (r == 0)
}
}
- if (t != USEC_INFINITY) {
- if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
+ if (t != USEC_INFINITY && fd > 0) {
+ /* Let's wait the specified amount of time for input. When we have no fd we skip this, under
+ * the assumption that this is an fmemopen() stream or so where waiting doesn't make sense
+ * anyway, as the data is either already in the stream or cannot possible be placed there
+ * while we access the stream */
+
+ if (fd_wait_for_event(fd, POLLIN, t) <= 0)
return -ETIMEDOUT;
}
}
int ask_string(char **ret, const char *text, ...) {
+ _cleanup_free_ char *line = NULL;
+ va_list ap;
int r;
assert(ret);
assert(text);
- for (;;) {
- _cleanup_free_ char *line = NULL;
- va_list ap;
-
- if (colors_enabled())
- fputs(ANSI_HIGHLIGHT, stdout);
+ if (colors_enabled())
+ fputs(ANSI_HIGHLIGHT, stdout);
- va_start(ap, text);
- vprintf(text, ap);
- va_end(ap);
+ va_start(ap, text);
+ vprintf(text, ap);
+ va_end(ap);
- if (colors_enabled())
- fputs(ANSI_NORMAL, stdout);
+ if (colors_enabled())
+ fputs(ANSI_NORMAL, stdout);
- fflush(stdout);
+ fflush(stdout);
- r = read_line(stdin, LONG_LINE_MAX, &line);
- if (r < 0)
- return r;
- if (r == 0)
- return -EIO;
+ r = read_line(stdin, LONG_LINE_MAX, &line);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EIO;
- if (!isempty(line)) {
- *ret = TAKE_PTR(line);
- return 0;
- }
- }
+ *ret = TAKE_PTR(line);
+ return 0;
}
int reset_terminal_fd(int fd, bool switch_to_text) {
}
int vt_disallocate(const char *name) {
- _cleanup_close_ int fd = -1;
- const char *e, *n;
- unsigned u;
+ const char *e;
int r;
/* Deallocate the VT if possible. If not possible
* (i.e. because it is the active one), at least clear it
- * entirely (including the scrollback buffer) */
+ * entirely (including the scrollback buffer). */
e = path_startswith(name, "/dev/");
if (!e)
return -EINVAL;
- if (!tty_is_vc(name)) {
- /* So this is not a VT. I guess we cannot deallocate
- * it then. But let's at least clear the screen */
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
-
- loop_write(fd,
- "\033[r" /* clear scrolling region */
- "\033[H" /* move home */
- "\033[2J", /* clear screen */
- 10, false);
- return 0;
- }
+ if (tty_is_vc(name)) {
+ _cleanup_close_ int fd = -1;
+ unsigned u;
+ const char *n;
- n = startswith(e, "tty");
- if (!n)
- return -EINVAL;
-
- r = safe_atou(n, &u);
- if (r < 0)
- return r;
+ n = startswith(e, "tty");
+ if (!n)
+ return -EINVAL;
- if (u <= 0)
- return -EINVAL;
+ r = safe_atou(n, &u);
+ if (r < 0)
+ return r;
- /* Try to deallocate */
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
- if (fd < 0)
- return fd;
+ if (u <= 0)
+ return -EINVAL;
- r = ioctl(fd, VT_DISALLOCATE, u);
- fd = safe_close(fd);
+ /* Try to deallocate */
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
+ if (fd < 0)
+ return fd;
- if (r >= 0)
- return 0;
+ r = ioctl(fd, VT_DISALLOCATE, u);
+ if (r >= 0)
+ return 0;
+ if (errno != EBUSY)
+ return -errno;
+ }
- if (errno != EBUSY)
- return -errno;
+ /* So this is not a VT (in which case we cannot deallocate it),
+ * or we failed to deallocate. Let's at least clear the screen. */
- /* Couldn't deallocate, so let's clear it fully with
- * scrollback */
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
- if (fd < 0)
- return fd;
+ _cleanup_close_ int fd2 = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd2 < 0)
+ return fd2;
- loop_write(fd,
- "\033[r" /* clear scrolling region */
- "\033[H" /* move home */
- "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
- 10, false);
+ (void) loop_write(fd2,
+ "\033[r" /* clear scrolling region */
+ "\033[H" /* move home */
+ "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
+ 10, false);
return 0;
}
int make_console_stdio(void) {
int fd, r;
- /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
+ /* Make /dev/console the controlling terminal and stdin/stdout/stderr, if we can. If we can't use
+ * /dev/null instead. This is particularly useful if /dev/console is turned off, e.g. if console=null
+ * is specified on the kernel command line. */
fd = acquire_terminal("/dev/console", ACQUIRE_TERMINAL_FORCE|ACQUIRE_TERMINAL_PERMISSIVE, USEC_INFINITY);
- if (fd < 0)
- return log_error_errno(fd, "Failed to acquire terminal: %m");
+ if (fd < 0) {
+ log_warning_errno(fd, "Failed to acquire terminal, using /dev/null stdin/stdout/stderr instead: %m");
- r = reset_terminal_fd(fd, true);
- if (r < 0)
- log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
+ r = make_null_stdio();
+ if (r < 0)
+ return log_error_errno(r, "Failed to make /dev/null stdin/stdout/stderr: %m");
- r = rearrange_stdio(fd, fd, fd); /* This invalidates 'fd' both on success and on failure. */
- if (r < 0)
- return log_error_errno(r, "Failed to make terminal stdin/stdout/stderr: %m");
+ } else {
+ r = reset_terminal_fd(fd, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
- reset_terminal_feature_caches();
+ r = rearrange_stdio(fd, fd, fd); /* This invalidates 'fd' both on success and on failure. */
+ if (r < 0)
+ return log_error_errno(r, "Failed to make terminal stdin/stdout/stderr: %m");
+ }
+ reset_terminal_feature_caches();
return 0;
}
p = line;
for (;;) {
- _cleanup_free_ char *tty = NULL;
- char *path;
+ _cleanup_free_ char *tty = NULL, *path = NULL;
r = extract_first_word(&p, &tty, NULL, 0);
if (r < 0)
return r;
}
- path = strappend("/dev/", tty);
+ path = path_join("/dev", tty);
if (!path)
return -ENOMEM;
if (access(path, F_OK) < 0) {
log_debug_errno(errno, "Console device %s is not accessible, skipping: %m", path);
- free(path);
continue;
}
- r = strv_consume(&l, path);
+ r = strv_consume(&l, TAKE_PTR(path));
if (r < 0)
return r;
}
int fd_columns(int fd) {
struct winsize ws = {};
+ if (fd < 0)
+ return -EBADF;
+
if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
return -errno;
int fd_lines(int fd) {
struct winsize ws = {};
+ if (fd < 0)
+ return -EBADF;
+
if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
return -errno;
}
}
-int ptsname_namespace(int pty, char **ret) {
+int openpt_allocate(int flags, char **ret_slave) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ fd = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (ret_slave) {
+ r = ptsname_malloc(fd, &p);
+ if (r < 0)
+ return r;
+
+ if (!path_startswith(p, "/dev/pts/"))
+ return -EINVAL;
+ }
+
+ if (unlockpt(fd) < 0)
+ return -errno;
+
+ if (ret_slave)
+ *ret_slave = TAKE_PTR(p);
+
+ return TAKE_FD(fd);
+}
+
+static int ptsname_namespace(int pty, char **ret) {
int no = -1, r;
/* Like ptsname(), but doesn't assume that the path is
return 0;
}
-int openpt_in_namespace(pid_t pid, int flags) {
- _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
+int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave) {
+ _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1, fd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
pid_t child;
int r;
if (r < 0)
return r;
if (r == 0) {
- int master;
-
pair[0] = safe_close(pair[0]);
- master = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
- if (master < 0)
- _exit(EXIT_FAILURE);
-
- if (unlockpt(master) < 0)
+ fd = openpt_allocate(flags, NULL);
+ if (fd < 0)
_exit(EXIT_FAILURE);
- if (send_one_fd(pair[1], master, 0) < 0)
+ if (send_one_fd(pair[1], fd, 0) < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
if (r != EXIT_SUCCESS)
return -EIO;
- return receive_one_fd(pair[0], 0);
+ fd = receive_one_fd(pair[0], 0);
+ if (fd < 0)
+ return fd;
+
+ if (ret_slave) {
+ r = ptsname_namespace(fd, ret_slave);
+ if (r < 0)
+ return r;
+ }
+
+ return TAKE_FD(fd);
}
int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
val = getenv_bool("SYSTEMD_COLORS");
if (val >= 0)
cached_colors_enabled = val;
+
+ else if (getenv("NO_COLOR"))
+ /* We only check for the presence of the variable; value is ignored. */
+ cached_colors_enabled = false;
+
else if (getpid_cached() == 1)
/* PID1 outputs to the console without holding it open all the time */
cached_colors_enabled = !getenv_terminal_is_dumb();
if (b >= 0)
return b;
+ if (getenv("NO_COLOR"))
+ return false;
+
if (getenv_for_pid(1, "TERM", &s) <= 0)
(void) proc_cmdline_get_key("TERM", 0, &s);
};
int r, q = 0;
- r = ioctl(fd, KDSETMODE, KD_TEXT);
- if (r < 0)
+ if (ioctl(fd, KDSETMODE, KD_TEXT) < 0)
q = log_debug_errno(errno, "Failed to set VT in text mode, ignoring: %m");
r = vt_reset_keyboard(fd);
q = r;
}
- r = ioctl(fd, VT_SETMODE, &mode);
- if (r < 0) {
+ if (ioctl(fd, VT_SETMODE, &mode) < 0) {
log_debug_errno(errno, "Failed to set VT_AUTO mode, ignoring: %m");
if (q >= 0)
q = -errno;
}
- r = fchown(fd, 0, (gid_t) -1);
+ r = fchmod_and_chown(fd, TTY_MODE, 0, (gid_t) -1);
if (r < 0) {
- log_debug_errno(errno, "Failed to chown VT, ignoring: %m");
+ log_debug_errno(r, "Failed to chmod()/chown() VT, ignoring: %m");
if (q >= 0)
- q = -errno;
+ q = r;
}
return q;
return 0;
}
+
+void get_log_colors(int priority, const char **on, const char **off, const char **highlight) {
+ /* Note that this will initialize output variables only when there's something to output.
+ * The caller must pre-initalize to "" or NULL as appropriate. */
+
+ if (priority <= LOG_ERR) {
+ if (on)
+ *on = ANSI_HIGHLIGHT_RED;
+ if (off)
+ *off = ANSI_NORMAL;
+ if (highlight)
+ *highlight = ANSI_HIGHLIGHT;
+
+ } else if (priority <= LOG_WARNING) {
+ if (on)
+ *on = ANSI_HIGHLIGHT_YELLOW;
+ if (off)
+ *off = ANSI_NORMAL;
+ if (highlight)
+ *highlight = ANSI_HIGHLIGHT;
+
+ } else if (priority <= LOG_NOTICE) {
+ if (on)
+ *on = ANSI_HIGHLIGHT;
+ if (off)
+ *off = ANSI_NORMAL;
+ if (highlight)
+ *highlight = ANSI_HIGHLIGHT_RED;
+
+ } else if (priority >= LOG_DEBUG) {
+ if (on)
+ *on = ANSI_GREY;
+ if (off)
+ *off = ANSI_NORMAL;
+ if (highlight)
+ *highlight = ANSI_HIGHLIGHT_RED;
+ }
+}