X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibsystemd%2Fsd-rtnl%2Fsd-rtnl.c;h=8e709bb66dfb16c3a9c73a7bf47af178b1e9006a;hb=77768cbabc97d27535bd5329a11490f5c35a63f3;hp=a2760d056bbf2f29fb69a93ea1412a4133395ea4;hpb=6a0f1f6d5af7c7300d3db7a0ba2b068f8abd222b;p=elogind.git diff --git a/src/libsystemd/sd-rtnl/sd-rtnl.c b/src/libsystemd/sd-rtnl/sd-rtnl.c index a2760d056..8e709bb66 100644 --- a/src/libsystemd/sd-rtnl/sd-rtnl.c +++ b/src/libsystemd/sd-rtnl/sd-rtnl.c @@ -31,7 +31,7 @@ #include "rtnl-util.h" static int sd_rtnl_new(sd_rtnl **ret) { - sd_rtnl *rtnl; + _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL; assert_return(ret, -EINVAL); @@ -51,13 +51,12 @@ static int sd_rtnl_new(sd_rtnl **ret) { /* We guarantee that wqueue always has space for at least * one entry */ - rtnl->wqueue = new(sd_rtnl_message*, 1); - if (!rtnl->wqueue) { - free(rtnl); + if (!GREEDY_REALLOC(rtnl->wqueue, rtnl->wqueue_allocated, 1)) return -ENOMEM; - } *ret = rtnl; + rtnl = NULL; + return 0; } @@ -104,6 +103,9 @@ int sd_rtnl_open(sd_rtnl **ret, uint32_t groups) { } sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) { + assert_return(rtnl, NULL); + assert_return(!rtnl_pid_changed(rtnl), NULL); + if (rtnl) assert_se(REFCNT_INC(rtnl->n_ref) >= 2); @@ -111,83 +113,54 @@ sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) { } sd_rtnl *sd_rtnl_unref(sd_rtnl *rtnl) { - unsigned long refs; - if (!rtnl) return NULL; - /* - * If our ref-cnt is exactly the number of internally queued messages - * plus the ref-cnt to be dropped, then we know there's no external - * reference to us. Hence, we look through all queued messages and if - * they also have no external references, we're about to drop the last - * ref. Flush the queues so the REFCNT_DEC() below will drop to 0. - * We must be careful not to introduce inter-message references or this - * logic will fall apart.. - */ + assert_return(!rtnl_pid_changed(rtnl), NULL); - refs = rtnl->rqueue_size + rtnl->wqueue_size + 1; - - if (REFCNT_GET(rtnl->n_ref) <= refs) { + if (REFCNT_DEC(rtnl->n_ref) <= 0) { struct match_callback *f; - bool q = true; unsigned i; - for (i = 0; i < rtnl->rqueue_size; i++) { - if (REFCNT_GET(rtnl->rqueue[i]->n_ref) > 1) { - q = false; - break; - } else if (rtnl->rqueue[i]->rtnl != rtnl) - --refs; - } + for (i = 0; i < rtnl->rqueue_size; i++) + sd_rtnl_message_unref(rtnl->rqueue[i]); + free(rtnl->rqueue); - if (q) { - for (i = 0; i < rtnl->wqueue_size; i++) { - if (REFCNT_GET(rtnl->wqueue[i]->n_ref) > 1) { - q = false; - break; - } else if (rtnl->wqueue[i]->rtnl != rtnl) - --refs; - } - } + for (i = 0; i < rtnl->wqueue_size; i++) + sd_rtnl_message_unref(rtnl->wqueue[i]); + free(rtnl->wqueue); - if (q && REFCNT_GET(rtnl->n_ref) == refs) { - /* Drop our own ref early to avoid recursion from: - * sd_rtnl_message_unref() - * sd_rtnl_unref() - * These must enter sd_rtnl_unref() with a ref-cnt - * smaller than us. */ - REFCNT_DEC(rtnl->n_ref); + hashmap_free_free(rtnl->reply_callbacks); + prioq_free(rtnl->reply_callbacks_prioq); - for (i = 0; i < rtnl->rqueue_size; i++) - sd_rtnl_message_unref(rtnl->rqueue[i]); - free(rtnl->rqueue); + sd_event_source_unref(rtnl->io_event_source); + sd_event_source_unref(rtnl->time_event_source); + sd_event_source_unref(rtnl->exit_event_source); + sd_event_unref(rtnl->event); - for (i = 0; i < rtnl->wqueue_size; i++) - sd_rtnl_message_unref(rtnl->wqueue[i]); - free(rtnl->wqueue); - - assert_se(REFCNT_GET(rtnl->n_ref) == 0); + while ((f = rtnl->match_callbacks)) { + LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f); + free(f); + } - hashmap_free_free(rtnl->reply_callbacks); - prioq_free(rtnl->reply_callbacks_prioq); + safe_close(rtnl->fd); + free(rtnl); + } - while ((f = rtnl->match_callbacks)) { - LIST_REMOVE(match_callbacks, rtnl->match_callbacks, f); - free(f); - } + return NULL; +} - safe_close(rtnl->fd); - free(rtnl); +static void rtnl_seal_message(sd_rtnl *rtnl, sd_rtnl_message *m) { + assert(rtnl); + assert(!rtnl_pid_changed(rtnl)); + assert(m); + assert(m->hdr); - return NULL; - } - } + m->hdr->nlmsg_seq = rtnl->serial++; - assert_se(REFCNT_GET(rtnl->n_ref) > 0); - REFCNT_DEC(rtnl->n_ref); + rtnl_message_seal(m); - return NULL; + return; } int sd_rtnl_send(sd_rtnl *nl, @@ -198,10 +171,9 @@ int sd_rtnl_send(sd_rtnl *nl, assert_return(nl, -EINVAL); assert_return(!rtnl_pid_changed(nl), -ECHILD); assert_return(message, -EINVAL); + assert_return(!message->sealed, -EPERM); - r = rtnl_message_seal(nl, message); - if (r < 0) - return r; + rtnl_seal_message(nl, message); if (nl->wqueue_size <= 0) { /* send directly */ @@ -215,18 +187,14 @@ int sd_rtnl_send(sd_rtnl *nl, nl->wqueue_size = 1; } } else { - sd_rtnl_message **q; - /* append to queue */ if (nl->wqueue_size >= RTNL_WQUEUE_MAX) return -ENOBUFS; - q = realloc(nl->wqueue, sizeof(sd_rtnl_message*) * (nl->wqueue_size + 1)); - if (!q) + if (!GREEDY_REALLOC(nl->wqueue, nl->wqueue_allocated, nl->wqueue_size + 1)) return -ENOMEM; - nl->wqueue = q; - q[nl->wqueue_size ++] = sd_rtnl_message_ref(message); + nl->wqueue[nl->wqueue_size ++] = sd_rtnl_message_ref(message); } if (serial) @@ -254,10 +222,8 @@ static int dispatch_rqueue(sd_rtnl *rtnl, sd_rtnl_message **message) { /* Try to read a new message */ r = socket_read_message(rtnl, &z); - if (r < 0) + if (r <= 0) return r; - if (r == 0) - return 0; *message = z;