dispatcher: pass user setting properties in the environment

Properties in the "user" setting are a convenient way to associate any
kind of user-provided metadata to connections.

However, nmcli doesn't support the user setting at the moment and
adding this feature requires a significant effort. Without nmcli
support, dispatcher scripts can only access user properties by either
parsing connection files or by using D-Bus (with or without libnm and
GObject introspection). Since both these solutions are not very
convenient, provide an alternative way: pass the properties as
environment variables.
This commit is contained in:
Beniamino Galvani 2023-09-29 14:14:34 +02:00
parent 38acb7a57d
commit d7c311eb85
2 changed files with 57 additions and 0 deletions

View file

@ -308,6 +308,33 @@
In case of VPN, VPN_IP_IFACE is set, and IP4_*, IP6_* variables with VPN prefix are
exported too, like VPN_IP4_ADDRESS_0, VPN_IP4_NUM_ADDRESSES.
</para>
<para>
The content of the <literal>user</literal> setting for the connection
being activated is also passed via environment variables. Each key is
stored in a variable with name <literal>CONNECTION_USER_</literal>
concatenated with the encoding of the key name. The encoding works as
follows:
<itemizedlist>
<listitem>
<para>lowercase letters become uppercase</para>
</listitem>
<listitem>
<para>uppercase letters are prefixed with an underscore</para>
</listitem>
<listitem>
<para>numbers do not change</para>
</listitem>
<listitem>
<para>a dot is replaced with a double underscore</para>
</listitem>
<listitem>
<para>any other character is encoded with an underscore followed by
its 3-digit octal representation</para>
</listitem>
</itemizedlist>
For example, key <literal>test.foo-Bar2</literal> is stored in a variable named
<literal>CONNECTION_USER_TEST__FOO_055_BAR2</literal>.
</para>
<para>
Dispatcher scripts are run one at a time, but asynchronously from the main
NetworkManager process, and will be killed if they run for too long. If your script

View file

@ -540,6 +540,36 @@ nm_dispatcher_utils_construct_envp(const char *action,
_items_add_key0(items, NULL, "DEVICE_IP_IFACE", ip_iface);
}
{
gs_unref_variant GVariant *user_setting = NULL;
user_setting = g_variant_lookup_value(connection_dict,
NM_SETTING_USER_SETTING_NAME,
NM_VARIANT_TYPE_SETTING);
if (user_setting) {
gs_unref_variant GVariant *data = NULL;
nm_auto_free_gstring GString *string = NULL;
GVariantIter iter;
const char *key;
const char *val;
data =
g_variant_lookup_value(user_setting, NM_SETTING_USER_DATA, G_VARIANT_TYPE("a{ss}"));
if (data) {
g_variant_iter_init(&iter, data);
while (g_variant_iter_next(&iter, "{&s&s}", &key, &val)) {
if (key) {
if (!string)
string = g_string_sized_new(64);
g_string_assign(string, "CONNECTION_USER_");
nm_utils_env_var_encode_name(key, string);
_items_add_key0(items, NULL, string->str, val);
}
}
}
}
}
/* Device items aren't valid if the device isn't activated */
if (iface && dev_state == NM_DEVICE_STATE_ACTIVATED) {
construct_proxy_items(items, device_proxy_props, NULL);