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-17 18:47:04
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/blog (Old)
 and      /work/SRC/openSUSE:Factory/.blog.new.24530 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "blog"

Fri Jul 17 18:47:04 2026 rev:29 rq:1365970 version:2.47

Changes:
--------
--- /work/SRC/openSUSE:Factory/blog/blog.changes        2026-07-10 
17:35:11.792585430 +0200
+++ /work/SRC/openSUSE:Factory/.blog.new.24530/blog.changes     2026-07-17 
18:47:16.020771938 +0200
@@ -1,0 +2,51 @@
+Wed Jul 15 12:29:23 UTC 2026 - Dr. Werner Fink <[email protected]>
+
+- Update to version 2.47
+  This addresses several race conditions, file descriptor leaks, and
+  architectural issues regarding virtual terminal (VT) handling in blogd.
+
+* VT Encapsulation: Extracted all kernel-specific terminal ioctls and
+  macros (e.g., KD_GRAPHICS, KD_TEXT) into a dedicated vt.c module.
+  Added clean abstractions like vt_is_graphics(), vt_is_text(), and
+  vt_set_text_mode() to decouple blogd.c and console.c from kernel headers.
+* List Management & FD Leaks: Fixed dynamic VT allocation in console.c.
+  By resetting the console pointer to NULL before consalloc() and
+  explicitly searching the doubly linked list for the target device ID
+  afterward, we ensure the exact newly allocated terminal node is referenced.
+  This fixes incorrect node assignments and prevents file descriptor leaks.
+* Smart VT Switching: Optimized the password prompt logic. If the
+  currently active VT is a text console, the prompt is displayed there
+  directly without unnecessary context switches. If the active VT is
+  running a GUI (X11/Wayland), it safely switches to a newly allocated
+  text VT and returns afterward.
+* EBUSY Race Condition: The child process now explicitly closes the
+  temporary VT file descriptor before initiating the vt_switch back to
+  the original console. This allows the parent process to safely perform
+  vt_disallocate without encountering EBUSY kernel errors.
+* Timeout Handling: Replaced the infinite wait loop in request_tty()
+  (tty.c) with a safe 2000ms timeout via can_read(). This prevents the
+  daemon from hanging indefinitely on locked terminals and allowed the
+  removal of error-prone alarm() signal hacks.
+* Lifecycle Management: Ensured newly allocated dynamic VTs are properly
+  initialized via consinitIO() and reliably deallocated upon daemon
+  exit (lcons_shutdown).
+
+  Fix epoll EEXIST crash and prevent infinite waits on busy terminals
+
+  This also addresses two critical edge cases that could cause the blogd
+  daemon to either crash or hang during concurrent or blocked prompt requests.
+
+  Key changes:
+* epoll.c: Prevent an `EEXIST` crash in `epoll_addition()`. If a file
+  descriptor is already registered in the watch list, the daemon now
+  gracefully updates it using `EPOLL_CTL_MOD` instead of blindly calling
+  `EPOLL_CTL_ADD`. This makes the socket handling robust against rapid,
+  overlapping client requests.
+* console.c: Add a 2-second deadline (`alarm(2)`) around the `request_tty()`
+  call inside the password-prompt child process. Previously, if a terminal
+  was exclusively locked (e.g. by an X11 server returning EPERM), the child
+  would fall into an infinite inotify wait-loop. With the timeout, the child
+  now dies cleanly, allowing the parent's reaping logic to properly cancel
+  the prompt and send an ANSWER_ENQ (abort) to the waiting client.
+
+-------------------------------------------------------------------

Old:
----
  showconsole-2.46.tar.gz

New:
----
  showconsole-2.47.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ blog.spec ++++++
--- /var/tmp/diff_new_pack.ZfOAME/_old  2026-07-17 18:47:16.824799169 +0200
+++ /var/tmp/diff_new_pack.ZfOAME/_new  2026-07-17 18:47:16.828799305 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           blog
-Version:        2.46
+Version:        2.47
 %define sonum   2
 Release:        0
 Summary:        Boot logging

