libzypp 17.36.1
MediaCD.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
12extern "C"
13{
14#include <sys/ioctl.h>
15#include <linux/cdrom.h>
16#if HAVE_UDEV
17#include <libudev.h>
18#endif
19}
20
21#include <cstring> // strerror
22#include <cstdlib> // getenv
23#include <iostream>
24
25#include <zypp/base/Logger.h>
27#include <zypp-media/Mount>
28#include <zypp-media/CDTools>
29#include <zypp/media/MediaCD.h>
31#include <zypp/Url.h>
32#include <zypp/AutoDispose.h>
33
34using std::endl;
35
36/*
37** if to throw exception on eject errors or ignore them
38*/
39#define REPORT_EJECT_ERRORS 0
40
41
43namespace zypp
44{
46 namespace media
47 {
48
50 namespace
51 {
52 using DeviceList = std::list<MediaSource>;
53
59 DeviceList systemDetectDevices( bool supportingDVD_r )
60 {
61 DeviceList detected;
62
63#ifdef HAVE_UDEV
64 // http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/index.html
65 zypp::AutoDispose<struct udev *> udev( ::udev_new(), ::udev_unref );
66 if ( ! udev )
67 {
68 ERR << "Can't create udev context." << endl;
69 return DeviceList();
70 }
71
72 zypp::AutoDispose<struct udev_enumerate *> enumerate( ::udev_enumerate_new(udev), ::udev_enumerate_unref );
73 if ( ! enumerate )
74 {
75 ERR << "Can't create udev list entry." << endl;
76 return DeviceList();
77 }
78
79 ::udev_enumerate_add_match_subsystem( enumerate, "block" );
80 ::udev_enumerate_add_match_property( enumerate, "ID_CDROM", "1" );
81 ::udev_enumerate_scan_devices( enumerate );
82
83 struct udev_list_entry * entry = 0;
84 udev_list_entry_foreach( entry, ::udev_enumerate_get_list_entry( enumerate ) )
85 {
86 zypp::AutoDispose<struct udev_device *> device( ::udev_device_new_from_syspath( ::udev_enumerate_get_udev( enumerate ),
87 ::udev_list_entry_get_name( entry ) ),
88 ::udev_device_unref );
89 if ( ! device )
90 {
91 ERR << "Can't create udev device." << endl;
92 continue;
93 }
94
95 if ( supportingDVD_r && ! ::udev_device_get_property_value( device, "ID_CDROM_DVD" ) )
96 {
97 continue; // looking for dvd only
98 }
99
100 const char * devnodePtr( ::udev_device_get_devnode( device ) );
101 if ( ! devnodePtr )
102 {
103 ERR << "Got NULL devicenode." << endl;
104 continue;
105 }
106
107 // In case we need it someday:
108 //const char * mountpath = ::udev_device_get_property_value( device, "FSTAB_DIR" );
109
111 if ( devnode.isBlk() )
112 {
113 MediaSource media( "cdrom", devnode.path().asString(), devnode.devMajor(), devnode.devMinor() );
114 DBG << "Found (udev): " << media << std::endl;
115 detected.push_back( media );
116 }
117 }
118 if ( detected.empty() )
119 {
120 WAR << "Did not find any CD/DVD device." << endl;
121 }
122#endif
123 return detected;
124 }
125
126 } // namespace
128
129
131 : MediaHandler( url_r, attach_point_hint_r, url_r.getPathName(), false )
132 , _lastdev( -1 )
133 , _lastdev_tried( -1 )
134 {
135 MIL << "MediaCD::MediaCD(" << url_r << ", " << attach_point_hint_r << ")" << endl;
136
137 if ( url_r.getScheme() != "dvd" && url_r.getScheme() != "cd" )
138 {
139 ERR << "Unsupported schema in the Url: " << url_r.asString() << endl;
141 }
142
143 std::string devices = _url.getQueryParam( "devices" );
144 if ( ! devices.empty() )
145 {
146 std::vector<std::string> words;
147 str::split( devices, std::back_inserter(words), "," );
148 for ( const std::string & device : words )
149 {
150 if ( device.empty() )
151 continue;
152
153 MediaSource media( "cdrom", device, 0, 0 );
154 _devices.push_back( media );
155 DBG << "use device (delayed verify)" << device << endl;
156 }
157 }
158 else
159 {
160 DBG << "going to use on-demand device list" << endl;
161 return;
162 }
163
164 if ( _devices.empty() )
165 {
166 ERR << "Unable to find any cdrom drive for " << _url.asString() << endl;
168 }
169 }
170
172 //
173 //
174 // METHOD NAME : MediaCD::openTray
175 // METHOD TYPE : bool
176 //
177 bool MediaCD::openTray( const std::string & device_r )
178 {
180 }
181
183 //
184 //
185 // METHOD NAME : MediaCD::closeTray
186 // METHOD TYPE : bool
187 //
188 bool MediaCD::closeTray( const std::string & device_r )
189 {
191 }
192
193
195 {
197
198 if ( detected.empty() )
199 {
200 WAR << "CD/DVD drive detection with UDEV failed! Guessing..." << std::endl;
201 PathInfo dvdinfo( "/dev/dvd" );
202 PathInfo cdrinfo( "/dev/cdrom" );
203 if ( dvdinfo.isBlk() )
204 {
205 MediaSource media( "cdrom", dvdinfo.path().asString(), dvdinfo.devMajor(), dvdinfo.devMinor() );
206 DBG << "Found (GUESS): " << media << std::endl;
207 detected.push_back( media );
208 }
209 if ( cdrinfo.isBlk()
210 && ! ( cdrinfo.devMajor() == dvdinfo.devMajor() && cdrinfo.devMinor() == dvdinfo.devMinor() ) )
211 {
212 MediaSource media( "cdrom", cdrinfo.path().asString(), cdrinfo.devMajor(), cdrinfo.devMinor() );
213 DBG << "Found (GUESS): " << media << std::endl;
214 detected.push_back( media );
215 }
216 }
217
218 // NOTE: On the fly build on-demand device list. Code was moved to
219 // here to get rid of code duplication, while keeping the ABI. Acuallty
220 // this code should be moved to a _devices accessor method.
221 if ( _devices.empty() )
222 {
223 DBG << "creating on-demand device list" << endl;
224 //default is /dev/cdrom; for dvd: /dev/dvd if it exists
225 std::string device( "/dev/cdrom" );
226 if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() )
227 {
228 device = "/dev/dvd";
229 }
230
231 PathInfo dinfo( device );
232 if ( dinfo.isBlk() )
233 {
234 MediaSource media( "cdrom", device, dinfo.devMajor(), dinfo.devMinor() );
235 if ( detected.empty() )
236 {
237 _devices.push_front( media ); // better try this than nothing
238 }
239 else
240 {
241 for( const auto & d : detected )
242 {
243 // /dev/cdrom or /dev/dvd to the front
244 if ( media.equals( d ) )
245 _devices.push_front( d );
246 else
247 _devices.push_back( d );
248 }
249 }
250 }
251 else
252 {
253 // no /dev/cdrom or /dev/dvd link
255 }
256 }
257
258 return detected;
259 }
260
261
263 //
264 //
265 // METHOD NAME : MediaCD::attachTo
266 // METHOD TYPE : PMError
267 //
268 // DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
269 //
270 void MediaCD::attachTo( bool next )
271 {
272 DBG << "next " << next << " last " << _lastdev << " last tried " << _lastdev_tried << endl;
273 if ( next && _lastdev == -1 )
275
276 // This also fills the _devices list on demand
277 DeviceList detected( detectDevices( _url.getScheme() == "dvd" ? true : false ) );
278
279 Mount mount;
281
282 std::string options = _url.getQueryParam( "mountoptions" );
283 if ( options.empty() )
284 {
285 options="ro";
286 }
287
288 //TODO: make configurable
289 std::list<std::string> filesystems;
290
291 filesystems.push_back("iso9660");
292
293 // if DVD, try UDF filesystem after iso9660
294 if ( _url.getScheme() == "dvd" )
295 filesystems.push_back("udf");
296
297 // try all devices in sequence
298 int count = 0;
299 std::string mountpoint( attachPoint().asString() );
300 bool mountsucceeded = false;
301 for ( DeviceList::iterator it = _devices.begin() ; ! mountsucceeded && it != _devices.end() ; ++it, ++count )
302 {
303 DBG << "count " << count << endl;
304 if (next && count <=_lastdev_tried )
305 {
306 DBG << "skipping device " << it->name << endl;
307 continue;
308 }
310
311 // bnc#755815: _devices contains either devices passed as url option
312 // or autodetected ones. Accept both as long as they are block
313 // devices.
314 MediaSource temp( *it );
315 PathInfo dinfo( temp.name );
316 if ( ! dinfo.isBlk() )
317 {
318 WAR << "skipping non block device: " << dinfo << endl;
319 continue;
320 }
321 DBG << "trying device " << dinfo << endl;
322
323 temp.maj_nr = dinfo.devMajor();
324 temp.min_nr = dinfo.devMinor();
325 MediaSourceRef media( new MediaSource(temp));
327
328 if( ret.mediaSource && ret.attachPoint &&
329 !ret.attachPoint->empty())
330 {
331 DBG << "Using a shared media "
332 << ret.mediaSource->name
333 << " attached on "
334 << ret.attachPoint->path
335 << endl;
337 setAttachPoint(ret.attachPoint);
338 setMediaSource(ret.mediaSource);
339 _lastdev = count;
340 mountsucceeded = true;
341 break;
342 }
343
344 {
346 MountEntries entries( manager.getMountEntries());
347 MountEntries::const_iterator e;
348 for( e = entries.begin(); e != entries.end(); ++e)
349 {
350 bool is_device = false;
351 std::string dev_path(Pathname(e->src).asString());
353
354 if( dev_path.compare(0, sizeof("/dev/")-1, "/dev/") == 0 &&
355 dev_info(e->src) && dev_info.isBlk())
356 {
357 is_device = true;
358 }
359
360 if( is_device && media->maj_nr == dev_info.devMajor() &&
361 media->min_nr == dev_info.devMinor())
362 {
363 AttachPointRef ap( new AttachPoint(e->dir, false));
364 AttachedMedia am( media, ap);
365 {
366 DBG << "Using a system mounted media "
367 << media->name
368 << " attached on "
369 << ap->path
370 << endl;
371
372 media->iown = false; // mark attachment as foreign
373
374 setMediaSource(media);
376 _lastdev = count;
377 mountsucceeded = true;
378 break;
379 }
380 }
381 }
382 if( mountsucceeded)
383 break;
384 }
385
386 // close tray
387 closeTray( it->name );
388
389 // try all filesystems in sequence
390 for(std::list<std::string>::iterator fsit = filesystems.begin()
391 ; !mountsucceeded && fsit != filesystems.end()
392 ; ++fsit)
393 {
394 try
395 {
397 {
400 }
401
402 mount.mount(it->name, mountpoint, *fsit, options);
403
404 setMediaSource(media);
405
406 // wait for /etc/mtab update ...
407 // (shouldn't be needed)
408 int limit = 2;
409 while( !(mountsucceeded=isAttached()) && --limit)
410 {
411 WAR << "Wait for /proc/mounts update and retry...." << endl;
412 sleep(1);
413 }
414
415 if( mountsucceeded)
416 {
417 _lastdev = count;
418 }
419 else
420 {
422 try
423 {
424 mount.umount(attachPoint().asString());
425 }
426 catch (const MediaException & excpt_r)
427 {
429 }
431 "Unable to verify that the media was mounted",
432 it->name, mountpoint
433 ));
434 }
435 }
436 catch (const MediaMountException &e)
437 {
438 merr = e;
440 ZYPP_CAUGHT(e);
441 }
442 catch (const MediaException & excpt_r)
443 {
446 }
447 } // for filesystems
448 } // for _devices
449
450 if (!mountsucceeded)
451 {
452 _lastdev = -1;
453
454 if( !merr.mountOutput().empty())
455 {
457 _url.asString(),
459 merr.mountOutput()));
460 }
461 else
462 {
463 ZYPP_THROW(MediaMountException("Mounting media failed",
465 }
466 }
467 DBG << _lastdev << " " << count << endl;
468 }
469
470
472 //
473 //
474 // METHOD NAME : MediaCD::releaseFrom
475 // METHOD TYPE : PMError
476 //
477 // DESCRIPTION : Asserted that media is attached.
478 //
479 void MediaCD::releaseFrom( const std::string & ejectDev )
480 {
481 Mount mount;
482 try
483 {
485 if(am.mediaSource && am.mediaSource->iown)
486 mount.umount(am.attachPoint->path.asString());
487 }
488 catch (const Exception & excpt_r)
489 {
491 if (!ejectDev.empty())
492 {
493 forceRelaseAllMedia(false);
494 if(openTray( ejectDev ))
495 return;
496 }
498 }
499
500 // eject device
501 if (!ejectDev.empty())
502 {
503 forceRelaseAllMedia(false);
504 if( !openTray( ejectDev ))
505 {
506#if REPORT_EJECT_ERRORS
508#endif
509 }
510 }
511 }
512
514 //
515 //
516 // METHOD NAME : MediaCD::forceEject
517 // METHOD TYPE : void
518 //
519 // Asserted that media is not attached.
520 //
521 void MediaCD::forceEject( const std::string & ejectDev_r )
522 {
523#if REPORT_EJECT_ERRORS
524 bool ejected = false;
525#endif
526 if ( ! isAttached() ) // no device mounted in this instance
527 {
528 // This also fills the _devices list on demand
529 DeviceList detected( detectDevices( _url.getScheme() == "dvd" ? true : false ) );
530 for_( it, _devices.begin(), _devices.end() )
531 {
532 MediaSourceRef media( new MediaSource( *it ) );
533 if ( media->name != ejectDev_r )
534 continue;
535
536 // bnc#755815: _devices contains either devices passed as url option
537 // or autodetected ones. Accept both as long as they are block
538 // devices.
539 PathInfo dinfo( media->name );
540 if( ! dinfo.isBlk() )
541 {
542 WAR << "skipping non block device: " << dinfo << endl;
543 continue;
544 }
545 DBG << "trying device " << dinfo << endl;
546
547 // FIXME: we have also to check if it is mounted in the system
549 if( !ret.mediaSource )
550 {
551 forceRelaseAllMedia( media, false );
552 if ( openTray( it->name ) )
553 {
554#if REPORT_EJECT_ERRORS
555 ejected = true;
556#endif
557 break; // on 1st success
558 }
559 }
560 }
561 }
562#if REPORT_EJECT_ERRORS
563 if( !ejected)
564 {
566 }
567#endif
568 }
569
571 //
572 // METHOD NAME : MediaCD::isAttached
573 // METHOD TYPE : bool
574 //
575 // DESCRIPTION : Override check if media is attached.
576 //
577 bool
579 {
580 return checkAttached(false);
581 }
582
584 //
585 // METHOD NAME : MediaCD::getFile
586 // METHOD TYPE : PMError
587 //
588 // DESCRIPTION : Asserted that media is attached.
589 //
590 void MediaCD::getFile( const OnMediaLocation &file ) const
591 {
592 MediaHandler::getFile( file );
593 }
594
596 //
597 // METHOD NAME : MediaCD::getDir
598 // METHOD TYPE : PMError
599 //
600 // DESCRIPTION : Asserted that media is attached.
601 //
602 void MediaCD::getDir( const Pathname & dirname, bool recurse_r ) const
603 {
605 }
606
608 //
609 //
610 // METHOD NAME : MediaCD::getDirInfo
611 // METHOD TYPE : PMError
612 //
613 // DESCRIPTION : Asserted that media is attached and retlist is empty.
614 //
615 void MediaCD::getDirInfo( std::list<std::string> & retlist,
616 const Pathname & dirname, bool dots ) const
617 {
619 }
620
622 //
623 //
624 // METHOD NAME : MediaCD::getDirInfo
625 // METHOD TYPE : PMError
626 //
627 // DESCRIPTION : Asserted that media is attached and retlist is empty.
628 //
629 void MediaCD::getDirInfo( filesystem::DirContent & retlist, const Pathname & dirname, bool dots ) const
630 {
632 }
633
634
635 bool MediaCD::getDoesFileExist( const Pathname & filename ) const
636 {
637 return MediaHandler::getDoesFileExist( filename );
638 }
639
640
642 {
643 if (_devices.size() == 0)
644 return false;
645 else if (_lastdev_tried < 0)
646 return true;
647
648 return (unsigned) _lastdev_tried < _devices.size() - 1;
649 }
650
651
652 void MediaCD::getDetectedDevices( std::vector<std::string> & devices, unsigned int & index ) const
653 {
654 if ( ! devices.empty() )
655 devices.clear();
656
657 if ( _devices.empty() )
658 // This also fills the _devices list on demand
659 detectDevices( _url.getScheme() == "dvd" ? true : false );
660
661 for ( const auto & it : _devices )
662 devices.push_back( it.name );
663
664 index = ( _lastdev >= 0 ? (unsigned)_lastdev : 0 );
665
666 MIL << "got " << devices.size() << " detected devices, current: "
667 << (index < devices.size() ? devices[index] : "<none>")
668 << "(" << index << ")" << endl;
669 }
670
671 } // namespace media
673} // namespace zypp
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition AutoDispose.h:95
Base class for Exception.
Definition Exception.h:147
Describes a resource file located on a medium.
Url manipulation class.
Definition Url.h:93
std::string getScheme() const
Returns the scheme name of the URL.
Definition Url.cc:551
std::string asString() const
Returns a default string representation of the Url object.
Definition Url.cc:515
std::string getQueryParam(const std::string &param, EEncoding eflag=zypp::url::E_DECODED) const
Return the value for the specified query parameter.
Definition Url.cc:678
Wrapper class for stat/lstat.
Definition PathInfo.h:226
const std::string & asString() const
String representation.
Definition Pathname.h:93
Attach point of a media source.
static bool openTray(const std::string &device_r)
Definition cdtools.cc:33
static bool closeTray(const std::string &device_r)
Definition cdtools.cc:86
static bool closeTray(const std::string &device_r)
Definition MediaCD.cc:188
bool isAttached() const override
True if media is attached.
Definition MediaCD.cc:578
void forceEject(const std::string &ejectDev) override
Call concrete handler to physically eject the media (i.e.
Definition MediaCD.cc:521
void getDirInfo(std::list< std::string > &retlist, const Pathname &dirname, bool dots=true) const override
Call concrete handler to provide a content list of directory on media via retlist.
Definition MediaCD.cc:615
void getDetectedDevices(std::vector< std::string > &devices, unsigned int &index) const override
Fill in a vector of detected ejectable devices and the index of the currently attached device within ...
Definition MediaCD.cc:652
MediaCD(const Url &url_r, const Pathname &attach_point_hint_r)
Definition MediaCD.cc:130
void getFile(const OnMediaLocation &file) const override
Call concrete handler to provide file below attach point.
Definition MediaCD.cc:590
void getDir(const Pathname &dirname, bool recurse_r) const override
Call concrete handler to provide directory content (not recursive!) below attach point.
Definition MediaCD.cc:602
void releaseFrom(const std::string &ejectDev) override
Call concrete handler to release the media.
Definition MediaCD.cc:479
bool getDoesFileExist(const Pathname &filename) const override
check if a file exists
Definition MediaCD.cc:635
std::list< MediaSource > DeviceList
Definition MediaCD.h:31
int _lastdev
number of last successful mounted device in list
Definition MediaCD.h:36
static bool openTray(const std::string &device_r)
Definition MediaCD.cc:177
DeviceList detectDevices(bool supportingDVD) const
Definition MediaCD.cc:194
bool hasMoreDevices() override
Check if the media has one more device available for attach(true).
Definition MediaCD.cc:641
DeviceList _devices
list of devices to try to mount
Definition MediaCD.h:33
void attachTo(bool next=false) override
Call concrete handler to attach the media.
Definition MediaCD.cc:270
Just inherits Exception to separate media exceptions.
Abstract base class for 'physical' MediaHandler like MediaCD, etc.
bool isUseableAttachPoint(const Pathname &path, bool mtab=true) const
Ask media manager, if the specified path is already used as attach point or if there are another atta...
virtual void getFile(const OnMediaLocation &file) const
Call concrete handler to provide file below attach point.
Url url() const
Url used.
virtual bool getDoesFileExist(const Pathname &filename) const =0
check if a file exists
Pathname createAttachPoint() const
Try to create a default / temporary attach point.
void setMediaSource(const MediaSourceRef &ref)
Set new media source reference.
void forceRelaseAllMedia(bool matchMountFs)
Call to this function will try to release all media matching the currenlty attached media source,...
const Url _url
Url to handle.
bool checkAttached(bool matchMountFs) const
Check actual mediaSource attachment against the current mount table of the system.
void removeAttachPoint()
Remove unused attach point.
void setAttachPoint(const Pathname &path, bool temp)
Set a new attach point.
Pathname attachPoint() const
Return the currently used attach point.
virtual void getDir(const Pathname &dirname, bool recurse_r) const =0
Call concrete handler to provide directory content (not recursive!) below attach point.
AttachedMedia findAttachedMedia(const MediaSourceRef &media) const
Ask the media manager if specified media source is already attached.
AttachedMedia attachedMedia() const
Returns the attached media.
virtual void getDirInfo(std::list< std::string > &retlist, const Pathname &dirname, bool dots=true) const =0
Call concrete handler to provide a content list of directory on media via retlist.
Manages access to the 'physical' media, e.g CDROM drives, Disk volumes, directory trees,...
Media source internally used by MediaManager and MediaHandler.
Definition MediaSource.h:38
unsigned int min_nr
A minor number if source is a device.
Definition MediaSource.h:91
unsigned int maj_nr
A major number if source is a device.
Definition MediaSource.h:90
std::string name
A media handler specific source name.
Definition MediaSource.h:93
virtual bool equals(const MediaSource &src) const
Check if the both sources are equal.
Definition MediaSource.h:63
Interface to the mount program.
Definition mount.h:76
void umount(const std::string &path)
umount device
Definition mount.cc:117
void mount(const std::string &source, const std::string &target, const std::string &filesystem, const std::string &options, const Environment &environment=Environment())
mount device
Definition mount.cc:62
std::list< DirEntry > DirContent
Returned by readdir.
Definition PathInfo.h:526
zypp::RW_pointer< MediaSource > MediaSourceRef
unsigned split(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \t", const Trim trim_r=NO_TRIM)
Split line_r into words.
Definition String.h:531
Easy-to use interface to the ZYPP dependency resolver.
std::string asString(const Patch::Category &obj)
Definition Patch.cc:122
A simple structure containing references to a media source and its attach point.
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition Easy.h:27
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition Exception.h:444
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition Exception.h:440
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition Exception.h:424
#define DBG
Definition Logger.h:99
#define MIL
Definition Logger.h:100
#define ERR
Definition Logger.h:102
#define WAR
Definition Logger.h:101