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