Merge pull request #5039 from CEikermann/patch-1
[vuplus_xbmc] / xbmc / linux / HALManager.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://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
21 #include "system.h"
22 #ifdef HAS_HAL
23 #include "HALManager.h"
24 #include "interfaces/Builtins.h"
25 #include <libhal-storage.h>
26 #include "threads/SingleLock.h"
27 #include "utils/URIUtils.h"
28 #include "guilib/LocalizeStrings.h"
29 #include "powermanagement/PowerManager.h"
30 #include "settings/AdvancedSettings.h"
31 #include "dialogs/GUIDialogKaiToast.h"
32
33 #ifdef HAS_SDL_JOYSTICK
34 #include <SDL/SDL.h>
35 #include <SDL/SDL_version.h>
36 #include "input/SDLJoystick.h"
37 #endif
38
39 bool CHALManager::NewMessage;
40 DBusError CHALManager::m_Error;
41 CCriticalSection CHALManager::m_lock;
42
43 /* A Removed device, It isn't possible to make a LibHalVolume from a removed device therefor
44    we catch the UUID from the udi on the removal */
45 void CHALManager::DeviceRemoved(LibHalContext *ctx, const char *udi)
46 {
47   NewMessage = true;
48   CLog::Log(LOGDEBUG, "HAL: Device (%s) Removed", udi);
49   g_HalManager.RemoveDevice(udi);
50 }
51
52 void CHALManager::DeviceNewCapability(LibHalContext *ctx, const char *udi, const char *capability)
53 {
54   NewMessage = true;
55   CLog::Log(LOGDEBUG, "HAL: Device (%s) gained capability %s", udi, capability);
56   g_HalManager.UpdateDevice(udi);
57 }
58
59 void CHALManager::DeviceLostCapability(LibHalContext *ctx, const char *udi, const char *capability)
60 {
61   NewMessage = true;
62   CLog::Log(LOGDEBUG, "HAL: Device (%s) lost capability %s", udi, capability);
63   g_HalManager.UpdateDevice(udi);
64 }
65
66 /* HAL Property modified callback. If a device is mounted. This is called. */
67 void CHALManager::DevicePropertyModified(LibHalContext *ctx, const char *udi, const char *key, dbus_bool_t is_removed, dbus_bool_t is_added)
68 {
69   NewMessage = true;
70   CLog::Log(LOGDEBUG, "HAL: Device (%s) Property %s modified", udi, key);
71   g_HalManager.UpdateDevice(udi);
72 }
73
74 void CHALManager::DeviceCondition(LibHalContext *ctx, const char *udi, const char *condition_name, const char *condition_details)
75 {
76   NewMessage = true;
77   CLog::Log(LOGDEBUG, "HAL: Device (%s) Condition %s | %s", udi, condition_name, condition_details);
78   if (!strcmp(condition_name, "ButtonPressed") && !strcmp(condition_details, "power"))
79     CBuiltins::Execute("XBMC.ShutDown()");
80   else
81     g_HalManager.UpdateDevice(udi);
82 }
83
84 /* HAL Device added. This is before mount. And here is the place to mount the volume in the future */
85 void CHALManager::DeviceAdded(LibHalContext *ctx, const char *udi)
86 {
87   NewMessage = true;
88   CLog::Log(LOGDEBUG, "HAL: Device (%s) Added", udi);
89   g_HalManager.AddDevice(udi);
90 }
91
92 CHALManager g_HalManager;
93
94 /* Iterate through all devices currently on the computer. Needed mostly at startup */
95 void CHALManager::GenerateGDL()
96 {
97   if (m_Context == NULL)
98     return;
99
100   char **GDL;
101   int i = 0;
102   CLog::Log(LOGDEBUG, "HAL: Clearing old global device list, if any");
103   m_Volumes.clear();
104
105   CLog::Log(LOGNOTICE, "HAL: Generating global device list");
106   GDL = libhal_get_all_devices(g_HalManager.m_Context, &i, &m_Error);
107
108   for (i = 0; GDL[i]; i++)
109   {
110     AddDevice(GDL[i]);
111   }
112   CLog::Log(LOGINFO, "HAL: Generated global device list, found %i", i);
113
114   libhal_free_string_array(GDL);
115 }
116
117 // Return all volumes that currently are available (Mostly needed at startup, the rest of the volumes comes as events.)
118 std::vector<CStorageDevice> CHALManager::GetVolumeDevices()
119 {
120   CSingleLock lock(m_lock);
121   return m_Volumes;
122 }
123
124 CHALManager::CHALManager()
125 {
126   m_Notifications = false;
127   m_Context = NULL;
128   m_DBusSystemConnection = NULL;
129 #if defined(HAS_SDL_JOYSTICK)
130   const SDL_version *sdl_version = SDL_Linked_Version();
131   m_bMultipleJoysticksSupport = (sdl_version->major >= 1 && sdl_version->minor >= 3)?true:false;
132 #endif
133 }
134
135 void CHALManager::Stop()
136 {
137   if (g_advancedSettings.m_handleMounting)
138   { // Unmount all media XBMC have mounted
139     for (unsigned int i = 0; i < m_Volumes.size(); i++)
140     {
141       if (m_Volumes[i].MountedByXBMC && m_Volumes[i].Mounted)
142       {
143         CLog::Log(LOGNOTICE, "HAL: Unmounts %s", m_Volumes[i].FriendlyName.c_str());
144         UnMount(m_Volumes[i]);
145       }
146     }
147   }
148
149   m_Volumes.clear();
150
151   if (m_Context != NULL)
152     libhal_ctx_shutdown(m_Context, NULL);
153   if (m_Context != NULL)
154     libhal_ctx_free(m_Context);
155
156   if (m_DBusSystemConnection != NULL)
157   {
158     dbus_connection_unref(m_DBusSystemConnection);
159     m_DBusSystemConnection = NULL;
160   }
161   dbus_error_free(&m_Error); // Needed?
162 }
163
164 // Initialize
165 void CHALManager::Initialize()
166 {
167   m_Notifications = false;
168   CLog::Log(LOGINFO, "HAL: Starting initializing");
169   g_HalManager.m_Context = g_HalManager.InitializeHal();
170   if (g_HalManager.m_Context == NULL)
171   {
172     CLog::Log(LOGERROR, "HAL: no Hal context");
173     return;
174   }
175
176   GenerateGDL();
177
178   CLog::Log(LOGINFO, "HAL: Successfully initialized");
179   m_Notifications = true;
180 }
181
182 // Initialize basic DBus connection
183 bool CHALManager::InitializeDBus()
184 {
185   if (m_DBusSystemConnection != NULL)
186     return true;
187
188   dbus_error_init (&m_Error);
189   if (m_DBusSystemConnection == NULL && !(m_DBusSystemConnection = dbus_bus_get (DBUS_BUS_SYSTEM, &m_Error)))
190   {
191     CLog::Log(LOGERROR, "DBus: Could not get system bus: %s", m_Error.message);
192     dbus_error_free (&m_Error);
193   }
194
195   if (m_DBusSystemConnection != NULL)
196     return true;
197   else
198     return false;
199 }
200
201 // Initialize basic HAL connection
202 LibHalContext *CHALManager::InitializeHal()
203 {
204   LibHalContext *ctx;
205   char **devices;
206   int nr;
207
208   if (!InitializeDBus())
209     return NULL;
210
211   if (!(ctx = libhal_ctx_new()))
212   {
213     CLog::Log(LOGERROR, "HAL: failed to create a HAL context!");
214     return NULL;
215   }
216
217   if (!libhal_ctx_set_dbus_connection(ctx, m_DBusSystemConnection))
218     CLog::Log(LOGERROR, "HAL: Failed to connect with dbus");
219
220   libhal_ctx_set_device_added(ctx, DeviceAdded);
221   libhal_ctx_set_device_removed(ctx, DeviceRemoved);
222   libhal_ctx_set_device_new_capability(ctx, DeviceNewCapability);
223   libhal_ctx_set_device_lost_capability(ctx, DeviceLostCapability);
224   libhal_ctx_set_device_property_modified(ctx, DevicePropertyModified);
225   libhal_ctx_set_device_condition(ctx, DeviceCondition);
226
227   if (!libhal_device_property_watch_all(ctx, &m_Error))
228   {
229     CLog::Log(LOGERROR, "HAL: Failed to set property watch %s", m_Error.message);
230     dbus_error_free(&m_Error);
231     libhal_ctx_free(ctx);
232     return NULL;
233   }
234
235   if (!libhal_ctx_init(ctx, &m_Error))
236   {
237     CLog::Log(LOGERROR, "HAL: Failed to initialize hal context: %s", m_Error.message);
238     dbus_error_free(&m_Error);
239     libhal_ctx_free(ctx);
240     return NULL;
241   }
242
243   /*
244  * Do something to ping the HAL daemon - the above functions will
245  * succeed even if hald is not running, so long as DBUS is.  But we
246  * want to exit silently if hald is not running, to behave on
247  * pre-2.6 systems.
248  */
249   if (!(devices = libhal_get_all_devices(ctx, &nr, &m_Error)))
250   {
251     CLog::Log(LOGERROR, "HAL: seems that Hal daemon is not running: %s", m_Error.message);
252     dbus_error_free(&m_Error);
253
254     libhal_ctx_shutdown(ctx, NULL);
255     libhal_ctx_free(ctx);
256     return NULL;
257   }
258
259   libhal_free_string_array(devices);
260
261   return ctx;
262 }
263
264 // Helper function. creates a CStorageDevice from a HAL udi
265 bool CHALManager::DeviceFromVolumeUdi(const char *udi, CStorageDevice *device)
266 {
267   if (g_HalManager.m_Context == NULL)
268     return false;
269
270   LibHalVolume *tempVolume;
271   LibHalDrive  *tempDrive;
272   bool Created = false;
273
274   tempVolume = libhal_volume_from_udi(g_HalManager.m_Context, udi);
275   if (tempVolume)
276   {
277     const char *DriveUdi = libhal_volume_get_storage_device_udi(tempVolume);
278     tempDrive = libhal_drive_from_udi(g_HalManager.m_Context, DriveUdi);
279
280     if (tempDrive)
281     {
282       char * FriendlyName   = libhal_device_get_property_string(g_HalManager.m_Context, udi, "info.product", NULL);
283       device->FriendlyName  = FriendlyName;
284       libhal_free_string(FriendlyName);
285       char *block = libhal_device_get_property_string(g_HalManager.m_Context, udi, "block.device", NULL);
286       device->DevID         = block;
287       libhal_free_string(block);
288
289       device->HotPlugged  = (bool)libhal_drive_is_hotpluggable(tempDrive);
290       device->Type        = libhal_drive_get_type(tempDrive);
291       device->Mounted     = (bool)libhal_volume_is_mounted(tempVolume);
292       device->MountPoint  = libhal_volume_get_mount_point(tempVolume);
293       if (device->Mounted)
294         URIUtils::AddSlashAtEnd(device->MountPoint);
295       device->Label       = libhal_volume_get_label(tempVolume);
296       device->UUID        = libhal_volume_get_uuid(tempVolume);
297       device->FileSystem  = libhal_volume_get_fstype(tempVolume);
298       device->HalIgnore   = libhal_device_get_property_bool(g_HalManager.m_Context, udi, "volume.ignore", NULL);
299       ApproveDevice(device);
300
301       libhal_drive_free(tempDrive);
302       Created = true;
303     }
304     else
305       CLog::Log(LOGERROR, "HAL: Couldn't create a Drive even if we had a volume - %s", udi);
306
307     libhal_volume_free(tempVolume);
308   }
309
310   return Created;
311 }
312
313 // Called from ProcessSlow to trigger the callbacks from DBus
314 bool CHALManager::Update()
315 {
316   CSingleLock lock(m_lock);
317   if (m_Context == NULL)
318     return false;
319
320   if (!dbus_connection_read_write_dispatch(m_DBusSystemConnection, 0)) // We choose 0 that means we won't wait for a message
321   {
322     CLog::Log(LOGERROR, "DBus: System - read/write dispatch");
323     return false;
324   }
325   if (NewMessage)
326   {
327     NewMessage = false;
328     return true;
329   }
330   else
331     return false;
332 }
333
334 /* libhal-storage type to readable form */
335 const char *CHALManager::StorageTypeToString(int DeviceType)
336 {
337   switch (DeviceType)
338   {
339   case 0:  return "removable disk";
340   case 1:  return "disk";
341   case 2:  return "cdrom";
342   case 3:  return "floppy";
343   case 4:  return "tape";
344   case 5:  return "compact flash";
345   case 6:  return "memory stick";
346   case 7:  return "smart media";
347   case 8:  return "sd mmc";
348   case 9:  return "camera";
349   case 10: return "audio player";
350   case 11: return "zip";
351   case 12: return "jaz";
352   case 13: return "flashkey";
353   case 14: return "magneto-optical";
354   default: return NULL;
355   }
356 }
357
358 /* Readable libhal-storage type to int type */
359 int CHALManager::StorageTypeFromString(const char *DeviceString)
360 {
361   if      (strcmp(DeviceString, "removable disk") == 0)  return 0;
362   else if (strcmp(DeviceString, "disk") == 0)            return 1;
363   else if (strcmp(DeviceString, "cdrom") == 0)           return 2;
364   else if (strcmp(DeviceString, "floppy") == 0)          return 3;
365   else if (strcmp(DeviceString, "tape") == 0)            return 4;
366   else if (strcmp(DeviceString, "compact flash") == 0)   return 5;
367   else if (strcmp(DeviceString, "memory stick") == 0)    return 6;
368   else if (strcmp(DeviceString, "smart media") == 0)     return 7;
369   else if (strcmp(DeviceString, "sd mmc") == 0)          return 8;
370   else if (strcmp(DeviceString, "camera") == 0)          return 9;
371   else if (strcmp(DeviceString, "audio player") == 0)    return 10;
372   else if (strcmp(DeviceString, "zip") == 0)             return 11;
373   else if (strcmp(DeviceString, "jaz") == 0)             return 12;
374   else if (strcmp(DeviceString, "flashkey") == 0)        return 13;
375   else if (strcmp(DeviceString, "magneto-optical") == 0) return 14;
376   return -1;
377 }
378
379 void CHALManager::UpdateDevice(const char *udi)
380 {
381   CSingleLock lock(m_lock);
382   char *category;
383   category = libhal_device_get_property_string(m_Context, udi, "info.category", NULL);
384   if (category == NULL)
385     return;
386
387   if (strcmp(category, "volume") == 0)
388   {
389     CStorageDevice dev(udi);
390     if (!DeviceFromVolumeUdi(udi, &dev))
391       return;
392     for (unsigned int i = 0; i < m_Volumes.size(); i++)
393     {
394       if (strcmp(m_Volumes[i].UDI.c_str(), udi) == 0)
395       {
396         CLog::Log(LOGDEBUG, "HAL: Update - %s | %s", CHALManager::StorageTypeToString(dev.Type),  dev.toString().c_str());
397         if (g_advancedSettings.m_handleMounting)  // If the device was mounted by XBMC before it's still mounted by XBMC.
398             dev.MountedByXBMC = m_Volumes[i].MountedByXBMC;
399         if (!dev.Mounted && m_Volumes[i].Mounted)
400           CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(13023), dev.FriendlyName.c_str(), TOAST_DISPLAY_TIME, false);
401         m_Volumes[i] = dev;
402
403         break;
404       }
405     }
406   }
407
408   libhal_free_string(category);
409 }
410 void CHALManager::HandleNewVolume(CStorageDevice *dev)
411 {
412   if (g_advancedSettings.m_handleMounting)
413   {
414 /* Here it can be checked if the device isn't mounted and then mount */
415 //TODO Have mountpoints be other than in /media/*
416     if (!dev->Mounted && (dev->HotPlugged || dev->Type == 2) && dev->Approved)
417     {
418       char **capability;
419       capability =libhal_device_get_property_strlist (m_Context, dev->UDI.c_str(), "info.capabilities", NULL);
420
421       bool Mountable = false;
422       if (dev->Type == 2 && (strcmp(capability[0], "volume.disc") == 0 && strcmp(capability[1], "volume") == 0)) // CD/DVD
423         Mountable = true;
424       else if ((strcmp(capability[0], "volume") == 0 && strcmp(capability[1], "block") == 0)) // HDD
425         Mountable = true;
426
427       if (Mountable)
428       {
429         CLog::Log(LOGNOTICE, "HAL: Trying to mount %s", dev->FriendlyName.c_str());
430         CStdString MountPoint;
431         CStdString TestPath;
432         if (dev->Label.size() > 0)
433         {
434           MountPoint = dev->Label.c_str();
435           TestPath = StringUtils::Format("/media/%s", MountPoint.c_str());
436           struct stat St;
437           if (stat("/media", &St) != 0)
438             return; //If /media doesn't exist something is wrong.
439           while(stat (TestPath.c_str(), &St) == 0 && S_ISDIR (St.st_mode))
440           {
441             CLog::Log(LOGDEBUG, "HAL: Proposed Mountpoint already existed");
442             MountPoint.append("_");
443             TestPath = StringUtils::Format("/media/%s", MountPoint.c_str());
444           }
445         }
446         else
447         {
448           MountPoint = StorageTypeToString(dev->Type);
449           TestPath = StringUtils::Format("/media/%s", MountPoint.c_str());
450           int Nbr = 0;
451           struct stat St;
452           if (stat("/media", &St) != 0)
453             return; //If /media doesn't exist something is wrong.
454           while(stat (TestPath.c_str(), &St) == 0 && S_ISDIR (St.st_mode))
455           {
456             CLog::Log(LOGDEBUG, "HAL: Proposed Mountpoint already existed");
457             Nbr++;
458             MountPoint = StringUtils::Format("%s%i", StorageTypeToString(dev->Type), Nbr);
459             TestPath = StringUtils::Format("/media/%s", MountPoint.c_str());
460           }
461         }
462         if (Mount(dev, MountPoint))
463         {
464           CLog::Log(LOGINFO, "HAL: mounted %s on %s", dev->FriendlyName.c_str(), dev->MountPoint.c_str());
465           if (m_Notifications)
466             CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(13021), dev->FriendlyName.c_str(), TOAST_DISPLAY_TIME, false);
467         }
468       }
469       libhal_free_string_array(capability);
470     }
471   }
472 }
473
474 /* Parse newly found device and add it to our remembered devices */
475 void CHALManager::AddDevice(const char *udi)
476 {
477   CSingleLock lock(m_lock);
478   char *category;
479   category = libhal_device_get_property_string(m_Context, udi, "info.category", NULL);
480   if (category == NULL)
481     return;
482
483   if (strcmp(category, "volume") == 0)
484   {
485     CStorageDevice dev(udi);
486     if (DeviceFromVolumeUdi(udi, &dev))
487     {
488       CLog::Log(LOGDEBUG, "HAL: Added - %s | %s", CHALManager::StorageTypeToString(dev.Type),  dev.toString().c_str());
489       HandleNewVolume(&dev);
490       m_Volumes.push_back(dev);
491     }
492   }
493 #if defined(HAS_SDL_JOYSTICK)
494   // Scan input devices
495   else if (strcmp(category, "input") == 0)
496   {
497     DBusError dbusError;
498     dbus_error_init(&dbusError);
499
500     char **capability;
501     capability =libhal_device_get_property_strlist (m_Context, udi, "info.capabilities", &dbusError);
502     for(char **ptr = capability; *ptr != NULL;ptr++)
503     {
504       // Reload joysticks
505       if(strcmp(*ptr, "input.joystick") == 0)
506       {
507         CLog::Log(LOGINFO, "HAL: Joystick plugged in");
508         CHALDevice dev = CHALDevice(udi);
509         dev.FriendlyName = libhal_device_get_property_string(m_Context, udi, "info.product", &m_Error);
510         m_Joysticks.push_back(dev);
511
512         if(m_Joysticks.size() < 2 || m_bMultipleJoysticksSupport)
513         {
514           // Restart SDL joystick subsystem
515           if (!g_Joystick.Reinitialize())
516             break;
517
518           if (m_Notifications)
519             CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(13024), dev.FriendlyName.c_str(), TOAST_DISPLAY_TIME, false);
520         }
521       }
522     }
523     libhal_free_string_array(capability);
524   }
525 #endif
526 /*
527   else if (strcmp(category, "camera") == 0)
528   { // PTP-Devices }
529   else if (strcmp(category, "bluetooth_hci") == 0)
530   { // Bluetooth-Devices }
531   else if (strcmp(category, "portable audio player") == 0)
532   { // MTP-Devices }
533   else if (strcmp(category, "alsa") == 0)
534   { //Alsa Devices }
535 */
536
537   libhal_free_string(category);
538 }
539
540 /* Here we should iterate through our remembered devices if any of them are removed */
541 bool CHALManager::RemoveDevice(const char *udi)
542 {
543   CSingleLock lock(m_lock);
544   for (unsigned int i = 0; i < m_Volumes.size(); i++)
545   {
546     if (strcmp(m_Volumes[i].UDI.c_str(), udi) == 0)
547     {
548       CLog::Log(LOGNOTICE, "HAL: Removed - %s | %s", CHALManager::StorageTypeToString(m_Volumes[i].Type), m_Volumes[i].toString().c_str());
549
550       if (m_Volumes[i].Mounted)
551       {
552         if (g_advancedSettings.m_handleMounting)
553           UnMount(m_Volumes[i]);
554         if (m_Notifications)
555           CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(13022), m_Volumes[i].FriendlyName.c_str());
556         CLog::Log(LOGNOTICE, "HAL: Unsafe drive removal");
557       }
558       m_Volumes.erase(m_Volumes.begin() + i);
559       return true;
560     }
561   }
562 #if defined(HAS_SDL_JOYSTICK)
563   for(uint i = 0; i < m_Joysticks.size(); i++)
564   {
565     if (strcmp(m_Joysticks[i].UDI.c_str(), udi) == 0)
566     {
567       if(m_Joysticks.size() < 3 || m_bMultipleJoysticksSupport)
568       {
569         // Restart SDL joystick subsystem
570         if (!g_Joystick.Reinitialize())
571           return false;
572
573         if (m_Notifications)
574           CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(13025), m_Joysticks[i].FriendlyName.c_str(), TOAST_DISPLAY_TIME, false);
575       }
576       m_Joysticks.erase(m_Joysticks.begin() + i);
577       return true;
578     }
579   }
580 #endif
581   return false;
582 }
583
584 bool CHALManager::ApproveDevice(CStorageDevice *device)
585 {
586   bool approve = true;
587   //This is only because it's easier to read...
588   const char *fs = device->FileSystem.c_str();
589
590   if ( strcmp(fs, "vfat") == 0    || strcmp(fs, "ext2") == 0
591        || strcmp(fs, "ext3") == 0 || strcmp(fs, "reiserfs") == 0
592        || strcmp(fs, "ntfs") == 0 || strcmp(fs, "ntfs-3g") == 0
593        || strcmp(fs, "udf") == 0  || strcmp(fs, "iso9660") == 0
594        || strcmp(fs, "xfs") == 0  || strcmp(fs, "hfsplus") == 0
595        || strcmp(fs, "ext4") == 0)
596     approve = true;
597   else
598     approve = false;
599
600   // Ignore some mountpoints, unless a weird setup these should never contain anything usefull for an enduser.
601   if (strcmp(device->MountPoint, "/") == 0 || strcmp(device->MountPoint, "/boot/") == 0 || strcmp(device->MountPoint, "/mnt/") == 0 || strcmp(device->MountPoint, "/home/") == 0)
602     approve = false;
603
604   if (device->HalIgnore)
605     approve = false;
606
607   device->Approved = approve;
608   return approve;
609 }
610
611 bool CHALManager::Eject(CStdString path)
612 {
613   for (unsigned int i = 0; i < m_Volumes.size(); i++)
614   {
615     if (m_Volumes[i].MountPoint.Equals(path))
616       return m_Volumes[i].HotPlugged ? UnMount(m_Volumes[i]) : false;
617   }
618
619   return false;
620 }
621
622 bool CHALManager::UnMount(CStorageDevice volume)
623 {
624   CLog::Log(LOGNOTICE, "HAL: UnMounting %s (%s)", volume.UDI.c_str(), volume.toString().c_str());
625   DBusMessage* msg;
626   DBusMessageIter args;
627   DBusError error;
628   dbus_error_init (&error);
629   DBusConnection *connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
630   if (connection)
631   {
632     msg = dbus_message_new_method_call("org.freedesktop.Hal", volume.UDI.c_str(), "org.freedesktop.Hal.Device.Volume", "Unmount");
633     dbus_message_iter_init_append(msg, &args);
634     DBusMessageIter sub;
635     dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &sub);
636     const char *s = "lazy";
637     dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &s);
638     dbus_message_iter_close_container(&args, &sub);
639
640     if (msg == NULL)
641         CLog::Log(LOGERROR, "DBus: Create UnMount Message failed");
642     else
643     {
644       DBusMessage *reply;
645       reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &error); //The reply timout might be bad to have as -1
646       if (dbus_error_is_set(&error))
647       {
648         CLog::Log(LOGERROR, "DBus: %s - %s", error.name, error.message);
649         dbus_error_free(&error);
650         return false;
651       }
652       // Need to create a reader for the Message
653       dbus_message_unref (reply);
654       dbus_message_unref(msg);
655       msg = NULL;
656     }
657
658     volume.MountPoint = "";
659     volume.Mounted    = false;
660     dbus_connection_unref(connection);
661     connection = NULL;
662     return true;
663   }
664   else
665   {
666     CLog::Log(LOGERROR, "DBus: Failed to connect to Systembus");
667     dbus_error_free(&error);
668     return false;
669   }
670 }
671
672 bool CHALManager::Mount(CStorageDevice *volume, CStdString mountpath)
673 {
674   CLog::Log(LOGNOTICE, "HAL: Mounting %s (%s) at %s with umask=%u", volume->UDI.c_str(), volume->toString().c_str(), mountpath.c_str(), umask (0));
675   DBusMessage* msg;
676   DBusMessageIter args;
677   DBusError error;
678   dbus_error_init (&error);
679   DBusConnection *connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
680   const char *s;
681   if (connection)
682   {
683     msg = dbus_message_new_method_call("org.freedesktop.Hal", volume->UDI.c_str(), "org.freedesktop.Hal.Device.Volume", "Mount");
684     dbus_message_iter_init_append(msg, &args);
685     s = mountpath.c_str();
686     if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &s))
687       CLog::Log(LOGERROR, "DBus: Failed to append arguments");
688     s = ""; //FileSystem
689     if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &s))
690       CLog::Log(LOGERROR, "DBus: Failed to append arguments");
691     DBusMessageIter sub;
692     dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &sub);
693
694     CStdString temporaryString;
695
696     if (volume->FileSystem.Equals("vfat"))
697     {
698       int mask = umask (0);
699       temporaryString = StringUtils::Format("umask=%#o", mask);
700       s = temporaryString.c_str();
701       dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &s);
702       temporaryString = StringUtils::Format("uid=%u", getuid());
703       s = temporaryString.c_str();
704       dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &s);
705       s = "shortname=mixed";
706       dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &s);
707       s = "utf8";
708       dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &s);
709       // 'sync' option will slow down transfer speed significantly for FAT filesystems. We prefer 'flush' instead.
710       s = "flush";
711       dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &s);
712     }
713     else
714     {
715       s = "sync";
716       dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &s);
717     }
718
719     dbus_message_iter_close_container(&args, &sub);
720
721     if (msg == NULL)
722         CLog::Log(LOGERROR, "DBus: Create Mount Message failed");
723     else
724     {
725       DBusMessage *reply;
726       reply = dbus_connection_send_with_reply_and_block(connection, msg, -1, &error); //The reply timout might be bad to have as -1
727       if (dbus_error_is_set(&error))
728       {
729         CLog::Log(LOGERROR, "DBus: %s - %s", error.name, error.message);
730         dbus_error_free(&error);
731         return false;
732       }
733       // Need to create a reader for the Message
734       dbus_message_unref (reply);
735       dbus_message_unref(msg);
736       msg = NULL;
737     }
738
739     volume->Mounted = true;
740     volume->MountedByXBMC = true;
741     volume->MountPoint = mountpath;
742     dbus_connection_unref(connection);
743     connection = NULL;
744     return true;
745   }
746   else
747   {
748     CLog::Log(LOGERROR, "DBus: Failed to connect to Systembus");
749     dbus_error_free(&error);
750     return false;
751   }
752 }
753 #endif // HAS_HAL