10f66f451Sopenharmony_ci/* df.c - report free disk space.
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2006 Rob Landley <rob@landley.net>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * See http://opengroup.org/onlinepubs/9699919799/utilities/df.html
60f66f451Sopenharmony_ci
70f66f451Sopenharmony_ciUSE_DF(NEWTOY(df, "HPkhit*a[-HPkh]", TOYFLAG_SBIN))
80f66f451Sopenharmony_ci
90f66f451Sopenharmony_ciconfig DF
100f66f451Sopenharmony_ci  bool "df"
110f66f451Sopenharmony_ci  default y
120f66f451Sopenharmony_ci  help
130f66f451Sopenharmony_ci    usage: df [-HPkhi] [-t type] [FILESYSTEM ...]
140f66f451Sopenharmony_ci
150f66f451Sopenharmony_ci    The "disk free" command shows total/used/available disk space for
160f66f451Sopenharmony_ci    each filesystem listed on the command line, or all currently mounted
170f66f451Sopenharmony_ci    filesystems.
180f66f451Sopenharmony_ci
190f66f451Sopenharmony_ci    -a	Show all (including /proc and friends)
200f66f451Sopenharmony_ci    -P	The SUSv3 "Pedantic" option
210f66f451Sopenharmony_ci    -k	Sets units back to 1024 bytes (the default without -P)
220f66f451Sopenharmony_ci    -h	Human readable (K=1024)
230f66f451Sopenharmony_ci    -H	Human readable (k=1000)
240f66f451Sopenharmony_ci    -i	Show inodes instead of blocks
250f66f451Sopenharmony_ci    -t type	Display only filesystems of this type
260f66f451Sopenharmony_ci
270f66f451Sopenharmony_ci    Pedantic provides a slightly less useful output format dictated by Posix,
280f66f451Sopenharmony_ci    and sets the units to 512 bytes instead of the default 1024 bytes.
290f66f451Sopenharmony_ci*/
300f66f451Sopenharmony_ci
310f66f451Sopenharmony_ci#define FOR_df
320f66f451Sopenharmony_ci#include "toys.h"
330f66f451Sopenharmony_ci
340f66f451Sopenharmony_ciGLOBALS(
350f66f451Sopenharmony_ci  struct arg_list *t;
360f66f451Sopenharmony_ci
370f66f451Sopenharmony_ci  long units;
380f66f451Sopenharmony_ci  int column_widths[5];
390f66f451Sopenharmony_ci  int header_shown;
400f66f451Sopenharmony_ci)
410f66f451Sopenharmony_ci
420f66f451Sopenharmony_cistatic void measure_column(int col, const char *s)
430f66f451Sopenharmony_ci{
440f66f451Sopenharmony_ci  size_t len = strlen(s);
450f66f451Sopenharmony_ci
460f66f451Sopenharmony_ci  if (TT.column_widths[col] < len) TT.column_widths[col] = len;
470f66f451Sopenharmony_ci}
480f66f451Sopenharmony_ci
490f66f451Sopenharmony_cistatic void measure_numeric_column(int col, long long n)
500f66f451Sopenharmony_ci{
510f66f451Sopenharmony_ci  snprintf(toybuf, sizeof(toybuf), "%llu", n);
520f66f451Sopenharmony_ci  return measure_column(col, toybuf);
530f66f451Sopenharmony_ci}
540f66f451Sopenharmony_ci
550f66f451Sopenharmony_cistatic void show_header()
560f66f451Sopenharmony_ci{
570f66f451Sopenharmony_ci  TT.header_shown = 1;
580f66f451Sopenharmony_ci
590f66f451Sopenharmony_ci  // The filesystem column is always at least this wide.
600f66f451Sopenharmony_ci  if (TT.column_widths[0] < 14) TT.column_widths[0] = 14;
610f66f451Sopenharmony_ci
620f66f451Sopenharmony_ci  if ((toys.optflags & (FLAG_H|FLAG_h))) {
630f66f451Sopenharmony_ci    xprintf((toys.optflags&FLAG_i) ?
640f66f451Sopenharmony_ci            "%-*sInodes  IUsed  IFree IUse%% Mounted on\n" :
650f66f451Sopenharmony_ci            "%-*s Size  Used Avail Use%% Mounted on\n",
660f66f451Sopenharmony_ci            TT.column_widths[0], "Filesystem");
670f66f451Sopenharmony_ci  } else {
680f66f451Sopenharmony_ci    const char *item_label, *used_label, *free_label, *use_label;
690f66f451Sopenharmony_ci
700f66f451Sopenharmony_ci    if (toys.optflags & FLAG_i) {
710f66f451Sopenharmony_ci      item_label = "Inodes";
720f66f451Sopenharmony_ci      used_label = "IUsed";
730f66f451Sopenharmony_ci      free_label = "IFree";
740f66f451Sopenharmony_ci      use_label = "IUse%";
750f66f451Sopenharmony_ci    } else {
760f66f451Sopenharmony_ci      item_label = TT.units == 512 ? "512-blocks" : "1K-blocks";
770f66f451Sopenharmony_ci      used_label = "Used";
780f66f451Sopenharmony_ci      free_label = "Available";
790f66f451Sopenharmony_ci      use_label = toys.optflags & FLAG_P ? "Capacity" : "Use%";
800f66f451Sopenharmony_ci    }
810f66f451Sopenharmony_ci
820f66f451Sopenharmony_ci    measure_column(1, item_label);
830f66f451Sopenharmony_ci    measure_column(2, used_label);
840f66f451Sopenharmony_ci    measure_column(3, free_label);
850f66f451Sopenharmony_ci    measure_column(4, use_label);
860f66f451Sopenharmony_ci    xprintf("%-*s %*s %*s %*s %*s Mounted on\n",
870f66f451Sopenharmony_ci            TT.column_widths[0], "Filesystem",
880f66f451Sopenharmony_ci            TT.column_widths[1], item_label,
890f66f451Sopenharmony_ci            TT.column_widths[2], used_label,
900f66f451Sopenharmony_ci            TT.column_widths[3], free_label,
910f66f451Sopenharmony_ci            TT.column_widths[4], use_label);
920f66f451Sopenharmony_ci
930f66f451Sopenharmony_ci    // For the "Use%" column, the trailing % should be inside the column.
940f66f451Sopenharmony_ci    TT.column_widths[4]--;
950f66f451Sopenharmony_ci  }
960f66f451Sopenharmony_ci}
970f66f451Sopenharmony_ci
980f66f451Sopenharmony_cistatic void show_mt(struct mtab_list *mt, int measuring)
990f66f451Sopenharmony_ci{
1000f66f451Sopenharmony_ci  unsigned long long size, used, avail, percent, block;
1010f66f451Sopenharmony_ci  char *device;
1020f66f451Sopenharmony_ci
1030f66f451Sopenharmony_ci  // Return if it wasn't found (should never happen, but with /etc/mtab...)
1040f66f451Sopenharmony_ci  if (!mt) return;
1050f66f451Sopenharmony_ci
1060f66f451Sopenharmony_ci  // If we have -t, skip other filesystem types
1070f66f451Sopenharmony_ci  if (TT.t) {
1080f66f451Sopenharmony_ci    struct arg_list *al;
1090f66f451Sopenharmony_ci
1100f66f451Sopenharmony_ci    for (al = TT.t; al; al = al->next)
1110f66f451Sopenharmony_ci      if (!strcmp(mt->type, al->arg)) break;
1120f66f451Sopenharmony_ci
1130f66f451Sopenharmony_ci    if (!al) return;
1140f66f451Sopenharmony_ci  }
1150f66f451Sopenharmony_ci
1160f66f451Sopenharmony_ci  // If we don't have -a, skip synthetic filesystems
1170f66f451Sopenharmony_ci  if (!(toys.optflags & FLAG_a) && !mt->statvfs.f_blocks) return;
1180f66f451Sopenharmony_ci
1190f66f451Sopenharmony_ci  // Figure out how much total/used/free space this filesystem has,
1200f66f451Sopenharmony_ci  // forcing 64-bit math because filesystems are big now.
1210f66f451Sopenharmony_ci  if (toys.optflags & FLAG_i) {
1220f66f451Sopenharmony_ci    size = mt->statvfs.f_files;
1230f66f451Sopenharmony_ci    used = mt->statvfs.f_files - mt->statvfs.f_ffree;
1240f66f451Sopenharmony_ci    avail = getuid() ? mt->statvfs.f_favail : mt->statvfs.f_ffree;
1250f66f451Sopenharmony_ci  } else {
1260f66f451Sopenharmony_ci    block = mt->statvfs.f_bsize ? mt->statvfs.f_bsize : 1;
1270f66f451Sopenharmony_ci    size = (block * mt->statvfs.f_blocks) / TT.units;
1280f66f451Sopenharmony_ci    used = (block * (mt->statvfs.f_blocks-mt->statvfs.f_bfree)) / TT.units;
1290f66f451Sopenharmony_ci    avail= (block*(getuid()?mt->statvfs.f_bavail:mt->statvfs.f_bfree))/TT.units;
1300f66f451Sopenharmony_ci  }
1310f66f451Sopenharmony_ci  if (!(used+avail)) percent = 0;
1320f66f451Sopenharmony_ci  else {
1330f66f451Sopenharmony_ci    percent = (used*100)/(used+avail);
1340f66f451Sopenharmony_ci    if (used*100 != percent*(used+avail)) percent++;
1350f66f451Sopenharmony_ci  }
1360f66f451Sopenharmony_ci
1370f66f451Sopenharmony_ci  device = *mt->device == '/' ? realpath(mt->device, NULL) : NULL;
1380f66f451Sopenharmony_ci  if (!device) device = mt->device;
1390f66f451Sopenharmony_ci
1400f66f451Sopenharmony_ci  if (measuring) {
1410f66f451Sopenharmony_ci    measure_column(0, device);
1420f66f451Sopenharmony_ci    measure_numeric_column(1, size);
1430f66f451Sopenharmony_ci    measure_numeric_column(2, used);
1440f66f451Sopenharmony_ci    measure_numeric_column(3, avail);
1450f66f451Sopenharmony_ci  } else {
1460f66f451Sopenharmony_ci    if (!TT.header_shown) show_header();
1470f66f451Sopenharmony_ci
1480f66f451Sopenharmony_ci    if (toys.optflags & (FLAG_H|FLAG_h)) {
1490f66f451Sopenharmony_ci      char *size_str = toybuf, *used_str = toybuf+64, *avail_str = toybuf+128;
1500f66f451Sopenharmony_ci      int hr_flags = (toys.optflags & FLAG_H) ? HR_1000 : 0;
1510f66f451Sopenharmony_ci      int w = 4 + !!(toys.optflags & FLAG_i);
1520f66f451Sopenharmony_ci
1530f66f451Sopenharmony_ci      human_readable(size_str, size, hr_flags);
1540f66f451Sopenharmony_ci      human_readable(used_str, used, hr_flags);
1550f66f451Sopenharmony_ci      human_readable(avail_str, avail, hr_flags);
1560f66f451Sopenharmony_ci      xprintf("%-*s %*s  %*s  %*s %*llu%% %s\n",
1570f66f451Sopenharmony_ci        TT.column_widths[0], device,
1580f66f451Sopenharmony_ci        w, size_str, w, used_str, w, avail_str, w-1, percent, mt->dir);
1590f66f451Sopenharmony_ci    } else xprintf("%-*s %*llu %*llu %*llu %*llu%% %s\n",
1600f66f451Sopenharmony_ci        TT.column_widths[0], device,
1610f66f451Sopenharmony_ci        TT.column_widths[1], size,
1620f66f451Sopenharmony_ci        TT.column_widths[2], used,
1630f66f451Sopenharmony_ci        TT.column_widths[3], avail,
1640f66f451Sopenharmony_ci        TT.column_widths[4], percent,
1650f66f451Sopenharmony_ci        mt->dir);
1660f66f451Sopenharmony_ci  }
1670f66f451Sopenharmony_ci
1680f66f451Sopenharmony_ci  if (device != mt->device) free(device);
1690f66f451Sopenharmony_ci}
1700f66f451Sopenharmony_ci
1710f66f451Sopenharmony_civoid df_main(void)
1720f66f451Sopenharmony_ci{
1730f66f451Sopenharmony_ci  struct mtab_list *mt, *mtstart, *mtend;
1740f66f451Sopenharmony_ci  int measuring;
1750f66f451Sopenharmony_ci
1760f66f451Sopenharmony_ci  if (toys.optflags & (FLAG_H|FLAG_h)) {
1770f66f451Sopenharmony_ci    TT.units = 1;
1780f66f451Sopenharmony_ci  } else {
1790f66f451Sopenharmony_ci    // Units are 512 bytes if you select "pedantic" without "kilobytes".
1800f66f451Sopenharmony_ci    TT.units = toys.optflags & FLAG_P ? 512 : 1024;
1810f66f451Sopenharmony_ci  }
1820f66f451Sopenharmony_ci
1830f66f451Sopenharmony_ci  if (!(mtstart = xgetmountlist(0))) return;
1840f66f451Sopenharmony_ci  mtend = dlist_terminate(mtstart);
1850f66f451Sopenharmony_ci
1860f66f451Sopenharmony_ci  // If we have a list of filesystems on the command line, loop through them.
1870f66f451Sopenharmony_ci  if (*toys.optargs) {
1880f66f451Sopenharmony_ci    // Measure the names then output the table.
1890f66f451Sopenharmony_ci    for (measuring = 1; measuring >= 0; --measuring) {
1900f66f451Sopenharmony_ci      char **next;
1910f66f451Sopenharmony_ci
1920f66f451Sopenharmony_ci      for (next = toys.optargs; *next; next++) {
1930f66f451Sopenharmony_ci        struct stat st;
1940f66f451Sopenharmony_ci
1950f66f451Sopenharmony_ci        // Stat it (complain if we can't).
1960f66f451Sopenharmony_ci        if (stat(*next, &st)) {
1970f66f451Sopenharmony_ci          perror_msg("'%s'", *next);
1980f66f451Sopenharmony_ci          continue;
1990f66f451Sopenharmony_ci        }
2000f66f451Sopenharmony_ci
2010f66f451Sopenharmony_ci        // Find and display this filesystem.  Use _last_ hit in case of
2020f66f451Sopenharmony_ci        // overmounts (which is first hit in the reversed list).
2030f66f451Sopenharmony_ci        for (mt = mtend; mt; mt = mt->prev) {
2040f66f451Sopenharmony_ci          if (st.st_dev == mt->stat.st_dev
2050f66f451Sopenharmony_ci              || (st.st_rdev && (st.st_rdev == mt->stat.st_dev)))
2060f66f451Sopenharmony_ci          {
2070f66f451Sopenharmony_ci            show_mt(mt, measuring);
2080f66f451Sopenharmony_ci            break;
2090f66f451Sopenharmony_ci          }
2100f66f451Sopenharmony_ci        }
2110f66f451Sopenharmony_ci      }
2120f66f451Sopenharmony_ci    }
2130f66f451Sopenharmony_ci  } else {
2140f66f451Sopenharmony_ci    // Loop through mount list to filter out overmounts.
2150f66f451Sopenharmony_ci    for (mt = mtend; mt; mt = mt->prev) {
2160f66f451Sopenharmony_ci      struct mtab_list *mt2, *mt3;
2170f66f451Sopenharmony_ci
2180f66f451Sopenharmony_ci      // 0:0 is LANANA null device
2190f66f451Sopenharmony_ci      if (!mt->stat.st_dev) continue;
2200f66f451Sopenharmony_ci
2210f66f451Sopenharmony_ci      // Filter out overmounts.
2220f66f451Sopenharmony_ci      mt3 = mt;
2230f66f451Sopenharmony_ci      for (mt2 = mt->prev; mt2; mt2 = mt2->prev) {
2240f66f451Sopenharmony_ci        if (mt->stat.st_dev == mt2->stat.st_dev) {
2250f66f451Sopenharmony_ci          // For --bind mounts, show earliest mount
2260f66f451Sopenharmony_ci          if (!strcmp(mt->device, mt2->device)) {
2270f66f451Sopenharmony_ci            if (!(toys.optflags & FLAG_a)) mt3->stat.st_dev = 0;
2280f66f451Sopenharmony_ci            mt3 = mt2;
2290f66f451Sopenharmony_ci          } else mt2->stat.st_dev = 0;
2300f66f451Sopenharmony_ci        }
2310f66f451Sopenharmony_ci      }
2320f66f451Sopenharmony_ci    }
2330f66f451Sopenharmony_ci
2340f66f451Sopenharmony_ci    // Measure the names then output the table.
2350f66f451Sopenharmony_ci    for (measuring = 1; measuring >= 0; --measuring) {
2360f66f451Sopenharmony_ci      // Cosmetic: show filesystems in creation order.
2370f66f451Sopenharmony_ci      for (mt = mtstart; mt; mt = mt->next) {
2380f66f451Sopenharmony_ci        if (mt->stat.st_dev) show_mt(mt, measuring);
2390f66f451Sopenharmony_ci      }
2400f66f451Sopenharmony_ci    }
2410f66f451Sopenharmony_ci  }
2420f66f451Sopenharmony_ci
2430f66f451Sopenharmony_ci  if (CFG_TOYBOX_FREE) llist_traverse(mtstart, free);
2440f66f451Sopenharmony_ci}
245