ada97b94900f503e13464259e4d5bfa0ba59ef6a
[platform/upstream/libzypp.git] / zypp / media / MediaCD.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/media/MediaCD.cc
10  *
11 */
12
13 #include <iostream>
14
15 #include "zypp/base/Logger.h"
16 #include "zypp/ExternalProgram.h"
17 #include "zypp/media/Mount.h"
18 #include "zypp/media/MediaCD.h"
19 #include "zypp/media/MediaManager.h"
20 #include "zypp/Url.h"
21 #include "zypp/target/hal/HalContext.h"
22
23 #include <cstring> // strerror
24 #include <cstdlib> // getenv
25
26 #include <errno.h>
27 #include <dirent.h>
28
29 #include <sys/ioctl.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <unistd.h> // geteuid, ...
33
34 #include <linux/cdrom.h>
35
36 /*
37 ** try umount of foreign (user/automounter) media on eject
38 **   0 = don't force, 1 = automounted only, 2 == all
39 */
40 #define  FORCE_RELEASE_FOREIGN   2
41
42 /*
43 ** Reuse foreign (user/automounter) mount points.
44 ** 0 = don't use, 1 = automounted only, 2 = all
45 */
46 #define  REUSE_FOREIGN_MOUNTS    2
47
48 /*
49 ** if to throw exception on eject errors or ignore them
50 */
51 #define  REPORT_EJECT_ERRORS     1
52
53 /*
54 ** If defined to the full path of the eject utility,
55 ** it will be used additionally to the eject-ioctl.
56 */
57 #define EJECT_TOOL_PATH "/bin/eject"
58
59
60 using namespace std;
61
62 namespace zypp {
63   namespace media {
64
65 ///////////////////////////////////////////////////////////////////
66 //
67 //  CLASS NAME : MediaCD
68 //
69 ///////////////////////////////////////////////////////////////////
70
71
72   ///////////////////////////////////////////////////////////////////
73   //
74   //
75   //  METHOD NAME : MediaCD::MediaCD
76   //  METHOD TYPE : Constructor
77   //
78   //  DESCRIPTION :
79   //
80   MediaCD::MediaCD( const Url &      url_r,
81                     const Pathname & attach_point_hint_r )
82     : MediaHandler( url_r, attach_point_hint_r,
83                     url_r.getPathName(), // urlpath below attachpoint
84                     false ),
85       _lastdev(-1), _lastdev_tried(-1)
86   {
87     MIL << "MediaCD::MediaCD(" << url_r << ", " << attach_point_hint_r << ")"
88         << endl;
89
90     if( url_r.getScheme() != "dvd" && url_r.getScheme() != "cd")
91     {
92       ERR << "Unsupported schema in the Url: " << url_r.asString() << endl;
93       ZYPP_THROW(MediaUnsupportedUrlSchemeException(_url));
94     }
95
96     string devices = _url.getQueryParam("devices");
97     if (!devices.empty())
98     {
99       string::size_type pos;
100       DBG << "parse " << devices << endl;
101       while(!devices.empty())
102       {
103         pos = devices.find(',');
104         string device = devices.substr(0,pos);
105         if (!device.empty())
106         {
107           MediaSource media("cdrom", device, 0, 0);
108           _devices.push_back( media);
109           DBG << "use device (delayed verify)" << device << endl;
110         }
111         if (pos!=string::npos)
112           devices=devices.substr(pos+1);
113         else
114           devices.erase();
115       }
116     }
117     else
118     {
119       DBG << "going to use on-demand device list" << endl;
120       return;
121     }
122
123     if( _devices.empty())
124     {
125       ERR << "Unable to find any cdrom drive for " << _url.asString() << endl;
126       ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
127     }
128   }
129
130   ///////////////////////////////////////////////////////////////////
131   //
132   //
133   //  METHOD NAME : MediaCD::openTray
134   //  METHOD TYPE : bool
135   //
136   bool MediaCD::openTray( const std::string & device_r )
137   {
138     int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
139     int res = -1;
140
141     if ( fd != -1)
142     {
143       res = ::ioctl( fd, CDROMEJECT );
144       ::close( fd );
145     }
146
147     if ( res )
148     {
149       if( fd == -1)
150       {
151         WAR << "Unable to open '" << device_r
152             << "' (" << ::strerror( errno ) << ")" << endl;
153       }
154       else
155       {
156         WAR << "Eject " << device_r
157             << " failed (" << ::strerror( errno ) << ")" << endl;
158       }
159
160 #if defined(EJECT_TOOL_PATH)
161       DBG << "Try to eject " << device_r << " using "
162         << EJECT_TOOL_PATH << " utility" << std::endl;
163
164       const char *cmd[3];
165       cmd[0] = EJECT_TOOL_PATH;
166       cmd[1] = device_r.c_str();
167       cmd[2] = NULL;
168       ExternalProgram eject(cmd, ExternalProgram::Stderr_To_Stdout);
169
170       for(std::string out( eject.receiveLine());
171           out.length(); out = eject.receiveLine())
172       {
173         DBG << " " << out;
174       }
175
176       if(eject.close() != 0)
177       {
178         WAR << "Eject of " << device_r << " failed." << std::endl;
179         return false;
180       }
181 #else
182       return false;
183 #endif
184     }
185     MIL << "Eject of " << device_r << " successful." << endl;
186     return true;
187   }
188
189   ///////////////////////////////////////////////////////////////////
190   //
191   //
192   //    METHOD NAME : MediaCD::closeTray
193   //    METHOD TYPE : bool
194   //
195   bool MediaCD::closeTray( const std::string & device_r )
196   {
197     int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
198     if ( fd == -1 ) {
199       WAR << "Unable to open '" << device_r << "' (" << ::strerror( errno ) << ")" << endl;
200       return false;
201     }
202     int res = ::ioctl( fd, CDROMCLOSETRAY );
203     ::close( fd );
204     if ( res ) {
205       WAR << "Close tray " << device_r << " failed (" << ::strerror( errno ) << ")" << endl;
206       return false;
207     }
208     DBG << "Close tray " << device_r << endl;
209     return true;
210   }
211
212   ///////////////////////////////////////////////////////////////////
213   //
214   //
215   //  METHOD NAME : MediaCD::detectDevices
216   //  METHOD TYPE : MediaCD::DeviceList
217   //
218   MediaCD::DeviceList
219   MediaCD::detectDevices(bool supportingDVD) const
220   {
221     DeviceList detected;
222
223 #ifndef NO_HAL
224     using namespace zypp::target::hal;
225     try
226     {
227       HalContext hal(true);
228
229       std::vector<std::string> drv_udis;
230       drv_udis = hal.findDevicesByCapability("storage.cdrom");
231
232       DBG << "Found " << drv_udis.size() << " cdrom drive udis" << std::endl;
233       for(size_t d = 0; d < drv_udis.size(); d++)
234       {
235         HalDrive drv( hal.getDriveFromUDI( drv_udis[d]));
236
237         if( drv)
238         {
239           bool supportsDVD=false;
240           if( supportingDVD)
241           {
242             std::vector<std::string> caps;
243             try {
244               caps = drv.getCdromCapabilityNames();
245             }
246             catch(const HalException &e)
247             {
248               ZYPP_CAUGHT(e);
249             }
250
251             std::vector<std::string>::const_iterator ci;
252             for( ci=caps.begin(); ci != caps.end(); ++ci)
253             {
254               if( *ci == "dvd")
255                 supportsDVD = true;
256             }
257           }
258
259           MediaSource media("cdrom", drv.getDeviceFile(),
260                             drv.getDeviceMajor(),
261                             drv.getDeviceMinor());
262           DBG << "Found " << drv_udis[d] << ": "
263               << media.asString() << std::endl;
264           if( supportingDVD && supportsDVD)
265           {
266             detected.push_front(media);
267           }
268           else
269           {
270             detected.push_back(media);
271           }
272         }
273       }
274     }
275     catch(const zypp::target::hal::HalException &e)
276     {
277       ZYPP_CAUGHT(e);
278     }
279 #else // NO_HAL
280 #warning Poor CDROM devices detection without HAL
281     WAR << "Cdrom drive detection without HAL! " << std::endl;
282     PathInfo dvdinfo( "/dev/dvd" );
283     PathInfo cdrinfo( "/dev/cdrom" );
284     if ( dvdinfo.isBlk() )
285     {
286       MediaSource media( "cdrom", dvdinfo.path().asString(), dvdinfo.major(), dvdinfo.minor() );
287       DBG << "Found (NO_HAL): " << media << std::endl;
288       detected.push_back( media );
289     }
290     if ( cdrinfo.isBlk()
291          && ! ( cdrinfo.major() == dvdinfo.major() && cdrinfo.minor() == dvdinfo.minor() ) )
292     {
293       MediaSource media( "cdrom", cdrinfo.path().asString(), cdrinfo.major(), cdrinfo.minor() );
294       DBG << "Found (NO_HAL): " << media << std::endl;
295       detected.push_back( media );
296     }
297 #endif
298     return detected;
299   }
300
301
302   ///////////////////////////////////////////////////////////////////
303   //
304   //
305   //  METHOD NAME : MediaCD::attachTo
306   //  METHOD TYPE : PMError
307   //
308   //  DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
309   //
310   void MediaCD::attachTo(bool next)
311   {
312     DBG << "next " << next << " last " << _lastdev << " last tried " << _lastdev_tried << endl;
313     if (next && _lastdev == -1)
314       ZYPP_THROW(MediaNotSupportedException(url()));
315
316     DeviceList detected(
317       detectDevices(_url.getScheme() == "dvd" ? true : false));
318
319     if(_devices.empty())
320     {
321       DBG << "creating on-demand device list" << endl;
322       //default is /dev/cdrom; for dvd: /dev/dvd if it exists
323       string device( "/dev/cdrom" );
324       if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() )
325       {
326         device = "/dev/dvd";
327       }
328
329       PathInfo dinfo(device);
330       if( dinfo.isBlk())
331       {
332         MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
333
334         DeviceList::const_iterator d( detected.begin());
335         for( ; d != detected.end(); ++d)
336         {
337           // /dev/cdrom or /dev/dvd to the front
338           if( media.equals( *d))
339             _devices.push_front( *d);
340           else
341             _devices.push_back( *d);
342         }
343       }
344       else
345       {
346         // no /dev/cdrom or /dev/dvd link
347         _devices = detected;
348       }
349     }
350
351     Mount mount;
352     string mountpoint = attachPoint().asString();
353     bool mountsucceeded = false;
354     int count = 0;
355     MediaMountException merr;
356
357     string options = _url.getQueryParam("mountoptions");
358     if (options.empty())
359     {
360       options="ro";
361     }
362
363     //TODO: make configurable
364     list<string> filesystems;
365
366     // if DVD, try UDF filesystem before iso9660
367     if ( _url.getScheme() == "dvd" )
368       filesystems.push_back("udf");
369
370     filesystems.push_back("iso9660");
371
372     // try all devices in sequence
373     for (DeviceList::iterator it = _devices.begin()
374     ; !mountsucceeded && it != _devices.end()
375     ; ++it, count++ )
376     {
377       DBG << "count " << count << endl;
378       if (next && count <=_lastdev_tried )
379       {
380         DBG << "skipping device " << it->name << endl;
381         continue;
382       }
383
384       _lastdev_tried = count;
385
386       MediaSource temp( *it);
387       bool        valid=false;
388       PathInfo    dinfo(temp.name);
389       if( dinfo.isBlk())
390       {
391         temp.maj_nr = dinfo.major();
392         temp.min_nr = dinfo.minor();
393
394         DeviceList::const_iterator d( detected.begin());
395         for( ; d != detected.end(); ++d)
396         {
397           if( temp.equals( *d))
398           {
399             valid = true;
400             break;
401           }
402         }
403       }
404       if( !valid)
405       {
406         DBG << "skipping invalid device: " << it->name << endl;
407         continue;
408       }
409
410       MediaSourceRef media( new MediaSource(temp));
411       AttachedMedia ret( findAttachedMedia( media));
412
413       if( ret.mediaSource && ret.attachPoint &&
414          !ret.attachPoint->empty())
415       {
416         DBG << "Using a shared media "
417             << ret.mediaSource->name
418             << " attached on "
419             << ret.attachPoint->path
420             << endl;
421         removeAttachPoint();
422         setAttachPoint(ret.attachPoint);
423         setMediaSource(ret.mediaSource);
424         _lastdev = count;
425         mountsucceeded = true;
426         break;
427       }
428
429 #if REUSE_FOREIGN_MOUNTS > 0
430       {
431         MediaManager  manager;
432         MountEntries  entries( manager.getMountEntries());
433         MountEntries::const_iterator e;
434         for( e = entries.begin(); e != entries.end(); ++e)
435         {
436           bool        is_device = false;
437           std::string dev_path(Pathname(e->src).asString());
438           PathInfo    dev_info;
439
440           if( dev_path.compare(0, sizeof("/dev/")-1, "/dev/") == 0 &&
441               dev_info(e->src) && dev_info.isBlk())
442           {
443             is_device = true;
444           }
445
446           if( is_device && media->maj_nr == dev_info.major() &&
447                            media->min_nr == dev_info.minor())
448           {
449             AttachPointRef ap( new AttachPoint(e->dir, false));
450             AttachedMedia  am( media, ap);
451             //
452             // 1 = automounted only, 2 == all
453             //
454 #if REUSE_FOREIGN_MOUNTS == 1
455             if( isAutoMountedMedia(am))
456 #endif
457             {
458               DBG << "Using a system mounted media "
459                   << media->name
460                   << " attached on "
461                   << ap->path
462                   << endl;
463
464               media->iown = false; // mark attachment as foreign
465
466               setMediaSource(media);
467               setAttachPoint(ap);
468               _lastdev = count;
469               mountsucceeded = true;
470               break;
471             }
472           }
473         }
474         if( mountsucceeded)
475           break;
476       }
477 #endif  // REUSE_FOREIGN_MOUNTS
478
479       // close tray
480       closeTray( it->name );
481
482       // try all filesystems in sequence
483       for(list<string>::iterator fsit = filesystems.begin()
484           ; !mountsucceeded && fsit != filesystems.end()
485           ; ++fsit)
486       {
487         try
488         {
489           if( !isUseableAttachPoint(Pathname(mountpoint)))
490           {
491             mountpoint = createAttachPoint().asString();
492             setAttachPoint( mountpoint, true);
493             if( mountpoint.empty())
494             {
495               ZYPP_THROW( MediaBadAttachPointException(url()));
496             }
497           }
498
499           mount.mount(it->name, mountpoint, *fsit, options);
500
501           setMediaSource(media);
502
503           // wait for /etc/mtab update ...
504           // (shouldn't be needed)
505           int limit = 5;
506           while( !(mountsucceeded=isAttached()) && --limit)
507           {
508             sleep(1);
509           }
510
511           if( mountsucceeded)
512           {
513             _lastdev = count;
514           }
515           else
516           {
517             setMediaSource(MediaSourceRef());
518             try
519             {
520               mount.umount(attachPoint().asString());
521             }
522             catch (const MediaException & excpt_r)
523             {
524               ZYPP_CAUGHT(excpt_r);
525             }
526             ZYPP_THROW(MediaMountException(
527               "Unable to verify that the media was mounted",
528               it->name, mountpoint
529             ));
530           }
531         }
532         catch (const MediaMountException &e)
533         {
534           merr = e;
535           removeAttachPoint();
536           ZYPP_CAUGHT(e);
537         }
538         catch (const MediaException & excpt_r)
539         {
540           removeAttachPoint();
541           ZYPP_CAUGHT(excpt_r);
542         }
543       } // for filesystems
544     } // for _devices
545
546     if (!mountsucceeded)
547     {
548       _lastdev = -1;
549
550       if( !merr.mountOutput().empty())
551       {
552         ZYPP_THROW(MediaMountException(merr.mountError(),
553                                        _url.asString(),
554                                        mountpoint,
555                                        merr.mountOutput()));
556       }
557       else
558       {
559         ZYPP_THROW(MediaMountException("Mounting media failed",
560                                        _url.asString(), mountpoint));
561       }
562     }
563     DBG << _lastdev << " " << count << endl;
564   }
565
566
567   ///////////////////////////////////////////////////////////////////
568   //
569   //
570   //  METHOD NAME : MediaCD::releaseFrom
571   //  METHOD TYPE : PMError
572   //
573   //  DESCRIPTION : Asserted that media is attached.
574   //
575   void MediaCD::releaseFrom( const std::string & ejectDev )
576   {
577     Mount mount;
578     try
579     {
580       AttachedMedia am( attachedMedia());
581       if(am.mediaSource && am.mediaSource->iown)
582         mount.umount(am.attachPoint->path.asString());
583     }
584     catch (const Exception & excpt_r)
585     {
586       ZYPP_CAUGHT(excpt_r);
587       if (!ejectDev.empty())
588       {
589 #if FORCE_RELEASE_FOREIGN > 0
590         /* 1 = automounted only, 2 = all */
591         forceRelaseAllMedia(false, FORCE_RELEASE_FOREIGN == 1);
592 #endif
593         if(openTray( ejectDev ))
594           return;
595       }
596       ZYPP_RETHROW(excpt_r);
597     }
598
599     // eject device
600     if (!ejectDev.empty())
601     {
602 #if FORCE_RELEASE_FOREIGN > 0
603       /* 1 = automounted only, 2 = all */
604       forceRelaseAllMedia(false, FORCE_RELEASE_FOREIGN == 1);
605 #endif
606       if( !openTray( ejectDev ))
607       {
608 #if REPORT_EJECT_ERRORS
609         ZYPP_THROW(MediaNotEjectedException(ejectDev));
610 #endif
611       }
612     }
613   }
614
615   ///////////////////////////////////////////////////////////////////
616   //
617   //
618   //  METHOD NAME : MediaCD::forceEject
619   //  METHOD TYPE : void
620   //
621   // Asserted that media is not attached.
622   //
623   void MediaCD::forceEject(const std::string & ejectDev)
624   {
625     bool ejected=false;
626     if ( !isAttached()) {       // no device mounted in this instance
627
628       DeviceList detected(
629           detectDevices(_url.getScheme() == "dvd" ? true : false));
630
631       if(_devices.empty())
632       {
633         DBG << "creating on-demand device list" << endl;
634         //default is /dev/cdrom; for dvd: /dev/dvd if it exists
635         string device( "/dev/cdrom" );
636         if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
637           device = "/dev/dvd";
638         }
639
640         PathInfo dinfo(device);
641         if( dinfo.isBlk())
642         {
643           MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
644
645           DeviceList::const_iterator d( detected.begin());
646           for( ; d != detected.end(); ++d)
647           {
648             // /dev/cdrom or /dev/dvd to the front
649             if( media.equals( *d))
650               _devices.push_front( *d);
651             else
652               _devices.push_back( *d);
653           }
654         }
655         else
656         {
657           // no /dev/cdrom or /dev/dvd link
658           _devices = detected;
659         }
660       }
661
662       DeviceList::iterator it;
663       for( it = _devices.begin(); it != _devices.end(); ++it ) {
664         MediaSourceRef media( new MediaSource( *it));
665         if (media->name != ejectDev)
666           continue;
667
668         bool        valid=false;
669         PathInfo    dinfo(media->name);
670         if( dinfo.isBlk())
671         {
672           media->maj_nr = dinfo.major();
673           media->min_nr = dinfo.minor();
674
675           DeviceList::const_iterator d( detected.begin());
676           for( ; d != detected.end(); ++d)
677           {
678             if( media->equals( *d))
679             {
680               valid = true;
681               break;
682             }
683           }
684         }
685         if( !valid)
686         {
687           DBG << "skipping invalid device: " << it->name << endl;
688           continue;
689         }
690
691         // FIXME: we have also to check if it is mounted in the system
692         AttachedMedia ret( findAttachedMedia( media));
693         if( !ret.mediaSource)
694         {
695 #if FORCE_RELEASE_FOREIGN > 0
696           /* 1 = automounted only, 2 = all */
697           forceRelaseAllMedia(media, false, FORCE_RELEASE_FOREIGN == 1);
698 #endif
699           if ( openTray( it->name ) )
700           {
701             ejected = true;
702             break; // on 1st success
703           }
704         }
705       }
706     }
707     if( !ejected)
708     {
709 #if REPORT_EJECT_ERRORS
710       ZYPP_THROW(MediaNotEjectedException());
711 #endif
712     }
713   }
714
715   bool MediaCD::isAutoMountedMedia(const AttachedMedia &media)
716   {
717     bool is_automounted = false;
718     if( media.mediaSource && !media.mediaSource->name.empty())
719     {
720 #ifndef NO_HAL
721       using namespace zypp::target::hal;
722       try
723       {
724         HalContext hal(true);
725
726         HalVolume vol = hal.getVolumeFromDeviceFile(media.mediaSource->name);
727         if( vol)
728         {
729           std::string udi = vol.getUDI();
730           std::string key;
731           std::string mnt;
732
733           try
734           {
735             key = "info.hal_mount.created_mount_point";
736             mnt = hal.getDevicePropertyString(udi, key);
737
738             if(media.attachPoint->path == mnt)
739               is_automounted = true;
740           }
741           catch(const HalException &e1)
742           {
743             ZYPP_CAUGHT(e1);
744
745             try
746             {
747               key = "volume.mount_point";
748               mnt = hal.getDevicePropertyString(udi, key);
749
750               if(media.attachPoint->path == mnt)
751                 is_automounted = true;
752             }
753             catch(const HalException &e2)
754             {
755               ZYPP_CAUGHT(e2);
756             }
757           }
758         }
759       }
760       catch(const HalException &e)
761       {
762         ZYPP_CAUGHT(e);
763       }
764 #else // NO_HAL
765 #warning Can not detect automounted media without HAL
766     INT << "Can not detect automounted media without HAL!" << endl;
767     // ma@: This codepath is probably unused due to 'REUSE_FOREIGN_MOUNTS == 2'
768     // Maybe we should cleanup all this automount-specail-handling.
769 #endif
770     }
771     DBG << "Media "        << media.mediaSource->asString()
772         << " attached on " << media.attachPoint->path
773         << " is"           << (is_automounted ? "" : " not")
774         << " automounted"  << std::endl;
775     return is_automounted;
776   }
777
778   ///////////////////////////////////////////////////////////////////
779   //
780   //  METHOD NAME : MediaCD::isAttached
781   //  METHOD TYPE : bool
782   //
783   //  DESCRIPTION : Override check if media is attached.
784   //
785   bool
786   MediaCD::isAttached() const
787   {
788     return checkAttached(false);
789   }
790
791   ///////////////////////////////////////////////////////////////////
792   //
793   //  METHOD NAME : MediaCD::getFile
794   //  METHOD TYPE : PMError
795   //
796   //  DESCRIPTION : Asserted that media is attached.
797   //
798   void MediaCD::getFile( const Pathname & filename ) const
799   {
800     MediaHandler::getFile( filename );
801   }
802
803   ///////////////////////////////////////////////////////////////////
804   //
805   //  METHOD NAME : MediaCD::getDir
806   //  METHOD TYPE : PMError
807   //
808   //  DESCRIPTION : Asserted that media is attached.
809   //
810   void MediaCD::getDir( const Pathname & dirname, bool recurse_r ) const
811   {
812     MediaHandler::getDir( dirname, recurse_r );
813   }
814
815   ///////////////////////////////////////////////////////////////////
816   //
817   //
818   //  METHOD NAME : MediaCD::getDirInfo
819   //  METHOD TYPE : PMError
820   //
821   //  DESCRIPTION : Asserted that media is attached and retlist is empty.
822   //
823   void MediaCD::getDirInfo( std::list<std::string> & retlist,
824                             const Pathname & dirname, bool dots ) const
825   {
826     MediaHandler::getDirInfo( retlist, dirname, dots );
827   }
828
829   ///////////////////////////////////////////////////////////////////
830   //
831   //
832   //  METHOD NAME : MediaCD::getDirInfo
833   //  METHOD TYPE : PMError
834   //
835   //  DESCRIPTION : Asserted that media is attached and retlist is empty.
836   //
837   void MediaCD::getDirInfo( filesystem::DirContent & retlist,
838                             const Pathname & dirname, bool dots ) const
839   {
840     MediaHandler::getDirInfo( retlist, dirname, dots );
841   }
842
843   bool MediaCD::getDoesFileExist( const Pathname & filename ) const
844   {
845     return MediaHandler::getDoesFileExist( filename );
846   }
847
848   bool MediaCD::hasMoreDevices()
849   {
850     if (_devices.size() == 0)
851       return false;
852     else if (_lastdev_tried < 0)
853       return true;
854
855     return (unsigned) _lastdev_tried < _devices.size() - 1;
856   }
857
858   void
859   MediaCD::getDetectedDevices(std::vector<std::string> & devices,
860                               unsigned int & index) const
861   {
862     index = 0;
863     if (!devices.empty())
864       devices.clear();
865
866     for (DeviceList::const_iterator it = _devices.begin();
867          it != _devices.end(); ++it)
868       devices.push_back(it->name);
869
870     if (_lastdev >= 0)
871       index = _lastdev;
872
873     // try to detect again if _devices are empty (maybe this method will be
874     // called before _devices get actually filled)
875     if (devices.empty())
876     {
877       DBG << "no device list so far, trying to detect" << endl;
878
879       DeviceList detected(
880         detectDevices(_url.getScheme() == "dvd" ? true : false));
881
882       for (DeviceList::const_iterator it = detected.begin();
883            it != detected.end(); ++it)
884         devices.push_back(it->name);
885
886       // don't know which one is in use in this case
887       index = 0;
888     }
889
890     MIL << "got " << devices.size() << " detected devices, current: "
891         << (index < devices.size() ? devices[index] : "<none>")
892         << "(" << index << ")" << endl;
893   }
894
895
896   } // namespace media
897 } // namespace zypp
898 // vim: set ts=8 sts=2 sw=2 ai noet: