/** \file zypp/media/MediaCD.cc
*
*/
+extern "C"
+{
+#include <sys/ioctl.h>
+#include <linux/cdrom.h>
+#if HAVE_UDEV
+#include <libudev.h>
+#endif
+}
+#ifndef HAVE_UDEV
+#if HAVE_HAL
+#include "zypp/target/hal/HalContext.h"
+#endif
+#endif
+
+#include <cstring> // strerror
+#include <cstdlib> // getenv
#include <iostream>
#include "zypp/base/Logger.h"
#include "zypp/media/MediaCD.h"
#include "zypp/media/MediaManager.h"
#include "zypp/Url.h"
-#include "zypp/target/hal/HalContext.h"
+#include "zypp/AutoDispose.h"
-#include <cstring> // strerror
-
-#include <errno.h>
-#include <dirent.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include <linux/cdrom.h>
/*
-** verify devices names as late as possible (while attach)
+** if to throw exception on eject errors or ignore them
*/
-#define DELAYED_VERIFY 1
+#define REPORT_EJECT_ERRORS 0
/*
-** try umount of foreign (user/automounter) media on eject
-** 0 = don't force, 1 = automounted only, 2 == all
+** If defined to the full path of the eject utility,
+** it will be used additionally to the eject-ioctl.
*/
-#define FORCE_RELEASE_FOREIGN 1
+#define EJECT_TOOL_PATH "/bin/eject"
-/*
-** if to throw exception on eject errors or ignore them
-*/
-#define REPORT_EJECT_ERRORS 1
using namespace std;
-namespace zypp {
- namespace media {
+//////////////////////////////////////////////////////////////////
+namespace zypp
+{
+ //////////////////////////////////////////////////////////////////
+ namespace media
+ {
- namespace {
- bool isNewDevice(const std::list<MediaSource> &devices,
- const MediaSource &media)
+ //////////////////////////////////////////////////////////////////
+ namespace
+ {
+ typedef std::list<MediaSource> DeviceList;
+
+ //////////////////////////////////////////////////////////////////
+ /// \brief Try to detect cd/dvd devices using hal/udev
+ ///
+ /// Returns an empty device list on error.
+ ///
+ /// \todo I took the code more or less as it was from MediaCD::detectDevices
+ /// into this function. Semantic between HAL and UDEV seems to be slightly
+ /// different, esp. in supportingDVD mode. This should be investigated and fixed.
+ //////////////////////////////////////////////////////////////////
+ DeviceList systemDetectDevices( bool supportingDVD_r )
{
- std::list<MediaSource>::const_iterator d( devices.begin());
- for( ; d != devices.end(); ++d)
+ DeviceList detected;
+
+#ifdef HAVE_UDEV
+ // http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/index.html
+ zypp::AutoDispose<struct udev *> udev( ::udev_new(), ::udev_unref );
+ if ( ! udev )
{
- if( media.equals( *d))
- return false;
+ ERR << "Can't create udev context." << endl;
+ return DeviceList();
}
- return true;
- }
- }
-///////////////////////////////////////////////////////////////////
-//
-// CLASS NAME : MediaCD
-//
-///////////////////////////////////////////////////////////////////
-
- ///////////////////////////////////////////////////////////////////
- //
- //
- // METHOD NAME : MediaCD::MediaCD
- // METHOD TYPE : Constructor
- //
- // DESCRIPTION :
- //
- MediaCD::MediaCD( const Url & url_r,
- const Pathname & attach_point_hint_r )
- : MediaHandler( url_r, attach_point_hint_r,
- url_r.getPathName(), // urlpath below attachpoint
- false )
- //, does_download
- , _lastdev(-1)
- {
- MIL << "MediaCD::MediaCD(" << url_r << ", "
- << attach_point_hint_r << ")" << endl;
+ zypp::AutoDispose<struct udev_enumerate *> enumerate( ::udev_enumerate_new(udev), ::udev_enumerate_unref );
+ if ( ! enumerate )
+ {
+ ERR << "Can't create udev list entry." << endl;
+ return DeviceList();
+ }
- if( url_r.getScheme() != "dvd" && url_r.getScheme() != "cd")
- {
- ERR << "Unsupported schema in the Url: " << url_r.asString()
- << std::endl;
- ZYPP_THROW(MediaUnsupportedUrlSchemeException(_url));
- }
+ ::udev_enumerate_add_match_subsystem( enumerate, "block" );
+ ::udev_enumerate_add_match_property( enumerate, "ID_CDROM", "1" );
+ ::udev_enumerate_scan_devices( enumerate );
-#if !DELAYED_VERIFY
- DeviceList detected( detectDevices(
- url_r.getScheme() == "dvd" ? true : false
- ));
-#endif
+ struct udev_list_entry * entry = 0;
+ udev_list_entry_foreach( entry, ::udev_enumerate_get_list_entry( enumerate ) )
+ {
+ zypp::AutoDispose<struct udev_device *> device( ::udev_device_new_from_syspath( ::udev_enumerate_get_udev( enumerate ),
+ ::udev_list_entry_get_name( entry ) ),
+ ::udev_device_unref );
+ if ( ! device )
+ {
+ ERR << "Can't create udev device." << endl;
+ continue;
+ }
- string devices = _url.getQueryParam("devices");
- if (!devices.empty())
- {
- string::size_type pos;
- DBG << "parse " << devices << endl;
- while(!devices.empty())
- {
- pos = devices.find(',');
- string device = devices.substr(0,pos);
- if (!device.empty())
- {
-#if DELAYED_VERIFY
- MediaSource media("cdrom", device, 0, 0);
- _devices.push_back( media);
- DBG << "use device (delayed verify)" << device << endl;
-#else
- bool is_ok = false;
- PathInfo dinfo(device);
- if( dinfo.isBlk())
- {
- MediaSource media("cdrom", device, dinfo.major(),
- dinfo.minor());
- DeviceList::const_iterator d( detected.begin());
- for( ; d != detected.end(); ++d)
- {
- if( media.equals( *d))
- {
- is_ok = true;
- _devices.push_back( *d);
- DBG << "use device " << device << endl;
- }
- }
- }
+ if ( supportingDVD_r && ! ::udev_device_get_property_value( device, "ID_CDROM_DVD" ) )
+ {
+ continue; // looking for dvd only
+ }
- if( !is_ok)
- {
- ERR << "Device " << device << " is not acceptable "
- << "for " << _url.getScheme() << std::endl;
- ZYPP_THROW(MediaBadUrlException(_url,
- "Invalid device name in URL devices argument"
- ));
- }
-#endif
- }
- if (pos!=string::npos)
- devices=devices.substr(pos+1);
- else
- devices.erase();
- }
- }
- else
- {
-#if DELAYED_VERIFY
- DBG << "going to use on-demand device list" << endl;
- return;
-#else
- DBG << "going to use default device list" << endl;
- //default is /dev/cdrom; for dvd: /dev/dvd if it exists
- string device( "/dev/cdrom" );
- if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
- device = "/dev/dvd";
- }
-
- PathInfo dinfo(device);
- if( dinfo.isBlk())
+ const char * devnodePtr( ::udev_device_get_devnode( device ) );
+ if ( ! devnodePtr )
+ {
+ ERR << "Got NULL devicenode." << endl;
+ continue;
+ }
+
+ // In case we need it someday:
+ //const char * mountpath = ::udev_device_get_property_value( device, "FSTAB_DIR" );
+
+ PathInfo devnode( devnodePtr );
+ if ( devnode.isBlk() )
+ {
+ MediaSource media( "cdrom", devnode.path().asString(), devnode.devMajor(), devnode.devMinor() );
+ DBG << "Found (udev): " << media << std::endl;
+ detected.push_back( media );
+ }
+ }
+ if ( detected.empty() )
{
- MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
+ WAR << "Did not find any CD/DVD device." << endl;
+ }
+#elif HAVE_HAL
+ using namespace zypp::target::hal;
+ try
+ {
+ HalContext hal(true);
- DeviceList::const_iterator d( detected.begin());
- for( ; d != detected.end(); ++d)
+ std::vector<std::string> drv_udis;
+ drv_udis = hal.findDevicesByCapability("storage.cdrom");
+
+ DBG << "Found " << drv_udis.size() << " cdrom drive udis" << std::endl;
+ for(size_t d = 0; d < drv_udis.size(); d++)
{
- // /dev/cdrom or /dev/dvd to the front
- if( media.equals( *d))
- _devices.push_front( *d);
- else
- _devices.push_back( *d);
+ HalDrive drv( hal.getDriveFromUDI( drv_udis[d]));
+
+ if( drv)
+ {
+ bool supportsDVD=false;
+ if( supportingDVD_r)
+ {
+ std::vector<std::string> caps;
+ try {
+ caps = drv.getCdromCapabilityNames();
+ }
+ catch(const HalException &e)
+ {
+ ZYPP_CAUGHT(e);
+ }
+
+ std::vector<std::string>::const_iterator ci;
+ for( ci=caps.begin(); ci != caps.end(); ++ci)
+ {
+ if( *ci == "dvd")
+ supportsDVD = true;
+ }
+ }
+
+ MediaSource media("cdrom", drv.getDeviceFile(),
+ drv.getDeviceMajor(),
+ drv.getDeviceMinor());
+ DBG << "Found " << drv_udis[d] << ": "
+ << media.asString() << std::endl;
+ if( supportingDVD_r && supportsDVD)
+ {
+ detected.push_front(media);
+ }
+ else
+ {
+ detected.push_back(media);
+ }
+ }
}
}
- else
+ catch(const zypp::target::hal::HalException &e)
{
- // no /dev/cdrom or /dev/dvd link
- _devices = detected;
+ ZYPP_CAUGHT(e);
}
#endif
+ return detected;
}
- if( _devices.empty())
- {
- ERR << "Unable to find any cdrom drive for " << _url.asString()
- << std::endl;
- ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
- }
+ } // namespace
+ //////////////////////////////////////////////////////////////////
+
+
+ MediaCD::MediaCD( const Url & url_r, const Pathname & attach_point_hint_r )
+ : MediaHandler( url_r, attach_point_hint_r, url_r.getPathName(), false )
+ , _lastdev( -1 )
+ , _lastdev_tried( -1 )
+ {
+ MIL << "MediaCD::MediaCD(" << url_r << ", " << attach_point_hint_r << ")" << endl;
+
+ if ( url_r.getScheme() != "dvd" && url_r.getScheme() != "cd" )
+ {
+ ERR << "Unsupported schema in the Url: " << url_r.asString() << endl;
+ ZYPP_THROW(MediaUnsupportedUrlSchemeException(_url));
}
- ///////////////////////////////////////////////////////////////////
- //
- //
- // METHOD NAME : MediaCD::openTray
- // METHOD TYPE : bool
- //
- bool MediaCD::openTray( const string & device_r )
+ string devices = _url.getQueryParam( "devices" );
+ if ( ! devices.empty() )
{
- int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
- if ( fd == -1 ) {
- WAR << "Unable to open '" << device_r << "' (" << ::strerror( errno ) << ")" << endl;
- return false;
- }
- int res = ::ioctl( fd, CDROMEJECT );
- ::close( fd );
- if ( res ) {
- WAR << "Eject " << device_r << " failed (" << ::strerror( errno ) << ")" << endl;
- return false;
+ std::vector<std::string> words;
+ str::split( devices, std::back_inserter(words), "," );
+ for ( const std::string & device : words )
+ {
+ if ( device.empty() )
+ continue;
+
+ MediaSource media( "cdrom", device, 0, 0 );
+ _devices.push_back( media );
+ DBG << "use device (delayed verify)" << device << endl;
}
- MIL << "Eject " << device_r << endl;
- return true;
+ }
+ else
+ {
+ DBG << "going to use on-demand device list" << endl;
+ return;
}
- ///////////////////////////////////////////////////////////////////
- //
- //
- // METHOD NAME : MediaCD::closeTray
- // METHOD TYPE : bool
- //
- bool MediaCD::closeTray( const string & device_r )
+ if ( _devices.empty() )
{
- int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
- if ( fd == -1 ) {
- WAR << "Unable to open '" << device_r << "' (" << ::strerror( errno ) << ")" << endl;
- return false;
- }
- int res = ::ioctl( fd, CDROMCLOSETRAY );
+ ERR << "Unable to find any cdrom drive for " << _url.asString() << endl;
+ ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : MediaCD::openTray
+ // METHOD TYPE : bool
+ //
+ bool MediaCD::openTray( const std::string & device_r )
+ {
+ int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK|O_CLOEXEC );
+ int res = -1;
+
+ if ( fd != -1)
+ {
+ res = ::ioctl( fd, CDROMEJECT );
::close( fd );
- if ( res ) {
- WAR << "Close tray " << device_r << " failed (" << ::strerror( errno ) << ")" << endl;
- return false;
- }
- DBG << "Close tray " << device_r << endl;
- return true;
}
- ///////////////////////////////////////////////////////////////////
- //
- //
- // METHOD NAME : MediaCD::detectDevices
- // METHOD TYPE : MediaCD::DeviceList
- //
- MediaCD::DeviceList
- MediaCD::detectDevices(bool supportingDVD)
+ if ( res )
{
- using namespace zypp::target::hal;
-
- DeviceList detected;
- try
+ if( fd == -1)
{
- HalContext hal(true);
-
- std::vector<std::string> drv_udis;
- drv_udis = hal.findDevicesByCapability("storage.cdrom");
-
- DBG << "Found " << drv_udis.size() << " cdrom drive udis" << std::endl;
- for(size_t d = 0; d < drv_udis.size(); d++)
- {
- HalDrive drv( hal.getDriveFromUDI( drv_udis[d]));
+ WAR << "Unable to open '" << device_r
+ << "' (" << ::strerror( errno ) << ")" << endl;
+ }
+ else
+ {
+ WAR << "Eject " << device_r
+ << " failed (" << ::strerror( errno ) << ")" << endl;
+ }
- if( drv)
- {
- if( supportingDVD)
- {
- std::vector<std::string> caps;
- try {
- caps = drv.getCdromCapabilityNames();
- }
- catch(const HalException &e)
- {
- ZYPP_CAUGHT(e);
- }
+#if defined(EJECT_TOOL_PATH)
+ DBG << "Try to eject " << device_r << " using "
+ << EJECT_TOOL_PATH << " utility" << std::endl;
- bool found=false;
- std::vector<std::string>::const_iterator ci;
- for( ci=caps.begin(); ci != caps.end(); ++ci)
- {
- if( *ci == "dvd")
- found = true;
- }
- if( !found)
- continue;
- }
+ const char *cmd[3];
+ cmd[0] = EJECT_TOOL_PATH;
+ cmd[1] = device_r.c_str();
+ cmd[2] = NULL;
+ ExternalProgram eject(cmd, ExternalProgram::Stderr_To_Stdout);
- MediaSource media("cdrom", drv.getDeviceFile(),
- drv.getDeviceMajor(),
- drv.getDeviceMinor());
- DBG << "Found " << drv_udis[d] << ": "
- << media.asString() << std::endl;
- detected.push_back(media);
- }
- }
- }
- catch(const zypp::target::hal::HalException &e)
+ for(std::string out( eject.receiveLine());
+ out.length(); out = eject.receiveLine())
{
- ZYPP_CAUGHT(e);
+ DBG << " " << out;
}
- //
- // Bug #163971
- // Hal does not include SCSI / Virtual CDROMs on iSeries ...
- //
- // Hmm... always? We can't detect DVD here.
- if( detected.empty())
+ if(eject.close() != 0)
{
- std::string sys_name;
- std::string dev_name;
+ WAR << "Eject of " << device_r << " failed." << std::endl;
+ return false;
+ }
+#else
+ return false;
+#endif
+ }
+ MIL << "Eject of " << device_r << " successful." << endl;
+ return true;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : MediaCD::closeTray
+ // METHOD TYPE : bool
+ //
+ bool MediaCD::closeTray( const std::string & device_r )
+ {
+ int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK|O_CLOEXEC );
+ if ( fd == -1 ) {
+ WAR << "Unable to open '" << device_r << "' (" << ::strerror( errno ) << ")" << endl;
+ return false;
+ }
+ int res = ::ioctl( fd, CDROMCLOSETRAY );
+ ::close( fd );
+ if ( res ) {
+ WAR << "Close tray " << device_r << " failed (" << ::strerror( errno ) << ")" << endl;
+ return false;
+ }
+ DBG << "Close tray " << device_r << endl;
+ return true;
+ }
- // SCSI cdrom devices (/dev/sr0, ...)
- sys_name = "/dev/sr";
- dev_name = "/sys/block/sr";
- for(size_t i=0; i < 16; i++)
- {
- PathInfo sys_info(sys_name + str::numstring(i));
- PathInfo dev_info(dev_name + str::numstring(i));
- if( sys_info.isDir() && dev_info.isBlk())
- {
- // Hmm.. how to check if it supports DVDs?
- MediaSource media("cdrom", dev_info.asString(),
- dev_info.major(),
- dev_info.minor());
- if( isNewDevice(detected, media))
- {
- DBG << "Found SCSI CDROM "
- << media.asString()
- << std::endl;
- detected.push_back(media);
- }
- }
- }
- // IBM iSeries virtual CD-ROM devices (how many?)
-#if powerpc
- sys_name = "/sys/block/iseries!vcd";
- dev_name = "/dev/iseries/vcd";
- for(size_t i=0; i < 8; i++)
- {
- char drive_letter = 'a' + i;
- PathInfo sys_info(sys_name + drive_letter);
- PathInfo dev_info(dev_name + drive_letter);
- if( sys_info.isDir() && dev_info.isBlk())
- {
- // Hmm.. how to check if it supports DVDs?
- MediaSource media("cdrom", dev_info.asString(),
- dev_info.major(),
- dev_info.minor());
- if( isNewDevice(detected, media))
- {
- DBG << "Found iSeries virtual CDROM "
- << media.asString()
- << std::endl;
- detected.push_back(media);
- }
- }
- }
-#endif // powerpc
+ MediaCD::DeviceList MediaCD::detectDevices( bool supportingDVD_r ) const
+ {
+ DeviceList detected( systemDetectDevices( supportingDVD_r ) );
- // Other device types?
+ if ( detected.empty() )
+ {
+ WAR << "CD/DVD drive detection with HAL/UDEV failed! Guessing..." << std::endl;
+ PathInfo dvdinfo( "/dev/dvd" );
+ PathInfo cdrinfo( "/dev/cdrom" );
+ if ( dvdinfo.isBlk() )
+ {
+ MediaSource media( "cdrom", dvdinfo.path().asString(), dvdinfo.devMajor(), dvdinfo.devMinor() );
+ DBG << "Found (GUESS): " << media << std::endl;
+ detected.push_back( media );
+ }
+ if ( cdrinfo.isBlk()
+ && ! ( cdrinfo.devMajor() == dvdinfo.devMajor() && cdrinfo.devMinor() == dvdinfo.devMinor() ) )
+ {
+ MediaSource media( "cdrom", cdrinfo.path().asString(), cdrinfo.devMajor(), cdrinfo.devMinor() );
+ DBG << "Found (GUESS): " << media << std::endl;
+ detected.push_back( media );
}
- return detected;
}
-
- ///////////////////////////////////////////////////////////////////
- //
- //
- // METHOD NAME : MediaCD::attachTo
- // METHOD TYPE : PMError
- //
- // DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
- //
- void MediaCD::attachTo(bool next)
+ // NOTE: On the fly build on-demand device list. Code was moved to
+ // here to get rid of code duplication, while keeping the ABI. Acuallty
+ // this code should be moved to a _devices accessor method.
+ if ( _devices.empty() )
{
- DBG << "next " << next << " last " << _lastdev << endl;
- if (next && _lastdev == -1)
- ZYPP_THROW(MediaNotSupportedException(url()));
-
-#if DELAYED_VERIFY
- DeviceList detected( detectDevices(
- _url.getScheme() == "dvd" ? true : false
- ));
+ DBG << "creating on-demand device list" << endl;
+ //default is /dev/cdrom; for dvd: /dev/dvd if it exists
+ string device( "/dev/cdrom" );
+ if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() )
+ {
+ device = "/dev/dvd";
+ }
- if(_devices.empty())
+ PathInfo dinfo( device );
+ if ( dinfo.isBlk() )
{
- DBG << "creating on-demand device list" << endl;
- //default is /dev/cdrom; for dvd: /dev/dvd if it exists
- string device( "/dev/cdrom" );
- if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
- device = "/dev/dvd";
- }
-
- PathInfo dinfo(device);
- if( dinfo.isBlk())
+ MediaSource media( "cdrom", device, dinfo.devMajor(), dinfo.devMinor() );
+ if ( detected.empty() )
{
- MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
-
- DeviceList::const_iterator d( detected.begin());
- for( ; d != detected.end(); ++d)
+ _devices.push_front( media ); // better try this than nothing
+ }
+ else
+ {
+ for( const auto & d : detected )
{
// /dev/cdrom or /dev/dvd to the front
- if( media.equals( *d))
- _devices.push_front( *d);
+ if ( media.equals( d ) )
+ _devices.push_front( d );
else
- _devices.push_back( *d);
+ _devices.push_back( d );
}
}
- else
- {
- // no /dev/cdrom or /dev/dvd link
- _devices = detected;
- }
}
-#endif
-
- Mount mount;
- string mountpoint = attachPoint().asString();
- bool mountsucceeded = false;
- int count = 0;
-
- string options = _url.getQueryParam("mountoptions");
- if (options.empty())
+ else
{
- options="ro";
+ // no /dev/cdrom or /dev/dvd link
+ _devices = detected;
}
-
- //TODO: make configurable
- list<string> filesystems;
-
- // if DVD, try UDF filesystem before iso9660
- if ( _url.getScheme() == "dvd" )
- filesystems.push_back("udf");
-
- filesystems.push_back("iso9660");
-
- // try all devices in sequence
- for (DeviceList::iterator it = _devices.begin()
- ; !mountsucceeded && it != _devices.end()
- ; ++it, count++ )
- {
- DBG << "count " << count << endl;
- if (next && count<=_lastdev )
- {
- DBG << "skipping device " << it->name << endl;
- continue;
- }
-#if DELAYED_VERIFY
- MediaSource temp( *it);
- bool valid=false;
- PathInfo dinfo(temp.name);
- if( dinfo.isBlk())
- {
- temp.maj_nr = dinfo.major();
- temp.min_nr = dinfo.minor();
+ }
- DeviceList::const_iterator d( detected.begin());
- for( ; d != detected.end(); ++d)
- {
- if( temp.equals( *d))
- {
- valid = true;
- break;
- }
- }
- }
- if( !valid)
- {
- DBG << "skipping invalid device: " << it->name << endl;
- continue;
- }
- MediaSourceRef media( new MediaSource(temp));
-#else
- MediaSourceRef media( new MediaSource( *it));
-#endif
+ return detected;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : MediaCD::attachTo
+ // METHOD TYPE : PMError
+ //
+ // DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
+ //
+ void MediaCD::attachTo( bool next )
+ {
+ DBG << "next " << next << " last " << _lastdev << " last tried " << _lastdev_tried << endl;
+ if ( next && _lastdev == -1 )
+ ZYPP_THROW(MediaNotSupportedException(url()));
+
+ // This also fills the _devices list on demand
+ DeviceList detected( detectDevices( _url.getScheme() == "dvd" ? true : false ) );
+
+ Mount mount;
+ MediaMountException merr;
+ string mountpoint = attachPoint().asString();
+
+ string options = _url.getQueryParam( "mountoptions" );
+ if ( options.empty() )
+ {
+ options="ro";
+ }
- AttachedMedia ret( findAttachedMedia( media));
+ //TODO: make configurable
+ list<string> filesystems;
- if( ret.mediaSource && ret.attachPoint &&
- !ret.attachPoint->empty())
- {
- DBG << "Using a shared media "
- << ret.mediaSource->name
- << " attached on "
- << ret.attachPoint->path
- << endl;
- removeAttachPoint();
- setAttachPoint(ret.attachPoint);
- setMediaSource(ret.mediaSource);
- _lastdev = count;
- mountsucceeded = true;
- break;
- }
- // FIXME: hmm... we may also
- // - check against hal/mtab if still mounted
- // - if !ret, check if already mounted (e.g.
- // by automounter) and reuse (!temp) ?
-
- // close tray
- closeTray( it->name );
-
- // try all filesystems in sequence
- for(list<string>::iterator fsit = filesystems.begin()
- ; !mountsucceeded && fsit != filesystems.end()
- ; ++fsit)
- {
- try {
- if( !isUseableAttachPoint(Pathname(mountpoint)))
- {
- mountpoint = createAttachPoint().asString();
- setAttachPoint( mountpoint, true);
- if( mountpoint.empty())
- {
- ZYPP_THROW( MediaBadAttachPointException(url()));
- }
- }
+ // if DVD, try UDF filesystem before iso9660
+ if ( _url.getScheme() == "dvd" )
+ filesystems.push_back("udf");
- mount.mount(it->name, mountpoint, *fsit, options);
+ filesystems.push_back("iso9660");
- setMediaSource(media);
+ // try all devices in sequence
+ int count = 0;
+ bool mountsucceeded = false;
+ for ( DeviceList::iterator it = _devices.begin() ; ! mountsucceeded && it != _devices.end() ; ++it, ++count )
+ {
+ DBG << "count " << count << endl;
+ if (next && count <=_lastdev_tried )
+ {
+ DBG << "skipping device " << it->name << endl;
+ continue;
+ }
+ _lastdev_tried = count;
+
+ // bnc#755815: _devices contains either devices passed as url option
+ // or autodetected ones. Accept both as long as they are block
+ // devices.
+ MediaSource temp( *it );
+ PathInfo dinfo( temp.name );
+ if ( ! dinfo.isBlk() )
+ {
+ WAR << "skipping non block device: " << dinfo << endl;
+ continue;
+ }
+ DBG << "trying device " << dinfo << endl;
- // wait for /etc/mtab update ...
- // (shouldn't be needed)
- int limit = 3;
- while( !(mountsucceeded=isAttached()) && --limit)
- {
- sleep(1);
- }
+ temp.maj_nr = dinfo.devMajor();
+ temp.min_nr = dinfo.devMinor();
+ MediaSourceRef media( new MediaSource(temp));
+ AttachedMedia ret( findAttachedMedia( media));
- if( mountsucceeded)
- {
- _lastdev = count;
- }
- else
- {
- setMediaSource(MediaSourceRef());
- try
- {
- mount.umount(attachPoint().asString());
- }
- catch (const MediaException & excpt_r)
- {
- ZYPP_CAUGHT(excpt_r);
- }
- ZYPP_THROW(MediaMountException(
- "Unable to verify that the media was mounted",
- it->name, mountpoint
- ));
- }
- }
- catch (const MediaException & excpt_r)
- {
- removeAttachPoint();
- ZYPP_CAUGHT(excpt_r);
- }
- }
+ if( ret.mediaSource && ret.attachPoint &&
+ !ret.attachPoint->empty())
+ {
+ DBG << "Using a shared media "
+ << ret.mediaSource->name
+ << " attached on "
+ << ret.attachPoint->path
+ << endl;
+ removeAttachPoint();
+ setAttachPoint(ret.attachPoint);
+ setMediaSource(ret.mediaSource);
+ _lastdev = count;
+ mountsucceeded = true;
+ break;
}
-
- if (!mountsucceeded)
+
{
- _lastdev = -1;
- ZYPP_THROW(MediaMountException(_url.asString(), mountpoint, "Mounting media failed"));
+ MediaManager manager;
+ MountEntries entries( manager.getMountEntries());
+ MountEntries::const_iterator e;
+ for( e = entries.begin(); e != entries.end(); ++e)
+ {
+ bool is_device = false;
+ std::string dev_path(Pathname(e->src).asString());
+ PathInfo dev_info;
+
+ if( dev_path.compare(0, sizeof("/dev/")-1, "/dev/") == 0 &&
+ dev_info(e->src) && dev_info.isBlk())
+ {
+ is_device = true;
+ }
+
+ if( is_device && media->maj_nr == dev_info.devMajor() &&
+ media->min_nr == dev_info.devMinor())
+ {
+ AttachPointRef ap( new AttachPoint(e->dir, false));
+ AttachedMedia am( media, ap);
+ {
+ DBG << "Using a system mounted media "
+ << media->name
+ << " attached on "
+ << ap->path
+ << endl;
+
+ media->iown = false; // mark attachment as foreign
+
+ setMediaSource(media);
+ setAttachPoint(ap);
+ _lastdev = count;
+ mountsucceeded = true;
+ break;
+ }
+ }
+ }
+ if( mountsucceeded)
+ break;
}
- DBG << _lastdev << " " << count << endl;
- }
+ // close tray
+ closeTray( it->name );
- ///////////////////////////////////////////////////////////////////
- //
- //
- // METHOD NAME : MediaCD::releaseFrom
- // METHOD TYPE : PMError
- //
- // DESCRIPTION : Asserted that media is attached.
- //
- void MediaCD::releaseFrom( bool eject )
+ // try all filesystems in sequence
+ for(list<string>::iterator fsit = filesystems.begin()
+ ; !mountsucceeded && fsit != filesystems.end()
+ ; ++fsit)
+ {
+ try
+ {
+ if( !isUseableAttachPoint(Pathname(mountpoint)))
+ {
+ mountpoint = createAttachPoint().asString();
+ setAttachPoint( mountpoint, true);
+ if( mountpoint.empty())
+ {
+ ZYPP_THROW( MediaBadAttachPointException(url()));
+ }
+ }
+
+ mount.mount(it->name, mountpoint, *fsit, options);
+
+ setMediaSource(media);
+
+ // wait for /etc/mtab update ...
+ // (shouldn't be needed)
+ int limit = 2;
+ while( !(mountsucceeded=isAttached()) && --limit)
+ {
+ WAR << "Wait for /proc/mounts update and retry...." << endl;
+ sleep(1);
+ }
+
+ if( mountsucceeded)
+ {
+ _lastdev = count;
+ }
+ else
+ {
+ setMediaSource(MediaSourceRef());
+ try
+ {
+ mount.umount(attachPoint().asString());
+ }
+ catch (const MediaException & excpt_r)
+ {
+ ZYPP_CAUGHT(excpt_r);
+ }
+ ZYPP_THROW(MediaMountException(
+ "Unable to verify that the media was mounted",
+ it->name, mountpoint
+ ));
+ }
+ }
+ catch (const MediaMountException &e)
+ {
+ merr = e;
+ removeAttachPoint();
+ ZYPP_CAUGHT(e);
+ }
+ catch (const MediaException & excpt_r)
+ {
+ removeAttachPoint();
+ ZYPP_CAUGHT(excpt_r);
+ }
+ } // for filesystems
+ } // for _devices
+
+ if (!mountsucceeded)
{
- Mount mount;
- try {
- mount.umount(attachPoint().asString());
+ _lastdev = -1;
+
+ if( !merr.mountOutput().empty())
+ {
+ ZYPP_THROW(MediaMountException(merr.mountError(),
+ _url.asString(),
+ mountpoint,
+ merr.mountOutput()));
}
- catch (const Exception & excpt_r)
+ else
{
- ZYPP_CAUGHT(excpt_r);
- if (eject)
- {
-#if FORCE_RELEASE_FOREIGN
- forceRelaseAllMedia(false, FORCE_RELEASE_FOREIGN != 2);
-#endif
- if(openTray( mediaSourceName()))
- return;
- }
- ZYPP_RETHROW(excpt_r);
+ ZYPP_THROW(MediaMountException("Mounting media failed",
+ _url.asString(), mountpoint));
}
-
- // eject device
- if (eject)
+ }
+ DBG << _lastdev << " " << count << endl;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : MediaCD::releaseFrom
+ // METHOD TYPE : PMError
+ //
+ // DESCRIPTION : Asserted that media is attached.
+ //
+ void MediaCD::releaseFrom( const std::string & ejectDev )
+ {
+ Mount mount;
+ try
+ {
+ AttachedMedia am( attachedMedia());
+ if(am.mediaSource && am.mediaSource->iown)
+ mount.umount(am.attachPoint->path.asString());
+ }
+ catch (const Exception & excpt_r)
+ {
+ ZYPP_CAUGHT(excpt_r);
+ if (!ejectDev.empty())
{
-#if FORCE_RELEASE_FOREIGN
- forceRelaseAllMedia(false, FORCE_RELEASE_FOREIGN != 2);
-#endif
- if( !openTray( mediaSourceName() ))
- {
-#if REPORT_EJECT_ERRORS
- ZYPP_THROW(MediaNotEjectedException(mediaSourceName()));
-#endif
- }
+ forceRelaseAllMedia(false);
+ if(openTray( ejectDev ))
+ return;
}
+ ZYPP_RETHROW(excpt_r);
}
- ///////////////////////////////////////////////////////////////////
- //
- //
- // METHOD NAME : MediaCD::forceEject
- // METHOD TYPE : void
- //
- // Asserted that media is not attached.
- //
- void MediaCD::forceEject()
+ // eject device
+ if (!ejectDev.empty())
{
- bool ejected=false;
- if ( !isAttached()) { // no device mounted in this instance
-#if DELAYED_VERIFY
- DeviceList detected( detectDevices(
- _url.getScheme() == "dvd" ? true : false
- ));
-
- if(_devices.empty())
- {
- DBG << "creating on-demand device list" << endl;
- //default is /dev/cdrom; for dvd: /dev/dvd if it exists
- string device( "/dev/cdrom" );
- if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
- device = "/dev/dvd";
- }
-
- PathInfo dinfo(device);
- if( dinfo.isBlk())
- {
- MediaSource media("cdrom", device, dinfo.major(), dinfo.minor());
-
- DeviceList::const_iterator d( detected.begin());
- for( ; d != detected.end(); ++d)
- {
- // /dev/cdrom or /dev/dvd to the front
- if( media.equals( *d))
- _devices.push_front( *d);
- else
- _devices.push_back( *d);
- }
- }
- else
- {
- // no /dev/cdrom or /dev/dvd link
- _devices = detected;
- }
- }
-#endif
-
- DeviceList::iterator it;
- for( it = _devices.begin(); it != _devices.end(); ++it ) {
- MediaSourceRef media( new MediaSource( *it));
-#if DELAYED_VERIFY
- bool valid=false;
- PathInfo dinfo(media->name);
- if( dinfo.isBlk())
- {
- media->maj_nr = dinfo.major();
- media->min_nr = dinfo.minor();
-
- DeviceList::const_iterator d( detected.begin());
- for( ; d != detected.end(); ++d)
- {
- if( media->equals( *d))
- {
- valid = true;
- break;
- }
- }
- }
- if( !valid)
- {
- DBG << "skipping invalid device: " << it->name << endl;
- continue;
- }
-#endif
-
- // FIXME: we have also to check if it is mounted in the system
- AttachedMedia ret( findAttachedMedia( media));
- if( !ret.mediaSource)
- {
-#if FORCE_RELEASE_FOREIGN
- forceRelaseAllMedia(media, false, FORCE_RELEASE_FOREIGN != 2);
-#endif
- if ( openTray( it->name ) )
- {
- ejected = true;
- break; // on 1st success
- }
- }
- }
- }
- if( !ejected)
+ forceRelaseAllMedia(false);
+ if( !openTray( ejectDev ))
{
#if REPORT_EJECT_ERRORS
- ZYPP_THROW(MediaNotEjectedException());
+ ZYPP_THROW(MediaNotEjectedException(ejectDev));
#endif
}
}
-
- bool MediaCD::isAutoMountedMedia(const AttachedMedia &media)
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : MediaCD::forceEject
+ // METHOD TYPE : void
+ //
+ // Asserted that media is not attached.
+ //
+ void MediaCD::forceEject( const std::string & ejectDev_r )
+ {
+#if REPORT_EJECT_ERRORS
+ bool ejected = false;
+#endif
+ if ( ! isAttached() ) // no device mounted in this instance
{
- bool is_automounted = false;
- if( media.mediaSource && !media.mediaSource->name.empty())
+ // This also fills the _devices list on demand
+ DeviceList detected( detectDevices( _url.getScheme() == "dvd" ? true : false ) );
+ for_( it, _devices.begin(), _devices.end() )
{
- using namespace zypp::target::hal;
-
- try
+ MediaSourceRef media( new MediaSource( *it ) );
+ if ( media->name != ejectDev_r )
+ continue;
+
+ // bnc#755815: _devices contains either devices passed as url option
+ // or autodetected ones. Accept both as long as they are block
+ // devices.
+ PathInfo dinfo( media->name );
+ if( ! dinfo.isBlk() )
{
- HalContext hal(true);
-
- HalVolume vol = hal.getVolumeFromDeviceFile(media.mediaSource->name);
- if( vol)
- {
- std::string udi = vol.getUDI();
- std::string key = "info.hal_mount.created_mount_point";
- std::string mnt = hal.getDevicePropertyString(udi, key);
-
- if(media.attachPoint->path == mnt)
- is_automounted = true;
- }
- }
- catch(const HalException &e)
- {
- ZYPP_CAUGHT(e);
+ WAR << "skipping non block device: " << dinfo << endl;
+ continue;
}
+ DBG << "trying device " << dinfo << endl;
+
+ // FIXME: we have also to check if it is mounted in the system
+ AttachedMedia ret( findAttachedMedia( media));
+ if( !ret.mediaSource )
+ {
+ forceRelaseAllMedia( media, false );
+ if ( openTray( it->name ) )
+ {
+#if REPORT_EJECT_ERRORS
+ ejected = true;
+#endif
+ break; // on 1st success
+ }
+ }
}
- DBG << "Media " << media.mediaSource->asString()
- << " attached on " << media.attachPoint->path
- << " is" << (is_automounted ? "" : " not")
- << " automounted" << std::endl;
- return is_automounted;
}
-
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : MediaCD::isAttached
- // METHOD TYPE : bool
- //
- // DESCRIPTION : Override check if media is attached.
- //
- bool
- MediaCD::isAttached() const
+#if REPORT_EJECT_ERRORS
+ if( !ejected)
{
- return checkAttached(false);
+ ZYPP_THROW(MediaNotEjectedException());
}
+#endif
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // METHOD NAME : MediaCD::isAttached
+ // METHOD TYPE : bool
+ //
+ // DESCRIPTION : Override check if media is attached.
+ //
+ bool
+ MediaCD::isAttached() const
+ {
+ return checkAttached(false);
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // METHOD NAME : MediaCD::getFile
+ // METHOD TYPE : PMError
+ //
+ // DESCRIPTION : Asserted that media is attached.
+ //
+ void MediaCD::getFile( const Pathname & filename ) const
+ {
+ MediaHandler::getFile( filename );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // METHOD NAME : MediaCD::getDir
+ // METHOD TYPE : PMError
+ //
+ // DESCRIPTION : Asserted that media is attached.
+ //
+ void MediaCD::getDir( const Pathname & dirname, bool recurse_r ) const
+ {
+ MediaHandler::getDir( dirname, recurse_r );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : MediaCD::getDirInfo
+ // METHOD TYPE : PMError
+ //
+ // DESCRIPTION : Asserted that media is attached and retlist is empty.
+ //
+ void MediaCD::getDirInfo( std::list<std::string> & retlist,
+ const Pathname & dirname, bool dots ) const
+ {
+ MediaHandler::getDirInfo( retlist, dirname, dots );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : MediaCD::getDirInfo
+ // METHOD TYPE : PMError
+ //
+ // DESCRIPTION : Asserted that media is attached and retlist is empty.
+ //
+ void MediaCD::getDirInfo( filesystem::DirContent & retlist, const Pathname & dirname, bool dots ) const
+ {
+ MediaHandler::getDirInfo( retlist, dirname, dots );
+ }
+
+
+ bool MediaCD::getDoesFileExist( const Pathname & filename ) const
+ {
+ return MediaHandler::getDoesFileExist( filename );
+ }
+
+
+ bool MediaCD::hasMoreDevices()
+ {
+ if (_devices.size() == 0)
+ return false;
+ else if (_lastdev_tried < 0)
+ return true;
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : MediaCD::getFile
- // METHOD TYPE : PMError
- //
- // DESCRIPTION : Asserted that media is attached.
- //
- void MediaCD::getFile( const Pathname & filename ) const
- {
- MediaHandler::getFile( filename );
- }
+ return (unsigned) _lastdev_tried < _devices.size() - 1;
+ }
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : MediaCD::getDir
- // METHOD TYPE : PMError
- //
- // DESCRIPTION : Asserted that media is attached.
- //
- void MediaCD::getDir( const Pathname & dirname, bool recurse_r ) const
- {
- MediaHandler::getDir( dirname, recurse_r );
- }
- ///////////////////////////////////////////////////////////////////
- //
- //
- // METHOD NAME : MediaCD::getDirInfo
- // METHOD TYPE : PMError
- //
- // DESCRIPTION : Asserted that media is attached and retlist is empty.
- //
- void MediaCD::getDirInfo( std::list<std::string> & retlist,
- const Pathname & dirname, bool dots ) const
- {
- MediaHandler::getDirInfo( retlist, dirname, dots );
- }
+ void MediaCD::getDetectedDevices( std::vector<std::string> & devices, unsigned int & index ) const
+ {
+ if ( ! devices.empty() )
+ devices.clear();
- ///////////////////////////////////////////////////////////////////
- //
- //
- // METHOD NAME : MediaCD::getDirInfo
- // METHOD TYPE : PMError
- //
- // DESCRIPTION : Asserted that media is attached and retlist is empty.
- //
- void MediaCD::getDirInfo( filesystem::DirContent & retlist,
- const Pathname & dirname, bool dots ) const
- {
- MediaHandler::getDirInfo( retlist, dirname, dots );
- }
+ if ( _devices.empty() )
+ // This also fills the _devices list on demand
+ detectDevices( _url.getScheme() == "dvd" ? true : false );
+
+ for ( const auto & it : _devices )
+ devices.push_back( it.name );
+
+ index = ( _lastdev >= 0 ? (unsigned)_lastdev : 0 );
+
+ MIL << "got " << devices.size() << " detected devices, current: "
+ << (index < devices.size() ? devices[index] : "<none>")
+ << "(" << index << ")" << endl;
+ }
} // namespace media
+ //////////////////////////////////////////////////////////////////
} // namespace zypp
-// vim: set ts=8 sts=2 sw=2 ai noet:
+//////////////////////////////////////////////////////////////////