++++++ showconsole-2.46.tar.gz -> showconsole-2.47.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/.github/workflows/c-cpp.yml 
new/showconsole-2.47/.github/workflows/c-cpp.yml
--- old/showconsole-2.46/.github/workflows/c-cpp.yml    1970-01-01 
01:00:00.000000000 +0100
+++ new/showconsole-2.47/.github/workflows/c-cpp.yml    2026-07-15 
14:22:56.000000000 +0200
@@ -0,0 +1,37 @@
+name: openSUSE Tumbleweed CI
+
+on:
+  push:
+    branches: [ "master" ]
+  pull_request:
+    branches: [ "master" ]
+  workflow_dispatch:
+
+jobs:
+  build-and-test:
+    runs-on: ubuntu-latest
+    container:
+      image: registry.opensuse.org/opensuse/tumbleweed:latest
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Refresh repositories and install dependencies
+        run: |
+          zypper --non-interactive refresh
+          zypper --non-interactive install -y \
+            gcc make glibc-devel \
+            rpm-config-SUSE \
+            bash \
+            coreutils \
+            findutils
+
+      - name: Run build or test scripts
+        run: |
+          echo "Running tasks inside openSUSE Tumbleweed..."
+          set -e
+          make
+          mkdir -p  $GITHUB_WORKSPACE/buildroot
+          make install DESTDIR=$GITHUB_WORKSPACE/buildroot
+          find $GITHUB_WORKSPACE/buildroot
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/Makefile 
new/showconsole-2.47/Makefile
--- old/showconsole-2.46/Makefile       2026-07-09 15:29:51.000000000 +0200
+++ new/showconsole-2.47/Makefile       2026-07-15 14:22:56.000000000 +0200
@@ -15,7 +15,7 @@
 DEBUG   =
 DESTDIR         =
 MAJOR   :=     2
-MINOR   :=     46
+MINOR   :=     47
 VERSION         :=     $(MAJOR).$(MINOR)
 DATE    =      $(shell date +'%d%b%y' | tr '[:lower:]' '[:upper:]')
 COPTS    =
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/README.md 
new/showconsole-2.47/README.md
--- old/showconsole-2.46/README.md      2026-07-09 15:29:51.000000000 +0200
+++ new/showconsole-2.47/README.md      2026-07-15 14:22:56.000000000 +0200
@@ -1,25 +1,54 @@
 # showconsole
 
-The package showconsole includes several helpers which had been developed
-over the years for SUSE and openSUSE Linux. There are
+The `showconsole` package provides a suite of lightweight system console and
+boot logging utilities, originally developed over the years for SUSE and
+openSUSE Linux.
 
-  * blogd       - Does boot logging on /dev/console
-  * blogctl     - Control the boot logging daemon blogd
-  * blogger     - Writes messages to a running blogd process
-  * isserial    - Determines if the underlying tty of stdin is a serial line
-  * showconsole - Determines the real tty of stdin
-  * setconsole  - Redirect system console output to a tty
-
-Whereas blogd had been written for SysVinit based systems this newer version
-also supports systemd based system.  The blogd together with the new tool
-blogctl can replace the well known plymouth but without any frame buffer and
-splash support.  Therefore blogd may used on workstations as well as on so
-called big irons.
-
-The blogd causes systemd to show its boot messages on the system console to
-collect them for repeating them on all devices used for the system console
-as well as it writes out a copy to boot.log and boot.old below /var/log/.
-
-Beside boot messages and logging them the blogd can handle password or
-passphrase requests send by password agents.  For those requests the blogd
-do ask on all terminal lines used for the system console.
+## Included Tools
+
+* **`blogd`**: Does boot logging on `/dev/console`.
+
+* **`blogctl`**: Control the boot logging daemon `blogd`.
+
+* **`blogger`**: Writes messages to a running `blogd` process.
+
+* **`showconsole`**: Determines the real TTY of standard input.
+
+* **`setconsole`**: Redirect system console output to a TTY.
+
+* **`isserial`**: Determines if the underlying TTY of standard input is
+     a serial line.
+
+## The `blogd` Boot Logging Daemon
+
+Whereas `blogd` had been written for SysVinit based systems, this newer
+version also supports systemd based systems.
+
+The `blogd` together with the new tool `blogctl` can replace the well-known
+plymouth but without any frame buffer and splash support. Therefore, `blogd`
+may be used on workstations as well as on so-called big irons.
+
+### Key Features & Purpose
+
+* **Boot Message Multiplexing:** The `blogd` causes systemd to show its boot
+    messages on the system console to collect them for repeating them on all
+    devices used for the system console.
+
+* **Persistent Logging:** It writes out a copy to `boot.log` and `boot.old`
+    below `/var/log/`.
+
+* **Advanced Password Handling:** Beside boot messages and logging them the
+    `blogd` can handle password or passphrase requests send by password
+     agents. For those requests the `blogd` do ask on all terminal lines
+     used for the system console.
+
+* **Smart Virtual Terminal (VT) Switching:** It features robust, dynamic
+    Virtual Terminal management. If the system is running a graphical
+    environment (X11/Wayland) that locks the active terminal, `blogd`
+    smartly allocates a free text terminal, safely switches the display
+    to prompt the user, and seamlessly switches back to the graphical
+    session once complete.
+
+* **Context-Aware Prompts:** If the user is already actively viewing a text
+    console, the prompt is seamlessly integrated directly into the active
+    text stream without unnecessary screen switching or visual disruption.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/blogctl.8 
new/showconsole-2.47/blogctl.8
--- old/showconsole-2.46/blogctl.8      2026-07-09 15:29:51.000000000 +0200
+++ new/showconsole-2.47/blogctl.8      2026-07-15 14:22:56.000000000 +0200
@@ -113,6 +113,9 @@
 .I /var/log/boot.old
 as well as mask it own program name in the process table
 with the @ character.
