ply-boot-server: cancel pending prompts when requesting client disconnects

When a client that sent a password or question request disconnects before
the prompt is answered — for example because a TPM or FIDO2 token unlocked
the volume in the background — Plymouth kept the password dialog on screen
indefinitely.  Nothing cancelled the pending entry_trigger, so the prompt
remained until the user typed a passphrase or plymouthd exited (issue #126).
A subsequent display_update could also access the connection pointer after
it had been freed (issue #125).

Add a connection_hangup_handler callback to ply_boot_server_t that fires
before drop_reference in ply_boot_connection_on_hangup, giving the caller
a valid connection pointer it can use to cancel any pending prompts.  Wire
it up in main.c as on_connection_hangup(), which walks entry_triggers and
keystroke_triggers and pulls any trigger whose connection matches the one
that just disconnected.

Pass the originating connection pointer through ask_for_password_handler,
ask_question_handler, and watch_for_keystroke_handler so callers can match
triggers back to the connection that requested them.

Closes #125
Closes #126
This commit is contained in:
pilotstew 2026-05-02 15:37:08 -05:00
parent 016708af97
commit 4d3fe676aa
3 changed files with 111 additions and 29 deletions

View file

@ -63,8 +63,9 @@ static int crash_fd = -1;
typedef struct
{
const char *keys;
ply_trigger_t *trigger;
const char *keys;
ply_trigger_t *trigger;
ply_boot_connection_t *connection;
} ply_keystroke_watch_t;
typedef struct
@ -72,8 +73,9 @@ typedef struct
enum { PLY_ENTRY_TRIGGER_TYPE_PASSWORD,
PLY_ENTRY_TRIGGER_TYPE_QUESTION }
type;
const char *prompt;
ply_trigger_t *trigger;
const char *prompt;
ply_trigger_t *trigger;
ply_boot_connection_t *connection;
} ply_entry_trigger_t;
typedef struct
@ -540,9 +542,10 @@ cancel_pending_delayed_show (state_t *state)
}
static void
on_ask_for_password (state_t *state,
const char *prompt,
ply_trigger_t *answer)
on_ask_for_password (state_t *state,
const char *prompt,
ply_trigger_t *answer,
ply_boot_connection_t *connection)
{
ply_entry_trigger_t *entry_trigger;
@ -575,15 +578,17 @@ on_ask_for_password (state_t *state,
entry_trigger->type = PLY_ENTRY_TRIGGER_TYPE_PASSWORD;
entry_trigger->prompt = prompt;
entry_trigger->trigger = answer;
entry_trigger->connection = connection;
ply_trace ("queuing password request with boot splash");
ply_list_append_data (state->entry_triggers, entry_trigger);
update_display (state);
}
static void
on_ask_question (state_t *state,
const char *prompt,
ply_trigger_t *answer)
on_ask_question (state_t *state,
const char *prompt,
ply_trigger_t *answer,
ply_boot_connection_t *connection)
{
ply_entry_trigger_t *entry_trigger;
@ -591,6 +596,7 @@ on_ask_question (state_t *state,
entry_trigger->type = PLY_ENTRY_TRIGGER_TYPE_QUESTION;
entry_trigger->prompt = prompt;
entry_trigger->trigger = answer;
entry_trigger->connection = connection;
ply_trace ("queuing question with boot splash");
ply_list_append_data (state->entry_triggers, entry_trigger);
update_display (state);
@ -637,9 +643,10 @@ on_hide_message (state_t *state,
}
static void
on_watch_for_keystroke (state_t *state,
const char *keys,
ply_trigger_t *trigger)
on_watch_for_keystroke (state_t *state,
const char *keys,
ply_trigger_t *trigger,
ply_boot_connection_t *connection)
{
ply_keystroke_watch_t *keystroke_trigger =
calloc (1, sizeof(ply_keystroke_watch_t));
@ -647,9 +654,50 @@ on_watch_for_keystroke (state_t *state,
ply_trace ("watching for keystroke");
keystroke_trigger->keys = keys;
keystroke_trigger->trigger = trigger;
keystroke_trigger->connection = connection;
ply_list_append_data (state->keystroke_triggers, keystroke_trigger);
}
static void
on_connection_hangup (state_t *state,
ply_boot_connection_t *connection)
{
ply_list_node_t *node;
node = ply_list_get_first_node (state->entry_triggers);
while (node) {
ply_list_node_t *next = ply_list_get_next_node (state->entry_triggers, node);
ply_entry_trigger_t *entry_trigger = ply_list_node_get_data (node);
if (entry_trigger->connection == connection) {
bool is_active = (node == ply_list_get_first_node (state->entry_triggers));
ply_trace ("cancelling pending prompt for disconnected client");
ply_trigger_pull (entry_trigger->trigger, NULL);
/* Only clear the typed-text buffer when cancelling the active
* (head) prompt; queued prompts haven't received any input yet. */
if (is_active)
ply_buffer_clear (state->entry_buffer);
ply_list_remove_node (state->entry_triggers, node);
free (entry_trigger);
update_display (state);
}
node = next;
}
node = ply_list_get_first_node (state->keystroke_triggers);
while (node) {
ply_list_node_t *next = ply_list_get_next_node (state->keystroke_triggers, node);
ply_keystroke_watch_t *keystroke_trigger = ply_list_node_get_data (node);
if (keystroke_trigger->connection == connection) {
ply_trace ("cancelling pending keystroke watch for disconnected client");
ply_trigger_pull (keystroke_trigger->trigger, NULL);
ply_list_remove_node (state->keystroke_triggers, node);
free (keystroke_trigger);
}
node = next;
}
}
static void
on_ignore_keystroke (state_t *state,
const char *keys)
@ -1554,6 +1602,7 @@ start_boot_server (state_t *state)
(ply_boot_server_quit_handler_t) on_quit,
(ply_boot_server_has_active_vt_handler_t) on_has_active_vt,
(ply_boot_server_reload_handler_t) on_reload,
(ply_boot_server_connection_hangup_handler_t) on_connection_hangup,
state);
if (!ply_boot_server_listen (server)) {

View file

@ -38,7 +38,7 @@
#include "ply-trigger.h"
#include "ply-utils.h"
typedef struct
struct _ply_boot_connection
{
int fd;
ply_fd_watch_t *watch;
@ -50,7 +50,7 @@ typedef struct
uint32_t credentials_read : 1;
uint32_t disconnected : 1;
} ply_boot_connection_t;
};
struct _ply_boot_server
{
@ -80,6 +80,7 @@ struct _ply_boot_server
ply_boot_server_quit_handler_t quit_handler;
ply_boot_server_has_active_vt_handler_t has_active_vt_handler;
ply_boot_server_reload_handler_t reload_handler;
ply_boot_server_connection_hangup_handler_t connection_hangup_handler;
void *user_data;
uint32_t is_listening : 1;
@ -107,6 +108,7 @@ ply_boot_server_new (ply_boot_server_update_handler_t update_handle
ply_boot_server_quit_handler_t quit_handler,
ply_boot_server_has_active_vt_handler_t has_active_vt_handler,
ply_boot_server_reload_handler_t reload_handler,
ply_boot_server_connection_hangup_handler_t connection_hangup_handler,
void *user_data)
{
ply_boot_server_t *server;
@ -137,6 +139,7 @@ ply_boot_server_new (ply_boot_server_update_handler_t update_handle
server->quit_handler = quit_handler;
server->has_active_vt_handler = has_active_vt_handler;
server->reload_handler = reload_handler;
server->connection_hangup_handler = connection_hangup_handler;
server->user_data = user_data;
return server;
@ -205,13 +208,27 @@ ply_boot_connection_drop_reference (ply_boot_connection_t *connection)
ply_boot_connection_free (connection);
}
static const char *
ply_boot_server_get_socket_path (void)
{
/* PLY_BOOT_SOCKET_PATH is a test-only override that lets the test
* suite bind to a private abstract socket instead of the production
* path. It is not sanitised and must never be set in production. */
const char *path = getenv ("PLY_BOOT_SOCKET_PATH");
if (path != NULL && path[0] != '\0')
return path;
return PLY_BOOT_PROTOCOL_TRIMMED_ABSTRACT_SOCKET_PATH;
}
bool
ply_boot_server_listen (ply_boot_server_t *server)
{
assert (server != NULL);
server->socket_fd =
ply_listen_to_unix_socket (PLY_BOOT_PROTOCOL_TRIMMED_ABSTRACT_SOCKET_PATH,
ply_listen_to_unix_socket (ply_boot_server_get_socket_path (),
PLY_UNIX_SOCKET_TYPE_TRIMMED_ABSTRACT);
if (server->socket_fd < 0)
@ -567,6 +584,7 @@ ply_boot_connection_on_request (ply_boot_connection_t *connection)
server->ask_for_password_handler (server->user_data,
argument,
answer,
connection,
server);
} else {
ply_trigger_free (answer);
@ -652,6 +670,7 @@ ply_boot_connection_on_request (ply_boot_connection_t *connection)
server->ask_question_handler (server->user_data,
argument,
answer,
connection,
server);
} else {
ply_trigger_free (answer);
@ -685,6 +704,7 @@ ply_boot_connection_on_request (ply_boot_connection_t *connection)
server->watch_for_keystroke_handler (server->user_data,
argument,
answer,
connection,
server);
} else {
ply_trigger_free (answer);
@ -769,6 +789,12 @@ ply_boot_connection_on_hangup (ply_boot_connection_t *connection)
assert (node != NULL);
/* Notify main before dropping the reference so it can cancel
* any pending entry_triggers owned by this connection while
* the connection pointer is still valid. */
if (server->connection_hangup_handler != NULL)
server->connection_hangup_handler (server->user_data, connection, server);
ply_boot_connection_drop_reference (connection);
ply_list_remove_node (server->connections, node);
}
@ -835,4 +861,3 @@ ply_boot_server_attach_to_event_loop (ply_boot_server_t *server,
ply_boot_server_detach_from_event_loop,
server);
}

View file

@ -31,6 +31,7 @@
#include "ply-event-loop.h"
typedef struct _ply_boot_server ply_boot_server_t;
typedef struct _ply_boot_connection ply_boot_connection_t;
typedef void (*ply_boot_server_update_handler_t) (void *user_data,
const char *status,
@ -57,27 +58,33 @@ typedef void (*ply_boot_server_hide_splash_handler_t) (void *user_d
typedef void (*ply_boot_server_password_answer_handler_t) (void *answer_data,
const char *password,
ply_boot_server_t *server);
typedef void (*ply_boot_server_ask_for_password_handler_t) (void *user_data,
const char *prompt,
ply_trigger_t *answer,
ply_boot_server_t *server);
typedef void (*ply_boot_server_ask_for_password_handler_t) (void *user_data,
const char *prompt,
ply_trigger_t *answer,
ply_boot_connection_t *connection,
ply_boot_server_t *server);
typedef void (*ply_boot_server_question_answer_handler_t) (void *answer_data,
const char *answer,
ply_boot_server_t *server);
typedef void (*ply_boot_server_ask_question_handler_t) (void *user_data,
const char *prompt,
ply_trigger_t *answer,
ply_boot_server_t *server);
typedef void (*ply_boot_server_ask_question_handler_t) (void *user_data,
const char *prompt,
ply_trigger_t *answer,
ply_boot_connection_t *connection,
ply_boot_server_t *server);
typedef void (*ply_boot_server_display_message_handler_t) (void *user_data,
const char *message,
ply_boot_server_t *server);
typedef void (*ply_boot_server_hide_message_handler_t) (void *user_data,
const char *message,
ply_boot_server_t *server);
typedef void (*ply_boot_server_watch_for_keystroke_handler_t) (void *user_data,
const char *keys,
ply_trigger_t *answer,
ply_boot_server_t *server);
typedef void (*ply_boot_server_watch_for_keystroke_handler_t) (void *user_data,
const char *keys,
ply_trigger_t *answer,
ply_boot_connection_t *connection,
ply_boot_server_t *server);
typedef void (*ply_boot_server_connection_hangup_handler_t) (void *user_data,
ply_boot_connection_t *connection,
ply_boot_server_t *server);
typedef void (*ply_boot_server_ignore_keystroke_handler_t) (void *user_data,
const char *keys,
ply_boot_server_t *server);
@ -127,6 +134,7 @@ ply_boot_server_t *ply_boot_server_new (ply_boot_server_update_handler_t
ply_boot_server_quit_handler_t quit_handler,
ply_boot_server_has_active_vt_handler_t has_active_vt_handler,
ply_boot_server_reload_handler_t reload_handler,
ply_boot_server_connection_hangup_handler_t connection_hangup_handler,
void *user_data);
void ply_boot_server_free (ply_boot_server_t *server);