Search code examples
cembedded-linuxsd-cardmountdiskspace

How to pull mount and df information for SD card from linux?


I am running an embedded software (written in C) on Linux kernel. I need to create an alarm on my software that checks whether an SD card is mounted on, and also find out it's capacity. I can brainstorm three ideas:

  1. I am thinking to read and parse the file /proc/mounts

    and then, use regex and a bunch of string comparisons to check if any SD card present, or

  2. scan and check for preferable directory name under /mnt/ using direntlibrary.

    Either way, I am not sure how I am going to get the memory usage on SD card, or

  3. I could use my software to run a shell script containing df command, outputting into a file, and then read it. It should tell me whether an SD card is present, where it is mounted on, and it's capacity.

I am sure there are plenty of easy and straightforward ways to acquire this information and feed it into my software. Perhaps a nifty library call? I did some research and saw resources on fstab, <sys/mount.h>, don't think these will take me any further. Does anyone have any better ideas?


Solution

  • I know that you didn't ask for code but here you go (I really had fun writing it).

    #include <stdio.h>
    
    #include <dirent.h>
    #include <limits.h>
    
    #include <string.h>
    
    #include <unistd.h>
    #include <fcntl.h>
    
    #include <stdlib.h>
    #include <sys/stat.h>
    
    static int
    walkdir(const char *const path, int (*visit)(const char *const, const char *const), void *data)
    {
        struct dirent *entry;
        DIR *dir;
        dir = opendir(path);
        if (dir == NULL)
            return -1;
        while ((entry = readdir(dir)) != NULL) {
            int code;
            code = visit(path, entry->d_name);
            if (code != 0) {
                closedir(dir);
                return code;
            }
        }
        closedir(dir);
        return 0;
    }
    
    static char *
    file_get_content(const char *const path)
    {
        int file;
        struct stat st;
        char *text;
        size_t size;
    
        text = NULL;
        file = -1;
        if (stat(path, &st) == -1)
            goto error;
        size = st.st_size;
        text = malloc(size + 1);
        if (text == NULL)
            goto error; // file too large, cannot read like this
        file = open(path, O_RDONLY);
        if (file == -1)
            goto error;
        if ((size = read(file, text, size)) <= 0)
            goto error;
        text[size] = '\0';
        if (file != -1)
            close(file);
        return text;
    error:
        if (file != -1)
            close(file);
        free(text);
        return NULL;
    }
    
    static size_t
    get_size(const char *const dirpath, const char *const name)
    {
        char path[PATH_MAX];
        char *text;
        int length;
        size_t size;
    
        length = snprintf(path, sizeof(path), "%s/%s/size", dirpath, name);
        if (((size_t) length) > sizeof(path))
            return 0;
        size = 0;
        text = file_get_content(path);
        if (text != NULL) { 
            size = strtoll(text, NULL, 10);
            free(text);
        }
        return size;
    }
    
    static int
    display_block(const char *const dirpath, const char *const name)
    {
        const char *block;
        block = strrchr(dirpath, '/');
        if (block == NULL)
            return -1;
        block += 1;
    
        if (strstr(name, block) == name) {
            size_t size;
            // get_ the parition size
            //
            // Note, I had to divice the size by 2 because it didn't
            // match the sizes reported by `df'.
            //
            // Also, it appears that it's in megabytes 
            // (i.e. twice the size in MB)
            size = get_size(dirpath, name) / (1 << 21);
            // Display the result
            fprintf(stdout, "\tpartition: %s (%zu GB)\n", name, size);
        }
    
        return 0;
    }
    
    static char *
    get_vendor(const char *const name)
    {
        char path[PATH_MAX];
        int length;
        char *value;
        // get_ partitions
        length = snprintf(path, sizeof(path), "/sys/block/%s/device/vendor", name);
        if (((size_t) length) > sizeof(path))
            return NULL;
        value = file_get_content(path);
        if (value == NULL)
            return NULL;        
        // Make the trailing `\n' a '\0' instead
        strtok(value, "\n");
        return value;
    }
    
    static char *
    get_model(const char *const name)
    {
        char path[PATH_MAX];
        int length;
        char *value;
        // get_ partitions
        length = snprintf(path, sizeof(path), "/sys/block/%s/device/model", name);
        if (((size_t) length) > sizeof(path))
            return NULL;
        value = file_get_content(path);
        if (value == NULL)
            return NULL;        
        // Make the trailing `\n' a '\0' instead
        strtok(value, "\n");
        return value;
    }
    
    static int
    parse_block(const char *const name)
    {
        char path[PATH_MAX];
        int length;
        char *vendor;
        char *model;
        // get_ partitions
        length = snprintf(path, sizeof(path), "/sys/block/%s", name);
        if (((size_t) length) > sizeof(path))
            return -1;      
        vendor = get_vendor(name);
        model = get_model(name);    
        if ((model != NULL) && (vendor != NULL)) {
            fprintf(stdout, "block device: %s (%s %s)\n", name, vendor, model);
            walkdir(path, display_block, NULL);
        }
        free(vendor);
        free(model);
        return 0;
    }
    
    static int
    list_devices(const char *const dirpath, const char *const name)
    {
        if (*name == '.')
            return 0;
        parse_block(name);
        return 0;
    }
    
    int
    main(void)
    {
        return walkdir("/sys/block", list_devices, NULL);
    }
    

    This will show you the devices and some information about them, also the size that you are very interested in.

    Note, that there is no need for them to be mounted.

    You can of course, find more information in other places. Just check the appropriate directories and you will be able to get everything. For instance, there is a file removable that tells you whether the device is removable, which I think will be very useful in your case.