/* * free.c - free(1) * procps-ng 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 "config.h" #include "proc/sysinfo.h" #include "proc/version.h" #include "c.h" #include "nls.h" #include "strutils.h" #include #include #include #include #include #include #include #include #ifndef SIZE_MAX #define SIZE_MAX 32 #endif #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) struct commandline_arguments { int exponent; /* demanded in kilos, magas... */ float repeat_interval; /* delay in seconds */ int repeat_counter; /* number of repeats */ }; /* 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); static void __attribute__ ((__noreturn__)) usage(FILE * out) { fputs(USAGE_HEADER, out); fprintf(out, _(" %s [options]\n"), program_invocation_short_name); fputs(USAGE_OPTIONS, out); fputs(_(" -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"), out); fputs(USAGE_SEPARATOR, out); fputs(_(" --help display this help text\n"), out); fputs(USAGE_VERSION, out); fprintf(out, USAGE_MAN_TAIL("free(1)")); 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[BUFSIZ]; 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), "%lld", (long 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; /* * 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; args.repeat_counter = 0; program_invocation_name = program_invocation_short_name; setlocale (LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); 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)) xerrx(EXIT_FAILURE, _("seconds argument `%s' failed"), optarg); if (args.repeat_interval < 1) xerrx(EXIT_FAILURE, _("seconds argument `%s' is not positive number"), optarg); break; case 'c': flags |= FREE_REPEAT; flags |= FREE_REPEATCOUNT; args.repeat_counter = strtol_or_err(optarg, _("failed to parse count argument")); if (args.repeat_counter < 1) error(EXIT_FAILURE, ERANGE, _("failed to parse count argument: '%s'"), optarg); break; case HELP_OPTION: usage(stdout); case 'V': printf(PROCPS_NG_VERSION); exit(EXIT_SUCCESS); default: usage(stderr); } do { meminfo(); /* Translation Hint: You can use 9 character words in * the header, and the words need to be right align to * beginning of a number. */ printf("%s\n", _(" total used free shared buffers cached")); 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); }