hostinfo-test/hostinfo-test.8
hostinfo-test/hostinfo-test
localconfigure
+local.conf
+local.guests.conf
install-sh
missing
stamp-h1
dnl Check support for 64 bit file offsets.
AC_SYS_LARGEFILE
+dnl C functions which may be missing on older systems.
+AC_CHECK_FUNCS([inotify_init1])
+
dnl Check for required packages using pkg-config.
PKG_CHECK_MODULES([HOSTINFOD],[apr-1 >= 1.3])
hostinfod_SOURCES = \
configuration.c \
+ error.c \
hostinfod.h \
- main.c
+ main.c \
+ monitor_sockets.c
hostinfod_CFLAGS = \
-Wall \
$(HOSTINFOD_CFLAGS) \
-DDEFAULT_CONF_FILE=\"$(sysconfdir)/hostinfo/hostinfo.conf\" \
+ -DDEFAULT_GUESTS_FILE=\"$(sysconfdir)/hostinfo/guests.conf\" \
-DDEFAULT_SOCKET_DIR=\"$(localstatedir)/lib/hostinfo\"
hostinfod_LDADD = $(HOSTINFOD_LIBS)
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include <apr_general.h>
+#include <apr_strings.h>
#include "hostinfod.h"
-typedef int (*process_line_fn) (const char *key, const char *value);
-static void process_conf_file (const char *path, int exit_if_not_exist, process_line_fn process_line);
+typedef int (*process_line_fn) (const char *path, int lineno,
+ const char *key, const char *value);
+typedef int (*process_section_fn) (const char *path, int lineno,
+ const char *section);
+static void process_conf_file (const char *path, int exit_if_not_exist,
+ process_line_fn process_line,
+ process_section_fn process_section);
+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);
-/* Read the main configuration file. This file is NOT reread if it
- * changes, but the auxiliary guests.conf file IS reread (and so is
- * the socket directory of course).
- */
void
read_main_conf_file (void)
{
- process_conf_file (conf_file, 0, NULL);
+ process_conf_file (conf_file, 0, main_process_line, NULL);
+}
+
+static int
+main_process_line (const char *path, int lineno,
+ const char *key, const char *value)
+{
+ int bool;
+
+ if (strcasecmp (key, "guests") == 0) {
+ if (!value) {
+ error ("%s:%d: directive is empty: %s", path, lineno, key);
+ return -1;
+ }
+ guests_file = apr_pstrdup (pool, value);
+ } else if (strcasecmp (key, "sockets") == 0) {
+ if (!value) {
+ error ("%s:%d: directive is empty: %s", path, lineno, key);
+ return -1;
+ }
+ socket_dir = apr_pstrdup (pool, value);
+ } else if (strcasecmp (key, "verbose") == 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;
+ }
+ if (!verbose_set_on_cmdline)
+ verbose = bool;
+ } else if (strcasecmp (key, "foreground") == 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;
+ }
+ if (!foreground_set_on_cmdline)
+ foreground = bool;
+ } else {
+ error ("%s:%d: unknown directive in configuration file: %s",
+ path, lineno, key);
+ return -1;
+ }
+ return 0;
}
+/* Configuration file parser. */
static void
process_conf_file (const char *path, int exit_if_not_exist,
- process_line_fn process_line)
+ process_line_fn process_line,
+ process_section_fn process_section)
{
static const char *whitespace = " \t\n\v";
FILE *fp;
char *line = NULL;
+ int lineno = 0;
size_t len = 0;
size_t real_len, key_len;
ssize_t r;
const char *key, *value;
+ debug ("begin processing configuration file %s", path);
+
fp = fopen (path, "r");
if (!fp) {
if (exit_if_not_exist) {
- perror (path);
+ perrorf ("%s", path);
exit (1);
- }
+ } else
+ pwarningf ("%s", path);
return;
}
while ((r = getline (&line, &len, fp)) != -1) {
+ lineno++;
+
/* Remove trailing \n */
real_len = len;
if (real_len > 0 && line[real_len-1] == '\n')
if (line[0] == '#' || strspn (line, whitespace) == real_len)
continue;
- /* Split into key value. */
- key_len = strcspn (line, whitespace);
- line[key_len] = '\0';
- key = line;
- value = key_len < real_len ? &line[key_len+1] : NULL;
- if (value) {
- value += strspn (line, whitespace);
- if (value[0] == '\0')
- value = NULL;
+ if (line[0] == '[') { /* Section. */
+ if (line[real_len-1] == ']')
+ line[--real_len] = '\0';
+ else {
+ error ("%s:%d: in section header, ']' not found (is there trailing whitespace or a comment?), near: %s",
+ path, lineno, line);
+ exit (1);
+ }
+
+ debug ("configuration file: section [%s]", line);
+
+ if (process_section && process_section (path, lineno, line) == -1)
+ exit (1);
}
+ else { /* Key value */
+ key_len = strcspn (line, whitespace);
+ line[key_len] = '\0';
+ key = line;
+ value = key_len < real_len ? &line[key_len+1] : NULL;
+ if (value) {
+ value += strspn (line, whitespace);
+ if (value[0] == '\0')
+ value = NULL;
+ }
- if (process_line && process_line (key, value) == -1)
- exit (1);
+ debug ("configuration file: key '%s', value '%s'", key, value);
+
+ if (process_line && process_line (path, lineno, key, value) == -1)
+ exit (1);
+ }
}
free (line);
if (ferror (fp)) {
- fprintf (stderr, "error reading configuration file: %s\n", path);
+ error ("%s: error reading configuration file", path);
exit (1);
}
if (fclose (fp) == EOF) {
- perror (path);
+ perrorf ("%s", path);
exit (1);
}
+
+ debug ("finished processing configuration file successfully");
+}
+
+static int
+get_bool (const char *str)
+{
+ if (!str)
+ return -1;
+
+ if (strcasecmp (str, "on") == 0)
+ return 1;
+ if (strcasecmp (str, "off") == 0)
+ return 0;
+
+ switch (str[0]) {
+ case '1': case 'y': case 'Y': case 't': case 'T': case 'e': case 'E':
+ return 1;
+ case '0': case 'n': case 'N': case 'f': case 'F': case 'd': case 'D':
+ return 0;
+ }
+
+ return -1;
}
--- /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 <stdarg.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#include <apr_general.h>
+#include <apr_errno.h>
+
+#include "hostinfod.h"
+
+void
+init_syslog (void)
+{
+ openlog ("hostinfo", 0, LOG_DAEMON);
+}
+
+static void
+print (int level, const char *msg)
+{
+ const char *errwarn;
+ switch (level) {
+ case LOG_WARNING: errwarn = "warning"; break;
+ case LOG_ERR: errwarn = "error"; break;
+ default: errwarn = "info";
+ }
+
+ if (messages_to_stderr)
+ fprintf (stderr, "hostinfo: %s: %s\n", errwarn, msg);
+ syslog (LOG_DAEMON|level, "%s: %s", errwarn, msg);
+}
+
+void
+debug (const char *fs, ...)
+{
+ va_list args;
+ char *msg;
+
+ if (verbose) {
+ va_start (args, fs);
+ int err = vasprintf (&msg, fs, args);
+ va_end (args);
+
+ if (err < 0) return;
+
+ print (LOG_INFO, msg);
+ free (msg);
+ }
+}
+
+void
+message (const char *fs, ...)
+{
+ va_list args;
+ char *msg;
+
+ va_start (args, fs);
+ int err = vasprintf (&msg, fs, args);
+ va_end (args);
+
+ if (err < 0) return;
+
+ print (LOG_INFO, msg);
+ free (msg);
+}
+
+void
+error (const char *fs, ...)
+{
+ va_list args;
+ char *msg;
+
+ va_start (args, fs);
+ int err = vasprintf (&msg, fs, args);
+ va_end (args);
+
+ if (err < 0) return;
+
+ print (LOG_ERR, msg);
+ free (msg);
+}
+
+void
+warning (const char *fs, ...)
+{
+ va_list args;
+ char *msg;
+
+ va_start (args, fs);
+ int err = vasprintf (&msg, fs, args);
+ va_end (args);
+
+ if (err < 0) return;
+
+ print (LOG_WARNING, msg);
+ free (msg);
+}
+
+void
+perrorf (const char *fs, ...)
+{
+ va_list args;
+ char *msg;
+ int errnum = errno;
+
+ va_start (args, fs);
+ int err = vasprintf (&msg, fs, args);
+ va_end (args);
+
+ if (err < 0) return;
+
+#ifndef _GNU_SOURCE
+ char buf[256];
+ strerror_r (errnum, buf, sizeof buf);
+#else
+ char _buf[256];
+ char *buf;
+ buf = strerror_r (errnum, _buf, sizeof _buf);
+#endif
+
+ msg = realloc (msg, strlen (msg) + 2 + strlen (buf) + 1);
+ strcat (msg, ": ");
+ strcat (msg, buf);
+
+ print (LOG_ERR, msg);
+ free (msg);
+}
+
+void
+pwarningf (const char *fs, ...)
+{
+ va_list args;
+ char *msg;
+ int errnum = errno;
+
+ va_start (args, fs);
+ int err = vasprintf (&msg, fs, args);
+ va_end (args);
+
+ if (err < 0) return;
+
+#ifndef _GNU_SOURCE
+ char buf[256];
+ strerror_r (errnum, buf, sizeof buf);
+#else
+ char _buf[256];
+ char *buf;
+ buf = strerror_r (errnum, _buf, sizeof _buf);
+#endif
+
+ msg = realloc (msg, strlen (msg) + 2 + strlen (buf) + 1);
+ strcat (msg, ": ");
+ strcat (msg, buf);
+
+ print (LOG_WARNING, msg);
+ free (msg);
+}
+
+void
+paprerror (apr_status_t r, const char *fs, ...)
+{
+ va_list args;
+ char *msg;
+ char buf[256];
+
+ va_start (args, fs);
+ int err = vasprintf (&msg, fs, args);
+ va_end (args);
+
+ if (err < 0) return;
+
+ apr_strerror (r, buf, sizeof buf);
+
+ msg = realloc (msg, strlen (msg) + 2 + strlen (buf) + 1);
+ strcat (msg, ": ");
+ strcat (msg, buf);
+
+ print (LOG_ERR, msg);
+ free (msg);
+}
=item *
-explicitly enable the hostinfo feature,
+explicitly enable the hostinfo feature (see below),
=item *
-select which virtual machines can see any data,
+select which virtual machines can see any data (see L<hostinfo-set(8)>),
=item *
-select which data they wish to disclose to virtual machines, and
+select which data they wish to disclose to virtual machines
+(C<guests.conf> below), and
=item *
-select the frequency that virtual machines can ask for data.
+select the frequency that virtual machines can ask for data
+(C<guests.conf>).
=back
Edits to this file take effect as soon as the next request is received
from any guest. You do not need to restart the daemon.
-B<However if this file contains any mistakes, then the daemon will
+However B<if this file contains any mistakes, then the daemon will
exit> (ie. the default becomes no access). The reason for the error
will be sent to a system log file. So after editing this file, you
should check that hostinfo is available end-to-end (ie. from a guest),
=item /etc/hostinfo/hostinfo.conf
-
=item /etc/hostinfo/guests.conf
-
=item /var/lib/hostinfo/
-
-
=back
=head1 SEE ALSO
#define HOSTINFOD_H
#include <apr_general.h>
+#include <apr_errno.h>
/* main.c */
extern const char *conf_file;
-extern const char *socket_dir;
+extern char *socket_dir;
extern char *guests_file;
-extern int socket_dir_set_on_cmdline;
-extern int debug;
-extern int debug_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 */
+/* error.c */
+extern void init_syslog (void);
+extern void error (const char *fs, ...)
+ __attribute__((format (printf,1,2)));
+extern void perrorf (const char *fs, ...)
+ __attribute__((format (printf,1,2)));
+extern void warning (const char *fs, ...)
+ __attribute__((format (printf,1,2)));
+extern void pwarningf (const char *fs, ...)
+ __attribute__((format (printf,1,2)));
+extern void debug (const char *fs, ...)
+ __attribute__((format (printf,1,2)));
+extern void message (const char *fs, ...)
+ __attribute__((format (printf,1,2)));
+extern void paprerror (apr_status_t r, const char *fs, ...)
+ __attribute__((format (printf,2,3)));
+
/* configuration.c */
extern void read_main_conf_file (void);
+/* monitor_sockets.c */
+extern int sockets_inotify_fd;
+extern void monitor_socket_dir (void);
+
#endif /* HOSTINFOD_H */
#include <apr_general.h>
#include <apr_network_io.h>
#include <apr_getopt.h>
+#include <apr_strings.h>
+#include <apr_thread_proc.h>
+#include <apr_poll.h>
+#include <apr_portable.h>
#include "hostinfod.h"
+static void main_loop (void);
+static void set_reread_socket_dir (const apr_pollfd_t *);
+static void do_reread_socket_dir (void);
+
+typedef void (*poll_callback) (const apr_pollfd_t *);
+
const char *conf_file = DEFAULT_CONF_FILE;
-const char *socket_dir = DEFAULT_SOCKET_DIR;
+char *socket_dir = NULL;
char *guests_file = NULL;
-int socket_dir_set_on_cmdline = 0;
-int debug = 0;
-int debug_set_on_cmdline = 0;
+
int verbose = 0;
int verbose_set_on_cmdline = 0;
int foreground = 0;
int foreground_set_on_cmdline = 0;
+
+int messages_to_stderr = 1;
+
+static int reread_socket_dir = 1;
+static int quit = 0;
+
apr_pool_t *pool = NULL;
+static apr_pollset_t *set;
static void
usage (void)
" --help Display full usage\n"
" -c file | --config file\n"
" Configuration file (default: %s)\n"
- " -d | --debug Enable debug messages to stderr (implies -v)\n"
" -f | --foreground\n"
" Run in the foreground (don't fork)\n"
- " -s dir | --socketdir dir\n"
- " Socket directory (default: %s)\n"
" -v Enable verbose messages (sent to syslog, and to\n"
" stderr if -d option is given)\n",
- DEFAULT_CONF_FILE, DEFAULT_SOCKET_DIR);
+ DEFAULT_CONF_FILE);
}
int
{
static const apr_getopt_option_t options[] = {
{ "config", 'c', TRUE, "configuration file" },
- { "debug", 'd', FALSE, "enable debug messages to stderr" },
{ "foreground", 'f', FALSE, "run in foreground (don't fork)" },
- { "socketdir", 's', TRUE, "socket directory" },
{ "verbose", 'v', FALSE, "enable verbose messages" },
{ "help", '?', FALSE, "display help" },
{ NULL, 0, 0, NULL },
apr_getopt_init (&opt, pool, argc, argv);
+ init_syslog ();
+
+ socket_dir = apr_pstrdup (pool, DEFAULT_SOCKET_DIR);
+ guests_file = apr_pstrdup (pool, DEFAULT_GUESTS_FILE);
+
+ /* Command line. */
while ((r = apr_getopt_long (opt, options, &c, &optarg)) == APR_SUCCESS) {
switch (c) {
case 'c':
* it should exist. They may have typo'd the name.
*/
if (access (conf_file, R_OK) == -1) {
- perror (conf_file);
+ perrorf ("%s", conf_file);
exit (1);
}
break;
- case 'd':
- debug = verbose = 1;
- debug_set_on_cmdline = verbose_set_on_cmdline = 1;
- break;
case 'f':
foreground = 1;
foreground_set_on_cmdline = 1;
break;
- case 's':
- socket_dir = optarg;
- socket_dir_set_on_cmdline = 1;
- break;
case 'v':
verbose = 1;
verbose_set_on_cmdline = 1;
exit (1);
}
+ /* Read the config file. */
read_main_conf_file ();
+ /* Monitor the socket directory. */
+ monitor_socket_dir ();
-
-
-
-
-
+ /* Create the initial pollset, just containing inotify socket. */
+ r = apr_pollset_create (&set, 1024 /* ? */, pool, 0);
+ if (r != APR_SUCCESS) {
+ paprerror (r, "apr_pollset_create");
+ exit (1);
+ }
+ apr_socket_t *tsock;
+ r = apr_os_sock_put (&tsock, &sockets_inotify_fd, pool);
+ if (r != APR_SUCCESS) {
+ paprerror (r, "apr_os_sock_put");
+ exit (1);
+ }
+ apr_pollfd_t *tpollfd = apr_palloc (pool, sizeof *tpollfd);
+ tpollfd->p = pool;
+ tpollfd->desc_type = APR_POLL_SOCKET;
+ tpollfd->reqevents = APR_POLLIN;
+ tpollfd->rtnevents = 0;
+ tpollfd->desc.s = tsock;
+ tpollfd->client_data = set_reread_socket_dir;
+ r = apr_pollset_add (set, tpollfd);
+ if (r != APR_SUCCESS) {
+ paprerror (r, "apr_pollset_add");
+ exit (1);
+ }
/* Daemonize. */
chdir ("/");
- if (!foreground)
+ if (!foreground) {
apr_proc_detach (1);
+ /* After we detach from the terminal, all further messages
+ * should just go to syslog.
+ */
+ messages_to_stderr = 0;
+ }
+ message (PACKAGE_STRING);
+ main_loop ();
apr_terminate ();
return 0;
}
+
+static void
+main_loop (void)
+{
+ apr_status_t r;
+ apr_int32_t numdescs;
+ const apr_pollfd_t *descs;
+ int i;
+
+ while (!quit) {
+ /* A socket has appeared or disappeared from the socket directory. */
+ if (reread_socket_dir) {
+ do_reread_socket_dir ();
+ reread_socket_dir = 0;
+ }
+
+ /* Poll. */
+ r = apr_pollset_poll (set, 0, &numdescs, &descs);
+ if (r != APR_SUCCESS) {
+ paprerror (r, "apr_pollset_poll");
+ exit (1);
+ }
+
+ /* Perform the callbacks. */
+ for (i = 0; i < numdescs; ++i) {
+ poll_callback callback;
+
+ callback = descs[i].client_data;
+ callback (&descs[i]);
+ }
+ }
+}
+
+static void
+set_reread_socket_dir (const apr_pollfd_t *_)
+{
+ reread_socket_dir = 1;
+}
+
+static void
+do_reread_socket_dir (void)
+{
+ char buf[256];
+
+ debug ("reading socket directory");
+
+ /* Discard anything which appears on the inotify socket. We will
+ * reread the whole directory each time.
+ */
+ while (read (sockets_inotify_fd, buf, sizeof buf) > 0)
+ ;
+
+
+
+
+
+
+
+
+
+
+
+}
--- /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 <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/inotify.h>
+#include <sys/stat.h>
+
+#include <apr_general.h>
+
+#include "hostinfod.h"
+
+int sockets_inotify_fd = -1;
+
+void
+monitor_socket_dir (void)
+{
+ struct stat statbuf;
+
+ if (!socket_dir) {
+ error ("no socket directory was configured - see hostinfo(8)");
+ exit (1);
+ }
+
+ if (stat (socket_dir, &statbuf) == -1) {
+ perrorf ("%s (socket directory): does not exist or is not accessible",
+ socket_dir);
+ exit (1);
+ }
+ if (!S_ISDIR (statbuf.st_mode)) {
+ error ("%s (socket directory): not a directory", socket_dir);
+ exit (1);
+ }
+
+#ifdef HAVE_INOTIFY_INIT1
+ sockets_inotify_fd = inotify_init1 (IN_NONBLOCK | IN_CLOEXEC);
+ if (sockets_inotify_fd == -1) {
+ perrorf ("inotify_init");
+ exit (1);
+ }
+#else
+ sockets_inotify_fd = inotify_init ();
+ if (sockets_inotify_fd == -1) {
+ perrorf ("inotify_init");
+ exit (1);
+ }
+ if (fcntl (sockets_inotify_fd, F_SETFL, O_NONBLOCK) == -1) {
+ perrorf ("fcntl: O_NONBLOCK");
+ exit (1);
+ }
+ if (fcntl (sockets_inotify_fd, F_SETFD, FD_CLOEXEC) == -1) {
+ perrorf ("fcntl: FD_CLOEXEC");
+ exit (1);
+ }
+#endif
+
+ if (inotify_add_watch (sockets_inotify_fd, socket_dir,
+ IN_CREATE|IN_DELETE|IN_MOVE) == -1) {
+ perrorf ("inotify_add_watch: %s", socket_dir);
+ exit (1);
+ }
+
+ debug ("inotify of %s successful, fd = %d",
+ socket_dir, sockets_inotify_fd);
+}