]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib/pty: allow use callback from mainloop
authorKarel Zak <kzak@redhat.com>
Tue, 30 Jul 2019 12:29:20 +0000 (14:29 +0200)
committerKarel Zak <kzak@redhat.com>
Tue, 8 Oct 2019 11:11:54 +0000 (13:11 +0200)
This allows to control mainloop behavior from PTY applications. For
example you can write to child (shell) process independently on the
current stdin.

Signed-off-by: Karel Zak <kzak@redhat.com>
include/pty-session.h
lib/Makemodule.am
lib/pty-session.c

index 908e75fc74b93470092aa65d86adc12bb9e2ecd0..2f685157d294b51a9a25cfee910e1b2dab144c9c 100644 (file)
@@ -9,10 +9,13 @@
 #include <pty.h>
 #include <termios.h>
 #include <signal.h>
+#include <sys/time.h>
 
 struct ul_pty_callbacks {
        void (*child_wait)(void *);
        void (*child_sigstop)(void *);
+
+       int (*mainloop)(void *);
 };
 
 struct ul_pty {
@@ -31,6 +34,8 @@ struct ul_pty {
 
        pid_t           child;
 
+       struct timeval  next_callback_time;
+
        unsigned int isterm:1;          /* is stdin terminal? */
 };
 
@@ -50,4 +55,7 @@ void ul_pty_cleanup(struct ul_pty *pty);
 void ul_pty_init_slave(struct ul_pty *pty);
 int ul_pty_proxy_master(struct ul_pty *pty);
 
+void ul_pty_set_mainloop_time(struct ul_pty *pty, struct timeval *tv);
+int ul_pty_get_childfd(struct ul_pty *pty);
+
 #endif /* UTIL_LINUX_PTY_H */
index ee5b68dbcb7a8a1ea012b3a41d9fc08a7cf77975..d00ef317b6b6398fb78341fea89c5d9f6ca668a9 100644 (file)
@@ -146,7 +146,8 @@ endif
 if HAVE_PTY
 check_PROGRAMS += test_pty
 test_pty_SOURCES = lib/pty-session.c \
-                  include/pty-session.h
+                  include/pty-session.h \
+                  lib/monotonic.c
 test_pty_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM_PTY
 test_pty_LDADD = $(LDADD) libcommon.la -lutil
 endif
index 7bb44622d62d790c02ef9e467b1852d0bf6bc8da..709c55bb70353b6dd116f8be57b8be56c9255e16 100644 (file)
@@ -23,6 +23,7 @@
 #include "all-io.h"
 #include "ttyutils.h"
 #include "pty-session.h"
+#include "monotonic.h"
 #include "debug.h"
 
 static UL_DEBUG_DEFINE_MASK(ulpty);
@@ -95,6 +96,12 @@ void ul_pty_set_child(struct ul_pty *pty, pid_t child)
        pty->child = child;
 }
 
+int ul_pty_get_childfd(struct ul_pty *pty)
+{
+       assert(pty);
+       return pty->master;
+}
+
 /* it's active when signals are redurected to sigfd */
 int ul_pty_is_running(struct ul_pty *pty)
 {
@@ -102,6 +109,19 @@ int ul_pty_is_running(struct ul_pty *pty)
        return pty->sigfd >= 0;
 }
 
+void ul_pty_set_mainloop_time(struct ul_pty *pty, struct timeval *tv)
+{
+       assert(pty);
+       if (!tv) {
+               DBG(IO, ul_debugobj(pty, "mainloop time: clear"));
+               timerclear(&pty->next_callback_time);
+       } else {
+               pty->next_callback_time.tv_sec = tv->tv_sec;
+               pty->next_callback_time.tv_usec = tv->tv_usec;
+               DBG(IO, ul_debugobj(pty, "mainloop time: %ld.%06ld", tv->tv_sec, tv->tv_usec));
+       }
+}
+
 /* call me before fork() */
 int ul_pty_setup(struct ul_pty *pty)
 {
@@ -192,8 +212,7 @@ static int write_output(char *obuf, ssize_t bytes)
        return 0;
 }
 
-static int write_to_child(struct ul_pty *pty,
-                         char *buf, size_t bufsz)
+static int write_to_child(struct ul_pty *pty, char *buf, size_t bufsz)
 {
        return write_all(pty->master, buf, bufsz);
 }
@@ -235,6 +254,15 @@ static void write_eof_to_child(struct ul_pty *pty)
        write_to_child(pty, &c, sizeof(char));
 }
 
+static int mainloop_callback(struct ul_pty *pty)
+{
+       if (!pty->callbacks.mainloop)
+               return 0;
+
+       DBG(IO, ul_debugobj(pty, "calling mainloop callback"));
+       return pty->callbacks.mainloop(pty->callback_data);
+}
+
 static int handle_io(struct ul_pty *pty, int fd, int *eof)
 {
        char buf[BUFSIZ];
@@ -372,27 +400,61 @@ int ul_pty_proxy_master(struct ul_pty *pty)
 
        while (!pty->delivered_signal) {
                size_t i;
-               int errsv;
+               int errsv, timeout;
+
+               DBG(IO, ul_debugobj(pty, "--poll() loop--"));
+
+               /* note, callback usually updates @next_callback_time */
+               if (timerisset(&pty->next_callback_time)) {
+                       struct timeval now;
+
+                       DBG(IO, ul_debugobj(pty, " callback requested"));
+                       gettime_monotonic(&now);
+                       if (timercmp(&now, &pty->next_callback_time, >)) {
+                               rc = mainloop_callback(pty);
+                               if (rc)
+                                       break;
+                       }
+               }
+
+               /* set timeout */
+               if (timerisset(&pty->next_callback_time)) {
+                       struct timeval now, rest;
 
-               DBG(IO, ul_debugobj(pty, "calling poll()"));
+                       gettime_monotonic(&now);
+                       timersub(&pty->next_callback_time, &now, &rest);
+                       timeout = (rest.tv_sec * 1000) +  (rest.tv_usec / 1000);
+               } else
+                       timeout = pty->poll_timeout;
+
+               /* wait for input, signal or timeout */
+               DBG(IO, ul_debugobj(pty, "calling poll() [timeout=%dms]", timeout));
+               ret = poll(pfd, ARRAY_SIZE(pfd), timeout);
 
-               /* wait for input or signal */
-               ret = poll(pfd, ARRAY_SIZE(pfd), pty->poll_timeout);
                errsv = errno;
                DBG(IO, ul_debugobj(pty, "poll() rc=%d", ret));
 
+               /* error */
                if (ret < 0) {
                        if (errsv == EAGAIN)
                                continue;
                        rc = -errno;
                        break;
                }
+
+               /* timeout */
                if (ret == 0) {
-                       DBG(IO, ul_debugobj(pty, "leaving poll() loop [timeout=%d]", pty->poll_timeout));
-                       rc = 0;
+                       if (timerisset(&pty->next_callback_time)) {
+                               rc = mainloop_callback(pty);
+                               if (rc == 0)
+                                       continue;
+                       } else
+                               rc = 0;
+
+                       DBG(IO, ul_debugobj(pty, "leaving poll() loop [timeout=%d, rc=%d]", pty->poll_timeout, rc));
                        break;
                }
-
+               /* event */
                for (i = 0; i < ARRAY_SIZE(pfd); i++) {
                        rc = 0;