Tune CheckAccessDeleted to focus on libraries and executables.
[platform/upstream/libzypp.git] / zypp / misc / CheckAccessDeleted.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/misc/CheckAccessDeleted.cc
10  *
11 */
12 #include <iostream>
13 #include "zypp/base/LogTools.h"
14 #include "zypp/base/String.h"
15 #include "zypp/base/Exception.h"
16
17 #include "zypp/PathInfo.h"
18 #include "zypp/ExternalProgram.h"
19
20 #include "zypp/misc/CheckAccessDeleted.h"
21
22 using std::endl;
23
24 #undef ZYPP_BASE_LOGGER_LOGGROUP
25 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::misc"
26
27 ///////////////////////////////////////////////////////////////////
28 namespace zypp
29 { /////////////////////////////////////////////////////////////////
30
31   ///////////////////////////////////////////////////////////////////
32   namespace
33   { /////////////////////////////////////////////////////////////////
34     //
35     // lsof output lines are a sequence of NUL terminated fields,
36     // where the 1st char determines the fiels type.
37     //
38     // (pcuL) pid command userid loginname
39     // (ftkn).filedescriptor type linkcount filename
40     //
41     /////////////////////////////////////////////////////////////////
42     /** Add \c cache to \c data if the process is accessing deleted files.
43      * \c pid string in \c cache is the proc line \c (pcuLR), \c iles
44      * are lready in place. Always clear the \c cache.files!
45     */
46     inline void addDataIf( std::vector<CheckAccessDeleted::ProcInfo> & data_r, CheckAccessDeleted::ProcInfo & cache_r )
47     {
48       if ( cache_r.files.empty() )
49         return;
50
51       // at least one file access so keep it:
52       data_r.push_back( CheckAccessDeleted::ProcInfo() );
53       CheckAccessDeleted::ProcInfo & pinfo( data_r.back() );
54
55       std::string pline;
56       cache_r.pid.swap( pline );
57       cache_r.files.swap( pinfo.files ); // clears cache.files
58
59       for_( ch, pline.begin(), pline.end() )
60       {
61         switch ( *ch )
62         {
63           case 'p':
64             pinfo.pid = &*(ch+1);
65             break;
66           case 'R':
67             pinfo.ppid = &*(ch+1);
68             break;
69           case 'u':
70             pinfo.puid = &*(ch+1);
71             break;
72           case 'L':
73             pinfo.login = &*(ch+1);
74             break;
75           case 'c':
76             pinfo.command = &*(ch+1);
77             break;
78         }
79         if ( *ch == '\n' ) break;               // end of data
80         do { ++ch; } while ( *ch != '\0' );     // skip to next field
81       }
82
83       if ( pinfo.command.size() == 15 )
84       {
85         // the command name might be truncated, so we check against /proc/<pid>/exe
86         Pathname command( filesystem::readlink( Pathname("/proc")/pinfo.pid/"exe" ) );
87         if ( ! command.empty() )
88           pinfo.command = command.basename();
89       }
90
91       MIL << " Take " << pinfo << endl;
92     }
93
94     /** Add line to cache if it refers to a deleted executable or library file:
95      * - Either the link count \c(k) os \c 0, or no link cout is present.
96      * - The type \c (t) is set to \c REG or \c DEL
97      * - The filedescriptor \c (f) is set to \c txt, \c mem or \c DEL
98     */
99     inline void addCacheIf( CheckAccessDeleted::ProcInfo & cache_r, const std::string & line_r )
100     {
101       const char * k = ".";
102       const char * f = ".";
103       const char * t = ".";
104       const char * n = ".";
105
106       for_( ch, line_r.c_str(), ch+line_r.size() )
107       {
108         switch ( *ch )
109         {
110           case 'k':
111             if ( *(ch+1) != '0' )       // skip non-zero link counts
112               return;
113             k = ch+1;
114             break;
115           case 'f':
116             f = ch+1;
117             break;
118           case 't':
119             t = ch+1;
120             break;
121           case 'n':
122             n = ch+1;
123             break;
124         }
125         if ( *ch == '\n' ) break;               // end of data
126         do { ++ch; } while ( *ch != '\0' );     // skip to next field
127       }
128
129       if ( !t || !f || !n )
130         return; // wrong filedescriptor/type/name
131
132       if ( !(    ( *t == 'R' && *(t+1) == 'E' && *(t+2) == 'G' && *(t+3) == '\0' )
133               || ( *t == 'D' && *(t+1) == 'E' && *(t+2) == 'L' && *(t+3) == '\0' ) ) )
134         return; // wrong type
135
136       if ( !(    ( *f == 'm' && *(f+1) == 'e' && *(f+2) == 'm' && *(f+3) == '\0' )
137               || ( *f == 't' && *(f+1) == 'x' && *(f+2) == 't' && *(f+3) == '\0' )
138               || ( *f == 'D' && *(f+1) == 'E' && *(f+2) == 'L' && *(f+3) == '\0' )
139               || ( *f == 'l' && *(f+1) == 't' && *(f+2) == 'x' && *(f+3) == '\0' ) ) )
140         return; // wrong filedescriptor type
141
142       std::string name( n );
143
144       if ( *f == 'm' || *f == 'D' )     // skip some wellknown nonlibrary memorymapped files
145       {
146         static const char * black[] = {
147           "/SYSV",
148           "/var/run/"
149         };
150         for_( it, arrayBegin( black ), arrayEnd( black ) )
151         {
152           if ( str::hasPrefix( n, *it ) )
153             return;
154         }
155       }
156
157       if ( std::find( cache_r.files.begin(), cache_r.files.end(), name ) == cache_r.files.end() )
158       {
159         // Add if no duplicate
160         cache_r.files.push_back( n );
161       }
162     }
163     /////////////////////////////////////////////////////////////////
164   } // namespace
165   ///////////////////////////////////////////////////////////////////
166
167   CheckAccessDeleted::size_type CheckAccessDeleted::check()
168   {
169     _data.clear();
170     std::vector<ProcInfo> data;
171
172     static const char* argv[] =
173     {
174       "lsof", "-n", "-FpcuLRftkn0", NULL
175     };
176     ExternalProgram prog( argv, ExternalProgram::Discard_Stderr );
177
178     CheckAccessDeleted::ProcInfo cache;
179     for( std::string line = prog.receiveLine(); ! line.empty(); line = prog.receiveLine() )
180     {
181       if ( line[0] == 'p' )
182       {
183         addDataIf( data, cache );
184         cache.pid = line; //
185       }
186       else
187       {
188         addCacheIf( cache, line );
189       }
190     }
191     addDataIf( data, cache );
192
193     int ret = prog.close();
194     if ( ret != 0 )
195     {
196       Exception err( str::form("Executing 'lsof' failed (%d).", ret) );
197       err.remember( prog.execError() );
198       ZYPP_THROW( err );
199     }
200
201     _data.swap( data );
202     return _data.size();
203   }
204
205   std::string CheckAccessDeleted::findService( const Pathname & command_r )
206   {
207     ProcInfo p;
208     p.command = command_r.basename();
209     return p.service();
210   }
211   std::string CheckAccessDeleted::findService( const char * command_r )
212   { return findService( Pathname( command_r ) ); }
213
214   std::string CheckAccessDeleted::findService( const std::string & command_r )
215   { return findService( Pathname( command_r ) ); }
216
217   std::string CheckAccessDeleted::findService( pid_t pid_r )
218   { return findService( filesystem::readlink( Pathname("/proc")/str::numstring(pid_r)/"exe" ) ); }
219
220   ///////////////////////////////////////////////////////////////////
221   namespace
222   { /////////////////////////////////////////////////////////////////
223     /////////////////////////////////////////////////////////////////
224   } // namespace
225   ///////////////////////////////////////////////////////////////////
226
227   std::string CheckAccessDeleted::ProcInfo::service() const
228   {
229     if ( command.empty() )
230       return std::string();
231     // TODO: This needs to be implemented smarter... be carefull
232     // as we don't know whether the target is up.
233
234     static const Pathname initD( "/etc/init.d" );
235     { // init.d script with same name
236       PathInfo pi( initD/command );
237       if ( pi.isFile() && pi.isX() )
238         return command;
239     }
240     { // init.d script with name + 'd'
241       std::string alt( command+"d" );
242       PathInfo pi( initD/alt );
243       if ( pi.isFile() && pi.isX() )
244         return alt;
245     }
246     if ( *command.rbegin() == 'd' )
247     { // init.d script with name - trailing'd'
248       std::string alt( command );
249       alt.erase( alt.size()-1 );
250       PathInfo pi( initD/alt );
251       WAR <<pi << endl;
252       if ( pi.isFile() && pi.isX() )
253         return alt;
254     }
255     return std::string();
256   }
257
258   /******************************************************************
259   **
260   **    FUNCTION NAME : operator<<
261   **    FUNCTION TYPE : std::ostream &
262   */
263   std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted & obj )
264   {
265     return dumpRange( str << "CheckAccessDeleted ",
266                       obj.begin(),
267                       obj.end() );
268   }
269
270    /******************************************************************
271   **
272   **    FUNCTION NAME : operator<<
273   **    FUNCTION TYPE : std::ostream &
274   */
275   std::ostream & operator<<( std::ostream & str, const CheckAccessDeleted::ProcInfo & obj )
276   {
277     if ( obj.pid.empty() )
278       return str << "<NoProc>";
279
280     return dumpRangeLine( str << obj.command
281                               << '<' << obj.pid
282                               << '|' << obj.ppid
283                               << '|' << obj.puid
284                               << '|' << obj.login
285                               << '>',
286                           obj.files.begin(),
287                           obj.files.end() );
288   }
289
290  /////////////////////////////////////////////////////////////////
291 } // namespace zypp
292 ///////////////////////////////////////////////////////////////////