1
0
mirror of https://github.com/systemd/systemd synced 2025-09-28 16:24:45 +02:00

Compare commits

...

12 Commits

Author SHA1 Message Date
Yu Watanabe
3e405f28f4
Merge pull request #16228 from ddstreet/administrative_state
Add ActivationPolicy= [Link] parameter
2021-01-22 12:11:29 +09:00
Luca Boccassi
87970ee5bc
Merge pull request #18338 from poettering/print-table-with-bells-and-whistles
json/table printing unification work
2021-01-21 22:41:18 +00:00
Lennart Poettering
8d0d1a309e man: share a common explanation for --json= 2021-01-21 18:21:41 +01:00
Lennart Poettering
bb1a570018 busctl: show --help in pager
We do this for other larger --help texts, do so here too.
2021-01-21 18:14:37 +01:00
Lennart Poettering
17547fb5cb dissect: almost port systemd-dissect to table_print_with_pager()
This doesn't actually port systemd-dissect to table_print_with_pager()
but at least rearranges things so that similar behaviour is exposed. The
reason it's not ported over 1:1 is that systemd-dissect actually adjusts
the JSON output of the table with additional info, and doesn't print the
table 1:1 as JSON.
2021-01-21 18:14:37 +01:00
Lennart Poettering
16a36b5616 sysext: port to table_print_with_pager()
This adds --no-legend as side-effect.
2021-01-21 18:14:21 +01:00
Lennart Poettering
896e678bea repart: port to table_print_with_pager()
This adds support for showing a pager, turning of the legend, like in
the other tools.
2021-01-21 18:14:21 +01:00
Lennart Poettering
665ffc7fba busctl/homectl: port the obvious cases to table_print_with_pager() 2021-01-21 18:14:21 +01:00
Lennart Poettering
e676b4fc8b format-table: add new helper table_print_with_pager()
This adds a new function table_print_with_pager() which is a wrapper
around table_print_json()/table_print() but spawns a pager first, if
that's enabled, and optionally turns off the header line of the table.

This addresses the fact that many of our tools actually keep doing very
this very similar stuff, over and over again. Let's unify this in one
place.
2021-01-21 18:14:21 +01:00
Dan Streetman
a853652ae9 save link activation policy to state file and display in networkctl 2021-01-21 12:05:17 -05:00
Dan Streetman
2236d75df9 test: add ActivationPolicy= unit tests 2021-01-21 12:05:17 -05:00
Dan Streetman
61135582e0 network: add ActivationPolicy= configuration parameter
This parameter allows configuring the activation policy for an interface,
meaning how it manages the interface's administrative state (IFF_UP flag).
The policy can be configured to bring the interface either up or down when
the interface is (re)configured, to always force the interface either up or
down, or to never change the interface administrative state.

If the interface is bound with BindCarrier=, its administrative state is
controlled by the interface(s) it's bound to, and this parameter is forced
to 'bound'.

This changes the default behavior of how systemd-networkd sets the IFF_UP
flag; previously, it was set up (if not already up) every time the
link_joined() function was called. Now, with the default ActivationPolicy=
setting of 'up', it will only set the IFF_UP flag once, the first time
link_joined() is called, during an interface's configuration; and on
the first link_joined() call each time the interface is reconfigured.

Fixes: #3031
Fixes: #17437
2021-01-21 11:49:43 -05:00
28 changed files with 361 additions and 76 deletions

View File

@ -52,4 +52,13 @@
Before each file, the filename is printed as a comment.</para>
</listitem>
</varlistentry>
<varlistentry id='json'>
<term><option>--json=</option><replaceable>MODE</replaceable></term>
<listitem><para>Shows output formatted as JSON. Expects one of <literal>short</literal> (for the
shortest possible output without any redundant whitespace or line breaks), <literal>pretty</literal>
(for a pretty version of the same, with indentation and line breaks) or <literal>off</literal> (to turn
off JSON output, the default).</para></listitem>
</varlistentry>
</variablelist>

View File

@ -162,15 +162,6 @@
operation begins.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--json=</option><replaceable>MODE</replaceable></term>
<listitem><para>Shows output formatted as JSON. Expects one of <literal>short</literal> (for the
shortest possible output without any redundant whitespace or line breaks), <literal>pretty</literal>
(for a pretty version of the same, with indentation and line breaks) or <literal>off</literal> (to turn
off json output).</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
@ -237,6 +228,9 @@
url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink>.</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
<xi:include href="standard-options.xml" xpointer="json" />
</variablelist>
</refsect1>

