libzypp  10.5.0
MediaCD.cc
Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 extern "C"
00013 {
00014 #include <sys/ioctl.h>
00015 #include <linux/cdrom.h>
00016 #if HAVE_UDEV
00017 #include <libudev.h>
00018 #endif
00019 }
00020 
00021 #ifndef HAVE_UDEV
00022 #if HAVE_HAL
00023 #include "zypp/target/hal/HalContext.h"
00024 #endif
00025 #endif
00026 
00027 #include <cstring> // strerror
00028 #include <cstdlib> // getenv
00029 #include <iostream>
00030 
00031 #include "zypp/base/Logger.h"
00032 #include "zypp/ExternalProgram.h"
00033 #include "zypp/media/Mount.h"
00034 #include "zypp/media/MediaCD.h"
00035 #include "zypp/media/MediaManager.h"
00036 #include "zypp/Url.h"
00037 #include "zypp/AutoDispose.h"
00038 
00039 
00040 
00041 /*
00042 ** if to throw exception on eject errors or ignore them
00043 */
00044 #define  REPORT_EJECT_ERRORS     1
00045 
00046 /*
00047 ** If defined to the full path of the eject utility,
00048 ** it will be used additionally to the eject-ioctl.
00049 */
00050 #define EJECT_TOOL_PATH "/bin/eject"
00051 
00052 
00053 using namespace std;
00054 
00056 namespace zypp
00057 {
00059   namespace media
00060   {
00061 
00063     namespace
00064     {
00065       typedef std::list<MediaSource> DeviceList;
00066 
00076       DeviceList systemDetectDevices( bool supportingDVD_r )
00077       {
00078         DeviceList detected;
00079 
00080 #ifdef HAVE_UDEV
00081         // http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/index.html
00082         zypp::AutoDispose<struct udev *> udev( ::udev_new(), ::udev_unref );
00083         if ( ! udev )
00084         {
00085           ERR << "Can't create udev context." << endl;
00086           return DeviceList();
00087         }
00088 
00089         zypp::AutoDispose<struct udev_enumerate *> enumerate( ::udev_enumerate_new(udev), ::udev_enumerate_unref );
00090         if ( ! enumerate )
00091         {
00092           ERR << "Can't create udev list entry." << endl;
00093           return DeviceList();
00094         }
00095 
00096         ::udev_enumerate_add_match_subsystem( enumerate, "block" );
00097         ::udev_enumerate_add_match_property( enumerate, "ID_CDROM", "1" );
00098         ::udev_enumerate_scan_devices( enumerate );
00099 
00100         struct udev_list_entry * entry = 0;
00101         udev_list_entry_foreach( entry, ::udev_enumerate_get_list_entry( enumerate ) )
00102         {
00103           zypp::AutoDispose<struct udev_device *> device( ::udev_device_new_from_syspath( ::udev_enumerate_get_udev( enumerate ),
00104                                                                                           ::udev_list_entry_get_name( entry ) ),
00105                                                           ::udev_device_unref );
00106           if ( ! device )
00107           {
00108             ERR << "Can't create udev device." << endl;
00109             continue;
00110           }
00111 
00112           if ( supportingDVD_r && ! ::udev_device_get_property_value( device, "ID_CDROM_DVD" ) )
00113           {
00114             continue;   // looking for dvd only
00115           }
00116 
00117           const char * devnodePtr( ::udev_device_get_devnode( device ) );
00118           if ( ! devnodePtr )
00119           {
00120             ERR << "Got NULL devicenode." << endl;
00121             continue;
00122           }
00123 
00124           // In case we need it someday:
00125           //const char * mountpath = ::udev_device_get_property_value( device, "FSTAB_DIR" );
00126 
00127           PathInfo devnode( devnodePtr );
00128           if ( devnode.isBlk() )
00129           {
00130             MediaSource media( "cdrom", devnode.path().asString(), devnode.major(), devnode.minor() );
00131             DBG << "Found (udev): " << media << std::endl;
00132             detected.push_back( media );
00133           }
00134         }
00135         if ( detected.empty() )
00136         {
00137           WAR << "Did not find any CD/DVD device." << endl;
00138         }
00139 #elif HAVE_HAL
00140         using namespace zypp::target::hal;
00141         try
00142         {
00143           HalContext hal(true);
00144 
00145           std::vector<std::string> drv_udis;
00146           drv_udis = hal.findDevicesByCapability("storage.cdrom");
00147 
00148           DBG << "Found " << drv_udis.size() << " cdrom drive udis" << std::endl;
00149           for(size_t d = 0; d < drv_udis.size(); d++)
00150           {
00151             HalDrive drv( hal.getDriveFromUDI( drv_udis[d]));
00152 
00153             if( drv)
00154             {
00155               bool supportsDVD=false;
00156               if( supportingDVD_r)
00157               {
00158                 std::vector<std::string> caps;
00159                 try {
00160                   caps = drv.getCdromCapabilityNames();
00161                 }
00162                 catch(const HalException &e)
00163                 {
00164                   ZYPP_CAUGHT(e);
00165                 }
00166 
00167                 std::vector<std::string>::const_iterator ci;
00168                 for( ci=caps.begin(); ci != caps.end(); ++ci)
00169                 {
00170                   if( *ci == "dvd")
00171                     supportsDVD = true;
00172                 }
00173               }
00174 
00175               MediaSource media("cdrom", drv.getDeviceFile(),
00176                       drv.getDeviceMajor(),
00177                       drv.getDeviceMinor());
00178                   DBG << "Found " << drv_udis[d] << ": "
00179                   << media.asString() << std::endl;
00180                   if( supportingDVD_r && supportsDVD)
00181                   {
00182                     detected.push_front(media);
00183                   }
00184                   else
00185                   {
00186                     detected.push_back(media);
00187                   }
00188             }
00189           }
00190         }
00191         catch(const zypp::target::hal::HalException &e)
00192         {
00193           ZYPP_CAUGHT(e);
00194         }
00195 #endif
00196         return detected;
00197       }
00198 
00199     } // namespace
00201 
00202 
00203   MediaCD::MediaCD( const Url & url_r, const Pathname & attach_point_hint_r )
00204     : MediaHandler( url_r, attach_point_hint_r, url_r.getPathName(), false )
00205     , _lastdev( -1 )
00206     , _lastdev_tried( -1 )
00207   {
00208     MIL << "MediaCD::MediaCD(" << url_r << ", " << attach_point_hint_r << ")" << endl;
00209 
00210     if ( url_r.getScheme() != "dvd" && url_r.getScheme() != "cd" )
00211     {
00212       ERR << "Unsupported schema in the Url: " << url_r.asString() << endl;
00213       ZYPP_THROW(MediaUnsupportedUrlSchemeException(_url));
00214     }
00215 
00216     string devices = _url.getQueryParam( "devices" );
00217     if ( ! devices.empty() )
00218     {
00219       std::vector<std::string> words;
00220       str::split( devices, std::back_inserter(words), "," );
00221       for ( const std::string & device : words )
00222       {
00223         if ( device.empty() )
00224           continue;
00225 
00226         MediaSource media( "cdrom", device, 0, 0 );
00227         _devices.push_back( media );
00228         DBG << "use device (delayed verify)" << device << endl;
00229       }
00230     }
00231     else
00232     {
00233       DBG << "going to use on-demand device list" << endl;
00234       return;
00235     }
00236 
00237     if ( _devices.empty() )
00238     {
00239       ERR << "Unable to find any cdrom drive for " << _url.asString() << endl;
00240       ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
00241     }
00242   }
00243 
00245   //
00246   //
00247   //  METHOD NAME : MediaCD::openTray
00248   //  METHOD TYPE : bool
00249   //
00250   bool MediaCD::openTray( const std::string & device_r )
00251   {
00252     int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK|O_CLOEXEC );
00253     int res = -1;
00254 
00255     if ( fd != -1)
00256     {
00257       res = ::ioctl( fd, CDROMEJECT );
00258       ::close( fd );
00259     }
00260 
00261     if ( res )
00262     {
00263       if( fd == -1)
00264       {
00265         WAR << "Unable to open '" << device_r
00266             << "' (" << ::strerror( errno ) << ")" << endl;
00267       }
00268       else
00269       {
00270         WAR << "Eject " << device_r
00271             << " failed (" << ::strerror( errno ) << ")" << endl;
00272       }
00273 
00274 #if defined(EJECT_TOOL_PATH)
00275       DBG << "Try to eject " << device_r << " using "
00276         << EJECT_TOOL_PATH << " utility" << std::endl;
00277 
00278       const char *cmd[3];
00279       cmd[0] = EJECT_TOOL_PATH;
00280       cmd[1] = device_r.c_str();
00281       cmd[2] = NULL;
00282       ExternalProgram eject(cmd, ExternalProgram::Stderr_To_Stdout);
00283 
00284       for(std::string out( eject.receiveLine());
00285           out.length(); out = eject.receiveLine())
00286       {
00287         DBG << " " << out;
00288       }
00289 
00290       if(eject.close() != 0)
00291       {
00292         WAR << "Eject of " << device_r << " failed." << std::endl;
00293         return false;
00294       }
00295 #else
00296       return false;
00297 #endif
00298     }
00299     MIL << "Eject of " << device_r << " successful." << endl;
00300     return true;
00301   }
00302 
00304   //
00305   //
00306   //    METHOD NAME : MediaCD::closeTray
00307   //    METHOD TYPE : bool
00308   //
00309   bool MediaCD::closeTray( const std::string & device_r )
00310   {
00311     int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK|O_CLOEXEC );
00312     if ( fd == -1 ) {
00313       WAR << "Unable to open '" << device_r << "' (" << ::strerror( errno ) << ")" << endl;
00314       return false;
00315     }
00316     int res = ::ioctl( fd, CDROMCLOSETRAY );
00317     ::close( fd );
00318     if ( res ) {
00319       WAR << "Close tray " << device_r << " failed (" << ::strerror( errno ) << ")" << endl;
00320       return false;
00321     }
00322     DBG << "Close tray " << device_r << endl;
00323     return true;
00324   }
00325 
00326 
00327   MediaCD::DeviceList MediaCD::detectDevices( bool supportingDVD_r ) const
00328   {
00329     DeviceList detected( systemDetectDevices( supportingDVD_r ) );
00330 
00331     if ( detected.empty() )
00332     {
00333       WAR << "CD/DVD drive detection with HAL/UDEV failed! Guessing..." << std::endl;
00334       PathInfo dvdinfo( "/dev/dvd" );
00335       PathInfo cdrinfo( "/dev/cdrom" );
00336       if ( dvdinfo.isBlk() )
00337       {
00338         MediaSource media( "cdrom", dvdinfo.path().asString(), dvdinfo.major(), dvdinfo.minor() );
00339         DBG << "Found (GUESS): " << media << std::endl;
00340         detected.push_back( media );
00341       }
00342       if ( cdrinfo.isBlk()
00343         && ! ( cdrinfo.major() == dvdinfo.major() && cdrinfo.minor() == dvdinfo.minor() ) )
00344       {
00345         MediaSource media( "cdrom", cdrinfo.path().asString(), cdrinfo.major(), cdrinfo.minor() );
00346         DBG << "Found (GUESS): " << media << std::endl;
00347         detected.push_back( media );
00348       }
00349     }
00350 
00351     // NOTE: On the fly build on-demand device list. Code was moved to
00352     // here to get rid of code duplication, while keeping the ABI. Acuallty
00353     // this code should be moved to a _devices accessor method.
00354     if ( _devices.empty() )
00355     {
00356       DBG << "creating on-demand device list" << endl;
00357       //default is /dev/cdrom; for dvd: /dev/dvd if it exists
00358       string device( "/dev/cdrom" );
00359       if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() )
00360       {
00361         device = "/dev/dvd";
00362       }
00363 
00364       PathInfo dinfo( device );
00365       if ( dinfo.isBlk() )
00366       {
00367         MediaSource media( "cdrom", device, dinfo.major(), dinfo.minor() );
00368         if ( detected.empty() )
00369         {
00370           _devices.push_front( media ); // better try this than nothing
00371         }
00372         else
00373         {
00374           for( const auto & d : detected )
00375           {
00376             // /dev/cdrom or /dev/dvd to the front
00377             if ( media.equals( d ) )
00378               _devices.push_front( d );
00379             else
00380               _devices.push_back( d );
00381           }
00382         }
00383       }
00384       else
00385       {
00386         // no /dev/cdrom or /dev/dvd link
00387         _devices = detected;
00388       }
00389     }
00390 
00391     return detected;
00392   }
00393 
00394 
00396   //
00397   //
00398   //  METHOD NAME : MediaCD::attachTo
00399   //  METHOD TYPE : PMError
00400   //
00401   //  DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
00402   //
00403   void MediaCD::attachTo( bool next )
00404   {
00405     DBG << "next " << next << " last " << _lastdev << " last tried " << _lastdev_tried << endl;
00406     if ( next && _lastdev == -1 )
00407       ZYPP_THROW(MediaNotSupportedException(url()));
00408 
00409     // This also fills the _devices list on demand
00410     DeviceList detected( detectDevices( _url.getScheme() == "dvd" ? true : false ) );
00411 
00412     Mount mount;
00413     MediaMountException merr;
00414     string mountpoint = attachPoint().asString();
00415 
00416     string options = _url.getQueryParam( "mountoptions" );
00417     if ( options.empty() )
00418     {
00419       options="ro";
00420     }
00421 
00422     //TODO: make configurable
00423     list<string> filesystems;
00424 
00425     // if DVD, try UDF filesystem before iso9660
00426     if ( _url.getScheme() == "dvd" )
00427       filesystems.push_back("udf");
00428 
00429     filesystems.push_back("iso9660");
00430 
00431     // try all devices in sequence
00432     int count = 0;
00433     bool mountsucceeded = false;
00434     for ( DeviceList::iterator it = _devices.begin() ; ! mountsucceeded && it != _devices.end() ; ++it, ++count )
00435     {
00436       DBG << "count " << count << endl;
00437       if (next && count <=_lastdev_tried )
00438       {
00439         DBG << "skipping device " << it->name << endl;
00440         continue;
00441       }
00442       _lastdev_tried = count;
00443 
00444       // bnc#755815: _devices contains either devices passed as url option
00445       //        or autodetected ones. Accept both as long as they are block
00446       //        devices.
00447       MediaSource temp( *it );
00448       PathInfo dinfo( temp.name );
00449       if ( ! dinfo.isBlk() )
00450       {
00451         WAR <<  "skipping non block device: " << dinfo << endl;
00452         continue;
00453       }
00454       DBG << "trying device " << dinfo << endl;
00455 
00456       temp.maj_nr = dinfo.major();
00457       temp.min_nr = dinfo.minor();
00458       MediaSourceRef media( new MediaSource(temp));
00459       AttachedMedia ret( findAttachedMedia( media));
00460 
00461       if( ret.mediaSource && ret.attachPoint &&
00462          !ret.attachPoint->empty())
00463       {
00464         DBG << "Using a shared media "
00465             << ret.mediaSource->name
00466             << " attached on "
00467             << ret.attachPoint->path
00468             << endl;
00469         removeAttachPoint();
00470         setAttachPoint(ret.attachPoint);
00471         setMediaSource(ret.mediaSource);
00472         _lastdev = count;
00473         mountsucceeded = true;
00474         break;
00475       }
00476 
00477       {
00478         MediaManager  manager;
00479         MountEntries  entries( manager.getMountEntries());
00480         MountEntries::const_iterator e;
00481         for( e = entries.begin(); e != entries.end(); ++e)
00482         {
00483           bool        is_device = false;
00484           std::string dev_path(Pathname(e->src).asString());
00485           PathInfo    dev_info;
00486 
00487           if( dev_path.compare(0, sizeof("/dev/")-1, "/dev/") == 0 &&
00488               dev_info(e->src) && dev_info.isBlk())
00489           {
00490             is_device = true;
00491           }
00492 
00493           if( is_device && media->maj_nr == dev_info.major() &&
00494                            media->min_nr == dev_info.minor())
00495           {
00496             AttachPointRef ap( new AttachPoint(e->dir, false));
00497             AttachedMedia  am( media, ap);
00498             {
00499               DBG << "Using a system mounted media "
00500                   << media->name
00501                   << " attached on "
00502                   << ap->path
00503                   << endl;
00504 
00505               media->iown = false; // mark attachment as foreign
00506 
00507               setMediaSource(media);
00508               setAttachPoint(ap);
00509               _lastdev = count;
00510               mountsucceeded = true;
00511               break;
00512             }
00513           }
00514         }
00515         if( mountsucceeded)
00516           break;
00517       }
00518 
00519       // close tray
00520       closeTray( it->name );
00521 
00522       // try all filesystems in sequence
00523       for(list<string>::iterator fsit = filesystems.begin()
00524           ; !mountsucceeded && fsit != filesystems.end()
00525           ; ++fsit)
00526       {
00527         try
00528         {
00529           if( !isUseableAttachPoint(Pathname(mountpoint)))
00530           {
00531             mountpoint = createAttachPoint().asString();
00532             setAttachPoint( mountpoint, true);
00533             if( mountpoint.empty())
00534             {
00535               ZYPP_THROW( MediaBadAttachPointException(url()));
00536             }
00537           }
00538 
00539           mount.mount(it->name, mountpoint, *fsit, options);
00540 
00541           setMediaSource(media);
00542 
00543           // wait for /etc/mtab update ...
00544           // (shouldn't be needed)
00545           int limit = 2;
00546           while( !(mountsucceeded=isAttached()) && --limit)
00547           {
00548             WAR << "Wait for /proc/mounts update and retry...." << endl;
00549             sleep(1);
00550           }
00551 
00552           if( mountsucceeded)
00553           {
00554             _lastdev = count;
00555           }
00556           else
00557           {
00558             setMediaSource(MediaSourceRef());
00559             try
00560             {
00561               mount.umount(attachPoint().asString());
00562             }
00563             catch (const MediaException & excpt_r)
00564             {
00565               ZYPP_CAUGHT(excpt_r);
00566             }
00567             ZYPP_THROW(MediaMountException(
00568               "Unable to verify that the media was mounted",
00569               it->name, mountpoint
00570             ));
00571           }
00572         }
00573         catch (const MediaMountException &e)
00574         {
00575           merr = e;
00576           removeAttachPoint();
00577           ZYPP_CAUGHT(e);
00578         }
00579         catch (const MediaException & excpt_r)
00580         {
00581           removeAttachPoint();
00582           ZYPP_CAUGHT(excpt_r);
00583         }
00584       } // for filesystems
00585     } // for _devices
00586 
00587     if (!mountsucceeded)
00588     {
00589       _lastdev = -1;
00590 
00591       if( !merr.mountOutput().empty())
00592       {
00593         ZYPP_THROW(MediaMountException(merr.mountError(),
00594                                        _url.asString(),
00595                                        mountpoint,
00596                                        merr.mountOutput()));
00597       }
00598       else
00599       {
00600         ZYPP_THROW(MediaMountException("Mounting media failed",
00601                                        _url.asString(), mountpoint));
00602       }
00603     }
00604     DBG << _lastdev << " " << count << endl;
00605   }
00606 
00607 
00609   //
00610   //
00611   //  METHOD NAME : MediaCD::releaseFrom
00612   //  METHOD TYPE : PMError
00613   //
00614   //  DESCRIPTION : Asserted that media is attached.
00615   //
00616   void MediaCD::releaseFrom( const std::string & ejectDev )
00617   {
00618     Mount mount;
00619     try
00620     {
00621       AttachedMedia am( attachedMedia());
00622       if(am.mediaSource && am.mediaSource->iown)
00623         mount.umount(am.attachPoint->path.asString());
00624     }
00625     catch (const Exception & excpt_r)
00626     {
00627       ZYPP_CAUGHT(excpt_r);
00628       if (!ejectDev.empty())
00629       {
00630         forceRelaseAllMedia(false);
00631         if(openTray( ejectDev ))
00632           return;
00633       }
00634       ZYPP_RETHROW(excpt_r);
00635     }
00636 
00637     // eject device
00638     if (!ejectDev.empty())
00639     {
00640       forceRelaseAllMedia(false);
00641       if( !openTray( ejectDev ))
00642       {
00643 #if REPORT_EJECT_ERRORS
00644         ZYPP_THROW(MediaNotEjectedException(ejectDev));
00645 #endif
00646       }
00647     }
00648   }
00649 
00651   //
00652   //
00653   //  METHOD NAME : MediaCD::forceEject
00654   //  METHOD TYPE : void
00655   //
00656   // Asserted that media is not attached.
00657   //
00658   void MediaCD::forceEject( const std::string & ejectDev_r )
00659   {
00660     bool ejected = false;
00661     if ( ! isAttached() )       // no device mounted in this instance
00662     {
00663       // This also fills the _devices list on demand
00664       DeviceList detected( detectDevices( _url.getScheme() == "dvd" ? true : false ) );
00665       for_( it, _devices.begin(), _devices.end() )
00666       {
00667         MediaSourceRef media( new MediaSource( *it ) );
00668         if ( media->name != ejectDev_r )
00669           continue;
00670 
00671         // bnc#755815: _devices contains either devices passed as url option
00672         //      or autodetected ones. Accept both as long as they are block
00673         //      devices.
00674         PathInfo dinfo( media->name );
00675         if( ! dinfo.isBlk() )
00676         {
00677           WAR <<  "skipping non block device: " << dinfo << endl;
00678           continue;
00679         }
00680         DBG << "trying device " << dinfo << endl;
00681 
00682         // FIXME: we have also to check if it is mounted in the system
00683         AttachedMedia ret( findAttachedMedia( media));
00684         if( !ret.mediaSource )
00685         {
00686           forceRelaseAllMedia( media, false );
00687           if ( openTray( it->name ) )
00688           {
00689             ejected = true;
00690             break; // on 1st success
00691           }
00692         }
00693       }
00694     }
00695 #if REPORT_EJECT_ERRORS
00696     if( !ejected)
00697     {
00698       ZYPP_THROW(MediaNotEjectedException());
00699     }
00700 #endif
00701   }
00702 
00704   //
00705   //  METHOD NAME : MediaCD::isAttached
00706   //  METHOD TYPE : bool
00707   //
00708   //  DESCRIPTION : Override check if media is attached.
00709   //
00710   bool
00711   MediaCD::isAttached() const
00712   {
00713     return checkAttached(false);
00714   }
00715 
00717   //
00718   //  METHOD NAME : MediaCD::getFile
00719   //  METHOD TYPE : PMError
00720   //
00721   //  DESCRIPTION : Asserted that media is attached.
00722   //
00723   void MediaCD::getFile( const Pathname & filename ) const
00724   {
00725     MediaHandler::getFile( filename );
00726   }
00727 
00729   //
00730   //  METHOD NAME : MediaCD::getDir
00731   //  METHOD TYPE : PMError
00732   //
00733   //  DESCRIPTION : Asserted that media is attached.
00734   //
00735   void MediaCD::getDir( const Pathname & dirname, bool recurse_r ) const
00736   {
00737     MediaHandler::getDir( dirname, recurse_r );
00738   }
00739 
00741   //
00742   //
00743   //  METHOD NAME : MediaCD::getDirInfo
00744   //  METHOD TYPE : PMError
00745   //
00746   //  DESCRIPTION : Asserted that media is attached and retlist is empty.
00747   //
00748   void MediaCD::getDirInfo( std::list<std::string> & retlist,
00749                             const Pathname & dirname, bool dots ) const
00750   {
00751     MediaHandler::getDirInfo( retlist, dirname, dots );
00752   }
00753 
00755   //
00756   //
00757   //  METHOD NAME : MediaCD::getDirInfo
00758   //  METHOD TYPE : PMError
00759   //
00760   //  DESCRIPTION : Asserted that media is attached and retlist is empty.
00761   //
00762   void MediaCD::getDirInfo( filesystem::DirContent & retlist, const Pathname & dirname, bool dots ) const
00763   {
00764     MediaHandler::getDirInfo( retlist, dirname, dots );
00765   }
00766 
00767 
00768   bool MediaCD::getDoesFileExist( const Pathname & filename ) const
00769   {
00770     return MediaHandler::getDoesFileExist( filename );
00771   }
00772 
00773 
00774   bool MediaCD::hasMoreDevices()
00775   {
00776     if (_devices.size() == 0)
00777       return false;
00778     else if (_lastdev_tried < 0)
00779       return true;
00780 
00781     return (unsigned) _lastdev_tried < _devices.size() - 1;
00782   }
00783 
00784 
00785   void MediaCD::getDetectedDevices( std::vector<std::string> & devices, unsigned int & index ) const
00786   {
00787     if ( ! devices.empty() )
00788       devices.clear();
00789 
00790     if ( _devices.empty() )
00791       // This also fills the _devices list on demand
00792       detectDevices( _url.getScheme() == "dvd" ? true : false );
00793 
00794     for ( const auto & it : _devices )
00795       devices.push_back( it.name );
00796 
00797     index = ( _lastdev >= 0  ? (unsigned)_lastdev : 0 );
00798 
00799     MIL << "got " << devices.size() << " detected devices, current: "
00800         << (index < devices.size() ? devices[index] : "<none>")
00801         << "(" << index << ")" << endl;
00802   }
00803 
00804   } // namespace media
00806 } // namespace zypp