Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package blog for openSUSE:Factory checked in at 2026-07-10 17:34:30 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/blog (Old) and /work/SRC/openSUSE:Factory/.blog.new.1991 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "blog" Fri Jul 10 17:34:30 2026 rev:28 rq:1364730 version:2.46 Changes: -------- --- /work/SRC/openSUSE:Factory/blog/blog.changes 2026-07-06 12:28:01.926437575 +0200 +++ /work/SRC/openSUSE:Factory/.blog.new.1991/blog.changes 2026-07-10 17:35:11.792585430 +0200 @@ -1,0 +2,110 @@ +Thu Jul 9 13:40:53 UTC 2026 - Dr. Werner Fink <[email protected]> + +- Update to version 2.46 + Implement Plymouth-compatible display-message and hide-message commands + + This commit adds support for the display-message and hide-message + Plymouth commands, allowing external tools to print status updates + directly to the boot console via blogctl. + + Key changes: + Add MAGIC_SHOW_MSG and MAGIC_HIDE_MSG protocols to blogctl and + parse the --text= argument using getopt_long_only(). + Broadcast display-message payloads to all active hardware consoles + via c->out() to ensure immediate visual feedback, while simultaneously + logging it to /var/log/boot.log via copylog(). + Add \r\n line endings to printed messages to prevent the staircase + effect on serial terminals operating in raw mode. + Handle hide-message as a visual no-op (replying with an ACK only) + to deliberately avoid ANSI clear-screen sequences that would corrupt + the output on line-based, half-duplex consoles like the s390x 3215. + Update the blogctl.8 manual page to document the new commands and + their specific behavior regarding screen clearing. + + Implement Plymouth-compatible ask-for-password and ask-question commands + + This commit extends blogctl to act as a lightweight, fully functional + replacement for Plymouth's interactive prompt commands, allowing scripts + to securely ask for passwords or questions on the system console. + + Key changes: + Add support for ask-for-password and ask-question in blogctl. + Use getopt_long_only() to robustly parse GNU-style long options + (--prompt, --command, --number-of-tries, --dont-pause-progress). + Offload the retry-loop and command execution (popen) entirely to + the blogctl client, keeping the blogd daemon simple and secure. + Fix an epoll EEXIST crash in console.c by separating the daemon's + process state (asking) from the requested prompt type (ask_mode), + resolving a race condition during socket handler registration. + Comply strictly with the Plymouth protocol: enforce 1-byte prompt + length limits, handle little-endian length headers (le32toh), + and ensure null-terminated payloads via asprintf. + Update the blogctl.8 manual page to document the new commands. + + Back to use constructor for initial seed + for the process lifetime and inherited across fork() + + Make tty wait logic real and fix SIGHUP/inotify cleanup + Better in-memory reversible obfuscation for cached passwords + This is not cryptographic protection against a memory-reading attacker. + It is only meant to avoid trivial/plaintext exposure in memory. + + Fix resource leaks and harden IPC validations + This commit addresses several issues found by static code analysis, + focusing on memory hygiene, file descriptor management, and robust + error handling. + + Key changes: + Fix memory and file descriptor leaks in error paths (proc.c, shm.c, tty.c) + and ensure all pending coldstart requests are freed on shutdown. + Harden the systemd ask-password interface by adding boundary checks + for UNIX socket paths, verifying sendto payloads, and gracefully + handling missing directories during early boot without fatal errors. + Improve IPC protocol safety by strictly validating the MAGIC_CHROOT + argument (must be an absolute path) and sending an explicit NACK on failure. + Prevent stale terminal states on s390x by clearing the VMCP cache + prior to parsing a new QUERY TERMINAL reply. + + User the --wait option of the blogctl quit command + Add an --wait option specially for quit of blogctl + + Fix gh##7 + setting blog.silent=true causes it to be non-silent + + Make shared library a executable + + Refactor daemon to use synchronous signalfd and epoll event loop + This commit migrates the core architecture from traditional, asynchronous + signal handling to a modern, synchronous event loop using signalfd. By + integrating signal handling directly into the epoll multiplexer, we eliminate + race conditions, unexpected EINTR interruptions during I/O operations, and + complex reentrancy issues. + + Key changes: + Add setup_signalfd() to block essential signals (SIGINT, SIGQUIT, SIGTERM, + SIGSYS, SIGIO, SIGCHLD) globally and route them through a dedicated file + descriptor monitored by epoll. + Shield epoll_pwait() using a fully populated signal mask (omask) to + guarantee uninterrupted I/O processing. + Prevent socket handlers (MAGIC_SYS_INIT, MAGIC_CLOSE) from accidentally + unblocking signals and resetting dispositions when signalfd is active. + Sanitize the inherited signal mask in forked child processes + (e.g., systemd password prompts on tty) by resetting it to an empty + mask via sigprocmask(), ensuring agents run without restrictions. + Replace the ALIGNED_SIZEOF macro with a cleaner align_up(type, boundary) + macro and fix memset() sizes to correctly zero out trailing padding bytes. + Update the Makefile to compile a stripped-down fallback object + (signals_stripped.o compiled with -DNO_SIGNALFD) to ensure the passive + libblogger.so library remains unaffected by the signalfd architecture. + + Some minor changes in quit and umount unit services + + The HVC consoles are like sclp console + and can do colours as well + +------------------------------------------------------------------- +Mon Jul 6 09:10:22 UTC 2026 - Dr. Werner Fink <[email protected]> + +- Silent some rpmlint warnings + +------------------------------------------------------------------- Old: ---- showconsole-2.45.tar.gz New: ---- showconsole-2.46.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ blog.spec ++++++ --- /var/tmp/diff_new_pack.jpigmN/_old 2026-07-10 17:35:14.772687399 +0200 +++ /var/tmp/diff_new_pack.jpigmN/_new 2026-07-10 17:35:14.776687536 +0200 @@ -17,7 +17,7 @@ Name: blog -Version: 2.45 +Version: 2.46 %define sonum 2 Release: 0 Summary: Boot logging @@ -66,6 +66,7 @@ Requires: udev Conflicts: plymouth Conflicts: plymouth-dracut +BuildArch: noarch %description plymouth The Blogd daemon can be used as a replacement for Plymouth in situations where @@ -143,7 +144,6 @@ %endif %files -%defattr(-,root,root) %license COPYING %doc README %if 0%{?suse_version} < 1550 @@ -169,15 +169,13 @@ %doc %{_mandir}/man8/showconsole.8.gz %files -n libblogger%{sonum} -%{_libdir}/libblogger.so.* +%attr(0755,root,root) %{_libdir}/libblogger.so.* %files devel -%defattr(-,root,root) %{_includedir}/libblogger.h %{_libdir}/libblogger.so %files plymouth -%defattr(-,root,root) %dir %{_prefix}/lib/dracut/ %dir %{_prefix}/lib/dracut/modules.d/ %dir %{_prefix}/lib/dracut/modules.d/99blog/ ++++++ blog-rpmlintrc ++++++ --- /var/tmp/diff_new_pack.jpigmN/_old 2026-07-10 17:35:14.808688631 +0200 +++ /var/tmp/diff_new_pack.jpigmN/_new 2026-07-10 17:35:14.816688905 +0200 @@ -1,3 +1,4 @@ addFilter(".*systemd-service-without-service_del_p.*") addFilter(".*systemd-service-without-service_add_p.*") +addFilter(".*W:.*no-%check-section.*") ++++++ showconsole-2.45.tar.gz -> showconsole-2.46.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/Makefile new/showconsole-2.46/Makefile --- old/showconsole-2.45/Makefile 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/Makefile 2026-07-09 15:29:51.000000000 +0200 @@ -15,7 +15,7 @@ DEBUG = DESTDIR = MAJOR := 2 -MINOR := 45 +MINOR := 46 VERSION := $(MAJOR).$(MINOR) DATE = $(shell date +'%d%b%y' | tr '[:lower:]' '[:upper:]') COPTS = @@ -48,6 +48,8 @@ INSTCON = install $(INSTDOCFLAGS) INSTSCRFLAGS = -c -m 0755 INSTSCR = install $(INSTSCRFLAGS) + INSTSOFLAGS = -c -m 0755 + INSTSO = install $(INSTSOFLAGS) LINK = ln -sf AR ?= ar LD ?= ld -shared -O2 @@ -86,11 +88,14 @@ libconsole.a: $(patsubst %.c,%.o,$(libfiles)) $(L)libconsole.h listing.h $(AR) rusv $@ $^ +$(L)signals_stripped.o: $(L)signals.c + $(CC) $(CFLAGS) $(CLOOP) -DNO_SIGNALFD -D_REENTRANT -fPIC -o $@ -c $< -pthread + libblogger.o: libconsole.a libblogger.o: libblogger.c libblogger.h $(CC) $(CFLAGS) $(CLOOP) -fPIC -c $< -libblogger.so: libblogger.o libconsole/signals.o +libblogger.so: libblogger.o libconsole/signals_stripped.o $(CC) -shared -Wl,-O2 -Wl,-soname=$@.$(MAJOR) -o $@ $^ showconsole: showconsole.c libconsole.a @@ -131,7 +136,7 @@ $(INSTBIN) blogctl $(DESTDIR)$(SBINDIR)/ $(INSTDOC) blogctl.8 $(DESTDIR)$(SDOCDIR)/ $(INSTCON) libblogger.h $(DESTDIR)$(INCDIR)/ - $(INSTCON) libblogger.so $(DESTDIR)$(LIBDIR)/libblogger.so.$(MAJOR).$(MINOR) + $(INSTSO) libblogger.so $(DESTDIR)$(LIBDIR)/libblogger.so.$(MAJOR).$(MINOR) $(INSTBIN) isserial $(DESTDIR)$(SBINDIR)/ $(INSTDOC) isserial.8 $(DESTDIR)$(SDOCDIR)/ $(INSTSCR) module-setup.sh $(DESTDIR)$(DRACUTMOD)/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/blog-quit.service new/showconsole-2.46/blog-quit.service --- old/showconsole-2.45/blog-quit.service 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/blog-quit.service 2026-07-09 15:29:51.000000000 +0200 @@ -2,10 +2,10 @@ Description=Tell blogd to Quit DefaultDependencies=no Conflicts=shutdown.target -After=rc-local.service blog.service systemd-user-sessions.service +After=default.target rc-local.service blog.service systemd-user-sessions.service [Service] Type=oneshot TimeoutSec=20 RemainAfterExit=yes -ExecStart=-/sbin/blogctl quit +ExecStart=-/sbin/blogctl quit --wait diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/blog-umount.service.in new/showconsole-2.46/blog-umount.service.in --- old/showconsole-2.45/blog-umount.service.in 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/blog-umount.service.in 2026-07-09 15:29:51.000000000 +0200 @@ -10,4 +10,5 @@ Type=oneshot TimeoutSec=0 RemainAfterExit=yes +ExecStart=/bin/true ExecStop=/sbin/blogctl close diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/blogctl.8 new/showconsole-2.46/blogctl.8 --- old/showconsole-2.45/blogctl.8 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/blogctl.8 2026-07-09 15:29:51.000000000 +0200 @@ -13,7 +13,7 @@ .SH SYNOPSIS .\" .B /sbin/blogctl -.RI [ ping | quit | root=<path> | ready | close ] +.RI [ ping | quit\ [--wait] | root=<path> | ready | close | ask-for-password | ask-question | display-message | hide-message ] .SH DESCRIPTION .B blogctl may be used to check if a @@ -22,15 +22,67 @@ .SH COMANDS The following commands are known: .TP +.B ask-for-password +Ask the user for a password on the system console. +.RS +.TP +.BI \-\-command= STRING +Command to send the password to via standard input. +.TP +.BI \-\-prompt= STRING +Message to display when asking for the password. +.TP +.BI \-\-number-of-tries= INTEGER +Number of times to ask before giving up (requires \fB\-\-command\fR). +.TP +.B \-\-dont-pause-progress +Silently ignored by \fBblogctl\fR (provided for plymouth compatibility). +.RE +.TP +.B ask-question +Ask the user a question on the system console. +.RS +.TP +.BI \-\-command= STRING +Command to send the answer to via standard input. +.TP +.BI \-\-prompt= STRING +Message to display when asking the question. +.TP +.B \-\-dont-pause-progress +Silently ignored by \fBblogctl\fR. +.RE +.TP +.B display-message +Display a message. \fBblogd\fR prints this message to all active hardware +consoles and injects it into the boot log stream. +.RS +.TP +.BI \-\-text= STRING +The message text to display. +.RE +.TP +.B hide-message +Hide a message. +.RS +.TP +.BI \-\-text= STRING +The message text to hide. Note: \fBblogd\fR intentionally ignores the visual +clearing of messages to prevent screen corruption and preserve the scroll +buffer on line-based hardware consoles (like s390x 3215). +.RE +.TP .B ping Send a simply ping request if .B blogd daemon is up and running. .TP -.B quit +.B quit \fR[\fB\-\-wait\fR] Cause the .B blogd -daemon to finish its logging and then gracefully quits. +daemon to finish its logging and then gracefully quits. If the optional +\fB\-\-wait\fR flag is specified, \fBblogctl\fR will block and wait +until the daemon has fully flushed the logs and closed the connection. .TP .BI root= <path> This command take a path as further value of the new root file @@ -65,7 +117,8 @@ .BR blogd (8), .BR systemd (1), .SH COPYRIGHT -2015 Werner Fink, -2015 SuSE Linux GmbH. +2015,2026 Werner Fink, +2015 SUSE Linux GmbH, +2026 SUSE Software Solutions Germany GmbH. .SH AUTHOR Werner Fink <[email protected]> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/blogctl.c new/showconsole-2.46/blogctl.c --- old/showconsole-2.45/blogctl.c 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/blogctl.c 2026-07-09 15:29:51.000000000 +0200 @@ -9,11 +9,16 @@ * (at your option) any later version. */ +#include <endian.h> /* For le32toh */ #include <err.h> +#include <getopt.h> #include <limits.h> #include <stdarg.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> +#include <poll.h> +#include <sys/socket.h> #include "libconsole.h" /* @@ -45,6 +50,10 @@ } cmds[] = { { "root=", MAGIC_CHROOT, 1, NULL }, /* New root */ { "ping", MAGIC_PING, 0, NULL }, /* Ping */ + { "ask-for-password", MAGIC_ASK_PWD, 0, NULL }, + { "ask-question", MAGIC_QUESTION, 0, NULL }, + { "display-message", MAGIC_SHOW_MSG, 0, NULL }, + { "hide-message", MAGIC_HIDE_MSG, 0, NULL }, { "ready", MAGIC_SYS_INIT, 0, NULL }, /* System ready */ { "quit", MAGIC_QUIT, 0, NULL }, /* Quit */ { "final", MAGIC_FINAL, 0, NULL }, /* Final */ @@ -80,11 +89,37 @@ return ret; } +static int wait_for_blogd_close(int fd) +{ + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN|POLLRDHUP; + pfd.revents = 0; + + while (1) { + int ret = poll(&pfd, 1, -1); + + if (ret < 0) { + if (errno == EINTR) + continue; + return -1; + } + + if (pfd.revents & (POLLHUP|POLLRDHUP|POLLIN)) { + char buf[1]; + /* Verify if socket is really down (EOF = 0 bytes) */ + int n = recv(fd, buf, sizeof(buf), MSG_DONTWAIT); + if (n == 0 || (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)) + return 0; /* down */ + } + } +} + int main(int argc, char *argv[]) { char *root = NULL; char *message, answer[2], cmd[2]; - int fdsock = -1, ret, len; + int fdsock = -1, ret, len, do_wait = 0; cmd[1] = '\0'; answer[0] = '\x15'; @@ -94,7 +129,7 @@ error("no blogd active"); while ((cmd[0] = getcmd(argc, argv)) != (char)-1) { - switch (cmd[0]) { + switch (cmd[0]) { case MAGIC_CHROOT: root = optarg; len = (int)strlen(root); @@ -112,21 +147,162 @@ case MAGIC_PING: case MAGIC_SYS_INIT: case MAGIC_QUIT: + /* Check if --wait was passed as an argument to quit */ + if (cmd[0] == MAGIC_QUIT && argv[optind] && strcmp(argv[optind], "--wait") == 0) { + do_wait = 1; + optind++; /* Consume the argument so getcmd doesn't trip over it */ + } case MAGIC_FINAL: case MAGIC_CLOSE: case MAGIC_DEACTIVATE: case MAGIC_REACTIVATE: safeout(fdsock, cmd, strlen(cmd)+1, SSIZE_MAX); break; + case MAGIC_ASK_PWD: + case MAGIC_QUESTION: { + char *prompt = NULL; + char *command = NULL; + int c, tries = 1; + + /* Parse the parameters */ + static struct option long_options[] = { + {"prompt", required_argument, 0, 'p'}, + {"command", required_argument, 0, 'c'}, + {"number-of-tries", required_argument, 0, 'n'}, + {"dont-pause-progress", no_argument, 0, 'd'}, + {0, 0, 0, 0} + }; + + /* * getopt_long_only parst ab argv[optind]. Da wir getcmd() + * benutzt haben, steht optind genau richtig auf der ersten Option. + */ + while ((c = getopt_long_only(argc, argv, "", long_options, NULL)) != -1) { + switch (c) { + case 'p': + prompt = optarg; + break; + case 'c': + command = optarg; + break; + case 'n': + tries = atoi(optarg); + break; + case 'd': + /* Ignored by blogd as it is useless */ + break; + case '?': + default: + break; + } + } + + if (!prompt) + prompt = (cmd[0] == MAGIC_ASK_PWD) ? "Password" : "Question"; + + len = (int)strlen(prompt); + if (len >= UCHAR_MAX) { + errno = EINVAL; + error("prompt string too long (max 254 chars)"); + } + + do { + message = NULL; + ret = asprintf(&message, "%c\002%c%s%n", cmd[0], len + 1, prompt, &len); + if (ret < 0) + error("can not allocate message"); + + safeout(fdsock, message, len + 1, SSIZE_MAX); + free(message); + + /* Wait for the users answer */ + if (can_read(fdsock, -1)) { + char ans; + safein(fdsock, &ans, 1); + + if (ans == '\t') { /* ANSWER_MLT */ + uint32_t plen; + safein(fdsock, &plen, sizeof(plen)); + plen = le32toh(plen); /* Plymouth sends le32 */ + + char *pwd = calloc(1, plen + 1); + if (!pwd) + error("memory allocation failed"); + + safein(fdsock, pwd, plen); + + if (command) { + FILE *p = popen(command, "w"); + if (p) { + fputs(pwd, p); + if (pclose(p) == 0) { + free(pwd); + answer[0] = '\x6'; /* Fake ACK for success */ + goto end_cmd; + } + } + } else { + printf("%s\n", pwd); + free(pwd); + answer[0] = '\x6'; /* Fake ACK for success */ + goto end_cmd; + } + free(pwd); + } else if (ans == '\x5') { + /* ANSWER_ENQ: Cancel / No input */ + break; + } + } + tries--; + } while (tries > 0); + + /* If we end up here, all attempts have failed */ + answer[0] = '\x15'; /* NACK */ + goto end_cmd; + } + case MAGIC_SHOW_MSG: + case MAGIC_HIDE_MSG: { + char *text = ""; + int c; + + static struct option long_options[] = { + {"text", required_argument, 0, 't'}, + {0, 0, 0, 0} + }; + + while ((c = getopt_long_only(argc, argv, "", long_options, NULL)) != -1) { + if (c == 't') + text = optarg; + } + + len = (int)strlen(text); + if (len >= UCHAR_MAX) { + errno = EINVAL; + error("message string too long (max 254 chars)"); + } + + message = NULL; + ret = asprintf(&message, "%c\002%c%s%n", cmd[0], len + 1, text, &len); + if (ret < 0) + error("can not allocate message"); + + safeout(fdsock, message, len + 1, SSIZE_MAX); + free(message); + + break; + } case '?': default: goto fail; - } + } if (can_read(fdsock, 1000)) { answer[0] = '\0'; safein(fdsock, &answer[0], sizeof(answer)); } +end_cmd: + /* If we received an ACK (0x06) and --wait was requested, block until closed */ + if (do_wait && answer[0] == '\x6') + wait_for_blogd_close(fdsock); break; /* One command per call only */ } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/blogd.c new/showconsole-2.46/blogd.c --- old/showconsole-2.45/blogd.c 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/blogd.c 2026-07-09 15:29:51.000000000 +0200 @@ -169,11 +169,12 @@ static struct sigaction saved_sigttou; static struct sigaction saved_sigtstp; static struct sigaction saved_sighup; +static struct sigaction saved_sigpipe; +#ifdef NO_SIGNALFD static struct sigaction saved_sigint; static struct sigaction saved_sigquit; static struct sigaction saved_sigterm; static struct sigaction saved_sigsys; -static struct sigaction saved_sigpipe; static void sighandle(int sig) { @@ -189,6 +190,7 @@ { nsigsys = (volatile sig_atomic_t)sig; } +#endif /* * To be able to reconnect to real tty on EIO @@ -289,12 +291,12 @@ parse_cmdline(); /* Parse the kernel command line for blog.<key>(=<val>) */ val = value_cmdline("silent"); if (val) { - if (strcmp(val, "1") == 0 || strcasecmp(val, "on") == 0 || strcasecmp(val, "yes") == 0 || strcasecmp(val, "true")) + if (strcmp(val, "1") == 0 || strcasecmp(val, "on") == 0 || strcasecmp(val, "yes") == 0 || strcasecmp(val, "true") == 0) console_silent = 1; } val = value_cmdline("coldboot"); if (val) { - if (strcmp(val, "1") == 0 || strcasecmp(val, "on") == 0 || strcasecmp(val, "yes") == 0 || strcasecmp(val, "true")) + if (strcmp(val, "1") == 0 || strcasecmp(val, "on") == 0 || strcasecmp(val, "yes") == 0 || strcasecmp(val, "true") == 0) coldboot = 1; } @@ -424,6 +426,7 @@ set_signal(SIGTSTP, &saved_sigtstp, SIG_IGN); set_signal(SIGHUP, &saved_sighup, SIG_IGN); set_signal(SIGPIPE, &saved_sigpipe, SIG_IGN); +#ifdef NO_SIGNALFD set_signal(SIGINT, &saved_sigint, sighandle); set_signal(SIGQUIT, &saved_sigquit, sighandle); set_signal(SIGTERM, &saved_sigterm, sighandle); @@ -432,6 +435,7 @@ (void)restart_sig(SIGQUIT, 1); (void)restart_sig(SIGTERM, 1); (void)restart_sig(SIGSYS, 1); +#endif list_for_each_entry(c, &lcons, node) { struct termios oldtio; @@ -654,10 +658,12 @@ reset_signal(SIGTSTP, &saved_sigtstp); reset_signal(SIGHUP, &saved_sighup); reset_signal(SIGPIPE, &saved_sigpipe); +#ifdef NO_SIGNALFD reset_signal(SIGINT, &saved_sigint); reset_signal(SIGQUIT, &saved_sigquit); reset_signal(SIGTERM, &saved_sigterm); reset_signal(SIGSYS, &saved_sigsys); +#endif } #if defined(__s390__) || defined(__s390x__) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/libconsole/coldstart.c new/showconsole-2.46/libconsole/coldstart.c --- old/showconsole-2.45/libconsole/coldstart.c 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/libconsole/coldstart.c 2026-07-09 15:29:51.000000000 +0200 @@ -32,8 +32,9 @@ void send_response_to_systemd(const char *socket_path, const char *password) { char *packet; - size_t pwd_len, packet_len; + size_t pwd_len, packet_len, sock_len; struct sockaddr_un addr; + ssize_t sent; int sock = socket(AF_UNIX, SOCK_DGRAM, 0); if (sock < 0) @@ -41,13 +42,19 @@ memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + + sock_len = strlen(socket_path); + if (sock_len >= sizeof(addr.sun_path)) { + warn("systemd ask-password socket path too long: %s", socket_path); + close(sock); + return; + } + memcpy(addr.sun_path, socket_path, sock_len + 1); /* systemd protocol requires: '+' + password (the \0 is optional) */ pwd_len = strlen(password); packet_len = pwd_len + 1; /* +1 for '+' */ packet = (char*)malloc(packet_len); - if (!packet) error("memory allocation: %m"); @@ -55,7 +62,9 @@ memcpy(packet + 1, password, pwd_len); /* Now send the packet as one datagramm (SOCK_DGRAM) */ - sendto(sock, packet, packet_len, 0, (struct sockaddr *)&addr, sizeof(addr)); + sent = sendto(sock, packet, packet_len, 0, (struct sockaddr *)&addr, sizeof(addr)); + if (sent < 0 || (size_t)sent != packet_len) + warn("failed to send password reply to %s: %m", socket_path); free(packet); close(sock); @@ -105,11 +114,11 @@ in_ask_section = 0; /* Allocate memory for our custom wrapper structure */ - if (posix_memalign((void**)&req, sizeof(void*), ALIGNED_SIZEOF(struct request)) != 0 || !req) { + if (posix_memalign((void**)&req, sizeof(void*), align_up(struct request, void*)) != 0 || !req) { fclose(fp); error("memory allocation: %m"); } - memset(req, 0, sizeof(struct request)); + memset(req, 0, align_up(struct request, void*)); while (fgets(line, sizeof(line), fp)) { char *eq, *key, *value; @@ -175,8 +184,13 @@ char full_path[MAX_LINE]; DIR *dir = opendir(dir_path); - if (!dir) - error("Failed to open systemd ask-password directory: %m"); + if (!dir) { + if (errno == ENOENT) + warn("systemd ask-password directory %s not available", dir_path); + else + error("Failed to open systemd ask-password directory %s: %m", dir_path); + return; + } while ((entry = readdir(dir)) != NULL) { if (entry->d_name[0] == '.') @@ -230,3 +244,19 @@ return 1; } + +void coldstart_free_requests(void) +{ + struct request *req, *next; + + list_for_each_entry_safe(req, next, &pwd_requests, node) { + if (req->message) + free(req->message); + if (req->socket_path) + free(req->socket_path); + delete(&req->node); + free(req); + } + + requests = NULL; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/libconsole/console.c new/showconsole-2.46/libconsole/console.c --- old/showconsole-2.45/libconsole/console.c 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/libconsole/console.c 2026-07-09 15:29:51.000000000 +0200 @@ -43,6 +43,8 @@ #include "listing.h" #include "libconsole.h" +weak_symbol(pthread_sigmask); + /* Fallback for older glibc */ #ifndef SYS_pidfd_open @@ -91,6 +93,7 @@ * Password/Passphrase is asked if true. */ volatile sig_atomic_t asking; +static int ask_mode = 1; /* 1 = Password, 2 = Question */ /* * Ths is the socket fd for the answer. @@ -157,10 +160,12 @@ } error(fmt, args); \ } while (0) -static volatile sig_atomic_t sigchild; +volatile sig_atomic_t sigchild; +#ifdef NO_SIGNALFD static void chld_handler(int sig) { ++sigchild; } +#endif /* * Console list @@ -493,8 +498,9 @@ * Signal control for writing on log file */ volatile sig_atomic_t nsigsys; -static volatile sig_atomic_t nsigio = -1; +volatile sig_atomic_t nsigio = -1; +#ifdef NO_SIGNALFD /* One shot signal handler */ static void sigio(int sig) { @@ -503,6 +509,7 @@ set_signal(sig, NULL, SIG_IGN); nsigio = sig; } +#endif /* * Our transfer buffer from system console to the devices @@ -568,13 +575,34 @@ void prepareIO(int (*rfunc)(int), const int listen, const int input) { struct console *c; +#ifndef NO_SIGNALFD + sigset_t sfd_mask; + + sigemptyset(&sfd_mask); + sigaddset(&sfd_mask, SIGINT); + sigaddset(&sfd_mask, SIGQUIT); + sigaddset(&sfd_mask, SIGTERM); + sigaddset(&sfd_mask, SIGSYS); + sigaddset(&sfd_mask, SIGIO); + sigaddset(&sfd_mask, SIGCHLD); +#endif + + epfd = epoll_create1(EPOLL_CLOEXEC); + if (epfd < 0) + error("can not open epoll file descriptor"); + +#ifndef NO_SIGNALFD + /* Block signals globally and register the file descriptor to epoll */ + setup_signalfd(sfd_mask); +#endif (void)sigfillset(&omask); +#ifdef NO_SIGNALFD (void)sigdelset(&omask, SIGQUIT); (void)sigdelset(&omask, SIGTERM); (void)sigdelset(&omask, SIGSYS); (void)sigdelset(&omask, SIGIO); - +#endif vc_reconnect = rfunc; fdsock = listen; /* We use only ONE socket ... see also safeout() */ fdread = input; @@ -595,10 +623,6 @@ } } - epfd = epoll_create1(EPOLL_CLOEXEC); - if (epfd < 0) - error("can not open epoll file descriptor"); - if (fdread >= 0) epoll_addread(fdread, &epoll_console_in); if (fdfifo >= 0) @@ -778,8 +802,9 @@ /* Currently no thread active */ nsigio = 0; atboot = 1; - +#ifdef NO_SIGNALFD set_signal(SIGIO, NULL, sigio); +#endif } if (flog) { @@ -805,7 +830,9 @@ } if (nsigio < 0) { nsigio = SIGIO; +#ifdef NO_SIGNALFD (void)set_signal(SIGIO, NULL, SIG_IGN); +#endif } } } @@ -881,6 +908,8 @@ pwprompt = NULL; } + coldstart_free_requests(); + list_for_each_entry(c, &lcons, node) { if (c->fd < 0) continue; @@ -948,10 +977,10 @@ if (!cons) error("missing console pointer"); - if (posix_memalign((void**)&newc, sizeof(void*), ALIGNED_SIZEOF(struct console)+strlen(name)+1) != 0 || !newc) + if (posix_memalign((void**)&newc, sizeof(void*), align_up(struct console, void*)+strlen(name)+1) != 0 || !newc) error("memory allocation"); - newc->tty = ((char*)newc)+ALIGNED_SIZEOF(struct console); + newc->tty = ((char*)newc)+align_up(struct console, void*); strcpy(newc->tty, name); newc->flags = cflags; newc->dev = dev; @@ -1051,6 +1080,8 @@ flags |= CON_SCLP; else if (maj == 227 && min >= 1) flags |= CON_3270; + else if (maj == 229 && min >= 0) + flags |= CON_HVC; #endif consalloc(&c, tty, flags, makedev(maj, min), io); free(tty); @@ -1445,6 +1476,7 @@ switch (magic[0]) { case MAGIC_ASK_PWD: + case MAGIC_QUESTION: if (magic[1] != '\002') { errno = EINVAL; warn("Got password invalid request for prompt"); @@ -1463,6 +1495,7 @@ if (pwprompt) free(pwprompt); pwprompt = strdup(arg); + ask_mode = (magic[0] == MAGIC_QUESTION) ? 2 : 1; epoll_answer_once(fd, &epoll_socket_answer); goto job; @@ -1477,6 +1510,7 @@ } password[0] = '\0'; pwprompt = strdup(arg); + ask_mode = (magic[0] == MAGIC_QUESTION) ? 2 : 1; epoll_answer_once(fd, &epoll_socket_answer); @@ -1493,9 +1527,11 @@ break; case MAGIC_CHROOT: - if (magic[1] != '\002') { + if (magic[1] != '\002' || !arg || !*arg || arg[0] != '/') { errno = EINVAL; warn("Got password invalid chroot request"); + enqry = ANSWER_NCK; + safeout(fd, enqry, strlen(enqry)+1, SSIZE_MAX); goto out; } @@ -1510,9 +1546,10 @@ enqry = ANSWER_ACK; safeout(fd, enqry, strlen(enqry)+1, SSIZE_MAX); - +#ifdef NO_SIGNALFD if (nsigio == 0) set_signal(SIGIO, NULL, SIG_IGN); +#endif nsigio = SIGIO; break; @@ -1667,13 +1704,54 @@ enqry = ANSWER_ACK; safeout(fd, enqry, strlen(enqry)+1, SSIZE_MAX); - +#ifdef NO_SIGNALFD if (nsigsys == 0) set_signal(SIGSYS, NULL, SIG_IGN); +#endif nsigsys = SIGSYS; break; + case MAGIC_SHOW_MSG: + if (magic[1] != '\002' || !arg) { + errno = EINVAL; + enqry = ANSWER_NCK; + safeout(fd, enqry, strlen(enqry)+1, SSIZE_MAX); + goto out; + } + if (*arg) { + char *logmsg; + int l = asprintf(&logmsg, "\n*** MESSAGE: %s ***\n", arg); + if (l > 0) { + struct console *c; + + /* 1. Write to /var/log/boot.log */ + copylog(logmsg, l); + flushlog(); + + /* 2. Write to all active physical screens */ + list_for_each_entry(c, &lcons, node) { + if (c->fd < 0 || FD_ISSET(c->fd, &blocked)) + continue; + if (!console_silent) + c->out(c->fd, logmsg, l, c->max_canon); + } + free(logmsg); + } + } + enqry = ANSWER_ACK; + safeout(fd, enqry, strlen(enqry)+1, SSIZE_MAX); + break; + + case MAGIC_HIDE_MSG: + /* * No-Op for the screen. We intentionally ignore the text payload + * because line-based consoles (like s390x 3215) cannot clear lines. + * We just acknowledge the command. + */ + enqry = ANSWER_ACK; + safeout(fd, enqry, strlen(enqry)+1, SSIZE_MAX); + break; + default: enqry = ANSWER_NCK; @@ -1723,9 +1801,10 @@ pwprompt[len] = '\0'; len--; } +#ifdef NO_SIGNALFD set_signal(SIGCHLD, NULL, chld_handler); - - asking = 1; /* Show only our question about password/passphrase */ +#endif + asking = ask_mode; /* Show only our question about password/passphrase */ /* pwprompt */ list_for_each_entry(c, &lcons, node) { @@ -1747,6 +1826,14 @@ #if defined(__s390__) || defined(__s390x__) int vmcpfd = -1; #endif +#ifndef NO_SIGNALFD + sigset_t empty_mask; + sigemptyset(&empty_mask); + if ((pthread_sigmask)) + pthread_sigmask(SIG_UNBLOCK, &empty_mask, NULL); + else + sigprocmask(SIG_SETMASK, &empty_mask, NULL); +#endif if (fdfifo >= 0) close(fdfifo); if (fdsock >= 0) @@ -1840,7 +1927,7 @@ * and blocks the subsequent readpw() call forever. */ len = asprintf(&message, "\n===>> %s:\n", pwprompt); - } else if (c->flags & (CON_SCLP|CON_3270)) { + } else if (c->flags & (CON_SCLP|CON_3270|CON_HVC)) { /* * The 3270 console can do ANSI colouring and carriage returns, * as well as the sclp consoles which are a full featured ttys. @@ -1889,17 +1976,24 @@ /* We read byte for byte */ newtio = c->ctio; #if defined(__s390__) || defined(__s390x__) - if (c->flags & CON_3270) - /* - * ONLY the 3270 block-mode terminal MUST keep ICANON enabled! - * Otherwise the tty3270 driver switches to raw mode and fails - * to apply the non-display hardware attribute to the input field. - */ - newtio.c_lflag &= ~(ECHO); - else - newtio.c_lflag &= ~(ECHO|ICANON); + if (asking == 2) { + newtio.c_lflag = (newtio.c_lflag | ECHO) & ~ICANON; + } else { + if (c->flags & CON_3270) + /* + * ONLY the 3270 block-mode terminal MUST keep ICANON enabled! + * Otherwise the tty3270 driver switches to raw mode and fails + * to apply the non-display hardware attribute to the input field. + */ + newtio.c_lflag &= ~(ECHO); + else + newtio.c_lflag &= ~(ECHO|ICANON); + } #else - newtio.c_lflag &= ~(ECHO|ICANON); + if (asking == 2) + newtio.c_lflag = (newtio.c_lflag | ECHO) & ~ICANON; + else + newtio.c_lflag &= ~(ECHO|ICANON); #endif newtio.c_lflag |= ECHONL; newtio.c_cc[VTIME] = 0; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/libconsole/epoll.c new/showconsole-2.46/libconsole/epoll.c --- old/showconsole-2.45/libconsole/epoll.c 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/libconsole/epoll.c 2026-07-09 15:29:51.000000000 +0200 @@ -40,7 +40,7 @@ ev.events = flags; - if (posix_memalign((void**)&ep, sizeof(void*), ALIGNED_SIZEOF(struct epolls)) != 0 || !ep) + if (posix_memalign((void**)&ep, sizeof(void*), align_up(struct epolls, void*)) != 0 || !ep) error("memory allocation"); ep->handle = (typeof(ep->handle))fptr; @@ -87,7 +87,7 @@ if (!target) { list_t *head; - if (posix_memalign((void**)&ep, sizeof(void*), ALIGNED_SIZEOF(struct epolls)) != 0 || !ep) + if (posix_memalign((void**)&ep, sizeof(void*), align_up(struct epolls, void*)) != 0 || !ep) error("memory allocation"); ep->fd = fd; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/libconsole/frobnicate.c new/showconsole-2.46/libconsole/frobnicate.c --- old/showconsole-2.45/libconsole/frobnicate.c 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/libconsole/frobnicate.c 2026-07-09 15:29:51.000000000 +0200 @@ -1,6 +1,10 @@ /* * frobnicate.c * + * In-memory reversible obfuscation for cached passwords. + * This is not cryptographic protection against a memory-reading attacker. + * It is only meant to avoid trivial/plaintext exposure in memory. + * * Copyright 2015 Werner Fink, 2015 SuSE Linux GmbH. * * This source is free software; you can redistribute it and/or modify @@ -12,29 +16,72 @@ #include <errno.h> #include <stdlib.h> #include <string.h> +#include <sys/random.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> -static unsigned char randnum; +#define FROBMASK_LEN 32 + +static unsigned char frobmask[FROBMASK_LEN]; + static void initialseed(void) __attribute__((__constructor__)); static void initialseed(void) { - struct timeval tv; - gettimeofday (&tv, NULL); - srand (tv.tv_sec ^ tv.tv_usec ^ getpid()); - randnum = (unsigned char)(rand() & 0xff); - if (!randnum) - randnum = 42; + size_t off = 0; + + while (off < sizeof(frobmask)) { + ssize_t ret = getrandom(frobmask+off, sizeof(frobmask)-off, GRND_NONBLOCK); + if (ret < 0) { + if (errno == EINTR) + continue; + break; + } + if (ret == 0) + break; + off += (size_t)ret; + } + + if (off == sizeof(frobmask)) + return; + + /* + * Early-boot fallback: + * weak but stable for the process lifetime and inherited across fork(). + */ + { + struct timeval tv; + unsigned int seed; + size_t pos; + + gettimeofday(&tv, NULL); + seed = (unsigned int)(tv.tv_sec ^ tv.tv_usec ^ getpid()); + + for (pos = 0; pos < sizeof(frobmask); pos++) { + seed = seed * 1103515245u + 12345u; + frobmask[pos] = (unsigned char)((seed >> 16) & 0xff); + } + + /* Avoid an all-zero mask, just in case */ + for (pos = 0; pos < sizeof(frobmask); pos++) { + if (frobmask[pos] != 0) + return; + } + + frobmask[0] = 42; + } } void *frobnicate(void *in, const size_t len) { unsigned char *ptr = (unsigned char *)in; - ssize_t pos = len; + size_t pos; + + if (!ptr || len == 0) + return in; - while (pos-- > 0) - *ptr++ ^= randnum; + for (pos = 0; pos < len; pos++) + ptr[pos] ^= frobmask[pos % FROBMASK_LEN]; return in; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/libconsole/libconsole.h new/showconsole-2.46/libconsole/libconsole.h --- old/showconsole-2.45/libconsole/libconsole.h 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/libconsole/libconsole.h 2026-07-09 15:29:51.000000000 +0200 @@ -11,6 +11,7 @@ #include <err.h> #include <errno.h> +#include <stddef.h> #include <stdio.h> #include <signal.h> #include <sys/types.h> @@ -49,7 +50,7 @@ #endif #include "listing.h" -#define ALIGNED_SIZEOF(type) ((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1)) +#define align_up(type,boundary) ((sizeof(type)+(sizeof(boundary)-1)) & ~(sizeof(boundary)-1)) #define strsize(string) ((strlen(string)+1)*sizeof(char)) #if defined __USE_ISOC99 @@ -121,6 +122,7 @@ #define CON_3215 (128) /* s390x 3215 halfduplex console */ #define CON_3270 (256) /* s390x 3270 console */ #define CON_SCLP (512) /* s390x sclp terminals */ +#define CON_HVC (1024) /* s390x hvc terminals */ extern sigset_t omask; extern int final; @@ -129,7 +131,9 @@ extern int epfd; extern int evmax; extern volatile sig_atomic_t signaled; +extern volatile sig_atomic_t sigchild; extern volatile sig_atomic_t nsigsys; +extern volatile sig_atomic_t nsigio; extern volatile sig_atomic_t asking; extern void remember_arg0(volatile char *arg0); @@ -151,6 +155,7 @@ extern void scan_ask_directory(const char *dir_path); extern void send_response_to_systemd(const char *socket_path, const char *password); extern int coldstart_pop_request(char **message, char **socket_path); +extern void coldstart_free_requests(void); /* devices.c */ extern char *charname(const char *dev); @@ -206,6 +211,9 @@ extern void set_signal(int sig, struct sigaction *old, sighandler_t handler); extern void reset_signal(int sig, struct sigaction *old); extern int restart_sig(int sig, int flag); +#ifndef NO_SIGNALFD +extern int setup_signalfd(sigset_t mask); +#endif /* socket.c */ extern int open_un_socket_and_listen(void); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/libconsole/proc.c new/showconsole-2.46/libconsole/proc.c --- old/showconsole-2.45/libconsole/proc.c 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/libconsole/proc.c 2026-07-09 15:29:51.000000000 +0200 @@ -58,6 +58,7 @@ dir = opendir(fds); if (!dir) { warn("can not open %s", fds); + free(fds); return; } free(fds); @@ -140,9 +141,9 @@ } } if (!exists) { - if (posix_memalign((void**)&pm, sizeof(void*), ALIGNED_SIZEOF(parameter_t)+strlen(key)+1) != 0 || !pm) + if (posix_memalign((void**)&pm, sizeof(void*), align_up(parameter_t, void*) + strlen(key)+1) != 0 || !pm) error("memory allocation"); - pm->key = ((char*)pm)+ALIGNED_SIZEOF(parameter_t); + pm->key = ((char*)pm)+align_up(parameter_t, void*); strcpy(pm->key, key); pm->val = strdup(val); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/libconsole/shm.c new/showconsole-2.46/libconsole/shm.c --- old/showconsole-2.45/libconsole/shm.c 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/libconsole/shm.c 2026-07-09 15:29:51.000000000 +0200 @@ -101,6 +101,9 @@ if (area == MAP_FAILED) error("can not map shared memory object into memory"); + if (shmfd >= 0) + close(shmfd); + if (devshm) { /* shm_ */ unlink(template); free(template); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/libconsole/signals.c new/showconsole-2.46/libconsole/signals.c --- old/showconsole-2.45/libconsole/signals.c 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/libconsole/signals.c 2026-07-09 15:29:51.000000000 +0200 @@ -12,6 +12,10 @@ #include <errno.h> #include <stddef.h> #include <signal.h> +#ifndef NO_SIGNALFD +# include <sys/signalfd.h> +# include <string.h> +#endif #include "libconsole.h" weak_symbol(pthread_sigmask); @@ -90,3 +94,63 @@ return ret; } + +#ifndef NO_SIGNALFD +/* Part of signal handling with signalfd only valid for libconsole */ + +static void handle_signal_event(int fd) +{ + struct signalfd_siginfo fdsi; + + ssize_t s = read(fd, &fdsi, sizeof(struct signalfd_siginfo)); + if (s != sizeof(struct signalfd_siginfo)) + return; /* Short reads are impossible by kernel design. + Catching -1 (e.g. EAGAIN) here. */ + + switch (fdsi.ssi_signo) { + case SIGTERM: + case SIGQUIT: + case SIGINT: + if (nsigsys && (fdsi.ssi_signo == SIGTERM)) + break; + signaled = fdsi.ssi_signo; + break; + case SIGSYS: + nsigsys = SIGSYS; + break; + case SIGIO: + nsigio = SIGIO; + break; + case SIGCHLD: + sigchild++; + break; + default: + warn("Signal catched %s but not handled", strsignal(fdsi.ssi_signo)); + break; + } +} + +int setup_signalfd(sigset_t mask) +{ + int sfd = -1; + + if ((pthread_sigmask)) + pthread_sigmask(SIG_BLOCK, &mask, NULL); + else + sigprocmask(SIG_BLOCK, &mask, NULL); + + /* + * The SFD_NONBLOCK is essential for (e)polling + * whereas SFD_CLOEXEC we might not need. + * Nevertheless how to handle the logging thread + * as well as the forking password questioners + */ + sfd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (sfd < 0) + error("can not open signal file descriptor"); + + epoll_addread(sfd, &handle_signal_event); + + return sfd; +} +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/libconsole/tty.c new/showconsole-2.46/libconsole/tty.c --- old/showconsole-2.45/libconsole/tty.c 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/libconsole/tty.c 2026-07-09 15:29:51.000000000 +0200 @@ -52,7 +52,7 @@ int request_tty(const char *tty) { struct sigaction saved_sighup; - int fd = -1, nd, wd; + int fd = -1, nd, wd = -1; #if defined(__s390__) || defined(__s390x__) struct stat sb; #endif @@ -74,12 +74,13 @@ wd = inotify_add_watch(nd, tty, IN_CLOSE); if (wd < 0) { warn("can not add a watch on inotifier %d for %s", nd, tty); + close(nd); return -1; } do { ssize_t len; - int ret, flags; + int ret, flags, got_close = 0; clear_input(nd); @@ -89,7 +90,7 @@ break; } - set_signal(SIGHUP, NULL, SIG_IGN); + set_signal(SIGHUP, &saved_sighup, SIG_IGN); ret = ioctl(fd, TIOCSCTTY, 0); reset_signal(SIGHUP, &saved_sighup); @@ -113,8 +114,8 @@ if (ret >= 0) break; /* Success */ - do { # define BUF_LEN ((sizeof(struct inotify_event)+NAME_MAX+1)) + while (!got_close) { unsigned char buf[BUF_LEN]; ssize_t e; @@ -133,14 +134,15 @@ struct inotify_event *ev; ev = (struct inotify_event *)&buf[e]; - if (ev->wd == wd && (ev->mask&IN_CLOSE)) + if (ev->wd == wd && (ev->mask & IN_CLOSE)) { + got_close = 1; break; + } e += sizeof(struct inotify_event) + ev->len; } - # undef BUF_LEN - } while (0); + } tcdrain(fd); close(fd); @@ -148,6 +150,8 @@ } while(0); out: + if (wd >= 0) + (void)inotify_rm_watch(nd, wd); close(nd); return fd; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/showconsole-2.45/libconsole/vmcp.c new/showconsole-2.46/libconsole/vmcp.c --- old/showconsole-2.45/libconsole/vmcp.c 2026-07-02 11:25:23.000000000 +0200 +++ new/showconsole-2.46/libconsole/vmcp.c 2026-07-09 15:29:51.000000000 +0200 @@ -113,6 +113,8 @@ char* queryterm(int fd) { const char* question = "QUERY TERMINAL"; + /* Reset cached terminal state before reparsing a fresh QUERY TERMINAL reply. */ + clearvmcp(); return askvmcp(fd, question); }
