udhcpc: code shrink
[oweals/busybox.git] / procps / free.c
index d287c03f565743c660a4bd8170292c58dd502566..e41601e08ee5bc64a67b9e091f5b2b80906f525d 100644 (file)
 /*
  * Mini free implementation for busybox
  *
- * Copyright (C) 1999,2000,2001 by Lineo, inc.
- * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  *
+ * 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.
+
+//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 "busybox.h"
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
+#include "libbb.h"
+#ifdef __linux__
+# include <sys/sysinfo.h>
+#endif
 
-extern int free_main(int argc, char **argv)
+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)
 {
-       struct sysinfo info;
-       sysinfo(&info);
+       return ((unsigned long long)d * g->mem_unit) >> G_unit_steps;
+}
 
-       /* Kernels prior to 2.4.x will return info.mem_unit==0, so cope... */
-       if (info.mem_unit==0) {
-               info.mem_unit=1;
+/* 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;
        }
-       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;
-       info.totalswap/=info.mem_unit;
-       info.freeswap/=info.mem_unit;
-       info.sharedram/=info.mem_unit;
-       info.bufferram/=info.mem_unit;
+       /* Have to close because of NOFORK */
+       fclose(fp);
 
-       if (argc > 1 && **(argv + 1) == '-')
-               show_usage();
+       return seen_cached_and_available == 0;
+}
 
-       printf("%6s%13s%13s%13s%13s%13s\n", "", "total", "used", "free", 
-                       "shared", "buffers");
+int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
+{
+       struct globals G;
+       struct sysinfo info;
+       unsigned long long cached, cached_plus_free, available;
+       unsigned long cached_kb, available_kb;
+       int seen_available;
 
-       printf("%6s%13ld%13ld%13ld%13ld%13ld\n", "Mem:", info.totalram, 
-                       info.totalram-info.freeram, info.freeram, 
-                       info.sharedram, info.bufferram);
+#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();
+               }
+       }
+#endif
+       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%13ld%13ld%13ld\n", "Swap:", info.totalswap,
-                       info.totalswap-info.freeswap, info.freeswap);
+       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;
+
+#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("%6s%13ld%13ld%13ld\n", "Total:", info.totalram+info.totalswap,
-                       (info.totalram-info.freeram)+(info.totalswap-info.freeswap),
-                       info.freeram+info.freeswap);
+       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("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;
 }
-
-