aboutsummaryrefslogblamecommitdiff
path: root/src/print_mem.c
blob: f9284f35c6c92fa6724f91d653136dc99fe94fc5 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13











                                                                                      
                                                       















                                                                     





                                                                     
   
                                                                     
                        
                                                  




                                                       
                     
                                            
                     
                                            
                     
                                            
                     


                                                              
                      
                      
         
     
                        
 
                                                                                                                                                                                                     



                                         
                                    
 





                            
 
                                              




                                             
                                                                     
                                            
                                                                   
                                                 
                                                                             
                                            
                                                                      
                                           
                                                                    
                                          
                                                                   
         
                                                                                                                                    



                  
                                                                                                                                

                   




                                                
                                                          
                                             
                                                              
                                                                   
     









                                                                  

         





                                              

                                                         
                                                    
                                                             
                                    
 
                                                   
                                                            
                                   
 
                                                   
                                                            
                                   
 
                                                        
                                                                 
                                        
 
                                                     
                                                              
                                     
 
                                                              
                                                                                           
                                              
 
                                                                   
                                                                                                
                                                   
 
                                                              
                                                                                           
                                              
 
                                                                
                                                                                             
                                                

                               

         
                     








                                                                               
                         

                                                                                           
// vim:ts=4:sw=4:expandtab
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <yajl/yajl_gen.h>
#include <yajl/yajl_version.h>
#include "i3status.h"

#define BINARY_BASE UINT64_C(1024)

#define MAX_EXPONENT 4
static const char *const iec_symbols[MAX_EXPONENT + 1] = {"", "Ki", "Mi", "Gi", "Ti"};

static const char memoryfile_linux[] = "/proc/meminfo";

/*
 * Prints the given amount of bytes in a human readable manner.
 *
 */
static int print_bytes_human(char *outwalk, uint64_t bytes) {
    double size = bytes;
    int exponent = 0;
    int bin_base = BINARY_BASE;
    while (size >= bin_base && exponent < MAX_EXPONENT) {
        size /= bin_base;
        exponent += 1;
    }
    return sprintf(outwalk, "%.1f %sB", size, iec_symbols[exponent]);
}

/*
 * Convert a string to its absolute representation based on the total
 * memory of `mem_total`.
 *
 * The string can contain any percentage values, which then return a
 * the value of `size` in relation to `mem_total`.
 * Alternatively an absolute value can be given, suffixed with an iec
 * symbol.
 *
 */
static long memory_absolute(const long mem_total, const char *size) {
    char *endptr = NULL;

    long mem_absolute = strtol(size, &endptr, 10);

    if (endptr) {
        while (endptr[0] != '\0' && isspace(endptr[0]))
            endptr++;

        switch (endptr[0]) {
            case 'T':
            case 't':
                mem_absolute *= BINARY_BASE;
            case 'G':
            case 'g':
                mem_absolute *= BINARY_BASE;
            case 'M':
            case 'm':
                mem_absolute *= BINARY_BASE;
            case 'K':
            case 'k':
                mem_absolute *= BINARY_BASE;
                break;
            case '%':
                mem_absolute = mem_total * mem_absolute / 100;
                break;
            default:
                break;
        }
    }

    return mem_absolute;
}

