#include "vici_builder.h"
#include <inttypes.h>
+#include <time.h>
#ifndef WIN32
#include <sys/utsname.h>
#endif
+#ifdef HAVE_MALLINFO
+#include <malloc.h>
+#endif
#include <daemon.h>
* Dispatcher
*/
vici_dispatcher_t *dispatcher;
+
+ /**
+ * Daemon startup timestamp
+ */
+ time_t uptime;
};
/**
return b->finalize(b);
}
+/**
+ * Callback function for memusage summary
+ */
+CALLBACK(sum_usage, void,
+ vici_builder_t *b, int count, size_t bytes, int whitelisted)
+{
+ b->begin_section(b, "mem");
+ b->add_kv(b, "total", "%zu", bytes);
+ b->add_kv(b, "allocs", "%d", count);
+ b->end_section(b);
+}
+
+CALLBACK(stats, vici_message_t*,
+ private_vici_query_t *this, char *name, u_int id, vici_message_t *request)
+{
+ vici_builder_t *b;
+ enumerator_t *enumerator;
+ plugin_t *plugin;
+ time_t since, now;
+ int i;
+
+ b = vici_builder_create();
+
+ now = time_monotonic(NULL);
+ since = time(NULL) - (now - this->uptime);
+
+ b->begin_section(b, "uptime");
+ b->add_kv(b, "running", "%V", &now, &this->uptime);
+ b->add_kv(b, "since", "%T", &since, FALSE);
+ b->end_section(b);
+
+ b->begin_section(b, "workers");
+ b->add_kv(b, "total", "%d",
+ lib->processor->get_total_threads(lib->processor));
+ b->add_kv(b, "idle", "%d",
+ lib->processor->get_idle_threads(lib->processor));
+ b->begin_section(b, "active");
+ for (i = 0; i < JOB_PRIO_MAX; i++)
+ {
+ b->add_kv(b, enum_to_name(job_priority_names, i), "%d",
+ lib->processor->get_working_threads(lib->processor, i));
+ }
+ b->end_section(b);
+ b->end_section(b);
+
+ b->begin_section(b, "queues");
+ for (i = 0; i < JOB_PRIO_MAX; i++)
+ {
+ b->add_kv(b, enum_to_name(job_priority_names, i), "%d",
+ lib->processor->get_job_load(lib->processor, i));
+ }
+ b->end_section(b);
+
+ b->add_kv(b, "scheduled", "%d",
+ lib->scheduler->get_job_load(lib->scheduler));
+
+ b->begin_section(b, "ikesas");
+ b->add_kv(b, "total", "%u",
+ charon->ike_sa_manager->get_count(charon->ike_sa_manager));
+ b->add_kv(b, "half-open", "%u",
+ charon->ike_sa_manager->get_half_open_count(charon->ike_sa_manager,
+ NULL));
+ b->end_section(b);
+
+ b->begin_list(b, "plugins");
+ enumerator = lib->plugins->create_plugin_enumerator(lib->plugins);
+ while (enumerator->enumerate(enumerator, &plugin, NULL))
+ {
+ b->add_li(b, "%s", plugin->get_name(plugin));
+ }
+ enumerator->destroy(enumerator);
+ b->end_list(b);
+
+ if (lib->leak_detective)
+ {
+ lib->leak_detective->usage(lib->leak_detective, NULL, sum_usage, b);
+ }
+#ifdef WIN32
+ else
+ {
+ DWORD lasterr = ERROR_INVALID_HANDLE;
+ HANDLE heaps[32];
+ int i, count;
+ char buf[16];
+ size_t total = 0;
+ int allocs = 0;
+
+ b->begin_section(b, "mem");
+ count = GetProcessHeaps(countof(heaps), heaps);
+ for (i = 0; i < count; i++)
+ {
+ PROCESS_HEAP_ENTRY entry = {};
+ size_t heap_total = 0;
+ int heap_allocs = 0;
+
+ if (HeapLock(heaps[i]))
+ {
+ while (HeapWalk(heaps[i], &entry))
+ {
+ if (entry.wFlags & PROCESS_HEAP_ENTRY_BUSY)
+ {
+ heap_total += entry.cbData;
+ heap_allocs++;
+ }
+ }
+ lasterr = GetLastError();
+ HeapUnlock(heaps[i]);
+ }
+ if (lasterr != ERROR_NO_MORE_ITEMS)
+ {
+ break;
+ }
+ snprintf(buf, sizeof(buf), "heap-%d", i);
+ b->begin_section(b, buf);
+ b->add_kv(b, "total", "%zu", heap_total);
+ b->add_kv(b, "allocs", "%d", heap_allocs);
+ b->end_section(b);
+
+ total += heap_total;
+ allocs += heap_allocs;
+ }
+ if (lasterr == ERROR_NO_MORE_ITEMS)
+ {
+ b->add_kv(b, "total", "%zu", total);
+ b->add_kv(b, "allocs", "%d", allocs);
+ }
+ b->end_section(b);
+ }
+#endif
+
+#ifdef HAVE_MALLINFO
+ {
+ struct mallinfo mi = mallinfo();
+
+ b->begin_section(b, "mallinfo");
+ b->add_kv(b, "sbrk", "%d", mi.arena);
+ b->add_kv(b, "mmap", "%d", mi.hblkhd);
+ b->add_kv(b, "used", "%d", mi.uordblks);
+ b->add_kv(b, "free", "%d", mi.fordblks);
+ b->end_section(b);
+ }
+#endif /* HAVE_MALLINFO */
+
+ return b->finalize(b);
+}
+
static void manage_command(private_vici_query_t *this,
char *name, vici_command_cb_t cb, bool reg)
{
manage_command(this, "list-conns", list_conns, reg);
manage_command(this, "list-certs", list_certs, reg);
manage_command(this, "version", version, reg);
+ manage_command(this, "stats", stats, reg);
}
METHOD(vici_query_t, destroy, void,
.destroy = _destroy,
},
.dispatcher = dispatcher,
+ .uptime = time_monotonic(NULL),
);
manage_commands(this, TRUE);
--- /dev/null
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * 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. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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.
+ */
+
+#include "command.h"
+
+#include <errno.h>
+
+static int stats(vici_conn_t *conn)
+{
+ vici_req_t *req;
+ vici_res_t *res;
+ char *arg;
+ command_format_options_t format = COMMAND_FORMAT_NONE;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 'P':
+ format |= COMMAND_FORMAT_PRETTY;
+ /* fall through to raw */
+ case 'r':
+ format |= COMMAND_FORMAT_RAW;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --stats option");
+ }
+ break;
+ }
+
+ req = vici_begin("stats");
+ res = vici_submit(req, conn);
+ if (!res)
+ {
+ fprintf(stderr, "stats request failed: %s\n", strerror(errno));
+ return errno;
+ }
+ if (format & COMMAND_FORMAT_RAW)
+ {
+ vici_dump(res, "stats reply", format & COMMAND_FORMAT_PRETTY, stdout);
+ }
+ else
+ {
+ printf("uptime: %s, since %s\n",
+ vici_find_str(res, "", "uptime.running"),
+ vici_find_str(res, "", "uptime.since"));
+
+ printf("worker threads: %s total, %s idle, working: %s/%s/%s/%s\n",
+ vici_find_str(res, "", "workers.total"),
+ vici_find_str(res, "", "workers.idle"),
+ vici_find_str(res, "", "workers.active.critical"),
+ vici_find_str(res, "", "workers.active.high"),
+ vici_find_str(res, "", "workers.active.medium"),
+ vici_find_str(res, "", "workers.active.low"));
+
+ printf("job queues: %s/%s/%s/%s\n",
+ vici_find_str(res, "", "queues.critical"),
+ vici_find_str(res, "", "queues.high"),
+ vici_find_str(res, "", "queues.medium"),
+ vici_find_str(res, "", "queues.low"));
+
+ printf("jobs scheduled: %s\n",
+ vici_find_str(res, "", "scheduled"));
+
+ printf("IKE_SAs: %s total, %s half-open\n",
+ vici_find_str(res, "", "ikesas.total"),
+ vici_find_str(res, "", "ikesas.half-open"));
+
+ if (vici_find_str(res, NULL, "mem.total"))
+ {
+ printf("memory usage: %s bytes, %s allocations\n",
+ vici_find_str(res, "", "mem.total"),
+ vici_find_str(res, "", "mem.allocs"));
+ }
+ if (vici_find_str(res, NULL, "mallinfo.sbrk"))
+ {
+ printf("mallinfo: sbrk %s, mmap %s, used %s, free %s\n",
+ vici_find_str(res, "", "mallinfo.sbrk"),
+ vici_find_str(res, "", "mallinfo.mmap"),
+ vici_find_str(res, "", "mallinfo.used"),
+ vici_find_str(res, "", "mallinfo.free"));
+ }
+ }
+ vici_free_res(res);
+ return 0;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ stats, 'S', "stats", "show daemon stats information",
+ {"[--raw|--pretty]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"raw", 'r', 0, "dump raw response message"},
+ {"pretty", 'P', 0, "dump raw response message in pretty print"},
+ }
+ });
+}