{
#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>
/*
** if to throw exception on eject errors or ignore them
*/
-#define REPORT_EJECT_ERRORS 1
+#define REPORT_EJECT_ERRORS 0
/*
** If defined to the full path of the eject utility,
using namespace std;
-namespace zypp {
- namespace media {
+//////////////////////////////////////////////////////////////////
+namespace zypp
+{
+ //////////////////////////////////////////////////////////////////
+ namespace 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 )
+ {
+ 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 )
+ {
+ ERR << "Can't create udev context." << endl;
+ return DeviceList();
+ }
+
+ 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();
+ }
+
+ ::udev_enumerate_add_match_subsystem( enumerate, "block" );
+ ::udev_enumerate_add_match_property( enumerate, "ID_CDROM", "1" );
+ ::udev_enumerate_scan_devices( enumerate );
+
+ 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;
+ }
+
+ if ( supportingDVD_r && ! ::udev_device_get_property_value( device, "ID_CDROM_DVD" ) )
+ {
+ continue; // looking for dvd only
+ }
+
+ 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() )
+ {
+ WAR << "Did not find any CD/DVD device." << endl;
+ }
+#elif HAVE_HAL
+ using namespace zypp::target::hal;
+ try
+ {
+ 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]));
+
+ 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);
+ }
+ }
+ }
+ }
+ catch(const zypp::target::hal::HalException &e)
+ {
+ ZYPP_CAUGHT(e);
+ }
+#endif
+ return detected;
+ }
-///////////////////////////////////////////////////////////////////
-//
-// CLASS NAME : MediaCD
-//
-///////////////////////////////////////////////////////////////////
+ } // namespace
+ //////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////
- //
- //
- // 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 ),
- _lastdev(-1), _lastdev_tried(-1)
+ 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;
+ MIL << "MediaCD::MediaCD(" << url_r << ", " << attach_point_hint_r << ")" << endl;
- if( url_r.getScheme() != "dvd" && url_r.getScheme() != "cd")
+ if ( url_r.getScheme() != "dvd" && url_r.getScheme() != "cd" )
{
ERR << "Unsupported schema in the Url: " << url_r.asString() << endl;
ZYPP_THROW(MediaUnsupportedUrlSchemeException(_url));
}
- string devices = _url.getQueryParam("devices");
- if (!devices.empty())
+ string devices = _url.getQueryParam( "devices" );
+ if ( ! devices.empty() )
{
- string::size_type pos;
- DBG << "parse " << devices << endl;
- while(!devices.empty())
+ std::vector<std::string> words;
+ str::split( devices, std::back_inserter(words), "," );
+ for ( const std::string & device : words )
{
- pos = devices.find(',');
- string device = devices.substr(0,pos);
- if (!device.empty())
- {
- MediaSource media("cdrom", device, 0, 0);
- _devices.push_back( media);
- DBG << "use device (delayed verify)" << device << endl;
- }
- if (pos!=string::npos)
- devices=devices.substr(pos+1);
- else
- devices.erase();
+ if ( device.empty() )
+ continue;
+
+ MediaSource media( "cdrom", device, 0, 0 );
+ _devices.push_back( media );
+ DBG << "use device (delayed verify)" << device << endl;
}
}
else
return;
}
- if( _devices.empty())
+ if ( _devices.empty() )
{
ERR << "Unable to find any cdrom drive for " << _url.asString() << endl;
ZYPP_THROW(MediaBadUrlEmptyDestinationException(_url));
//
bool MediaCD::openTray( const std::string & device_r )
{
- int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
+ int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK|O_CLOEXEC );
int res = -1;
if ( fd != -1)
//
bool MediaCD::closeTray( const std::string & device_r )
{
- int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
+ 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;
return true;
}
- ///////////////////////////////////////////////////////////////////
- //
- //
- // METHOD NAME : MediaCD::detectDevices
- // METHOD TYPE : MediaCD::DeviceList
- //
- MediaCD::DeviceList MediaCD::detectDevices( bool supportingDVD ) const
- {
- zypp::AutoDispose<struct udev *> udev( ::udev_new(), ::udev_unref );
- if ( ! udev )
- {
- ERR << "Can't create udev context." << endl;
- return DeviceList();
- }
-
- 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();
- }
- ::udev_enumerate_add_match_subsystem( enumerate, "block" );
- ::udev_enumerate_add_match_property( enumerate, "ID_CDROM", "1" );
- ::udev_enumerate_scan_devices( enumerate );
+ MediaCD::DeviceList MediaCD::detectDevices( bool supportingDVD_r ) const
+ {
+ DeviceList detected( systemDetectDevices( supportingDVD_r ) );
- DeviceList detected;
- struct udev_list_entry * entry = 0;
- udev_list_entry_foreach( entry, ::udev_enumerate_get_list_entry( enumerate ) )
+ if ( detected.empty() )
{
- 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 )
+ WAR << "CD/DVD drive detection with HAL/UDEV failed! Guessing..." << std::endl;
+ PathInfo dvdinfo( "/dev/dvd" );
+ PathInfo cdrinfo( "/dev/cdrom" );
+ if ( dvdinfo.isBlk() )
{
- ERR << "Can't create udev device." << endl;
- continue;
+ MediaSource media( "cdrom", dvdinfo.path().asString(), dvdinfo.devMajor(), dvdinfo.devMinor() );
+ DBG << "Found (GUESS): " << media << std::endl;
+ detected.push_back( media );
}
-
- if ( supportingDVD && ! ::udev_device_get_property_value( device, "ID_CDROM_DVD" ) )
+ if ( cdrinfo.isBlk()
+ && ! ( cdrinfo.devMajor() == dvdinfo.devMajor() && cdrinfo.devMinor() == dvdinfo.devMinor() ) )
{
- continue; // looking for dvd only
+ MediaSource media( "cdrom", cdrinfo.path().asString(), cdrinfo.devMajor(), cdrinfo.devMinor() );
+ DBG << "Found (GUESS): " << media << std::endl;
+ detected.push_back( media );
}
+ }
- const char * devnodePtr( ::udev_device_get_devnode( device ) );
- if ( ! devnodePtr )
+ // 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 << "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() )
{
- ERR << "Got NULL devicenode." << endl;
- continue;
+ device = "/dev/dvd";
}
- // In case we need it someday:
- //const char * mountpath = ::udev_device_get_property_value( device, "FSTAB_DIR" );
-
- PathInfo devnode( devnodePtr );
- if ( devnode.isBlk() )
+ PathInfo dinfo( device );
+ if ( dinfo.isBlk() )
{
- MediaSource media( "cdrom", devnode.path().asString(), devnode.major(), devnode.minor() );
- DBG << "Found (udev): " << media << std::endl;
- detected.push_back( media );
+ MediaSource media( "cdrom", device, dinfo.devMajor(), dinfo.devMinor() );
+ if ( detected.empty() )
+ {
+ _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 );
+ else
+ _devices.push_back( d );
+ }
+ }
+ }
+ else
+ {
+ // no /dev/cdrom or /dev/dvd link
+ _devices = detected;
}
}
+
return detected;
}
//
// DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
//
- void MediaCD::attachTo(bool next)
+ void MediaCD::attachTo( bool next )
{
DBG << "next " << next << " last " << _lastdev << " last tried " << _lastdev_tried << endl;
- if (next && _lastdev == -1)
+ if ( next && _lastdev == -1 )
ZYPP_THROW(MediaNotSupportedException(url()));
- DeviceList detected(
- detectDevices(_url.getScheme() == "dvd" ? true : false));
+ // This also fills the _devices list on demand
+ DeviceList detected( detectDevices( _url.getScheme() == "dvd" ? true : false ) );
- if(_devices.empty())
+ if( !isUseableAttachPoint( attachPoint() ) )
{
- 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;
- }
+ setAttachPoint( createAttachPoint(), true );
}
+ std::string mountpoint( attachPoint().asString() );
Mount mount;
- string mountpoint = attachPoint().asString();
- bool mountsucceeded = false;
- int count = 0;
MediaMountException merr;
- string options = _url.getQueryParam("mountoptions");
- if (options.empty())
+ string options = _url.getQueryParam( "mountoptions" );
+ if ( options.empty() )
{
options="ro";
}
filesystems.push_back("iso9660");
// try all devices in sequence
- for (DeviceList::iterator it = _devices.begin()
- ; !mountsucceeded && it != _devices.end()
- ; ++it, count++ )
+ 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;
- 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)
+ // 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() )
{
- DBG << "skipping invalid device: " << it->name << endl;
- continue;
+ WAR << "skipping non block device: " << dinfo << endl;
+ continue;
}
+ DBG << "trying device " << dinfo << endl;
+ temp.maj_nr = dinfo.devMajor();
+ temp.min_nr = dinfo.devMinor();
MediaSourceRef media( new MediaSource(temp));
AttachedMedia ret( findAttachedMedia( media));
is_device = true;
}
- if( is_device && media->maj_nr == dev_info.major() &&
- media->min_nr == dev_info.minor())
+ 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);
{
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 = 5;
+ int limit = 2;
while( !(mountsucceeded=isAttached()) && --limit)
{
+ WAR << "Wait for /proc/mounts update and retry...." << endl;
sleep(1);
}
//
// Asserted that media is not attached.
//
- void MediaCD::forceEject(const std::string & ejectDev)
+ void MediaCD::forceEject( const std::string & ejectDev_r )
{
- bool ejected=false;
- if ( !isAttached()) { // no device mounted in this instance
-
- DeviceList detected(
- detectDevices(_url.getScheme() == "dvd" ? true : false));
-
- if(_devices.empty())
+#if REPORT_EJECT_ERRORS
+ bool ejected = false;
+#endif
+ if ( ! isAttached() ) // no device mounted in this instance
+ {
+ // This also fills the _devices list on demand
+ DeviceList detected( detectDevices( _url.getScheme() == "dvd" ? true : false ) );
+ for_( it, _devices.begin(), _devices.end() )
{
- 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;
- }
- }
-
- DeviceList::iterator it;
- for( it = _devices.begin(); it != _devices.end(); ++it ) {
- MediaSourceRef media( new MediaSource( *it));
- if (media->name != ejectDev)
+ MediaSourceRef media( new MediaSource( *it ) );
+ if ( media->name != ejectDev_r )
continue;
- 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;
- }
+ // 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() )
+ {
+ 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)
+ if( !ret.mediaSource )
{
- forceRelaseAllMedia(media, false);
+ forceRelaseAllMedia( media, false );
if ( openTray( it->name ) )
{
+#if REPORT_EJECT_ERRORS
ejected = true;
+#endif
break; // on 1st success
}
}
}
}
+#if REPORT_EJECT_ERRORS
if( !ejected)
{
-#if REPORT_EJECT_ERRORS
ZYPP_THROW(MediaNotEjectedException());
-#endif
}
+#endif
}
///////////////////////////////////////////////////////////////////
//
// DESCRIPTION : Asserted that media is attached and retlist is empty.
//
- void MediaCD::getDirInfo( filesystem::DirContent & retlist,
- const Pathname & dirname, bool dots ) const
+ 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 (unsigned) _lastdev_tried < _devices.size() - 1;
}
- void
- MediaCD::getDetectedDevices(std::vector<std::string> & devices,
- unsigned int & index) const
+
+ void MediaCD::getDetectedDevices( std::vector<std::string> & devices, unsigned int & index ) const
{
- index = 0;
- if (!devices.empty())
+ if ( ! devices.empty() )
devices.clear();
- for (DeviceList::const_iterator it = _devices.begin();
- it != _devices.end(); ++it)
- devices.push_back(it->name);
-
- if (_lastdev >= 0)
- index = _lastdev;
-
- // try to detect again if _devices are empty (maybe this method will be
- // called before _devices get actually filled)
- if (devices.empty())
- {
- DBG << "no device list so far, trying to detect" << endl;
+ if ( _devices.empty() )
+ // This also fills the _devices list on demand
+ detectDevices( _url.getScheme() == "dvd" ? true : false );
- DeviceList detected(
- detectDevices(_url.getScheme() == "dvd" ? true : false));
+ for ( const auto & it : _devices )
+ devices.push_back( it.name );
- for (DeviceList::const_iterator it = detected.begin();
- it != detected.end(); ++it)
- devices.push_back(it->name);
-
- // don't know which one is in use in this case
- index = 0;
- }
+ 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:
+//////////////////////////////////////////////////////////////////