From d13fbc34a7a198fb507f04e823ea80f8866b6131 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Mon, 18 Apr 2011 19:30:20 +0200 Subject: [PATCH] free: great modernization Support long options, use program_invocation_short_name, print version up on request, new giga & tera byte sizes switches, exit when numeric arguments has garbage... and for rest see the diff. Signed-off-by: Sami Kerola --- free.c | 426 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 317 insertions(+), 109 deletions(-) diff --git a/free.c b/free.c index bd78f028..e485f196 100644 --- a/free.c +++ b/free.c @@ -1,122 +1,330 @@ -// free.c - free(1) -// procps utility to display free memory information -// -// All new, Robert Love 18 Nov 2002 -// Original by Brian Edmonds and Rafal Maszkowski 14 Dec 1992 -// -// This program is licensed under the GNU Library General Public License, v2 -// -// Copyright 2003 Robert Love -// Copyright 2004 Albert Cahalan - +/* + * free.c - free(1) + * procps utility to display free memory information + * + * Mostly new, Sami Kerola 15 Apr 2011 + * All new, Robert Love 18 Nov 2002 + * Original by Brian Edmonds and Rafal Maszkowski 14 Dec 1992 + * + * This program is licensed under the GNU Library General Public License, v2 + * + * Copyright 2003 Robert Love + * Copyright 2004 Albert Cahalan + */ #include "proc/sysinfo.h" #include "proc/version.h" -//#include -#include +#include +#include +#include +#include #include #include #include -#include #include -#define S(X) ( ((unsigned long long)(X) << 10) >> shift) +#ifndef SIZE_MAX +#define SIZE_MAX 32 +#endif -const char help_message[] = -"usage: free [-b|-k|-m|-g] [-l] [-o] [-t] [-s delay] [-c count] [-V]\n" -" -b,-k,-m,-g show output in bytes, KB, MB, or GB\n" -" -l show detailed low and high memory statistics\n" -" -o use old format (no -/+buffers/cache line)\n" -" -t display total for RAM + swap\n" -" -s update every [delay] seconds\n" -" -c update [count] times\n" -" -V display version information and exit\n" -; +#define FREE_HUMANREADABLE (1 << 1) +#define FREE_LOHI (1 << 2) +#define FREE_OLDFMT (1 << 3) +#define FREE_TOTAL (1 << 4) +#define FREE_SI (1 << 5) +#define FREE_REPEAT (1 << 6) +#define FREE_REPEATCOUNT (1 << 7) -int main(int argc, char *argv[]){ - int i; - int count = 0; - int shift = 10; - int pause_length = 0; - int show_high = 0; - int show_total = 0; - int old_fmt = 0; +struct commandline_arguments { + int exponent; /* demanded in kilos, magas... */ + float repeat_interval; /* delay in seconds */ + int repeat_counter; /* number of repeats */ +}; - /* check startup flags */ - while( (i = getopt(argc, argv, "bkmglotc:s:V") ) != -1 ) - switch (i) { - case 'b': shift = 0; break; - case 'k': shift = 10; break; - case 'm': shift = 20; break; - case 'g': shift = 30; break; - case 'l': show_high = 1; break; - case 'o': old_fmt = 1; break; - case 't': show_total = 1; break; - case 's': pause_length = 1000000 * atof(optarg); break; - case 'c': count = strtoul(optarg, NULL, 10); break; - case 'V': display_version(); exit(0); - default: - fwrite(help_message,1,strlen(help_message),stderr); - return 1; - } +/* function prototypes */ +static void usage(FILE * out); +double power(unsigned int base, unsigned int expo); +static const char *scale_size(unsigned long size, int flags, struct commandline_arguments args); - do { - meminfo(); - printf(" total used free shared buffers cached\n"); - printf( - "%-7s %10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n", "Mem:", - S(kb_main_total), - S(kb_main_used), - S(kb_main_free), - S(kb_main_shared), - S(kb_main_buffers), - S(kb_main_cached) - ); - // Print low vs. high information, if the user requested it. - // Note we check if low_total==0: if so, then this kernel does - // not export the low and high stats. Note we still want to - // print the high info, even if it is zero. - if (show_high) { - printf( - "%-7s %10Lu %10Lu %10Lu\n", "Low:", - S(kb_low_total), - S(kb_low_total - kb_low_free), - S(kb_low_free) - ); - printf( - "%-7s %10Lu %10Lu %10Lu\n", "High:", - S(kb_high_total), - S(kb_high_total - kb_high_free), - S(kb_high_free) - ); - } - if(!old_fmt){ - unsigned KLONG buffers_plus_cached = kb_main_buffers + kb_main_cached; - printf( - "-/+ buffers/cache: %10Lu %10Lu\n", - S(kb_main_used - buffers_plus_cached), - S(kb_main_free + buffers_plus_cached) - ); - } - printf( - "%-7s %10Lu %10Lu %10Lu\n", "Swap:", - S(kb_swap_total), - S(kb_swap_used), - S(kb_swap_free) - ); - if(show_total){ - printf( - "%-7s %10Lu %10Lu %10Lu\n", "Total:", - S(kb_main_total + kb_swap_total), - S(kb_main_used + kb_swap_used), - S(kb_main_free + kb_swap_free) - ); - } - if(pause_length){ - fputc('\n', stdout); - fflush(stdout); - if (count != 1) usleep(pause_length); - } - } while(pause_length && --count); +static void __attribute__ ((__noreturn__)) + usage(FILE * out) +{ + fprintf(out, "\nUsage: %s [options]\n" "\nOptions:\n", program_invocation_short_name); + fprintf(out, + " -b, --bytes show output in bytes\n" + " -k, --kilo show output in kilobytes\n" + " -m, --mega show output in megabytes\n" + " -g, --giga show output in gigabytes\n" + " --tera show output in terabytes\n" + " -h, --human show human readable output\n" + " --si use powers of 1000 not 1024\n" + " -l, --lohi show detailed low and high memory statistics\n" + " -o, --old use old format (no -/+buffers/cache line)\n" + " -t, --total show total for RAM + swap\n" + " -s N, --seconds N repeat printing every N seconds\n" + " -c N, --count N repeat printing N times\n"); + fprintf(out, + " --help display this help text\n" + " -V, --version display version information and exit\n"); + fprintf(out, "\nFor more information see free(1).\n"); - return 0; + exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS); +} + +double power(unsigned int base, unsigned int expo) +{ + return (expo == 0) ? 1 : base * power(base, expo - 1); +} + +/* idea of this function is copied from top size scaling */ +static const char *scale_size(unsigned long size, int flags, struct commandline_arguments args) +{ + static char nextup[] = { 'B', 'K', 'M', 'G', 'T', 0 }; + static char buf[SIZE_MAX]; + int i; + char *up; + float base; + + if (flags & FREE_SI) + base = 1000.0; + else + base = 1024.0; + + /* default output */ + if (args.exponent == 0 && !(flags & FREE_HUMANREADABLE)) { + snprintf(buf, sizeof(buf), "%ld", size); + return buf; + } + + if (!(flags & FREE_HUMANREADABLE)) { + if (args.exponent == 1) { + /* in bytes, which can not be in SI */ + snprintf(buf, sizeof(buf), "%ld", (long int)(size * 1024)); + return buf; + } + if (args.exponent == 2) { + if (!(flags & FREE_SI)) + snprintf(buf, sizeof(buf), "%ld", size); + else + snprintf(buf, sizeof(buf), "%ld", (long int)(size / 0.9765625)); + return buf; + } + if (args.exponent > 2) { + /* In desired scale. */ + snprintf(buf, sizeof(buf), "%ld", + (long int)(size / power(base, args.exponent - 2)) + ); + return buf; + } + } + + /* human readable output */ + up = nextup; + for (i = 1; up[0] != '0'; i++, up++) { + switch (i) { + case 1: + if (4 >= snprintf(buf, sizeof(buf), "%ld%c", (long)size * 1024, *up)) + return buf; + break; + case 2: + + if (!(flags & FREE_SI)) { + if (4 >= snprintf(buf, sizeof(buf), "%ld%c", size, *up)) + return buf; + } else { + if (4 >= + snprintf(buf, sizeof(buf), "%ld%c", + (long)(size / 0.9765625), *up)) + return buf; + } + break; + case 3: + case 4: + case 5: + if (4 >= + snprintf(buf, sizeof(buf), "%.1f%c", + (float)(size / power(base, i - 2)), *up)) + return buf; + if (4 >= + snprintf(buf, sizeof(buf), "%ld%c", + (long)(size / power(base, i - 2)), *up)) + return buf; + break; + case 6: + break; + } + } + /* + * On system where there is more than petabyte of memory or swap the + * output does not fit to column. For incoming few years this should + * not be a big problem (wrote at Apr, 2011). + */ + return buf; +} + +int main(int argc, char **argv) +{ + int c, flags = 0; + char *endptr; + struct commandline_arguments args; + args.repeat_counter = 0; + + /* + * For long options that have no equivalent short option, use a + * non-character as a pseudo short option, starting with CHAR_MAX + 1. + */ + enum { + SI_OPTION = CHAR_MAX + 1, + TERA_OPTION, + HELP_OPTION + }; + + static const struct option longopts[] = { + { "bytes", no_argument, NULL, 'b' }, + { "kilo", no_argument, NULL, 'k' }, + { "mega", no_argument, NULL, 'm' }, + { "giga", no_argument, NULL, 'g' }, + { "tera", no_argument, NULL, TERA_OPTION }, + { "human", no_argument, NULL, 'h' }, + { "si", no_argument, NULL, SI_OPTION }, + { "lohi", no_argument, NULL, 'l' }, + { "old", no_argument, NULL, 'o' }, + { "total", no_argument, NULL, 't' }, + { "seconds", required_argument, NULL, 's' }, + { "count", required_argument, NULL, 'c' }, + { "help", no_argument, NULL, HELP_OPTION }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + /* defaults to old format */ + args.exponent = 0; + args.repeat_interval = 1000000; + + while ((c = getopt_long(argc, argv, "bkmghlotc:s:V", longopts, NULL)) != -1) + switch (c) { + case 'b': + args.exponent = 1; + break; + case 'k': + args.exponent = 2; + break; + case 'm': + args.exponent = 3; + break; + case 'g': + args.exponent = 4; + break; + case TERA_OPTION: + args.exponent = 5; + break; + case 'h': + flags |= FREE_HUMANREADABLE; + break; + case SI_OPTION: + flags |= FREE_SI; + break; + case 'l': + flags |= FREE_LOHI; + break; + case 'o': + flags |= FREE_OLDFMT; + break; + case 't': + flags |= FREE_TOTAL; + break; + case 's': + flags |= FREE_REPEAT; + args.repeat_interval = (1000000 * strtof(optarg, &endptr)); + if (errno || optarg == endptr || (endptr && *endptr)) + errx(EXIT_FAILURE, "seconds argument `%s' failed", optarg); + if (args.repeat_interval < 1) + errx(EXIT_FAILURE, + "seconds argument `%s' is not positive number", optarg); + break; + case 'c': + flags |= FREE_REPEAT; + flags |= FREE_REPEATCOUNT; + args.repeat_counter = strtoul(optarg, &endptr, 10); + if (errno || optarg == endptr || (endptr && *endptr)) + errx(EXIT_FAILURE, "count argument `%s' failed", optarg); + + break; + case HELP_OPTION: + usage(stdout); + case 'V': + display_version(); + exit(EXIT_SUCCESS); + default: + usage(stderr); + } + + do { + + meminfo(); + + printf + (" total used free shared buffers cached\n"); + printf("%-7s", "Mem:"); + printf(" %10s", scale_size(kb_main_total, flags, args)); + printf(" %10s", scale_size(kb_main_used, flags, args)); + printf(" %10s", scale_size(kb_main_free, flags, args)); + printf(" %10s", scale_size(kb_main_shared, flags, args)); + printf(" %10s", scale_size(kb_main_buffers, flags, args)); + printf(" %10s", scale_size(kb_main_cached, flags, args)); + printf("\n"); + /* + * Print low vs. high information, if the user requested it. + * Note we check if low_total == 0: if so, then this kernel + * does not export the low and high stats. Note we still want + * to print the high info, even if it is zero. + */ + if (flags & FREE_LOHI) { + printf("%-7s", "Low:"); + printf(" %10s", scale_size(kb_low_total, flags, args)); + printf(" %10s", scale_size(kb_low_total - kb_low_free, flags, args)); + printf(" %10s", scale_size(kb_low_free, flags, args)); + printf("\n"); + + printf("%-7s", "High:"); + printf(" %10s", scale_size(kb_high_total, flags, args)); + printf(" %10s", scale_size(kb_high_total - kb_high_free, flags, args)); + printf(" %10s", scale_size(kb_high_free, flags, args)); + printf("\n"); + } + + if (!(flags & FREE_OLDFMT)) { + unsigned KLONG buffers_plus_cached = kb_main_buffers + kb_main_cached; + printf("-/+ buffers/cache:"); + printf(" %10s", + scale_size(kb_main_used - buffers_plus_cached, flags, args)); + printf(" %10s", + scale_size(kb_main_free + buffers_plus_cached, flags, args)); + printf("\n"); + } + printf("%-7s", "Swap:"); + printf(" %10s", scale_size(kb_swap_total, flags, args)); + printf(" %10s", scale_size(kb_swap_used, flags, args)); + printf(" %10s", scale_size(kb_swap_free, flags, args)); + printf("\n"); + + if (flags & FREE_TOTAL) { + printf("%-7s", "Total:"); + printf(" %10s", scale_size(kb_main_total + kb_swap_total, flags, args)); + printf(" %10s", scale_size(kb_main_used + kb_swap_used, flags, args)); + printf(" %10s", scale_size(kb_main_free + kb_swap_free, flags, args)); + printf("\n"); + } + fflush(stdout); + if (flags & FREE_REPEATCOUNT) { + args.repeat_counter--; + if (args.repeat_counter < 1) + exit(EXIT_SUCCESS); + } + if (flags & FREE_REPEAT) { + printf("\n"); + usleep(args.repeat_interval); + } + } while ((flags & FREE_REPEAT)); + + exit(EXIT_SUCCESS); }