• src/sbbs3/main.cpp qwkjob.cpp

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Mon Oct 26 00:09:03 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/e2cc0e5bba9f364d27e9a59d
    Modified Files:
    src/sbbs3/main.cpp qwkjob.cpp
    Log Message:
    qwkjob: run the event thread's QWK sweeps and hub packing as jobs

    The Terminal Server's event thread had its own copy of the QWK logic: the data/pack*.now and data/file/*.rep sweeps for users, the data/<HUBID>.q?? inbound sweep and the timed hub REP pack. All of it shared temp/event/,
    and the hub code took no lock, so a script's system.qwk_pack("HUB") could
    merge new messages into data/<HUBID>.REP while the call-out was sending
    it. The call-out then deleted the REP, the export pointers had already advanced, and those messages were never sent.

    The sweeps now call qwkjob_unpack_rep(), qwkjob_pack() and
    qwkjob_unpack_qwk(), so the logic lives in one place and every packet
    gets its own temp/qwk/ job directory. The globs and the removal of
    packets for deleted, inactive or unknown users stay as they were; a .now semaphore or a queued REP whose user is busy elsewhere is left for the
    next pass. Three event-thread host contexts carry the existing
    unpackREP, packQWK and QNET prefixes, so the job lines still reach
    events.log as before, and they are exempt from the concurrency cap.

    The timed hub pack takes the hub lock with qwkjob_lock_hub(), packs with qwkjob_pack_rep_locked(), updates time.ini and runs the call-out as
    before (still in the event thread, with its node directory and JS
    context), and only then unlocks. While the lock is held, a pack or import
    for that hub from any other job, including the call-out script itself,
    gets "busy"; a hub whose lock is held when it is due is retried on the
    next pass rather than marked as run.

    Behavior changes:
    - A failed user REP is kept as data/file/<user#>.rep.<hex>.bad and the
    newest 10 per user are retained. The event thread's old prune pattern
    ("%u.rep.*.bad", no zero padding) never matched, so they accumulated.
    - A failed hub packet keeps its own name (<HUBID>.qwk, or .qw1 etc. from
    qnet-ftp.js) in the .bad name, and the newest 10 <HUBID>.q??.*.bad are
    retained, as the event thread did. Renaming a .bad file back still gets
    it swept, for user and hub packets alike.
    - A 0-byte inbound hub packet is removed and logged instead of being
    skipped on every pass forever.
    - An import whose packet disappeared after the caller found it (imported
    by another host's sweep) is skipped with a debug line, as the event
    thread did, instead of being logged as a missing packet.

    The Terminal Server's job hosts also used scfg.temp_dir, which in that
    process is the node-relative "temp", so jobs started from the node and
    event JS contexts made their directories under ctrl/tempqwk/. They now
    use the parent of the event thread's temp directory (startup temp_dir,
    else ../temp), as Services does.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

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