• src/sftp/sftp_outcome.csrc/sbbs3/main.cpp sftp.cpp src/sftp/sftp.c sft

    From Deucе@1:103/705 to Git commit to main/sbbs/master on Sat Apr 25 02:38:04 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/800c8b09317e30a0868a4d10
    Added Files:
    src/sftp/sftp_outcome.c
    Modified Files:
    src/sbbs3/main.cpp sftp.cpp src/sftp/sftp.c sftp.h sftp_client.c sftp_server.c src/syncterm/sftp_browser.c sftp_queue.c ssh.c
    Log Message:
    sftp: replace last_error TLS hack with sftp{c,s}_outcome

    The pthread_key_t TLS for sftpc_get_err was removed for portability in 2710c20e54, but that exposed a real race the TLS had been masking: the
    three SyncTERM user threads (browser, uploader, downloader) sharing one sftpc_state_t can clobber each other's last_error between op return
    and sftpc_get_err call.

    Reshape the API so per-op state lives in caller-supplied storage and
    shared scalar state on the connection goes away. New
    struct sftp{c,s}_outcome carries:

    - err code (sftp_err_code_t -- flat enum of specific lib failure
    modes; SFTP_ERR_OK on success), valid when op returns false
    - result code (SSH_FX_* from server reply, client-side only),
    valid when op returns true
    - estr text accumulator with file:line prefixes per record, plus
    server-supplied SSH_FXP_STATUS messages as `Reply: "..."` lines

    Bool contract:
    true - op completed; act on result. err/estr are diagnostic only.
    false - op didn't complete; result undefined. Switch on err for
    retry decisions; show estr to the user.

    Stack-allocated via SFTPC_OUTCOME_DECL(name, textsz) /
    SFTPS_OUTCOME_DECL macros. Pass NULL for "don't care", or sz=0 for
    "codes but no text". A single internal outcome_record_impl backs both
    public record entry points so formatting/accumulation lives in one
    place. sftpc_outcome_reply formats server-supplied SSH_FXP_STATUS
    text as `Reply: "msg" (lang)` lines, distinguishable from
    file:line-prefixed lib-internal records.

    Server side gets sftps_outcome too (no result field -- server supplies
    the SSH_FX_* code rather than receiving it). Threading covers
    sftps_recv, sftps_send_packet/error/handle/data/name/attrs/extended_reply
    and the static dispatch helpers. sbbs3/main.cpp's sftps_recv sites
    get real outcomes that log via lprintf on failure; sbbs3/sftp.cpp's
    ~100 sftps_send_* sites pass nullptr for now (existing lprintf
    plumbing in the lib already logs the immediate context).

    SyncTERM consumers (ssh.c, sftp_browser.c, sftp_queue.c) take real
    outcomes. The queue worker uses sftp_err_is_transient() to choose
    between SFTP_JOB_QUEUED (retryable) and SFTP_JOB_FAILED for op-level
    failures. ssh.c's SSH_FX_EOF handling correctly lives on the
    post-true branch.

    set_thread_err / get_thread_err / sftpc_get_err / state->last_error
    all gone.

    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)