libyui-ncurses-pkg  2.48.5.2
NCPkgPopupDiskspace.cc
1 /****************************************************************************
2 |
3 | Copyright (c) [2002-2011] Novell, Inc.
4 | Copyright (c) 2018 SUSE LLC
5 | All Rights Reserved.
6 |
7 | This program is free software; you can redistribute it and/or
8 | modify it under the terms of version 2 of the GNU General Public License as
9 | published by the Free Software Foundation.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program; if not, contact Novell, Inc.
18 |
19 | To contact Novell about this file by physical or electronic mail,
20 | you may find current contact information at www.novell.com
21 |
22 |***************************************************************************/
23 
24 
25 /*---------------------------------------------------------------------\
26 | |
27 | __ __ ____ _____ ____ |
28 | \ \ / /_ _/ ___|_ _|___ \ |
29 | \ V / _` \___ \ | | __) | |
30 | | | (_| |___) || | / __/ |
31 | |_|\__,_|____/ |_| |_____| |
32 | |
33 | core system |
34 | (C) SuSE GmbH |
35 \----------------------------------------------------------------------/
36 
37  File: NCPkgPopupDiskspace.cc
38 
39  Author: Gabriele Strattner <gs@suse.de>
40 
41 /-*/
42 #define YUILogComponent "ncurses-pkg"
43 #include <YUILog.h>
44 
45 #include "YMenuButton.h"
46 #include "YDialog.h"
47 #include "YTypes.h"
48 
49 #include "NCLayoutBox.h"
50 #include "NCSpacing.h"
51 #include "NCPkgStrings.h"
52 #include "NCLabel.h"
53 #include "NCPushButton.h"
54 #include "NCTable.h"
55 
56 #include "NCZypp.h"
57 
58 #include "NCPkgPopupDiskspace.h"
59 
60 #include "NCi18n.h"
61 
62 // zypp::str::form()
63 #include <zypp/base/String.h>
64 
65 // set values as set in YQPkgDiskUsageList.cc
66 #define MIN_FREE_MB_WARN 400
67 #define MIN_FREE_MB_PROXIMITY 700
68 
69 #define MIN_PERCENT_WARN 90
70 #define MIN_PERCENT_PROXIMITY 80
71 
72 #define OVERFLOW_MB_WARN 0
73 #define OVERFLOW_MB_PROXIMITY 300
74 
75 using std::endl;
76 
77 /*
78  Textdomain "ncurses-pkg"
79 */
80 
81  ///////////////////////////////////////////////////////////////////
82 //
83 //
84 // METHOD NAME : NCPkgDiskspace::NCPkgDiskspace
85 // METHOD TYPE : Constructor
86 //
87 // DESCRIPTION :
88 //
89 NCPkgDiskspace::NCPkgDiskspace( bool testMode )
90  : testmode( testMode )
91  , popupWin( 0 )
92 {
93 
94  if ( testMode )
95  {
96  yuiMilestone() << "TESTMODE Diskspace" << endl;
97  zypp::getZYpp()->setPartitions(zypp::DiskUsageCounter::detectMountPoints ());
98  testDiskUsage = zypp::getZYpp()->diskUsage();
99  }
100 }
101 
102 ///////////////////////////////////////////////////////////////////
103 //
104 //
105 // METHOD NAME : NCPkgDiskspace::~NCPkgDiskspace
106 // METHOD TYPE : Destructor
107 //
108 // DESCRIPTION :
109 //
110 NCPkgDiskspace::~NCPkgDiskspace()
111 {
112 }
113 
114 namespace {
115  std::string formatSize(double size, int width = 0)
116  {
117  // FSize::bestUnit does not work for huge numbers so only use it for small ones
118  FSize::Unit unit = (size >= FSize::TB) ? FSize::T : FSize(size).bestUnit();
119  int prec = unit == FSize::B ? 0 : 2;
120 
121  return zypp::str::form( "%*.*f %s", width, prec, size / FSize::factor(unit), FSize::unit(unit));
122  }
123 
124  /**
125  * Compute percent usage
126  * @param used used size (any unit, but the same as the "total")
127  * @param total total size (any unit, but the same as the "used")
128  * @return percent, might be more than 100 if the size of the selected
129  * packages is bigger than the available free size
130  */
131  int usedPercentInt(long long used, long long total)
132  {
133  int percent = 0;
134 
135  if ( total != 0 )
136  // temporarily use double to avoid overflow
137  percent = ( 100.0 * used ) / total;
138 
139  return percent;
140  }
141 
142  // see usedPercentInt()
143  std::string usedPercentStr(long long used, long long total)
144  {
145  int percent = usedPercentInt(used, total);
146  return zypp::str::form( "%2d%%", percent );
147  }
148 }
149 
150 ///////////////////////////////////////////////////////////////////
151 //
152 //
153 // METHOD NAME : NCPkgDiskspace::fillPartitionTable
154 // METHOD TYPE : void
155 //
156 // DESCRIPTION :
157 //
158 void NCPkgDiskspace::fillPartitionTable()
159 {
160  NCTable * partitions = popupWin->Partitions();
161  partitions->deleteAllItems(); // clear table
162 
163  zypp::ZYpp::Ptr z = zypp::getZYpp();
164  ZyppDuSet du = z->diskUsage ();
165 
166  if (du.empty())
167  {
168  // retry after detecting from the target
169  z->setPartitions(zypp::DiskUsageCounter::detectMountPoints ());
170  du = z->diskUsage();
171  }
172 
173  for (const ZyppPartitionDu &item: du)
174  {
175  if (item.readonly)
176  continue;
177 
178  double pkg_used = double(item.pkg_size) * FSize::KB;
179  double pkg_available = double(item.total_size - item.pkg_size) * FSize::KB;
180  double total = double(item.total_size) * FSize::KB;
181 
182  YTableItem * newItem = new YTableItem( item.dir,
183  formatSize(pkg_used, 8),
184  formatSize(pkg_available, 8),
185  formatSize(total, 8),
186  usedPercentStr( item.pkg_size, item.total_size ) );
187 
188  partitions->addItem( newItem );
189  }
190 }
191 
192 ///////////////////////////////////////////////////////////////////
193 //
194 //
195 // METHOD NAME : NCPkgDiskspace::checkDiskSpace
196 // METHOD TYPE : std::string
197 //
198 // DESCRIPTION : called to check disk space before installation
199 // (after OK button is pressed)
200 //
201 std::string NCPkgDiskspace::checkDiskSpace()
202 {
203  std::string text;
204 
205  zypp::ZYpp::Ptr z = zypp::getZYpp();
206  ZyppDuSet du = z->diskUsage ();
207 
208  if (du.empty())
209  {
210  // retry after detecting from the target
211  z->setPartitions(zypp::DiskUsageCounter::detectMountPoints ());
212  du = z->diskUsage();
213  }
214 
215  for (const ZyppPartitionDu &item: du)
216  {
217  if (item.readonly)
218  continue;
219 
220  // available size (in KiB!)
221  long long pkg_available = item.total_size - item.pkg_size;
222  if ( pkg_available < 0 )
223  {
224  text += "\"";
225  text += item.dir;
226  text += "\"";
227  text += " ";
228  text += NCPkgStrings::MoreText();
229  text += " ";
230  // make positive, use double to avoid overflow
231  text += formatSize(-1.0 * double(pkg_available) * FSize::KB);
232  text += " ";
233  text += NCPkgStrings::MoreSpaceText();
234  text += "<br>";
235  }
236  }
237  return text;
238 }
239 
240 ///////////////////////////////////////////////////////////////////
241 //
242 //
243 // METHOD NAME : NCPkgDiskspace::checkRemainingDiskSpace
244 // METHOD TYPE : void
245 //
246 // DESCRIPTION : check whether remaining disk space enters
247 // warning or error range
248 //
249 void NCPkgDiskspace::checkRemainingDiskSpace( const ZyppPartitionDu & partition )
250 {
251  if ( partition.readonly )
252  return;
253 
254  int percent = usedPercentInt(partition.pkg_size, partition.total_size);
255 
256  // free in MiB - libzyp sizes are already in KiB, divide by 1024 to get MiB
257  long long free = (partition.total_size - partition.pkg_size) / 1024;
258 
259  yuiMilestone() << "Partition: " << partition.dir << " Total (MiB): "
260  << partition.total_size / 1024 << " Used (MiB): " << partition.pkg_size / 1024
261  << " Used percent: " << percent << "% Free (MiB): " << free << endl;
262 
263  if ( percent > MIN_PERCENT_WARN )
264  {
265  // Modern hard disks can be huge, so a warning based on percentage only
266  // can be misleading - check the absolute value, too.
267  if ( free < MIN_FREE_MB_PROXIMITY )
268  {
269  yuiWarning() << "free < MIN_FREE_MB_PROXIMITY (" << MIN_FREE_MB_PROXIMITY << ")" << endl;
270  runningOutWarning.enterProximity();
271  }
272  if ( free < MIN_FREE_MB_WARN )
273  {
274  yuiWarning() << "free < MIN_FREE_MB_WARN (" << MIN_FREE_MB_WARN << ")" << endl;
275  runningOutWarning.enterRange();
276  }
277  }
278 
279  if ( free < MIN_FREE_MB_PROXIMITY )
280  {
281  if ( percent > MIN_PERCENT_PROXIMITY )
282  runningOutWarning.enterProximity();
283  }
284 
285  if ( free < OVERFLOW_MB_WARN )
286  overflowWarning.enterRange();
287 
288  if ( free < OVERFLOW_MB_PROXIMITY )
289  overflowWarning.enterProximity();
290 
291 }
292 
293 ///////////////////////////////////////////////////////////////////
294 //
295 //
296 // METHOD NAME : NCPkgDiskspace::setDiskSpace
297 // METHOD TYPE : void
298 //
299 // DESCRIPTION : For testing only; called from NCPkgTable if the PackageSelector
300 // running in testMode
301 // TESTDESCRIPTION: Call `PackageSelector with `opt(`testMode) (ycp example).
302 // With focus on the package list press '+' or '-' to
303 // increase/decrease used diskspace (see y2log).
304 // Use the 'Actions' menu to select/delete a package.
305 //
306 void NCPkgDiskspace::setDiskSpace( wint_t ch )
307 {
308  // set diskspace values in ZyppDuSet testDiskSpace
309  for ( const ZyppPartitionDu &partitionDu: testDiskUsage )
310  {
311  int percent = usedPercentInt(partitionDu.pkg_size, partitionDu.total_size);
312 
313  if ( ch == '+' )
314  percent += 3;
315  else if ( ch == '-' )
316  percent -= 3;
317 
318  if ( percent < 0 )
319  percent = 0;
320 
321  partitionDu.pkg_size = partitionDu.total_size / 100 * percent;
322 
323  // libzyp sizes are already in KiB, divide by 1024 to get MiB
324  yuiMilestone() << "Used size (MiB): " << partitionDu.pkg_size / 1024 << endl;
325  yuiMilestone() << "Total size (MiB): " << partitionDu.total_size / 1024 << endl;
326  }
327 }
328 
329 ///////////////////////////////////////////////////////////////////
330 //
331 //
332 // METHOD NAME : NCPkgDiskspace::checkDiskSpaceRange
333 // METHOD TYPE : void
334 //
335 // DESCRIPTION : calls checkRemaingDiskspace for every partition
336 //
337 void NCPkgDiskspace::checkDiskSpaceRange( )
338 {
339  // see YQPkgDiskUsageList::updateDiskUsage()
340  runningOutWarning.clear();
341  overflowWarning.clear();
342  ZyppDuSet diskUsage;
343 
344  if ( testmode )
345  diskUsage = testDiskUsage;
346  else
347  diskUsage = zypp::getZYpp()->diskUsage();
348 
349  for ( ZyppDuSetIterator it = diskUsage.begin();
350  it != diskUsage.end();
351  ++it )
352  {
353  //Exclude readonly dirs from the check (#384368)
354  if( it->readonly )
355  continue;
356  checkRemainingDiskSpace( *it );
357  }
358 
359  // see YQPkgDiskUsageList::postPendingWarnings()
360  if ( overflowWarning.needWarning() )
361  {
362  showInfoPopup( _( "Error: Out of disk space!" ) );
363 
364  overflowWarning.warningPostedNotify();
365  runningOutWarning.warningPostedNotify(); // Suppress this ( now redundant ) other warning
366  }
367 
368  if ( runningOutWarning.needWarning() )
369  {
370  showInfoPopup( _( "Warning: Disk space is running out!" ) );
371 
372  runningOutWarning.warningPostedNotify();
373  }
374 
375  if ( overflowWarning.leavingProximity() )
376  overflowWarning.clearHistory();
377 
378  if ( runningOutWarning.leavingProximity() )
379  runningOutWarning.clearHistory();
380 
381  if ( testmode )
382  {
383  yuiMilestone() << "Running out Warning:" << endl;
384  runningOutWarning.logSettings();
385 
386  yuiMilestone() << "Overflow Warning:" << endl;
387  overflowWarning.logSettings();
388  }
389 }
390 
391 // FIXME: do not use this, contains overflow bug, use usedPercentStr() instead!
392 std::string NCPkgDiskspace::usedPercent( FSize used, FSize total )
393 {
394  int percent = 0;
395  char percentStr[10];
396 
397  if ( total != 0 )
398  percent = ( 100 * used ) / total;
399 
400  sprintf( percentStr, "%d%%", percent );
401 
402  return percentStr;
403 }
404 
405 ///////////////////////////////////////////////////////////////////
406 //
407 //
408 // METHOD NAME : NCPkgDiskspace::showInfoPopup
409 // METHOD TYPE : void
410 //
411 // DESCRIPTION :
412 //
413 void NCPkgDiskspace::showInfoPopup( std::string headline )
414 {
415 
416  popupWin = new NCPkgPopupDiskspace (wpos( (NCurses::lines() - 15)/2, NCurses::cols()/6 ), headline );
417  // update values in partition table
418  fillPartitionTable();
419  popupWin->doit();
420  YDialog::deleteTopmostDialog();
421 }
422 
423 zypp::ByteCount NCPkgDiskspace::calculateDiff()
424 {
425  zypp::ZYpp::Ptr z = zypp::getZYpp();
426  ZyppDuSet du = z->diskUsage ();
427 
428  if (du.empty())
429  {
430  // retry after detecting from the target
431  z->setPartitions(zypp::DiskUsageCounter::detectMountPoints ());
432  du = z->diskUsage();
433  }
434 
435  zypp::ByteCount diff = 0;
436  for (const ZyppPartitionDu &item: du)
437  {
438  // the diff should be normally very small, the "long long" limit should be never reached (TM)
439  diff += (item.pkg_size - item.used_size) * 1024;
440  }
441 
442  return diff;
443 }
444 
445 //////////////////////////////////////////////////////////////////
446 //
447 //
448 // METHOD NAME : NCPkgPopupDiskspace::NCPkgPopupDiskspace
449 // METHOD TYPE : Constructor
450 //
451 NCPkgPopupDiskspace::NCPkgPopupDiskspace( const wpos at, std::string headline )
452  : NCPopup( at, false )
453  , partitions( 0 )
454  , okButton( 0 )
455  , head( 0 )
456 {
457  createLayout( headline );
458 }
459 
460 ///////////////////////////////////////////////////////////////////
461 //
462 //
463 // METHOD NAME : NCPkgPopupDiskspace::~NCPkgPopupDiskspace
464 // METHOD TYPE : Destructor
465 //
466 NCPkgPopupDiskspace::~NCPkgPopupDiskspace()
467 {
468 }
469 
470 //////////////////////////////////////////////////////////////////
471 //
472 //
473 // METHOD NAME : NCPkgPopupDiskspace::createLyout
474 // METHOD TYPE : void
475 //
476 // DESCRIPTION : create layout (partition table)
477 //
478 void NCPkgPopupDiskspace::createLayout( std::string headline )
479 {
480  // the vertical split is the (only) child of the dialog
481  NCLayoutBox * split = new NCLayoutBox( this, YD_VERT );
482 
483  head = new NCLabel( split, "", true, false ); // isHeading = true
484  head->setLabel( headline );
485 
486  YTableHeader * tableHeader = new YTableHeader();
487  tableHeader->addColumn( NCPkgStrings::Partition(), YAlignBegin );
488  tableHeader->addColumn( NCPkgStrings::UsedSpace(), YAlignBegin );
489  tableHeader->addColumn( NCPkgStrings::FreeSpace(), YAlignBegin );
490  tableHeader->addColumn( NCPkgStrings::TotalSpace(), YAlignBegin );
491  tableHeader->addColumn( "% ", YAlignBegin );
492 
493  // add the partition table
494  partitions = new NCTable( split, tableHeader );
495 
496  // add the ok button
497  okButton = new NCPushButton( split, NCPkgStrings::OKLabel() );
498  okButton->setFunctionKey( 10 );
499  okButton->setKeyboardFocus();
500 }
501 
502 
503 ///////////////////////////////////////////////////////////////////
504 //
505 //
506 // METHOD NAME : NCPkgPopupDiskspace::preferredWidth
507 // METHOD TYPE : int
508 //
509 int NCPkgPopupDiskspace::preferredWidth()
510 {
511  return NCurses::cols()*2/3;
512 }
513 
514 ///////////////////////////////////////////////////////////////////
515 //
516 //
517 // METHOD NAME : NCPkgPopupDiskspace::preferredHeight
518 // METHOD TYPE : int
519 //
520 int NCPkgPopupDiskspace::preferredHeight()
521 {
522  if ( NCurses::lines() > 15 )
523  return 15;
524  else
525  return NCurses::lines()-4;
526 }
527 
528 void NCPkgPopupDiskspace::doit()
529 {
530  postevent = NCursesEvent();
531  do {
532  // show the popup
533  popupDialog( );
534  } while ( postAgain() );
535 
536  popdownDialog();
537 
538 }
539 ///////////////////////////////////////////////////////////////////
540 //
541 //
542 // METHOD NAME : NCPopup::wHandleInput
543 // METHOD TYPE : NCursesEvent
544 //
545 // DESCRIPTION :
546 //
547 NCursesEvent NCPkgPopupDiskspace::wHandleInput( wint_t ch )
548 {
549  if ( ch == 27 ) // ESC
550  return NCursesEvent::cancel;
551 
552  if ( ch == KEY_RETURN )
553  return NCursesEvent::button;
554 
555  return NCDialog::wHandleInput( ch );
556 }
557 
558 ///////////////////////////////////////////////////////////////////
559 //
560 //
561 // METHOD NAME : NCPkgPopupDiskspace::postAgain
562 // METHOD TYPE : bool
563 //
564 // DESCRIPTION :
565 //
566 bool NCPkgPopupDiskspace::postAgain()
567 {
568  if ( ! postevent.widget )
569  return false;
570 
571  if ( postevent == NCursesEvent::button || postevent == NCursesEvent::cancel )
572  {
573  // return false means: close the popup dialog
574  return false;
575  }
576  return true;
577 }
578 
579 
580 
581 
583 {
584  clearHistory();
585 }
586 
587 
588 void
590 {
591  _inRange = false;
592  _hasBeenClose = _isClose;
593  _isClose = false;
594 }
595 
596 
597 void
599 {
600  clear();
601  _hasBeenClose = false;
602  _warningPosted = false;
603 }
604 
605 
606 void
608 {
609  _inRange = true;
610  enterProximity();
611 }
612 
613 
614 void
616 {
617  _isClose = true;
618  _hasBeenClose = true;
619 }
620 
621 
622 void
624 {
625  _warningPosted = true;
626 }
627 
628 
629 bool
631 {
632  return _inRange;
633 }
634 
635 
636 bool
638 {
639  return ! _isClose && ! _hasBeenClose;
640 }
641 
642 
643 bool
645 {
646  return _inRange && ! _warningPosted;
647 }
648 
649 void
650 NCPkgWarningRangeNotifier::logSettings() const
651 {
652  yuiMilestone() << "in range: " << (_inRange?"true":"false") << endl;
653  yuiMilestone() << "is close: " << (_isClose?"true":"false") << endl;
654  yuiMilestone() << "has been close: " << (_hasBeenClose?"true":"false") << endl;
655  yuiMilestone() << "warning posted: " << (_warningPosted?"true":"false") << endl;
656 }
657 
bool needWarning() const
Check if a warning should be posted, i.e.
void warningPostedNotify()
Notification that a warning has been posted.
void clearHistory()
Clear everything, including all history values such as if a warning has been posted.
bool leavingProximity() const
Check if the value is leaving the proximity range.
void clear()
Clear the current values, i.e.
NCPkgWarningRangeNotifier()
Constructor.
bool inRange() const
Check if the value is in range, i.e.
void enterProximity()
Notification that the proximity range is entered, i.e.
void enterRange()
Notification that the inner range is entered.
static const std::string OKLabel()
The label of the OK button.