Note: I hit this many times while I develop and test the VSCode extension at https://github.com/lukka/c64jasm which uses VICE.
This path fixes the endless "vice_network_send ... Broken pipe" spam from the text remote monitor
Subsystem: monitor (text remote monitor, src/monitor/monitor_network.c)
Affects: all ports (code is arch-shared; reproduced with x64sc)
Fixes: bug #2029 "incorrect retval type used for socket API resulting in
internal error" (https://sourceforge.net/p/vice-emu/bugs/2029/)
-- specifically the still-open part of it:
"An additional anomaly is that when this happens during a remote
monitor session, it will loop with this message indefinitely,
probably due to the condition not being checked correctly further
down." (Daniel Kahlin, 2024-05-11)
The ssize_t/size_t part of #2029 was addressed by the return-value
rewrite, but as noted in that ticket the symptom remained
("There was a rewrite of the return value types. The actual symptom
reporter here remains however.", 2024-07-06) and was reconfirmed by
gpz on 2025-11-18: "still the same in trunk, it loops the message
forever". Daniel Kahlin already pointed at the right place:
"src/monitor/monitor_network.c: monitor_network_transmit() casts
this back from int to size_t again, not sure where it ends up."
This patch fixes where it ends up.
NOTE (AI use): The root-cause analysis was performed with AI assistance
(GitHub Copilot). Every statement below was verified directly against the
VICE source (functions cited inline: monitor_network_transmit/quit/
data_available in src/monitor/monitor_network.c, uimon_in in
src/monitor/mon_util.c, make_prompt/monitor_startup in src/monitor/monitor.c,
vice_network_send in src/socket.c) and against an actual "x64sc -logfile"
capture. Please review before applying.
When a client of the text remote monitor (-remotemonitor) closes its
connection without leaving the monitor cleanly (no "x"/"exit"/"quit"),
VICE floods the logfile with, forever:
Error - vice_network_send: internal error (ret:-1 buffer_length:10 errno:32 - Broken pipe)
Error - vice_network_send: internal error (ret:-1 buffer_length:10 errno:32 - Broken pipe)
VSync: Sync reset
(buffer_length:10 is the 10-byte monitor prompt "(C:$xxxx) ".)
monitor_network_transmit() never closes the connection when send() fails.
A half-closed socket is reported as readable by select(), so
monitor_network_data_available() -> monitor_check_remote() re-enters the
monitor every frame. On entry uimon_in() writes the prompt first and, on
failure, returns before monitor_network_get_command_line() (the only place
that detects the EOF via recv() <= 0 and calls monitor_network_quit()) is
ever reached. The dead socket is therefore never released and the monitor
busy-loops: prompt write -> EPIPE -> mon_exit() -> "VSync: Sync reset" ->
re-enter -> repeat. (The binary monitor is unaffected: it closes on recv()
<= 0.)
Note that a graceful close is handled correctly: the first send() still
succeeds, so the read path runs and drops the connection
("monitor_network_receive(): vice_network_receive() returned -1, breaking
connection"). Only an aborted connection (RST) that arrives while the
emulation is running triggers the loop, because then the very first send()
of the prompt already fails and the read path is never reached.
Close the connection in monitor_network_transmit() when the send fails, so
the half-open socket is dropped and monitor_check_remote() stops re-entering
the monitor (it falls back to listening for a new connection).
Verified against the released VICE 3.10 (arm64 GTK3 build) on macOS.
Beware: connecting with "nc" and pressing Ctrl-C does NOT reproduce it, and
neither does closing while stopped in the monitor -- both take the read path,
which already handles the disconnect. The connection must be aborted (RST)
while the emulation is running:
x64sc -default -logfile /tmp/vice.log \
-remotemonitor -remotemonitoraddress ip4://127.0.0.1:6510
# then, in another shell:
python3 - <<'EOF'
import socket, struct, time
s = socket.create_connection(("127.0.0.1", 6510))
time.sleep(0.5)
s.sendall(b"x\n") # leave the monitor -> emulation resumes
time.sleep(1.0)
# SO_LINGER {on,0} makes close() send RST, so VICE's next send() gets EPIPE
s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", 1, 0))
s.close()
EOF
grep -c 'Broken pipe' /tmp/vice.log # keeps growing, ~70 lines/second
Observed without the patch: ~70 errors/second, unbounded (196 KB of logfile
within ~15 s), and the connection is never dropped. With the patch the
connection is closed on the first failed send and the monitor goes back to
listening for a new client.
The recipe given in bug #2029 works too:
echo -en 'help\n\x03\n' | nc localhost 6510 # then Ctrl-C
diff --git a/src/monitor/monitor_network.c b/src/monitor/monitor_network.c
index 759dc5d..66b28a1 100644
--- a/src/monitor/monitor_network.c
+++ b/src/monitor/monitor_network.c
@@ -53,6 +53,8 @@ static vice_network_socket_t * connected_socket = NULL;
static char * monitor_server_address = NULL;
static int monitor_enabled = 0;
+static void monitor_network_quit(void);
+
int monitor_network_transmit(const char * buffer, size_t buffer_length)
{
int error = 0;
@@ -61,6 +63,15 @@ int monitor_network_transmit(const char * buffer, size_t buffer_length)
size_t len = (size_t)vice_network_send(connected_socket, buffer, buffer_length, 0);
if (len != buffer_length) {
could you attach the patch as a file please?
yes, attaching the whole patch file, thanks.
With the patch, on Linux, it behaves like this:
in log i see (once)
the emulation stays "frozen" (as if monitor is open) and i can't reopen the remote monitor nor the normal monitor