1

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?

Community
  • 1
  • 1
blackbeard
  • 377
  • 1
  • 11
  • There would be more information in `/sys/class` about the device. This is really fun, let me try a few things and if I succeed I will answer. – Iharob Al Asimi Jan 31 '18 at 22:08
  • https://stackoverflow.com/questions/646241/c-run-a-system-command-and-get-output – pm100 Jan 31 '18 at 22:13
  • I just found that if you inspect `/sys/block` you might solve this. – Iharob Al Asimi Jan 31 '18 at 22:15
  • 2
    @IharobAlAsimi: I have inspected `/sys/block` and found a script called `size` which doesn't represent the total or available size of the SD card (has only 1 partition). There is another script called `stat` whose information is not human-readable (at least to me). – blackbeard Jan 31 '18 at 23:01
  • 2
    @pm100 `popen` and `pclose` might work! let me try. – blackbeard Jan 31 '18 at 23:03
  • @blackbeard Read the corresponding kernel documentation for each of the files, and it should become clear what every file and every value is. – Iharob Al Asimi Feb 01 '18 at 02:15

2 Answers2

1

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.

Iharob Al Asimi
  • 51,091
  • 5
  • 53
  • 91
1

I have used the statfs() system call to get the SD card capacity and usage. As for detecting card presence, I have used stat() call to look for the preferable directory at /mnt/sd_card_dir where sd_card_dir is set during auto-mount configuration. If it exists, the SD card is (most likely) present - because the sd_card_dir folder gets created automatically whenever the SD card is inserted. If you create partitions, they should appear as sub-directories of sd_card_dir.

Following libraries should be called:

#include <sys/stat.h>
#include <sys/vfs.h>

Following function handles the detection and it needs to be called periodically - at least once per minute. In my particular case, I have called this function every 10 sec as a low-priority task and attached to the software's main loop.

#define PATH "/mnt/sd_card_dir"
static int handler_sd_card_status(void)
{
    struct stat st;
    struct statfs fs;

    if (stat(PATH, &st))
        printf("Missing or unreadable\n");
    else {
        if (!statfs(PATH, &fs)) {
            char sd_stat[32];
            double scale = fs.f_bsize / 1e9;
            snprintf(sd_stat, sizeof(sd_stat),
                "%.2f of %.1f GB used",
                (fs.f_blocks - fs.f_bavail) * scale,
                fs.f_blocks * scale);
            printf("%s\n", sd_stat);
        } else
            printf("Size unreadable\n");
    }
    return 0;
}
blackbeard
  • 377
  • 1
  • 11