--- /dev/null
+/*
+ * Copyright (c) 2016, Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <netinet/in.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sched.h>
+#include <assert.h>
+
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+
+#include <locale.h>
+
+#include <xenctrl.h>
+#include <xenstore.h>
+#include <xenevtchn.h>
+#include <xengnttab.h>
+
+#include "xen_stats.h"
+
+#include "debug.h"
+
+#define FALSE 0
+
+enum {
+ STATS_OPT_DOMAIN,
+ STATS_OPT_PROVIDER,
+ STATS_OPT_SET,
+ STATS_OPT_INTERVAL,
+ STATS_NR_OPTS
+ };
+
+static struct option stats_option[] = {
+ {"domain", 1, NULL, 0},
+ {"provider", 1, NULL, 0},
+ {"set", 1, NULL, 0},
+ {"interval", 1, NULL, 0},
+ {NULL, 0, NULL, 0}
+};
+
+static const char *stats_option_text[] = {
+ "<domid>",
+ "<provider>",
+ "<set>",
+ "<interval>",
+ NULL
+};
+
+static const char *prog;
+
+static void
+usage(void)
+{
+ int i;
+
+ fprintf(stderr, "Usage: %s <options>\n\n", prog);
+
+ for (i = 0; i < STATS_NR_OPTS; i++)
+ fprintf(stderr, "\t--%s %s\n",
+ stats_option[i].name,
+ stats_option_text[i]);
+
+ fprintf(stderr, "\n");
+
+ exit(2);
+}
+
+typedef enum {
+ STATS_SEQ_UNINITIALIZED = 0,
+ STATS_SEQ_CTRL_OPEN,
+ STATS_SEQ_STORE_OPEN,
+ STATS_SEQ_EVTCHN_OPEN,
+ STATS_SEQ_GNTTAB_OPEN,
+ STATS_SEQ_SET_FOUND,
+ STATS_SEQ_SET_NOTIFIED,
+ STATS_SEQ_WATCH_CREATED,
+ STATS_SEQ_TIMER_CREATED,
+ STATS_SEQ_INITIALIZED,
+ STATS_NR_SEQS
+} stats_seq_t;
+
+typedef struct stats_state {
+ stats_seq_t seq;
+ domid_t domid;
+ xc_interface *xch;
+ struct xs_handle *xsh;
+ xenevtchn_handle *xeh;
+ xengnttab_handle *xgh;
+ char *path;
+ char *monitor_id_key;
+ char *event_channel_key;
+ char *event_channel_token;
+ evtchn_port_t local_port;
+ char **name;
+ unsigned int *type;
+ void *values;
+ unsigned int nr_stats;
+ unsigned int nr_pages;
+ bool bound;
+ timer_t tid;
+ int tfd;
+} stats_state_t;
+
+static stats_state_t stats_state;
+
+static void
+stats_seq_next(stats_seq_t seq)
+{
+ assert(stats_state.seq < STATS_SEQ_INITIALIZED);
+
+ stats_state.seq++;
+ assert(stats_state.seq == seq);
+
+ switch (stats_state.seq) {
+ case STATS_SEQ_CTRL_OPEN:
+ DBG(">CTRL_OPEN\n");
+ break;
+
+ case STATS_SEQ_STORE_OPEN:
+ DBG(">STORE_OPEN\n");
+ break;
+
+ case STATS_SEQ_EVTCHN_OPEN:
+ DBG(">EVTCHN_OPEN\n");
+ break;
+
+ case STATS_SEQ_GNTTAB_OPEN:
+ DBG(">GNTTAB_OPEN\n");
+ break;
+
+ case STATS_SEQ_SET_FOUND:
+ DBG(">SET_FOUND\n");
+ break;
+
+ case STATS_SEQ_SET_NOTIFIED:
+ DBG(">SET_NOTIFIED\n");
+ break;
+
+ case STATS_SEQ_WATCH_CREATED:
+ DBG(">WATCH_CREATED\n");
+ break;
+
+ case STATS_SEQ_TIMER_CREATED:
+ DBG(">TIMER_CREATED\n");
+ break;
+
+ case STATS_SEQ_INITIALIZED:
+ DBG(">INITIALIZED\n");
+ break;
+
+ default:
+ assert(FALSE);
+ break;
+ }
+}
+
+static int
+stats_set_monitor_id(void)
+{
+ char *domid_str;
+ domid_t domid;
+ char *key;
+ char *value;
+ int rc;
+
+ domid_str = xs_read(stats_state.xsh, XBT_NULL,
+ "domid", NULL);
+
+ if (domid_str == NULL)
+ goto fail1;
+
+ domid = (domid_t)strtol(domid_str, NULL, 0);
+
+ rc = asprintf(&key, "%s/monitor-id", stats_state.path);
+ if (rc < 0)
+ goto fail2;
+
+ rc = asprintf(&value, "%u", domid);
+ if (rc < 0)
+ goto fail3;
+
+ DBG("%s -> %s\n", key, value);
+
+ if (!xs_write(stats_state.xsh, XBT_NULL, key, value, strlen(value)))
+ goto fail4;
+
+ free(value);
+
+ stats_state.monitor_id_key = key;
+
+ free(domid_str);
+
+ return 0;
+
+fail4:
+ DBG("fail4\n");
+
+ free(value);
+
+fail3:
+ DBG("fail3\n");
+
+ free(key);
+
+fail2:
+ DBG("fail2\n");
+
+ free(domid_str);
+
+fail1:
+ DBG("fail1\n");
+
+ return -1;
+}
+
+static void
+stats_clear_monitor_id(void)
+{
+ (void) xs_rm(stats_state.xsh, XBT_NULL, stats_state.monitor_id_key);
+
+ free(stats_state.monitor_id_key);
+}
+
+static int
+stats_create_watch(void)
+{
+ char *key;
+ char *token;
+ int rc;
+
+ rc = asprintf(&key, "%s/event-channel", stats_state.path);
+ if (rc < 0)
+ goto fail1;
+
+ DBG("%s\n", key);
+
+ rc = asprintf(&token, "%u:event-channel", getpid());
+ if (rc < 0)
+ goto fail2;
+
+ if (!xs_watch(stats_state.xsh, key, token))
+ goto fail3;
+
+ stats_state.event_channel_key = key;
+ stats_state.event_channel_token = token;
+
+ return 0;
+
+fail3:
+ DBG("fail3\n");
+
+ free(token);
+
+fail2:
+ DBG("fail2\n");
+
+ free(key);
+
+fail1:
+ DBG("fail1\n");
+
+ return -1;
+}
+
+static void
+stats_destroy_watch(void)
+{
+ (void) xs_unwatch(stats_state.xsh, stats_state.event_channel_key,
+ stats_state.event_channel_token);
+
+ free(stats_state.event_channel_key);
+ free(stats_state.event_channel_token);
+}
+
+static int
+stats_parse_name_types(void *name_types, unsigned int nr_pages)
+{
+ struct xen_stats_name_type *name_type = name_types;
+ unsigned int max_nr_stats;
+ unsigned int i, nr_stats;
+
+ max_nr_stats = (nr_pages * XC_PAGE_SIZE) /
+ sizeof(struct xen_stats_name_type);
+
+ i = 0;
+ while (i < max_nr_stats) {
+ if (name_type[i].type == XEN_STATS_TYPE_INVALID)
+ break;
+
+ i++;
+ }
+ nr_stats = i;
+
+ if (nr_stats == 0)
+ goto fail1;
+
+ stats_state.name = calloc(nr_stats, sizeof (char *));
+ if (stats_state.name == NULL)
+ goto fail2;
+
+ stats_state.type = calloc(nr_stats, sizeof (unsigned int));
+ if (stats_state.type == NULL)
+ goto fail3;
+
+ for (i = 0; i < nr_stats; i++) {
+ int rc;
+
+ rc = asprintf(&stats_state.name[i], "%s", name_type[i].name);
+ if (rc < 0)
+ goto fail4;
+
+ stats_state.type[i] = name_type[i].type;
+ }
+
+ stats_state.nr_stats = nr_stats;
+
+ return 0;
+
+fail4:
+ DBG("fail4\n");
+
+ while (i-- != 0)
+ free(stats_state.name[i]);
+
+ free(stats_state.type);
+
+fail3:
+ DBG("fail3\n");
+
+ free(stats_state.name);
+
+fail2:
+ DBG("fail2\n");
+
+fail1:
+ DBG("fail1\n");
+
+ return -1;
+}
+
+static void
+stats_free_name_types(void)
+{
+ unsigned int i = stats_state.nr_stats;
+
+ while (i-- != 0)
+ free(stats_state.name[i]);
+
+ free(stats_state.type);
+ free(stats_state.name);
+}
+
+#define MAX_NR_PAGES 8
+
+static int
+stats_bind(unsigned int port)
+{
+ uint32_t name_ref[MAX_NR_PAGES];
+ uint32_t val_ref[MAX_NR_PAGES];
+ void *name_types;
+ unsigned int i, nr_pages;
+ int rc;
+
+ rc = xenevtchn_bind_interdomain(stats_state.xeh, stats_state.domid,
+ port);
+ if (rc < 0)
+ goto fail1;
+
+ stats_state.local_port = rc;
+
+ DBG("(%u:%u) -> %u\n", stats_state.domid, port, stats_state.local_port);
+
+ i = 0;
+ while (i < MAX_NR_PAGES) {
+ char *key;
+ char *ref_str;
+ uint32_t ref;
+
+ rc = asprintf(&key, "%s/name-ref%u", stats_state.path, i);
+ if (rc < 0)
+ goto fail2;
+
+ ref_str = xs_read(stats_state.xsh, XBT_NULL, key, NULL);
+ free(key);
+
+ if (ref_str == NULL)
+ break;
+
+ ref = strtol(ref_str, NULL, 0);
+ free(ref_str);
+
+ DBG("name-ref%u = %u\n", i, ref);
+
+ name_ref[i++] = ref;
+ }
+ nr_pages = i;
+
+ i = 0;
+ while (i < nr_pages) {
+ char *key;
+ char *ref_str;
+ uint32_t ref;
+
+ rc = asprintf(&key, "%s/val-ref%u", stats_state.path, i);
+ if (rc < 0)
+ goto fail3;
+
+ ref_str = xs_read(stats_state.xsh, XBT_NULL, key, NULL);
+ free(key);
+
+ if (ref_str == NULL)
+ break;
+
+ ref = strtol(ref_str, NULL, 0);
+ free(ref_str);
+
+ DBG("val-ref%u = %u\n", i, ref);
+
+ val_ref[i++] = ref;
+ }
+ assert(i == nr_pages);
+
+ DBG("%u page(s)\n", nr_pages);
+
+ name_types = xengnttab_map_domain_grant_refs(stats_state.xgh, nr_pages,
+ stats_state.domid,
+ name_ref,
+ PROT_READ);
+ if (name_types == NULL)
+ goto fail4;
+
+ rc = stats_parse_name_types(name_types, nr_pages);
+ if (rc < 0)
+ goto fail5;
+
+ stats_state.values = xengnttab_map_domain_grant_refs(stats_state.xgh,
+ nr_pages,
+ stats_state.domid,
+ val_ref,
+ PROT_READ);
+ if (stats_state.values == NULL)
+ goto fail6;
+
+ (void) xengnttab_unmap(stats_state.xgh, name_types, nr_pages);
+
+ stats_state.nr_pages = nr_pages;
+ stats_state.bound = true;
+
+ return 0;
+
+fail6:
+ DBG("fail6\n");
+
+ stats_free_name_types();
+
+fail5:
+ DBG("fail5\n");
+
+ (void) xengnttab_unmap(stats_state.xgh, name_types, nr_pages);
+
+fail4:
+ DBG("fail4\n");
+
+fail3:
+ DBG("fail3\n");
+
+fail2:
+ DBG("fail2\n");
+
+ (void) xenevtchn_unbind(stats_state.xeh, stats_state.local_port);
+
+fail1:
+ DBG("fail1\n");
+
+ return -1;
+}
+
+static void
+stats_unbind(void)
+{
+ unsigned int nr_pages;
+
+ nr_pages = stats_state.nr_pages;
+
+ (void) xengnttab_unmap(stats_state.xgh, stats_state.values, nr_pages);
+
+ stats_free_name_types();
+
+ (void) xenevtchn_unbind(stats_state.xeh, stats_state.local_port);
+ stats_state.bound = false;
+}
+
+static int
+stats_create_timer(void)
+{
+ int pfd[2];
+ int flags;
+ timer_t tid;
+ struct sigevent sigev;
+
+ memset(pfd, 0, sizeof (pfd));
+ if (pipe(pfd) < 0)
+ goto fail1;
+
+ flags = fcntl(pfd[0], F_GETFL);
+ fcntl(pfd[0], F_SETFL, flags | O_NONBLOCK);
+
+ flags = fcntl(pfd[1], F_GETFL);
+ fcntl(pfd[1], F_SETFL, flags | O_NONBLOCK);
+
+ sigev.sigev_notify = SIGEV_SIGNAL;
+ sigev.sigev_signo = SIGRTMIN;
+ sigev.sigev_value.sival_int = pfd[1];
+
+ if (timer_create(CLOCK_MONOTONIC, &sigev, &tid) < 0)
+ goto fail2;
+
+ stats_state.tid = tid;
+ stats_state.tfd = pfd[0];
+
+ return 0;
+
+fail2:
+ DBG("fail2\n");
+
+ close(pfd[1]);
+ close(pfd[0]);
+
+fail1:
+ DBG("fail1\n");
+
+ return -1;
+}
+
+static void
+stats_set_timer(unsigned int s)
+{
+ struct itimerspec it;
+
+ it.it_interval.tv_sec = it.it_value.tv_sec = s;
+ it.it_interval.tv_nsec = it.it_value.tv_nsec = 0;
+
+ (void) timer_settime(stats_state.tid, 0, &it, NULL);
+}
+
+static void
+stats_destroy_timer(void)
+{
+ timer_delete(stats_state.tid);
+ close(stats_state.tfd);
+}
+
+static void
+stats_teardown(void)
+{
+ if (stats_state.seq == STATS_SEQ_INITIALIZED) {
+ DBG("<INITIALIZED\n");
+
+ if (stats_state.bound)
+ stats_unbind();
+
+ stats_state.seq = STATS_SEQ_TIMER_CREATED;
+ }
+
+ if (stats_state.seq == STATS_SEQ_TIMER_CREATED) {
+ DBG("<TIMER_CREATED\n");
+
+ stats_destroy_timer();
+
+ stats_state.seq = STATS_SEQ_WATCH_CREATED;
+ }
+
+ if (stats_state.seq == STATS_SEQ_WATCH_CREATED) {
+ DBG("<WATCH_CREATED\n");
+
+ stats_destroy_watch();
+
+ stats_state.seq = STATS_SEQ_SET_NOTIFIED;
+ }
+
+ if (stats_state.seq == STATS_SEQ_SET_NOTIFIED) {
+ DBG("<SET_NOTIFIED\n");
+
+ stats_clear_monitor_id();
+
+ stats_state.seq = STATS_SEQ_SET_FOUND;
+ }
+
+ if (stats_state.seq == STATS_SEQ_SET_FOUND) {
+ DBG("<SET_FOUND\n");
+
+ free(stats_state.path);
+
+ stats_state.seq = STATS_SEQ_GNTTAB_OPEN;
+ }
+
+ if (stats_state.seq == STATS_SEQ_GNTTAB_OPEN) {
+ DBG("<GNTTAB_OPEN\n");
+
+ (void) xengnttab_close(stats_state.xgh);
+
+ stats_state.seq = STATS_SEQ_EVTCHN_OPEN;
+ }
+
+ if (stats_state.seq == STATS_SEQ_EVTCHN_OPEN) {
+ DBG("<EVTCHN_OPEN\n");
+
+ (void) xenevtchn_close(stats_state.xeh);
+
+ stats_state.seq = STATS_SEQ_STORE_OPEN;
+ }
+
+ if (stats_state.seq == STATS_SEQ_STORE_OPEN) {
+ DBG("<GNTTAB_OPEN\n");
+
+ xs_close(stats_state.xsh);
+
+ stats_state.seq = STATS_SEQ_CTRL_OPEN;
+ }
+
+ if (stats_state.seq >= STATS_SEQ_CTRL_OPEN) {
+ DBG("<CTRL_OPEN\n");
+
+ (void) xc_interface_close(stats_state.xch);
+
+ stats_state.seq = STATS_SEQ_UNINITIALIZED;
+ }
+}
+
+static struct sigaction sigabrt_handler;
+
+static void
+stats_sigabrt(int num)
+{
+ DBG("%s\n", strsignal(num));
+
+ sigaction(SIGABRT, &sigabrt_handler, NULL);
+}
+
+static struct sigaction sigterm_handler;
+
+static void
+stats_sigterm(int num)
+{
+ DBG("%s\n", strsignal(num));
+
+ stats_teardown();
+
+ exit(0);
+}
+
+static struct sigaction sigusr1_handler;
+
+static void
+stats_sigusr1(int num)
+{
+ DBG("%s\n", strsignal(num));
+
+ sigaction(SIGHUP, &sigusr1_handler, NULL);
+}
+
+static struct sigaction sigrt_handler;
+
+static void
+stats_sigrt(int num, siginfo_t *si, void *arg)
+{
+ int tfd;
+ char buf = 'T';
+
+ sigaction(SIGRTMIN, &sigrt_handler, NULL);
+
+ tfd = (int)si->si_value.sival_int;
+ write(tfd, &buf, 1);
+}
+
+static char *
+stats_find_set(char *provider, char *set)
+{
+ char *path = NULL;
+ char **list = NULL;
+ unsigned int i, n;
+ bool found;
+ int rc;
+
+ rc = asprintf(&path, "/local/domain/%u/stats", stats_state.domid);
+ if (rc < 0)
+ goto fail1;
+
+ list = xs_directory(stats_state.xsh, XBT_NULL, path, &n);
+ if (list == NULL)
+ goto fail2;
+
+ free(path);
+ path = NULL;
+
+ found = false;
+ for (i = 0; i < n; i++) {
+ if (strcmp(provider, list[i]) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ goto fail3;
+
+ free(list);
+ list = NULL;
+
+ rc = asprintf(&path, "/local/domain/%u/stats/%s", stats_state.domid,
+ provider);
+ if (rc < 0)
+ goto fail4;
+
+ list = xs_directory(stats_state.xsh, XBT_NULL, path, &n);
+ if (list == NULL)
+ goto fail5;
+
+ free(path);
+ path = NULL;
+
+ found = false;
+ for (i = 0; i < n; i++) {
+ if (strcmp(set, list[i]) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ goto fail6;
+
+ free(list);
+ list = NULL;
+
+ rc = asprintf(&path, "/local/domain/%u/stats/%s/%s", stats_state.domid,
+ provider, set);
+ if (rc < 0)
+ goto fail7;
+
+ return path;
+
+fail7:
+ DBG("fail7\n");
+
+fail6:
+ DBG("fail6\n");
+
+ free(list);
+ list = NULL;
+
+fail5:
+ DBG("fail5\n");
+
+ free(path);
+ path = NULL;
+
+fail4:
+ DBG("fail4\n");
+
+fail3:
+ DBG("fail3\n");
+
+ if (list != NULL) {
+ free(list);
+ list = NULL;
+ }
+
+fail2:
+ DBG("fail2\n");
+
+ if (list != NULL) {
+ free(path);
+ path = NULL;
+ }
+
+fail1:
+ DBG("fail1\n");
+
+ return NULL;
+}
+
+static int
+stats_initialize(domid_t domid, char *provider, char *set)
+{
+ int rc;
+ xc_dominfo_t dominfo;
+
+ stats_state.domid = domid;
+
+ stats_state.xch = xc_interface_open(NULL, NULL, 0);
+ if (stats_state.xch == NULL)
+ goto fail1;
+
+ stats_seq_next(STATS_SEQ_CTRL_OPEN);
+
+ rc = xc_domain_getinfo(stats_state.xch, stats_state.domid, 1, &dominfo);
+ if (rc < 0 || dominfo.domid != stats_state.domid)
+ goto fail2;
+
+ stats_state.xsh = xs_open(0);
+ if (stats_state.xsh == NULL)
+ goto fail3;
+
+ stats_seq_next(STATS_SEQ_STORE_OPEN);
+
+ stats_state.xeh = xenevtchn_open(NULL, 0);
+
+ if (stats_state.xeh == NULL)
+ goto fail4;
+
+ stats_seq_next(STATS_SEQ_EVTCHN_OPEN);
+
+ stats_state.xgh = xengnttab_open(NULL, 0);
+
+ if (stats_state.xgh == NULL)
+ goto fail5;
+
+ stats_seq_next(STATS_SEQ_GNTTAB_OPEN);
+
+ stats_state.path = stats_find_set(provider, set);
+
+ if (stats_state.path == NULL)
+ goto fail6;
+
+ stats_seq_next(STATS_SEQ_SET_FOUND);
+
+ rc = stats_set_monitor_id();
+ if (rc < 0)
+ goto fail7;
+
+ stats_seq_next(STATS_SEQ_SET_NOTIFIED);
+
+ rc = stats_create_watch();
+ if (rc < 0)
+ goto fail8;
+
+ stats_seq_next(STATS_SEQ_WATCH_CREATED);
+
+ rc = stats_create_timer();
+ if (rc < 0)
+ goto fail9;
+
+ stats_seq_next(STATS_SEQ_TIMER_CREATED);
+
+ stats_seq_next(STATS_SEQ_INITIALIZED);
+
+ return 0;
+
+fail9:
+ DBG("fail9\n");
+
+fail8:
+ DBG("fail8\n");
+
+fail7:
+ DBG("fail7\n");
+
+fail6:
+ DBG("fail6\n");
+
+fail5:
+ DBG("fail5\n");
+
+fail4:
+ DBG("fail4\n");
+
+fail3:
+ DBG("fail3\n");
+
+fail2:
+ DBG("fail2\n");
+
+fail1:
+ DBG("fail1\n");
+
+ warn("fail");
+ return -1;
+}
+
+static void
+stats_dump(void)
+{
+ struct xen_stats_value *value = stats_state.values;
+ unsigned int i;
+
+ for (i = 0; i < stats_state.nr_stats; i++) {
+ switch (stats_state.type[i]) {
+ case XEN_STATS_TYPE_S64:
+ printf("%s = %ld\n", stats_state.name[i],
+ *(long *)value[i].buffer);
+ break;
+
+ case XEN_STATS_TYPE_U64:
+ printf("%s = %lu\n", stats_state.name[i],
+ *(unsigned long *)value[i].buffer);
+ break;
+
+ case XEN_STATS_TYPE_DOUBLE:
+ printf("%s = %f\n", stats_state.name[i],
+ *(double *)value[i].buffer);
+ break;
+
+ case XEN_STATS_TYPE_ASCII:
+ printf("%s = %s\n", stats_state.name[i],
+ value[i].buffer);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static void
+stats_event(void)
+{
+ evtchn_port_t port;
+
+ port = xenevtchn_pending(stats_state.xeh);
+ if (port != stats_state.local_port)
+ return;
+
+ stats_dump();
+
+ xenevtchn_unmask(stats_state.xeh, port);
+}
+
+static void
+stats_watch(void)
+{
+ char **vec;
+ unsigned int n;
+
+ vec = xs_read_watch(stats_state.xsh, &n);
+ if (vec == NULL)
+ return;
+
+ assert(n == 2);
+
+ if (strcmp(vec[XS_WATCH_TOKEN], stats_state.event_channel_token) == 0) {
+ char *port_str;
+
+ port_str = xs_read(stats_state.xsh, XBT_NULL,
+ stats_state.event_channel_key, NULL);
+ if (port_str != NULL && !stats_state.bound) {
+ evtchn_port_t port;
+
+ port = (evtchn_port_t)strtol(port_str, NULL, 0);
+
+ (void) stats_bind(port);
+ } else if (port_str == NULL && stats_state.bound) {
+ stats_unbind();
+ }
+ }
+
+ free(vec);
+}
+
+static void
+stats_tick()
+{
+ if (stats_state.bound)
+ xenevtchn_notify(stats_state.xeh, stats_state.local_port);
+}
+
+int
+main(int argc, char **argv, char **envp)
+{
+ char *domain_str;
+ char *provider_str;
+ char *set_str;
+ char *interval_str;
+ int index;
+ domid_t domid;
+ unsigned int interval;
+ sigset_t block;
+ sigset_t unblock;
+ struct pollfd pfd[3];
+ int rc;
+
+ prog = basename(argv[0]);
+
+ domain_str = NULL;
+ provider_str = NULL;
+ set_str = NULL;
+ interval_str = NULL;
+
+ for (;;) {
+ char c;
+
+ c = getopt_long(argc, argv, "", stats_option, &index);
+ if (c == -1)
+ break;
+
+ if (c != 0) {
+ usage();
+ /*NOTREACHED*/
+ }
+
+ DBG("--%s = '%s'\n", stats_option[index].name, optarg);
+
+ switch (index) {
+ case STATS_OPT_DOMAIN:
+ domain_str = optarg;
+ break;
+
+ case STATS_OPT_PROVIDER:
+ provider_str = optarg;
+ break;
+
+ case STATS_OPT_SET:
+ set_str = optarg;
+ break;
+
+ case STATS_OPT_INTERVAL:
+ interval_str = optarg;
+ break;
+
+ default:
+ assert(FALSE);
+ break;
+ }
+ }
+
+ if (domain_str == NULL ||
+ provider_str == NULL ||
+ set_str == NULL ||
+ interval_str == NULL) {
+ usage();
+ /*NOTREACHED*/
+ }
+
+ domid = (domid_t)strtol(domain_str, NULL, 0);
+ interval = (unsigned int)strtol(interval_str, NULL, 0);
+
+ sigfillset(&block);
+
+ memset(&sigterm_handler, 0, sizeof (struct sigaction));
+ sigterm_handler.sa_handler = stats_sigabrt;
+
+ sigaction(SIGABRT, &sigabrt_handler, NULL);
+ sigdelset(&block, SIGABRT);
+
+ sigprocmask(SIG_BLOCK, &block, NULL);
+
+ rc = stats_initialize(domid, provider_str, set_str);
+ if (rc < 0) {
+ stats_teardown();
+ exit(1);
+ }
+
+ sigemptyset(&unblock);
+
+ memset(&sigterm_handler, 0, sizeof (struct sigaction));
+ sigterm_handler.sa_handler = stats_sigterm;
+
+ sigaction(SIGTERM, &sigterm_handler, NULL);
+ sigaddset(&unblock, SIGTERM);
+
+ sigaction(SIGINT, &sigterm_handler, NULL);
+ sigaddset(&unblock, SIGINT);
+
+ sigaction(SIGHUP, &sigterm_handler, NULL);
+ sigaddset(&unblock, SIGHUP);
+
+ memset(&sigusr1_handler, 0, sizeof (struct sigaction));
+ sigusr1_handler.sa_handler = stats_sigusr1;
+
+ sigaction(SIGUSR1, &sigusr1_handler, NULL);
+ sigaddset(&unblock, SIGUSR1);
+
+ memset(&sigrt_handler, 0, sizeof (struct sigaction));
+ sigrt_handler.sa_flags = SA_SIGINFO;
+ sigrt_handler.sa_sigaction = stats_sigrt;
+
+ sigaction(SIGRTMIN, &sigrt_handler, NULL);
+ sigaddset(&unblock, SIGRTMIN);
+
+ sigprocmask(SIG_UNBLOCK, &unblock, NULL);
+
+ pfd[0].fd = xenevtchn_fd(stats_state.xeh);
+ pfd[0].events = POLLIN | POLLERR | POLLHUP;
+ pfd[0].revents = 0;
+
+ pfd[1].fd = xs_fileno(stats_state.xsh);
+ pfd[1].events = POLLIN | POLLERR | POLLHUP;
+ pfd[1].revents = 0;
+
+ pfd[2].fd = stats_state.tfd;
+ pfd[2].events = POLLIN | POLLERR | POLLHUP;
+ pfd[2].revents = 0;
+
+ stats_set_timer(interval);
+
+ for (;;) {
+ rc = poll(pfd, 3, 5000);
+
+ if (rc > 0) {
+ if (pfd[0].revents & POLLIN)
+ stats_event();
+
+ if (pfd[1].revents & POLLIN)
+ stats_watch();
+
+ if (pfd[2].revents & POLLIN) {
+ char buf;
+
+ read(stats_state.tfd, &buf, 1);
+ stats_tick();
+ stats_set_timer(interval);
+ }
+ } else if (rc < 0 && errno != EINTR) {
+ DBG("%s\n", strerror(errno));
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * c-tab-always-indent: nil
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */