sd-varlink: extract varlink_handle_upgrade_fds() helper

Extract the fd-handling logic from sd_varlink_call_and_upgrade() into a
shared static helper so that it can be reused by the upcoming server-side
sd_varlink_reply_and_upgrade().
This commit is contained in:
Michael Vogt
2026-04-09 08:50:40 +02:00
parent 9bd72b612b
commit 82b04e7f8b
+57 -42
View File
@@ -2385,6 +2385,58 @@ _public_ int sd_varlink_call(
return sd_varlink_call_full(v, method, parameters, ret_parameters, ret_error_id, NULL);
}
static int varlink_handle_upgrade_fds(sd_varlink *v, int *ret_input_fd, int *ret_output_fd) {
int r;
assert(v);
assert(ret_input_fd || ret_output_fd);
/* Ensure no post-upgrade data was consumed into our input buffer (we ensure this via MSG_PEEK or
* byte-to-byte) and refuse the upgrade rather than silently losing the data. */
if (v->input_buffer_size != 0)
return varlink_log_errno(v, SYNTHETIC_ERRNO(EPROTO),
"Unexpected buffered data during protocol upgrade, refusing.");
/* Pass the connection fds to the caller, it owns them now. Reset to blocking mode
* since callers of the upgraded protocol will generally expect normal blocking
* semantics. */
r = fd_nonblock(v->input_fd, false);
if (r < 0)
return varlink_log_errno(v, r, "Failed to set input fd to blocking mode: %m");
if (v->input_fd != v->output_fd) {
r = fd_nonblock(v->output_fd, false);
if (r < 0)
return varlink_log_errno(v, r, "Failed to set output fd to blocking mode: %m");
}
/* Hand out the fds to the caller. When the caller doesn't want one direction, shut it
* down: but avoid closing the underlying fd if the other direction still needs it
* (i.e. when input_fd == output_fd). */
bool same_fd = v->input_fd == v->output_fd;
if (ret_input_fd)
*ret_input_fd = TAKE_FD(v->input_fd);
else {
(void) shutdown(v->input_fd, SHUT_RD);
if (same_fd && ret_output_fd)
TAKE_FD(v->input_fd); /* don't close yet, output branch needs it */
else
v->input_fd = safe_close(v->input_fd);
}
if (ret_output_fd)
*ret_output_fd = TAKE_FD(v->output_fd);
else {
(void) shutdown(v->output_fd, SHUT_WR);
if (same_fd && ret_input_fd)
TAKE_FD(v->output_fd);
else
v->output_fd = safe_close(v->output_fd);
}
return 0;
}
_public_ int sd_varlink_call_and_upgrade(
sd_varlink *v,
const char *method,
@@ -2436,45 +2488,12 @@ _public_ int sd_varlink_call_and_upgrade(
goto finish;
}
/* Pass the connection fds to the caller, it owns them now. Reset to blocking mode
* since callers of the upgraded protocol will generally expect normal blocking
* semantics. */
r = fd_nonblock(v->input_fd, false);
/* Even if setting up the fds fails we must disconnect: the server already accepted the
* upgrade, so the other side is speaking raw protocol while we expect JSON. */
r = varlink_handle_upgrade_fds(v, ret_input_fd, ret_output_fd);
if (r < 0) {
varlink_log_errno(v, r, "Failed to set input fd to blocking mode: %m");
goto disconnect;
}
if (v->input_fd != v->output_fd) {
r = fd_nonblock(v->output_fd, false);
if (r < 0) {
varlink_log_errno(v, r, "Failed to set output fd to blocking mode: %m");
goto disconnect;
}
}
/* Hand out the fds to the caller. When the caller doesn't want one direction, shut it
* down: but avoid closing the underlying fd if the other direction still needs it
* (i.e. when input_fd == output_fd). */
bool same_fd = v->input_fd == v->output_fd;
if (ret_input_fd)
*ret_input_fd = TAKE_FD(v->input_fd);
else {
(void) shutdown(v->input_fd, SHUT_RD);
if (same_fd && ret_output_fd)
TAKE_FD(v->input_fd); /* don't close yet, output branch needs it */
else
v->input_fd = safe_close(v->input_fd);
}
if (ret_output_fd)
*ret_output_fd = TAKE_FD(v->output_fd);
else {
(void) shutdown(v->output_fd, SHUT_WR);
if (same_fd && ret_input_fd)
TAKE_FD(v->output_fd);
else
v->output_fd = safe_close(v->output_fd);
varlink_set_state(v, VARLINK_DISCONNECTED);
goto finish;
}
varlink_set_state(v, VARLINK_DISCONNECTED);
@@ -2488,10 +2507,6 @@ _public_ int sd_varlink_call_and_upgrade(
return 1;
disconnect:
/* If we fail after the server already accepted the upgrade, nothing can be done but disconnect.
* The other side is speaking raw protocol while we expect JSON. */
varlink_set_state(v, VARLINK_DISCONNECTED);
finish:
v->protocol_upgrade = false;
assert(v->n_pending == 1);