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