diff options
| author | Michael Biebl <biebl@debian.org> | 2019-04-21 21:09:51 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2019-04-21 21:09:51 +0200 |
| commit | 85563b7fc7ec2cd21e38debb9b28db342e2e8e7c (patch) | |
| tree | cce7b0b02d28fae2df9fdf2c1804cacd1500f2d7 /src/platform/nmp-rules-manager.c | |
| parent | 9a6dcbf895f9da01768e64b73cec88c16157d91e (diff) | |
New upstream version 1.18.0 upstream/1.18.0
Diffstat (limited to 'src/platform/nmp-rules-manager.c')
| -rw-r--r-- | src/platform/nmp-rules-manager.c | 737 |
1 files changed, 737 insertions, 0 deletions
diff --git a/src/platform/nmp-rules-manager.c b/src/platform/nmp-rules-manager.c new file mode 100644 index 00000000..970afcde --- /dev/null +++ b/src/platform/nmp-rules-manager.c @@ -0,0 +1,737 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#include "nm-default.h" + +#include "nmp-rules-manager.h" + +#include <linux/fib_rules.h> +#include <linux/rtnetlink.h> + +#include "nm-std-aux/c-list-util.h" +#include "nmp-object.h" + +/*****************************************************************************/ + +struct _NMPRulesManager { + NMPlatform *platform; + GHashTable *by_obj; + GHashTable *by_user_tag; + GHashTable *by_data; + guint ref_count; +}; + +/*****************************************************************************/ + +static void _rules_init (NMPRulesManager *self); + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_PLATFORM +#define _NMLOG_PREFIX_NAME "rules-manager" + +#define _NMLOG(level, ...) \ + G_STMT_START { \ + const NMLogLevel __level = (level); \ + \ + if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \ + _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \ + "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + _NMLOG_PREFIX_NAME \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } \ + } G_STMT_END + +/*****************************************************************************/ + +static gboolean +NMP_IS_RULES_MANAGER (gpointer self) +{ + return self + && ((NMPRulesManager *) self)->ref_count > 0 + && NM_IS_PLATFORM (((NMPRulesManager *) self)->platform); +} + +#define _USER_TAG_LOG(user_tag) nm_hash_obfuscate_ptr (1240261787u, (user_tag)) + +/*****************************************************************************/ + +typedef struct { + const NMPObject *obj; + gconstpointer user_tag; + CList obj_lst; + CList user_tag_lst; + + /* track_priority_val zero is special: those are weakly tracked rules. + * That means: NetworkManager will restore them only if it removed them earlier. + * But it will not remove or add them otherwise. + * + * Otherwise, the track_priority_val goes together with track_priority_present. + * In case of one rule being tracked multile times (with different priorities), + * the one with higher priority wins. See _rules_obj_get_best_data(). + * Then, the winning present state either enforces that the rule is present + * or absent. + * + * If a rules is not tracked at all, it is ignored by NetworkManager. Assuming + * that it was added externally by the user. But unlike weakly tracked rules, + * NM will *not* restore such rules if NetworkManager themself removed them. */ + guint32 track_priority_val; + bool track_priority_present:1; + + bool dirty:1; +} RulesData; + +typedef enum { + CONFIG_STATE_NONE = 0, + CONFIG_STATE_ADDED_BY_US = 1, + CONFIG_STATE_REMOVED_BY_US = 2, +} ConfigState; + +typedef struct { + const NMPObject *obj; + CList obj_lst_head; + + /* indicates whether we configured/removed the rule (during sync()). We need that, so + * if the rule gets untracked, that we know to remove/restore it. + * + * This makes NMPRulesManager stateful (beyond the configuration that indicates + * which rules are tracked). + * After a restart, NetworkManager would no longer remember which rules were added + * by us. That would need to be fixed by persisting the state and reloading it after + * restart. */ + ConfigState config_state; +} RulesObjData; + +typedef struct { + gconstpointer user_tag; + CList user_tag_lst_head; +} RulesUserTagData; + +static void +_rules_data_assert (const RulesData *rules_data, gboolean linked) +{ + nm_assert (rules_data); + nm_assert (NMP_OBJECT_GET_TYPE (rules_data->obj) == NMP_OBJECT_TYPE_ROUTING_RULE); + nm_assert (nmp_object_is_visible (rules_data->obj)); + nm_assert (rules_data->user_tag); + nm_assert (!linked || !c_list_is_empty (&rules_data->obj_lst)); + nm_assert (!linked || !c_list_is_empty (&rules_data->user_tag_lst)); +} + +static guint +_rules_data_hash (gconstpointer data) +{ + const RulesData *rules_data = data; + NMHashState h; + + _rules_data_assert (rules_data, FALSE); + + nm_hash_init (&h, 269297543u); + nm_platform_routing_rule_hash_update (NMP_OBJECT_CAST_ROUTING_RULE (rules_data->obj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, + &h); + nm_hash_update_val (&h, rules_data->user_tag); + return nm_hash_complete (&h); +} + +static gboolean +_rules_data_equal (gconstpointer data_a, gconstpointer data_b) +{ + const RulesData *rules_data_a = data_a; + const RulesData *rules_data_b = data_b; + + _rules_data_assert (rules_data_a, FALSE); + _rules_data_assert (rules_data_b, FALSE); + + return rules_data_a->user_tag == rules_data_b->user_tag + && (nm_platform_routing_rule_cmp (NMP_OBJECT_CAST_ROUTING_RULE (rules_data_a->obj), + NMP_OBJECT_CAST_ROUTING_RULE (rules_data_b->obj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); +} + +static void +_rules_data_destroy (gpointer data) +{ + RulesData *rules_data = data; + + _rules_data_assert (rules_data, FALSE); + + c_list_unlink_stale (&rules_data->obj_lst); + c_list_unlink_stale (&rules_data->user_tag_lst); + nmp_object_unref (rules_data->obj); + g_slice_free (RulesData, rules_data); +} + +static const RulesData * +_rules_obj_get_best_data (RulesObjData *obj_data) +{ + RulesData *rules_data; + const RulesData *rd_best = NULL; + + c_list_for_each_entry (rules_data, &obj_data->obj_lst_head, obj_lst) { + + _rules_data_assert (rules_data, TRUE); + + if (rd_best) { + if (rd_best->track_priority_val > rules_data->track_priority_val) + continue; + if (rd_best->track_priority_val == rules_data->track_priority_val) { + if ( rd_best->track_priority_present + || !rules_data->track_priority_present) { + /* if the priorities are identical, then "present" wins over + * "!present" (absent). */ + continue; + } + } + } + + rd_best = rules_data; + } + + return rd_best; +} + +static guint +_rules_obj_hash (gconstpointer data) +{ + const RulesObjData *obj_data = data; + NMHashState h; + + nm_hash_init (&h, 432817559u); + nm_platform_routing_rule_hash_update (NMP_OBJECT_CAST_ROUTING_RULE (obj_data->obj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID, + &h); + return nm_hash_complete (&h); +} + +static gboolean +_rules_obj_equal (gconstpointer data_a, gconstpointer data_b) +{ + const RulesObjData *obj_data_a = data_a; + const RulesObjData *obj_data_b = data_b; + + return (nm_platform_routing_rule_cmp (NMP_OBJECT_CAST_ROUTING_RULE (obj_data_a->obj), + NMP_OBJECT_CAST_ROUTING_RULE (obj_data_b->obj), + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_ID) == 0); +} + +static void +_rules_obj_destroy (gpointer data) +{ + RulesObjData *obj_data = data; + + c_list_unlink_stale (&obj_data->obj_lst_head); + nmp_object_unref (obj_data->obj); + g_slice_free (RulesObjData, obj_data); +} + +static guint +_rules_user_tag_hash (gconstpointer data) +{ + const RulesUserTagData *user_tag_data = data; + + return nm_hash_val (644693447u, user_tag_data->user_tag); +} + +static gboolean +_rules_user_tag_equal (gconstpointer data_a, gconstpointer data_b) +{ + const RulesUserTagData *user_tag_data_a = data_a; + const RulesUserTagData *user_tag_data_b = data_b; + + return user_tag_data_a->user_tag == user_tag_data_b->user_tag; +} + +static void +_rules_user_tag_destroy (gpointer data) +{ + RulesUserTagData *user_tag_data = data; + + c_list_unlink_stale (&user_tag_data->user_tag_lst_head); + g_slice_free (RulesUserTagData, user_tag_data); +} + +static RulesData * +_rules_data_lookup (GHashTable *by_data, + const NMPObject *obj, + gconstpointer user_tag) +{ + RulesData rules_data_needle = { + .obj = obj, + .user_tag = user_tag, + }; + + return g_hash_table_lookup (by_data, &rules_data_needle); +} + +void +nmp_rules_manager_track (NMPRulesManager *self, + const NMPlatformRoutingRule *routing_rule, + gint32 track_priority, + gconstpointer user_tag) +{ + NMPObject obj_stack; + const NMPObject *p_obj_stack; + RulesData *rules_data; + RulesObjData *obj_data; + RulesUserTagData *user_tag_data; + gboolean changed = FALSE; + guint32 track_priority_val; + gboolean track_priority_present; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + g_return_if_fail (routing_rule); + g_return_if_fail (user_tag); + nm_assert (track_priority != G_MININT32); + + _rules_init (self); + + p_obj_stack = nmp_object_stackinit (&obj_stack, NMP_OBJECT_TYPE_ROUTING_RULE, routing_rule); + + nm_assert (nmp_object_is_visible (p_obj_stack)); + + if (track_priority >= 0) { + track_priority_val = track_priority; + track_priority_present = TRUE; + } else { + track_priority_val = -track_priority; + track_priority_present = FALSE; + } + + rules_data = _rules_data_lookup (self->by_data, p_obj_stack, user_tag); + + if (!rules_data) { + rules_data = g_slice_new (RulesData); + *rules_data = (RulesData) { + .obj = nm_dedup_multi_index_obj_intern (nm_platform_get_multi_idx (self->platform), + p_obj_stack), + .user_tag = user_tag, + .track_priority_val = track_priority_val, + .track_priority_present = track_priority_present, + .dirty = FALSE, + }; + g_hash_table_add (self->by_data, rules_data); + + obj_data = g_hash_table_lookup (self->by_obj, &rules_data->obj); + if (!obj_data) { + obj_data = g_slice_new (RulesObjData); + *obj_data = (RulesObjData) { + .obj = nmp_object_ref (rules_data->obj), + .obj_lst_head = C_LIST_INIT (obj_data->obj_lst_head), + .config_state = CONFIG_STATE_NONE, + }; + g_hash_table_add (self->by_obj, obj_data); + } + c_list_link_tail (&obj_data->obj_lst_head, &rules_data->obj_lst); + + user_tag_data = g_hash_table_lookup (self->by_user_tag, &rules_data->user_tag); + if (!user_tag_data) { + user_tag_data = g_slice_new (RulesUserTagData); + *user_tag_data = (RulesUserTagData) { + .user_tag = user_tag, + .user_tag_lst_head = C_LIST_INIT (user_tag_data->user_tag_lst_head), + }; + g_hash_table_add (self->by_user_tag, user_tag_data); + } + c_list_link_tail (&user_tag_data->user_tag_lst_head, &rules_data->user_tag_lst); + changed = TRUE; + } else { + rules_data->dirty = FALSE; + if ( rules_data->track_priority_val != track_priority_val + || rules_data->track_priority_present != track_priority_present) { + rules_data->track_priority_val = track_priority_val; + rules_data->track_priority_present = track_priority_present; + changed = TRUE; + } + } + + _rules_data_assert (rules_data, TRUE); + + if (changed) { + _LOGD ("routing-rule: track ["NM_HASH_OBFUSCATE_PTR_FMT",%s%u] \"%s\")", + _USER_TAG_LOG (rules_data->user_tag), + ( rules_data->track_priority_val == 0 + ? "" + : ( rules_data->track_priority_present + ? "+" + : "-")), + (guint) rules_data->track_priority_val, + nmp_object_to_string (rules_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + } +} + +static void +_rules_data_untrack (NMPRulesManager *self, + RulesData *rules_data, + gboolean remove_user_tag_data) +{ + RulesObjData *obj_data; + + nm_assert (NMP_IS_RULES_MANAGER (self)); + _rules_data_assert (rules_data, TRUE); + nm_assert (self->by_data); + nm_assert (g_hash_table_lookup (self->by_data, rules_data) == rules_data); + + _LOGD ("routing-rule: untrack ["NM_HASH_OBFUSCATE_PTR_FMT"] \"%s\"", + _USER_TAG_LOG (rules_data->user_tag), + nmp_object_to_string (rules_data->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + +#if NM_MORE_ASSERTS + { + RulesUserTagData *user_tag_data; + + user_tag_data = g_hash_table_lookup (self->by_user_tag, &rules_data->user_tag); + nm_assert (user_tag_data); + nm_assert (c_list_contains (&user_tag_data->user_tag_lst_head, &rules_data->user_tag_lst)); + } +#endif + + nm_assert (!c_list_is_empty (&rules_data->user_tag_lst)); + if ( remove_user_tag_data + && c_list_length_is (&rules_data->user_tag_lst, 1)) + g_hash_table_remove (self->by_user_tag, &rules_data->user_tag); + + obj_data = g_hash_table_lookup (self->by_obj, &rules_data->obj); + nm_assert (obj_data); + nm_assert (c_list_contains (&obj_data->obj_lst_head, &rules_data->obj_lst)); + nm_assert (obj_data == g_hash_table_lookup (self->by_obj, &rules_data->obj)); + + /* if obj_data is marked to be "added_by_us" or "removed_by_us", we need to keep this entry + * around for the next sync -- so that we can undo what we did earlier. */ + if ( obj_data->config_state == CONFIG_STATE_NONE + && c_list_length_is (&rules_data->obj_lst, 1)) + g_hash_table_remove (self->by_obj, &rules_data->obj); + + g_hash_table_remove (self->by_data, rules_data); +} + +void +nmp_rules_manager_untrack (NMPRulesManager *self, + const NMPlatformRoutingRule *routing_rule, + gconstpointer user_tag) +{ + NMPObject obj_stack; + const NMPObject *p_obj_stack; + RulesData *rules_data; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + g_return_if_fail (routing_rule); + g_return_if_fail (user_tag); + + _rules_init (self); + + p_obj_stack = nmp_object_stackinit (&obj_stack, NMP_OBJECT_TYPE_ROUTING_RULE, routing_rule); + + nm_assert (nmp_object_is_visible (p_obj_stack)); + + rules_data = _rules_data_lookup (self->by_data, p_obj_stack, user_tag); + if (rules_data) + _rules_data_untrack (self, rules_data, TRUE); +} + +void +nmp_rules_manager_set_dirty (NMPRulesManager *self, + gconstpointer user_tag) +{ + RulesData *rules_data; + RulesUserTagData *user_tag_data; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + g_return_if_fail (user_tag); + + if (!self->by_data) + return; + + user_tag_data = g_hash_table_lookup (self->by_user_tag, &user_tag); + if (!user_tag_data) + return; + + c_list_for_each_entry (rules_data, &user_tag_data->user_tag_lst_head, user_tag_lst) + rules_data->dirty = TRUE; +} + +void +nmp_rules_manager_untrack_all (NMPRulesManager *self, + gconstpointer user_tag, + gboolean all /* or only dirty */) +{ + RulesData *rules_data; + RulesData *rules_data_safe; + RulesUserTagData *user_tag_data; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + g_return_if_fail (user_tag); + + if (!self->by_data) + return; + + user_tag_data = g_hash_table_lookup (self->by_user_tag, &user_tag); + if (!user_tag_data) + return; + + c_list_for_each_entry_safe (rules_data, rules_data_safe, &user_tag_data->user_tag_lst_head, user_tag_lst) { + if ( all + || rules_data->dirty) + _rules_data_untrack (self, rules_data, FALSE); + } + if (c_list_is_empty (&user_tag_data->user_tag_lst_head)) + g_hash_table_remove (self->by_user_tag, user_tag_data); +} + +void +nmp_rules_manager_sync (NMPRulesManager *self, + gboolean keep_deleted_rules) +{ + const NMDedupMultiHeadEntry *pl_head_entry; + NMDedupMultiIter pl_iter; + const NMPObject *plobj; + gs_unref_ptrarray GPtrArray *rules_to_delete = NULL; + RulesObjData *obj_data; + GHashTableIter h_iter; + guint i; + const RulesData *rd_best; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + if (!self->by_data) + return; + + _LOGD ("sync%s", keep_deleted_rules ? " (don't remove any rules)" : ""); + + pl_head_entry = nm_platform_lookup_obj_type (self->platform, NMP_OBJECT_TYPE_ROUTING_RULE); + if (pl_head_entry) { + nmp_cache_iter_for_each (&pl_iter, pl_head_entry, &plobj) { + obj_data = g_hash_table_lookup (self->by_obj, &plobj); + + if (!obj_data) { + /* this rule is not tracked. It was externally added, hence we + * ignore it. */ + continue; + } + + rd_best = _rules_obj_get_best_data (obj_data); + if (rd_best) { + if (rd_best->track_priority_present) + continue; + if (rd_best->track_priority_val == 0) { + if (obj_data->config_state != CONFIG_STATE_ADDED_BY_US) + continue; + obj_data->config_state = CONFIG_STATE_NONE; + } + } + + if (keep_deleted_rules) { + _LOGD ("forget/leak rule added by us: %s", nmp_object_to_string (plobj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + continue; + } + + if (!rules_to_delete) + rules_to_delete = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + g_ptr_array_add (rules_to_delete, (gpointer) nmp_object_ref (plobj)); + + obj_data->config_state = CONFIG_STATE_REMOVED_BY_US; + } + } + + if (rules_to_delete) { + for (i = 0; i < rules_to_delete->len; i++) + nm_platform_object_delete (self->platform, rules_to_delete->pdata[i]); + } + + g_hash_table_iter_init (&h_iter, self->by_obj); + while (g_hash_table_iter_next (&h_iter, (gpointer *) &obj_data, NULL)) { + + rd_best = _rules_obj_get_best_data (obj_data); + + if (!rd_best) { + g_hash_table_iter_remove (&h_iter); + continue; + } + + if (!rd_best->track_priority_present) + continue; + if (rd_best->track_priority_val == 0) { + if (obj_data->config_state != CONFIG_STATE_REMOVED_BY_US) + continue; + obj_data->config_state = CONFIG_STATE_NONE; + } + + plobj = nm_platform_lookup_obj (self->platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj_data->obj); + if (plobj) + continue; + + obj_data->config_state = CONFIG_STATE_ADDED_BY_US; + nm_platform_routing_rule_add (self->platform, NMP_NLM_FLAG_ADD, NMP_OBJECT_CAST_ROUTING_RULE (obj_data->obj)); + } +} + +void +nmp_rules_manager_track_from_platform (NMPRulesManager *self, + NMPlatform *platform, + int addr_family, + gint32 tracking_priority, + gconstpointer user_tag) +{ + NMPLookup lookup; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *o; + + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + if (!platform) + platform = self->platform; + else + g_return_if_fail (NM_IS_PLATFORM (platform)); + + nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_ROUTING_RULE); + head_entry = nm_platform_lookup (platform, &lookup); + nmp_cache_iter_for_each (&iter, head_entry, &o) { + const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE (o); + + if ( addr_family != AF_UNSPEC + && rr->addr_family != addr_family) + continue; + + nmp_rules_manager_track (self, rr, tracking_priority, user_tag); + } +} + +/*****************************************************************************/ + +void +nmp_rules_manager_track_default (NMPRulesManager *self, + int addr_family, + gint32 track_priority, + gconstpointer user_tag) +{ + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6)); + + /* track the default rules. See also `man ip-rule`. */ + + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) { + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET, + .priority = 0, + .table = RT_TABLE_LOCAL, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag); + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET, + .priority = 32766, + .table = RT_TABLE_MAIN, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag); + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET, + .priority = 32767, + .table = RT_TABLE_DEFAULT, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag); + } + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) { + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET6, + .priority = 0, + .table = RT_TABLE_LOCAL, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag); + nmp_rules_manager_track (self, + &((NMPlatformRoutingRule) { + .addr_family = AF_INET6, + .priority = 32766, + .table = RT_TABLE_MAIN, + .action = FR_ACT_TO_TBL, + .protocol = RTPROT_KERNEL, + }), + track_priority, + user_tag); + } +} + +static void +_rules_init (NMPRulesManager *self) +{ + if (self->by_data) + return; + + self->by_data = g_hash_table_new_full (_rules_data_hash, _rules_data_equal, NULL, _rules_data_destroy); + self->by_obj = g_hash_table_new_full (_rules_obj_hash, _rules_obj_equal, NULL, _rules_obj_destroy); + self->by_user_tag = g_hash_table_new_full (_rules_user_tag_hash, _rules_user_tag_equal, NULL, _rules_user_tag_destroy); +} + +/*****************************************************************************/ + +NMPRulesManager * +nmp_rules_manager_new (NMPlatform *platform) +{ + NMPRulesManager *self; + + g_return_val_if_fail (NM_IS_PLATFORM (platform), NULL); + + self = g_slice_new (NMPRulesManager); + *self = (NMPRulesManager) { + .ref_count = 1, + .platform = g_object_ref (platform), + }; + return self; +} + +void +nmp_rules_manager_ref (NMPRulesManager *self) +{ + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + self->ref_count++; +} + +void nmp_rules_manager_unref (NMPRulesManager *self) +{ + g_return_if_fail (NMP_IS_RULES_MANAGER (self)); + + if (--self->ref_count > 0) + return; + + if (self->by_data) { + g_hash_table_destroy (self->by_user_tag); + g_hash_table_destroy (self->by_obj); + g_hash_table_destroy (self->by_data); + } + g_object_unref (self->platform); + g_slice_free (NMPRulesManager, self); +} |