From a7e24a66b69f02106db00e5392b68c39f5d4eb59 Mon Sep 17 00:00:00 2001 From: info-cppsp Date: Wed, 13 Sep 2017 13:13:30 +0200 Subject: added udisks2 plugin --- plugins/Makefile.am | 5 + plugins/udisks2/Makefile.am | 24 ++ plugins/udisks2/udisks2-plugin.c | 550 +++++++++++++++++++++++++++++++++++++++ plugins/udisks2/udisks2-plugin.h | 24 ++ 4 files changed, 603 insertions(+) create mode 100644 plugins/udisks2/Makefile.am create mode 100644 plugins/udisks2/udisks2-plugin.c create mode 100644 plugins/udisks2/udisks2-plugin.h (limited to 'plugins') diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 74161f5..d787000 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -1,6 +1,7 @@ DIST_SUBDIRS = \ acpi \ udisks \ + udisks2 \ eee \ hddtemp \ i2c-proc \ @@ -32,6 +33,10 @@ if UDISKS SUBDIRS += udisks endif +if UDISKS2 +SUBDIRS += udisks2 +endif + if LIBSENSORS SUBDIRS += libsensors else diff --git a/plugins/udisks2/Makefile.am b/plugins/udisks2/Makefile.am new file mode 100644 index 0000000..14e512a --- /dev/null +++ b/plugins/udisks2/Makefile.am @@ -0,0 +1,24 @@ +# UDisks plugin +plugindir = $(libdir)/mate-sensors-applet/plugins + +AM_CPPFLAGS = -DMATELOCALEDIR=\""$(datadir)/locale/"\" \ + -DG_LOG_DOMAIN=\""Sensors Applet"\" \ + -DPIXMAPS_DIR=\""$(datadir)/pixmaps/$(PACKAGE)/"\" \ + -DDATADIR=\""$(datadir)"\" \ + -DLIBDIR=\""$(libdir)"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" \ + -DPREFIX=\""$(prefix)"\" \ + -I$(top_srcdir) \ + $(GLIB_CFLAGS) + + + +plugin_LTLIBRARIES = libudisks2.la + +libudisks2_la_SOURCES = \ + udisks2-plugin.h \ + udisks2-plugin.c + +libudisks2_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS) $(GLIB_LIBS) + +libudisks2_la_LIBADD = $(top_builddir)/lib/libmate-sensors-applet-plugin.la diff --git a/plugins/udisks2/udisks2-plugin.c b/plugins/udisks2/udisks2-plugin.c new file mode 100644 index 0000000..3bc6eef --- /dev/null +++ b/plugins/udisks2/udisks2-plugin.c @@ -0,0 +1,550 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * vim: sts=0 sw=8 ts=8 tw=78 noexpandtab + * + * Copyright (C) 2009 Pramod Dematagoda + * + * 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 + */ + +/* +Udisks2 plugin for the mate-sensors-applet + +written by info@cppsp.de using the structure and code of the previous version +from above author + +fd1 - from doc1 - dbus-glib documentation +https://dbus.freedesktop.org/doc/dbus-glib/ +fd2 - from doc2 - GDBUS documentation +https://developer.gnome.org/gio/stable/index.html + +I couldn't figure out debug, so I used syslog +syslog(LOG_ERR, "hellodd"); +-> /var/log/syslog +*/ + + + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include +#include +#include "udisks2-plugin.h" + + +// remove // from next line for syslog debug +//#define UD2PD 1 + +#ifdef UD2PD +#include +#endif + + +#define UDISKS2_BUS_NAME "org.freedesktop.UDisks2" +#define UDISKS2_INTERFACE_NAME "org.freedesktop.DBus.ObjectManager" +#define UDISKS2_DEVICE_INTERFACE_NAME "org.freedesktop.UDisks2.Drive" +#define UDISKS2_DEVICE_INTERFACE2_NAME "org.freedesktop.UDisks2.Drive.Ata" + +#define UDISKS2_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties" +#define UDISKS2_OBJECT_PATH "/org/freedesktop/UDisks2" + + +/* + * Info about a single sensor + */ +typedef struct _DevInfo{ + gchar *path; + gchar *id; + gdouble temp; + GDBusProxy *sensor_proxy; // dbus object + GError *error; +} DevInfo; + + +const gchar *plugin_name = "udisks2"; + +// a container for the devices found to have smart enabled +GHashTable *devices = NULL; + +/* This is a global variable for convenience */ +GDBusConnection *connection; + + +static void update_device(DevInfo *info) +{ + GError *error = NULL; + GVariant *tempgvar; + gdouble temp; + + // check valid input parameter + g_return_if_fail(info != NULL); + + // check connection too + g_return_if_fail(connection != NULL); + + g_clear_error(&info->error); + +// for the udisks plugin a new sensor_proxy was created here, which seems stupid, as one is already stored in the DevInfo struct +// I only create one direct connect proxy here in this function + + // check for sensor_proxy, which should exist at this point, make one if necessary and save it into DevInfo + // this is used to get the temp value the direct way + if(NULL == info->sensor_proxy) + { + info->sensor_proxy = g_dbus_proxy_new_sync(connection, G_DBUS_PROXY_FLAGS_NONE, NULL, + UDISKS2_BUS_NAME, + info->path, + UDISKS2_PROPERTIES_INTERFACE, + NULL, NULL + ); + + } + +// "DriveAtaSmartTimeCollected" in ud2 is "drive ata SmartUpdated" +// fd: The point in time (seconds since the Unix Epoch) that the SMART status was updated or 0 if never updated. +// should we bother getting this time?? + +/* + if (!g_dbus_proxy_call_sync(sensor_proxy, "Get", NULL, + G_TYPE_STRING, UDISKS2_BUS_NAME, + G_TYPE_STRING, "DriveAtaSmartTimeCollected", G_TYPE_INVALID, + G_TYPE_VALUE, &smart_time, + G_TYPE_INVALID) || + !g_value_get_uint64(&smart_time)) + { + g_object_unref(sensor_proxy); + return; + } +*/ + + +// reading "DriveAtaSmartBlob" doesn't make it refresh +// it seems to me that smart updates occur automatically every 10 minutes +// mate-sensor-applet has a default refresh of 2 seconds... +// it is possible to force a smart update with udisks2: SmartUpdate (IN a{sv} options); + +// fd: A blob containing the ATA SMART data. This blob can be used with libatasmart to get more information. This property is only valid if DriveAtaSmartTimeCollected is greater than zero. +// this smartblob was needed, bc there was no other way to get the smart temperature from udisks +/* + + if (!g_dbus_proxy_call_sync(sensor_proxy, "Get", &info->error, + G_TYPE_STRING, UDISKS2_BUS_NAME, + G_TYPE_STRING, "DriveAtaSmartBlob", G_TYPE_INVALID, + G_TYPE_VALUE, &smart_blob_val, + G_TYPE_INVALID)) + { + g_debug("Error getting DriveAtaSmartBlob %s", + info->error ? info->error->message : "NULL"); + g_object_unref(sensor_proxy); + return; + } + smart_blob = g_value_get_boxed(&smart_blob_val); + + sk_disk_open(NULL, &sk_disk); + sk_disk_set_blob(sk_disk, smart_blob->data, smart_blob->len); + if (sk_disk_smart_get_temperature(sk_disk, &temperature) < 0) + { + g_debug("Error getting temperature from AtaSmartBlob"); + g_free(sk_disk); + g_array_free(smart_blob, TRUE); + g_object_unref(sensor_proxy); + return; + } +*/ + + // directly asking the device's DBus object for the temp + tempgvar = g_dbus_proxy_call_sync(info->sensor_proxy, "Get", + g_variant_new ("(ss)", + UDISKS2_DEVICE_INTERFACE2_NAME, + "SmartTemperature"), // parameters + G_DBUS_CALL_FLAGS_NONE, // flags + -1, // timeout + NULL, // cancellable + &error); + + if (NULL == tempgvar) + { + +#ifdef UD2PD +syslog(LOG_ERR, "Failed to get drive temperature"); +#endif + g_debug("Failed to get drive temperature: %s", + error->message); + g_error_free(error); + // throw away proxy, maybe next time it will be better + g_object_unref(info->sensor_proxy); + return; + } + else + { + +#ifdef UD2PD +syslog(LOG_ERR, "tempgvar value: %s", g_variant_print(g_variant_get_variant(g_variant_get_child_value(tempgvar, 0)), TRUE)); +#endif + + // tempgvar comes back as sg along the lines of array(gvariant(tempasdouble)) + // hence unpacking + temp = g_variant_get_double(g_variant_get_variant(g_variant_get_child_value(tempgvar, 0))); + // temp in K + info->temp = temp - 273.15; + g_variant_unref(tempgvar); + +#ifdef UD2PD +syslog(LOG_ERR, "Refresh udisks2 device temp: '%f'\n", info->temp); +#endif + + } + + +} + + + +/* This is the handler for the Changed() signal emitted by UDisks. */ +/* +static void udisks2_changed_signal_cb(GDBusProxy *sensor_proxy) { + const gchar *path; + DevInfo *info; + + // fd2: Gets the object path sensor_proxy is for. + path = g_dbus_proxy_get_object_path(sensor_proxy); + g_debug("%s changed()", path); + info = g_hash_table_lookup(devices, path); + +// update_device(info); +} +*/ + + +// in this function we would like to get a list of device (hdd/ssd) paths +// then with each path we get the temperature +// it is possible with udisks2 to get all the above information in one g_dbus_proxy_call_sync(), so that is how I did it +// a better version would be to use GDBusObjectManager Server + Client ?? +static void udisks2_plugin_get_sensors(GList **sensors) { + +#ifdef UD2PD +syslog(LOG_ERR, "fstart"); +#endif + + GDBusProxy *proxy; + GError *error = NULL; + + DevInfo *info; + + /* This connection will be used for everything, including the obtaining + * of sensor data + */ + // fd1: Returns a connection to the given bus. The connection is a global variable shared with other callers of this function. + // fd2: Synchronously connects to the message bus specified by bus_type . Note that the returned object may shared with other callers, e.g. if two separate parts of a process calls this function with the same bus_type , they will share the same object. + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (connection == NULL) + { + +#ifdef UD2PD +syslog(LOG_ERR, "dbus conn fail"); +#endif + + g_debug("Failed to open connection to DBUS: %s", + error->message); + g_error_free(error); + return; + } + +#ifdef UD2PD +syslog(LOG_ERR, "dbus conn success"); +#endif + + /* This was the proxy which is only used once during the enumeration of + * the device object paths + */ + // I use it to get all info of all devices at once + // fd1: Creates a new proxy for a remote interface exported by a connection on a message bus. + // fd2: Creates a proxy for accessing interface_name on the remote object at object_path owned by name at connection and synchronously loads D-Bus properties unless the G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used. + proxy = g_dbus_proxy_new_sync(connection, G_DBUS_PROXY_FLAGS_NONE, NULL, + UDISKS2_BUS_NAME, + UDISKS2_OBJECT_PATH, + UDISKS2_INTERFACE_NAME, + NULL, NULL + ); + +#ifdef UD2PD +syslog(LOG_ERR, "dbus conn proxy success"); +#endif + + /* The object paths of the disks are enumerated and placed in an array + * of object paths + */ + // fd1: Function for synchronously invoking a method and receiving reply values. + // fd2: Synchronously invokes the method_name method on proxy. + // called "EnumerateDevices" method on UDisks object through dbus + // which returned an array of objectpaths + // "GetManagedObjects" returns dict of (objectpath, (dict of (string [ie. if. name], dict of(string [ie. property name], variant [ie. prop. value])))) + + // g_dbus_proxy_call_sync() returns NULL on error, GVariant * otherwise + GVariant *managed_objects; + + + managed_objects = g_dbus_proxy_call_sync(proxy, "GetManagedObjects", + NULL, // parameters + G_DBUS_CALL_FLAGS_NONE, // flags + -1, // timeout + NULL, // cancellable + &error); + + if (NULL == managed_objects) + { + +#ifdef UD2PD +syslog(LOG_ERR, "Failed to enumerate disk devices"); +#endif + + g_debug("Failed to enumerate disk devices: %s", + error->message); + g_error_free(error); + g_object_unref(proxy); + g_object_unref(connection); + connection = NULL; + return; + } + + // the result dictionary is enclosed in an array, unpack + managed_objects = g_variant_get_child_value(managed_objects, 0); + +#ifdef UD2PD +//syslog(LOG_ERR, "managed_objects type: %s", g_variant_print(managed_objects, TRUE)); +syslog(LOG_ERR, "success to enumerate disk devices"); +#endif + + // iterator for the result dictionary + + // iterator code is based on the g_variant_iter_next() documentation + // iter is freed if the GVariant is, when using g_variant_iter_init() + GVariantIter iter; + gchar *key; // object path (like '/org/freedesktop/UDisks2/drives/Samsung_SSD_840_EVO_250GB_*insert drive serial nr.*') + GVariant *value; + +#ifdef UD2PD +// log collection size +syslog(LOG_ERR, "iter init count: %d", (int) g_variant_iter_init(&iter, managed_objects)); +#else + g_variant_iter_init(&iter, managed_objects); +#endif + + // "{sv}" is a GVariant format string + // {} dictionary of, s string, v GVariant + // changed to "{oa{sa{sv}}}" on error message 'the GVariant format string '{sv}' has a type of '{sv}' but the given value has a type of 'a{oa{sa{sv}}}'' + // a is array, o is object path + // NOO!! the right format string is "{o@*}", which means get an object path into the 1st variable (key) + // and get 'everything else' (as a GVariant) into the 2nd variable (value) + // needs the & before the key and value params! + while (g_variant_iter_next(&iter, "{o@*}", &key, &value)) + { + +#ifdef UD2PD +syslog(LOG_ERR, "in iter while loop"); +syslog(LOG_ERR, "key value: %s", key); +//syslog(LOG_ERR, "value type: %s", g_variant_print(value, TRUE)); +#endif + + // level 2 + // create a dictionary of value + // the two interface names that we are searching for are known and defined + // can't use GVariantDict, it only supports '{sv}' but the given value has a type of '{sa{sv}}' + // using general lookup + + GVariant *propdict; // drive data + GVariant *propdict2; // drive smart data + + // make two dictionaries that contain the properties of the drive interfaces + propdict = g_variant_lookup_value(value, UDISKS2_DEVICE_INTERFACE_NAME, G_VARIANT_TYPE_DICTIONARY); + propdict2 = g_variant_lookup_value(value, UDISKS2_DEVICE_INTERFACE2_NAME, G_VARIANT_TYPE_DICTIONARY); + + + // do we have the right ifname keys? + if((NULL != propdict) && (NULL != propdict2)) + { + +#ifdef UD2PD +syslog(LOG_ERR, "propdict type: %s", g_variant_print(propdict, TRUE)); +syslog(LOG_ERR, "propdict2 type: %s", g_variant_print(propdict2, TRUE)); +#endif + + // get data + gchar *id; + gchar *model; + + gboolean smartenabled; + gdouble temp; + + // NULL, bc we don't care about the length of the string + // typecast bc g_variant_get_string() returns const char* + id = (gchar *) g_variant_get_string(g_variant_lookup_value(propdict, "Id", G_VARIANT_TYPE_STRING), NULL); + model = (gchar *) g_variant_get_string(g_variant_lookup_value(propdict, "Model", G_VARIANT_TYPE_STRING), NULL); + + smartenabled = g_variant_get_boolean(g_variant_lookup_value(propdict2, "SmartEnabled", G_VARIANT_TYPE_BOOLEAN)); + temp = g_variant_get_double(g_variant_lookup_value(propdict2, "SmartTemperature", G_VARIANT_TYPE_DOUBLE)); + +#ifdef UD2PD +syslog(LOG_ERR, "Found udisks2 device id: '%s'\n", id); +syslog(LOG_ERR, "Found udisks2 device model: '%s'\n", model); +syslog(LOG_ERR, "Found udisks2 device smartenabled: '%d'\n", smartenabled); +syslog(LOG_ERR, "Found udisks2 device temp: '%f'\n", temp); +#endif + + + // only go on if smart is enabled + // save data + if(smartenabled) + { + + info = g_malloc0(sizeof(DevInfo)); + if (devices == NULL) + { + devices = g_hash_table_new(g_str_hash, + g_str_equal); + } + + info->id = g_strdup(id); + info->path = g_strdup(key); + + // temp in K + // this could be left at 0.0, 2 seconds later it will be refreshed anyway + info->temp = (gdouble)temp - 273.15; + g_hash_table_insert(devices, info->id, info); + + // Write the sensor data + sensors_applet_plugin_add_sensor(sensors, + id, + "Disk Temperature", + model, + TEMP_SENSOR, + FALSE, + HDD_ICON, + DEFAULT_GRAPH_COLOR); + + g_debug("Added %s", id); + +#ifdef UD2PD +syslog(LOG_ERR, "Added %s", id); +#endif + + } + else + { + +#ifdef UD2PD +syslog(LOG_ERR, "No temp data for device: %s\n", key); +#endif + + g_debug ("No temp data for device: %s\n", key); + } + +#ifdef UD2PD +syslog(LOG_ERR, "b4 free1"); +#endif + + g_free(id); + g_free(model); + + } + +#ifdef UD2PD +syslog(LOG_ERR, "b4 free2"); +#endif + + // free propdict, propdict2 + // g_variant_dict_unref() may not work a few times, gives error + // this one seems to do fine + if(NULL != propdict) {g_variant_unref(propdict);} + if(NULL != propdict2) {g_variant_unref(propdict2);} + +#ifdef UD2PD +syslog(LOG_ERR, "b4 free3"); +#endif + + g_free(key); + g_variant_unref(value); + + } // end of while loop + + + g_variant_unref(managed_objects); + g_object_unref(proxy); + if (devices == NULL) + { + g_object_unref(connection); + connection = NULL; + } +} + + +// this is the function called every refresh cycle +static gdouble udisks2_plugin_get_sensor_value(const gchar *path, + const gchar *id, + SensorType type, + GError **error) { + DevInfo *info; + + // get device stuct from data store + info = (DevInfo *)g_hash_table_lookup(devices, path); + if (info == NULL) + { + g_set_error(error, SENSORS_APPLET_PLUGIN_ERROR, 0, + "Error finding disk with path %s", path); + return 0.0; + } + + if (info->error) + { + *error = info->error; + info->error = NULL; + return 0.0; + } + + // refresh device temp + /* update value since Changed() signal doesn't fire manually enough so + * poll instead */ + update_device(info); + return info->temp; +} + + +// API functions +const gchar *sensors_applet_plugin_name(void) +{ + return plugin_name; +} + +static GList *udisks2_plugin_init(void) { + GList *sensors = NULL; + + udisks2_plugin_get_sensors(&sensors); + + return sensors; +} + +GList *sensors_applet_plugin_init(void) +{ + return udisks2_plugin_init(); +} + +gdouble sensors_applet_plugin_get_sensor_value(const gchar *path, + const gchar *id, + SensorType type, + GError **error) { + return udisks2_plugin_get_sensor_value(path, id, type, error); +} diff --git a/plugins/udisks2/udisks2-plugin.h b/plugins/udisks2/udisks2-plugin.h new file mode 100644 index 0000000..026ee8c --- /dev/null +++ b/plugins/udisks2/udisks2-plugin.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2009 Pramod Dematagoda + * + * 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 + */ + +#ifndef UDISKS2_PLUGIN_H +#define UDISKS2_PLUGIN_H + +#include + +#endif /* UDISKS2_PLUGIN_H */ -- cgit v1.2.1