X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Flibsystemd%2Fsd-rtnl%2Fsd-rtnl.c;h=50162c3ee9267a175480573f484be605fdf920e0;hp=5778ea53c0eb20f76d0ad18ed7dc80f1813b0ca0;hb=f436aa11f72e69e7b19cb4d73c78716296e3e417;hpb=f0c4b1c3fd827b429ba36aa45fd39e0a023cbf2c diff --git a/src/libsystemd/sd-rtnl/sd-rtnl.c b/src/libsystemd/sd-rtnl/sd-rtnl.c index 5778ea53c..50162c3ee 100644 --- a/src/libsystemd/sd-rtnl/sd-rtnl.c +++ b/src/libsystemd/sd-rtnl/sd-rtnl.c @@ -61,6 +61,36 @@ static int sd_rtnl_new(sd_rtnl **ret) { sizeof(struct nlmsghdr), sizeof(uint8_t))) return -ENOMEM; + /* Change notification responses have sequence 0, so we must + * start our request sequence numbers at 1, or we may confuse our + * responses with notifications from the kernel */ + rtnl->serial = 1; + + *ret = rtnl; + rtnl = NULL; + + return 0; +} + +int sd_rtnl_new_from_netlink(sd_rtnl **ret, int fd) { + _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL; + socklen_t addrlen; + int r; + + assert_return(ret, -EINVAL); + + r = sd_rtnl_new(&rtnl); + if (r < 0) + return r; + + addrlen = sizeof(rtnl->sockaddr); + + r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen); + if (r < 0) + return -errno; + + rtnl->fd = fd; + *ret = rtnl; rtnl = NULL; @@ -121,12 +151,13 @@ static int rtnl_open_fd_ap(sd_rtnl **ret, int fd, unsigned n_groups, va_list ap) addrlen = sizeof(rtnl->sockaddr); r = bind(fd, &rtnl->sockaddr.sa, addrlen); - if (r < 0) + /* ignore EINVAL to allow opening an already bound socket */ + if (r < 0 && errno != EINVAL) return -errno; r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen); if (r < 0) - return r; + return -errno; rtnl->fd = fd; @@ -231,7 +262,9 @@ static void rtnl_seal_message(sd_rtnl *rtnl, sd_rtnl_message *m) { assert(m); assert(m->hdr); - m->hdr->nlmsg_seq = rtnl->serial++; + /* don't use seq == 0, as that is used for broadcasts, so we + would get confused by replies to such messages */ + m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++; rtnl_message_seal(m); @@ -379,9 +412,12 @@ static int process_timeout(sd_rtnl *rtnl) { hashmap_remove(rtnl->reply_callbacks, &c->serial); r = c->callback(rtnl, m, c->userdata); + if (r < 0) + log_debug_errno(r, "sd-rtnl: timedout callback failed: %m"); + free(c); - return r < 0 ? r : 1; + return 1; } static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) { @@ -392,9 +428,6 @@ static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) { assert(rtnl); assert(m); - if (sd_rtnl_message_is_broadcast(m)) - return 0; - serial = rtnl_message_get_serial(m); c = hashmap_remove(rtnl->reply_callbacks, &serial); if (!c) @@ -404,9 +437,12 @@ static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) { prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx); r = c->callback(rtnl, m, c->userdata); + if (r < 0) + log_debug_errno(r, "sd-rtnl: callback failed: %m"); + free(c); - return r; + return 1; } static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) { @@ -424,12 +460,16 @@ static int process_match(sd_rtnl *rtnl, sd_rtnl_message *m) { LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) { if (type == c->type) { r = c->callback(rtnl, m, c->userdata); - if (r != 0) - return r; + if (r != 0) { + if (r < 0) + log_debug_errno(r, "sd-rtnl: match callback failed: %m"); + + break; + } } } - return 0; + return 1; } static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) { @@ -452,13 +492,15 @@ static int process_running(sd_rtnl *rtnl, sd_rtnl_message **ret) { if (!m) goto null_message; - r = process_reply(rtnl, m); - if (r != 0) - goto null_message; - - r = process_match(rtnl, m); - if (r != 0) - goto null_message; + if (sd_rtnl_message_is_broadcast(m)) { + r = process_match(rtnl, m); + if (r != 0) + goto null_message; + } else { + r = process_reply(rtnl, m); + if (r != 0) + goto null_message; + } if (ret) { *ret = m; @@ -957,7 +999,7 @@ int sd_rtnl_add_match(sd_rtnl *rtnl, assert_return(!rtnl_pid_changed(rtnl), -ECHILD); assert_return(rtnl_message_type_is_link(type) || rtnl_message_type_is_addr(type) || - rtnl_message_type_is_route(type), -ENOTSUP); + rtnl_message_type_is_route(type), -EOPNOTSUPP); c = new0(struct match_callback, 1); if (!c)