chiark / gitweb /
basic/strv: use SWAP_TWO() macro (#3602)
[elogind.git] / src / basic / terminal-util.c
index 8e101c60a18f18af2c859a9a6063434e2a574d0b..fa4f60826c9821b3f26f87ac2c6a355fde8ade02 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/socket.h>
+#include <sys/sysmacros.h>
+#include <sys/time.h>
+#include <linux/kd.h>
+#include <linux/tiocl.h>
+#include <linux/vt.h>
+#include <poll.h>
+#include <signal.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 #include <termios.h>
 #include <unistd.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <time.h>
-#include <assert.h>
-#include <poll.h>
-#include <linux/vt.h>
-#include <linux/tiocl.h>
-#include <linux/kd.h>
 
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "fs-util.h"
+#include "io-util.h"
+#include "log.h"
+#include "macro.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "socket-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
 #include "terminal-util.h"
 #include "time-util.h"
-#include "process-util.h"
 #include "util.h"
-#include "fileio.h"
-#include "path-util.h"
 
 static volatile unsigned cached_columns = 0;
 static volatile unsigned cached_lines = 0;
@@ -48,7 +65,7 @@ int chvt(int vt) {
         if (fd < 0)
                 return -errno;
 
-        if (vt < 0) {
+        if (vt <= 0) {
                 int tiocl[2] = {
                         TIOCL_GETKMSGREDIRECT,
                         0
@@ -66,6 +83,7 @@ int chvt(int vt) {
         return 0;
 }
 
+#if 0 /// UNNEEDED by elogind
 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];
@@ -112,7 +130,7 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
 
         errno = 0;
         if (!fgets(line, sizeof(line), f))
-                return errno ? -errno : -EIO;
+                return errno > 0 ? -errno : -EIO;
 
         truncate_nl(line);
 
@@ -126,8 +144,6 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
         return 0;
 }
 
-/// UNNEEDED by elogind
-#if 0
 int ask_char(char *ret, const char *replies, const char *text, ...) {
         int r;
 
@@ -140,15 +156,15 @@ int ask_char(char *ret, const char *replies, const char *text, ...) {
                 char c;
                 bool need_nl = true;
 
-                if (on_tty())
-                        fputs(ANSI_HIGHLIGHT_ON, stdout);
+                if (colors_enabled())
+                        fputs(ANSI_HIGHLIGHT, stdout);
 
                 va_start(ap, text);
                 vprintf(text, ap);
                 va_end(ap);
 
-                if (on_tty())
-                        fputs(ANSI_HIGHLIGHT_OFF, stdout);
+                if (colors_enabled())
+                        fputs(ANSI_NORMAL, stdout);
 
                 fflush(stdout);
 
@@ -184,21 +200,21 @@ int ask_string(char **ret, const char *text, ...) {
                 char line[LINE_MAX];
                 va_list ap;
 
-                if (on_tty())
-                        fputs(ANSI_HIGHLIGHT_ON, stdout);
+                if (colors_enabled())
+                        fputs(ANSI_HIGHLIGHT, stdout);
 
                 va_start(ap, text);
                 vprintf(text, ap);
                 va_end(ap);
 
-                if (on_tty())
-                        fputs(ANSI_HIGHLIGHT_OFF, stdout);
+                if (colors_enabled())
+                        fputs(ANSI_NORMAL, stdout);
 
                 fflush(stdout);
 
                 errno = 0;
                 if (!fgets(line, sizeof(line), stdin))
-                        return errno ? -errno : -EIO;
+                        return errno > 0 ? -errno : -EIO;
 
                 if (!endswith(line, "\n"))
                         putchar('\n');
@@ -218,7 +234,6 @@ int ask_string(char **ret, const char *text, ...) {
                 }
         }
 }
-#endif // 0
 
 int reset_terminal_fd(int fd, bool switch_to_text) {
         struct termios termios;
@@ -297,6 +312,7 @@ int reset_terminal(const char *name) {
 
         return reset_terminal_fd(fd, true);
 }
+#endif // 0
 
 int open_terminal(const char *name, int mode) {
         int fd, r;
@@ -344,6 +360,7 @@ int open_terminal(const char *name, int mode) {
         return fd;
 }
 
+#if 0 /// UNNEEDED by elogind
 int acquire_terminal(
                 const char *name,
                 bool fail,
@@ -415,7 +432,7 @@ int acquire_terminal(
 
                 assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
 
-                /* Sometimes it makes sense to ignore TIOCSCTTY
+                /* Sometimes, it makes sense to ignore TIOCSCTTY
                  * returning EPERM, i.e. when very likely we already
                  * are have this controlling terminal. */
                 if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
@@ -483,10 +500,6 @@ int acquire_terminal(
 
         safe_close(notify);
 
-        r = reset_terminal_fd(fd, true);
-        if (r < 0)
-                log_warning_errno(r, "Failed to reset terminal: %m");
-
         return fd;
 
 fail:
@@ -495,9 +508,9 @@ fail:
 
         return r;
 }
+#endif // 0
 
-/// UNNEEDED by elogind
-#if 0
+#if 0 /// UNNEEDED by elogind
 int release_terminal(void) {
         static const struct sigaction sa_new = {
                 .sa_handler = SIG_IGN,
@@ -523,7 +536,6 @@ int release_terminal(void) {
 
         return r;
 }
-#endif // 0
 
 int terminal_vhangup_fd(int fd) {
         assert(fd >= 0);
@@ -545,8 +557,9 @@ int terminal_vhangup(const char *name) {
 }
 
 int vt_disallocate(const char *name) {
-        int fd, r;
+        _cleanup_close_ int fd = -1;
         unsigned u;
+        int r;
 
         /* Deallocate the VT if possible. If not possible
          * (i.e. because it is the active one), at least clear it
@@ -568,8 +581,6 @@ int vt_disallocate(const char *name) {
                            "\033[H"    /* move home */
                            "\033[2J",  /* clear screen */
                            10, false);
-                safe_close(fd);
-
                 return 0;
         }
 
@@ -589,7 +600,7 @@ int vt_disallocate(const char *name) {
                 return fd;
 
         r = ioctl(fd, VT_DISALLOCATE, u);
-        safe_close(fd);
+        fd = safe_close(fd);
 
         if (r >= 0)
                 return 0;
@@ -608,34 +619,9 @@ int vt_disallocate(const char *name) {
                    "\033[H"   /* move home */
                    "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
                    10, false);
-        safe_close(fd);
-
         return 0;
 }
 
-void warn_melody(void) {
-        _cleanup_close_ int fd = -1;
-
-        fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY);
-        if (fd < 0)
-                return;
-
-        /* Yeah, this is synchronous. Kinda sucks. But well... */
-
-        (void) ioctl(fd, KIOCSOUND, (int)(1193180/440));
-        usleep(125*USEC_PER_MSEC);
-
-        (void) ioctl(fd, KIOCSOUND, (int)(1193180/220));
-        usleep(125*USEC_PER_MSEC);
-
-        (void) ioctl(fd, KIOCSOUND, (int)(1193180/220));
-        usleep(125*USEC_PER_MSEC);
-
-        (void) ioctl(fd, KIOCSOUND, 0);
-}
-
-/// UNNEEDED by elogind
-#if 0
 int make_console_stdio(void) {
         int fd, r;
 
@@ -645,6 +631,10 @@ int make_console_stdio(void) {
         if (fd < 0)
                 return log_error_errno(fd, "Failed to acquire terminal: %m");
 
+        r = reset_terminal_fd(fd, true);
+        if (r < 0)
+                log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
+
         r = make_stdio(fd);
         if (r < 0)
                 return log_error_errno(r, "Failed to duplicate terminal fd: %m");
@@ -653,84 +643,6 @@ int make_console_stdio(void) {
 }
 #endif // 0
 
-int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
-        static const char status_indent[] = "         "; /* "[" STATUS "] " */
-        _cleanup_free_ char *s = NULL;
-        _cleanup_close_ int fd = -1;
-        struct iovec iovec[6] = {};
-        int n = 0;
-        static bool prev_ephemeral;
-
-        assert(format);
-
-        /* This is independent of logging, as status messages are
-         * optional and go exclusively to the console. */
-
-        if (vasprintf(&s, format, ap) < 0)
-                return log_oom();
-
-        fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
-        if (fd < 0)
-                return fd;
-
-        if (ellipse) {
-                char *e;
-                size_t emax, sl;
-                int c;
-
-                c = fd_columns(fd);
-                if (c <= 0)
-                        c = 80;
-
-                sl = status ? sizeof(status_indent)-1 : 0;
-
-                emax = c - sl - 1;
-                if (emax < 3)
-                        emax = 3;
-
-                e = ellipsize(s, emax, 50);
-                if (e) {
-                        free(s);
-                        s = e;
-                }
-        }
-
-        if (prev_ephemeral)
-                IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
-        prev_ephemeral = ephemeral;
-
-        if (status) {
-                if (!isempty(status)) {
-                        IOVEC_SET_STRING(iovec[n++], "[");
-                        IOVEC_SET_STRING(iovec[n++], status);
-                        IOVEC_SET_STRING(iovec[n++], "] ");
-                } else
-                        IOVEC_SET_STRING(iovec[n++], status_indent);
-        }
-
-        IOVEC_SET_STRING(iovec[n++], s);
-        if (!ephemeral)
-                IOVEC_SET_STRING(iovec[n++], "\n");
-
-        if (writev(fd, iovec, n) < 0)
-                return -errno;
-
-        return 0;
-}
-
-int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) {
-        va_list ap;
-        int r;
-
-        assert(format);
-
-        va_start(ap, format);
-        r = status_vprintf(status, ellipse, ephemeral, format, ap);
-        va_end(ap);
-
-        return r;
-}
-
 bool tty_is_vc(const char *tty) {
         assert(tty);
 
@@ -770,6 +682,7 @@ int vtnr_from_tty(const char *tty) {
         return i;
 }
 
+#if 0 /// UNNEEDED by elogind
 char *resolve_dev_console(char **active) {
         char *tty;
 
@@ -803,6 +716,64 @@ char *resolve_dev_console(char **active) {
         return tty;
 }
 
+int get_kernel_consoles(char ***consoles) {
+        _cleanup_strv_free_ char **con = NULL;
+        _cleanup_free_ char *line = NULL;
+        const char *active;
+        int r;
+
+        assert(consoles);
+
+        r = read_one_line_file("/sys/class/tty/console/active", &line);
+        if (r < 0)
+                return r;
+
+        active = line;
+        for (;;) {
+                _cleanup_free_ char *tty = NULL;
+                char *path;
+
+                r = extract_first_word(&active, &tty, NULL, 0);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                if (streq(tty, "tty0")) {
+                        tty = mfree(tty);
+                        r = read_one_line_file("/sys/class/tty/tty0/active", &tty);
+                        if (r < 0)
+                                return r;
+                }
+
+                path = strappend("/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(&con, path);
+                if (r < 0)
+                        return r;
+        }
+
+        if (strv_isempty(con)) {
+                log_debug("No devices found for system console");
+
+                r = strv_extend(&con, "/dev/console");
+                if (r < 0)
+                        return r;
+        }
+
+        *consoles = con;
+        con = NULL;
+        return 0;
+}
+
 bool tty_is_vc_resolve(const char *tty) {
         _cleanup_free_ char *active = NULL;
 
@@ -820,12 +791,8 @@ bool tty_is_vc_resolve(const char *tty) {
         return tty_is_vc(tty);
 }
 
-/// UNNEEDED by elogind
-#if 0
 const char *default_term_for_tty(const char *tty) {
-        assert(tty);
-
-        return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
+        return tty && tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
 }
 #endif // 0
 
@@ -898,8 +865,7 @@ unsigned lines(void) {
 }
 
 /* intended to be used as a SIGWINCH sighandler */
-/// UNNEEDED by elogind
-#if 0
+#if 0 /// UNNEEDED by elogind
 void columns_lines_cache_reset(int signum) {
         cached_columns = 0;
         cached_lines = 0;
@@ -982,8 +948,6 @@ int getttyname_malloc(int fd, char **ret) {
         return 0;
 }
 
-/// UNNEEDED by elogind
-#if 0
 int getttyname_harder(int fd, char **r) {
         int k;
         char *s = NULL;
@@ -1000,7 +964,6 @@ int getttyname_harder(int fd, char **r) {
         *r = s;
         return 0;
 }
-#endif // 0
 
 int get_ctty_devnr(pid_t pid, dev_t *d) {
         int r;
@@ -1093,8 +1056,34 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
         return 0;
 }
 
-/// UNNEEDED by elogind
-#if 0
+#if 0 /// UNNEEDED by elogind
+int ptsname_malloc(int fd, char **ret) {
+        size_t l = 100;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        for (;;) {
+                char *c;
+
+                c = new(char, l);
+                if (!c)
+                        return -ENOMEM;
+
+                if (ptsname_r(fd, c, l) == 0) {
+                        *ret = c;
+                        return 0;
+                }
+                if (errno != ERANGE) {
+                        free(c);
+                        return -errno;
+                }
+
+                free(c);
+                l *= 2;
+        }
+}
+
 int ptsname_namespace(int pty, char **ret) {
         int no = -1, r;
 
@@ -1113,4 +1102,123 @@ int ptsname_namespace(int pty, char **ret) {
 
         return 0;
 }
+
+int openpt_in_namespace(pid_t pid, int flags) {
+        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
+        _cleanup_close_pair_ int pair[2] = { -1, -1 };
+        siginfo_t si;
+        pid_t child;
+        int r;
+
+        assert(pid > 0);
+
+        r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
+        if (r < 0)
+                return r;
+
+        if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+                return -errno;
+
+        child = fork();
+        if (child < 0)
+                return -errno;
+
+        if (child == 0) {
+                int master;
+
+                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);
+
+                if (unlockpt(master) < 0)
+                        _exit(EXIT_FAILURE);
+
+                if (send_one_fd(pair[1], master, 0) < 0)
+                        _exit(EXIT_FAILURE);
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        pair[1] = safe_close(pair[1]);
+
+        r = wait_for_terminate(child, &si);
+        if (r < 0)
+                return r;
+        if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+                return -EIO;
+
+        return receive_one_fd(pair[0], 0);
+}
+
+int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
+        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
+        _cleanup_close_pair_ int pair[2] = { -1, -1 };
+        siginfo_t si;
+        pid_t child;
+        int r;
+
+        r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
+        if (r < 0)
+                return r;
+
+        if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+                return -errno;
+
+        child = fork();
+        if (child < 0)
+                return -errno;
+
+        if (child == 0) {
+                int master;
+
+                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);
+
+                if (send_one_fd(pair[1], master, 0) < 0)
+                        _exit(EXIT_FAILURE);
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        pair[1] = safe_close(pair[1]);
+
+        r = wait_for_terminate(child, &si);
+        if (r < 0)
+                return r;
+        if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+                return -EIO;
+
+        return receive_one_fd(pair[0], 0);
+}
 #endif // 0
+
+bool colors_enabled(void) {
+        static int enabled = -1;
+
+        if (_unlikely_(enabled < 0)) {
+                const char *colors;
+
+                colors = getenv("SYSTEMD_COLORS");
+                if (colors)
+                        enabled = parse_boolean(colors) != 0;
+                else if (streq_ptr(getenv("TERM"), "dumb"))
+                        enabled = false;
+                else
+                        enabled = on_tty();
+        }
+
+        return enabled;
+}