View File

@ -278,15 +278,6 @@
and graphic illustrating the changes applied.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--json=</option><replaceable>MODE</replaceable></term>
<listitem><para>Shows output formatted as JSON. Expects one of <literal>short</literal> (for the
shortest possible output without any redundant whitespace or line breaks), <literal>pretty</literal>
(for a pretty version of the same, with indentation and line breaks) or <literal>off</literal> (to turn
off json output).</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--definitions=</option></term>
@ -321,6 +312,9 @@
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
<xi:include href="standard-options.xml" xpointer="json" />
</variablelist>
</refsect1>

View File

@ -211,14 +211,6 @@
<filename>/opt/</filename> hierarchies, but below some specified root directory.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--json=</option></term>
<listitem><para>Generate JSON output, instead of human readable tabular output. Takes one of
<literal>short</literal>, <literal>pretty</literal> or <literal>off</literal> in order to control the
output style, or explicitly disabling JSON output.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--force</option></term>
@ -229,6 +221,8 @@
</varlistentry>
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
<xi:include href="standard-options.xml" xpointer="json" />
</variablelist>
</refsect1>

View File

@ -249,6 +249,36 @@
if <literal>RequiredForOnline=no</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ActivationPolicy=</varname></term>
<listitem>
<para>Specifies the policy for <command>systemd-networkd</command> managing the link
administrative state. Specifically, this controls how <command>systemd-networkd</command>
changes the network device's <literal>IFF_UP</literal> flag, which is sometimes
controlled by system administrators by running e.g., <command>ip set dev eth0 up</command>
or <command>ip set dev eth0 down</command>, and can also be changed with
<command>networkctl up eth0</command> or <command>networkctl down eth0</command>.</para>
<para>Takes one of <literal>up</literal>, <literal>always-up</literal>,
<literal>manual</literal>, <literal>always-down</literal>, <literal>down</literal>,
or <literal>bound</literal>. When <literal>manual</literal>, <command>systemd-networkd</command>
will not change the link's admin state automatically; the system administrator must bring the
interface up or down manually, as desired. When <literal>up</literal> (the default) or
<literal>always-up</literal>, or <literal>down</literal> or <literal>always-down</literal>,
<command>systemd-networkd</command> will set the link up or down, respectively,
when the interface is (re)configured. When <literal>always-up</literal> or
<literal>always-down</literal>, <command>systemd-networkd</command> will set the link up
or down, respectively, any time <command>systemd-networkd</command> detects a change in
the administrative state. When <varname>BindCarrier=</varname> is also set, this is
automatically set to <literal>bound</literal> and any other value is ignored.</para>
<para>The administrative state is not the same as the carrier state, so using
<literal>always-up</literal> does not mean the link will never lose carrier. The link
carrier depends on both the administrative state as well as the network device's physical
connection. However, to avoid reconfiguration failures, when using <literal>always-up</literal>,
<varname>IgnoreCarrierLoss=</varname> is forced to true.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
@ -592,8 +622,9 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
<listitem>
<para>A link name or a list of link names. When set, controls the behavior of the current
link. When all links in the list are in an operational down state, the current link is brought
down. When at least one link has carrier, the current interface is brought up.
</para>
down. When at least one link has carrier, the current interface is brought up.</para>
<para>This forces <varname>ActivationPolicy=</varname> to be set to <literal>bound</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
@ -966,6 +997,10 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
of the interface even if its carrier is lost. When unset, the value specified with
<option>ConfigureWithoutCarrier=</option> is used.
</para>
<para>When <varname>ActivationPolicy=</varname> is set to <literal>always-up</literal>, this
is forced to <literal>true</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>

View File

