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