Merge pull request #473 from Montellese/onplaybackspeedchanged
[vuplus_xbmc] / xbmc / storage / linux / DeviceKitDisksProvider.cpp
1 /*
2  *      Copyright (C) 2005-2009 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21 #include "DeviceKitDisksProvider.h"
22 #ifdef HAS_DBUS
23 #include "settings/AdvancedSettings.h"
24 #include "guilib/LocalizeStrings.h"
25 #include "utils/log.h"
26 #include "utils/URIUtils.h"
27 #include "PosixMountProvider.h"
28
29 void CDeviceKitDiskDeviceOldAPI::Update()
30 {
31   CVariant properties = CDBusUtil::GetAll("org.freedesktop.DeviceKit.Disks", m_DeviceKitUDI.c_str(), "org.freedesktop.DeviceKit.Disks.Device");
32
33   m_isFileSystem = CStdString(properties["id-usage"].asString()) == "filesystem";
34   if (m_isFileSystem)
35   {
36     m_UDI         = properties["id-uuid"].asString();
37     m_Label       = properties["id-label"].asString();
38     m_FileSystem  = properties["id-type"].asString();
39   }
40   else
41   {
42     m_UDI.clear();
43     m_Label.clear();
44     m_FileSystem.clear();
45   }
46   m_isMounted   = properties["device-is-mounted"].asBoolean();
47   if (m_isMounted && properties["device-mount-paths"].size() > 0)
48     m_MountPath   = properties["device-mount-paths"][0].asString();
49   else
50     m_MountPath.clear();
51
52   m_PartitionSizeGiB = properties["partition-size"].asUnsignedInteger() / 1024.0 / 1024.0 / 1024.0;
53   m_isPartition = properties["device-is-partition"].asBoolean();
54   m_isSystemInternal = properties["device-is-system-internal"].asBoolean();
55   m_isOptical = properties["device-is-optical-disc"].asBoolean();
56   if (m_isPartition)
57     m_isRemovable = CDBusUtil::GetVariant("org.freedesktop.DeviceKit.Disks", properties["partition-slave"].asString().c_str(), "org.freedesktop.DeviceKit.Disks.Device", "device-is-removable").asBoolean();
58   else
59     m_isRemovable = properties["device-is-removable"].asBoolean();
60 }
61
62 void CDeviceKitDiskDeviceNewAPI::Update()
63 {
64   CVariant properties = CDBusUtil::GetAll("org.freedesktop.DeviceKit.Disks", m_DeviceKitUDI.c_str(), "org.freedesktop.DeviceKit.Disks.Device");
65
66   m_isFileSystem = CStdString(properties["IdUsage"].asString()) == "filesystem";
67   if (m_isFileSystem)
68   {
69     m_UDI         = properties["IdUuid"].asString();
70     m_Label       = properties["IdLabel"].asString();
71     m_FileSystem  = properties["IdType"].asString();
72   }
73   else
74   {
75     m_UDI.clear();
76     m_Label.clear();
77     m_FileSystem.clear();
78   }
79
80   m_isMounted   = properties["DeviceIsMounted"].asBoolean();
81   if (m_isMounted && properties["DeviceMountPaths"].size() > 0)
82     m_MountPath   = properties["DeviceMountPaths"][0].asString();
83   else
84     m_MountPath.clear();
85
86   m_PartitionSizeGiB = properties["PartitionSize"].asUnsignedInteger() / 1024.0 / 1024.0 / 1024.0;
87   m_isPartition = properties["DeviceIsPartition"].asBoolean();
88   m_isSystemInternal = properties["DeviceIsSystemInternal"].asBoolean();
89   m_isOptical = properties["DeviceIsOpticalDisc"].asBoolean();
90   if (m_isPartition)
91     m_isRemovable = CDBusUtil::GetVariant("org.freedesktop.DeviceKit.Disks", properties["PartitionSlave"].asString().c_str(), "org.freedesktop.DeviceKit.Disks.Device", "DeviceIsRemovable").asBoolean();
92   else
93     m_isRemovable = properties["DeviceIsRemovable"].asBoolean();
94 }
95
96 CDeviceKitDiskDevice::CDeviceKitDiskDevice(const char *DeviceKitUDI)
97 {
98   m_DeviceKitUDI = DeviceKitUDI;
99   m_UDI = "";
100   m_MountPath = "";
101   m_FileSystem = "";
102   m_isMounted = false;
103   m_isMountedByUs = false;
104   m_isRemovable = false;
105   m_isPartition = false;
106   m_isFileSystem = false;
107   m_isSystemInternal = false;
108   m_isOptical = false;
109   m_PartitionSizeGiB = 0.0f;
110 }
111
112 bool CDeviceKitDiskDevice::Mount()
113 {
114   if (!m_isMounted && !m_isSystemInternal && m_isFileSystem)
115   {
116     CLog::Log(LOGDEBUG, "DeviceKit.Disks: Mounting %s", m_DeviceKitUDI.c_str());
117     CDBusMessage message("org.freedesktop.DeviceKit.Disks", m_DeviceKitUDI.c_str(), "org.freedesktop.DeviceKit.Disks.Device", "FilesystemMount");
118     message.AppendArgument("");
119     const char *array[] = {};
120     message.AppendArgument(array, 0);
121
122     DBusMessage *reply = message.SendSystem();
123     if (reply)
124     {
125       char *mountPoint;
126       if (dbus_message_get_args (reply, NULL, DBUS_TYPE_STRING, &mountPoint, DBUS_TYPE_INVALID))
127       {
128         m_MountPath = mountPoint;
129         CLog::Log(LOGDEBUG, "DeviceKit.Disks: Sucessfully mounted %s on %s", m_DeviceKitUDI.c_str(), mountPoint);
130         m_isMountedByUs = m_isMounted = true;
131       }
132     }
133
134     return m_isMounted;
135   }
136   else
137     CLog::Log(LOGDEBUG, "DeviceKit.Disks: Is not able to mount %s", toString().c_str());
138
139   return false;
140 }
141
142 bool CDeviceKitDiskDevice::UnMount()
143 {
144   if (m_isMounted && !m_isSystemInternal && m_isFileSystem)
145   {
146     CDBusMessage message("org.freedesktop.DeviceKit.Disks", m_DeviceKitUDI.c_str(), "org.freedesktop.DeviceKit.Disks.Device", "FilesystemUnmount");
147
148     const char *array[1];
149     message.AppendArgument(array, 0);
150
151     DBusMessage *reply = message.SendSystem();
152     if (reply)
153       m_isMountedByUs = m_isMounted = false;
154
155     return !m_isMounted;
156   }
157   else
158     CLog::Log(LOGDEBUG, "DeviceKit.Disks: Is not able to unmount %s", toString().c_str());
159
160   return false;
161 }
162
163 CMediaSource CDeviceKitDiskDevice::ToMediaShare()
164 {
165   CMediaSource source;
166   source.strPath = m_MountPath;
167   if (m_Label.empty())
168     source.strName.Format("%.1f GB %s", m_PartitionSizeGiB, g_localizeStrings.Get(155).c_str());
169   else
170     source.strName = m_Label;
171   if (m_isOptical)
172     source.m_iDriveType = CMediaSource::SOURCE_TYPE_DVD;
173   else if (m_isSystemInternal)
174     source.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL;
175   else
176     source.m_iDriveType = CMediaSource::SOURCE_TYPE_REMOVABLE;
177   source.m_ignore = true;
178   return source;
179 }
180
181 bool CDeviceKitDiskDevice::IsApproved()
182 {
183   return (m_isFileSystem && m_isMounted && m_UDI.length() > 0 && (m_FileSystem.length() > 0 && !m_FileSystem.Equals("swap")) && !m_MountPath.Equals("/")) || m_isOptical;
184 }
185
186 #define BOOL2SZ(b) ((b) ? "true" : "false")
187
188 CStdString CDeviceKitDiskDevice::toString()
189 {
190   CStdString str;
191   str.Format("DeviceUDI %s: IsFileSystem %s HasFileSystem %s "
192       "IsSystemInternal %s IsMounted %s IsRemovable %s IsPartition %s "
193       "IsOptical %s",
194       m_DeviceKitUDI.c_str(), BOOL2SZ(m_isFileSystem), m_FileSystem,
195       BOOL2SZ(m_isSystemInternal), BOOL2SZ(m_isMounted),
196       BOOL2SZ(m_isRemovable), BOOL2SZ(m_isPartition), BOOL2SZ(m_isOptical));
197
198   return str;
199 }
200
201 CDeviceKitDisksProvider::CDeviceKitDisksProvider()
202 {
203   dbus_error_init (&m_error);
204   // TODO: do not use dbus_connection_pop_message() that requires the use of a
205   // private connection
206   m_connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &m_error);
207   dbus_connection_set_exit_on_disconnect(m_connection, false);
208
209   dbus_bus_add_match(m_connection, "type='signal',interface='org.freedesktop.DeviceKit.Disks'", &m_error);
210   dbus_connection_flush(m_connection);
211   if (dbus_error_is_set(&m_error))
212   {
213     CLog::Log(LOGERROR, "DeviceKit.Disks: Failed to attach to signal %s", m_error.message);
214     dbus_connection_close(m_connection);
215     dbus_connection_unref(m_connection);
216     m_connection = NULL;
217   }
218 }
219
220 CDeviceKitDisksProvider::~CDeviceKitDisksProvider()
221 {
222   DeviceMap::iterator itr;
223
224   for (itr = m_AvailableDevices.begin(); itr != m_AvailableDevices.end(); ++itr)
225     delete m_AvailableDevices[itr->first];
226
227   m_AvailableDevices.clear();
228
229   if (m_connection)
230   {
231     dbus_connection_close(m_connection);
232     dbus_connection_unref(m_connection);
233     m_connection = NULL;
234   }
235
236   dbus_error_free (&m_error);
237 }
238
239 void CDeviceKitDisksProvider::Initialize()
240 {
241   CLog::Log(LOGDEBUG, "Selected DeviceKit.Disks as storage provider");
242   m_DaemonVersion = atoi(CDBusUtil::GetVariant("org.freedesktop.DeviceKit.Disks", "/org/freedesktop/DeviceKit/Disks", "org.freedesktop.DeviceKit.Disks", "DaemonVersion").asString().c_str());
243   CLog::Log(LOGDEBUG, "DeviceKit.Disks: DaemonVersion %i", m_DaemonVersion);
244
245   CLog::Log(LOGDEBUG, "DeviceKit.Disks: Querying available devices");
246   std::vector<CStdString> devices = EnumerateDisks();
247   for (unsigned int i = 0; i < devices.size(); i++)
248     DeviceAdded(devices[i].c_str(), NULL);
249 }
250
251 bool CDeviceKitDisksProvider::Eject(CStdString mountpath)
252 {
253   DeviceMap::iterator itr;
254   CStdString path(mountpath);
255   URIUtils::RemoveSlashAtEnd(path);
256
257   for (itr = m_AvailableDevices.begin(); itr != m_AvailableDevices.end(); ++itr)
258   {
259     CDeviceKitDiskDevice *device = itr->second;
260     if (device->m_MountPath.Equals(path))
261       return device->UnMount();
262   }
263
264   return false;
265 }
266
267 std::vector<CStdString> CDeviceKitDisksProvider::GetDiskUsage()
268 {
269   CPosixMountProvider legacy;
270   return legacy.GetDiskUsage();
271 }
272
273 bool CDeviceKitDisksProvider::PumpDriveChangeEvents(IStorageEventsCallback *callback)
274 {
275   bool result = false;
276   if (m_connection)
277   {
278     dbus_connection_read_write(m_connection, 0);
279     DBusMessage *msg = dbus_connection_pop_message(m_connection);
280
281     if (msg)
282     {
283       char *object;
284       if (dbus_message_get_args (msg, NULL, DBUS_TYPE_OBJECT_PATH, &object, DBUS_TYPE_INVALID))
285       {
286         result = true;
287         if (dbus_message_is_signal(msg, "org.freedesktop.DeviceKit.Disks", "DeviceAdded"))
288           DeviceAdded(object, callback);
289         else if (dbus_message_is_signal(msg, "org.freedesktop.DeviceKit.Disks", "DeviceRemoved"))
290           DeviceRemoved(object, callback);
291         else if (dbus_message_is_signal(msg, "org.freedesktop.DeviceKit.Disks", "DeviceChanged"))
292           DeviceChanged(object, callback);
293       }
294       dbus_message_unref(msg);
295     }
296   }
297   return result;
298 }
299
300 bool CDeviceKitDisksProvider::HasDeviceKitDisks()
301 {
302   bool hasDeviceKitDisks = false;
303   CDBusMessage message("org.freedesktop.DeviceKit.Disks", "/org/freedesktop/DeviceKit/Disks", "org.freedesktop.DeviceKit.Disks", "EnumerateDevices");
304
305   DBusError error;
306   dbus_error_init (&error);
307   DBusConnection *con = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
308
309   if (con)
310     message.Send(con, &error);
311
312   if (!dbus_error_is_set(&error))
313     hasDeviceKitDisks = true;
314   else
315     CLog::Log(LOGDEBUG, "DeviceKit.Disks: %s - %s", error.name, error.message);
316
317   dbus_error_free (&error);
318   if (con)
319     dbus_connection_unref(con);
320
321   return hasDeviceKitDisks;
322 }
323
324 void CDeviceKitDisksProvider::DeviceAdded(const char *object, IStorageEventsCallback *callback)
325 {
326   CLog::Log(LOGDEBUG, "DeviceKit.Disks: DeviceAdded (%s)", object);
327
328   if (m_AvailableDevices[object])
329   {
330     CLog::Log(LOGWARNING, "DeviceKit.Disks: Inconsistency found! DeviceAdded on an indexed disk");
331     delete m_AvailableDevices[object];
332   }
333
334   CDeviceKitDiskDevice *device = NULL;
335   if (m_DaemonVersion >= 7)
336     device = new CDeviceKitDiskDeviceNewAPI(object);
337   else
338     device = new CDeviceKitDiskDeviceOldAPI(object);
339   m_AvailableDevices[object] = device;
340
341   if (g_advancedSettings.m_handleMounting)
342     device->Mount();
343
344   CLog::Log(LOGDEBUG, "DeviceKit.Disks: DeviceAdded - %s", device->toString().c_str());
345   if (device->m_isMounted && device->IsApproved())
346   {
347     CLog::Log(LOGNOTICE, "DeviceKit.Disks: Added %s", device->m_MountPath.c_str());
348     if (callback)
349       callback->OnStorageAdded(device->m_Label, device->m_MountPath);
350   }
351 }
352
353 void CDeviceKitDisksProvider::DeviceRemoved(const char *object, IStorageEventsCallback *callback)
354 {
355   CLog::Log(LOGDEBUG, "DeviceKit.Disks: DeviceRemoved (%s)", object);
356
357   CDeviceKitDiskDevice *device = m_AvailableDevices[object];
358   if (device)
359   {
360     if (device->m_isMounted && callback)
361       callback->OnStorageUnsafelyRemoved(device->m_Label);
362
363     delete m_AvailableDevices[object];
364     m_AvailableDevices.erase(object);
365   }
366 }
367
368 void CDeviceKitDisksProvider::DeviceChanged(const char *object, IStorageEventsCallback *callback)
369 {
370   CLog::Log(LOGDEBUG, "DeviceKit.Disks: DeviceChanged (%s)", object);
371
372   CDeviceKitDiskDevice *device = m_AvailableDevices[object];
373   if (device == NULL)
374   {
375     CLog::Log(LOGWARNING, "DeviceKit.Disks: Inconsistency found! DeviceChanged on an unindexed disk");
376     DeviceAdded(object, callback);
377   }
378   else
379   {
380     bool mounted = device->m_isMounted;
381     device->Update();
382     if (!mounted && device->m_isMounted && callback)
383       callback->OnStorageAdded(device->m_Label, device->m_MountPath);
384     else if (mounted && !device->m_isMounted && callback)
385       callback->OnStorageSafelyRemoved(device->m_Label);
386
387     CLog::Log(LOGDEBUG, "DeviceKit.Disks: DeviceChanged - %s", device->toString().c_str());
388   }
389 }
390
391 std::vector<CStdString> CDeviceKitDisksProvider::EnumerateDisks()
392 {
393   std::vector<CStdString> devices;
394   CDBusMessage message("org.freedesktop.DeviceKit.Disks", "/org/freedesktop/DeviceKit/Disks", "org.freedesktop.DeviceKit.Disks", "EnumerateDevices");
395   DBusMessage *reply = message.SendSystem();
396   if (reply)
397   {
398     char** disks  = NULL;
399     int    length = 0;
400
401     if (dbus_message_get_args (reply, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &disks, &length, DBUS_TYPE_INVALID))
402     {
403       for (int i = 0; i < length; i++)
404         devices.push_back(disks[i]);
405
406       dbus_free_string_array(disks);
407     }
408   }
409
410   return devices;
411 }
412
413 void CDeviceKitDisksProvider::GetDisks(VECSOURCES& devices, bool EnumerateRemovable)
414 {
415   DeviceMap::iterator itr;
416
417   for (itr = m_AvailableDevices.begin(); itr != m_AvailableDevices.end(); ++itr)
418   {
419     CDeviceKitDiskDevice *device = itr->second;
420     if (device && device->IsApproved() && device->m_isSystemInternal != EnumerateRemovable)
421       devices.push_back(device->ToMediaShare());
422   }
423 }
424 #endif