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