- pod2man and other perldoc tools
* these are usually supplied with Perl
-- libvirt
+- libvirt & development tools
- Perl module Sys::Virt
#interval 60
# Catch-all default rule for guests. This rule MUST be last in the file.
+# This lists commands that are enabled. Any commands not listed here
+# will be disabled by default.
[*]
interval 1
-physcpus on
+availcpus on
+corespersocket on
+memory on
+mhz on
+model on
+nodes on
+physcpus on
+ping on
+socketspernode on
+threadspercore on
# change this.
#sockets @localstatedir@/lib/hostinfo
+# Libvirt connection URI.
+# See: http://libvirt.org/uri.html
+#libvirt xen:///
+
# Enable verbose messages sent to the system log files.
#verbose 1
AC_CHECK_FUNCS([inotify_init1 clock_gettime])
dnl Check for required packages using pkg-config.
-PKG_CHECK_MODULES([HOSTINFOD],[apr-1 >= 1.3])
+PKG_CHECK_MODULES([HOSTINFOD],[apr-1 >= 1.3 libvirt >= 0.2.1])
dnl Check for Perl and POD.
AC_CHECK_PROG([PERL],[perl],[perl],[no])
hostinfod.h \
main.c \
monitor_sockets.c \
- ping.c
+ nodeinfo.c \
+ ping.c \
+ virt.c
hostinfod_CFLAGS = \
-Wall \
#include <ctype.h>
#include <unistd.h>
#include <time.h>
+#include <math.h>
#include <apr_general.h>
#include <apr_pools.h>
apr_array_header_t *args;
struct arg arg;
command_fn fn;
+ int enabled;
+ double interval;
+ struct timespec *last;
debug ("%s: %s", hval->name, command);
/* Before dispatching the command, check the command is enabled
* and guest is not calling it too frequently.
*/
- error ("XXXXXXX enabled check not implemented XXXXXXX");
- error ("XXXXXXX frequency check not implemented XXXXXXX");
+ check_guests_file (hval, cmd, &interval, &enabled);
+
+ if (!enabled) {
+ warning ("%s: guest tried disabled command '%s'", hval->name, cmd);
+ send_error (hval, 401);
+ return;
+ }
+
+ last = apr_hash_get (hval->lasttime, cmd, APR_HASH_KEY_STRING);
+ if (last) {
+ struct timespec timediff;
+ double interval_int, interval_frac;
+ struct timespec interval_ts;
+
+ diff_timespec (&timediff, now, last);
+
+ interval_frac = modf (interval, &interval_int);
+ interval_ts.tv_sec = interval_int;
+ interval_ts.tv_nsec = interval_frac * 1000000000;
+
+ debug ("%s: %s: interval %ds %ldns, time since last %ds %ldns",
+ hval->name, cmd,
+ (int) interval_ts.tv_sec, interval_ts.tv_nsec,
+ (int) timediff.tv_sec, timediff.tv_nsec);
+
+ if (interval_ts.tv_sec > timediff.tv_sec ||
+ (interval_ts.tv_sec == timediff.tv_sec &&
+ interval_ts.tv_nsec > timediff.tv_nsec)) {
+ warning ("%s: command '%s' exceeded interval allowed", hval->name, cmd);
+ send_error (hval, 406);
+ return;
+ }
+ }
+
+ last = apr_pmemdup (hval->pool, now, sizeof *now);
+ apr_hash_set (hval->lasttime, cmd, APR_HASH_KEY_STRING, last);
/* Dispatch the command. */
fn (hval, cmd, args);
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <fnmatch.h>
#include <apr_general.h>
#include <apr_strings.h>
#include "hostinfod.h"
typedef int (*process_line_fn) (const char *path, int lineno,
- const char *key, const char *value);
+ const char *key, const char *value,
+ void *data);
typedef int (*process_section_fn) (const char *path, int lineno,
- const char *section);
+ const char *section,
+ void *data);
static void process_conf_file (const char *path, int exit_if_not_exist,
process_line_fn process_line,
- process_section_fn process_section);
+ process_section_fn process_section,
+ void *data);
static int get_bool (const char *str);
/* Read the main configuration file. */
-static int main_process_line (const char *path, int lineno, const char *key, const char *value);
+static int main_process_line (const char *path, int lineno, const char *key, const char *value, void *data);
void
read_main_conf_file (void)
{
- process_conf_file (conf_file, 0, main_process_line, NULL);
+ process_conf_file (conf_file, 0, main_process_line, NULL, NULL);
}
static int
main_process_line (const char *path, int lineno,
- const char *key, const char *value)
+ const char *key, const char *value,
+ void *data)
{
int bool;
return -1;
}
socket_dir = apr_pstrdup (pool, value);
+ } else if (strcasecmp (key, "libvirt") == 0) {
+ if (!value) {
+ error ("%s:%d: directive is empty: %s", path, lineno, key);
+ return -1;
+ }
+ if (!libvirt_uri_set_on_cmdline)
+ libvirt_uri = apr_pstrdup (pool, value);
} else if (strcasecmp (key, "verbose") == 0) {
bool = get_bool (value);
if (bool == -1) {
return 0;
}
+/* Check that 'cmd' is enabled for the named guest, and that
+ * it is not being called too frequently.
+ *
+ * XXX Rereading the configuration file each time is possibly
+ * inefficient.
+ *
+ * Returns 0 = proceed, -1 = fail.
+ */
+static int guests_process_line (const char *path, int lineno,
+ const char *key, const char *value,
+ void *data);
+static int guests_process_section (const char *path, int lineno,
+ const char *section,
+ void *data);
+
+struct guests_data {
+ const char *name; /* guest "driver-name" */
+ const char *cmd; /* command being tested */
+ int in_section; /* currently processing the right section? */
+ double interval; /* interval for this guest (0 = any) */
+ int enabled; /* is command enabled? */
+};
+
+void
+check_guests_file (struct guest_description *hval, const char *cmd,
+ double *interval, int *enabled)
+{
+ struct guests_data *data = apr_palloc (hval->rpool, sizeof *data);
+
+ data->name = hval->name;
+ data->cmd = cmd;
+ data->in_section = 0;
+ data->interval = 60.; /* default */
+ data->enabled = 0; /* default */
+
+ process_conf_file (guests_file, 1,
+ guests_process_line, guests_process_section, data);
+
+ *interval = data->interval;
+ *enabled = data->enabled;
+}
+
+static int
+guests_process_line (const char *path, int lineno,
+ const char *key, const char *value,
+ void *datav)
+{
+ struct guests_data *data = datav;
+ int bool;
+
+ if (!data->in_section)
+ return 0;
+
+ if (strcasecmp (key, "interval") == 0) {
+ if (strcasecmp (value, "any") == 0)
+ data->interval = 0;
+ else {
+ if (sscanf (value, "%lg", &data->interval) != 1) {
+ error ("%s:%d: %s: not a valid decimal number", path, lineno, key);
+ return -1;
+ }
+ }
+ } else if (strcasecmp (key, data->cmd) == 0) {
+ bool = get_bool (value);
+ if (bool == -1) {
+ error ("%s:%d: %s: not a valid boolean - use 1 or 0", path, lineno, key);
+ return -1;
+ }
+ data->enabled = bool;
+ }
+
+ return 0;
+}
+
+static int
+guests_process_section (const char *path, int lineno,
+ const char *section,
+ void *datav)
+{
+ struct guests_data *data = datav;
+ int flags = 0;
+
+#ifdef FNM_CASEFOLD
+ flags |= FNM_CASEFOLD;
+#endif
+
+ data->in_section = fnmatch (section, data->name, flags) == 0;
+ return 0;
+}
+
/* Configuration file parser. */
static void
process_conf_file (const char *path, int exit_if_not_exist,
process_line_fn process_line,
- process_section_fn process_section)
+ process_section_fn process_section,
+ void *data)
{
static const char *whitespace = " \t\n\v";
FILE *fp;
exit (1);
}
- debug ("configuration file: section [%s]", line);
+ debug ("configuration file: section [%s]", &line[1]);
if (!process_section) {
error ("%s:%d: unexpected section header ([%s]) in file",
- path, lineno, line);
+ path, lineno, &line[1]);
exit (1);
}
- if (process_section (path, lineno, line) == -1)
+ if (process_section (path, lineno, &line[1], data) == -1)
exit (1);
}
else { /* Key value */
key = line;
value = key_len < real_len ? &line[key_len+1] : NULL;
if (value) {
- value += strspn (line, whitespace);
+ value += strspn (value, whitespace);
if (value[0] == '\0')
value = NULL;
}
debug ("configuration file: key '%s', value '%s'", key, value);
- if (process_line && process_line (path, lineno, key, value) == -1)
+ if (process_line && process_line (path, lineno, key, value, data) == -1)
exit (1);
}
}
Boolean arguments should be sent as I<true> or I<false>.
+=head2 AVAILCPUS
+
+ AVAILCPUS
+
+=head3 Returns
+
+Number of available physical cores in the host.
+
+=head3 Example
+
+ >>> AVAILCPUS
+ <<< 1.0 200 4
+
+=head3 Description
+
+Returns the number of physical cores in the host that are available to
+the virtualization system (ie. have not been disabled). See also
+I<PHYSCPUS> which would in almost every case return the same number.
+
+=head2 CORESPERSOCKET
+
+ CORESPERSOCKET
+
+=head3 Returns
+
+Number of cores per socket in the physical host.
+
+=head3 Example
+
+ >>> CORESPERSOCKET
+ <<< 1.0 200 2
+
+=head3 Description
+
+Returns the number of physical cores per socket in the host.
+
+=head2 MEMORY
+
+ MEMORY
+
+=head3 Returns
+
+Amount of memory in host, in kilobytes.
+
+=head3 Example
+
+ >>> MEMORY
+ <<< 1.0 200 2097022
+
+=head3 Description
+
+Returns the total memory in the host, in kilobytes.
+
+=head2 MHZ
+
+ MHZ
+
+=head3 Returns
+
+Speed of host cores in MHz.
+
+=head3 Example
+
+ >>> MHZ
+ <<< 1.0 200 2047
+
+=head3 Description
+
+Returns the clockspeed of host cores in MHz.
+
+=head2 MODEL
+
+ MODEL
+
+=head3 Returns
+
+The host CPU model, a string such as C<i686> or C<x86_64>.
+
+=head3 Example
+
+ >>> MODEL
+ <<< 1.0 200 x86_64
+
+=head3 Description
+
+Returns the host CPU model.
+
+=head2 NODES
+
+ NODES
+
+=head3 Returns
+
+The number of NUMA nodes in the host.
+
+=head3 Example
+
+ >>> NODES
+ <<< 1.0 200 1
+
+=head3 Description
+
+Returns the number of NUMA nodes in the host. If this is 1
+then host memory access is uniform.
+
+=head2 PHYSCPUS
+
+ PHYSCPUS
+
+=head3 Returns
+
+The number of physical cores.
+
+=head3 Example
+
+ >>> PHYSCPUS
+ <<< 1.0 200 4
+
+=head3 Description
+
+Returns the number of physical cores available on the host.
+
+In some (highly unusual) situations, some cores might be
+disabled. To get the number of cores available to do work,
+use C<AVAILCPUS>.
+
+Note that it is common for the guest not to see all of the
+physical CPUs (virtual CPUs E<lt> physical CPUs).
+
=head2 PING
PING echodata
=back
+=head2 SOCKETSPERNODE
+
+ SOCKETSPERNODE
+
+=head3 Returns
+
+The number of sockets on each node.
+
+=head3 Example
+
+ >>> SOCKETSPERNODE
+ <<< 1.0 200 2
+
+=head3 Description
+
+Returns the number CPU sockets in each NUMA node.
+
+=head2 THREADSPERCORE
+
+ THREADSPERCORE
+
+=head3 Returns
+
+The number of hyperthreads per core.
+
+=head3 Example
+
+ >>> THREADSPERCORE
+ <<< 1.0 200 1
+
+=head3 Description
+
+If hyperthreading is enabled on the host, this returns
+the number of threads on each real core. The numbers
+returned by C<AVAILCPUS> and C<PHYSCPUS> are multiplied
+accordingly.
+
#include <apr_poll.h>
#include <apr_hash.h>
+#include <libvirt/libvirt.h>
+
enum guest_state {
guest_state_connecting, /* Connecting to socket. */
guest_state_request, /* Waiting or reading the request. */
/* Increments every time guest does something bad, decremented once per min */
unsigned penalty;
struct timespec last_penalty_decr;
+
+ /* Hash records last time each command was run by this guest. */
+ apr_hash_t *lasttime;
};
enum arg_type {
extern const char *conf_file;
extern char *socket_dir;
extern char *guests_file;
+extern char *libvirt_uri;
+extern int libvirt_uri_set_on_cmdline;
extern int verbose;
extern int verbose_set_on_cmdline;
extern int foreground;
extern int foreground_set_on_cmdline;
extern int messages_to_stderr;
extern apr_pool_t *pool; /* pool for global memory allocation */
-
extern void initialize (void);
+extern struct timespec *diff_timespec (struct timespec *r, const struct timespec *a, const struct timespec *b); /* r = a - b */
/* error.c */
extern void init_syslog (void);
/* configuration.c */
extern void read_main_conf_file (void);
+extern void check_guests_file (struct guest_description *hval, const char *cmd, double *interval, int *enabled);
/* monitor_sockets.c */
extern int sockets_inotify_fd;
apr_hash_set (commands, #name, APR_HASH_KEY_STRING, cb); \
}
+/* virt.c */
+extern void init_libvirt (void);
+extern virConnectPtr conn;
+extern virNodeInfo nodeinfo;
+
#endif /* HOSTINFOD_H */
char *socket_dir = NULL;
char *guests_file = NULL;
+char *libvirt_uri = NULL;
+int libvirt_uri_set_on_cmdline = 0;
+
int verbose = 0;
int verbose_set_on_cmdline = 0;
int foreground = 0;
" --help Display full usage\n"
" -c file | --config file\n"
" Configuration file (default: %s)\n"
+ " -C uri | --connect uri\n"
+ " Set libvirt connection URI (default: NULL)\n"
" -f | --foreground\n"
" Run in the foreground (don't fork)\n"
" -v Enable verbose messages (sent to syslog)\n",
{
static const apr_getopt_option_t options[] = {
{ "config", 'c', TRUE, "configuration file" },
+ { "connect", 'C', TRUE, "libvirt connection URI" },
{ "foreground", 'f', FALSE, "run in foreground (don't fork)" },
{ "verbose", 'v', FALSE, "enable verbose messages" },
{ "help", '?', FALSE, "display help" },
exit (1);
}
break;
+ case 'C':
+ libvirt_uri = optarg;
+ libvirt_uri_set_on_cmdline = 1;
+ break;
case 'f':
foreground = 1;
foreground_set_on_cmdline = 1;
/* Read the config file. */
read_main_conf_file ();
+ /* Connect to libvirt. */
+ init_libvirt ();
+
/* Monitor the socket directory. */
monitor_socket_dir ();
hval->sock = sock;
hval->request_max = 4096;
hval->request = apr_palloc (hval->pool, hval->request_max);
+ hval->lasttime = apr_hash_make (hval->pool);
/* Convert Unix fd into APR socket type. */
r = apr_os_sock_put (&hval->aprsock, &sock, hval->pool);
}
/* Difference between two timespec structures (r = a - b) */
-static struct timespec *
+struct timespec *
diff_timespec (struct timespec *r,
const struct timespec *a, const struct timespec *b)
{
--- /dev/null
+/* virt-hostinfo
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file makes available all entries in the libvirt NodeInfo
+ * structure.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <libvirt/libvirt.h>
+
+#include "hostinfod.h"
+
+static void
+model (struct guest_description *hval,
+ const char *cmd, apr_array_header_t *args)
+{
+ if (get_args (args, "") == -1) {
+ warning ("%s: %s: wrong number or type of arguments",
+ hval->name, __func__);
+ send_error (hval, 400);
+ return;
+ }
+
+ send_reply (hval, 200, "%s", nodeinfo.model);
+}
+REGISTER_COMMAND (model)
+
+static void
+memory (struct guest_description *hval,
+ const char *cmd, apr_array_header_t *args)
+{
+ if (get_args (args, "") == -1) {
+ warning ("%s: %s: wrong number or type of arguments",
+ hval->name, __func__);
+ send_error (hval, 400);
+ return;
+ }
+
+ send_reply (hval, 200, "%lu", nodeinfo.memory);
+}
+REGISTER_COMMAND (memory)
+
+static void
+availcpus (struct guest_description *hval,
+ const char *cmd, apr_array_header_t *args)
+{
+ if (get_args (args, "") == -1) {
+ warning ("%s: %s: wrong number or type of arguments",
+ hval->name, __func__);
+ send_error (hval, 400);
+ return;
+ }
+
+ send_reply (hval, 200, "%u", nodeinfo.cpus);
+}
+REGISTER_COMMAND (availcpus)
+
+static void
+physcpus (struct guest_description *hval,
+ const char *cmd, apr_array_header_t *args)
+{
+ if (get_args (args, "") == -1) {
+ warning ("%s: %s: wrong number or type of arguments",
+ hval->name, __func__);
+ send_error (hval, 400);
+ return;
+ }
+
+ send_reply (hval, 200, "%u", VIR_NODEINFO_MAXCPUS (nodeinfo));
+}
+REGISTER_COMMAND (physcpus)
+
+static void
+mhz (struct guest_description *hval,
+ const char *cmd, apr_array_header_t *args)
+{
+ if (get_args (args, "") == -1) {
+ warning ("%s: %s: wrong number or type of arguments",
+ hval->name, __func__);
+ send_error (hval, 400);
+ return;
+ }
+
+ send_reply (hval, 200, "%u", nodeinfo.mhz);
+}
+REGISTER_COMMAND (mhz)
+
+static void
+nodes (struct guest_description *hval,
+ const char *cmd, apr_array_header_t *args)
+{
+ if (get_args (args, "") == -1) {
+ warning ("%s: %s: wrong number or type of arguments",
+ hval->name, __func__);
+ send_error (hval, 400);
+ return;
+ }
+
+ send_reply (hval, 200, "%u", nodeinfo.nodes);
+}
+REGISTER_COMMAND (nodes)
+
+static void
+socketspernode (struct guest_description *hval,
+ const char *cmd, apr_array_header_t *args)
+{
+ if (get_args (args, "") == -1) {
+ warning ("%s: %s: wrong number or type of arguments",
+ hval->name, __func__);
+ send_error (hval, 400);
+ return;
+ }
+
+ send_reply (hval, 200, "%u", nodeinfo.sockets);
+}
+REGISTER_COMMAND (socketspernode)
+
+static void
+corespersocket (struct guest_description *hval,
+ const char *cmd, apr_array_header_t *args)
+{
+ if (get_args (args, "") == -1) {
+ warning ("%s: %s: wrong number or type of arguments",
+ hval->name, __func__);
+ send_error (hval, 400);
+ return;
+ }
+
+ send_reply (hval, 200, "%u", nodeinfo.cores);
+}
+REGISTER_COMMAND (corespersocket)
+
+static void
+threadspercore (struct guest_description *hval,
+ const char *cmd, apr_array_header_t *args)
+{
+ if (get_args (args, "") == -1) {
+ warning ("%s: %s: wrong number or type of arguments",
+ hval->name, __func__);
+ send_error (hval, 400);
+ return;
+ }
+
+ send_reply (hval, 200, "%u", nodeinfo.threads);
+}
+REGISTER_COMMAND (threadspercore)
size_t i, len;
if (get_args (args, "s", &echodata) == -1) {
- warning ("%s: %s: wrong number of type of arguments", hval->name, "ping");
+ warning ("%s: %s: wrong number or type of arguments", hval->name,
+ __func__);
send_error (hval, 400);
return;
}
--- /dev/null
+/* virt-hostinfo
+ * Copyright (C) 2009 Red Hat Inc.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <libvirt/libvirt.h>
+#include <libvirt/virterror.h>
+
+#include "hostinfod.h"
+
+virConnectPtr conn = NULL;
+virNodeInfo nodeinfo;
+
+void
+init_libvirt (void)
+{
+ virErrorPtr err;
+
+ conn = virConnectOpenReadOnly (libvirt_uri);
+ if (!conn) {
+ err = virGetLastError ();
+ error ("could not connect to libvirt (code %d, domain %d): %s",
+ err->code, err->domain, err->message);
+ exit (1);
+ }
+
+ if (virNodeGetInfo (conn, &nodeinfo) == -1) {
+ err = virConnGetLastError (conn);
+ error ("virGetNodeInfo failed (code %d, domain %d): %s",
+ err->code, err->domain, err->message);
+ exit (1);
+ }
+}