[rygel] core: Add initial EnergyManagement service implementation
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [rygel] core: Add initial EnergyManagement service implementation
- Date: Sat, 8 Nov 2014 14:11:48 +0000 (UTC)
commit 0ca67df803f8f31070b88c613e5ce19565e973a8
Author: Jussi Kukkonen <jussi kukkonen intel com>
Date: Mon Sep 2 11:28:28 2013 +0300
core: Add initial EnergyManagement service implementation
EnergyManagement is a way to tell controlpoints that the device
(e.g. Mediaserver or Renderer) may suspend, and also advice on
how it can be woken up:
http://upnp.org/specs/lp/UPnP-lp-EnergyManagement-v1-Service.pdf
data/xml/EnergyManagement.xml.in | 127 +++++++++
data/xml/Makefile.am | 3 +-
doc/man/rygel.conf.xml | 40 +++
src/librygel-core/Makefile.am | 2 +
src/librygel-core/filelist.am | 1 +
.../rygel-energy-management-helper.vapi | 9 +
src/librygel-core/rygel-energy-management.vala | 282 ++++++++++++++++++++
src/librygel-core/rygel-plugin.vala | 17 ++
8 files changed, 480 insertions(+), 1 deletions(-)
---
diff --git a/data/xml/EnergyManagement.xml.in b/data/xml/EnergyManagement.xml.in
new file mode 100644
index 0000000..59a5da7
--- /dev/null
+++ b/data/xml/EnergyManagement.xml.in
@@ -0,0 +1,127 @@
+<?xml version="1.0"?>
+<scpd xmlns="urn:schemas-upnp-org:service-1-0">
+ <specVersion>
+ <major>1</major>
+ <minor>0</minor>
+ </specVersion>
+
+ <actionList>
+ <action>
+ <name>GetInterfaceInfo</name>
+ <argumentList>
+ <argument>
+ <name>NetworkInterfaceInfo</name>
+ <direction>out</direction>
+ <relatedStateVariable>NetworkInterfaceInfo</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>ProxiedNetworkInterfaceInfo</name>
+ <direction>out</direction>
+ <relatedStateVariable>ProxiedNetworkInterfaceInfo</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+<!--
+ <action>
+ <name>ServiceSubscription</name>
+ <argumentList>
+ <argument>
+ <name>UniqueServiceName</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_UniqueServiceName</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>ResourceURI</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_URI</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>DurationRequest</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Duration</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>ServiceSubscriptionID</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_ServiceSubscriptionID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Duration</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_Duration</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>ServiceRenewal</name>
+ <argumentList>
+ <argument>
+ <name>DurationRequest</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_Duration</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>ServiceSubscriptionID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ServiceSubscriptionID</relatedStateVariable>
+ </argument>
+ <argument>
+ <name>Duration</name>
+ <direction>out</direction>
+ <relatedStateVariable>A_ARG_TYPE_Duration</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+
+ <action>
+ <name>ServiceRelease</name>
+ <argumentList>
+ <argument>
+ <name>ServiceSubscriptionID</name>
+ <direction>in</direction>
+ <relatedStateVariable>A_ARG_TYPE_ServiceSubscriptionID</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+-->
+
+ </actionList>
+
+ <serviceStateTable>
+ <stateVariable sendEvents="yes">
+ <name>NetworkInterfaceInfo</name>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable sendEvents="no">
+ <name>ProxiedNetworkInterfaceInfo</name>
+ <dataType>string</dataType>
+ </stateVariable>
+
+<!--
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_Duration</name>
+ <dataType>ui4</dataType>
+ </stateVariable>
+
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_ServiceSubscriptionID</name>
+ <dataType>ui4</dataType>
+ </stateVariable>
+
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_UniqueServiceName</name>
+ <dataType>string</dataType>
+ </stateVariable>
+
+ <stateVariable sendEvents="no">
+ <name>A_ARG_TYPE_URI</name>
+ <dataType>string</dataType>
+ </stateVariable>
+-->
+
+ </serviceStateTable>
+
+</scpd>
diff --git a/data/xml/Makefile.am b/data/xml/Makefile.am
index c0f9f29..5fe4f1f 100644
--- a/data/xml/Makefile.am
+++ b/data/xml/Makefile.am
@@ -1,4 +1,5 @@
-xml_in_files = MediaServer3.xml.in \
+xml_in_files = EnergyManagement.xml.in \
+ MediaServer3.xml.in \
MediaRenderer2.xml.in \
RuihServer2.xml.in \
ContentDirectory.xml.in \
diff --git a/doc/man/rygel.conf.xml b/doc/man/rygel.conf.xml
index 5cab37f..057c8b0 100644
--- a/doc/man/rygel.conf.xml
+++ b/doc/man/rygel.conf.xml
@@ -276,6 +276,16 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
</varlistentry>
<varlistentry>
<term>
+ <option>energy-management</option>
+ </term>
+ <listitem>
+ <para>
+ Set to <userinput>true</userinput> to if you would like the UPnP device to contain a
EnergyManagement service. Not that additional configuration is required, see EnergyManagement settings.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
<option>diagnostics</option>
</term>
<listitem>
@@ -287,6 +297,36 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
</variablelist>
</refsect1>
<refsect1>
+ <title>EnergyManagement settings</title>
+ <para>The settings in <option>[EnergyManagement-IFACENAME]</option> sections specify the settings that
relate to EnergyManagement services on this interface. Example:
<option>[EnergyManagement-eth0].</option></para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>mode-on-suspend</option>
+ </term>
+ <listitem>
+ <para>The <code>NetworkInterfaceMode</code> that should be used when suspended. Default is
"Unimplemented", other valid values are "IP-up-Periodic”, "IP-down-no-Wake", "IP-down-with-WakeOn",
"IP-down-with-WakeAuto", "IP-down-with-WakeOnAuto".</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>supported-transport</option>
+ </term>
+ <listitem>
+ <para>Optional <code>WakeSupportedTransport</code> that the service should advertize. Valid values
are "UDP-Broadcast", "UDP-Unicast", "TCP-Unicast", "Other".</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <option>password</option>
+ </term>
+ <listitem>
+ <para>Optional hexadecimal password that will be used to build the
<code>WakeOnPattern</code>.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+ <refsect1>
<title>Tracker Plugin</title>
<para>The tracker plugin uses the centralized database of meta information
from the tracker project. See the
diff --git a/src/librygel-core/Makefile.am b/src/librygel-core/Makefile.am
index 715be18..97f51c7 100644
--- a/src/librygel-core/Makefile.am
+++ b/src/librygel-core/Makefile.am
@@ -17,6 +17,8 @@ librygel_core_2_4_la_VALAFLAGS = \
--vapidir=$(srcdir) \
--pkg uuid \
--pkg posix \
+ --pkg linux \
+ --pkg rygel-energy-management-helper \
$(LIBRYGEL_CORE_DEPS_VALAFLAGS) \
$(RYGEL_COMMON_VALAFLAGS)
diff --git a/src/librygel-core/filelist.am b/src/librygel-core/filelist.am
index 70c00ec..4ac48da 100644
--- a/src/librygel-core/filelist.am
+++ b/src/librygel-core/filelist.am
@@ -7,6 +7,7 @@ LIBRYGEL_CORE_VAPI_SOURCE_FILES = \
rygel-basic-management-test-traceroute.vala \
rygel-description-file.vala \
rygel-dlna-profile.vala \
+ rygel-energy-management.vala \
rygel-root-device.vala \
rygel-root-device-factory.vala \
rygel-dbus-interface.vala \
diff --git a/src/librygel-core/rygel-energy-management-helper.vapi
b/src/librygel-core/rygel-energy-management-helper.vapi
new file mode 100644
index 0000000..bdb66bf
--- /dev/null
+++ b/src/librygel-core/rygel-energy-management-helper.vapi
@@ -0,0 +1,9 @@
+namespace Rygel.EnergyManagementHelper {
+ /* Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=707180 */
+ [CCode (cname = "struct sockaddr", cheader_filename = "sys/socket.h", destroy_function = "")]
+ public struct SockAddr {
+ public int sa_family;
+ [CCode (array_length = false)]
+ public char[] sa_data;
+ }
+}
diff --git a/src/librygel-core/rygel-energy-management.vala b/src/librygel-core/rygel-energy-management.vala
new file mode 100644
index 0000000..ab8e113
--- /dev/null
+++ b/src/librygel-core/rygel-energy-management.vala
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2013 Intel Corporation.
+ *
+ * Author: Jussi Kukkonen <jussi kukkonen intel com>
+ *
+ * This file is part of Rygel.
+ *
+ * Rygel 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.
+ *
+ * Rygel 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* EnergyManagement service implementation. The service will be run on
+ * plugins that have
+ * energy-management=true
+ * set in their configuration. It requires UPower to function properly.
+ *
+ * Every network interface that supports Wake-On should have a
+ * configuration group:
+ * [EnergyManagement-eth0]
+ * mode-on-suspend=IP-down-WakeOn
+ * supported-transport=UDP-Broadcast
+ * password=FEEDDEADBEEF
+ * mode-on-suspend is required (without it the mode will always be
+ * "Unimplemented"), other configuration items are not.
+ */
+
+
+using Gee;
+using GLib;
+using GUPnP;
+
+[DBus (name = "org.freedesktop.UPower")]
+interface UPower : Object {
+ public signal void sleeping ();
+ public signal void resuming ();
+}
+
+/**
+ * Implementation of UPnP EnergyManagement service.
+ */
+public class Rygel.EnergyManagement : Service {
+ public const string UPNP_ID = "urn:upnp-org:serviceId:EnergyManagement";
+ public const string UPNP_TYPE = "urn:schemas-upnp-org:service:EnergyManagement:1";
+ public const string DESCRIPTION_PATH = "xml/EnergyManagement.xml";
+
+ private const string TEMPLATE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
+ "<NetworkInterfaceInfo
xsi:schemaLocation=\"urn:schemas-upnp-org:lp:em-NetworkInterfaceInfo
http://www.upnp.org/schemas/lp/em-NetworkInterfaceInfo.xsd\" " +
+ "
xmlns=\"urn:schemas-upnp-org:lp:em-NetworkInterfaceInfo\" " +
+ "
xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
+ "%s" +
+ "</NetworkInterfaceInfo>";
+
+ private Configuration config;
+ private bool sleeping;
+ private UPower upower;
+
+ public override void constructed () {
+ base.constructed ();
+
+ this.config = MetaConfig.get_default ();
+
+ this.sleeping = false;
+
+
+ try {
+ this.upower = Bus.get_proxy_sync
+ (BusType.SYSTEM,
+ "org.freedesktop.UPower",
+ "/org/freedesktop/UPower",
+ DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);
+ this.upower.sleeping.connect (this.upower_sleeping_cb);
+ this.upower.resuming.connect (this.upower_resuming_cb);
+ } catch (GLib.IOError err) {
+ /* this will lead to NetworkInterfaceMode "Unimplemented" */
+ }
+
+ this.query_variable["NetworkInterfaceInfo"].connect
+ (this.query_network_interface_info_cb);
+ this.query_variable["ProxiedNetworkInterfaceInfo"].connect
+ (this.query_proxied_network_interface_info_cb);
+
+ this.action_invoked["GetInterfaceInfo"].connect
+ (this.get_interface_info_cb);
+ }
+
+ private void upower_sleeping_cb () {
+ if (this.sleeping == true) {
+ return;
+ }
+
+ this.sleeping = true;
+ this.notify ("NetworkInterfaceInfo",
+ typeof (string),
+ this.create_network_interface_info ());
+ }
+
+ private void upower_resuming_cb () {
+ if (this.sleeping == false) {
+ return;
+ }
+
+ this.sleeping = false;
+ this.notify ("NetworkInterfaceInfo",
+ typeof (string),
+ this.create_network_interface_info ());
+ }
+
+ private bool get_mac_and_network_type (string iface,
+ out string mac,
+ out string type) {
+ var success = true;
+
+ var sock = Posix.socket (Posix.AF_INET, Posix.SOCK_STREAM, 0);
+ if (sock == -1) {
+ warning ("Failed to get a socket: %s\n", strerror (errno));
+ mac = "00:00:00:00:00:00";
+ type = "Other";
+
+ return false;
+ }
+
+ var ifreq = Linux.Network.IfReq ();
+ var ifreqp = (Linux.Network.IfReq*)(&ifreq);
+ Posix.strncpy ((string)ifreqp->ifr_name,
+ iface,
+ Linux.Network.INTERFACE_NAME_SIZE);
+
+ if (Posix.ioctl (sock, Linux.Network.SIOCGIFHWADDR, &ifreq) < 0) {
+ warning ("Failed to get mac address: %s\n",
+ strerror (errno));
+ mac = "00:00:00:00:00:00";
+ success = false;
+ } else {
+ /* workaround for https://bugzilla.gnome.org/show_bug.cgi?id=707180 */
+ EnergyManagementHelper.SockAddr *addr =
+ (EnergyManagementHelper.SockAddr*)(&ifreq.ifr_hwaddr);
+
+ mac = "%02X:%02X:%02X:%02X:%02X:%02X".printf
+ ((uchar)addr.sa_data[0], (uchar)addr.sa_data[1],
+ (uchar)addr.sa_data[2], (uchar)addr.sa_data[3],
+ (uchar)addr.sa_data[4], (uchar)addr.sa_data[5]);
+ }
+
+ /* Note that this call really takes a struct IwReq, but this
+ * works since we only test if the call fails or not */
+ var ret_val = Posix.ioctl (sock, Linux.WirelessExtensions.SIOCGIWNAME, &ifreq);
+ if (ret_val == -1) {
+ type = "Ethernet";
+ } else {
+ type = "Wi-Fi";
+ }
+
+ return success;
+ }
+
+ private string create_network_interface_info () {
+
+ string mac_address, type;
+ bool success = true;
+
+ var iface = this.root_device.context.interface;
+ var config_section ="EnergyManagement-%s".printf (iface);
+
+ success = this.get_mac_and_network_type (iface, out mac_address, out type);
+
+ var mac = mac_address.replace (":", "");
+
+ var wake_pattern = "FFFFFFFFFFFF%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s".printf (
+ mac, mac, mac, mac, mac, mac, mac, mac,
+ mac, mac, mac, mac, mac, mac, mac, mac);
+ try {
+ var password = this.config.get_string (config_section, "password");
+ wake_pattern = wake_pattern.concat (password);
+ } catch (GLib.Error error) { }
+
+ var ip_addr = new InetAddress.from_string (this.root_device.context.host_ip);
+ bool is_ipv6 = (ip_addr != null && ip_addr.family == SocketFamily.IPV6);
+ var associated_ips = "<Ipv%d>%s</Ipv%d>".printf
+ (is_ipv6 ? 6 : 4,
+ this.root_device.context.host_ip,
+ is_ipv6 ? 6 : 4);
+
+ string mode;
+ if (!success || this.upower == null) {
+ mode = "Unimplemented";
+ } else {
+ try {
+ /* Note: we want to check the value exists even when not sleeping */
+ var sleep_mode = this.config.get_string (config_section,
+ "mode-on-suspend");
+ mode = this.sleeping ? sleep_mode : "IP-up";
+ } catch (GLib.Error error) {
+ mode = "Unimplemented";
+ }
+ }
+
+ string transport_node;
+ try {
+ var val = this.config.get_string (config_section,
+ "supported-transport");
+ transport_node = "<WakeSupportedTransport>%s</WakeSupportedTransport>".printf
+ (val);
+ } catch (GLib.Error error) {
+ transport_node = "";
+ }
+
+ var device_info = ("<DeviceInterface>" +
+ "<DeviceUUID>%s</DeviceUUID>" +
+ "<FriendlyName>%s</FriendlyName>" +
+ "<NetworkInterface>" +
+ "<SystemName>%s</SystemName>" +
+ "<MacAddress>%s</MacAddress>" +
+ "<InterfaceType>%s</InterfaceType>" +
+ "<NetworkInterfaceMode>%s</NetworkInterfaceMode>" +
+ "<AssociatedIpAddresses>%s</AssociatedIpAddresses>" +
+ "<WakeOnPattern>%s</WakeOnPattern>" +
+ "%s" +
+ "</NetworkInterface>" +
+ "</DeviceInterface>").printf
+ (this.root_device.udn,
+ this.root_device.get_friendly_name (),
+ iface,
+ mac_address,
+ type,
+ mode,
+ associated_ips,
+ wake_pattern,
+ transport_node);
+
+ return TEMPLATE.printf (device_info);
+ }
+
+ private string create_proxied_network_interface_info () {
+ /* No proxy support: Return empty NetworkInterfaceInfo */
+
+ return TEMPLATE.printf ("");
+ }
+
+ private void query_network_interface_info_cb (Service em,
+ string var,
+ ref Value val) {
+ val.init (typeof (string));
+ val.set_string (this.create_network_interface_info ());
+ }
+
+ private void query_proxied_network_interface_info_cb (Service em,
+ string var,
+ ref Value val) {
+ val.init (typeof (string));
+ val.set_string (this.create_proxied_network_interface_info ());
+ }
+
+ private void get_interface_info_cb (Service em,
+ ServiceAction action) {
+ if (action.get_argument_count () != 0) {
+ action.return_error (402, _("Invalid argument"));
+
+ return;
+ }
+
+ action.set ("NetworkInterfaceInfo",
+ typeof (string),
+ this.create_network_interface_info ());
+
+ action.set ("ProxiedNetworkInterfaceInfo",
+ typeof (string),
+ this.create_proxied_network_interface_info ());
+
+ action.return ();
+ }
+}
diff --git a/src/librygel-core/rygel-plugin.vala b/src/librygel-core/rygel-plugin.vala
index ddaa842..4059cc7 100644
--- a/src/librygel-core/rygel-plugin.vala
+++ b/src/librygel-core/rygel-plugin.vala
@@ -194,6 +194,23 @@ public class Rygel.Plugin : GUPnP.ResourceFactory {
ICON_SMALL_WIDTH,
ICON_SMALL_HEIGHT,
ICON_JPG_DEPTH);
+
+ /* Enable EnergyManagement service on this device if needed */
+ var config = MetaConfig.get_default ();
+ try {
+ if (config.get_bool (this.name, "energy-management")) {
+ var resource = new ResourceInfo (EnergyManagement.UPNP_ID,
+ EnergyManagement.UPNP_TYPE,
+ EnergyManagement.DESCRIPTION_PATH,
+ typeof (EnergyManagement));
+ this.add_resource (resource);
+
+ }
+ } catch (GLib.Error error) {
+ if (!(error is ConfigurationError.NO_VALUE_SET))
+ warning ("Failed to read configuration: %s", error.message);
+ }
+
}
public void add_resource (ResourceInfo resource_info) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]