udhcpc: code shrink
[oweals/busybox.git] / procps / free.c
index e8bea50642897add644c31d5ff950eebf633838f..e41601e08ee5bc64a67b9e091f5b2b80906f525d 100644 (file)
  *
  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
- * Licensed under the GPL version 2, see the file LICENSE in this tarball.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
+//config:config FREE
+//config:      bool "free (2.4 kb)"
+//config:      default y
+//config:      select PLATFORM_LINUX #sysinfo()
+//config:      help
+//config:      free displays the total amount of free and used physical and swap
+//config:      memory in the system, as well as the buffers used by the kernel.
+//config:      The shared memory column should be ignored; it is obsolete.
 
-/* getopt not needed */
+//applet:IF_FREE(APPLET_NOFORK(free, free, BB_DIR_USR_BIN, BB_SUID_DROP, free))
+
+//kbuild:lib-$(CONFIG_FREE) += free.o
+
+//usage:#define free_trivial_usage
+//usage:       "" IF_DESKTOP("[-b/k/m/g]")
+//usage:#define free_full_usage "\n\n"
+//usage:       "Display the amount of free and used system memory"
+//usage:
+//usage:#define free_example_usage
+//usage:       "$ free\n"
+//usage:       "              total         used         free       shared      buffers\n"
+//usage:       "  Mem:       257628       248724         8904        59644        93124\n"
+//usage:       " Swap:       128516         8404       120112\n"
+//usage:       "Total:       386144       257128       129016\n"
 
 #include "libbb.h"
+#ifdef __linux__
+# include <sys/sysinfo.h>
+#endif
+
+struct globals {
+       unsigned mem_unit;
+#if ENABLE_DESKTOP
+       uint8_t unit_steps;
+# define G_unit_steps g->unit_steps
+#else
+# define G_unit_steps 10
+#endif
+};
+/* Because of NOFORK, "globals" are not in global data */
+
+static unsigned long long scale(struct globals *g, unsigned long d)
+{
+       return ((unsigned long long)d * g->mem_unit) >> G_unit_steps;
+}
+
+/* NOINLINE reduces main() stack usage, which makes code smaller (on x86 at least) */
+static NOINLINE unsigned int parse_meminfo(unsigned long *cached_kb, unsigned long *available_kb)
+{
+       char buf[60]; /* actual lines we expect are ~30 chars or less */
+       FILE *fp;
+       int seen_cached_and_available;
+
+       fp = xfopen_for_read("/proc/meminfo");
+       *cached_kb = *available_kb = 0;
+       seen_cached_and_available = 2;
+       while (fgets(buf, sizeof(buf), fp)) {
+               if (sscanf(buf, "Cached: %lu %*s\n", cached_kb) == 1)
+                       if (--seen_cached_and_available == 0)
+                               break;
+               if (sscanf(buf, "MemAvailable: %lu %*s\n", available_kb) == 1)
+                       if (--seen_cached_and_available == 0)
+                               break;
+       }
+       /* Have to close because of NOFORK */
+       fclose(fp);
+
+       return seen_cached_and_available == 0;
+}
 
 int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int free_main(int argc UNUSED_PARAM, char **argv)