void print_memory(yajl_gen json_gen, char *buffer, const char *format, const char *format_degraded, const char *threshold_degraded, const char *threshold_critical, const char *memory_used_method) {
    char *outwalk = buffer;

#if defined(linux)
    const char *selected_format = format;
    const char *walk;
    const char *output_color = NULL;

    long ram_total = -1;
    long ram_free = -1;
    long ram_available = -1;
    long ram_used = -1;
    long ram_shared = -1;
    long ram_cached = -1;
    long ram_buffers = -1;

    FILE *file = fopen(memoryfile_linux, "r");
    if (!file) {
        goto error;
    }
    char line[128];
    while (fgets(line, sizeof line, file)) {
        if (BEGINS_WITH(line, "MemTotal:")) {
            ram_total = strtol(line + strlen("MemTotal:"), NULL, 10);
        }
        if (BEGINS_WITH(line, "MemFree:")) {
            ram_free = strtol(line + strlen("MemFree:"), NULL, 10);
        }
        if (BEGINS_WITH(line, "MemAvailable:")) {
            ram_available = strtol(line + strlen("MemAvailable:"), NULL, 10);
        }
        if (BEGINS_WITH(line, "Buffers:")) {
            ram_buffers = strtol(line + strlen("Buffers:"), NULL, 10);
        }
        if (BEGINS_WITH(line, "Cached:")) {
            ram_cached = strtol(line + strlen("Cached:"), NULL, 10);
        }
        if (BEGINS_WITH(line, "Shmem:")) {
            ram_shared = strtol(line + strlen("Shmem:"), NULL, 10);
        }
        if (ram_total != -1 && ram_free != -1 && ram_available != -1 && ram_buffers != -1 && ram_cached != -1 && ram_shared != -1) {
            break;
        }
    }
    fclose(file);

    if (ram_total == -1 || ram_free == -1 || ram_available == -1 || ram_buffers == -1 || ram_cached == -1 || ram_shared == -1) {
        goto error;
    }

    ram_total = ram_total * BINARY_BASE;
    ram_free = ram_free * BINARY_BASE;
    ram_available = ram_available * BINARY_BASE;
    ram_buffers = ram_buffers * BINARY_BASE;
    ram_cached = ram_cached * BINARY_BASE;
    ram_shared = ram_shared * BINARY_BASE;

    if (BEGINS_WITH(memory_used_method, "memavailable")) {
        ram_used = ram_total - ram_available;
    } else if (BEGINS_WITH(memory_used_method, "classical")) {
        ram_used = ram_total - ram_free - ram_buffers - ram_cached;
    }

    if (threshold_degraded) {
        long abs = memory_absolute(ram_total, threshold_degraded);
        if (ram_available < abs) {
            output_color = "color_degraded";
        }
    }

    if (threshold_critical) {
        long abs = memory_absolute(ram_total, threshold_critical);
        if (ram_available < abs) {
            output_color = "color_bad";
        }
    }

    if (output_color) {
        START_COLOR(output_color);

        if (format_degraded)
            selected_format = format_degraded;
    }

    for (walk = selected_format; *walk != '\0'; walk++) {
        if (*walk != '%') {
            *(outwalk++) = *walk;

        } else if (BEGINS_WITH(walk + 1, "total")) {
            outwalk += print_bytes_human(outwalk, ram_total);
            walk += strlen("total");

        } else if (BEGINS_WITH(walk + 1, "used")) {
            outwalk += print_bytes_human(outwalk, ram_used);
            walk += strlen("used");

        } else if (BEGINS_WITH(walk + 1, "free")) {
            outwalk += print_bytes_human(outwalk, ram_free);
            walk += strlen("free");

        } else if (BEGINS_WITH(walk + 1, "available")) {
            outwalk += print_bytes_human(outwalk, ram_available);
            walk += strlen("available");

        } else if (BEGINS_WITH(walk + 1, "shared")) {
            outwalk += print_bytes_human(outwalk, ram_shared);
            walk += strlen("shared");

        } else if (BEGINS_WITH(walk + 1, "percentage_free")) {
            outwalk += sprintf(outwalk, "%.01f%s", 100.0 * ram_free / ram_total, pct_mark);
            walk += strlen("percentage_free");

        } else if (BEGINS_WITH(walk + 1, "percentage_available")) {
            outwalk += sprintf(outwalk, "%.01f%s", 100.0 * ram_available / ram_total, pct_mark);
            walk += strlen("percentage_available");

        } else if (BEGINS_WITH(walk + 1, "percentage_used")) {
            outwalk += sprintf(outwalk, "%.01f%s", 100.0 * ram_used / ram_total, pct_mark);
            walk += strlen("percentage_used");

        } else if (BEGINS_WITH(walk + 1, "percentage_shared")) {
            outwalk += sprintf(outwalk, "%.01f%s", 100.0 * ram_shared / ram_total, pct_mark);
            walk += strlen("percentage_shared");

        } else {
            *(outwalk++) = '%';
        }
    }

    if (output_color)
        END_COLOR;

    *outwalk = '\0';
    OUTPUT_FULL_TEXT(buffer);

    return;
error:
    OUTPUT_FULL_TEXT("can't read memory");
    fputs("i3status: Cannot read system memory using /proc/meminfo\n", stderr);
#else
    OUTPUT_FULL_TEXT("");
    fputs("i3status: Memory status information is not supported on this system\n", stderr);
#endif
}