mirror of
https://github.com/troglobit/finit.git
synced 2026-10-07 16:14:21 +07:00
libink: let a handler park its call and reply later
The park machinery was welded to broker uid resolution; a handler that cannot answer yet, like a device-settle call waiting for the event queue to drain, had no way to defer. link_call_park() holds the request, link_call_resume() re-runs the handler with link_call_resumed() reading true, and the expire sweep remains the backstop for a resume that never comes. Resume also no longer drops a local caller's kernel group set in the privileged re-check: group source now keys on broker-ness. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
+68
-9
@@ -393,6 +393,7 @@ static struct link_parked *park(link_connection_t *conn, const uint8_t *frame,
|
||||
bus->parked[i].tok = ++bus->next_tok;
|
||||
bus->parked[i].conn = conn;
|
||||
bus->parked[i].stamp = __now_ms();
|
||||
bus->parked[i].uid = (uid_t)-1;
|
||||
bus->parked[i].len = len;
|
||||
memcpy(bus->parked[i].buf, frame, len);
|
||||
*tok = bus->parked[i].tok;
|
||||
@@ -477,13 +478,20 @@ void __dispatch_forget_conn(link_connection_t *conn)
|
||||
|
||||
static int dispatch_call(link_connection_t *conn, const struct link_msg *m,
|
||||
const uint8_t *frame, size_t framelen,
|
||||
const uid_t *known_uid);
|
||||
const uid_t *known_uid, int resumed);
|
||||
|
||||
void link_uid_resolved(link_connection_t *conn, link_authz_t tok, uid_t uid)
|
||||
/*
|
||||
* Shared resume: copy the parked frame out, free the slot, re-enter
|
||||
* dispatch. `uid` NULL means "use what the slot recorded" (a
|
||||
* handler-parked call); non-NULL is a resolver's answer.
|
||||
*/
|
||||
static void resume_parked(link_connection_t *conn, link_authz_t tok,
|
||||
const uid_t *uid, int resumed)
|
||||
{
|
||||
uint8_t buf[LINK_PARKED_MSG_MAX];
|
||||
struct link_parked *p = NULL;
|
||||
struct link_msg msg;
|
||||
uid_t slot_uid;
|
||||
size_t len;
|
||||
int i;
|
||||
|
||||
@@ -501,15 +509,58 @@ void link_uid_resolved(link_connection_t *conn, link_authz_t tok, uid_t uid)
|
||||
|
||||
/* Copy the message out and free the slot before dispatching:
|
||||
* the handler may park a call of its own. */
|
||||
len = p->len;
|
||||
len = p->len;
|
||||
slot_uid = p->uid;
|
||||
memcpy(buf, p->buf, len);
|
||||
unpark(p);
|
||||
|
||||
if (__msg_parse(buf, len, &msg) <= 0)
|
||||
return;
|
||||
|
||||
__dbg("resumed %s, caller uid %d", msg.member ? msg.member : "call", (int)uid);
|
||||
(void)dispatch_call(conn, &msg, NULL, 0, &uid);
|
||||
if (!uid)
|
||||
uid = &slot_uid;
|
||||
|
||||
__dbg("resumed %s, caller uid %d", msg.member ? msg.member : "call", (int)*uid);
|
||||
(void)dispatch_call(conn, &msg, NULL, 0, uid, resumed);
|
||||
}
|
||||
|
||||
void link_uid_resolved(link_connection_t *conn, link_authz_t tok, uid_t uid)
|
||||
{
|
||||
resume_parked(conn, tok, &uid, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Defer the reply: hold the request and re-run the handler later via
|
||||
* link_call_resume(), where link_call_resumed() reads true. A resumed
|
||||
* call cannot park again -- the raw frame is gone -- so the handler
|
||||
* must answer on the second run. link_connection_expire() is the
|
||||
* backstop for a resume that never comes.
|
||||
*/
|
||||
int link_call_park(link_call_t *call, link_authz_t *tok)
|
||||
{
|
||||
struct link_parked *p;
|
||||
|
||||
if (!call || !tok || call->resumed)
|
||||
return -1;
|
||||
|
||||
p = park(call->conn, call->frame, call->framelen, tok);
|
||||
if (!p)
|
||||
return -1;
|
||||
|
||||
p->uid = call->uid;
|
||||
call->parked = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void link_call_resume(link_connection_t *conn, link_authz_t tok)
|
||||
{
|
||||
resume_parked(conn, tok, NULL, 1);
|
||||
}
|
||||
|
||||
int link_call_resumed(const link_call_t *call)
|
||||
{
|
||||
return call ? call->resumed : 0;
|
||||
}
|
||||
|
||||
int __dispatch_message(link_connection_t *conn, const struct link_msg *m, size_t framelen)
|
||||
@@ -524,7 +575,7 @@ int __dispatch_message(link_connection_t *conn, const struct link_msg *m, size_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dispatch_call(conn, m, conn->rxbuf, framelen, NULL);
|
||||
return dispatch_call(conn, m, conn->rxbuf, framelen, NULL, 0);
|
||||
}
|
||||
|
||||
/* Who may invoke a privileged method. Without an authorizer, only
|
||||
@@ -582,7 +633,7 @@ static int resolve_caller(link_connection_t *conn, const struct link_msg *m,
|
||||
|
||||
static int dispatch_call(link_connection_t *conn, const struct link_msg *m,
|
||||
const uint8_t *frame, size_t framelen,
|
||||
const uid_t *known_uid)
|
||||
const uid_t *known_uid, int resumed)
|
||||
{
|
||||
struct link_object *o;
|
||||
struct link_vtable_entry *e = NULL;
|
||||
@@ -676,8 +727,8 @@ static int dispatch_call(link_connection_t *conn, const struct link_msg *m,
|
||||
* resolved over a broker carries only its uid, so the
|
||||
* authorizer sees no groups and falls back to root-only. */
|
||||
if (!caller_may(conn->server, call_uid,
|
||||
known_uid ? NULL : conn->peer_groups,
|
||||
known_uid ? 0 : conn->peer_ngroups)) {
|
||||
conn->broker ? NULL : conn->peer_groups,
|
||||
conn->broker ? 0 : conn->peer_ngroups)) {
|
||||
__dbg("denied %s, caller uid %d is not privileged",
|
||||
m->member, (int)call_uid);
|
||||
return __send_error(conn, m,
|
||||
@@ -690,10 +741,18 @@ static int dispatch_call(link_connection_t *conn, const struct link_msg *m,
|
||||
call.conn = conn;
|
||||
call.uid = call_uid;
|
||||
call.incoming = *m;
|
||||
call.frame = frame;
|
||||
call.framelen = framelen;
|
||||
call.resumed = resumed;
|
||||
__r_init(&call.read_cursor, m->body, m->body_avail);
|
||||
|
||||
rc = meth->handler(&call, e->userdata);
|
||||
|
||||
/* The handler parked the call: no reply yet, it is resumed via
|
||||
* link_call_resume() or expired by link_connection_expire(). */
|
||||
if (call.parked)
|
||||
return 0;
|
||||
|
||||
/* Every path returns the status of whatever it put on the wire,
|
||||
* so a failed send propagates out and the read loop drops the
|
||||
* peer rather than leaving a half-written frame on a live link.
|
||||
|
||||
+6
-1
@@ -40,7 +40,7 @@ typedef enum {
|
||||
* than a number per call site. */
|
||||
#define LINK_CALL_HDR_MAX 1024
|
||||
#define LINK_CALL_BODY_MAX 1024
|
||||
#define LINK_PARKED_CAP 4 /* inbound calls awaiting a uid */
|
||||
#define LINK_PARKED_CAP 4 /* inbound calls held for later */
|
||||
/* A call parked for authorization is a privileged one: an object path
|
||||
* and at most a service name. Finit's per-service paths alone run to
|
||||
* 512 bytes, so leave room for the header around one. Anything that
|
||||
@@ -74,6 +74,7 @@ struct link_parked {
|
||||
link_authz_t tok;
|
||||
link_connection_t *conn;
|
||||
uint64_t stamp;
|
||||
uid_t uid; /* caller, for handler-parked calls */
|
||||
size_t len;
|
||||
uint8_t buf[LINK_PARKED_MSG_MAX];
|
||||
};
|
||||
@@ -133,6 +134,10 @@ struct link_call {
|
||||
int reply_consumed;
|
||||
int error_sent;
|
||||
uid_t uid; /* caller, resolved for a broker peer */
|
||||
const uint8_t *frame; /* raw frame, for link_call_park() */
|
||||
size_t framelen;
|
||||
int parked; /* handler deferred its reply */
|
||||
int resumed; /* re-run via link_call_resume() */
|
||||
};
|
||||
|
||||
/* A parsed AddMatch rule. Fields are NULL when the rule omits the
|
||||
|
||||
@@ -155,6 +155,19 @@ void link_server_set_authorizer(link_server_t *server, link_authorizer_t cb,
|
||||
* asked about; a reply callback is handed it as its first argument. */
|
||||
void link_uid_resolved(link_connection_t *conn, link_authz_t tok, uid_t uid);
|
||||
|
||||
/* ---------- handler-deferred replies ---------- */
|
||||
|
||||
/* A handler that cannot answer yet parks the call and returns 0
|
||||
* without replying; the framework sends nothing. Resume re-runs the
|
||||
* handler with link_call_resumed() reading true, and this time it
|
||||
* must reply -- a resumed call cannot park again. Parked calls that
|
||||
* are never resumed are expired by link_connection_expire() with a
|
||||
* TimedOut error. Room is limited (four per connection); a failed
|
||||
* park means answer now. */
|
||||
int link_call_park (link_call_t *call, link_authz_t *tok);
|
||||
void link_call_resume (link_connection_t *conn, link_authz_t tok);
|
||||
int link_call_resumed(const link_call_t *call);
|
||||
|
||||
/* Called with the reply to an outbound link_connection_call(). `reply`
|
||||
* is NULL if the connection dropped before one arrived. */
|
||||
typedef void (*link_reply_cb_t)(link_connection_t *conn, const link_reply_t *reply,
|
||||
|
||||
+1
-1
@@ -21,8 +21,8 @@ ssize_t __w_finish(struct link_writer *w);
|
||||
|
||||
void __w_byte (struct link_writer *w, uint8_t v);
|
||||
void __w_bool (struct link_writer *w, int v);
|
||||
void __w_u64 (struct link_writer *w, uint64_t v);
|
||||
void __w_u32 (struct link_writer *w, uint32_t v);
|
||||
void __w_u64 (struct link_writer *w, uint64_t v);
|
||||
void __w_string (struct link_writer *w, const char *s); /* "s" */
|
||||
void __w_path (struct link_writer *w, const char *s); /* "o" */
|
||||
void __w_sig (struct link_writer *w, const char *s); /* "g" */
|
||||
|
||||
Reference in New Issue
Block a user