+int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
 {
+       struct globals G;
        struct sysinfo info;
-       sysinfo(&info);
+       unsigned long long cached, cached_plus_free, available;
+       unsigned long cached_kb, available_kb;
+       int seen_available;
 
-       /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */
-       if (info.mem_unit == 0) {
-               info.mem_unit=1;
+#if ENABLE_DESKTOP
+       G.unit_steps = 10;
+       if (argv[1] && argv[1][0] == '-') {
+               switch (argv[1][1]) {
+               case 'b':
+                       G.unit_steps = 0;
+                       break;
+               case 'k': /* 2^10 */
+                       /* G.unit_steps = 10; - already is */
+                       break;
+               case 'm': /* 2^(2*10) */
+                       G.unit_steps = 20;
+                       break;
+               case 'g': /* 2^(3*10) */
+                       G.unit_steps = 30;
+                       break;
+               default:
+                       bb_show_usage();
+               }
        }
-       if (info.mem_unit == 1) {
-               info.mem_unit=1024;
-
-               /* TODO:  Make all this stuff not overflow when mem >= 4 Gib */
-               info.totalram/=info.mem_unit;
-               info.freeram/=info.mem_unit;
-#if BB_MMU
-               info.totalswap/=info.mem_unit;
-               info.freeswap/=info.mem_unit;
-#endif
-               info.sharedram/=info.mem_unit;
-               info.bufferram/=info.mem_unit;
-       } else {
-               info.mem_unit/=1024;
-               /* TODO:  Make all this stuff not overflow when mem >= 4 Gib */
-               info.totalram*=info.mem_unit;
-               info.freeram*=info.mem_unit;
-#if BB_MMU
-               info.totalswap*=info.mem_unit;
-               info.freeswap*=info.mem_unit;
 #endif
-               info.sharedram*=info.mem_unit;
-               info.bufferram*=info.mem_unit;
-       }
-
-       if (argv[1] && argv[1][0] == '-')
-               bb_show_usage();
+       printf("       %12s%12s%12s%12s%12s%12s\n"
+       "Mem:   ",
+               "total",
+               "used",
+               "free",
+               "shared", "buff/cache", "available" /* swap and total don't have these columns */
+       );
 
-       printf("%6s%13s%13s%13s%13s%13s\n", "", "total", "used", "free",
-                       "shared", "buffers");
+       sysinfo(&info);
+       /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */
+       G.mem_unit = (info.mem_unit ? info.mem_unit : 1);
+       /* Extract cached and memavailable from /proc/meminfo and convert to mem_units */
+       seen_available = parse_meminfo(&cached_kb, &available_kb);
+       available = ((unsigned long long) available_kb * 1024) / G.mem_unit;
+       cached = ((unsigned long long) cached_kb * 1024) / G.mem_unit;
+       cached += info.bufferram;
+       cached_plus_free = cached + info.freeram;
 
-       printf("%6s%13ld%13ld%13ld%13ld%13ld\n", "Mem:", info.totalram,
-                       info.totalram-info.freeram, info.freeram,
-                       info.sharedram, info.bufferram);
+#define FIELDS_6 "%12llu%12llu%12llu%12llu%12llu%12llu\n"
+#define FIELDS_3 (FIELDS_6 + 3*6)
+#define FIELDS_2 (FIELDS_6 + 4*6)
 
+       printf(FIELDS_6,
+               scale(&G, info.totalram),                //total
+               scale(&G, info.totalram - cached_plus_free), //used
+               scale(&G, info.freeram),                 //free
+               scale(&G, info.sharedram),               //shared
+               scale(&G, cached),                       //buff/cache
+               scale(&G, available)                     //available
+       );
+       /* On kernels < 3.14, MemAvailable is not provided.
+        * Show alternate, more meaningful busy/free numbers by counting
+        * buffer cache as free memory. */
+       if (!seen_available) {
+               printf("-/+ buffers/cache: ");
+               printf(FIELDS_2,
+                       scale(&G, info.totalram - cached_plus_free), //used
+                       scale(&G, cached_plus_free)                  //free
+               );
+       }
 #if BB_MMU
-       printf("%6s%13ld%13ld%13ld\n", "Swap:", info.totalswap,
-                       info.totalswap-info.freeswap, info.freeswap);
-
-       printf("%6s%13ld%13ld%13ld\n", "Total:", info.totalram+info.totalswap,
-                       (info.totalram-info.freeram)+(info.totalswap-info.freeswap),
-                       info.freeram+info.freeswap);
+       printf("Swap:  ");
+       printf(FIELDS_3,
+               scale(&G, info.totalswap),                 //total
+               scale(&G, info.totalswap - info.freeswap), //used
+               scale(&G, info.freeswap)                   //free
+       );
 #endif
        return EXIT_SUCCESS;
 }