+.TP
+.B help
+Show a help text.
 .SH SEE ALSO
 .BR blogd (8),
 .BR systemd (1),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/blogctl.c 
new/showconsole-2.47/blogctl.c
--- old/showconsole-2.46/blogctl.c      2026-07-09 15:29:51.000000000 +0200
+++ new/showconsole-2.47/blogctl.c      2026-07-15 14:22:56.000000000 +0200
@@ -1,7 +1,8 @@
 /*
- * blogd.c
+ * blogctl.c
  *
- * Copyright 2015 Werner Fink, 2015 SuSE Linux GmbH.
+ * Copyright 2015, 2026 Werner Fink, 2015 SuSE Linux GmbH,
+ * 2026 SUSE Software Solutions Germany GmbH
  *
  * This source is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -40,6 +41,7 @@
     return fd;
 }
 
+#define MAGIC_HELP     0x19
 static char getcmd(int argc, char *argv[])
 {
     static const struct {
@@ -50,16 +52,17 @@
     } 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 },
+       { "ask-for-password",   MAGIC_ASK_PWD,          0, NULL },      /* Ask 
for password */
+       { "ask-question",       MAGIC_QUESTION,         0, NULL },      /* Ask 
a question */
+       { "display-message",    MAGIC_SHOW_MSG,         0, NULL },      /* 
Display a message */
+       { "hide-message",       MAGIC_HIDE_MSG,         0, NULL },      /* 
Sorry, no backroll */
        { "ready",              MAGIC_SYS_INIT,         0, NULL },      /* 
System ready */
        { "quit",               MAGIC_QUIT,             0, NULL },      /* Quit 
*/
        { "final",              MAGIC_FINAL,            0, NULL },      /* 
Final */
        { "close",              MAGIC_CLOSE,            0, NULL },      /* 
Close logging only */
        { "deactivate",         MAGIC_DEACTIVATE,       0, NULL },      /* 
Deactivate logging */
        { "reactivate",         MAGIC_REACTIVATE,       0, NULL },      /* 
Reactivate logging */
+       { "help",               MAGIC_HELP,             0, NULL },      /* End 
Of Medium aka Help */
        {}
     }, *cmd = cmds;
     char ret = (char)-1;
@@ -124,11 +127,12 @@
     cmd[1] = '\0';
     answer[0] = '\x15';
 
