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