diff --git a/libnm-core/nm-setting-tc-config.c b/libnm-core/nm-setting-tc-config.c index 08929f08b2f2b97aaf2e0123ab42f429c1cfd9bc..f42318d2b2857b47aa87b1e0db60f48b14adc393 100644 --- a/libnm-core/nm-setting-tc-config.c +++ b/libnm-core/nm-setting-tc-config.c @@ -276,6 +276,14 @@ nm_tc_action_new (const char *kind, { NMTCAction *action; + if (!kind || !*kind || strchr (kind, ' ') || strchr (kind, '\t')) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("'%s' is not a valid kind"), kind); + return NULL; + } + action = g_slice_new0 (NMTCAction); action->refcount = 1; diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index c1373126cd79ea99f88f189b3f144fdfc768d842..b9a50e11f68b7cb6231e33d36b84b55f5498f9ac 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -2314,6 +2314,154 @@ nm_utils_tc_qdisc_from_str (const char *str, GError **error) return qdisc; } +/*****************************************************************************/ + +static const NMVariantAttributeSpec * const tc_action_simple_attribute_spec[] = { + TC_ATTR_SPEC_PTR ("sdata", G_VARIANT_TYPE_BYTESTRING, FALSE, FALSE, 0 ), + NULL, +}; + +static const NMVariantAttributeSpec * const tc_action_attribute_spec[] = { + TC_ATTR_SPEC_PTR ("kind", G_VARIANT_TYPE_STRING, TRUE, FALSE, 'a' ), + TC_ATTR_SPEC_PTR ("", G_VARIANT_TYPE_STRING, TRUE, TRUE, 'a' ), + NULL, +}; + +static gboolean +_string_append_tc_action (GString *string, NMTCAction *action, GError **error) +{ + gs_unref_hashtable GHashTable *ht = NULL; + const char *kind = nm_tc_action_get_kind (action); + gs_strfreev char **attr_names = NULL; + gs_free char *str = NULL; + int i; + + ht = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL); + + g_string_append (string, kind); + + attr_names = nm_tc_action_get_attribute_names (action); + for (i = 0; attr_names[i]; i++) { + g_hash_table_insert (ht, attr_names[i], + nm_tc_action_get_attribute (action, attr_names[i])); + } + + if (i) { + str = nm_utils_format_variant_attributes (ht, ' ', ' '); + g_string_append_c (string, ' '); + g_string_append (string, str); + } + + return TRUE; +} + +/** + * nm_utils_tc_action_to_str: + * @action: the %NMTCAction + * @error: location of the error + * + * Turns the %NMTCAction into a tc style string representation of the queueing + * discipline. + * + * Returns: formatted string or %NULL + * + * Since: 1.12 + */ +char * +nm_utils_tc_action_to_str (NMTCAction *action, GError **error) +{ + GString *string; + + string = g_string_sized_new (60); + if (!_string_append_tc_action (string, action, error)) { + g_string_free (string, TRUE); + return NULL; + } + + return g_string_free (string, FALSE); +} + +/** + * nm_utils_tc_action_from_str: + * @str: the string representation of a action + * @error: location of the error + * + * Parces the tc style string action representation of the queueing + * discipline to a %NMTCAction instance. Supports a subset of the tc language. + * + * Returns: the %NMTCAction or %NULL + * + * Since: 1.12 + */ +NMTCAction * +nm_utils_tc_action_from_str (const char *str, GError **error) +{ + const char *kind = NULL; + const char *rest = NULL; + NMTCAction *action = NULL; + gs_unref_hashtable GHashTable *ht = NULL; + gs_unref_hashtable GHashTable *options = NULL; + GVariant *variant; + const NMVariantAttributeSpec * const *attrs; + + nm_assert (str); + nm_assert (!error || !*error); + + ht = nm_utils_parse_variant_attributes (str, + ' ', ' ', FALSE, + tc_action_attribute_spec, + error); + if (!ht) + return FALSE; + + variant = g_hash_table_lookup (ht, "kind"); + if (variant) { + kind = g_variant_get_string (variant, NULL); + } else { + g_set_error_literal (error, 1, 0, _("action name missing.")); + return NULL; + } + + kind = g_variant_get_string (variant, NULL); + if (strcmp (kind, "simple") == 0) + attrs = tc_action_simple_attribute_spec; + else + attrs = NULL; + + variant = g_hash_table_lookup (ht, ""); + if (variant) + rest = g_variant_get_string (variant, NULL); + + action = nm_tc_action_new (kind, error); + if (!action) + return NULL; + + if (rest) { + GHashTableIter iter; + gpointer key, value; + + if (!attrs) { + nm_tc_action_unref (action); + g_set_error (error, 1, 0, _("unsupported action option: '%s'."), rest); + return NULL; + } + + options = nm_utils_parse_variant_attributes (rest, + ' ', ' ', FALSE, + attrs, + error); + if (!options) { + nm_tc_action_unref (action); + return NULL; + } + + g_hash_table_iter_init (&iter, options); + while (g_hash_table_iter_next (&iter, &key, &value)) + nm_tc_action_set_attribute (action, key, g_variant_ref_sink (value)); + } + + return action; +} /*****************************************************************************/ diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h index 0d496d7f061731938304c852102da85df97ad8b2..10e81d668ef2f6186e93bc851cd3c1809e8ab89e 100644 --- a/libnm-core/nm-utils.h +++ b/libnm-core/nm-utils.h @@ -238,6 +238,11 @@ NMTCQdisc *nm_utils_tc_qdisc_from_str (const char *str, GError **error); NM_AVAILABLE_IN_1_12 char *nm_utils_tc_qdisc_to_str (NMTCQdisc *qdisc, GError **error); +NM_AVAILABLE_IN_1_12 +NMTCAction *nm_utils_tc_action_from_str (const char *str, GError **error); +NM_AVAILABLE_IN_1_12 +char *nm_utils_tc_action_to_str (NMTCAction *action, GError **error); + G_END_DECLS #endif /* __NM_UTILS_H__ */ diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 13b0ec8e677bbcd8d40807ca0054dbba7bbcdbdc..b3ae29e941e4338a4129b4e7a5308d96a3defe3d 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1280,6 +1280,8 @@ global: nm_team_link_watcher_new_nsna_ping; nm_team_link_watcher_ref; nm_team_link_watcher_unref; + nm_utils_tc_action_from_str; + nm_utils_tc_action_to_str; nm_utils_tc_qdisc_from_str; nm_utils_tc_qdisc_to_str; } libnm_1_10_0;