-    fdsock = getsocket();
-    if (fdsock < 0)
-       error("no blogd active");
-
     while ((cmd[0] = getcmd(argc, argv)) != (char)-1) {
+       if (cmd[0] != MAGIC_HELP && fdsock < 0) {
+           fdsock = getsocket();
+           if (fdsock < 0)
+               error("no blogd active");
+       }
        switch (cmd[0]) {
        case MAGIC_CHROOT:
            root = optarg;
@@ -173,8 +177,9 @@
                {0, 0, 0, 0}
            };
 
-           /* * getopt_long_only parst ab argv[optind]. Da wir getcmd()
-            * benutzt haben, steht optind genau richtig auf der ersten Option.
+           /* 
+            * The getopt_long_only function parses starting from argv[optind].
+            * Since we used getcmd(), optind is pointing exactly at the first 
option.
             */
            while ((c = getopt_long_only(argc, argv, "", long_options, NULL)) 
!= -1) {
                switch (c) {
@@ -290,6 +295,30 @@
            
            break;
        }
+       case MAGIC_HELP:
+           printf("Usage: /sbin/blogctl [COMMAND] [OPTIONS]\n\n"
+                  "Commands:\n"
+                  "  ping                  Check if blogd is active\n"
+                  "  quit [--wait]         Gracefully terminate blogd\n"
+                  "  root=<path>           Set new root file system path\n"
+                  "  ready                 Signal that file systems are 
writable\n"
+                  "  close                 Finish logging, allow systemd to 
unmount\n"
+                  "  ask-for-password      Ask the user for a password\n"
+                  "    --prompt=STRING       Message to display\n"
+                  "    --command=STRING      Command to receive password via 
stdin\n"
+                  "    --number-of-tries=INT Number of retries\n"
+                  "  ask-question          Ask the user a question\n"
+                  "    --prompt=STRING       Message to display\n"
+                  "    --command=STRING      Command to receive answer via 
stdin\n"
+                  "  display-message       Display a status message\n"
+                  "    --text=STRING         The message text\n"
+                  "  hide-message          Hide a status message\n"
+                  "  deactivate            Disconnect blogd from system 
console\n"
+                  "  reactivate            Reconnect blogd to system console\n"
+                  "  final                 Rotate boot.log to boot.old\n"
+                  "  help                  Show this help text\n");
+           answer[0] = '\x6';
+           goto fail;
        case '?':
        default:
            goto fail;
@@ -309,6 +338,8 @@
 
     argv += optind;
     argc -= optind;
+    if (argc != 0)
+       printf("Usage: /sbin/blogctl help\n");
 fail:
     if (fdsock >= 0)
        close(fdsock);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/blogd.c new/showconsole-2.47/blogd.c
--- old/showconsole-2.46/blogd.c        2026-07-09 15:29:51.000000000 +0200
+++ new/showconsole-2.47/blogd.c        2026-07-15 14:22:56.000000000 +0200
@@ -19,7 +19,6 @@
 #include <sys/stat.h>
 #include <sys/ioctl.h>
 #include <sys/vt.h>
-#include <sys/kd.h>
 #include <time.h>
 #include <pty.h>
 #include <stdio.h>
@@ -54,10 +53,12 @@
 static void _initialize(void) __attribute__((__constructor__));
 static void _initialize(void)
 {
-    const char *run;
+    char *run;
     run = realpath(varrun, NULL);
     if (run && *run)
        varrun = run;
+    else if (run)
+       free(run);
 }
 
 /*
@@ -143,6 +144,7 @@
                        exe = proc2exe(pid);
                        if (exe) {
                            errno = EACCES;
+                           free(exe);
                            error("plymouth is active %ld", (long int)pid);
                        }
                    }
@@ -291,13 +293,13 @@
     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") == 0)
-            console_silent = 1;
+       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") == 0)
-            coldboot = 1;
+       if (strcmp(val, "1") == 0 || strcasecmp(val, "on") == 0 || 
strcasecmp(val, "yes") == 0 || strcasecmp(val, "true") == 0)
+           coldboot = 1;
     }
 
     myname = program_invocation_short_name;
@@ -478,9 +480,12 @@
            cfsetispeed(&c->otio, ispeed);
            cfsetospeed(&c->otio, ospeed);
        } else {
-           if (major(c->dev) == 4 && minor(c->dev) <= 63)
-               ioctl(c->fd, KDSETMODE, KD_TEXT);       /* Enforce text mode */
-
+#if !defined(__s390__) && !defined(__s390x__)
+           if (major(c->dev) == 4 && minor(c->dev) <= 63) {
+               if (!vt_is_graphics(c->fd))
+                   vt_set_text_mode(c->fd);            /* Enforce text mode */
+           }
+#endif
            c->otio.c_iflag |= (ICRNL | IXON);
            c->otio.c_iflag &= ~(INLCR | IGNCR | BRKINT);
            c->otio.c_oflag |= (ONLCR | OPOST);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/libconsole/console.c 
new/showconsole-2.47/libconsole/console.c
--- old/showconsole-2.46/libconsole/console.c   2026-07-09 15:29:51.000000000 
+0200
+++ new/showconsole-2.47/libconsole/console.c   2026-07-15 14:22:56.000000000 
+0200
@@ -171,11 +171,34 @@
  * Console list
  */
 list_t lcons = { &(lcons), &(lcons) };
+#if !defined(__s390__) && !defined(__s390x__)
+static int active_vt_fd = -1;
+static struct console *dyn_vt_cons = NULL;     /* For dynamic allocated VT */
+static void epoll_vt_active_change(int fd);
+#endif
 
 static __attribute__((destructor)) void lcons_shutdown(void)
 {
     struct console *c, *n;
 
+#if !defined(__s390__) && !defined(__s390x__)
+    if (dyn_vt_cons) {
+       int old_vt = -1;
+       if (major(dyn_vt_cons->dev) == 4)
+           old_vt = minor(dyn_vt_cons->dev);
+
+       if (old_vt > 0) {
+           int fd_tty0 = open("/dev/tty0", O_RDWR | O_CLOEXEC);
+           if (fd_tty0 >= 0) {
+               vt_disallocate(fd_tty0, old_vt);
+               close(fd_tty0);
+           }
+       }
+       /* Set pointer to NULL, the final free happens in the lcons loop below 
*/
+       dyn_vt_cons = NULL;
+    }
+#endif
+
     if (list_empty(&lcons))
        return;
 
@@ -607,6 +630,17 @@
     fdsock  = listen;                  /* We use only ONE socket ... see also 
safeout() */
     fdread  = input;
 
+#if !defined(__s390__) && !defined(__s390x__)
+    if (vt_supported()) {
+       active_vt_fd = open("/sys/devices/virtual/tty/tty0/active", 
O_RDONLY|O_CLOEXEC);
+       if (active_vt_fd >= 0) {
+           epoll_addsysfs(active_vt_fd, &epoll_vt_active_change);
+           /* Trigger initially once to get the actual VT */
+           epoll_vt_active_change(active_vt_fd);
+       }
+    }
+#endif
+
     /* Phase 1: Scan for pending systemd password queries */
     scan_ask_directory("/run/systemd/ask-password");
 
@@ -893,7 +927,7 @@
        fdsock = -1;
     }
 
-    epoll_close_fd();
+    epoll_close_fd(-1);
     if (epfd >= 0)
        close(epfd);
 
@@ -1097,8 +1131,10 @@
 #ifdef TIOCGDEV
     fd = open("/dev/console", O_RDONLY|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
     if (fd >= 0) {
-       if (ioctl (0, TIOCGDEV, &devnum) < 0)
+       if (ioctl (0, TIOCGDEV, &devnum) < 0) {
+           close(fd);
            goto fallback;
+       }
        close(fd);
        tty = chardev((dev_t)devnum);
        if (!tty)
@@ -1121,6 +1157,82 @@
     free(tty);
 }
 
+#if !defined(__s390__) && !defined(__s390x__)
+/*
+ * Be noted and handle change of active virtual console
+ */
+static void epoll_vt_active_change(int fd)
+{
+    char tty_name[32];
+    char path[64];
+    struct stat st;
+
+    /* Rewind file descriptor before reading EPOLLPRI data */
+    if (lseek(fd, 0, SEEK_SET) < 0)
+       return;
+
+    if (read(fd, tty_name, sizeof(tty_name)-1) > 0) {
+       struct console *c;
+       dev_t target_dev;
+       int found = 0;
+
+       tty_name[strcspn(tty_name, "\n")] = 0;  /* remove newline */
+
+       /* Close old virtual console and remove it from lcons */
+       if (dyn_vt_cons) {
+           struct console *n;
+           int old_vt = -1;
+
+           if (major(dyn_vt_cons->dev) == 4)
+               old_vt = minor(dyn_vt_cons->dev);
+
+           list_for_each_entry_safe(c, n, &lcons, node) {
+               if (c != dyn_vt_cons)
+                   continue;
+               delete(&c->node);
+               if (c->fd >= 0)
+                   close(c->fd);
+               free(c);
+               dyn_vt_cons = NULL;
+               break;
+           }
+
+           if (old_vt > 0) {
+               int fd_tty0 = open("/dev/tty0", O_RDWR | O_CLOEXEC);
+               if (fd_tty0 >= 0) {
+                   vt_disallocate(fd_tty0, old_vt);
+                   close(fd_tty0);
+               }
+           }
+       }
+
+       snprintf(path, sizeof(path), "/dev/%s", tty_name);
+       target_dev = (stat(path, &st) == 0 && S_ISCHR(st.st_mode)) ? st.st_rdev 
: makedev(4,0);
+
+       list_for_each_entry(c, &lcons, node) {
+           if (c->dev == st.st_rdev) {
+               found++;
+               break;
+           }
+       }
+
+       if (!found) {
+           struct console *d = NULL;
+           consalloc(&d, path, CON_CONSDEV, st.st_rdev, 0);
+
+           list_for_each_entry(c, &lcons, node) {
+               if (c->dev == target_dev) {
+                   dyn_vt_cons = c;
+                   break;
+               }
+           }
+           if (dyn_vt_cons)
+               consinitIO(dyn_vt_cons);
+       }
+    }
+}
+#endif
+
 /*
  * Do handle the console in data
  */
@@ -1370,12 +1482,38 @@
                coldstart_next();
        }
     } else {
+       int still_running = 0;
+
        /* Clear Zombies */
        list_for_each_entry(c, &lcons, node) {
            if (c->pid == info.si_pid) {
                c->pid = -1;
            }
        }
+
+       /* Check, if there is any active child for this prompt */
+       list_for_each_entry(c, &lcons, node) {
+           if (c->pid > 0)
+               still_running = 1;
+       }
+           
+       if (!still_running) {
+           /* All consoles have failed. cancel the prompt! */
+           asking = 0;
+           if (coldstart_active && coldstart_socket_path) {
+               free(coldstart_socket_path);
+               coldstart_socket_path = NULL;
+           }
+           if (pwd_client_fd >= 0) {
+               const char *enq = ANSWER_ENQ; /* ENQ = send Cancel/Abort to 
blogctl */
+               safeout(pwd_client_fd, enq, strlen(enq)+1, SSIZE_MAX);
+               epoll_delete(pwd_client_fd);
+               close(pwd_client_fd);
+               pwd_client_fd = -1;
+           }
+           if (coldstart_active)
+               coldstart_next();
+       }
     }
 }
 
