move into trunk
[platform/upstream/libzypp.git] / zypp / DiskUsageCounter.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/DiskUsageCounter.cc
10  *
11  */
12 extern "C"
13 {
14 #include <sys/statvfs.h>
15 }
16
17 #include <iostream>
18 #include <fstream>
19
20 #include "zypp/base/Easy.h"
21 #include "zypp/base/Logger.h"
22 #include "zypp/base/String.h"
23
24 #include "zypp/DiskUsageCounter.h"
25 #include "zypp/Package.h"
26
27 using std::endl;
28
29 ///////////////////////////////////////////////////////////////////
30 namespace zypp
31 { /////////////////////////////////////////////////////////////////
32
33   ///////////////////////////////////////////////////////////////////
34   namespace
35   { /////////////////////////////////////////////////////////////////
36
37     inline void addDu( DiskUsageCounter::MountPointSet & result_r, DiskUsage & du_r )
38     {
39       // traverse mountpoints in reverse order. This is done beacuse
40       // DiskUsage::extract computes the mountpoint size, and then
41       // removes the entry. So we must process leaves first.
42       for_( mpit, result_r.rbegin(), result_r.rend() )
43       {
44         // Extract usage for the mount point
45         DiskUsage::Entry entry = du_r.extract( mpit->dir );
46         // Adjust the data.
47         mpit->pkg_size += entry._size;
48       }
49     }
50
51     inline void delDu( DiskUsageCounter::MountPointSet & result_r, DiskUsage & du_r )
52     {
53       // traverse mountpoints in reverse order. This is done beacuse
54       // DiskUsage::extract computes the mountpoint size, and then
55       // removes the entry. So we must process leaves first.
56       for_( mpit, result_r.rbegin(), result_r.rend() )
57       {
58         // Extract usage for the mount point
59         DiskUsage::Entry entry = du_r.extract( mpit->dir );
60         // Adjust the data.
61         mpit->pkg_size -= entry._size;
62       }
63     }
64
65     /////////////////////////////////////////////////////////////////
66   } // namespace
67   ///////////////////////////////////////////////////////////////////
68
69  DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( const ResPool & pool_r )
70   {
71     DiskUsageCounter::MountPointSet result = mps;
72
73     if ( result.empty() )
74     {
75       // partitioning is not set
76       return result;
77     }
78
79     // set used size after commit to the current used size
80     for_( it, result.begin(), result.end() )
81     {
82       it->pkg_size = it->used_size;
83     }
84
85     // iterate through all items
86     for_( it, pool_r.begin(), pool_r.end() )
87     {
88       // skip items that do not transact
89       if ( ! it->status().transacts() )
90         continue;
91
92       DiskUsage du( (*it)->diskusage() );
93
94       // skip items without du info
95       if ( du.empty() )
96         continue; // or find some substitute info
97
98       // Adjust the data.
99       if ( it->status().isUninstalled() )
100       {
101         // an uninstalled item gets installed:
102         addDu( result, du );
103
104         // While there is no valid solver result, items to update
105         // are selected, but installed old versions are not yet
106         // deselected. We try to compensate this:
107         if ( ! (*it)->installOnly() )
108         {
109           // Item to update -> check the installed ones.
110           for_( nit, pool_r.byIdentBegin( *it ), pool_r.byIdentEnd( *it ) )
111           {                                       // same kind and name
112             if ( nit->status().staysInstalled() ) // and unselected installed
113             {
114               DiskUsage ndu( (*nit)->diskusage() );
115               if ( ! ndu.empty() )
116               {
117                 delDu( result, ndu );
118               }
119             }
120           }
121         }
122       }
123       else
124       {
125         // an installed item gets deleted:
126         delDu( result, du );
127       }
128     }
129     return result;
130   }
131
132   DiskUsageCounter::MountPointSet DiskUsageCounter::detectMountPoints(const std::string &rootdir)
133   {
134     DiskUsageCounter::MountPointSet ret;
135
136       std::ifstream procmounts( "/proc/mounts" );
137
138       if ( !procmounts ) {
139         WAR << "Unable to read /proc/mounts" << std::endl;
140       } else {
141
142         std::string prfx;
143         if ( rootdir != "/" )
144           prfx = rootdir; // rootdir not /
145
146         while ( procmounts ) {
147           std::string l = str::getline( procmounts );
148           if ( !(procmounts.fail() || procmounts.bad()) ) {
149             // data to consume
150
151             // rootfs   /               rootfs          rw 0 0
152             // /dev/root        /               reiserfs        rw 0 0
153             // proc     /proc           proc            rw 0 0
154             // devpts   /dev/pts        devpts          rw 0 0
155             // /dev/hda5        /boot           ext2            rw 0 0
156             // shmfs    /dev/shm        shm             rw 0 0
157             // usbdevfs         /proc/bus/usb   usbdevfs        rw 0 0
158
159             std::vector<std::string> words;
160             str::split( l, std::back_inserter(words) );
161
162             if ( words.size() < 3 ) {
163               WAR << "Suspicious entry in /proc/mounts: " << l << std::endl;
164               continue;
165             }
166
167             //
168             // Filter devices without '/' (proc,shmfs,..)
169             //
170             if ( words[0].find( '/' ) == std::string::npos ) {
171               DBG << "Discard mount point : " << l << std::endl;
172               continue;
173             }
174
175             //
176             // Filter mountpoints not at or below _rootdir
177             //
178             std::string mp = words[1];
179             if ( prfx.size() ) {
180               if ( mp.compare( 0, prfx.size(), prfx ) != 0 ) {
181                 // mountpoint not below rootdir
182                 DBG << "Unwanted mount point : " << l << std::endl;
183                 continue;
184               }
185               // strip prfx
186               mp.erase( 0, prfx.size() );
187               if ( mp.empty() ) {
188                 mp = "/";
189               } else if ( mp[0] != '/' ) {
190                 // mountpoint not below rootdir
191                 DBG << "Unwanted mount point : " << l << std::endl;
192                 continue;
193               }
194             }
195
196             //
197             // Filter cdrom
198             //
199             if ( words[2] == "iso9660" ) {
200               DBG << "Discard cdrom : " << l << std::endl;
201               continue;
202             }
203
204             //
205             // Filter some common unwanted mountpoints
206             //
207             const char * mpunwanted[] = {
208               "/mnt", "/media", "/mounts", "/floppy", "/cdrom",
209               "/suse", "/var/tmp", "/var/adm/mount", "/var/adm/YaST",
210               /*last*/0/*entry*/
211             };
212
213             const char ** nomp = mpunwanted;
214             for ( ; *nomp; ++nomp ) {
215               std::string pre( *nomp );
216               if ( mp.compare( 0, pre.size(), pre ) == 0 // mp has prefix pre
217                    && ( mp.size() == pre.size() || mp[pre.size()] == '/' ) ) {
218                 break;
219               }
220             }
221             if ( *nomp ) {
222               DBG << "Filter mount point : " << l << std::endl;
223               continue;
224             }
225
226             //
227             // Check whether mounted readonly
228             //
229             bool ro = false;
230             std::vector<std::string> flags;
231             str::split( words[3], std::back_inserter(flags), "," );
232
233             for ( unsigned i = 0; i < flags.size(); ++i ) {
234               if ( flags[i] == "ro" ) {
235                 ro = true;
236                 break;
237               }
238             }
239             if ( ro ) {
240               DBG << "Filter ro mount point : " << l << std::endl;
241               continue;
242             }
243
244             //
245             // statvfs (full path!) and get the data
246             //
247             struct statvfs sb;
248             if ( statvfs( words[1].c_str(), &sb ) != 0 ) {
249               WAR << "Unable to statvfs(" << words[1] << "); errno " << errno << std::endl;
250               ret.insert( DiskUsageCounter::MountPoint( mp ) );
251             }
252             else
253             {
254               ret.insert( DiskUsageCounter::MountPoint( mp, sb.f_bsize,
255                 ((long long)sb.f_blocks)*sb.f_bsize/1024,
256                 ((long long)(sb.f_blocks - sb.f_bfree))*sb.f_bsize/1024, 0LL, ro ) );
257             }
258           }
259         }
260     }
261
262     return ret;
263   }
264
265   std::ostream & operator<<( std::ostream & str, const DiskUsageCounter::MountPoint & obj )
266   {
267     str << "dir:[" << obj.dir << "] [ bs: " << obj.block_size
268         << " ts: " << obj.total_size
269         << " us: " << obj.used_size
270         << " (+-: " << (obj.pkg_size-obj.used_size)
271         << ")]" << std::endl;
272     return str;
273   }
274
275 } // namespace zypp
276 ///////////////////////////////////////////////////////////////////