mirror of
https://github.com/troglobit/finit.git
synced 2026-11-08 16:11:10 +00: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:
4 files changed
+88
-11
No files matched your search
+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].tok = ++bus->next_tok;
|
||||||
bus->parked[i].conn = conn;
|
bus->parked[i].conn = conn;
|
||||||
bus->parked[i].stamp = __now_ms();
|
bus->parked[i].stamp = __now_ms();
|
||||||
|
bus->parked[i].uid = (uid_t)-1;
|
||||||
bus->parked[i].len = len;
|
bus->parked[i].len = len;
|
||||||
memcpy(bus->parked[i].buf, frame, len);
|
memcpy(bus->parked[i].buf, frame, len);
|
||||||
*tok = bus->parked[i].tok;
|
*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,
|
static int dispatch_call(link_connection_t *conn, const struct link_msg *m,
|
||||||
const uint8_t *frame, size_t framelen,
|
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];
|
uint8_t buf[LINK_PARKED_MSG_MAX];
|
||||||
struct link_parked *p = NULL;
|
struct link_parked *p = NULL;
|
||||||
struct link_msg msg;
|
struct link_msg msg;
|
||||||
|
uid_t slot_uid;
|
||||||
size_t len;
|
size_t len;
|
||||||
int i;
|
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:
|
/* Copy the message out and free the slot before dispatching:
|
||||||
* the handler may park a call of its own. */
|
* the handler may park a call of its own. */
|
||||||
len = p->len;
|
len = p->len;
|
||||||
|
slot_uid = p->uid;
|
||||||
memcpy(buf, p->buf, len);
|
memcpy(buf, p->buf, len);
|
||||||
unpark(p);
|
unpark(p);
|
||||||
|
|
||||||
if (__msg_parse(buf, len, &msg) <= 0)
|
if (__msg_parse(buf, len, &msg) <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
__dbg("resumed %s, caller uid %d", msg.member ? msg.member : "call", (int)uid);
|
if (!uid)
|
||||||
(void)dispatch_call(conn, &msg, NULL, 0, &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)
|
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 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
|
/* 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,
|
static int dispatch_call(link_connection_t *conn, const struct link_msg *m,
|
||||||
const uint8_t *frame, size_t framelen,
|
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_object *o;
|
||||||
struct link_vtable_entry *e = NULL;
|
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
|
* resolved over a broker carries only its uid, so the
|
||||||
* authorizer sees no groups and falls back to root-only. */
|
* authorizer sees no groups and falls back to root-only. */
|
||||||
if (!caller_may(conn->server, call_uid,
|
if (!caller_may(conn->server, call_uid,
|
||||||
known_uid ? NULL : conn->peer_groups,
|
conn->broker ? NULL : conn->peer_groups,
|
||||||
known_uid ? 0 : conn->peer_ngroups)) {
|
conn->broker ? 0 : conn->peer_ngroups)) {
|
||||||
__dbg("denied %s, caller uid %d is not privileged",
|
__dbg("denied %s, caller uid %d is not privileged",
|
||||||
m->member, (int)call_uid);
|
m->member, (int)call_uid);
|
||||||
return __send_error(conn, m,
|
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.conn = conn;
|
||||||
call.uid = call_uid;
|
call.uid = call_uid;
|
||||||
call.incoming = *m;
|
call.incoming = *m;
|
||||||
|
call.frame = frame;
|
||||||
|
call.framelen = framelen;
|
||||||
|
call.resumed = resumed;
|
||||||
__r_init(&call.read_cursor, m->body, m->body_avail);
|
__r_init(&call.read_cursor, m->body, m->body_avail);
|
||||||
|
|
||||||
rc = meth->handler(&call, e->userdata);
|
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,
|
/* Every path returns the status of whatever it put on the wire,
|
||||||
* so a failed send propagates out and the read loop drops the
|
* so a failed send propagates out and the read loop drops the
|
||||||
* peer rather than leaving a half-written frame on a live link.
|
* 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. */
|
* than a number per call site. */
|
||||||
#define LINK_CALL_HDR_MAX 1024
|
#define LINK_CALL_HDR_MAX 1024
|
||||||
#define LINK_CALL_BODY_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
|
/* 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
|
* 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
|
* 512 bytes, so leave room for the header around one. Anything that
|
||||||
@@ -74,6 +74,7 @@ struct link_parked {
|
|||||||
link_authz_t tok;
|
link_authz_t tok;
|
||||||
link_connection_t *conn;
|
link_connection_t *conn;
|
||||||
uint64_t stamp;
|
uint64_t stamp;
|
||||||
|
uid_t uid; /* caller, for handler-parked calls */
|
||||||
size_t len;
|
size_t len;
|
||||||
uint8_t buf[LINK_PARKED_MSG_MAX];
|
uint8_t buf[LINK_PARKED_MSG_MAX];
|
||||||
};
|
};
|
||||||
@@ -133,6 +134,10 @@ struct link_call {
|
|||||||
int reply_consumed;
|
int reply_consumed;
|
||||||
int error_sent;
|
int error_sent;
|
||||||
uid_t uid; /* caller, resolved for a broker peer */
|
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
|
/* 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. */
|
* 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);
|
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`
|
/* Called with the reply to an outbound link_connection_call(). `reply`
|
||||||
* is NULL if the connection dropped before one arrived. */
|
* is NULL if the connection dropped before one arrived. */
|
||||||
typedef void (*link_reply_cb_t)(link_connection_t *conn, const link_reply_t *reply,
|
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_byte (struct link_writer *w, uint8_t v);
|
||||||
void __w_bool (struct link_writer *w, int 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_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_string (struct link_writer *w, const char *s); /* "s" */
|
||||||
void __w_path (struct link_writer *w, const char *s); /* "o" */
|
void __w_path (struct link_writer *w, const char *s); /* "o" */
|
||||||
void __w_sig (struct link_writer *w, const char *s); /* "g" */
|
void __w_sig (struct link_writer *w, const char *s); /* "g" */
|
||||||
|
|||||||
Reference in new issue
Block a user