From 4d5b2fed8ff6d3678a179bb3396bec7701720217 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Tue, 30 Jul 2019 14:29:20 +0200 Subject: [PATCH] lib/pty: allow use callback from mainloop 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 --- include/pty-session.h | 8 +++++ lib/Makemodule.am | 3 +- lib/pty-session.c | 80 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 81 insertions(+), 10 deletions(-) diff --git a/include/pty-session.h b/include/pty-session.h index 908e75fc74..2f685157d2 100644 --- a/include/pty-session.h +++ b/include/pty-session.h @@ -9,10 +9,13 @@ #include #include #include +#include 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 */ diff --git a/lib/Makemodule.am b/lib/Makemodule.am index ee5b68dbcb..d00ef317b6 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -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 diff --git a/lib/pty-session.c b/lib/pty-session.c index 7bb44622d6..709c55bb70 100644 --- a/lib/pty-session.c +++ b/lib/pty-session.c @@ -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; -- 2.47.3