@ -239,8 +239,6 @@ static int list_bus_names(int argc, char **argv, void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to set columns to display: %m");
table_set_header(table, arg_legend);
HASHMAP_FOREACH_KEY(v, k, names) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
@ -357,14 +355,7 @@ static int list_bus_names(int argc, char **argv, void *userdata) {
return log_error_errno(r, "Failed to fill line: %m");
}
if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
(void) pager_open(arg_pager_flags);
r = table_print_json(table, NULL, arg_json_format_flags);
if (r < 0)
return table_log_print_error(r);
return 0;
return table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
}
static void print_subtree(const char *prefix, const char *path, char **l) {
@ -2255,6 +2246,8 @@ static int help(void) {
if (r < 0)
return log_oom();
(void) pager_open(arg_pager_flags);
printf("%s [OPTIONS...] COMMAND ...\n\n"
"%sIntrospect the D-Bus IPC bus.%s\n"
"\nCommands:\n"

View File

@ -46,6 +46,8 @@ static const char *arg_target = NULL;
static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK;
static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done);
@ -63,6 +65,8 @@ static int help(void) {
"%1$s [OPTIONS...] --copy-to IMAGE [SOURCE] PATH\n\n"
"%5$sDissect a file system OS image.%6$s\n\n"
"%3$sOptions:%4$s\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not show the headers and footers\n"
" -r --read-only Mount read-only\n"
" --fsck=BOOL Run fsck before mounting\n"
" --mkdir Make mount directory before mounting, if missing\n"
@ -96,6 +100,8 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_NO_LEGEND,
ARG_DISCARD,
ARG_FSCK,
ARG_ROOT_HASH,
@ -108,6 +114,8 @@ static int parse_argv(int argc, char *argv[]) {
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "mount", no_argument, NULL, 'm' },
{ "read-only", no_argument, NULL, 'r' },
{ "discard", required_argument, NULL, ARG_DISCARD },
@ -137,6 +145,14 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_VERSION:
return version();
case ARG_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_LEGEND:
arg_legend = false;
break;
case 'm':
arg_action = ACTION_MOUNT;
break;
@ -339,6 +355,9 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
assert(m);
assert(d);
if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
(void) pager_open(arg_pager_flags);
if (arg_json_format_flags & JSON_FORMAT_OFF)
printf(" Name: %s\n", basename(arg_image));
@ -482,7 +501,9 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
}
if (arg_json_format_flags & JSON_FORMAT_OFF) {
r = table_print(t, stdout);
(void) table_set_header(t, arg_legend);
r = table_print(t, NULL);
if (r < 0)
return table_log_print_error(r);
} else {

View File

@ -113,8 +113,6 @@ static int list_homes(int argc, char *argv[], void *userdata) {
_cleanup_(table_unrefp) Table *table = NULL;
int r;
(void) pager_open(arg_pager_flags);
r = acquire_bus(&bus);
if (r < 0)
return r;
@ -175,11 +173,9 @@ static int list_homes(int argc, char *argv[], void *userdata) {
if (r < 0)
return table_log_sort_error(r);
table_set_header(table, arg_legend);
r = table_print_json(table, stdout, arg_json_format_flags);
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
if (r < 0)
return table_log_print_error(r);
return r;
}
if (arg_legend && (arg_json_format_flags & JSON_FORMAT_OFF)) {

View File

@ -212,6 +212,27 @@ _public_ int sd_network_link_get_required_operstate_for_online(int ifindex, char
return 0;
}
_public_ int sd_network_link_get_activation_policy(int ifindex, char **policy) {
_cleanup_free_ char *s = NULL;
int r;
assert_return(policy, -EINVAL);
r = network_link_get_string(ifindex, "ACTIVATION_POLICY", &s);
if (r < 0) {
if (r != -ENODATA)
return r;
/* For compatibility, assuming up. */
s = strdup("up");
if (!s)
return -ENOMEM;
}
*policy = TAKE_PTR(s);
return 0;
}
_public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) {
return network_link_get_string(ifindex, "LLMNR", llmnr);
}

View File

@ -1387,7 +1387,7 @@ static int link_status_one(
_cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **sip = NULL, **search_domains = NULL, **route_domains = NULL;
_cleanup_free_ char *t = NULL, *network = NULL, *iaid = NULL, *duid = NULL,
*setup_state = NULL, *operational_state = NULL, *lease_file = NULL;
*setup_state = NULL, *operational_state = NULL, *lease_file = NULL, *activation_policy = NULL;
const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL,
*on_color_operational, *off_color_operational, *on_color_setup, *off_color_setup;
_cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL;
@ -2062,6 +2062,16 @@ static int link_status_one(
if (r < 0)
return r;
r = sd_network_link_get_activation_policy(info->ifindex, &activation_policy);
if (r >= 0) {
r = table_add_many(table,
TABLE_EMPTY,
TABLE_STRING, "Activation Policy:",
TABLE_STRING, activation_policy);
if (r < 0)
return table_log_add_error(r);
}
if (lease) {
const void *client_id;
size_t client_id_len;

View File

@ -1831,17 +1831,38 @@ static int link_joined(Link *link) {
assert(link);
assert(link->network);
if (!hashmap_isempty(link->bound_to_links)) {
switch (link->network->activation_policy) {
case ACTIVATION_POLICY_BOUND:
r = link_handle_bound_to_list(link);
if (r < 0)
return r;
} else if (!(link->flags & IFF_UP)) {
break;
case ACTIVATION_POLICY_UP:
if (link->activated)
break;
_fallthrough_;
case ACTIVATION_POLICY_ALWAYS_UP:
r = link_up(link);
if (r < 0) {
link_enter_failed(link);
return r;
}
break;
case ACTIVATION_POLICY_DOWN:
if (link->activated)
break;
_fallthrough_;
case ACTIVATION_POLICY_ALWAYS_DOWN:
r = link_down(link, NULL);
if (r < 0) {
link_enter_failed(link);
return r;
}
break;
default:
break;
}
link->activated = true;
if (link->network->bridge) {
r = link_set_bridge(link);
@ -2254,6 +2275,7 @@ static int link_reconfigure_internal(Link *link, sd_netlink_message *m, bool for
return r;
link_set_state(link, LINK_STATE_INITIALIZED);
link->activated = false;
link_dirty(link);
/* link_configure_duid() returns 0 if it requests product UUID. In that case,
@ -2658,6 +2680,16 @@ int link_carrier_reset(Link *link) {
static int link_admin_state_up(Link *link) {
int r;
assert(link);
if (!link->network)
return 0;
if (link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_DOWN) {
log_link_info(link, "ActivationPolicy is \"always-off\", forcing link down");
return link_down(link, NULL);
}
/* We set the ipv6 mtu after the device mtu, but the kernel resets
* ipv6 mtu on NETDEV_UP, so we need to reset it. The check for
* ipv6_mtu_set prevents this from trying to set it too early before
@ -2672,6 +2704,21 @@ static int link_admin_state_up(Link *link) {
return 0;
}
static int link_admin_state_down(Link *link) {
assert(link);
if (!link->network)
return 0;
if (link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) {
log_link_info(link, "ActivationPolicy is \"always-on\", forcing link up");
return link_up(link);
}
return 0;
}
int link_update(Link *link, sd_netlink_message *m) {
_cleanup_strv_free_ char **s = NULL;
hw_addr_data hw_addr;
@ -2784,9 +2831,14 @@ int link_update(Link *link, sd_netlink_message *m) {
r = link_admin_state_up(link);
if (r < 0)
return r;
} else if (link_was_admin_up && !(link->flags & IFF_UP))
} else if (link_was_admin_up && !(link->flags & IFF_UP)) {
log_link_info(link, "Link DOWN");
r = link_admin_state_down(link);
if (r < 0)
return r;
}
r = link_update_lldp(link);
if (r < 0)
return r;
@ -2959,6 +3011,9 @@ int link_save(Link *link) {
st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "",
st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : "");
fprintf(f, "ACTIVATION_POLICY=%s\n",
activation_policy_to_string(link->network->activation_policy));
fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
/************************************************************/

View File

@ -129,6 +129,7 @@ typedef struct Link {
bool setting_genmode:1;
bool ipv6_mtu_set:1;
bool bridge_mdb_configured:1;
bool activated:1;
sd_dhcp_server *dhcp_server;

View File

@ -66,6 +66,7 @@ Link.Multicast, config_parse_tristate,
Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast)
Link.Promiscuous, config_parse_tristate, 0, offsetof(Network, promiscuous)
Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged)
Link.ActivationPolicy, config_parse_activation_policy, 0, offsetof(Network, activation_policy)
Link.RequiredForOnline, config_parse_required_for_online, 0, 0
SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, 0
SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, 0

View File

@ -225,9 +225,6 @@ int network_verify(Network *network) {
if (network->dhcp_use_gateway < 0)
network->dhcp_use_gateway = network->dhcp_use_routes;
if (network->ignore_carrier_loss < 0)
network->ignore_carrier_loss = network->configure_without_carrier;
if (network->dhcp_critical >= 0) {
if (network->keep_configuration >= 0)
log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. "
@ -239,6 +236,30 @@ int network_verify(Network *network) {
network->keep_configuration = KEEP_CONFIGURATION_NO;
}
if (!strv_isempty(network->bind_carrier)) {
if (!IN_SET(network->activation_policy, _ACTIVATION_POLICY_INVALID, ACTIVATION_POLICY_BOUND))
log_warning("%s: ActivationPolicy=bound is required with BindCarrier=. "
"Setting ActivationPolicy=bound.", network->filename);
network->activation_policy = ACTIVATION_POLICY_BOUND;
} else if (network->activation_policy == ACTIVATION_POLICY_BOUND) {
log_warning("%s: ActivationPolicy=bound requires BindCarrier=. "
"Ignoring ActivationPolicy=bound.", network->filename);
network->activation_policy = ACTIVATION_POLICY_UP;
}
if (network->activation_policy == _ACTIVATION_POLICY_INVALID)
network->activation_policy = ACTIVATION_POLICY_UP;
if (network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) {
if (network->ignore_carrier_loss == false)
log_warning("%s: IgnoreCarrierLoss=false conflicts with ActivationPolicy=always-up. "
"Setting IgnoreCarrierLoss=true.", network->filename);
network->ignore_carrier_loss = true;
}
if (network->ignore_carrier_loss < 0)
network->ignore_carrier_loss = network->configure_without_carrier;
if (network->keep_configuration < 0)
network->keep_configuration = KEEP_CONFIGURATION_NO;
@ -316,6 +337,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.required_for_online = true,
.required_operstate_for_online = LINK_OPERSTATE_RANGE_DEFAULT,
.activation_policy = _ACTIVATION_POLICY_INVALID,
.arp = -1,
.multicast = -1,
.allmulticast = -1,
@ -1247,3 +1269,15 @@ static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL
DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_link_local_address_gen_mode, ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode, "Failed to parse IPv6 link local address generation mode");
static const char* const activation_policy_table[_ACTIVATION_POLICY_MAX] = {
[ACTIVATION_POLICY_UP] = "up",
[ACTIVATION_POLICY_ALWAYS_UP] = "always-up",
[ACTIVATION_POLICY_MANUAL] = "manual",
[ACTIVATION_POLICY_ALWAYS_DOWN] = "always-down",
[ACTIVATION_POLICY_DOWN] = "down",
[ACTIVATION_POLICY_BOUND] = "bound",
};
DEFINE_STRING_TABLE_LOOKUP(activation_policy, ActivationPolicy);
DEFINE_CONFIG_PARSE_ENUM(config_parse_activation_policy, activation_policy, ActivationPolicy, "Failed to parse activation policy");

View File

@ -47,6 +47,17 @@ typedef enum IPv6LinkLocalAddressGenMode {
_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID = -1
} IPv6LinkLocalAddressGenMode;
typedef enum ActivationPolicy {
ACTIVATION_POLICY_UP,
ACTIVATION_POLICY_ALWAYS_UP,
ACTIVATION_POLICY_MANUAL,
ACTIVATION_POLICY_ALWAYS_DOWN,
ACTIVATION_POLICY_DOWN,
ACTIVATION_POLICY_BOUND,
_ACTIVATION_POLICY_MAX,
_ACTIVATION_POLICY_INVALID = -1
} ActivationPolicy;
typedef struct Manager Manager;
typedef struct NetworkDHCPServerEmitAddress {
@ -93,6 +104,7 @@ struct Network {
bool unmanaged;
bool required_for_online; /* Is this network required to be considered online? */
LinkOperationalStateRange required_operstate_for_online;
ActivationPolicy activation_policy;
/* misc settings */
bool configure_without_carrier;
@ -334,6 +346,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online);
CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_rx_tx_queues);
CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy);
const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
@ -342,3 +355,6 @@ KeepConfiguration keep_configuration_from_string(const char *s) _pure_;
const char* ipv6_link_local_address_gen_mode_to_string(IPv6LinkLocalAddressGenMode s) _const_;
IPv6LinkLocalAddressGenMode ipv6_link_local_address_gen_mode_from_string(const char *s) _pure_;
const char* activation_policy_to_string(ActivationPolicy i) _const_;
ActivationPolicy activation_policy_from_string(const char *s) _pure_;

View File

@ -107,6 +107,8 @@ static int arg_pretty = -1;
static uint64_t arg_size = UINT64_MAX;
static bool arg_size_auto = false;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
static void *arg_key = NULL;
static size_t arg_key_size = 0;
static char *arg_tpm2_device = NULL;
@ -1918,11 +1920,7 @@ static int context_dump_partitions(Context *context, const char *node) {
return table_log_add_error(r);
}
r = table_print_json(t, stdout, arg_json_format_flags);
if (r < 0)
return table_log_print_error(r);
return 0;
return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
}
static void context_bar_char_process_partition(
@ -3479,6 +3477,8 @@ static int help(void) {
"\n%sGrow and add partitions to partition table.%s\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not show the headers and footers\n"
" --dry-run=BOOL Whether to run dry-run operation\n"
" --empty=MODE One of refuse, allow, require, force, create; controls\n"
" how to handle empty disks lacking partition tables\n"
@ -3510,6 +3510,8 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_NO_LEGEND,
ARG_DRY_RUN,
ARG_EMPTY,
ARG_DISCARD,
@ -3529,6 +3531,8 @@ static int parse_argv(int argc, char *argv[]) {
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "dry-run", required_argument, NULL, ARG_DRY_RUN },
{ "empty", required_argument, NULL, ARG_EMPTY },
{ "discard", required_argument, NULL, ARG_DISCARD },
@ -3561,6 +3565,14 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_VERSION:
return version();
case ARG_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_LEGEND:
arg_legend = false;
break;
case ARG_DRY_RUN:
r = parse_boolean(optarg);
if (r < 0)

View File

@ -2550,3 +2550,30 @@ int table_print_json(Table *t, FILE *f, JsonFormatFlags flags) {
return fflush_and_check(f);
}
int table_print_with_pager(
Table *t,
JsonFormatFlags json_format_flags,
PagerFlags pager_flags,
bool show_header) {
bool saved_header;
int r;
assert(t);
/* A all-in-one solution for showing tables, and turning on a pager first. Also optionally suppresses
* the table header and logs about any error. */
if (json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
(void) pager_open(pager_flags);
saved_header = t->header;
t->header = show_header;
r = table_print_json(t, stdout, json_format_flags);
t->header = saved_header;
if (r < 0)
return table_log_print_error(r);
return 0;
}

View File

@ -7,6 +7,7 @@
#include "json.h"
#include "macro.h"
#include "pager.h"
typedef enum TableDataType {
TABLE_EMPTY,
@ -129,6 +130,8 @@ const void *table_get_at(Table *t, size_t row, size_t column);
int table_to_json(Table *t, JsonVariant **ret);
int table_print_json(Table *t, FILE *f, JsonFormatFlags json_flags);
int table_print_with_pager(Table *t, JsonFormatFlags json_format_flags, PagerFlags pager_flags, bool show_header);
#define table_log_add_error(r) \
log_error_errno(r, "Failed to add cell(s) to table: %m")

View File

@ -35,6 +35,7 @@ static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default */
static char *arg_root = NULL;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
static bool arg_force = false;
STATIC_DESTRUCTOR_REGISTER(arg_hierarchies, strv_freep);
@ -226,12 +227,9 @@ static int verb_status(int argc, char **argv, void *userdata) {
(void) table_set_sort(t, (size_t) 0, (size_t) -1);
if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
(void) pager_open(arg_pager_flags);
r = table_print_json(t, stdout, arg_json_format_flags);
r = table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
if (r < 0)
return table_log_add_error(r);
return r;
return ret;
}
@ -860,14 +858,7 @@ static int verb_list(int argc, char **argv, void *userdata) {
(void) table_set_sort(t, (size_t) 0, (size_t) -1);
if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO))
(void) pager_open(arg_pager_flags);
r = table_print_json(t, stdout, arg_json_format_flags);
if (r < 0)
return table_log_print_error(r);
return 0;
return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
}
static int verb_help(int argc, char **argv, void *userdata) {
@ -890,6 +881,7 @@ static int verb_help(int argc, char **argv, void *userdata) {
" --version Show package version\n"
"\n%3$sOptions:%4$s\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not show the headers and footers\n"
" --root=PATH Operate relative to root path\n"
" --json=pretty|short|off\n"
" Generate JSON output\n"
@ -909,6 +901,7 @@ static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_PAGER,
ARG_NO_LEGEND,
ARG_ROOT,
ARG_JSON,
ARG_FORCE,
@ -918,6 +911,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "root", required_argument, NULL, ARG_ROOT },
{ "json", required_argument, NULL, ARG_JSON },
{ "force", no_argument, NULL, ARG_FORCE },
@ -943,6 +937,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_pager_flags |= PAGER_DISABLE;
break;
case ARG_NO_LEGEND:
arg_legend = false;
break;
case ARG_ROOT:
r = parse_path_argument_and_warn(optarg, false, &arg_root);
if (r < 0)

View File

@ -103,6 +103,11 @@ int sd_network_link_get_address_state(int ifindex, char **state);
*/
int sd_network_link_get_required_for_online(int ifindex);
/* Get activation policy for ifindex.
* Possible values are as specified for ActivationPolicy=
*/
int sd_network_link_get_activation_policy(int ifindex, char **policy);
/* Get path to .network file applied to link */
int sd_network_link_get_network_file(int ifindex, char **filename);

View File

@ -30,6 +30,7 @@ Host=
MACAddress=
PermanentMACAddress=
[Link]
ActivationPolicy=
RequiredForOnline=
ARP=
AllMulticast=

View File

@ -0,0 +1,6 @@
[Match]
Name=test1
[Network]
Address=192.168.10.30/24
Gateway=192.168.10.1

View File

@ -0,0 +1,2 @@
[Link]
ActivationPolicy=always-down

View File

@ -0,0 +1,2 @@
[Link]
ActivationPolicy=always-up

View File

@ -0,0 +1,2 @@
[Link]
ActivationPolicy=down

View File

@ -0,0 +1,2 @@
[Link]
ActivationPolicy=manual

View File

@ -0,0 +1,2 @@
[Link]
ActivationPolicy=up

View File

@ -1755,6 +1755,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-address-peer-ipv4.network',
'25-address-preferred-lifetime-zero.network',
'25-address-static.network',
'25-activation-policy.network',
'25-bind-carrier.network',
'25-bond-active-backup-slave.netdev',
'25-fibrule-invert.network',
@ -2711,6 +2712,53 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
self.wait_operstate('test1', 'routable')
def _test_activation_policy(self, test):
self.setUp()
conffile = '25-activation-policy.network'
if test:
conffile = f'{conffile}.d/{test}.conf'
copy_unit_to_networkd_unit_path('11-dummy.netdev', conffile, dropins=False)
start_networkd()
always = test.startswith('always')
if test == 'manual':
initial_up = 'UP' in check_output('ip link show test1')
else:
initial_up = not test.endswith('down') # note: default is up
expect_up = initial_up
next_up = not expect_up
# if initial expected state is down, must wait for setup_state to reach configuring
# so systemd-networkd considers it 'activated'
setup_state = None if initial_up else 'configuring'
for iteration in range(4):
with self.subTest(iteration=iteration, expect_up=expect_up):
operstate = 'routable' if expect_up else 'off'
self.wait_operstate('test1', operstate, setup_state=setup_state, setup_timeout=20)
setup_state = None
if expect_up:
self.assertIn('UP', check_output('ip link show test1'))
self.assertIn('192.168.10.30/24', check_output('ip address show test1'))
self.assertIn('default via 192.168.10.1', check_output('ip route show'))
else:
self.assertIn('DOWN', check_output('ip link show test1'))
if next_up:
check_output('ip link set dev test1 up')
else:
check_output('ip link set dev test1 down')
expect_up = initial_up if always else next_up
next_up = not next_up
self.tearDown()
def test_activation_policy(self):
for test in ['up', 'always-up', 'manual', 'always-down', 'down', '']:
with self.subTest(test=test):
self._test_activation_policy(test)
def test_domain(self):
copy_unit_to_networkd_unit_path('12-dummy.netdev', '24-search-domain.network')
start_networkd()
@ -2985,6 +3033,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
self.assertRegex(data, r'OPER_STATE=routable')
self.assertRegex(data, r'REQUIRED_FOR_ONLINE=yes')
self.assertRegex(data, r'REQUIRED_OPER_STATE_FOR_ONLINE=routable')
self.assertRegex(data, r'ACTIVATION_POLICY=up')
self.assertRegex(data, r'NETWORK_FILE=/run/systemd/network/state-file-tests.network')
self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')
self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')