connection: add support to down-on-poweroff

The new option at NMSettingConnection allow the user to specify if the
connection needs to be down when powering off the system. This is useful
for IP address removal prior powering off. In order to accomplish that,
we listen on "Shutdown" systemd DBus signal.

The option is set to FALSE by default, it can be specified globally on
configuration file or per profile.
This commit is contained in:
Fernando Fernandez Mancera
2024-01-31 15:38:58 +01:00
parent c8cf02e6b8
commit bd38a19832
15 changed files with 858 additions and 547 deletions

View File

@@ -860,6 +860,10 @@ ipv6.ip6-privacy=0
<varlistentry>
<term><varname>connection.autoconnect-slaves</varname></term>
</varlistentry>
<varlistentry>
<term><varname>connection.down-on-poweroff</varname></term>
<listitem><para>Whether the connection will be brought down before the system is powered off.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>connection.mud-url</varname></term>
<listitem><para>If unspecified, MUD URL defaults to <literal>"none"</literal>.</para></listitem>

View File

@@ -7216,14 +7216,18 @@ device_sleep_cb(NMDevice *device, GParamSpec *pspec, NMManager *self)
}
static void
_handle_device_takedown(NMManager *self, NMDevice *device, gboolean suspending)
_handle_device_takedown(NMManager *self,
NMDevice *device,
gboolean suspending,
gboolean is_shutdown)
{
nm_device_notify_sleeping(device);
if (nm_device_is_activating(device)
|| nm_device_get_state(device) == NM_DEVICE_STATE_ACTIVATED) {
_LOGD(LOGD_SUSPEND,
"sleep: wait disconnection of device %s",
"%s: wait disconnection of device %s",
is_shutdown ? "shutdown" : "sleep",
nm_device_get_ip_iface(device));
if (sleep_devices_add(self, device, suspending))
@@ -7272,7 +7276,7 @@ do_sleep_wake(NMManager *self, gboolean sleeping_changed)
continue;
}
_handle_device_takedown(self, device, suspending);
_handle_device_takedown(self, device, suspending, FALSE);
}
} else {
_LOGD(LOGD_SUSPEND, "sleep: %s...", waking_from_suspend ? "waking up" : "re-enabling");
@@ -7452,6 +7456,41 @@ sleeping_cb(NMPowerMonitor *monitor, gboolean is_about_to_suspend, gpointer user
_internal_sleep(self, is_about_to_suspend);
}
static void
shutdown_cb(NMPowerMonitor *monitor, gpointer user_data)
{
NMManager *self = user_data;
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE(self);
NMDevice *device;
_LOGT(LOGD_SUSPEND, "shutdown: received shutdown signal");
c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
NMSettingConnection *s_con;
gboolean take_down = FALSE;
s_con = nm_device_get_applied_setting(device, NM_META_SETTING_TYPE_CONNECTION);
if (!s_con)
continue;
if (nm_setting_connection_get_down_on_poweroff(s_con)
== NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_YES)
take_down = TRUE;
else if (nm_setting_connection_get_down_on_poweroff(s_con)
== NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT)
take_down = nm_config_data_get_connection_default_int64(
NM_CONFIG_GET_DATA,
NM_CON_DEFAULT("connection.down-on-poweroff"),
device,
NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_NO,
NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_YES,
NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_NO);
if (take_down)
_handle_device_takedown(self, device, FALSE, TRUE);
}
}
static void
_internal_enable(NMManager *self, gboolean enable)
{
@@ -8842,6 +8881,7 @@ nm_manager_init(NMManager *self)
/* sleep/wake handling */
priv->power_monitor = nm_power_monitor_new();
g_signal_connect(priv->power_monitor, NM_POWER_MONITOR_SLEEPING, G_CALLBACK(sleeping_cb), self);
g_signal_connect(priv->power_monitor, NM_POWER_MONITOR_SHUTDOWN, G_CALLBACK(shutdown_cb), self);
/* Listen for authorization changes */
priv->auth_mgr = g_object_ref(nm_auth_manager_get());

View File

@@ -47,6 +47,7 @@
enum {
SLEEPING,
SHUTDOWN,
LAST_SIGNAL,
};
@@ -66,6 +67,7 @@ struct _NMPowerMonitor {
gulong sig_id_1;
gulong sig_id_2;
gulong sig_id_3;
};
struct _NMPowerMonitorClass {
@@ -83,6 +85,8 @@ G_DEFINE_TYPE(NMPowerMonitor, nm_power_monitor, G_TYPE_OBJECT);
static void sleep_signal(NMPowerMonitor *self, gboolean is_about_to_suspend);
static void shutdown_signal(NMPowerMonitor *self);
/*****************************************************************************/
static void
@@ -165,6 +169,12 @@ prepare_for_sleep_cb(GDBusProxy *proxy, gboolean is_about_to_suspend, gpointer d
sleep_signal(data, is_about_to_suspend);
}
static void
prepare_for_shutdown_cb(GDBusProxy *proxy, gpointer data)
{
shutdown_signal(data);
}
static void
name_owner_cb(GObject *object, GParamSpec *pspec, gpointer user_data)
{
@@ -198,6 +208,16 @@ sleep_signal(NMPowerMonitor *self, gboolean is_about_to_suspend)
drop_inhibitor(self, FALSE);
}
static void
shutdown_signal(NMPowerMonitor *self)
{
g_return_if_fail(NM_IS_POWER_MONITOR(self));
_LOGD("received SHUTDOWN signal");
g_signal_emit(self, signals[SHUTDOWN], 0);
}
/**
* nm_power_monitor_inhibit_take:
* @self: the #NMPowerMonitor instance
@@ -268,6 +288,12 @@ on_proxy_acquired(GObject *object, GAsyncResult *res, NMPowerMonitor *self)
G_VARIANT_TYPE("(b)"),
G_CALLBACK(prepare_for_sleep_cb),
self);
self->sig_id_3 = _nm_dbus_proxy_signal_connect(self->proxy,
"PrepareForShutdown",
G_VARIANT_TYPE("(b)"),
G_CALLBACK(prepare_for_shutdown_cb),
self);
{
gs_free char *owner = NULL;
@@ -314,6 +340,7 @@ dispose(GObject *object)
if (self->proxy) {
nm_clear_g_signal_handler(self->proxy, &self->sig_id_1);
nm_clear_g_signal_handler(self->proxy, &self->sig_id_2);
nm_clear_g_signal_handler(self->proxy, &self->sig_id_3);
g_clear_object(&self->proxy);
}
@@ -339,4 +366,14 @@ nm_power_monitor_class_init(NMPowerMonitorClass *klass)
G_TYPE_NONE,
1,
G_TYPE_BOOLEAN);
signals[SHUTDOWN] = g_signal_new(NM_POWER_MONITOR_SHUTDOWN,
NM_TYPE_POWER_MONITOR,
G_SIGNAL_RUN_LAST,
0,
NULL,
NULL,
g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE,
1,
G_TYPE_BOOLEAN);
}

View File

@@ -18,6 +18,7 @@
#define NM_IS_POWER_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), NM_TYPE_POWER_MONITOR))
#define NM_POWER_MONITOR_SLEEPING "sleeping"
#define NM_POWER_MONITOR_SHUTDOWN "shutdown"
typedef struct _NMPowerMonitorClass NMPowerMonitorClass;

View File

@@ -1958,6 +1958,7 @@ global:
nm_ethtool_optname_is_eee;
nm_setting_connection_get_autoconnect_ports;
nm_setting_connection_get_controller;
nm_setting_connection_get_down_on_poweroff;
nm_setting_connection_get_port_type;
nm_setting_generic_get_device_handler;
nm_setting_get_enum_property_type;
@@ -1977,3 +1978,9 @@ global:
nm_sriov_eswitch_inline_mode_get_type;
nm_sriov_eswitch_mode_get_type;
} libnm_1_44_0;
libnm_1_48_0 {
global:
nm_setting_connection_down_on_poweroff_get_type;
nm_setting_connection_get_down_on_poweroff;
} libnm_1_46_0;

View File

@@ -792,6 +792,10 @@
dbus-type="i"
gprop-type="gint"
/>
<property name="down-on-poweroff"
dbus-type="i"
gprop-type="gint"
/>
<property name="gateway-ping-timeout"
dbus-type="u"
gprop-type="guint"

View File

@@ -73,7 +73,8 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingConnection,
PROP_AUTH_RETRIES,
PROP_WAIT_DEVICE_TIMEOUT,
PROP_MUD_URL,
PROP_WAIT_ACTIVATION_DELAY, );
PROP_WAIT_ACTIVATION_DELAY,
PROP_DOWN_ON_POWEROFF, );
typedef struct {
GArray *permissions;
@@ -89,6 +90,7 @@ typedef struct {
char *mud_url;
guint64 timestamp;
int autoconnect_ports;
int down_on_poweroff;
int metered;
gint32 autoconnect_priority;
gint32 autoconnect_retries;
@@ -828,6 +830,26 @@ nm_setting_connection_get_wait_activation_delay(NMSettingConnection *setting)
return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->wait_activation_delay;
}
/**
* nm_setting_connection_get_down_on_poweroff:
* @setting: the #NMSettingConnection
*
* Returns the %NM_SETTING_CONNECTION_DOWN_ON_POWEROFF property.
*
* Returns: whether the connection will be brought down before the system
* is powered off.
*
* Since: 1.48
*/
NMSettingConnectionDownOnPoweroff
nm_setting_connection_get_down_on_poweroff(NMSettingConnection *setting)
{
g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting),
NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT);
return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->down_on_poweroff;
}
/**
* nm_setting_connection_get_autoconnect_ports:
* @setting: the #NMSettingConnection
@@ -3158,6 +3180,29 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
NMSettingConnectionPrivate,
wait_activation_delay);
/**
* NMSettingConnection:down-on-poweroff:
*
*
* Whether the connection will be brought down before the system is powered
* off. The default value is %NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT. When
* the default value is specified, then the global value from
* NetworkManager configuration is looked up, if not set, it is considered
* as %NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_NO.
*
* Since: 1.48
**/
_nm_setting_property_define_direct_enum(properties_override,
obj_properties,
NM_SETTING_CONNECTION_DOWN_ON_POWEROFF,
PROP_DOWN_ON_POWEROFF,
NM_TYPE_SETTING_CONNECTION_DOWN_ON_POWEROFF,
NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT,
NM_SETTING_PARAM_NONE,
NULL,
NMSettingConnectionPrivate,
down_on_poweroff);
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
_nm_setting_class_commit(setting_class,

View File

@@ -3962,7 +3962,7 @@ typedef struct {
typedef struct {
const char *name;
DiffKey keys[32];
DiffKey keys[33];
} DiffSetting;
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
@@ -4036,6 +4036,7 @@ test_connection_diff_a_only(void)
{NM_SETTING_CONNECTION_MUD_URL, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY, NM_SETTING_DIFF_RESULT_IN_A},
{NM_SETTING_CONNECTION_DOWN_ON_POWEROFF, NM_SETTING_DIFF_RESULT_IN_A},
{NULL, NM_SETTING_DIFF_RESULT_UNKNOWN}}},
{NM_SETTING_WIRED_SETTING_NAME,
{

View File

@@ -64,6 +64,7 @@ G_BEGIN_DECLS
#define NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT "wait-device-timeout"
#define NM_SETTING_CONNECTION_MUD_URL "mud-url"
#define NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY "wait-activation-delay"
#define NM_SETTING_CONNECTION_DOWN_ON_POWEROFF "down-on-poweroff"
/* Types for property values */
/**
@@ -158,6 +159,23 @@ typedef enum {
NM_SETTING_CONNECTION_DNS_OVER_TLS_YES = 2,
} NMSettingConnectionDnsOverTls;
/**
* NMSettingConnectionDownOnPoweroff:
* @NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT: default value
* @NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_NO: disable down-on-poweroff
* @NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_YES: enable down-on-poweroff
*
* #NMSettingConnectionDownOnPoweroff indicates whether the connection will be
* brought down before the system is powered off.
*
* Since: 1.48
*/
typedef enum {
NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_DEFAULT = -1,
NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_NO = 0,
NM_SETTING_CONNECTION_DOWN_ON_POWEROFF_YES = 1,
} NMSettingConnectionDownOnPoweroff;
typedef struct _NMSettingConnectionClass NMSettingConnectionClass;
GType nm_setting_connection_get_type(void);
@@ -254,6 +272,10 @@ gint32 nm_setting_connection_get_wait_device_timeout(NMSettingConnection *settin
NM_AVAILABLE_IN_1_40
gint32 nm_setting_connection_get_wait_activation_delay(NMSettingConnection *setting);
NM_AVAILABLE_IN_1_48
NMSettingConnectionDownOnPoweroff
nm_setting_connection_get_down_on_poweroff(NMSettingConnection *setting);
NM_AVAILABLE_IN_1_26
const char *nm_setting_connection_get_mud_url(NMSettingConnection *setting);

View File

@@ -5663,6 +5663,9 @@ static const NMMetaPropertyInfo *const property_infos_CONNECTION[] = {
PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_AUTOCONNECT_PORTS,
.property_type = &_pt_gobject_enum,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_DOWN_ON_POWEROFF,
.property_type = &_pt_gobject_ternary,
),
PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_SECONDARIES,
.describe_message =
N_("Enter secondary connections that should be activated when this connection is\n"

View File

@@ -8,6 +8,7 @@
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES N_("Whether or not slaves of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for master connections. The properties \"autoconnect\", \"autoconnect-priority\" and \"autoconnect-retries\" are unrelated to this setting. The permitted values are: 0: leave slave connections untouched, 1: activate all the slave connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-slaves is read to determine the real value. If it is default as well, this fallbacks to 0.")
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_CONTROLLER N_("Interface name of the controller device or UUID of the controller connection.")
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_DNS_OVER_TLS N_("Whether DNSOverTls (dns-over-tls) is enabled for the connection. DNSOverTls is a technology which uses TLS to encrypt dns traffic. The permitted values are: \"yes\" (2) use DNSOverTls and disabled fallback, \"opportunistic\" (1) use DNSOverTls but allow fallback to unencrypted resolution, \"no\" (0) don't ever use DNSOverTls. If unspecified \"default\" depends on the plugin used. Systemd-resolved uses global setting. This feature requires a plugin which supports DNSOverTls. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved.")
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_DOWN_ON_POWEROFF N_("Whether the connection will be brought down before the system is powered off. The default value is \"default\" (-1). When the default value is specified, then the global value from NetworkManager configuration is looked up, if not set, it is considered as \"no\" (0).")
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT N_("If greater than zero, delay success of IP addressing until either the timeout is reached, or an IP gateway replies to a ping.")
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_ID N_("A human readable unique identifier for the connection, like \"Work Wi-Fi\" or \"T-Mobile 3G\".")
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_INTERFACE_NAME N_("The name of the network interface this connection is bound to. If not set, then the connection can be attached to any interface of the appropriate type (subject to restrictions imposed by other settings). For software devices this specifies the name of the created device. For connection types where interface names cannot easily be made persistent (e.g. mobile broadband or USB Ethernet), this property should not be used. Setting this property restricts the interfaces a connection can be used with, and if interface names change or are reordered the connection may be applied to the wrong interface.")

View File

@@ -678,6 +678,10 @@
nmcli-description="Whether or not ports of this connection should be automatically brought up when NetworkManager activates this connection. This only has a real effect for controller connections. The properties &quot;autoconnect&quot;, &quot;autoconnect-priority&quot; and &quot;autoconnect-retries&quot; are unrelated to this setting. The permitted values are: 0: leave port connections untouched, 1: activate all the port connections with this connection, -1: default. If -1 (default) is set, global connection.autoconnect-ports is read to determine the real value. If it is default as well, this fallbacks to 0."
format="choice (NMTernary)"
values="default (-1), false (0), true (1)" />
<property name="down-on-poweroff"
nmcli-description="Whether the connection will be brought down before the system is powered off. The default value is &quot;default&quot; (-1). When the default value is specified, then the global value from NetworkManager configuration is looked up, if not set, it is considered as &quot;no&quot; (0)."
format="ternary"
values="true/yes/on, false/no/off, default/unknown" />
<property name="secondaries"
nmcli-description="List of connection UUIDs that should be activated when the base connection itself is activated. Currently, only VPN connections are supported."
format="list of strings" />

View File

@@ -502,12 +502,12 @@ NAME UUID TYPE DEVICE
con-1 5fcfd6d7-1e63-3332-8826-a7eda103792d ethernet --
<<<
size: 1512
size: 1565
location: src/tests/client/test-client.py:test_002()/23
cmd: $NMCLI c s con-1
lang: C
returncode: 0
stdout: 1384 bytes
stdout: 1437 bytes
>>>
connection.id: con-1
connection.uuid: 5fcfd6d7-1e63-3332-8826-a7eda103792d
@@ -528,6 +528,7 @@ connection.slave-type: --
connection.port-type: --
connection.autoconnect-slaves: -1 (default)
connection.autoconnect-ports: -1 (default)
connection.down-on-poweroff: -1 (default)
connection.secondaries: --
connection.gateway-ping-timeout: 0
connection.metered: unknown
@@ -540,12 +541,12 @@ connection.wait-device-timeout: -1
connection.wait-activation-delay: -1
<<<
size: 1523
size: 1576
location: src/tests/client/test-client.py:test_002()/24
cmd: $NMCLI c s con-1
lang: pl_PL.UTF-8
returncode: 0
stdout: 1385 bytes
stdout: 1438 bytes
>>>
connection.id: con-1
connection.uuid: 5fcfd6d7-1e63-3332-8826-a7eda103792d
@@ -566,6 +567,7 @@ connection.slave-type: --
connection.port-type: --
connection.autoconnect-slaves: -1 (default)
connection.autoconnect-ports: -1 (default)
connection.down-on-poweroff: -1 (default)
connection.secondaries: --
connection.gateway-ping-timeout: 0
connection.metered: nieznane

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff