This patch is preparing the way for future work on allowing the libvirtd
daemon to run as a less-privileged user ID. The idea is that we will
switch from 'root' to 'libvirtd', but use Linux capabilties to keep the
handful of higher privileges we need for our work. Thus any code which
does a check of 'getuid() == 0' is guarenteed to break [1].
The way this patch approaches this problem, is to change the driver
initialization function virStateInitialize() to have it be passed in a
'int privileged' flag from the libvirtd daemon. Each driver is updated
to record this flag, and use it for checks where needed. The only real
exception is the Xen driver, where we simply check access(2) against
the file we need to open.
[1] Technically this is already broken on Solaris, since they already
change user id
qemud/qemud.c | 39 ++++++++++++++++++++++-----------------
qemud/qemud.h | 2 ++
src/driver.h | 2 +-
src/libvirt.c | 4 ++--
src/libvirt_internal.h | 2 +-
src/lxc_driver.c | 25 +++----------------------
src/network_driver.c | 4 ++--
src/node_device_devkit.c | 2 +-
src/node_device_hal.c | 2 +-
src/qemu_conf.h | 2 ++
src/qemu_driver.c | 37 +++++++++++++++++++------------------
src/remote_internal.c | 2 +-
src/storage_driver.c | 6 +++---
src/uml_conf.h | 2 ++
src/uml_driver.c | 18 +++++++++---------
src/xen_internal.c | 2 +-
src/xen_unified.c | 2 +-
17 files changed, 73 insertions(+), 80 deletions(-)
Daniel
diff -r 5e3b5d1f91c2 qemud/qemud.c
--- a/qemud/qemud.c Thu May 21 16:21:20 2009 +0100
+++ b/qemud/qemud.c Thu May 21 16:27:16 2009 +0100
@@ -112,8 +112,6 @@ static int unix_sock_ro_mask = 0666;
#else
-#define SYSTEM_UID 0
-
static gid_t unix_sock_gid = 0; /* Only root by default */
static int unix_sock_rw_mask = 0700; /* Allow user only */
static int unix_sock_ro_mask = 0777; /* Allow world */
@@ -512,7 +510,7 @@ static int qemudListenUnix(struct qemud_
oldgrp = getgid();
oldmask = umask(readonly ? ~unix_sock_ro_mask : ~unix_sock_rw_mask);
- if (getuid() == 0)
+ if (server->privileged)
setgid(unix_sock_gid);
if (bind(sock->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
@@ -521,7 +519,7 @@ static int qemudListenUnix(struct qemud_
goto cleanup;
}
umask(oldmask);
- if (getuid() == 0)
+ if (server->privileged)
setgid(oldgrp);
if (listen(sock->fd, 30) < 0) {
@@ -696,7 +694,6 @@ static int qemudInitPaths(struct qemud_s
char *roSockname,
int maxlen)
{
- uid_t uid = geteuid();
char *sock_dir;
char *dir_prefix = NULL;
int ret = -1;
@@ -706,7 +703,7 @@ static int qemudInitPaths(struct qemud_s
sock_dir = unix_sock_dir;
else {
sock_dir = sockname;
- if (uid == SYSTEM_UID) {
+ if (server->privileged) {
dir_prefix = strdup (LOCAL_STATE_DIR);
if (dir_prefix == NULL) {
virReportOOMError(NULL);
@@ -716,6 +713,7 @@ static int qemudInitPaths(struct qemud_s
dir_prefix) >= maxlen)
goto snprintf_error;
} else {
+ uid_t uid = geteuid();
dir_prefix = virGetUserDirectory(NULL, uid);
if (dir_prefix == NULL) {
/* Do not diagnose here; virGetUserDirectory does that. */
@@ -733,7 +731,7 @@ static int qemudInitPaths(struct qemud_s
goto cleanup;
}
- if (uid == SYSTEM_UID) {
+ if (server->privileged) {
if (snprintf (sockname, maxlen, "%s/libvirt-sock",
sock_dir_prefix) >= maxlen
|| (snprintf (roSockname, maxlen, "%s/libvirt-sock-ro",
@@ -747,10 +745,10 @@ static int qemudInitPaths(struct qemud_s
goto snprintf_error;
}
- if (uid == SYSTEM_UID)
- server->logDir = strdup (LOCAL_STATE_DIR "/log/libvirt");
+ if (server->privileged)
+ server->logDir = strdup (LOCAL_STATE_DIR "/log/libvirt");
else
- virAsprintf(&server->logDir, "%s/.libvirt/log", dir_prefix);
+ virAsprintf(&server->logDir, "%s/.libvirt/log", dir_prefix);
if (server->logDir == NULL)
virReportOOMError(NULL);
@@ -786,6 +784,7 @@ static struct qemud_server *qemudInitial
VIR_FREE(server);
}
+ server->privileged = getuid() == 0 ? 1 : 0;
server->sigread = sigread;
if (virEventInit() < 0) {
@@ -844,7 +843,7 @@ static struct qemud_server *qemudInitial
virEventUpdateTimeoutImpl,
virEventRemoveTimeoutImpl);
- virStateInitialize();
+ virStateInitialize(server->privileged);
return server;
}
@@ -915,7 +914,7 @@ static struct qemud_server *qemudNetwork
}
#ifdef HAVE_AVAHI
- if (getuid() == 0 && mdns_adv) {
+ if (server->privileged && mdns_adv) {
struct libvirtd_mdns_group *group;
int port = 0;
@@ -2507,9 +2506,9 @@ remoteReadConfigFile (struct qemud_serve
#if HAVE_POLKIT
/* Change the default back to no auth for non-root */
- if (getuid() != 0 && auth_unix_rw == REMOTE_AUTH_POLKIT)
+ if (!server->privileged && auth_unix_rw == REMOTE_AUTH_POLKIT)
auth_unix_rw = REMOTE_AUTH_NONE;
- if (getuid() != 0 && auth_unix_ro == REMOTE_AUTH_POLKIT)
+ if (!server->privileged && auth_unix_ro == REMOTE_AUTH_POLKIT)
auth_unix_ro = REMOTE_AUTH_NONE;
#endif
@@ -2546,7 +2545,7 @@ remoteReadConfigFile (struct qemud_serve
GET_CONF_STR (conf, filename, unix_sock_group);
if (unix_sock_group) {
- if (getuid() != 0) {
+ if (!server->privileged) {
VIR_WARN0(_("Cannot set group when not running as root"));
} else {
int ret;
@@ -2871,7 +2870,7 @@ int main(int argc, char **argv) {
sigaction(SIGPIPE, &sig_action, NULL);
/* Ensure the rundir exists (on tmpfs on some systems) */
- if (geteuid () == 0) {
+ if (getuid() == 0) {
const char *rundir = LOCAL_STATE_DIR "/run/libvirt";
if (mkdir (rundir, 0755)) {
@@ -2882,6 +2881,12 @@ int main(int argc, char **argv) {
}
}
+ /* Beyond this point, nothing should rely on using
+ * getuid/geteuid() == 0, for privilege level checks.
+ * It must all use the flag 'server->privileged'
+ * which is also passed into all libvirt stateful
+ * drivers
+ */
if (qemudSetupPrivs() < 0)
goto error2;
@@ -2895,7 +2900,7 @@ int main(int argc, char **argv) {
goto error2;
/* Change the group ownership of /var/run/libvirt to unix_sock_gid */
- if (unix_sock_dir && geteuid() == 0) {
+ if (unix_sock_dir && server->privileged) {
if (chown(unix_sock_dir, -1, unix_sock_gid) < 0)
VIR_ERROR(_("Failed to change group ownership of %s"),
unix_sock_dir);
diff -r 5e3b5d1f91c2 qemud/qemud.h
--- a/qemud/qemud.h Thu May 21 16:21:20 2009 +0100
+++ b/qemud/qemud.h Thu May 21 16:27:16 2009 +0100
@@ -172,6 +172,8 @@ struct qemud_server {
virMutex lock;
virCond job;
+ int privileged;
+
int nworkers;
int nactiveworkers;
struct qemud_worker *workers;
diff -r 5e3b5d1f91c2 src/driver.h
--- a/src/driver.h Thu May 21 16:21:20 2009 +0100
+++ b/src/driver.h Thu May 21 16:27:16 2009 +0100
@@ -717,7 +717,7 @@ struct _virStorageDriver {
};
#ifdef WITH_LIBVIRTD
-typedef int (*virDrvStateInitialize) (void);
+typedef int (*virDrvStateInitialize) (int privileged);
typedef int (*virDrvStateCleanup) (void);
typedef int (*virDrvStateReload) (void);
typedef int (*virDrvStateActive) (void);
diff -r 5e3b5d1f91c2 src/libvirt.c
--- a/src/libvirt.c Thu May 21 16:21:20 2009 +0100
+++ b/src/libvirt.c Thu May 21 16:27:16 2009 +0100
@@ -765,7 +765,7 @@ virRegisterStateDriver(virStateDriverPtr
*
* Return 0 if all succeed, -1 upon any failure.
*/
-int virStateInitialize(void) {
+int virStateInitialize(int privileged) {
int i, ret = 0;
if (virInitialize() < 0)
@@ -773,7 +773,7 @@ int virStateInitialize(void) {
for (i = 0 ; i < virStateDriverTabCount ; i++) {
if (virStateDriverTab[i]->initialize &&
- virStateDriverTab[i]->initialize() < 0)
+ virStateDriverTab[i]->initialize(privileged) < 0)
ret = -1;
}
return ret;
diff -r 5e3b5d1f91c2 src/libvirt_internal.h
--- a/src/libvirt_internal.h Thu May 21 16:21:20 2009 +0100
+++ b/src/libvirt_internal.h Thu May 21 16:27:16 2009 +0100
@@ -26,7 +26,7 @@
#ifdef WITH_LIBVIRTD
-int virStateInitialize(void);
+int virStateInitialize(int privileged);
int virStateCleanup(void);
int virStateReload(void);
int virStateActive(void);
diff -r 5e3b5d1f91c2 src/lxc_driver.c
--- a/src/lxc_driver.c Thu May 21 16:21:20 2009 +0100
+++ b/src/lxc_driver.c Thu May 21 16:27:16 2009 +0100
@@ -51,7 +51,7 @@
#define VIR_FROM_THIS VIR_FROM_LXC
-static int lxcStartup(void);
+static int lxcStartup(int privileged);
static int lxcShutdown(void);
static lxc_driver_t *lxc_driver = NULL;
@@ -67,15 +67,6 @@ static void lxcDriverUnlock(lxc_driver_t
}
-static int lxcProbe(void)
-{
- if (getuid() != 0 ||
- lxcContainerAvailable(0) < 0)
- return 0;
-
- return 1;
-}
-
static virDrvOpenStatus lxcOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED)
@@ -86,9 +77,6 @@ static virDrvOpenStatus lxcOpen(virConne
if (lxc_driver == NULL)
return VIR_DRV_OPEN_DECLINED;
- if (!lxcProbe())
- return VIR_DRV_OPEN_DECLINED;
-
conn->uri = xmlParseURI("lxc:///");
if (!conn->uri) {
virReportOOMError(conn);
@@ -115,12 +103,6 @@ static virDrvOpenStatus lxcOpen(virConne
_("lxc state driver is not active"));
return VIR_DRV_OPEN_ERROR;
}
-
- if (!lxcProbe()) {
- lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
- _("kernel does not support LXC feature"));
- return VIR_DRV_OPEN_ERROR;
- }
}
conn->privateData = lxc_driver;
@@ -1149,9 +1131,8 @@ static int lxcCheckNetNsSupport(void)
return 1;
}
-static int lxcStartup(void)
+static int lxcStartup(int privileged)
{
- uid_t uid = getuid();
unsigned int i;
char *ld;
@@ -1164,7 +1145,7 @@ static int lxcStartup(void)
return -1;
/* Check that the user is root */
- if (0 != uid) {
+ if (!privileged) {
return -1;
}
diff -r 5e3b5d1f91c2 src/network_driver.c
--- a/src/network_driver.c Thu May 21 16:21:20 2009 +0100
+++ b/src/network_driver.c Thu May 21 16:27:16 2009 +0100
@@ -182,7 +182,7 @@ networkAutostartConfigs(struct network_d
* Initialization function for the QEmu daemon
*/
static int
-networkStartup(void) {
+networkStartup(int privileged) {
uid_t uid = geteuid();
char *base = NULL;
int err;
@@ -196,7 +196,7 @@ networkStartup(void) {
}
networkDriverLock(driverState);
- if (!uid) {
+ if (privileged) {
if (virAsprintf(&driverState->logDir,
"%s/log/libvirt/qemu", LOCAL_STATE_DIR) == -1)
goto out_of_memory;
diff -r 5e3b5d1f91c2 src/node_device_devkit.c
--- a/src/node_device_devkit.c Thu May 21 16:21:20 2009 +0100
+++ b/src/node_device_devkit.c Thu May 21 16:27:16 2009 +0100
@@ -284,7 +284,7 @@ static void dev_create(void *_dkdev, voi
}
-static int devkitDeviceMonitorStartup(void)
+static int devkitDeviceMonitorStartup(int privileged ATTRIBUTE_UNUSED)
{
size_t caps_tbl_len = sizeof(caps_tbl) / sizeof(caps_tbl[0]);
DevkitClient *devkit_client = NULL;
diff -r 5e3b5d1f91c2 src/node_device_hal.c
--- a/src/node_device_hal.c Thu May 21 16:21:20 2009 +0100
+++ b/src/node_device_hal.c Thu May 21 16:27:16 2009 +0100
@@ -661,7 +661,7 @@ static void toggle_dbus_watch(DBusWatch
}
-static int halDeviceMonitorStartup(void)
+static int halDeviceMonitorStartup(int privileged ATTRIBUTE_UNUSED)
{
LibHalContext *hal_ctx = NULL;
DBusConnection *dbus_conn = NULL;
diff -r 5e3b5d1f91c2 src/qemu_conf.h
--- a/src/qemu_conf.h Thu May 21 16:21:20 2009 +0100
+++ b/src/qemu_conf.h Thu May 21 16:27:16 2009 +0100
@@ -62,6 +62,8 @@ enum qemud_cmd_flags {
struct qemud_driver {
virMutex lock;
+ int privileged;
+
unsigned int qemuVersion;
int nextvmid;
diff -r 5e3b5d1f91c2 src/qemu_driver.c
--- a/src/qemu_driver.c Thu May 21 16:21:20 2009 +0100
+++ b/src/qemu_driver.c Thu May 21 16:27:16 2009 +0100
@@ -130,24 +130,26 @@ static struct qemud_driver *qemu_driver
static int
-qemudLogFD(virConnectPtr conn, const char* logDir, const char* name)
+qemudLogFD(virConnectPtr conn, struct qemud_driver *driver, const char* name)
{
char logfile[PATH_MAX];
mode_t logmode;
- uid_t uid = geteuid();
int ret, fd = -1;
- if ((ret = snprintf(logfile, sizeof(logfile), "%s/%s.log", logDir, name))
+ if ((ret = snprintf(logfile, sizeof(logfile), "%s/%s.log",
+ driver->logDir, name))
< 0 || ret >= sizeof(logfile)) {
virReportOOMError(conn);
return -1;
}
logmode = O_CREAT | O_WRONLY;
- if (uid != 0)
+ /* Only logrotate files in /var/log, so only append if running privileged */
+ if (driver->privileged)
+ logmode |= O_APPEND;
+ else
logmode |= O_TRUNC;
- else
- logmode |= O_APPEND;
+
if ((fd = open(logfile, logmode, S_IRUSR | S_IWUSR)) < 0) {
virReportSystemError(conn, errno,
_("failed to create logfile %s"),
@@ -210,9 +212,9 @@ qemudAutostartConfigs(struct qemud_drive
* to lookup the bridge associated with a virtual
* network
*/
- virConnectPtr conn = virConnectOpen(getuid() ?
- "qemu:///session" :
- "qemu:///system");
+ virConnectPtr conn = virConnectOpen(driver->privileged ?
+ "qemu:///system" :
+ "qemu:///session");
/* Ignoring NULL conn which is mostly harmless here */
qemuDriverLock(driver);
@@ -333,7 +335,7 @@ qemudReconnectVMs(struct qemud_driver *d
goto next_error;
}
- if ((vm->logfile = qemudLogFD(NULL, driver->logDir, vm->def->name))
< 0)
+ if ((vm->logfile = qemudLogFD(NULL, driver, vm->def->name)) < 0)
goto next_error;
if (vm->def->id >= driver->nextvmid)
@@ -420,8 +422,7 @@ qemudSecurityInit(struct qemud_driver *q
* Initialization function for the QEmu daemon
*/
static int
-qemudStartup(void) {
- uid_t uid = geteuid();
+qemudStartup(int privileged) {
char *base = NULL;
char driverConf[PATH_MAX];
@@ -434,6 +435,7 @@ qemudStartup(void) {
return -1;
}
qemuDriverLock(qemu_driver);
+ qemu_driver->privileged = privileged;
/* Don't have a dom0 so start from 1 */
qemu_driver->nextvmid = 1;
@@ -448,7 +450,7 @@ qemudStartup(void) {
virEventAddTimeout(-1, qemuDomainEventFlush, qemu_driver, NULL)) < 0)
goto error;
- if (!uid) {
+ if (privileged) {
if (virAsprintf(&qemu_driver->logDir,
"%s/log/libvirt/qemu", LOCAL_STATE_DIR) == -1)
goto out_of_memory;
@@ -460,6 +462,7 @@ qemudStartup(void) {
"%s/run/libvirt/qemu/", LOCAL_STATE_DIR) == -1)
goto out_of_memory;
} else {
+ uid_t uid = geteuid();
char *userdir = virGetUserDirectory(NULL, uid);
if (!userdir)
goto error;
@@ -1372,7 +1375,7 @@ static int qemudStartVMDaemon(virConnect
goto cleanup;
}
- if((vm->logfile = qemudLogFD(conn, driver->logDir, vm->def->name)) <
0)
+ if ((vm->logfile = qemudLogFD(conn, driver, vm->def->name)) < 0)
goto cleanup;
emulator = vm->def->emulator;
@@ -1773,8 +1776,6 @@ static int qemudProbe(void)
static virDrvOpenStatus qemudOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED) {
- uid_t uid = getuid();
-
if (conn->uri == NULL) {
if (qemu_driver == NULL)
return VIR_DRV_OPEN_DECLINED;
@@ -1782,7 +1783,7 @@ static virDrvOpenStatus qemudOpen(virCon
if (!qemudProbe())
return VIR_DRV_OPEN_DECLINED;
- conn->uri = xmlParseURI(uid == 0 ?
+ conn->uri = xmlParseURI(qemu_driver->privileged ?
"qemu:///system" :
"qemu:///session");
if (!conn->uri) {
@@ -1805,7 +1806,7 @@ static virDrvOpenStatus qemudOpen(virCon
return VIR_DRV_OPEN_ERROR;
}
- if (!uid) {
+ if (qemu_driver->privileged) {
if (STRNEQ (conn->uri->path, "/system") &&
STRNEQ (conn->uri->path, "/session")) {
qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
diff -r 5e3b5d1f91c2 src/remote_internal.c
--- a/src/remote_internal.c Thu May 21 16:21:20 2009 +0100
+++ b/src/remote_internal.c Thu May 21 16:27:16 2009 +0100
@@ -235,7 +235,7 @@ static gnutls_session_t negotiate_gnutls
#ifdef WITH_LIBVIRTD
static int
-remoteStartup(void)
+remoteStartup(int privileged ATTRIBUTE_UNUSED)
{
/* Mark that we're inside the daemon so we can avoid
* re-entering ourselves
diff -r 5e3b5d1f91c2 src/storage_driver.c
--- a/src/storage_driver.c Thu May 21 16:21:20 2009 +0100
+++ b/src/storage_driver.c Thu May 21 16:27:16 2009 +0100
@@ -106,8 +106,7 @@ storageDriverAutostart(virStorageDriverS
* Initialization function for the QEmu daemon
*/
static int
-storageDriverStartup(void) {
- uid_t uid = geteuid();
+storageDriverStartup(int privileged) {
char *base = NULL;
char driverConf[PATH_MAX];
@@ -120,10 +119,11 @@ storageDriverStartup(void) {
}
storageDriverLock(driverState);
- if (!uid) {
+ if (privileged) {
if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL)
goto out_of_memory;
} else {
+ uid_t uid = geteuid();
char *userdir = virGetUserDirectory(NULL, uid);
if (!userdir)
diff -r 5e3b5d1f91c2 src/uml_conf.h
--- a/src/uml_conf.h Thu May 21 16:21:20 2009 +0100
+++ b/src/uml_conf.h Thu May 21 16:27:16 2009 +0100
@@ -42,6 +42,8 @@
struct uml_driver {
virMutex lock;
+ int privileged;
+
unsigned int umlVersion;
int nextvmid;
diff -r 5e3b5d1f91c2 src/uml_driver.c
--- a/src/uml_driver.c Thu May 21 16:21:20 2009 +0100
+++ b/src/uml_driver.c Thu May 21 16:27:16 2009 +0100
@@ -131,9 +131,9 @@ umlAutostartConfigs(struct uml_driver *d
* to lookup the bridge associated with a virtual
* network
*/
- virConnectPtr conn = virConnectOpen(getuid() ?
- "uml:///session" :
- "uml:///system");
+ virConnectPtr conn = virConnectOpen(driver->privileged ?
+ "uml:///system" :
+ "uml:///session");
/* Ignoring NULL conn which is mostly harmless here */
for (i = 0 ; i < driver->domains.count ; i++) {
@@ -308,7 +308,7 @@ cleanup:
* Initialization function for the Uml daemon
*/
static int
-umlStartup(void) {
+umlStartup(int privileged) {
uid_t uid = geteuid();
char *base = NULL;
char driverConf[PATH_MAX];
@@ -317,6 +317,8 @@ umlStartup(void) {
if (VIR_ALLOC(uml_driver) < 0)
return -1;
+ uml_driver->privileged = privileged;
+
if (virMutexInit(¨_driver->lock) < 0) {
VIR_FREE(uml_driver);
return -1;
@@ -331,7 +333,7 @@ umlStartup(void) {
if (!userdir)
goto error;
- if (!uid) {
+ if (privileged) {
if (virAsprintf(¨_driver->logDir,
"%s/log/libvirt/uml", LOCAL_STATE_DIR) == -1)
goto out_of_memory;
@@ -887,13 +889,11 @@ static void umlShutdownVMDaemon(virConne
static virDrvOpenStatus umlOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED) {
- uid_t uid = getuid();
-
if (conn->uri == NULL) {
if (uml_driver == NULL)
return VIR_DRV_OPEN_DECLINED;
- conn->uri = xmlParseURI(uid == 0 ?
+ conn->uri = xmlParseURI(uml_driver->privileged ?
"uml:///system" :
"uml:///session");
if (!conn->uri) {
@@ -915,7 +915,7 @@ static virDrvOpenStatus umlOpen(virConne
return VIR_DRV_OPEN_ERROR;
}
- if (uid == 0) {
+ if (uml_driver->privileged) {
if (STRNEQ (conn->uri->path, "/system") &&
STRNEQ (conn->uri->path, "/session")) {
umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
diff -r 5e3b5d1f91c2 src/xen_internal.c
--- a/src/xen_internal.c Thu May 21 16:21:20 2009 +0100
+++ b/src/xen_internal.c Thu May 21 16:27:16 2009 +0100
@@ -3434,6 +3434,6 @@ xenHavePrivilege()
#ifdef __sun
return priv_ineffect (PRIV_XVM_CONTROL);
#else
- return getuid () == 0;
+ return access(XEN_HYPERVISOR_SOCKET, R_OK) == 0;
#endif
}
diff -r 5e3b5d1f91c2 src/xen_unified.c
--- a/src/xen_unified.c Thu May 21 16:21:20 2009 +0100
+++ b/src/xen_unified.c Thu May 21 16:27:16 2009 +0100
@@ -175,7 +175,7 @@ done:
#ifdef WITH_LIBVIRTD
static int
-xenInitialize (void)
+xenInitialize (int privileged ATTRIBUTE_UNUSED)
{
inside_daemon = 1;
return 0;
--
|: Red Hat, Engineering, London -o-
http://people.redhat.com/berrange/ :|
|:
http://libvirt.org -o-
http://virt-manager.org -o-
http://ovirt.org :|
|:
http://autobuild.org -o-
http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|