Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / media / MediaCD.cc
index 3976aef..8452736 100644 (file)
@@ -13,9 +13,17 @@ extern "C"
 {
 #include <sys/ioctl.h>
 #include <linux/cdrom.h>
+#if HAVE_UDEV
 #include <libudev.h>
+#endif
 }
 
+#ifndef HAVE_UDEV
+#if HAVE_HAL
+#include "zypp/target/hal/HalContext.h"
+#endif
+#endif
+
 #include <cstring> // strerror
 #include <cstdlib> // getenv
 #include <iostream>
@@ -33,7 +41,7 @@ extern "C"
 /*
 ** if to throw exception on eject errors or ignore them
 */
-#define  REPORT_EJECT_ERRORS     1
+#define  REPORT_EJECT_ERRORS     0
 
 /*
 ** If defined to the full path of the eject utility,
@@ -44,59 +52,180 @@ extern "C"
 
 using namespace std;
 
-namespace zypp {
-  namespace media {
+//////////////////////////////////////////////////////////////////
+namespace zypp
+{
+  //////////////////////////////////////////////////////////////////
+  namespace media
+  {
+
+    //////////////////////////////////////////////////////////////////
+    namespace
+    {
+      typedef std::list<MediaSource> DeviceList;
+
+      //////////////////////////////////////////////////////////////////
+      /// \brief Try to detect cd/dvd devices using hal/udev
+      ///
+      /// Returns an empty device list on error.
+      ///
+      /// \todo I took the code more or less as it was from MediaCD::detectDevices
+      /// into this function. Semantic between HAL and UDEV seems to be slightly
+      /// different, esp. in supportingDVD mode. This should be investigated and fixed.
+      //////////////////////////////////////////////////////////////////
+      DeviceList systemDetectDevices( bool supportingDVD_r )
+      {
+       DeviceList detected;
+
+#ifdef HAVE_UDEV
+       // http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/index.html
+       zypp::AutoDispose<struct udev *> udev( ::udev_new(), ::udev_unref );
+       if ( ! udev )
+       {
+         ERR << "Can't create udev context." << endl;
+         return DeviceList();
+       }
+
+       zypp::AutoDispose<struct udev_enumerate *> enumerate( ::udev_enumerate_new(udev), ::udev_enumerate_unref );
+       if ( ! enumerate )
+       {
+         ERR << "Can't create udev list entry." << endl;
+         return DeviceList();
+       }
+
+       ::udev_enumerate_add_match_subsystem( enumerate, "block" );
+       ::udev_enumerate_add_match_property( enumerate, "ID_CDROM", "1" );
+       ::udev_enumerate_scan_devices( enumerate );
+
+       struct udev_list_entry * entry = 0;
+       udev_list_entry_foreach( entry, ::udev_enumerate_get_list_entry( enumerate ) )
+       {
+         zypp::AutoDispose<struct udev_device *> device( ::udev_device_new_from_syspath( ::udev_enumerate_get_udev( enumerate ),
+                                                                                         ::udev_list_entry_get_name( entry ) ),
+                                                         ::udev_device_unref );
+         if ( ! device )
+         {
+           ERR << "Can't create udev device." << endl;
+           continue;
+         }
+
+         if ( supportingDVD_r && ! ::udev_device_get_property_value( device, "ID_CDROM_DVD" ) )
+         {
+           continue;   // looking for dvd only
+         }
+
+         const char * devnodePtr( ::udev_device_get_devnode( device ) );
+         if ( ! devnodePtr )
+         {
+           ERR << "Got NULL devicenode." << endl;
+           continue;
+         }
+
+         // In case we need it someday:
+         //const char * mountpath = ::udev_device_get_property_value( device, "FSTAB_DIR" );
+
+         PathInfo devnode( devnodePtr );
+         if ( devnode.isBlk() )
+         {
+           MediaSource media( "cdrom", devnode.path().asString(), devnode.devMajor(), devnode.devMinor() );
+           DBG << "Found (udev): " << media << std::endl;
+           detected.push_back( media );
+         }
+       }
+       if ( detected.empty() )
+       {
+         WAR << "Did not find any CD/DVD device." << endl;
+       }
+#elif HAVE_HAL
+       using namespace zypp::target::hal;
+       try
+       {
+         HalContext hal(true);
+
+         std::vector<std::string> drv_udis;
+         drv_udis = hal.findDevicesByCapability("storage.cdrom");
+
+         DBG << "Found " << drv_udis.size() << " cdrom drive udis" << std::endl;
+         for(size_t d = 0; d < drv_udis.size(); d++)
+         {
+           HalDrive drv( hal.getDriveFromUDI( drv_udis[d]));
+
+           if( drv)
+           {
+             bool supportsDVD=false;
+             if( supportingDVD_r)
+             {
+               std::vector<std::string> caps;
+               try {
+                 caps = drv.getCdromCapabilityNames();
+               }
+               catch(const HalException &e)
+               {
+                 ZYPP_CAUGHT(e);
+               }
+
+               std::vector<std::string>::const_iterator ci;
+               for( ci=caps.begin(); ci != caps.end(); ++ci)
+               {
+                 if( *ci == "dvd")
+                   supportsDVD = true;
+               }
+             }
+
+             MediaSource media("cdrom", drv.getDeviceFile(),
+                     drv.getDeviceMajor(),
+                     drv.getDeviceMinor());
+                 DBG << "Found " << drv_udis[d] << ": "
+                 << media.asString() << std::endl;
+                 if( supportingDVD_r && supportsDVD)
+                 {
+                   detected.push_front(media);
+                 }
+                 else
+                 {
+                   detected.push_back(media);
+                 }
+           }
+         }
+       }
+       catch(const zypp::target::hal::HalException &e)
+       {
+         ZYPP_CAUGHT(e);
+       }
+#endif
+       return detected;
+      }
 
-///////////////////////////////////////////////////////////////////
-//
-//  CLASS NAME : MediaCD
-//
-///////////////////////////////////////////////////////////////////
+    } // namespace
+    //////////////////////////////////////////////////////////////////
 
 
-  ///////////////////////////////////////////////////////////////////
-  //
-  //
-  //  METHOD NAME : MediaCD::MediaCD
-  //  METHOD TYPE : Constructor
-  //
-  //  DESCRIPTION :
-  //
-  MediaCD::MediaCD( const Url &      url_r,
-                    const Pathname & attach_point_hint_r )
-    : MediaHandler( url_r, attach_point_hint_r,
-                    url_r.getPathName(), // urlpath below attachpoint
-                    false ),
-      _lastdev(-1), _lastdev_tried(-1)
+  MediaCD::MediaCD( const Url & url_r, const Pathname & attach_point_hint_r )
+    : MediaHandler( url_r, attach_point_hint_r, url_r.getPathName(), false )
+    , _lastdev( -1 )
+    , _lastdev_tried( -1 )
   {
-    MIL << "MediaCD::MediaCD(" << url_r << ", " << attach_point_hint_r << ")"
-        << endl;
+    MIL << "MediaCD::MediaCD(" << url_r << ", " << attach_point_hint_r << ")" << endl;
 
-    if( url_r.getScheme() != "dvd" && url_r.getScheme() != "cd")
+    if ( url_r.getScheme() != "dvd" && url_r.getScheme() != "cd" )
     {
       ERR << "Unsupported schema in the Url: " << url_r.asString() << endl;
       ZYPP_THROW(MediaUnsupportedUrlSchemeException(_url));
     }
 
-    string devices = _url.getQueryParam("devices");
-    if (!devices.empty())
+    string devices = _url.getQueryParam( "devices" );
+    if ( ! devices.empty() )
     {
-      string::size_type pos;
-      DBG << "parse " << devices << endl;
-      while(!devices.empty())
+      std::vector<std::string> words;
+      str::split( devices, std::back_inserter(words), "," );
+      for ( const std::string & device : words )
       {
-        pos = devices.find(',');
-        string device = devices.substr(0,pos);
-        if (!device.empty())
-        {
-          MediaSource media("cdrom", device, 0, 0);
-          _devices.push_back( media);
-          DBG << "use device (delayed verify)" << device << endl;
-        }
-        if (pos!=string::npos)
-          devices=devices.substr(pos+1);
-        else
-          devices.erase();
+       if ( device.empty() )
+         continue;
+
+       MediaSource media( "cdrom", device, 0, 0 );
+       _devices.push_back( media );
+       DBG << "use device (delayed verify)" << device << endl;
       }
     }
     else
@@ -105,7 +234,7 @@ namespace zypp {
       return;
     }
 
-    if( _devices.empty())
+    if ( _devices.empty() )
     {
       ERR << "Unable to find any cdrom drive for " << _url.asString() << endl;
       ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
@@ -120,7 +249,7 @@ namespace zypp {
   //
   bool MediaCD::openTray( const std::string & device_r )
   {
-    int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
+    int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK|O_CLOEXEC );
     int res = -1;
 
     if ( fd != -1)
@@ -179,7 +308,7 @@ namespace zypp {
   //
   bool MediaCD::closeTray( const std::string & device_r )
   {
-    int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
+    int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK|O_CLOEXEC );
     if ( fd == -1 ) {
       WAR << "Unable to open '" << device_r << "' (" << ::strerror( errno ) << ")" << endl;
       return false;
@@ -194,68 +323,71 @@ namespace zypp {
     return true;
   }
 
-  ///////////////////////////////////////////////////////////////////
-  //
-  //
-  //  METHOD NAME : MediaCD::detectDevices
-  //  METHOD TYPE : MediaCD::DeviceList
-  //
-  MediaCD::DeviceList MediaCD::detectDevices( bool supportingDVD ) const
-  {
-    zypp::AutoDispose<struct udev *> udev( ::udev_new(), ::udev_unref );
-    if ( ! udev )
-    {
-      ERR << "Can't create udev context." << endl;
-      return DeviceList();
-    }
-
-    zypp::AutoDispose<struct udev_enumerate *> enumerate( ::udev_enumerate_new(udev), ::udev_enumerate_unref );
-    if ( ! enumerate )
-    {
-      ERR << "Can't create udev list entry." << endl;
-      return DeviceList();
-    }
 
-    ::udev_enumerate_add_match_subsystem( enumerate, "block" );
-    ::udev_enumerate_add_match_property( enumerate, "ID_CDROM", "1" );
-    ::udev_enumerate_scan_devices( enumerate );
+  MediaCD::DeviceList MediaCD::detectDevices( bool supportingDVD_r ) const
+  {
+    DeviceList detected( systemDetectDevices( supportingDVD_r ) );
 
-    DeviceList detected;
-    struct udev_list_entry * entry = 0;
-    udev_list_entry_foreach( entry, ::udev_enumerate_get_list_entry( enumerate ) )
+    if ( detected.empty() )
     {
-      zypp::AutoDispose<struct udev_device *> device( ::udev_device_new_from_syspath( ::udev_enumerate_get_udev( enumerate ),
-                                                                                     ::udev_list_entry_get_name( entry ) ),
-                                                     ::udev_device_unref );
-      if ( ! device )
+      WAR << "CD/DVD drive detection with HAL/UDEV failed! Guessing..." << std::endl;
+      PathInfo dvdinfo( "/dev/dvd" );
+      PathInfo cdrinfo( "/dev/cdrom" );
+      if ( dvdinfo.isBlk() )
       {
-       ERR << "Can't create udev device." << endl;
-       continue;
+       MediaSource media( "cdrom", dvdinfo.path().asString(), dvdinfo.devMajor(), dvdinfo.devMinor() );
+       DBG << "Found (GUESS): " << media << std::endl;
+       detected.push_back( media );
       }
-
-      if ( supportingDVD && ! ::udev_device_get_property_value( device, "ID_CDROM_DVD" ) )
+      if ( cdrinfo.isBlk()
+       && ! ( cdrinfo.devMajor() == dvdinfo.devMajor() && cdrinfo.devMinor() == dvdinfo.devMinor() ) )
       {
-       continue;       // looking for dvd only
+       MediaSource media( "cdrom", cdrinfo.path().asString(), cdrinfo.devMajor(), cdrinfo.devMinor() );
+       DBG << "Found (GUESS): " << media << std::endl;
+       detected.push_back( media );
       }
+    }
 
-      const char * devnodePtr( ::udev_device_get_devnode( device ) );
-      if ( ! devnodePtr )
+    // NOTE: On the fly build on-demand device list. Code was moved to
+    // here to get rid of code duplication, while keeping the ABI. Acuallty
+    // this code should be moved to a _devices accessor method.
+    if ( _devices.empty() )
+    {
+      DBG << "creating on-demand device list" << endl;
+      //default is /dev/cdrom; for dvd: /dev/dvd if it exists
+      string device( "/dev/cdrom" );
+      if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() )
       {
-       ERR << "Got NULL devicenode." << endl;
-       continue;
+        device = "/dev/dvd";
       }
 
-      // In case we need it someday:
-      //const char * mountpath = ::udev_device_get_property_value( device, "FSTAB_DIR" );
-
-      PathInfo devnode( devnodePtr );
-      if ( devnode.isBlk() )
+      PathInfo dinfo( device );
+      if ( dinfo.isBlk() )
       {
-       MediaSource media( "cdrom", devnode.path().asString(), devnode.major(), devnode.minor() );
-       DBG << "Found (udev): " << media << std::endl;
-       detected.push_back( media );
+       MediaSource media( "cdrom", device, dinfo.devMajor(), dinfo.devMinor() );
+       if ( detected.empty() )
+       {
+         _devices.push_front( media ); // better try this than nothing
+       }
+       else
+       {
+         for( const auto & d : detected )
+         {
+           // /dev/cdrom or /dev/dvd to the front
+           if ( media.equals( d ) )
+             _devices.push_front( d );
+           else
+             _devices.push_back( d );
+         }
+       }
+      }
+      else
+      {
+       // no /dev/cdrom or /dev/dvd link
+       _devices = detected;
       }
     }
+
     return detected;
   }
 
@@ -268,55 +400,26 @@ namespace zypp {
   //
   //  DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
   //
-  void MediaCD::attachTo(bool next)
+  void MediaCD::attachTo( bool next )
   {
     DBG << "next " << next << " last " << _lastdev << " last tried " << _lastdev_tried << endl;
-    if (next && _lastdev == -1)
+    if ( next && _lastdev == -1 )
       ZYPP_THROW(MediaNotSupportedException(url()));
 
-    DeviceList detected(
-      detectDevices(_url.getScheme() == "dvd" ? true : false));
+    // This also fills the _devices list on demand
+    DeviceList detected( detectDevices( _url.getScheme() == "dvd" ? true : false ) );
 
-    if(_devices.empty())
+    if( !isUseableAttachPoint( attachPoint() ) )
     {
-      DBG << "creating on-demand device list" << endl;
-      //default is /dev/cdrom; for dvd: /dev/dvd if it exists
-      string device( "/dev/cdrom" );
-      if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() )
-      {
-        device = "/dev/dvd";
-      }
-
-      PathInfo dinfo(device);
-      if( dinfo.isBlk())
-      {
-        MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
-
-        DeviceList::const_iterator d( detected.begin());
-        for( ; d != detected.end(); ++d)
-        {
-          // /dev/cdrom or /dev/dvd to the front
-          if( media.equals( *d))
-            _devices.push_front( *d);
-          else
-            _devices.push_back( *d);
-        }
-      }
-      else
-      {
-        // no /dev/cdrom or /dev/dvd link
-        _devices = detected;
-      }
+      setAttachPoint( createAttachPoint(), true );
     }
+    std::string mountpoint( attachPoint().asString() );
 
     Mount mount;
-    string mountpoint = attachPoint().asString();
-    bool mountsucceeded = false;
-    int count = 0;
     MediaMountException merr;
 
-    string options = _url.getQueryParam("mountoptions");
-    if (options.empty())
+    string options = _url.getQueryParam( "mountoptions" );
+    if ( options.empty() )
     {
       options="ro";
     }
@@ -331,9 +434,9 @@ namespace zypp {
     filesystems.push_back("iso9660");
 
     // try all devices in sequence
-    for (DeviceList::iterator it = _devices.begin()
-    ; !mountsucceeded && it != _devices.end()
-    ; ++it, count++ )
+    int count = 0;
+    bool mountsucceeded = false;
+    for ( DeviceList::iterator it = _devices.begin() ; ! mountsucceeded && it != _devices.end() ; ++it, ++count )
     {
       DBG << "count " << count << endl;
       if (next && count <=_lastdev_tried )
@@ -341,33 +444,22 @@ namespace zypp {
         DBG << "skipping device " << it->name << endl;
         continue;
       }
-
       _lastdev_tried = count;
 
-      MediaSource temp( *it);
-      bool        valid=false;
-      PathInfo    dinfo(temp.name);
-      if( dinfo.isBlk())
-      {
-        temp.maj_nr = dinfo.major();
-        temp.min_nr = dinfo.minor();
-
-        DeviceList::const_iterator d( detected.begin());
-        for( ; d != detected.end(); ++d)
-        {
-          if( temp.equals( *d))
-          {
-            valid = true;
-            break;
-          }
-        }
-      }
-      if( !valid)
+      // bnc#755815: _devices contains either devices passed as url option
+      //       or autodetected ones. Accept both as long as they are block
+      //       devices.
+      MediaSource temp( *it );
+      PathInfo dinfo( temp.name );
+      if ( ! dinfo.isBlk() )
       {
-        DBG << "skipping invalid device: " << it->name << endl;
-        continue;
+       WAR <<  "skipping non block device: " << dinfo << endl;
+       continue;
       }
+      DBG << "trying device " << dinfo << endl;
 
+      temp.maj_nr = dinfo.devMajor();
+      temp.min_nr = dinfo.devMinor();
       MediaSourceRef media( new MediaSource(temp));
       AttachedMedia ret( findAttachedMedia( media));
 
@@ -403,8 +495,8 @@ namespace zypp {
             is_device = true;
           }
 
-          if( is_device && media->maj_nr == dev_info.major() &&
-                           media->min_nr == dev_info.minor())
+          if( is_device && media->maj_nr == dev_info.devMajor() &&
+                           media->min_nr == dev_info.devMinor())
           {
             AttachPointRef ap( new AttachPoint(e->dir, false));
             AttachedMedia  am( media, ap);
@@ -439,25 +531,16 @@ namespace zypp {
       {
         try
         {
-          if( !isUseableAttachPoint(Pathname(mountpoint)))
-          {
-            mountpoint = createAttachPoint().asString();
-            setAttachPoint( mountpoint, true);
-            if( mountpoint.empty())
-            {
-              ZYPP_THROW( MediaBadAttachPointException(url()));
-            }
-          }
-
           mount.mount(it->name, mountpoint, *fsit, options);
 
           setMediaSource(media);
 
           // wait for /etc/mtab update ...
           // (shouldn't be needed)
-          int limit = 5;
+          int limit = 2;
           while( !(mountsucceeded=isAttached()) && --limit)
           {
+           WAR << "Wait for /proc/mounts update and retry...." << endl;
             sleep(1);
           }
 
@@ -567,93 +650,53 @@ namespace zypp {
   //
   // Asserted that media is not attached.
   //
-  void MediaCD::forceEject(const std::string & ejectDev)
+  void MediaCD::forceEject( const std::string & ejectDev_r )
   {
-    bool ejected=false;
-    if ( !isAttached()) {      // no device mounted in this instance
-
-      DeviceList detected(
-          detectDevices(_url.getScheme() == "dvd" ? true : false));
-
-      if(_devices.empty())
+#if REPORT_EJECT_ERRORS
+    bool ejected = false;
+#endif
+    if ( ! isAttached() )      // no device mounted in this instance
+    {
+      // This also fills the _devices list on demand
+      DeviceList detected( detectDevices( _url.getScheme() == "dvd" ? true : false ) );
+      for_( it, _devices.begin(), _devices.end() )
       {
-        DBG << "creating on-demand device list" << endl;
-        //default is /dev/cdrom; for dvd: /dev/dvd if it exists
-        string device( "/dev/cdrom" );
-        if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
-          device = "/dev/dvd";
-        }
-
-        PathInfo dinfo(device);
-        if( dinfo.isBlk())
-        {
-          MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
-
-          DeviceList::const_iterator d( detected.begin());
-          for( ; d != detected.end(); ++d)
-          {
-            // /dev/cdrom or /dev/dvd to the front
-            if( media.equals( *d))
-              _devices.push_front( *d);
-            else
-              _devices.push_back( *d);
-          }
-        }
-        else
-        {
-          // no /dev/cdrom or /dev/dvd link
-          _devices = detected;
-        }
-      }
-
-      DeviceList::iterator it;
-      for( it = _devices.begin(); it != _devices.end(); ++it ) {
-        MediaSourceRef media( new MediaSource( *it));
-        if (media->name != ejectDev)
+        MediaSourceRef media( new MediaSource( *it ) );
+        if ( media->name != ejectDev_r )
           continue;
 
-        bool        valid=false;
-        PathInfo    dinfo(media->name);
-        if( dinfo.isBlk())
-        {
-          media->maj_nr = dinfo.major();
-          media->min_nr = dinfo.minor();
-
-          DeviceList::const_iterator d( detected.begin());
-          for( ; d != detected.end(); ++d)
-          {
-            if( media->equals( *d))
-            {
-              valid = true;
-              break;
-            }
-          }
-        }
-        if( !valid)
-        {
-          DBG << "skipping invalid device: " << it->name << endl;
-          continue;
-        }
+       // bnc#755815: _devices contains either devices passed as url option
+       //      or autodetected ones. Accept both as long as they are block
+       //      devices.
+       PathInfo dinfo( media->name );
+       if( ! dinfo.isBlk() )
+       {
+         WAR <<  "skipping non block device: " << dinfo << endl;
+         continue;
+       }
+       DBG << "trying device " << dinfo << endl;
 
         // FIXME: we have also to check if it is mounted in the system
         AttachedMedia ret( findAttachedMedia( media));
-        if( !ret.mediaSource)
+        if( !ret.mediaSource )
         {
-          forceRelaseAllMedia(media, false);
+          forceRelaseAllMedia( media, false );
           if ( openTray( it->name ) )
           {
+#if REPORT_EJECT_ERRORS
             ejected = true;
+#endif
             break; // on 1st success
           }
         }
       }
     }
+#if REPORT_EJECT_ERRORS
     if( !ejected)
     {
-#if REPORT_EJECT_ERRORS
       ZYPP_THROW(MediaNotEjectedException());
-#endif
     }
+#endif
   }
 
   ///////////////////////////////////////////////////////////////////
@@ -715,17 +758,18 @@ namespace zypp {
   //
   //  DESCRIPTION : Asserted that media is attached and retlist is empty.
   //
-  void MediaCD::getDirInfo( filesystem::DirContent & retlist,
-                            const Pathname & dirname, bool dots ) const
+  void MediaCD::getDirInfo( filesystem::DirContent & retlist, const Pathname & dirname, bool dots ) const
   {
     MediaHandler::getDirInfo( retlist, dirname, dots );
   }
 
+
   bool MediaCD::getDoesFileExist( const Pathname & filename ) const
   {
     return MediaHandler::getDoesFileExist( filename );
   }
 
+
   bool MediaCD::hasMoreDevices()
   {
     if (_devices.size() == 0)
@@ -736,44 +780,27 @@ namespace zypp {
     return (unsigned) _lastdev_tried < _devices.size() - 1;
   }
 
-  void
-  MediaCD::getDetectedDevices(std::vector<std::string> & devices,
-                              unsigned int & index) const
+
+  void MediaCD::getDetectedDevices( std::vector<std::string> & devices, unsigned int & index ) const
   {
-    index = 0;
-    if (!devices.empty())
+    if ( ! devices.empty() )
       devices.clear();
 
-    for (DeviceList::const_iterator it = _devices.begin();
-         it != _devices.end(); ++it)
-      devices.push_back(it->name);
-
-    if (_lastdev >= 0)
-      index = _lastdev;
-
-    // try to detect again if _devices are empty (maybe this method will be
-    // called before _devices get actually filled)
-    if (devices.empty())
-    {
-      DBG << "no device list so far, trying to detect" << endl;
+    if ( _devices.empty() )
+      // This also fills the _devices list on demand
+      detectDevices( _url.getScheme() == "dvd" ? true : false );
 
-      DeviceList detected(
-        detectDevices(_url.getScheme() == "dvd" ? true : false));
+    for ( const auto & it : _devices )
+      devices.push_back( it.name );
 
-      for (DeviceList::const_iterator it = detected.begin();
-           it != detected.end(); ++it)
-        devices.push_back(it->name);
-
-      // don't know which one is in use in this case
-      index = 0;
-    }
+    index = ( _lastdev >= 0  ? (unsigned)_lastdev : 0 );
 
     MIL << "got " << devices.size() << " detected devices, current: "
         << (index < devices.size() ? devices[index] : "<none>")
         << "(" << index << ")" << endl;
   }
 
-
   } // namespace media
+  //////////////////////////////////////////////////////////////////
 } // namespace zypp
-// vim: set ts=8 sts=2 sw=2 ai noet:
+//////////////////////////////////////////////////////////////////