/* * Copyright (C) 2005-2009 Alex Murray * 2013 Stefano Karapetsas * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include #include #include "sensors-applet.h" #include "sensors-applet-settings.h" /* #define SORT_DEBUG */ #ifdef SORT_DEBUG #include #endif /* these must be as the gsettings schema in file org.mate.sensors-applet.gschema.xml.in * gsettings gvariant type string */ #define GSGVTS "s" /* gsettings gvariant type string for the array */ #define GSGVTSA "as" gchar* sensors_applet_settings_get_unique_id (const gchar *interface, const gchar *id, const gchar *path) { gchar *unique_id = NULL; gchar *unique_id_hash = NULL; GChecksum *checksum = NULL; guint8 digest[16]; gsize digest_len = sizeof (digest); unique_id = g_strdup_printf ("%s/%s/%s", interface, id, path); checksum = g_checksum_new (G_CHECKSUM_MD5); g_checksum_update (checksum, (const guchar *) unique_id, strlen (unique_id)); g_checksum_get_digest (checksum, digest, &digest_len); g_assert (digest_len == 16); unique_id_hash = g_strdup (g_checksum_get_string (checksum)); g_checksum_free (checksum); g_free (unique_id); return unique_id_hash; } /* load sensors from gsettings with sorting */ gboolean sensors_applet_settings_load_sensors (SensorsApplet *sensors_applet) { /* everything gets stored except alarm timeout indexes, which we set to -1, and visible which we set to false for all parent nodes and true for all child nodes */ gchar *applet_path = NULL; /* not sure about pointer, it is unclear if it is freed by loop, probably yes */ GVariantIter *iter = NULL; gchar *gsuid = NULL; /* string variables, to help free up memory in loop */ gchar *current_path = NULL; gchar *current_id = NULL; gchar *current_label = NULL; gchar *current_interface = NULL; gchar *current_low_alarm_command = NULL; gchar *current_high_alarm_command = NULL; gchar *current_graph_color = NULL; /* get gsettings path for applet */ applet_path = mate_panel_applet_get_preferences_path (sensors_applet->applet); /* get sensors-list array from gsettings */ g_settings_get (sensors_applet->settings, "sensors-list", GSGVTSA, &iter); /* load sensor data into applet one by one * first get unique id for the sensor * then load sensor data from gsettings * then add sensor * gsuid is freed by g_variant_iter_loop() */ while (g_variant_iter_loop (iter, GSGVTS, &gsuid)) { /* load sensor data from gsettings individually * g_strdup_printf doesn't free args! * applet_path is freed at the end */ gchar *path = g_strdup_printf ("%s%s/", applet_path, gsuid); /*g_free (gsuid); // freed by loop */ /* make a schema which points to one sensor data */ GSettings *settings; settings = g_settings_new_with_path ("org.mate.sensors-applet.sensor", path); g_free (path); /* laod sensor data to temp variables * g_settings_get_string allocates memory! * sensors_applet_add_sensor() gtk_tree_store_set() copies strings, so free them */ sensors_applet_add_sensor(sensors_applet, current_path = g_settings_get_string (settings, PATH), current_id = g_settings_get_string (settings, ID), current_label = g_settings_get_string (settings, LABEL), current_interface = g_settings_get_string (settings, INTERFACE), g_settings_get_int (settings, SENSOR_TYPE), g_settings_get_boolean (settings, ENABLED), g_settings_get_double (settings, LOW_VALUE), g_settings_get_double (settings, HIGH_VALUE), g_settings_get_boolean (settings, ALARM_ENABLED), current_low_alarm_command = g_settings_get_string (settings, LOW_ALARM_COMMAND), current_high_alarm_command = g_settings_get_string (settings, HIGH_ALARM_COMMAND), g_settings_get_int (settings, ALARM_TIMEOUT), g_settings_get_double (settings, MULTIPLIER), g_settings_get_double (settings, OFFSET), g_settings_get_int (settings, ICON_TYPE), current_graph_color = g_settings_get_string (settings, GRAPH_COLOR)); g_free (current_path); g_free (current_id); g_free (current_label); g_free (current_interface); g_free (current_low_alarm_command); g_free (current_high_alarm_command); g_free (current_graph_color); g_object_unref (settings); } g_free (applet_path); return TRUE; } #ifdef SORT_DEBUG /* print a gtk_tree_store (for debug) */ static void sensors_applet_settings_print_sensors_tree (SensorsApplet *sensors_applet) { g_assert(sensors_applet); g_assert(sensors_applet->sensors); GtkTreeIter interfaces_iter; GtkTreeIter sensors_iter; gboolean not_end_of_interfaces = TRUE; gboolean not_end_of_sensors = TRUE; gint interfaces_counter = 0; gint sensors_counter = 0; gchar *interface_name = NULL; gchar *sensor_id = NULL; gchar *sensor_path = NULL; gchar *sensor_hash = NULL; /* iterate through the sensor tree * code from sensors-applet.c sensors_applet_add_sensor() * first go through the interfaces */ for (not_end_of_interfaces = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(sensors_applet->sensors), &interfaces_iter); not_end_of_interfaces; not_end_of_interfaces = gtk_tree_model_iter_next(GTK_TREE_MODEL(sensors_applet->sensors), &interfaces_iter), interfaces_counter++) { /* get interface name */ gtk_tree_model_get(GTK_TREE_MODEL(sensors_applet->sensors), &interfaces_iter, INTERFACE_COLUMN, &interface_name, -1); /* print interface name */ syslog(LOG_ERR, "#%d interface name: %s\n", interfaces_counter, interface_name); /* then go through the individual sensors under one interface */ for (not_end_of_sensors = gtk_tree_model_iter_children(GTK_TREE_MODEL(sensors_applet->sensors), &sensors_iter, &interfaces_iter); not_end_of_sensors; not_end_of_sensors = gtk_tree_model_iter_next(GTK_TREE_MODEL(sensors_applet->sensors), &sensors_iter), sensors_counter++) { gtk_tree_model_get(GTK_TREE_MODEL(sensors_applet->sensors), &sensors_iter, PATH_COLUMN, &sensor_path, ID_COLUMN, &sensor_id, -1); sensor_hash = sensors_applet_settings_get_unique_id (interface_name, sensor_id, sensor_path); /* print sensor data */ syslog(LOG_ERR, "\t#%d sensor id: %s\n", sensors_counter, sensor_id); syslog(LOG_ERR, "\t#%d sensor path: %s\n", sensors_counter, sensor_path); syslog(LOG_ERR, "\t#%d sensor hash: %s\n\n", sensors_counter, sensor_hash); g_free(sensor_id); g_free(sensor_path); g_free(sensor_hash); } g_free(interface_name); } } #endif /* compare two iters using their paths * kinda same as active_sensor_compare () */ static gint sensors_applet_settings_sort_sensors_iter_compare (SensorsApplet *sensors_applet, GtkTreeIter ti_a, GtkTreeIter ti_b) { GtkTreePath *tp_a; GtkTreePath *tp_b; gint ret_val; tp_a = gtk_tree_model_get_path (GTK_TREE_MODEL(sensors_applet->sensors), &ti_a); tp_b = gtk_tree_model_get_path (GTK_TREE_MODEL(sensors_applet->sensors), &ti_b); ret_val = gtk_tree_path_compare(tp_a, tp_b); gtk_tree_path_free(tp_a); gtk_tree_path_free(tp_b); return ret_val; } /* find two sensors in the sensors tree based on their hash and sort them * Return codes: * 0: something went very wrong... * 1: normal run, a and b are in place * 2: hash_b has already been removed from the system * 3: hash_a is not found */ static gint sensors_applet_settings_sort_sensors_sort (SensorsApplet *sensors_applet, const gchar *hash_a, const gchar *hash_b, gboolean a_is_first) { g_assert(sensors_applet); g_assert(sensors_applet->sensors); GtkTreeIter interfaces_iter; GtkTreeIter sensors_iter; gboolean not_end_of_interfaces = TRUE; gboolean not_end_of_sensors = TRUE; gchar *interface_name = NULL; gchar *sensor_id = NULL; gchar *sensor_path = NULL; gchar *sensor_hash = NULL; GtkTreeIter interface_iter_a; GtkTreeIter interface_iter_b; GtkTreeIter sensor_iter_a; GtkTreeIter sensor_iter_b; gboolean found_a = FALSE; gboolean found_b = FALSE; /* iterate through the sensor tree * code from sensors-applet.c sensors_applet_add_sensor() * first go through the interfaces */ for (not_end_of_interfaces = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(sensors_applet->sensors), &interfaces_iter); not_end_of_interfaces; not_end_of_interfaces = gtk_tree_model_iter_next(GTK_TREE_MODEL(sensors_applet->sensors), &interfaces_iter)) { /* get interface name */ gtk_tree_model_get(GTK_TREE_MODEL(sensors_applet->sensors), &interfaces_iter, INTERFACE_COLUMN, &interface_name, -1); /* then go through the individual sensors under one interface */ for (not_end_of_sensors = gtk_tree_model_iter_children(GTK_TREE_MODEL(sensors_applet->sensors), &sensors_iter, &interfaces_iter); not_end_of_sensors; not_end_of_sensors = gtk_tree_model_iter_next(GTK_TREE_MODEL(sensors_applet->sensors), &sensors_iter)) { gtk_tree_model_get(GTK_TREE_MODEL(sensors_applet->sensors), &sensors_iter, PATH_COLUMN, &sensor_path, ID_COLUMN, &sensor_id, -1); sensor_hash = sensors_applet_settings_get_unique_id (interface_name, sensor_id, sensor_path); /* save interface_name and iters for swap */ if (g_ascii_strcasecmp(sensor_hash, hash_a) == 0) { /* can copy by value, don't free */ interface_iter_a = interfaces_iter; sensor_iter_a = sensors_iter; found_a = TRUE; } if (g_ascii_strcasecmp(sensor_hash, hash_b) == 0) { interface_iter_b = interfaces_iter; sensor_iter_b = sensors_iter; found_b = TRUE; } g_free(sensor_id); g_free(sensor_path); g_free(sensor_hash); /* break after freeing loop variables */ if (found_a && found_b) { break; } } g_free(interface_name); /* break after freeing loop variables */ if (found_a && found_b) { break; } } /* make the switch */ if (found_a && found_b) { GtkTreeIter first_iter; GtkTreeIter iter_next; gint ret_val; /* set a's interface to be the first interface in the sensors tree */ if (a_is_first) { /* this 'fails' if the tree is empty, but this is already checked with the first loop */ gtk_tree_model_get_iter_first (GTK_TREE_MODEL(sensors_applet->sensors), &first_iter); /* we are only interested in the case, where the two are not equal * that means the a's interface is not the first interface */ if ( 0 != sensors_applet_settings_sort_sensors_iter_compare (sensors_applet, first_iter, interface_iter_a)) { /* it should be, so it needs to be moved to the first position */ gtk_tree_store_move_after (GTK_TREE_STORE(sensors_applet->sensors), &interface_iter_a, NULL); } } /* check b's interface position * (1) if it is before a's if. - not possible, bc I have just set a's if. as first * afterwards every new if. will be set right after (one) a's if. * (0) if it is the same as a's, noop * (-1) if it is after a, check if it is right after a, if not move it there */ ret_val = sensors_applet_settings_sort_sensors_iter_compare (sensors_applet, interface_iter_a, interface_iter_b); if (-1 == ret_val) { /* set iter_next to the iter after a's if. iter, can copy like this */ iter_next = interface_iter_a; /* this 'fails' if there is no node after a's if. iter, but we already know, that at least b's if. iter is */ gtk_tree_model_iter_next (GTK_TREE_MODEL(sensors_applet->sensors), &iter_next); /* the node right after a's if. iter is not b's if. iter, so move b's if. iter */ if (0 != sensors_applet_settings_sort_sensors_iter_compare (sensors_applet, iter_next, interface_iter_b)) { gtk_tree_store_move_after (GTK_TREE_STORE(sensors_applet->sensors), &interface_iter_b, &interface_iter_a); } } /* at this point the interfaces are sorted, the sensors are next */ /* set a to be the first sensor in the sensors tree, under the first if. iter */ if (a_is_first) { /* this 'fails' if the tree is empty, but at least a is in it */ gtk_tree_model_iter_children(GTK_TREE_MODEL(sensors_applet->sensors), &first_iter, &interface_iter_a); /* we are only interested in the case, where the two are not equal * that means the a is not the first sensor */ if ( 0 != sensors_applet_settings_sort_sensors_iter_compare (sensors_applet, first_iter, sensor_iter_a)) { /* it should be, so it needs to be moved to the first position */ gtk_tree_store_move_after (GTK_TREE_STORE(sensors_applet->sensors), &sensor_iter_a, NULL); } } /* check b's position * if a's if. and b's if is the same * (1) if it is before a's if. - not possible, bc I have just set a as first * afterwards every new sensor will be set right after (one) a * (0) if it is the same as a's, not possible, a and b would be equal * (-1) if it is after a, check if it is right after a, if not move it there */ if (0 == ret_val && -1 == sensors_applet_settings_sort_sensors_iter_compare (sensors_applet, sensor_iter_a, sensor_iter_b)) { /* set iter_next to the iter after a's iter, can copy like this */ iter_next = sensor_iter_a; /* this 'fails' if there is no node after a's iter, but we already know, that at least b's iter is */ gtk_tree_model_iter_next (GTK_TREE_MODEL(sensors_applet->sensors), &iter_next); /* the node right after a's iter is not b's iter, so move b's iter */ if (0 != sensors_applet_settings_sort_sensors_iter_compare (sensors_applet, iter_next, sensor_iter_b)) { gtk_tree_store_move_after (GTK_TREE_STORE(sensors_applet->sensors), &sensor_iter_b, &sensor_iter_a); } } /* if a's if. and b's if is not the same * as b comes after a, b must be under a new if. * and so b must be the first node under that if. */ /* set b to be the first sensor in the sensors tree, under the b's if. iter */ if (-1 == ret_val) { /* this 'fails' if the tree is empty, but at least b is in it */ gtk_tree_model_iter_children(GTK_TREE_MODEL(sensors_applet->sensors), &first_iter, &interface_iter_b); /* we are only interested in the case, where the two are not equal * that means the b is not the first sensor */ if ( 0 != sensors_applet_settings_sort_sensors_iter_compare (sensors_applet, first_iter, sensor_iter_b)) { /* it should be, so it needs to be moved to the first position */ gtk_tree_store_move_after (GTK_TREE_STORE(sensors_applet->sensors), &sensor_iter_b, NULL); } } return 1; } /* hash_b has already been removed from the system */ if (found_a && !found_b) { return 2; } /* this is the least likely case * if hash_a is not found, then the sensors-list starts with a sensor * that has already been removed from the system */ if (!found_a) { return 3; } /* we should never get here */ return 0; } /* sort sensors based on sensors-list array in gsettings */ gboolean sensors_applet_settings_sort_sensors (SensorsApplet *sensors_applet) { gchar **sensors_list = NULL; gchar *hash_a = NULL; gchar *hash_b = NULL; gint ret_val = -1; /* marks the first iteration */ gint first_counter = 1; /* get sensors-list array from gsettings * newly allocated, has to be freed fully * for an empty array a pointer to a NULL pointer will be returned (g_variant_dup_strv ()) */ sensors_list = g_settings_get_strv (sensors_applet->settings, "sensors-list"); /* list is empty (on the first run) */ if (NULL == *sensors_list) { /* still need to free the array, even if it is empty */ g_strfreev (sensors_list); return FALSE; } gint i; /* hash lists are equal */ gboolean hle = TRUE; /* compare saved sorted list to newly loaded sensors' list */ for (i = 0; (NULL != sensors_applet->sensors_hash_array[i]) && (NULL != sensors_list[i]); i++) { if (g_ascii_strcasecmp(sensors_applet->sensors_hash_array[i], sensors_list[i]) != 0) { hle = FALSE; break; } } /* lists are the same -> nothing to do */ if (hle) { #ifdef SORT_DEBUG syslog(LOG_ERR, "sensor sort: saved list is the same as the new one, returning"); #endif /* still need to free the array */ g_strfreev (sensors_list); return TRUE; } #ifdef SORT_DEBUG sensors_applet_settings_print_sensors_tree (sensors_applet); #endif for (i = 0; NULL != sensors_list[i]; i++) { /* first pass */ if (i == 0) { /* make a copy */ hash_a = g_strdup (sensors_list[i]); continue; } hash_b = g_strdup (sensors_list[i]); /* now that we have two hashes, find the two corresponding sensors and sort them * if i == 1, we have both a and b set, this is the first time calling this function */ ret_val = sensors_applet_settings_sort_sensors_sort (sensors_applet, hash_a, hash_b, (i == first_counter)); /* hash_b has already been removed from the system * don't free hash_a, don't reassign hash_b * as we already know that b is no longer in the system */ if (2 == ret_val) { continue; } /* after sorting free hash_a (it should be in place) and reassign hash_b to hash_a */ g_free (hash_a); hash_a = hash_b; /* hash_a not found * set hash_b as hash_a and set the first element counter * (essentially skip the first element in the sensors-list array) */ if (3 == ret_val) { first_counter++; } } #ifdef SORT_DEBUG sensors_applet_settings_print_sensors_tree (sensors_applet); #endif /* hash_a already freed * only in the following case do we need to free it */ if (2 == ret_val) { g_free (hash_a); } g_free (hash_b); g_strfreev (sensors_list); /* reorder active sensors based on reordered sensors tree */ sensors_applet_reorder_sensors (sensors_applet); return TRUE; } /* save sensor data under a unique hash * save sensor sort in an array, with above hash */ gboolean sensors_applet_settings_save_sensors (SensorsApplet *sensors_applet) { /* write everything to GSettings except VISIBLE and ALARM_TIMEOUT_INDEX * for stepping through GtkTreeStore data structure */ GtkTreeIter interfaces_iter; GtkTreeIter sensors_iter; gboolean not_end_of_interfaces = TRUE; gboolean not_end_of_sensors = TRUE; gchar *applet_path = NULL; gchar *current_path = NULL; gchar *current_id = NULL; gchar *current_label = NULL; gchar *current_interface = NULL; gchar *current_low_alarm_command = NULL; gchar *current_high_alarm_command = NULL; gchar *current_graph_color = NULL; gboolean current_enable, current_alarm_enable; gdouble current_low_value, current_high_value, current_multiplier, current_offset; guint current_alarm_timeout, current_sensor_type, current_icon_type; /* data structure (array) to be able to save sensors list to gsettings */ GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE (GSGVTSA)); applet_path = mate_panel_applet_get_preferences_path (sensors_applet->applet); /* now step through the GtkTreeStore sensors to find which sensors are available / loaded */ for (not_end_of_interfaces = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(sensors_applet->sensors), &interfaces_iter); not_end_of_interfaces; not_end_of_interfaces = gtk_tree_model_iter_next(GTK_TREE_MODEL(sensors_applet->sensors), &interfaces_iter)) { for (not_end_of_sensors = gtk_tree_model_iter_children(GTK_TREE_MODEL(sensors_applet->sensors), &sensors_iter, &interfaces_iter); not_end_of_sensors; not_end_of_sensors = gtk_tree_model_iter_next(GTK_TREE_MODEL(sensors_applet->sensors), &sensors_iter)) { gtk_tree_model_get(GTK_TREE_MODEL(sensors_applet->sensors), &sensors_iter, PATH_COLUMN, ¤t_path, ID_COLUMN, ¤t_id, LABEL_COLUMN, ¤t_label, INTERFACE_COLUMN, ¤t_interface, SENSOR_TYPE_COLUMN, ¤t_sensor_type, ENABLE_COLUMN, ¤t_enable, LOW_VALUE_COLUMN, ¤t_low_value, HIGH_VALUE_COLUMN, ¤t_high_value, ALARM_ENABLE_COLUMN, ¤t_alarm_enable, LOW_ALARM_COMMAND_COLUMN, ¤t_low_alarm_command, HIGH_ALARM_COMMAND_COLUMN, ¤t_high_alarm_command, ALARM_TIMEOUT_COLUMN, ¤t_alarm_timeout, MULTIPLIER_COLUMN, ¤t_multiplier, OFFSET_COLUMN, ¤t_offset, ICON_TYPE_COLUMN, ¤t_icon_type, GRAPH_COLOR_COLUMN, ¤t_graph_color, -1); /* GSettings unique id for one sensor data */ gchar *gsuid = sensors_applet_settings_get_unique_id (current_interface, current_id, current_path); /* save sensor uid to gvariant array */ g_variant_builder_add(&builder, GSGVTS, /* must be related to the G_VARIANT_TYPE in init and gsettings schema */ gsuid); /* save sensor data to gsettings individually * g_strdup_printf doesn't free args! * applet_path is freed at the end */ gchar *path = g_strdup_printf ("%s%s/", applet_path, gsuid); g_free (gsuid); GSettings *settings; settings = g_settings_new_with_path ("org.mate.sensors-applet.sensor", path); g_free (path); /* wait until g_settings_apply() is called to save all changes at once to gsettings */ g_settings_delay (settings); g_settings_set_string (settings, PATH, current_path); g_settings_set_string (settings, ID, current_id); g_settings_set_string (settings, LABEL, current_label); g_settings_set_string (settings, INTERFACE, current_interface); g_settings_set_int (settings, SENSOR_TYPE, current_sensor_type); g_settings_set_boolean (settings, ENABLED, current_enable); g_settings_set_double (settings, LOW_VALUE, current_low_value); g_settings_set_double (settings, HIGH_VALUE, current_high_value); g_settings_set_boolean (settings, ALARM_ENABLED, current_alarm_enable); g_settings_set_string (settings, LOW_ALARM_COMMAND, current_low_alarm_command); g_settings_set_string (settings, HIGH_ALARM_COMMAND, current_high_alarm_command); g_settings_set_int (settings, ALARM_TIMEOUT, current_alarm_timeout); g_settings_set_double (settings, MULTIPLIER, current_multiplier); g_settings_set_double (settings, OFFSET, current_offset); g_settings_set_int (settings, ICON_TYPE, current_icon_type); g_settings_set_string (settings, GRAPH_COLOR, current_graph_color); g_settings_apply (settings); g_object_unref (settings); } } /* save the sensor-list array to gsettings * builder is freed by g_variant_builder_end() * the gvariant returned from g_variant_builder_end() is floating, so it is freed by g_settings_set_value() */ g_settings_set_value (sensors_applet->settings, "sensors-list", g_variant_builder_end (&builder)); g_free (applet_path); return TRUE; }