• src/sbbs3/main.cpp

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Mon Jul 6 00:00:16 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/39112546f2b835451e37289b
    Modified Files:
    src/sbbs3/main.cpp
    Log Message:
    Fix client-socket errors misreported as spy-socket errors on *nix (#1184)

    input_thread() identified which socket a failed/completed recv() belonged
    to by re-comparing 'sock' against the atomic sbbs->client_socket after the fact. Since sbbs_t::hangup() (and other cross-thread teardown) closes the client socket and stores INVALID_SOCKET to wake this thread, the identity re-check could fail for a socket that WAS the client socket, misrouting an ordinary disconnect into the *nix else-branch that assumes any non-client socket is the node's local spy socket. Result: routine (usually SSH)
    session teardowns were logged to error.log as, e.g.:

    Node 11 !ERROR 9 (Bad file descriptor) on local spy socket 179 receive

    where fd 179 was actually the node's passthru/client socket, never a spy connection -- sending the sysop hunting for spy-socket problems that don't exist. Observed four times since Jan-2025 on Vertrauen's Linux host,
    always coinciding with 'disconnecting client' teardown.

    Remember which socket was selected at poll time (new 'spy_sock' bool) and branch on that instead, at all four re-comparison sites: the SSH receive
    path, the receive-error handler, the EOF/'disconnected' check, and the skip-telnet-interpretation check. A post-hangup wake-up now routes to the client branch, which exits quietly via the existing !online check (hangup clears 'online' before closing the socket).

    Validated against a live scratch terminal server: client disconnect, spy connect/mirror/disconnect (clean 'Closing local spy socket' notice), and server-terminated-mid-session teardown -- no misattributed spy-socket
    errors logged. Closes issue #1184.

    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Mon Jul 6 00:00:17 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/8101584ded3352342328fcb6
    Modified Files:
    src/sbbs3/main.cpp
    Log Message:
    Terminal server: shutdown(), don't close(), node sockets at terminate/recycle

    At server termination/recycle, the terminal server thread force-
    disconnected active nodes by calling close_socket() on each node's client socket. But that descriptor is owned by the node thread: sbbs_t::hangup() closes it again during the node's own teardown (after its 1-second output- flush mswait), producing a double-close. On *nix that logged the (benign
    but alarming) shutdown-time noise:

    !ERROR 9 closing socket 11

    one second after 'Closing node N socket 11'; on Windows the same double-
    close was silent only because close_socket() exempts ENOTSOCK. Worse than
    the noise: between the two closes the fd number could be reused (a new
    inbound connection during recycle, an opened file) and hangup() would then close an unrelated descriptor out from under its new owner.

    Shut the socket down (SHUT_RDWR) instead of closing it: that wakes every blocked reader -- and unlike close(), propagates EOF through dup'd
    descriptors held by external programs -- while the fd number stays
    allocated until the owning node thread performs the one real close in
    hangup(). node_socket[] is still marked INVALID_SOCKET here, preserving
    the input_thread 'shutdown locally' signal that terminates JS execution promptly.

    Same fd-lifetime family as 39112546f2 (tables-20-ideas) / issue #1184. Validated against a live scratch terminal server: SIGTERM with an active
    client session now yields a clean 'Shutting down node 1 socket 11' ->
    'Node 1 disconnected' -> node-thread-terminated sequence with no EBADF
    errors logged.

    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Mon Jul 6 00:00:17 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/46ebc4f26ace3485ff434fe3
    Modified Files:
    src/sbbs3/main.cpp
    Log Message:
    Terminal server: keep node.dab in sync through unclean node teardowns (#1178)

    Address the two defects behind the 'Node N status with invalid socket corrected' error (reported by Nelgin, issue #1178, seen when a user is
    online at weekly-maintenance time):

    1. node_thread()'s final status reset gave up after one failed locked
    getnodedat() -- e.g. when node.dab is briefly locked or inaccessible
    during an exclusive event -- silently stranding the node record in-use
    (with no live thread) until the next inbound connection 'corrects' it.
    Retry the reset (3 attempts) and log an error if it still fails.

    2. The exclusive-event force-disconnect (after 60 minutes of waiting for a
    node to become inactive) called close_socket() on the node's client
    socket, which the node thread's own teardown (hangup) also closes --
    the same cross-thread double-close/fd-reuse hazard fixed at server
    termination in 8101584ded (symbol-19-seek). Use shutdown() instead and
    leave the one real close to the owning node thread.

    Also demote the self-healing 'corrected' message from LOG_CRIT to LOG_ERR: still recorded in data/error.log (deliberately, so any recurrence of a
    stranded node record remains visible/trackable), but no longer at the
    highest severity.

    Validated against a live scratch terminal server: normal disconnect resets node.dab to WFC via the new retry loop; a hand-stranded NODE_INUSE record
    is corrected on the next connect and logged (once) to error.log.

    Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net