libzypp  17.31.31
devicedriver.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
9 
10 #include "devicedriver.h"
13 #include <zypp-media/ng/MediaVerifier>
14 #include <zypp-media/MediaException>
15 #include <zypp-core/fs/PathInfo.h>
16 #include <zypp-core/fs/TmpPath.h>
17 #include <zypp-core/Date.h>
18 
19 #undef ZYPP_BASE_LOGGER_LOGGROUP
20 #define ZYPP_BASE_LOGGER_LOGGROUP "zyppng::worker::DeviceDriver"
21 
22 namespace zyppng::worker
23 {
24 
25  DeviceDriver::DeviceDriver ( WorkerCaps::WorkerType wType )
26  : _wType( wType )
27  { }
28 
29  void DeviceDriver::setProvider ( ProvideWorkerWeakRef workerRef )
30  {
31  _parentWorker = workerRef;
32  }
33 
34  zyppng::expected<WorkerCaps> DeviceDriver::initialize(const zyppng::worker::Configuration &conf)
35  {
36  const auto &values = conf.values();
37  if ( const auto &i = values.find( std::string(zyppng::ATTACH_POINT) ); i != values.end() ) {
38  const auto &val = i->second;
39  MIL << "Got attachpoint from controller: " << val << std::endl;
41  } else {
42  return zyppng::expected<zyppng::worker::WorkerCaps>::error(ZYPP_EXCPT_PTR( zypp::Exception("Attach point required to work.") ));
43  }
44 
45  _config = conf;
46 
48  caps.set_worker_type ( _wType );
49  caps.set_cfg_flags(
50  zyppng::worker::WorkerCaps::Flags (
51  zyppng::worker::WorkerCaps::Pipeline
52  | zyppng::worker::WorkerCaps::ZyppLogFormat
53  | zyppng::worker::WorkerCaps::SingleInstance
54  )
55  );
56 
57  return zyppng::expected<zyppng::worker::WorkerCaps>::success(caps);
58  }
59 
60  bool DeviceDriver::detachMedia ( const std::string &attachId )
61  {
62  auto i = _attachedMedia.find( attachId );
63  if ( i == _attachedMedia.end() )
64  return false;
65 
66  _attachedMedia.erase(i);
67  return true;
68  }
69 
71  {
72  for ( auto i = _sysDevs.begin (); i != _sysDevs.end(); ) {
73  if ( i->use_count() == 1 && !(*i)->_mountPoint.empty() ) {
74  MIL << "Unmounting device " << (*i)->_name << " since its not used anymore" << std::endl;
75  unmountDevice(*(*i));
76  if ( (*i)->_ephemeral ) {
77  i = _sysDevs.erase(i);
78  continue;
79  }
80  }
81  ++i;
82  }
83  }
84 
86  {
87  return;
88  }
89 
90  std::vector<std::shared_ptr<Device>> &DeviceDriver::knownDevices()
91  {
92  return _sysDevs;
93  }
94 
95  const std::vector<std::shared_ptr<Device>> &DeviceDriver::knownDevices() const
96  {
97  return _sysDevs;
98  }
99 
100  std::unordered_map<std::string, AttachedMedia> &DeviceDriver::attachedMedia()
101  {
102  return _attachedMedia;
103  }
104 
106  {
107  // here we need to unmount everything
108  for ( auto i = _sysDevs.begin (); i != _sysDevs.end(); ) {
109  unmountDevice(*(*i));
110  if ( (*i)->_ephemeral ) {
111  i = _sysDevs.erase(i);
112  continue;
113  }
114  ++i;
115  }
116  _attachedMedia.clear();
117  }
118 
119  ProvideWorkerRef DeviceDriver::parentWorker () const
120  {
121  return _parentWorker.lock();
122  }
123 
125  {
126  if ( dev._mountPoint.empty () )
127  return;
128  try {
129  zypp::media::Mount mount;
130  mount.umount( dev._mountPoint.asString() );
132  } catch (const zypp::media::MediaException & excpt_r) {
133  ERR << "Failed to unmount device: " << dev._name << std::endl;
134  ZYPP_CAUGHT(excpt_r);
135  }
136  dev._mountPoint = zypp::Pathname();
137  }
138 
140  {
141  return false;
142  }
143 
145  {
146  _attachRoot = root;
147  }
148 
150  {
151  if ( _attachRoot.empty() ) {
152  MIL << "Attach root is empty" << std::endl;
153  return zypp::Pathname(".").realpath();
154  }
155  return _attachRoot;
156  }
157 
159  {
160  return _config;
161  }
162 
163  zyppng::expected<void> DeviceDriver::isDesiredMedium ( const zypp::Url &deviceUrl, const zypp::Pathname &mountPoint, const zyppng::MediaDataVerifierRef &verifier, uint mediaNr )
164  {
165  if ( !verifier ) {
166  // at least the requested path must exist on the medium
167  zypp::PathInfo p( mountPoint );
168  if ( p.isExist() && p.isDir() )
169  return zyppng::expected<void>::success(); // we have no valid data
170  return zyppng::expected<void>::error( ZYPP_EXCPT_PTR( zypp::media::MediaNotDesiredException( deviceUrl ) ) );
171  }
172 
173  auto devVerifier = verifier->clone();
174  if ( !devVerifier ) {
175  // unlikely to happen
176  return zyppng::expected<void>::error( ZYPP_EXCPT_PTR( zypp::Exception("Failed to clone verifier") ) );
177  }
178 
179  // bsc#1180851: If there is just one not-volatile medium in the set
180  // tolerate a missing (vanished) media identifier and let the URL rule.
181  bool relaxed = verifier->totalMedia() == 1 && !isVolatile();
182 
183  const auto &relMediaPath = devVerifier->mediaFilePath( mediaNr );
184  zypp::Pathname mediaFile { mountPoint / relMediaPath };
185  zypp::PathInfo pi( mediaFile );
186  if ( !pi.isExist() ) {
187  if ( relaxed )
188  return zyppng::expected<void>::success();
189  auto excpt = zypp::media::MediaFileNotFoundException( deviceUrl, relMediaPath ) ;
190  excpt.addHistory( verifier->expectedAsUserString( mediaNr ) );
191  return zyppng::expected<void>::error( ZYPP_EXCPT_PTR( std::move(excpt) ) );
192  }
193  if ( !pi.isFile() ) {
194  if ( relaxed )
195  return zyppng::expected<void>::success();
196  auto excpt = zypp::media::MediaNotAFileException( deviceUrl, relMediaPath ) ;
197  excpt.addHistory( verifier->expectedAsUserString( mediaNr ) );
198  return zyppng::expected<void>::error( ZYPP_EXCPT_PTR( std::move(excpt) ) );
199  }
200 
201  if ( !devVerifier->load( mediaFile ) ) {
202  return zyppng::expected<void>::error( ZYPP_EXCPT_PTR( zypp::Exception("Failed to load media information from medium") ) );
203  }
204  if ( !verifier->matches( devVerifier ) ) {
205  return zyppng::expected<void>::error( ZYPP_EXCPT_PTR( zypp::media::MediaNotDesiredException( deviceUrl ) ) );
206  }
207  return zyppng::expected<void>::success();
208  }
209 
211  {
212  zypp::Pathname apoint;
213 
214  if( attach_root.empty() || !attach_root.absolute()) {
215  ERR << "Create attach point: invalid attach root: '"
216  << attach_root << "'" << std::endl;
217  return apoint;
218  }
219 
220  zypp::PathInfo adir( attach_root );
221  if( !adir.isDir() || (geteuid() != 0 && !adir.userMayRWX())) {
222  DBG << "Create attach point: attach root is not a writable directory: '"
223  << attach_root << "'" << std::endl;
224  return apoint;
225  }
226 
227  static bool cleanup_once( true );
228  if ( cleanup_once )
229  {
230  cleanup_once = false;
231  DBG << "Look for orphaned attach points in " << adir << std::endl;
232  std::list<std::string> entries;
233  zypp::filesystem::readdir( entries, attach_root, false );
234  for ( const std::string & entry : entries )
235  {
236  if ( ! zypp::str::hasPrefix( entry, "AP_0x" ) )
237  continue;
238  zypp::PathInfo sdir( attach_root + entry );
239  if ( sdir.isDir()
240  && sdir.dev() == adir.dev()
241  && ( zypp::Date::now()-sdir.mtime() > zypp::Date::month ) )
242  {
243  DBG << "Remove orphaned attach point " << sdir << std::endl;
245  }
246  }
247  }
248 
249  zypp::filesystem::TmpDir tmpdir( attach_root, "AP_0x" );
250  if ( tmpdir )
251  {
252  apoint = tmpdir.path().asString();
253  if ( ! apoint.empty() )
254  {
255  tmpdir.autoCleanup( false ); // Take responsibility for cleanup.
256  }
257  else
258  {
259  ERR << "Unable to resolve real path for attach point " << tmpdir << std::endl;
260  }
261  }
262  else
263  {
264  ERR << "Unable to create attach point below " << attach_root << std::endl;
265  }
266  return apoint;
267  }
268 
270  {
271  if( !attachRoot.empty() &&
273  attachRoot != "/" ) {
274  int res = recursive_rmdir( attachRoot );
275  if ( res == 0 ) {
276  MIL << "Deleted default attach point " << attachRoot << std::endl;
277  } else {
278  ERR << "Failed to Delete default attach point " << attachRoot
279  << " errno(" << res << ")" << std::endl;
280  }
281  }
282  }
283 
284  bool DeviceDriver::checkAttached ( const zypp::filesystem::Pathname &mountPoint, const std::function<bool (const zypp::media::MountEntry &)> predicate )
285  {
286  bool isAttached = false;
287  time_t old_mtime = _attach_mtime;
289  if( !(old_mtime <= 0 || _attach_mtime != old_mtime) ) {
290  // OK, skip the check (we've seen it at least once)
291  isAttached = true;
292  } else {
293  if( old_mtime > 0)
294  DBG << "Mount table changed - rereading it" << std::endl;
295  else
296  DBG << "Forced check of the mount table" << std::endl;
297 
298  for( const auto &entry : zypp::media::Mount::getEntries() ) {
299 
300  if ( mountPoint != zypp::Pathname(entry.dir) )
301  continue; // at least the mount points must match
302  if ( predicate(entry) ) {
303  isAttached = true;
304  break;
305  }
306  }
307  }
308 
309  // force recheck
310  if ( !isAttached )
311  _attach_mtime = 0;
312 
313  return isAttached;
314  }
315 
316  const std::function<bool (const zypp::media::MountEntry &)> DeviceDriver::devicePredicate( unsigned int majNr, unsigned int minNr )
317  {
318  return [ majNr, minNr ]( const zypp::media::MountEntry &entry ) -> bool {
319  if( entry.isBlockDevice() ) {
320  zypp::PathInfo dev_info( entry.src );
321  if ( dev_info.devMajor () == majNr && dev_info.devMinor () == minNr ) {
322  DBG << "Found device "
323  << majNr << ":" << minNr
324  << " in the mount table as " << entry.src << std::endl;
325  return true;
326  }
327  }
328  return false;
329  };
330  }
331 
332  const std::function<bool (const zypp::media::MountEntry &)> DeviceDriver::fstypePredicate( const std::string &src, const std::vector<std::string> &fstypes )
333  {
334  return [ srcdev=src, fst=fstypes ]( const zypp::media::MountEntry &entry ) -> bool {
335  if( !entry.isBlockDevice() ) {
336  if ( std::find( fst.begin(), fst.end(), entry.type ) != fst.end() ) {
337  if ( srcdev == entry.src ) {
338  DBG << "Found media mount"
339  << " in the mount table as " << entry.src << std::endl;
340  return true;
341  }
342  }
343  }
344  return false;
345  };
346  }
347 
348  const std::function<bool (const zypp::media::MountEntry &)> DeviceDriver::bindMountPredicate( const std::string &src )
349  {
350  return [ srcdev=src ]( const zypp::media::MountEntry &entry ) -> bool {
351  if( !entry.isBlockDevice() ) {
352  if ( srcdev == entry.src ) {
353  DBG << "Found bound media "
354  << " in the mount table as " << entry.src << std::endl;
355  return true;
356  }
357  }
358  return false;
359  };
360  }
361 
362  AttachError::AttachError ( const uint code, const std::string &reason, const bool transient, const HeaderValueMap &extra)
363  : _code( code ),
364  _reason( reason ),
365  _transient( transient ),
366  _extra( extra )
367  {
368 
369  }
370 
371  AttachError::AttachError ( const uint code, const bool transient, const zypp::Exception &e )
372  : _code( code ),
373  _reason( e.asUserString() ),
374  _transient( transient )
375  {
376  if ( !e.historyEmpty() ) {
378  }
379  }
380 
381 
382 }
DeviceDriver(WorkerCaps::WorkerType wType)
Definition: devicedriver.cc:25
#define MIL
Definition: Logger.h:96
bool autoCleanup() const
Whether path is valid and deleted when the last reference drops.
Definition: TmpPath.cc:163
Interface to the mount program.
Definition: mount.h:74
const Pathname & path() const
Return current Pathname.
Definition: PathInfo.h:246
bool detachMedia(const std::string &attachId)
Definition: devicedriver.cc:60
Pathname realpath() const
Returns this path as the absolute canonical pathname.
Definition: Pathname.cc:231
std::unordered_map< std::string, AttachedMedia > & attachedMedia()
zypp::proto::Capabilities WorkerCaps
Definition: provideworker.h:31
ProvideWorkerWeakRef _parentWorker
Definition: devicedriver.h:193
zypp::Pathname attachRoot() const
constexpr std::string_view ATTACH_POINT("zconfig://media/AttachPoint")
CURLcode _code
std::vector< std::shared_ptr< Device > > _sysDevs
Definition: devicedriver.h:191
time_t mtime() const
Definition: PathInfo.h:376
Pathname path() const
Definition: TmpPath.cc:146
WorkerCaps::WorkerType _wType
Definition: devicedriver.h:187
unsigned int devMinor() const
Definition: PathInfo.cc:251
#define ZYPP_EXCPT_PTR(EXCPT)
Drops a logline and returns Exception as a std::exception_ptr.
Definition: Exception.h:432
virtual void immediateShutdown()
virtual zyppng::expected< WorkerCaps > initialize(const zyppng::worker::Configuration &conf)
Definition: devicedriver.cc:34
#define ERR
Definition: Logger.h:98
std::unordered_map< std::string, AttachedMedia > _attachedMedia
Definition: devicedriver.h:192
ProvideWorkerRef parentWorker() const
static const ValueType month
Definition: Date.h:49
static const std::function< bool(const zypp::media::MountEntry &)> bindMountPredicate(const std::string &src)
const zyppng::worker::Configuration & config() const
bool empty() const
Test for an empty path.
Definition: Pathname.h:114
Provide a new empty temporary directory and recursively delete it when no longer needed.
Definition: TmpPath.h:177
const std::string & asString() const
String representation.
Definition: Pathname.h:91
bool isExist() const
Return whether valid stat info exists.
Definition: PathInfo.h:281
Just inherits Exception to separate media exceptions.
std::string _name
Path of the device node or URL for e.g. nfs devices.
Definition: devicedriver.h:31
int recursive_rmdir(const Pathname &path)
Like &#39;rm -r DIR&#39;.
Definition: PathInfo.cc:412
void removeAttachPoint(const zypp::Pathname &attach_pt) const
bool historyEmpty() const
Whether the history list is empty.
Definition: Exception.h:262
std::string historyAsString() const
The history as string.
Definition: Exception.cc:146
bool absolute() const
Test for an absolute path.
Definition: Pathname.h:116
std::vector< std::shared_ptr< Device > > & knownDevices()
Definition: devicedriver.cc:90
static time_t getMTime()
Get the modification time of the /etc/mtab file.
Definition: mount.cc:264
zypp::Pathname createAttachPoint(const zypp::Pathname &attach_root) const
void setAttachRoot(const zypp::Pathname &root)
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition: Exception.h:436
int readdir(std::list< std::string > &retlist_r, const Pathname &path_r, bool dots_r)
Return content of directory via retlist.
Definition: PathInfo.cc:605
bool checkAttached(const zypp::Pathname &mountPoint, const std::function< bool(const zypp::media::MountEntry &)> predicate)
Base class for Exception.
Definition: Exception.h:145
static const std::function< bool(const zypp::media::MountEntry &)> devicePredicate(unsigned int majNr, unsigned int minNr)
static MountEntries getEntries(const std::string &mtab="")
Return mount entries from /etc/mtab or /etc/fstab file.
Definition: mount.cc:169
static Date now()
Return the current time.
Definition: Date.h:78
Predicate predicate
Definition: PoolQuery.cc:313
MediaVerifierRef verifier
A "struct mntent" like mount entry structure, but using std::strings.
Definition: mount.h:34
unsigned int devMajor() const
Definition: PathInfo.cc:241
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:220
constexpr std::string_view History("history")
zypp::Pathname _mountPoint
Mountpoint of the device, if empty dev is not mounted.
Definition: devicedriver.h:34
std::string asUserString(VendorSupportOption opt)
converts the support option to a name intended to be printed to the user.
zypp::proto::Configuration Configuration
Definition: provideworker.h:33
void setProvider(ProvideWorkerWeakRef workerRef)
Definition: devicedriver.cc:29
zyppng::expected< void > isDesiredMedium(const zypp::Url &deviceUrl, const zypp::Pathname &mountPoint, const zyppng::MediaDataVerifierRef &verifier, uint mediaNr=1)
bool hasPrefix(const C_Str &str_r, const C_Str &prefix_r)
Return whether str_r has prefix prefix_r.
Definition: String.h:1027
virtual bool isVolatile() const
static const std::function< bool(const zypp::media::MountEntry &)> fstypePredicate(const std::string &src, const std::vector< std::string > &fstypes)
zyppng::worker::Configuration _config
Definition: devicedriver.h:188
bool userMayRWX() const
Definition: PathInfo.h:353
Url manipulation class.
Definition: Url.h:91
void umount(const std::string &path)
umount device
Definition: mount.cc:117
virtual void unmountDevice(Device &dev)
#define DBG
Definition: Logger.h:95
AttachError(const uint code, const std::string &reason, const bool transient, const HeaderValueMap &extra={})