@@ -1787,6 +1925,7 @@
 {
     struct console *c;
     size_t len;
+    int any_running = 0;
 
     if (!pwprompt || !*pwprompt)
        return;
@@ -1822,9 +1961,12 @@
            struct console *d;
            char *message;
            int eightbit;
-           int len, fdc, tflags;
+           int len, fdc = -1, tflags;
 #if defined(__s390__) || defined(__s390x__)
            int vmcpfd = -1;
+#else
+           int fd_tty0 = -1, orig_vt = -1, prompt_vt = -1;
+           char newtty[32];
 #endif
 #ifndef NO_SIGNALFD
            sigset_t empty_mask;
@@ -1840,16 +1982,18 @@
                close(fdsock);
            if (flog)
                (void)fclose(flog);
+           if (pwd_client_fd >= 0)
+               close(pwd_client_fd);
            if (epfd) {
-               epoll_close_fd();
+               epoll_close_fd(c->fd);  /* We keep c->fd */
                close(epfd);
            }
            FD_ZERO(&blocked);
            vc_reconnect = NULL;
 
-           dup2(1, 2);
            dup2(c->fd, 0);
            dup2(c->fd, 1);
+           dup2(c->fd, 2);
            currenttty = c->tty;        /* Used in readpw() in case of an error 
*/
 
            list_for_each_entry(d, &lcons, node)
@@ -1871,14 +2015,80 @@
            set_signal(SIGSYS,  NULL, SIG_DFL);
            set_signal(SIGQUIT, NULL, SIG_IGN);
 
-           fdc = request_tty(c->tty);
+#if !defined(__s390__) && !defined(__s390x__)
+           if (major(c->dev) == 4 && vt_supported()) {
+               int blocked = 0;
+               /* Open the terminal without hanging or be blocked */
+               int fdc_test = open(c->tty, O_RDWR | O_NOCTTY | O_NONBLOCK | 
O_CLOEXEC);
+               
+               if (fdc_test >= 0) {
+                   /* Is terminal locked by getty/login/shell ? */
+                   if (ioctl(fdc_test, TIOCSCTTY, 0) < 0 && errno == EPERM)
+                       blocked = 1;
+                   /* It the terminal in graphic mode (X11/Wayland)? */
+                   if (vt_is_graphics(fdc_test))
+                       blocked = 1;
+
+                   if (blocked) {
+                       close(fdc_test);
+                   } else {
+                       /* Terminal is free! Use it and remove O_NONBLOCK */
+                       int flags = fcntl(fdc_test, F_GETFL);
+                       fcntl(fdc_test, F_SETFL, flags & ~O_NONBLOCK);
+                       fdc = fdc_test;
+
+                       /* Are we here? */
+                       fd_tty0 = open("/dev/tty0", O_RDWR | O_CLOEXEC);
+                       if (fd_tty0 >= 0) {
+                           orig_vt = vt_get_active(fd_tty0);
+                           int target_vt = minor(c->dev);
+ 
+                           /* If the user is e.g. on tty2 (X11/Wayland), but 
the terminal at tty7 -> switch over! */
+                           if (orig_vt > 0 && target_vt > 0 && orig_vt != 
target_vt) {
+                               vt_switch(fd_tty0, target_vt);
+                               prompt_vt = target_vt; /* Remember: to find 
back to tty2 */
+                           }
+                       }
+                   }
+               } else {
+                   blocked = 1;
+               }
+
+               if (blocked) {
+                   if (fd_tty0 < 0)
+                       fd_tty0 = open("/dev/tty0", O_RDWR | O_CLOEXEC);
+
+                   if (fd_tty0 >= 0) {
+                       orig_vt = vt_get_active(fd_tty0);
+                       prompt_vt = vt_get_free(fd_tty0);
+
+                       if (prompt_vt > 0) {
+                           vt_switch(fd_tty0, prompt_vt);
+                           snprintf(newtty, sizeof(newtty), "/dev/tty%d", 
prompt_vt);
+                           currenttty = newtty;
+                           fdc = open(currenttty, O_RDWR | O_CLOEXEC);
+                           if (fdc >= 0)
+                               ioctl(fdc, TIOCSCTTY, 0);
+                       }
+                   }
+               }
+           }
+#endif
            if (fdc < 0)
+               fdc = request_tty(c->tty);
+           if (fdc < 0) {
+#if !defined(__s390__) && !defined(__s390x__)
+               if (fd_tty0 >= 0)
+                   close(fd_tty0);
+#endif
                _exit(1);
+           }
 
-           dup2(fdc, 0);
-           dup2(fdc, 1);
-           if (fdc > 1)
+           dup2(fdc, 0);       /* stdin */
+           dup2(fdc, 1);       /* stdout */
+           if (fdc > 1) {      /* Intentional: let 0 and 1 open */
                close(fdc);
+           }
 
            /*
             * Mandatory: Remove none blocking mode for reading
@@ -1944,6 +2154,10 @@
 #endif
            if (len < 0) {
                warn("can not set password prompt");
+#if !defined(__s390__) && !defined(__s390x__)
+               if (fd_tty0 >= 0)
+                   close(fd_tty0);
+#endif
                _exit(1);
            }
            /*
@@ -2026,6 +2240,18 @@
                warn("can not read password");
 
            password = frobnicate(password, *pwsize);
+
+#if !defined(__s390__) && !defined(__s390x__)
+           if (orig_vt > 0 && prompt_vt > 0) {
+               if (fdc >= 0) {
+                   close(fdc);
+                   fdc = -1;
+               }
+               vt_switch(fd_tty0, orig_vt);
+           }
+           if (fd_tty0 >= 0)
+               close(fd_tty0);
+#endif
            _exit(0);
        }
 
@@ -2067,4 +2293,26 @@
            } while (c->pid != -1 && errno == EINTR);
        }
     }
+
+    /* Fallback: When we waited synchronously and everything failed 
immediately */
+    list_for_each_entry(c, &lcons, node) {
+       if (c->pid > 0)
+           any_running = 1;
+    }
+    if (asking && !any_running) {
+       asking = 0;
+       if (coldstart_active && coldstart_socket_path) {
+           free(coldstart_socket_path);
+           coldstart_socket_path = NULL;
+       }
+       if (pwd_client_fd >= 0) {
+           const char *enq = ANSWER_ENQ;
+           safeout(pwd_client_fd, enq, strlen(enq)+1, SSIZE_MAX);
+           epoll_delete(pwd_client_fd);
+           close(pwd_client_fd);
+           pwd_client_fd = -1;
+       }
+       if (coldstart_active)
+           coldstart_next();
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/libconsole/epoll.c 
new/showconsole-2.47/libconsole/epoll.c
--- old/showconsole-2.46/libconsole/epoll.c     2026-07-09 15:29:51.000000000 
+0200
+++ new/showconsole-2.47/libconsole/epoll.c     2026-07-15 14:22:56.000000000 
+0200
@@ -40,6 +40,18 @@
 
     ev.events = flags;
 
+    /* Prevent EEXIST crash: If fd is already registered, just modify it */
+    list_for_each_entry(ep, &lpolls, watch) {
+       if (ep->fd == fd) {
+           ep->handle = (typeof(ep->handle))fptr;
+           ev.data.ptr = (void*)ep;
+           ret = epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);
+           if (ret < 0)
+               error("can not modify %d file descriptor on epoll file 
descriptor", fd);
+           return;
+       }
+    }
+
     if (posix_memalign((void**)&ep, sizeof(void*), align_up(struct epolls, 
void*)) != 0 || !ep)
        error("memory allocation");
 
@@ -66,6 +78,12 @@
     epoll_addition(fd, fptr, EPOLLIN|EPOLLPRI|EPOLLRDHUP);
 }
 
+void epoll_addsysfs(int fd, void *fptr)
+{
+    /* ONLY wait for exceptions (EPOLLPRI|EPOLLERR), never for EPOLLIN! */
+    epoll_addition(fd, fptr, EPOLLPRI|EPOLLERR);
+}
+
 void epoll_addwrite(int fd, void *fptr)
 {
     epoll_addition(fd, fptr, EPOLLOUT|EPOLLONESHOT|EPOLLPRI|EPOLLERR);
@@ -168,10 +186,12 @@
 }
 
 /* Only usefull within forked sub processes */
-void epoll_close_fd(void)
+void epoll_close_fd(int keep_fd)
 {
     struct epolls *ep;
 
-    list_for_each_entry(ep, &lpolls, watch)
-       close(ep->fd);
+    list_for_each_entry(ep, &lpolls, watch) {
+       if (ep->fd >= 0 && ep->fd != keep_fd)
+           close(ep->fd);
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/libconsole/libconsole.h 
new/showconsole-2.47/libconsole/libconsole.h
--- old/showconsole-2.46/libconsole/libconsole.h        2026-07-09 
15:29:51.000000000 +0200
+++ new/showconsole-2.47/libconsole/libconsole.h        2026-07-15 
14:22:56.000000000 +0200
@@ -167,12 +167,13 @@
 
 /* epoll.c */
 extern void epoll_addread(int fd, void *fptr);
+extern void epoll_addsysfs(int fd, void *fptr);
 extern void epoll_addwrite(int fd, void *fptr);
 extern void epoll_answer_once(int fd, void *fptr);
 extern void epoll_reenable(int fd);
 extern void epoll_delete(int fd);
 extern void (*epoll_handle(void *ptr, int *fd))(int);
-extern void epoll_close_fd(void);
+extern void epoll_close_fd(int keep_fd);
 
 /* frobnicate.c */
 extern void *frobnicate(void *in, const size_t len);
@@ -240,6 +241,17 @@
 extern void parseterm(char *msg);
 extern void parsespool(char *msg);
 extern void warning3215(int fd);
+#else
+/* vt.c */
+extern int vt_supported(void);
+extern int vt_is_graphics(int fd);
+extern int vt_is_text(int fd);
+extern int vt_get_mode(int fd);
+extern int vt_set_text_mode(int fd);
+extern int vt_get_free(int fd);
+extern int vt_get_active(int fd);
+extern int vt_switch(int fd, int vtno);
+extern void vt_disallocate(int fd, int vtno);
 #endif
 
 #define MAX_PASSLEN    LINE_MAX
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/libconsole/tty.c 
new/showconsole-2.47/libconsole/tty.c
--- old/showconsole-2.46/libconsole/tty.c       2026-07-09 15:29:51.000000000 
+0200
+++ new/showconsole-2.47/libconsole/tty.c       2026-07-15 14:22:56.000000000 
+0200
@@ -119,7 +119,7 @@
            unsigned char buf[BUF_LEN];
            ssize_t e;
 
-           if (!can_read(nd, -1))
+           if (!can_read(nd, 2000))
                break;
 
            len = read (nd, buf, BUF_LEN);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/libconsole/vmcp.c 
new/showconsole-2.47/libconsole/vmcp.c
--- old/showconsole-2.46/libconsole/vmcp.c      2026-07-09 15:29:51.000000000 
+0200
+++ new/showconsole-2.47/libconsole/vmcp.c      2026-07-15 14:22:56.000000000 
+0200
@@ -102,8 +102,11 @@
     do {
        rc = read(fd, ret, buffersize);
        if (rc < 0) {
-           if (errno != EINTR)
+           if (errno != EINTR) {
+               free(ret);
+               ret = NULL;
                goto out;
+           }
        }
     } while (rc < 0);
 out:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/libconsole/vt.c 
new/showconsole-2.47/libconsole/vt.c
--- old/showconsole-2.46/libconsole/vt.c        1970-01-01 01:00:00.000000000 
+0100
+++ new/showconsole-2.47/libconsole/vt.c        2026-07-15 14:22:56.000000000 
+0200
@@ -0,0 +1,99 @@
+/*
+ * vt.c - Virtual Terminal Management for blogd
+ *
+ * Copyright 2026 Werner Fink
+ * Copyright 2026 SUSE Software Solutions Germany GmbH
+ *
+ * This source is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <linux/vt.h>
+#include <linux/kd.h>
+#include "libconsole.h"
+
+#if !defined(__s390__) && !defined(__s390x__)
+/* Check if the system supports VTs (e.g. not s390x) */
+int vt_supported(void)
+{
+    return (access("/sys/devices/virtual/tty/tty0/active", R_OK) == 0);
+}
+
+/* Checks if the terminal is running within a graphical environment 
(X11/Wayland) */
+int vt_is_graphics(int fd)
+{
+    int mode = KD_TEXT;
+    if (ioctl(fd, KDGETMODE, &mode) < 0)
+        return 0;
+    return (mode == KD_GRAPHICS);
+}
+
+/* Checks if the terminal is a pure text console (TTY) */
+int vt_is_text(int fd)
+{
+    int mode = KD_TEXT;
+    if (ioctl(fd, KDGETMODE, &mode) < 0)
+        return 0;
+    return (mode == KD_TEXT);
+}
+
+/* Get current KD mode (KD_TEXT or KD_GRAPHICS) */
+int vt_get_mode(int fd)
+{
+    int mode = KD_TEXT;
+    if (ioctl(fd, KDGETMODE, &mode) < 0)
+       return -1;
+    return mode;
+}
+
+/* Set KD mode to KD_TEXT */
+int vt_set_text_mode(int fd)
+{
+    int mode = KD_TEXT;
+    if (ioctl(fd, KDSETMODE, &mode) < 0)
+       return -1;
+    return mode;
+}
+
+/* Find the first free Virtual Terminal */
+int vt_get_free(int fd)
+{
+    int vtno = -1;
+    if (ioctl(fd, VT_OPENQRY, &vtno) < 0)
+       return -1;
+    return vtno;
+}
+
+/* Get currently active VT number */
+int vt_get_active(int fd)
+{
+    struct vt_stat vstate;
+    if (ioctl(fd, VT_GETSTATE, &vstate) < 0)
+       return -1;
+    return vstate.v_active;
+}
+
+/* Switch to a specific VT and wait until the switch completes */
+int vt_switch(int fd, int vtno)
+{
+    if (ioctl(fd, VT_ACTIVATE, vtno) < 0)
+       return -1;
+    if (ioctl(fd, VT_WAITACTIVE, vtno) < 0)
+       return -1;
+    return 0;
+}
+
+/* Deallocate a VT after use to free up kernel memory */
+void vt_disallocate(int fd, int vtno)
+{
+    ioctl(fd, VT_DISALLOCATE, vtno);
+}
+#endif
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/showconsole-2.46/listing.h 
new/showconsole-2.47/listing.h
--- old/showconsole-2.46/listing.h      2026-07-09 15:29:51.000000000 +0200
+++ new/showconsole-2.47/listing.h      2026-07-15 14:22:56.000000000 +0200
@@ -102,6 +102,8 @@
 
 #define list_first_entry(ptr, type, member) \
        list_entry((ptr)->next, type, member)
+#define list_last_entry(ptr, type, member) \
+       list_entry((ptr)->prev, type, member)
 #define list_next_entry(pos, member)   \
        list_entry((pos)->member.next, typeof(*(pos)), member)
 

Reply via email to