I need simple a way to get the secondary storage details (like total size, used and free space) in a (daemon) C code for Linux;
This are the things I tried
df -h --total | grep total > disk.stat
) in the C code and then read the file.But the above involves file write and read which is not efficient cause this C code is a daemon which will be polling the system details continuously as input to a graph generation.
If there no is other way, tell me a simple and fast ipc mechanism with example for communication between this bash and C code.
/*
* @breif returns total percentage of secondary storage used
*
* - uses bash command to get storage data and store in a file
* - and use c code retrive the percent of usage from file and return it
*/
int calculate_storage_size( )
{
if ( system("df -h --total | grep total > disk.stat") >= 0 )
{
char *temp_char_ptr = (char *)NULL;
int storage_size_percent = -1;
FILE *fp ;
fp = fopen ("disk.stat" , "r");
if (fp != (FILE *)NULL)
{
temp_char_ptr = (char*) calloc ( 6 , 1 );
fscanf( fp,"%s %s %s %s %d", temp_char_ptr, temp_char_ptr, temp_char_ptr, temp_char_ptr, &storage_size_percent);
}
free (temp_char_ptr);
fclose(fp);
return storage_size_percent;
}
return -1;
}
I would suggest it would be better to let the user specify which mounts should be considered in the total, or use a heuristic to omit system and temporary mounts.
Consider the following example, info.c
:
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <sys/statvfs.h>
#include <mntent.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
static void free_array(char **array)
{
if (array) {
size_t i;
for (i = 0; array[i] != NULL; i++) {
free(array[i]);
array[i] = NULL;
}
free(array);
}
}
static char **normal_mounts(void)
{
char **list = NULL, **temp;
size_t size = 0;
size_t used = 0;
char buffer[4096];
struct mntent entry;
FILE *mounts;
mounts = fopen("/proc/mounts", "r");
if (!mounts)
return NULL;
while (getmntent_r(mounts, &entry, buffer, sizeof buffer) == &entry)
if (strcmp(entry.mnt_fsname, "tmpfs") &&
strcmp(entry.mnt_fsname, "swap") &&
strcmp(entry.mnt_dir, "/proc") && strncmp(entry.mnt_dir, "/proc/", 6) &&
strcmp(entry.mnt_dir, "/boot") && strncmp(entry.mnt_dir, "/boot/", 6) &&
strcmp(entry.mnt_dir, "/sys") && strncmp(entry.mnt_dir, "/sys/", 5) &&
strcmp(entry.mnt_dir, "/run") && strncmp(entry.mnt_dir, "/run/", 5) &&
strcmp(entry.mnt_dir, "/dev") && strncmp(entry.mnt_dir, "/dev/", 5) &&
strcmp(entry.mnt_dir, "/mnt") && strncmp(entry.mnt_dir, "/mnt/", 5) &&
strcmp(entry.mnt_dir, "/media") && strncmp(entry.mnt_dir, "/media/", 7) &&
strcmp(entry.mnt_dir, "/var/run") && strncmp(entry.mnt_dir, "/var/run/", 9)) {
if (used >= size) {
size = (used | 15) + 17;
temp = realloc(list, size * sizeof list[0]);
if (!temp) {
endmntent(mounts);
free_array(list);
errno = ENOMEM;
return NULL;
}
list = temp;
}
if (!(list[used++] = strdup(entry.mnt_dir))) {
endmntent(mounts);
free_array(list);
errno = ENOMEM;
return NULL;
}
}
if (ferror(mounts) || !feof(mounts)) {
endmntent(mounts);
free_array(list);
errno = EIO;
return NULL;
} else
endmntent(mounts);
if (!used) {
free_array(list);
errno = 0;
return NULL;
}
if (size != used + 1) {
size = used + 1;
temp = realloc(list, size * sizeof list[0]);
if (!temp) {
free_array(list);
errno = ENOMEM;
return NULL;
}
list = temp;
}
list[used] = NULL;
errno = 0;
return list;
}
static int statistics(const char **mountpoint, uint64_t *bytes_total, uint64_t *bytes_free)
{
struct statvfs info;
uint64_t btotal = 0;
uint64_t bfree = 0;
size_t i;
if (!mountpoint)
return errno = EINVAL;
for (i = 0; mountpoint[i] != NULL; i++)
if (statvfs(mountpoint[i], &info) != -1) {
btotal += (uint64_t)info.f_frsize * (uint64_t)info.f_blocks;
bfree += (uint64_t)info.f_bsize * (uint64_t)info.f_bavail;
} else
return errno;
if (bytes_total)
*bytes_total = btotal;
if (bytes_free)
*bytes_free = bfree;
return 0;
}
int main(int argc, char *argv[])
{
uint64_t total = 0;
uint64_t nfree = 0;
if (argc > 1) {
if (statistics((const char **)argv + 1, &total, &nfree)) {
fprintf(stderr, "%s.\n", strerror(errno));
return EXIT_FAILURE;
}
} else {
char **mounts = normal_mounts();
size_t i;
if (!mounts) {
if (errno)
fprintf(stderr, "Error determining file systems: %s.\n", strerror(errno));
else
fprintf(stderr, "No normal file systems found.\n");
return EXIT_FAILURE;
}
fprintf(stderr, "Considering mount points");
for (i = 0; mounts[i] != NULL; i++)
fprintf(stderr, " %s", mounts[i]);
fprintf(stderr, "\n");
if (statistics((const char **)mounts, &total, &nfree)) {
fprintf(stderr, "%s.\n", strerror(errno));
return EXIT_FAILURE;
}
free_array(mounts);
}
printf("%20" PRIu64 " bytes total\n", total);
printf("%20" PRIu64 " bytes free\n", nfree);
return EXIT_SUCCESS;
}
The statistics()
function takes a NULL-terminated array of mount points, and two pointers to unsigned 64-bit integers. The function returns 0 if successful, and a nonzero errno code otherwise. If successful, the function will set the total number of bytes in the filesystems to the first integer, and the number of free bytes in the second.
If you supply one or more mounts points as command line arguments, only those are considered. (POSIX says argv[argc] == NULL
, so this usage is safe.)
Otherwise, the normal_mounts()
function is used to parse /proc/mounts
to obtain a list of "normal" mount points. The function uses getmntent()
to read each entry (line) from the kernel-provided pseudo-file. All tmpfs
(ramdisks) and swap
filesystems are excluded, as are those mounted at or under /proc
, /boot
, /sys
, /run
, /dev
, /mnt
, /media
, and /var/run
. This is just a crude heuristic, not a known good approach.
In a daemon, or even in a graphical application, you call only (your equivalent of) the statistics()
function, with the same array of mount points. You could even consider tracking each mount point separately, and let the user filter and combine the information they are interested in. In fact, I would recommend that: I personally might be interested in seeing the fluctuations in my temporary file usage (on machines where /tmp
and /var/tmp
are tmpfs mounts), as well as track my long-term usage of /home
.
In a daemon, you can use HUP
or USR1
or USR2
signals to indicate when the user wants you to reload the configuration -- the mount point list, here. I do not believe it would be that interesting to integrate it to DBUS for detecting removable media mounts/unmounts, but of course you can if you think it useful.
If you compile the above program using e.g.
gcc -Wall -O2 info.c -o info
and run
./info
it will output something like
Considering mount points / /home
119989497856 bytes total
26786156544 bytes free
where the first line is output to standard error, and the bytes lines to standard output. You can also specifically name the mount points -- make sure they are different, as the code does not check for duplicate mounts --:
./info /home /tmp
If you are wondering how you could determine whether two directories are on the same mount or not: call stat(path1, &info1)
on one, and stat(path2, &info2)
on the other. If and only if (info1.st_dev == info2.st_dev)
, the two paths are on the same mount. (One device may be mounted multiple times at different points, using e.g. bind mounts, but usually the above check suffices.)
If you find all the above code annoying, you can always rely on the df
utility. To ensure the output is in the C/POSIX locale (and not, say, in French or Finnish), use
handle = popen("LANG=C LC_ALL=C df -Pl", "r");
or similar, and read the output using len = getline(&line, &size, handle)
.
You can use popen()
instead of system()/fopen()
: The system will give you a readable file without using hard-drive.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With