/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2013 Red Hat, Inc. */ /** * SECTION:nmt-route-table * @short_description: An editable list of IP4 or IP6 routes * * #NmtRouteTable implements a list of #NmtRouteEntry, plus headers, * and buttons to add and remove entries. */ #include "nm-default.h" #include #include #include #include "nmt-route-table.h" #include "nmt-route-entry.h" #include "nmt-widget-list.h" G_DEFINE_TYPE(NmtRouteTable, nmt_route_table, NMT_TYPE_NEWT_GRID) #define NMT_ROUTE_TABLE_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_ROUTE_TABLE, NmtRouteTablePrivate)) typedef struct { int family; int ip_entry_width; int metric_entry_width; GPtrArray * routes; NmtNewtWidget *list; } NmtRouteTablePrivate; enum { PROP_0, PROP_FAMILY, PROP_ROUTES, LAST_PROP }; /** * nmt_route_table_new: * @family: the address family, eg %AF_INET * * Creates a new #NmtRouteTable * * Returns: a new #NmtRouteTable */ NmtNewtWidget * nmt_route_table_new(int family) { return g_object_new(NMT_TYPE_ROUTE_TABLE, "family", family, NULL); } static gboolean route_list_transform_to_route(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { NmtRouteTable * table = NMT_ROUTE_TABLE(g_binding_get_source(binding)); NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(table); int n = GPOINTER_TO_INT(user_data); NMIPRoute * route; if (n >= priv->routes->len) return FALSE; route = priv->routes->pdata[n]; g_value_set_boxed(target_value, route); return TRUE; } static gboolean route_list_transform_from_route(GBinding * binding, const GValue *source_value, GValue * target_value, gpointer user_data) { NmtRouteTable * table = NMT_ROUTE_TABLE(g_binding_get_source(binding)); NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(table); int n = GPOINTER_TO_INT(user_data); GPtrArray * routes; NMIPRoute * route; if (n >= priv->routes->len) return FALSE; route = priv->routes->pdata[n]; routes = priv->routes; priv->routes = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); if (route) nm_ip_route_unref(route); routes->pdata[n] = g_value_dup_boxed(source_value); g_value_take_boxed(target_value, routes); return TRUE; } static NmtNewtWidget * create_route_entry(NmtWidgetList *list, int num, gpointer table) { NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(table); NmtNewtWidget * entry; entry = nmt_route_entry_new(priv->family, priv->ip_entry_width, priv->metric_entry_width); g_object_bind_property_full(table, "routes", entry, "route", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, route_list_transform_to_route, route_list_transform_from_route, GINT_TO_POINTER(num), NULL); return entry; } static void add_route(NmtWidgetList *list, gpointer table) { NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(table); NMIPRoute * route; if (priv->family == AF_INET) route = nm_ip_route_new(AF_INET, "0.0.0.0", 32, NULL, -1, NULL); else route = nm_ip_route_new(AF_INET6, "::", 128, NULL, -1, NULL); g_ptr_array_add(priv->routes, route); nmt_widget_list_set_length(list, priv->routes->len); g_object_notify(table, "routes"); } static void remove_route(NmtWidgetList *list, int num, gpointer table) { NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(table); if (num >= priv->routes->len) return; g_ptr_array_remove_index(priv->routes, num); nmt_widget_list_set_length(list, priv->routes->len); g_object_notify(table, "routes"); } static void nmt_route_table_init(NmtRouteTable *table) { NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(table); NmtNewtWidget * header, *empty; NmtNewtWidget * dest_prefix_label, *next_hop_label, *metric_label; int dest_prefix_width, next_hop_width, metric_width; char * text; priv->routes = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); header = nmt_newt_grid_new(); text = g_strdup_printf("%s/%s", _("Destination"), _("Prefix")); dest_prefix_width = nmt_newt_text_width(text); dest_prefix_label = g_object_new(NMT_TYPE_NEWT_LABEL, "text", text, "style", NMT_NEWT_LABEL_PLAIN, NULL); g_free(text); nmt_newt_grid_add(NMT_NEWT_GRID(header), dest_prefix_label, 0, 0); text = _("Next Hop"); next_hop_label = g_object_new(NMT_TYPE_NEWT_LABEL, "text", text, "style", NMT_NEWT_LABEL_PLAIN, NULL); next_hop_width = nmt_newt_text_width(text); nmt_newt_grid_add(NMT_NEWT_GRID(header), next_hop_label, 1, 0); text = _("Metric"); metric_label = g_object_new(NMT_TYPE_NEWT_LABEL, "text", text, "style", NMT_NEWT_LABEL_PLAIN, NULL); metric_width = nmt_newt_text_width(text); nmt_newt_grid_add(NMT_NEWT_GRID(header), metric_label, 2, 0); priv->ip_entry_width = MAX(20, MAX(dest_prefix_width, next_hop_width)); priv->metric_entry_width = MAX(7, metric_width); nmt_newt_widget_set_padding(dest_prefix_label, 0, 0, priv->ip_entry_width - dest_prefix_width, 0); nmt_newt_widget_set_padding(next_hop_label, 2, 0, priv->ip_entry_width - next_hop_width, 0); nmt_newt_widget_set_padding(metric_label, 2, 0, priv->metric_entry_width - metric_width, 0); nmt_newt_grid_add(NMT_NEWT_GRID(table), header, 0, 0); empty = nmt_newt_label_new(_("No custom routes are defined.")); priv->list = nmt_widget_list_new(create_route_entry, table, NULL, empty); g_signal_connect(priv->list, "add-clicked", G_CALLBACK(add_route), table); g_signal_connect(priv->list, "remove-clicked", G_CALLBACK(remove_route), table); nmt_newt_grid_add(NMT_NEWT_GRID(table), priv->list, 0, 1); } static void nmt_route_table_finalize(GObject *object) { NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(object); g_ptr_array_unref(priv->routes); G_OBJECT_CLASS(nmt_route_table_parent_class)->finalize(object); } static void nmt_route_table_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(object); GPtrArray * array; int i; switch (prop_id) { case PROP_FAMILY: priv->family = g_value_get_int(value); break; case PROP_ROUTES: array = g_value_get_boxed(value); g_ptr_array_set_size(priv->routes, 0); for (i = 0; i < array->len; i++) { nm_ip_route_ref(array->pdata[i]); g_ptr_array_add(priv->routes, array->pdata[i]); } nmt_widget_list_set_length(NMT_WIDGET_LIST(priv->list), priv->routes->len); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void nmt_route_table_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE(object); switch (prop_id) { case PROP_FAMILY: g_value_set_int(value, priv->family); break; case PROP_ROUTES: g_value_set_boxed(value, priv->routes); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } static void nmt_route_table_class_init(NmtRouteTableClass *table_class) { GObjectClass *object_class = G_OBJECT_CLASS(table_class); g_type_class_add_private(table_class, sizeof(NmtRouteTablePrivate)); /* virtual methods */ object_class->set_property = nmt_route_table_set_property; object_class->get_property = nmt_route_table_get_property; object_class->finalize = nmt_route_table_finalize; /** * NmtRouteTable:family: * * The network address family of the routes, eg %AF_INET */ g_object_class_install_property( object_class, PROP_FAMILY, g_param_spec_int("family", "", "", -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * NmtRouteTable:routes: * * The array of routes, suitable for binding to #NMSettingIP4Config:routes * or #NMSettingIP6Config:routes. * * Element-type: NMIPRoute */ g_object_class_install_property(object_class, PROP_ROUTES, g_param_spec_boxed("routes", "", "", G_TYPE_PTR_ARRAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); }