Add a new --no-reconfigure flag to 'networkctl reload' that reloads .network and .netdev files from disk without reconfiguring any network interfaces. This may be useful to avoid reconfiguring multiple interfaces simultaneously when multiple .network files are updated, or when an updated .network file is applied to multiple interfaces. On the networkd side, manager_reload() gains a reconfigure_links parameter that gates the per-link reconfiguration loop. A new io.systemd.Network.Reload varlink method is added that exposes this as an optional reconfigureLinks boolean (defaults to true). Both plain 'networkctl reload' and '--no-reconfigure' now unconditionally call this method first. If an older networkd returns MethodNotFound, plain reload falls back to io.systemd.service.Reload for backward compatibility; '--no-reconfigure' fails with a clear error in that case.
163 lines
5.5 KiB
C
163 lines
5.5 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include "sd-netlink.h"
|
|
|
|
#include "bus-util.h"
|
|
#include "errno-util.h"
|
|
#include "fd-util.h"
|
|
#include "format-ifname.h"
|
|
#include "log.h"
|
|
#include "netlink-util.h"
|
|
#include "networkctl.h"
|
|
#include "networkctl-misc.h"
|
|
#include "networkctl-util.h"
|
|
#include "ordered-set.h"
|
|
#include "parse-util.h"
|
|
#include "polkit-agent.h"
|
|
#include "string-util.h"
|
|
#include "strv.h"
|
|
#include "varlink-util.h"
|
|
|
|
static int parse_interfaces(sd_netlink **rtnl, char *argv[], OrderedSet **ret) {
|
|
_cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
|
|
_cleanup_ordered_set_free_ OrderedSet *indexes = NULL;
|
|
int r;
|
|
|
|
assert(ret);
|
|
|
|
if (!rtnl)
|
|
rtnl = &our_rtnl;
|
|
|
|
STRV_FOREACH(s, strv_skip(argv, 1)) {
|
|
int index = rtnl_resolve_interface_or_warn(rtnl, *s);
|
|
if (index < 0)
|
|
return index;
|
|
assert(index > 0);
|
|
|
|
r = ordered_set_ensure_put(&indexes, /* ops= */ NULL, INT_TO_PTR(index));
|
|
if (r < 0)
|
|
return log_oom();
|
|
}
|
|
|
|
*ret = TAKE_PTR(indexes);
|
|
return 0;
|
|
}
|
|
|
|
int verb_link_delete(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
|
int r, ret = 0;
|
|
|
|
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
|
|
_cleanup_ordered_set_free_ OrderedSet *indexes = NULL;
|
|
r = parse_interfaces(&rtnl, argv, &indexes);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
void *p;
|
|
ORDERED_SET_FOREACH(p, indexes) {
|
|
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
|
|
int index = PTR_TO_INT(p);
|
|
|
|
r = sd_rtnl_message_new_link(rtnl, &req, RTM_DELLINK, index);
|
|
if (r < 0)
|
|
return rtnl_log_create_error(r);
|
|
|
|
r = sd_netlink_call(rtnl, req, /* timeout= */ 0, /* ret= */ NULL);
|
|
if (r < 0) {
|
|
RET_GATHER(ret, r);
|
|
log_error_errno(r, "Failed to delete interface %s: %m",
|
|
FORMAT_IFNAME_FULL(index, FORMAT_IFNAME_IFINDEX));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int verb_link_varlink_simple_method(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
|
int r, ret = 0;
|
|
|
|
typedef struct LinkVarlinkAction {
|
|
const char *verb;
|
|
const char *method;
|
|
} LinkVarlinkAction;
|
|
|
|
static const LinkVarlinkAction link_varlink_action_table[] = {
|
|
{ "up", "io.systemd.Network.Link.Up", },
|
|
{ "down", "io.systemd.Network.Link.Down", },
|
|
{ "renew", "io.systemd.Network.Link.Renew", },
|
|
{ "forcerenew", "io.systemd.Network.Link.ForceRenew", },
|
|
{ "reconfigure", "io.systemd.Network.Link.Reconfigure", },
|
|
};
|
|
|
|
/* Common implementation for 'simple' method calls that just take an ifindex, and nothing else. */
|
|
|
|
const LinkVarlinkAction *a = NULL;
|
|
FOREACH_ELEMENT(i, link_varlink_action_table)
|
|
if (streq(argv[0], i->verb)) {
|
|
a = i;
|
|
break;
|
|
}
|
|
assert(a);
|
|
|
|
_cleanup_ordered_set_free_ OrderedSet *indexes = NULL;
|
|
r = parse_interfaces(/* rtnl= */ NULL, argv, &indexes);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
_cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *vl = NULL;
|
|
r = varlink_connect_networkd(&vl);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
(void) polkit_agent_open_if_enabled(BUS_TRANSPORT_LOCAL, arg_ask_password);
|
|
|
|
void *p;
|
|
ORDERED_SET_FOREACH(p, indexes)
|
|
RET_GATHER(ret, varlink_callbo_and_log(
|
|
vl,
|
|
a->method,
|
|
/* reply= */ NULL,
|
|
SD_JSON_BUILD_PAIR_INTEGER("InterfaceIndex", PTR_TO_INT(p)),
|
|
SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", arg_ask_password)));
|
|
|
|
return ret;
|
|
}
|
|
|
|
int verb_reload(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
|
return reload_networkd(!arg_no_reconfigure);
|
|
}
|
|
|
|
int verb_persistent_storage(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
|
_cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *vl = NULL;
|
|
bool ready;
|
|
int r;
|
|
|
|
r = parse_boolean(argv[1]);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to parse argument: %s", argv[1]);
|
|
ready = r;
|
|
|
|
r = varlink_connect_networkd(&vl);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (ready) {
|
|
_cleanup_close_ int fd = -EBADF;
|
|
|
|
fd = open("/var/lib/systemd/network/", O_CLOEXEC | O_DIRECTORY);
|
|
if (fd < 0)
|
|
return log_error_errno(errno, "Failed to open %s: %m", "/var/lib/systemd/network/");
|
|
|
|
r = sd_varlink_push_fd(vl, fd);
|
|
if (r < 0)
|
|
return log_error_errno(r, "Failed to push file descriptor of /var/lib/systemd/network/ into varlink: %m");
|
|
|
|
TAKE_FD(fd);
|
|
}
|
|
|
|
return varlink_callbo_and_log(
|
|
vl,
|
|
"io.systemd.Network.SetPersistentStorage",
|
|
/* reply= */ NULL,
|
|
SD_JSON_BUILD_PAIR_BOOLEAN("Ready", ready));
|
|
}
|