diff options
author | Alin Rauta <alin.rauta@intel.com> | 2015-03-18 05:06:19 -0700 |
---|---|---|
committer | Tom Gundersen <teg@jklm.no> | 2015-03-23 22:36:17 +0100 |
commit | ea342a99fd4bbdb25e690186b25a8f1c88ed61b3 (patch) | |
tree | 695cf8081d30ef1047922195cd1590cd39825cbc /src/libsystemd/sd-rtnl/sd-rtnl.c | |
parent | eaa5251d9167027275d8275862e23e0b7dc8866e (diff) |
sd-rtnl: handle empty multi-part message from the kernel
We strips out NLMSG_DONE piece from a multi-part message adding into the
receive queue only the messages containing actual data.
If we send a request to the kernel for getting the forwarding database table (just an example),
the response will be a multi-part message like below:
1. FDB entry 1;
2. FDB entry 2;
3. NLMSG_DONE;
We strip out "3. NLMSG_DONE;" part and places into the receive queue a pointer to
"1. FDB entry 1; 2. FDB entry 2".
But if the FDB table is empty, the respose from the kernel will look like below:
1. NLMSG_DONE;
We strip out "1. NLMSG_DONE;" part and since there is no actual data got, it continues
waiting until reaching timeout.
Therefore, a call to "sd_rtnl_call" to send and wait for a response from kernel will exit
with timeout which is interpreted as error in communication.
This patch puts the NLMSG_DONE message on the receive queue if it ends an empty multi-part
message. This situation is detected in sd_rtnl_call() and in the callback code and NULL is
returned to the caller instead.
[tomegun:
- added/reworded commit message
- extend the same support to sd_rtnl_call_async()
- drop debug logging from library, we only do this if something is really wrong, but an
empty multi-part message is perfectly normal
- modernize the code we touch whilst we are at it]
Diffstat (limited to 'src/libsystemd/sd-rtnl/sd-rtnl.c')
-rw-r--r-- | src/libsystemd/sd-rtnl/sd-rtnl.c | 43 |
1 files changed, 28 insertions, 15 deletions
diff --git a/src/libsystemd/sd-rtnl/sd-rtnl.c b/src/libsystemd/sd-rtnl/sd-rtnl.c index 50162c3ee9..40dea1252f 100644 --- a/src/libsystemd/sd-rtnl/sd-rtnl.c +++ b/src/libsystemd/sd-rtnl/sd-rtnl.c @@ -421,8 +421,9 @@ static int process_timeout(sd_rtnl *rtnl) { } static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) { - struct reply_callback *c; + _cleanup_free_ struct reply_callback *c = NULL; uint64_t serial; + uint16_t type; int r; assert(rtnl); @@ -436,12 +437,17 @@ static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) { if (c->timeout != 0) prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx); + r = sd_rtnl_message_get_type(m, &type); + if (r < 0) + return 0; + + if (type == NLMSG_DONE) + m = NULL; + r = c->callback(rtnl, m, c->userdata); if (r < 0) log_debug_errno(r, "sd-rtnl: callback failed: %m"); - free(c); - return 1; } @@ -702,7 +708,6 @@ int sd_rtnl_call(sd_rtnl *rtnl, sd_rtnl_message **ret) { usec_t timeout; uint32_t serial; - unsigned i = 0; int r; assert_return(rtnl, -EINVAL); @@ -717,36 +722,44 @@ int sd_rtnl_call(sd_rtnl *rtnl, for (;;) { usec_t left; + unsigned i; - while (i < rtnl->rqueue_size) { - sd_rtnl_message *incoming; + for (i = 0; i < rtnl->rqueue_size; i++) { uint32_t received_serial; - incoming = rtnl->rqueue[i]; - received_serial = rtnl_message_get_serial(incoming); + received_serial = rtnl_message_get_serial(rtnl->rqueue[i]); if (received_serial == serial) { + _cleanup_rtnl_message_unref_ sd_rtnl_message *incoming = NULL; + uint16_t type; + + incoming = rtnl->rqueue[i]; + /* found a match, remove from rqueue and return it */ memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1, sizeof(sd_rtnl_message*) * (rtnl->rqueue_size - i - 1)); rtnl->rqueue_size--; r = sd_rtnl_message_get_errno(incoming); - if (r < 0) { - sd_rtnl_message_unref(incoming); + if (r < 0) + return r; + + r = sd_rtnl_message_get_type(incoming, &type); + if (r < 0) return r; + + if (type == NLMSG_DONE) { + *ret = NULL; + return 0; } if (ret) { *ret = incoming; - } else - sd_rtnl_message_unref(incoming); + incoming = NULL; + } return 1; } - - /* Try to read more, right away */ - i ++; } r = socket_read_message(rtnl); |