commit: 2ff495382f01063adff6085a885b9deea139779b
parent b56ecbcaf9613b85c7ee04604d54de263e284ef2
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Sun, 11 Feb 2024 20:13:02 +0100
cmd/df: new
Diffstat:
5 files changed, 244 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -64,3 +64,7 @@ cmd/sleep: cmd/sleep.c lib/strtodur.c Makefile
test-lib/strtodur: test-lib/strtodur.c lib/strtodur.c Makefile
$(CC) -std=c99 $(CFLAGS) $(ATF_CFLAGS) -o $@ test-lib/strtodur.c lib/strtodur.c $(LDFLAGS) $(ATF_LIBS)
+
+cmd/df: cmd/df.c lib/humanize.c Makefile
+ rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno}
+ $(CC) -std=c99 $(CFLAGS) -o $@ cmd/df.c lib/humanize.c $(LDFLAGS) $(LDSTATIC)
diff --git a/cmd/df.c b/cmd/df.c
@@ -0,0 +1,172 @@
+// utils-std: Collection of commonly available Unix tools
+// SPDX-FileCopyrightText: 2017-2024 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+// SPDX-License-Identifier: MPL-2.0
+
+#define _POSIX_C_SOURCE 200809L
+#define _DEFAULT_SOURCE // mntent in glibc 2.19+
+
+#include "../lib/humanize.h"
+
+#include <errno.h> // errno
+#include <mntent.h>
+#include <stdbool.h>
+#include <stdio.h> // printf
+#include <stdlib.h> // abort, exit
+#include <string.h> // strerror
+#include <sys/statvfs.h>
+#include <unistd.h> // getopt
+
+size_t forced_bsize = 0;
+
+static void
+unescape_path(char *path)
+{
+ char *needle = "\\040";
+ size_t esc_len = 3; // 4 chars but leading-slash is kept for space
+
+ char *str = strstr(path, needle);
+
+ if(str != NULL)
+ {
+ size_t pos = 0;
+
+ str[pos++] = ' ';
+
+ while(str[pos] != '\0')
+ {
+ str[pos] = str[pos + esc_len];
+ pos++;
+ }
+
+ unescape_path(str + pos);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ bool opt_P = false, opt_h = false;
+
+ int c = EOF;
+ while((c = getopt(argc, argv, ":hPk")) != EOF)
+ {
+ switch(c)
+ {
+ case 'P':
+ if(forced_bsize == 0) forced_bsize = 512;
+
+ opt_P = true;
+ break;
+ case 'k':
+ forced_bsize = 1024;
+ break;
+ case 'h':
+ opt_h = true;
+ break;
+ case ':':
+ fprintf(stderr, "df: Error: Missing operand for option: '-%c'\n", optopt);
+ return 1;
+ case '?':
+ fprintf(stderr, "df: Error: Unrecognised option: '-%c'\n", optopt);
+ return 1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(forced_bsize != 0)
+ {
+ printf("Filesystem %zd-blocks Used Available Capacity Mounted on\n", forced_bsize);
+ }
+ else
+ {
+ printf("Filesystem Total Used Available Use%% Mountpoint\n");
+ }
+
+ FILE *mnt = setmntent(MOUNTED, "r");
+ if(mnt == NULL)
+ {
+ fprintf(stderr, "df: Error opening setmntent(\"" MOUNTED "\", \"r\"): %s", strerror(errno));
+ return 1;
+ }
+
+ while(1)
+ {
+ struct mntent *mntent = getmntent(mnt);
+ if(mntent == NULL) break;
+
+ // Linux escapes spaces in filesystems and mountpoints with \040
+ // which statvfs(3) doesn't interprets
+ // Also shouldn't be printed out to not break parsing of df(1) output
+ char *mountpoint = strdup(mntent->mnt_dir);
+ if(mountpoint == NULL)
+ {
+ fprintf(stderr, "df: Failed duplicating mnt_dir string: %s\n", strerror(errno));
+ return 1;
+ exit(1);
+ }
+ unescape_path(mountpoint);
+
+ struct statvfs stats;
+ if(statvfs(mountpoint, &stats) != 0)
+ {
+ fprintf(stderr, "df: Error statvfs(\"%s\", _): %s\n", mountpoint, strerror(errno));
+ printf("%s - - - - %s\n", mntent->mnt_fsname, mntent->mnt_dir);
+
+ free(mountpoint);
+ continue;
+ }
+ free(mountpoint);
+
+ size_t percent = 0;
+ size_t total = stats.f_frsize * stats.f_blocks;
+ size_t free = stats.f_bfree * stats.f_bsize;
+ size_t used = total - free;
+
+ if(used + free)
+ {
+ percent = (used * 100) / (used + free);
+ if(used * 100 != percent * (used + free)) percent++;
+ }
+
+ if(forced_bsize != 0)
+ {
+ total /= forced_bsize;
+ free /= forced_bsize;
+ used /= forced_bsize;
+ }
+
+ if(opt_h && !opt_P)
+ {
+ struct si_scale total_scl = dtosi(total, true);
+ struct si_scale used_scl = dtosi(used, true);
+ struct si_scale free_scl = dtosi(free, true);
+
+ // clang-format off
+ printf("%s %.2f%s %.2f%s %.2f%s %zd%% %s\n",
+ mntent->mnt_fsname,
+ total_scl.number, total_scl.prefix,
+ used_scl.number, used_scl.prefix,
+ free_scl.number, free_scl.prefix,
+ percent,
+ mntent->mnt_dir
+ );
+ // clang-format on
+ }
+ else
+ {
+ printf("%s %zd %zd %zd %zd%% %s\n",
+ mntent->mnt_fsname,
+ total,
+ used,
+ free,
+ percent,
+ mntent->mnt_dir);
+ }
+ }
+
+ endmntent(mnt);
+
+ return 0;
+}
diff --git a/configure b/configure
@@ -89,6 +89,17 @@ check_cmd() {
command -v "$cmd" >/dev/null ; is_ok
}
+check_header() {
+ header="$1"
+
+ printf 'Checking <%s> header ...' "$header"
+
+ $CC -E - >/dev/null 2>/dev/null <<-EOF
+ #include <$header>
+ EOF
+ is_ok
+}
+
## User configuration
# defaults
@@ -215,6 +226,11 @@ if pkg_config_check --exists atf-c; then
ATF_LIBS=$("${PKGCONFIG}" --libs atf-c)
fi
+if ! check_header mntent.h; then
+ echo 'Disabling cmd/df'
+ echo 'cmd/df' >> target_filter
+fi
+
echo
## Configuration write
diff --git a/lib/humanize.c b/lib/humanize.c
@@ -0,0 +1,37 @@
+// utils-std: Collection of commonly available Unix tools
+// SPDX-FileCopyrightText: 2017-2024 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+// SPDX-License-Identifier: MPL-2.0
+
+#define _POSIX_C_SOURCE 200809L
+
+#include "../lib/humanize.h"
+
+#include <stdio.h> // snprintf
+
+struct si_scale
+dtosi(double num, bool iec)
+{
+#define PFX 11
+ char *si_prefixes[PFX] = {"", "k", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"};
+ char *iec_prefixes[PFX] = {
+ "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB"};
+
+ int div = iec ? 1024 : 1000;
+ char **prefixes = iec ? iec_prefixes : si_prefixes;
+
+ struct si_scale ret = {
+ .number = num,
+ .prefix = "",
+ .quotient = 0,
+ };
+
+ while(ret.number > div && ret.quotient < PFX)
+ {
+ ret.number /= div;
+ ret.quotient += 1;
+ }
+
+ ret.prefix = prefixes[ret.quotient];
+
+ return ret;
+}
diff --git a/lib/humanize.h b/lib/humanize.h
@@ -0,0 +1,15 @@
+// utils-std: Collection of commonly available Unix tools
+// SPDX-FileCopyrightText: 2017-2024 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+// SPDX-License-Identifier: MPL-2.0
+
+#include <stdbool.h> // bool
+#include <stddef.h> // size_t
+
+struct si_scale
+{
+ double number;
+ char *prefix;
+ unsigned quotient;
+};
+
+struct si_scale dtosi(double num, bool iec);