Imported Upstream version 17.23.5
[platform/upstream/libzypp.git] / zypp / target / modalias / Modalias.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/target/modalias/Modalias.cc
10  *
11 */
12 extern "C"
13 {
14 #include <fnmatch.h>
15 }
16
17 #include <iostream>
18 #include <fstream>
19 #include <vector>
20
21 #undef ZYPP_BASE_LOGGER_LOGGROUP
22 #define ZYPP_BASE_LOGGER_LOGGROUP "MODALIAS"
23
24 #include <zypp/base/LogTools.h>
25 #include <zypp/base/IOStream.h>
26 #include <zypp/base/InputStream.h>
27 #include <zypp/AutoDispose.h>
28 #include <zypp/PathInfo.h>
29
30 #include <zypp/target/modalias/Modalias.h>
31
32 using std::endl;
33
34 ///////////////////////////////////////////////////////////////////
35 namespace zypp
36 {
37   ///////////////////////////////////////////////////////////////////
38   namespace target
39   {
40     ///////////////////////////////////////////////////////////////////
41     namespace
42     {
43       /** Filter subtrees known to contain no modalias files */
44       inline bool isBlackListed( const Pathname & dir_r, const char * file_r )
45       {
46 #define PATH_IS( D, F ) ( ::strcmp( file_r, F ) == 0 && ::strcmp( dir_r.c_str(), D ) == 0 )
47         switch ( file_r[0] )
48         {
49           case 'm':
50             return PATH_IS( "/sys/devices/system", "memory" );  // bnc#824110: huge tree for systems with large RAM
51             break;
52         }
53         return false;
54 #undef PATH_IS
55       }
56
57       void foreach_file_recursive( const Pathname & dir_r, std::set<std::string> & arg_r )
58       {
59         AutoDispose<DIR *> dir( ::opendir( dir_r.c_str() ), ::closedir );
60         if ( ! dir )
61           return;
62
63         struct dirent * dirent = NULL;
64         while ( (dirent = ::readdir(dir)) != NULL )
65         {
66           if ( dirent->d_name[0] == '.' )
67             continue;
68
69           if ( isBlackListed( dir_r, dirent->d_name ) )
70             continue;
71
72           Pathname path;        // lazy init as needed
73           unsigned char d_type = dirent->d_type;
74           if ( d_type == DT_UNKNOWN )
75           {
76             path = dir_r/dirent->d_name;
77             PathInfo pi( path, PathInfo::LSTAT );
78             if ( pi.isDir() )
79               d_type = DT_DIR;
80             else if ( pi.isFile() )
81               d_type = DT_REG;
82           }
83
84           if ( d_type == DT_DIR )
85           {
86             if ( path.empty() )
87               path = dir_r/dirent->d_name;
88             foreach_file_recursive( path, arg_r );
89           }
90           else if ( d_type == DT_REG && ::strcmp( dirent->d_name, "modalias" ) == 0 )
91           {
92             if ( path.empty() )
93               path = dir_r/dirent->d_name;
94             // read modalias line from file
95             std::ifstream str( path.c_str() );
96             std::string line( iostr::getline( str ) );
97             if ( ! line.empty() )
98               arg_r.insert( line );
99           }
100         }
101       }
102
103       /** Recursively scan for modalias files and scan them to \a arg. */
104       void foreach_file_recursive( const Pathname & dir_r, Modalias::ModaliasList & arg_r )
105       {
106         std::set<std::string> arg;      // we want the aliases to be unified (the public API uses a vector)
107         foreach_file_recursive( dir_r, arg );
108         arg_r.insert( arg_r.end(), arg.begin(), arg.end() );
109       }
110     } // namespace
111     ///////////////////////////////////////////////////////////////////
112
113     ///////////////////////////////////////////////////////////////////
114     //
115     //  CLASS NAME : Modalias::Impl
116     //
117     /** Modalias implementation. */
118     struct Modalias::Impl
119     {
120       /** Ctor. */
121       Impl()
122       {
123         const char * dir = getenv("ZYPP_MODALIAS_SYSFS");
124         if ( dir )
125         {
126           PathInfo pi( dir );
127           if (  pi.isFile() )
128           {
129             // Debug/testcases:
130             //   find /sys/ -type f -name modalias -print0 | xargs -0 cat >/tmp/modaliases
131             //   ZYPP_MODALIAS_SYSFS=/tmp/modaliases
132             DBG << "Using $ZYPP_MODALIAS_SYSFS modalias file: " << dir << endl;
133             iostr::forEachLine( InputStream( pi.path() ),
134                                 [&]( int num_r, std::string line_r )->bool
135                                 {
136                                   this->_modaliases.push_back( line_r );
137                                   return true;
138                                 } );
139             return;
140           }
141           DBG << "Using $ZYPP_MODALIAS_SYSFS: " << dir << endl;
142         }
143         else
144         {
145           dir = "/sys";
146           DBG << "Using /sys directory." << endl;
147         }
148
149         foreach_file_recursive( dir, _modaliases );
150       }
151
152       /** Dtor. */
153       ~Impl()
154       {}
155
156       /*
157        * Check if a device on the system matches a modalias PATTERN.
158        *
159        * Returns NULL if no matching device is found, and the modalias
160        * of the first matching device otherwise. (More than one device
161        * may match a given pattern.)
162        *
163        * On a system that has the following device,
164        *
165        *   pci:v00008086d0000265Asv00008086sd00004556bc0Csc03i00
166        *
167        * modalias_matches("pci:v00008086d0000265Asv*sd*bc*sc*i*") will
168        * return a non-NULL value.
169        */
170       bool query( const char * cap_r ) const
171       {
172         if ( cap_r && *cap_r )
173         {
174           for_( it, _modaliases.begin(), _modaliases.end() )
175           {
176             if ( fnmatch( cap_r, (*it).c_str(), 0 ) == 0 )
177               return true;
178           }
179         }
180         return false;
181       }
182
183     public:
184       ModaliasList _modaliases;
185
186     public:
187       /** Offer default Impl. */
188       static shared_ptr<Impl> nullimpl()
189       {
190         static shared_ptr<Impl> _nullimpl( new Impl );
191         return _nullimpl;
192       }
193
194     };
195     ///////////////////////////////////////////////////////////////////
196
197     /** \relates Modalias::Impl Stream output
198      * And maybe std::ostream & operator<< Modalias::Impl below too.
199      * return libhal version or something like that.
200      */
201     inline std::ostream & operator<<( std::ostream & str, const Modalias::Impl & obj )
202     {
203       return dumpRange( str << "Modaliases: (" << obj._modaliases.size() << ") ", obj._modaliases.begin(), obj._modaliases.end() );
204     }
205
206     ///////////////////////////////////////////////////////////////////
207     //
208     //  CLASS NAME : Modalias
209     //
210     ///////////////////////////////////////////////////////////////////
211
212     Modalias::Modalias()
213     : _pimpl( Impl::nullimpl() )
214     {}
215
216     Modalias::~Modalias()
217     {}
218
219     Modalias & Modalias::instance()
220     {
221       static Modalias _singleton;
222       return _singleton;
223     }
224
225     bool Modalias::query( const char * cap_r ) const
226     { return _pimpl->query( cap_r ); }
227
228     const Modalias::ModaliasList & Modalias::modaliasList() const
229     { return _pimpl->_modaliases; }
230
231     void Modalias::modaliasList( ModaliasList newlist_r )
232     { _pimpl->_modaliases.swap( newlist_r ); }
233
234     std::ostream & operator<<( std::ostream & str, const Modalias & obj )
235     { return str << *obj._pimpl; }
236
237   } // namespace target
238   ///////////////////////////////////////////////////////////////////
239 } // namespace zypp
240 ///////////////////////////////////////////////////////////////////
241