
 Makefile.maint                     |    1 
 autobuild.sh                       |   10 +-
 include/libvirt/libvirt.h          |   67 ++++++++++++++
 include/libvirt/libvirt.h.in       |   67 ++++++++++++++
 include/libvirt/virterror.h        |    2 
 po/POTFILES.in                     |    2 
 python/generator.py                |    2 
 qemud/Makefile.am                  |    1 
 qemud/remote.c                     |   82 +++++++++++++++++
 qemud/remote_dispatch_args.h       |    2 
 qemud/remote_dispatch_prototypes.h |   14 ++
 qemud/remote_dispatch_ret.h        |    2 
 qemud/remote_dispatch_table.h      |   10 ++
 qemud/remote_protocol.c            |   46 +++++++++
 qemud/remote_protocol.h            |   44 +++++++++
 qemud/remote_protocol.x            |   38 +++++++-
 src/Makefile.am                    |   26 ++++-
 src/capabilities.c                 |   10 ++
 src/capabilities.h                 |    7 +
 src/domain_conf.c                  |   73 +++++++++++++++
 src/domain_conf.h                  |    9 +
 src/driver.h                       |    8 +
 src/libvirt.c                      |   69 ++++++++++++++
 src/libvirt_sym.version.in         |    3 
 src/lxc_driver.c                   |    2 
 src/openvz_driver.c                |    2 
 src/qemu_conf.h                    |    3 
 src/qemu_driver.c                  |  175 +++++++++++++++++++++++++++++++++++++
 src/remote_internal.c              |   66 +++++++++++++
 src/seclabel.c                     |  114 ++++++++++++++++++++++++
 src/seclabel.h                     |   62 +++++++++++++
 src/seclabel_selinux.c             |  106 ++++++++++++++++++++++
 src/seclabel_selinux.h             |   18 +++
 src/storage_backend.c              |    1 
 src/test.c                         |    2 
 src/uml_driver.c                   |    2 
 src/virsh.c                        |   22 ++++
 src/virterror.c                    |    9 +
 tests/daemon-conf                  |    3 
 39 files changed, 1177 insertions(+), 5 deletions(-)

diff --git a/Makefile.maint b/Makefile.maint
index 10d481b..0dcdcf8 100644
--- a/Makefile.maint
+++ b/Makefile.maint
@@ -353,6 +353,7 @@ msg_gen_function += qemudReportError
 msg_gen_function += openvzLog
 msg_gen_function += openvzError
 msg_gen_function += virDomainReportError
+msg_gen_function += virSecLabelReportError
 msg_gen_function += virReportErrorHelper
 msg_gen_function += lxcError
 msg_gen_function += umlError
diff --git a/autobuild.sh b/autobuild.sh
index e5d8554..bee3f34 100755
--- a/autobuild.sh
+++ b/autobuild.sh
@@ -14,10 +14,18 @@ rm -rf coverage
 #mkdir build
 #cd build
 
+SELINUXENABLED=/usr/sbin/selinuxenabled
+
+if [ -x $SELINUXENABLED ] && $SELINUXENABLED ; then
+    WITH_SELINUX="--with-selinux=yes"
+else
+    WITH_SELINUX=""
+fi
+
 ./autogen.sh --prefix="$AUTOBUILD_INSTALL_ROOT" \
   --enable-test-coverage \
   --enable-compile-warnings=error \
-  --with-xen-proxy
+  --with-xen-proxy $WITH_SELINUX
 
 # If the MAKEFLAGS envvar does not yet include a -j option,
 # add -jN where N depends on the number of processors.
diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h
index 97b4489..46cd3f4 100644
--- a/include/libvirt/libvirt.h
+++ b/include/libvirt/libvirt.h
@@ -111,6 +111,69 @@ typedef enum {
 } virDomainCreateFlags;
 
 /**
+ * VIR_SECLABEL_LABEL_BUFLEN:
+ *
+ * Macro providing the maximum length of the virDomainSecLabel
+ * label string.  Note that this value is based on that used
+ * by Labeled NFS.
+ */
+#define VIR_SECLABEL_LABEL_BUFLEN (4096 + 1)
+
+/**
+ * virDomainSecLabel:
+ *
+ * a virDomainSecLabel is a structure filled by virDomainGetSecLabel(),
+ * providing the security label and associated attributes for the specified
+ * domain.
+ *
+ */
+typedef struct _virDomainSecLabel {
+    char label[VIR_SECLABEL_LABEL_BUFLEN];      /* security label string */
+    int enforcing;                              /* 1 if security policy is being enforced for domain */
+} virDomainSecLabel;
+
+/**
+ * virDomainSecLabelPtr:
+ *
+ * a virDomainSecLabelPtr is a pointer to a virDomainSecLabel.
+ */
+typedef virDomainSecLabel *virDomainSecLabelPtr;
+
+/**
+ * VIR_SECLABEL_MODEL_BUFLEN:
+ *
+ * Macro providing the maximum length of the virDomainSecModel model string.
+ */
+#define VIR_SECLABEL_MODEL_BUFLEN (256 + 1)
+
+/**
+ * VIR_SECLABEL_DOI_BUFLEN:
+ *
+ * Macro providing the maximum length of the virDomainSecModel doi string.
+ */
+#define VIR_SECLABEL_DOI_BUFLEN (256 + 1)
+
+/**
+ * virDomainSecModel:
+ *
+ * a virDomainSecModel is a structure filled by virDomainGetSecModel(),
+ * providing the per-hypervisor security model and DOI attributes for the
+ * specified domain.
+ *
+ */
+typedef struct _virDomainSecModel {
+    char model[VIR_SECLABEL_MODEL_BUFLEN];      /* security model string */
+    char doi[VIR_SECLABEL_DOI_BUFLEN];          /* domain of interpetation */
+} virDomainSecModel;
+
+/**
+ * virDomainSecModelPtr:
+ *
+ * a virDomainSecModelPtr is a pointer to a virDomainSecModel.
+ */
+typedef virDomainSecModel *virDomainSecModelPtr;
+
+/**
  * virNodeInfoPtr:
  *
  * a virNodeInfo is a structure filled by virNodeGetInfo() and providing
@@ -504,6 +567,10 @@ int                     virDomainSetMaxMemory   (virDomainPtr domain,
 int                     virDomainSetMemory      (virDomainPtr domain,
                                                  unsigned long memory);
 int                     virDomainGetMaxVcpus    (virDomainPtr domain);
+int                     virDomainGetSecLabel	(virDomainPtr domain,
+                                                 virDomainSecLabelPtr seclabel);
+int                     virDomainGetSecModel	(virDomainPtr domain,
+                                                virDomainSecModelPtr secmodel);
 
 /*
  * XML domain description
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index fc322e6..63cf21d 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -111,6 +111,69 @@ typedef enum {
 } virDomainCreateFlags;
 
 /**
+ * VIR_SECLABEL_LABEL_BUFLEN:
+ *
+ * Macro providing the maximum length of the virDomainSecLabel
+ * label string.  Note that this value is based on that used
+ * by Labeled NFS.
+ */
+#define VIR_SECLABEL_LABEL_BUFLEN (4096 + 1)
+
+/**
+ * virDomainSecLabel:
+ *
+ * a virDomainSecLabel is a structure filled by virDomainGetSecLabel(),
+ * providing the security label and associated attributes for the specified
+ * domain.
+ *
+ */
+typedef struct _virDomainSecLabel {
+    char label[VIR_SECLABEL_LABEL_BUFLEN];      /* security label string */
+    int enforcing;                              /* 1 if security policy is being enforced for domain */
+} virDomainSecLabel;
+
+/**
+ * virDomainSecLabelPtr:
+ *
+ * a virDomainSecLabelPtr is a pointer to a virDomainSecLabel.
+ */
+typedef virDomainSecLabel *virDomainSecLabelPtr;
+
+/**
+ * VIR_SECLABEL_MODEL_BUFLEN:
+ *
+ * Macro providing the maximum length of the virDomainSecModel model string.
+ */
+#define VIR_SECLABEL_MODEL_BUFLEN (256 + 1)
+
+/**
+ * VIR_SECLABEL_DOI_BUFLEN:
+ *
+ * Macro providing the maximum length of the virDomainSecModel doi string.
+ */
+#define VIR_SECLABEL_DOI_BUFLEN (256 + 1)
+
+/**
+ * virDomainSecModel:
+ *
+ * a virDomainSecModel is a structure filled by virDomainGetSecModel(),
+ * providing the per-hypervisor security model and DOI attributes for the
+ * specified domain.
+ *
+ */
+typedef struct _virDomainSecModel {
+    char model[VIR_SECLABEL_MODEL_BUFLEN];      /* security model string */
+    char doi[VIR_SECLABEL_DOI_BUFLEN];          /* domain of interpetation */
+} virDomainSecModel;
+
+/**
+ * virDomainSecModelPtr:
+ *
+ * a virDomainSecModelPtr is a pointer to a virDomainSecModel.
+ */
+typedef virDomainSecModel *virDomainSecModelPtr;
+
+/**
  * virNodeInfoPtr:
  *
  * a virNodeInfo is a structure filled by virNodeGetInfo() and providing
@@ -504,6 +567,10 @@ int                     virDomainSetMaxMemory   (virDomainPtr domain,
 int                     virDomainSetMemory      (virDomainPtr domain,
                                                  unsigned long memory);
 int                     virDomainGetMaxVcpus    (virDomainPtr domain);
+int                     virDomainGetSecLabel	(virDomainPtr domain,
+                                                 virDomainSecLabelPtr seclabel);
+int                     virDomainGetSecModel	(virDomainPtr domain,
+                                                virDomainSecModelPtr secmodel);
 
 /*
  * XML domain description
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index 0f1f08c..e987d9e 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -61,6 +61,7 @@ typedef enum {
     VIR_FROM_UML,       /* Error at the UML driver */
     VIR_FROM_NODEDEV, /* Error from node device monitor */
     VIR_FROM_XEN_INOTIFY, /* Error from xen inotify layer */
+    VIR_FROM_SECLABEL,  /* Error from security labeling framework */
 } virErrorDomain;
 
 
@@ -154,6 +155,7 @@ typedef enum {
     VIR_WAR_NO_NODE, /* failed to start node driver */
     VIR_ERR_INVALID_NODE_DEVICE,/* invalid node device object */
     VIR_ERR_NO_NODE_DEVICE,/* node device not found */
+    VIR_ERR_NO_SECLABEL_MODEL, /* security labeling model not found */
 } virErrorNumber;
 
 /**
diff --git a/po/POTFILES.in b/po/POTFILES.in
index d33d24b..8dab48e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -20,6 +20,8 @@ src/proxy_internal.c
 src/qemu_conf.c
 src/qemu_driver.c
 src/remote_internal.c
+src/seclabel.c
+src/seclabel_selinux.c
 src/sexpr.c
 src/storage_backend.c
 src/storage_backend_disk.c
diff --git a/python/generator.py b/python/generator.py
index 9c71c05..7a99eb3 100755
--- a/python/generator.py
+++ b/python/generator.py
@@ -342,6 +342,8 @@ skip_function = (
     'virCopyLastError', # Python API is called virGetLastError instead
     'virConnectOpenAuth', # Python C code is manually written
     'virDefaultErrorFunc', # Python virErrorFuncHandler impl calls this from C
+    'virDomainGetSecLabel', # Needs investigation...
+    'virDomainGetSecModel', # Needs investigation...
     'virConnectDomainEventRegister',   # overridden in virConnect.py
     'virConnectDomainEventDeregister', # overridden in virConnect.py
 )
diff --git a/qemud/Makefile.am b/qemud/Makefile.am
index 8cb0847..9db9ee8 100644
--- a/qemud/Makefile.am
+++ b/qemud/Makefile.am
@@ -122,6 +122,7 @@ libvirtd_LDADD += ../src/libvirt_driver_nodedev.la
 endif
 endif
 
+libvirtd_LDADD += ../src/libvirt_driver_seclabel.la
 libvirtd_LDADD += ../src/libvirt.la
 
 if HAVE_POLKIT
diff --git a/qemud/remote.c b/qemud/remote.c
index 0488e6c..ab2d754 100644
--- a/qemud/remote.c
+++ b/qemud/remote.c
@@ -1319,6 +1319,88 @@ remoteDispatchDomainGetMaxVcpus (struct qemud_server *server ATTRIBUTE_UNUSED,
 }
 
 static int
+remoteDispatchDomainGetSeclabel(struct qemud_server *server ATTRIBUTE_UNUSED,
+                                struct qemud_client *client ATTRIBUTE_UNUSED,
+                                virConnectPtr conn,
+                                remote_error *rerr,
+                                remote_domain_get_seclabel_args *args,
+                                remote_domain_get_seclabel_ret *ret)
+{
+    virDomainPtr dom;
+    virDomainSecLabel seclabel;
+
+    dom = get_nonnull_domain(conn, args->dom);
+    if (dom == NULL) {
+        remoteDispatchFormatError(rerr, "%s", _("domain not found"));
+        return -2;
+    }
+
+    memset(&seclabel, 0, sizeof seclabel);
+
+    if (virDomainGetSecLabel(dom, &seclabel) == -1) {
+        virDomainFree(dom);
+        remoteDispatchFormatError(rerr, "%s", _("unable to get security label"));
+        return -1;
+    }
+
+    ret->label.label_len = strlen(seclabel.label) + 1;
+    if (VIR_ALLOC_N(ret->label.label_val, ret->label.label_len) < 0) {
+        virDomainFree (dom);
+        remoteDispatchFormatError(rerr, "%s", strerror (errno));
+        return -2;
+    }
+    strcpy(ret->label.label_val, seclabel.label);
+    ret->enforcing = seclabel.enforcing;
+
+    virDomainFree(dom);
+    return 0;
+}
+
+static int
+remoteDispatchDomainGetSecmodel(struct qemud_server *server ATTRIBUTE_UNUSED,
+                                struct qemud_client *client ATTRIBUTE_UNUSED,
+                                virConnectPtr conn,
+                                remote_error *rerr,
+                                remote_domain_get_secmodel_args *args,
+                                remote_domain_get_secmodel_ret *ret)
+{
+    virDomainPtr dom;
+    virDomainSecModel secmodel;
+
+    dom = get_nonnull_domain(conn, args->dom);
+    if (dom == NULL) {
+        remoteDispatchFormatError(rerr, "%s", _("domain not found"));
+        return -2;
+    }
+
+    memset(&secmodel, 0, sizeof secmodel);
+    if (virDomainGetSecModel(dom, &secmodel) == -1) {
+        virDomainFree(dom);
+        remoteDispatchFormatError(rerr, "%s", _("unable to get security model"));
+        return -1;
+    }
+
+    ret->model.model_len = strlen(secmodel.model) + 1;
+    if (VIR_ALLOC_N(ret->model.model_val, ret->model.model_len) < 0) {
+        virDomainFree(dom);
+        remoteDispatchFormatError(rerr, "%s", strerror (errno));
+        return -2;
+    }
+    strcpy(ret->model.model_val, secmodel.model);
+
+    ret->doi.doi_len = strlen(secmodel.doi) + 1;
+    if (VIR_ALLOC_N(ret->doi.doi_val, ret->doi.doi_len) < 0) {
+        virDomainFree(dom);
+        remoteDispatchFormatError(rerr, "%s", strerror (errno));
+        return -2;
+    }
+    strcpy(ret->doi.doi_val, secmodel.doi);
+
+    virDomainFree(dom);
+    return 0;
+}
+
+static int
 remoteDispatchDomainGetOsType (struct qemud_server *server ATTRIBUTE_UNUSED,
                                struct qemud_client *client ATTRIBUTE_UNUSED,
                                virConnectPtr conn,
diff --git a/qemud/remote_dispatch_args.h b/qemud/remote_dispatch_args.h
index a19ab79..ec5e346 100644
--- a/qemud/remote_dispatch_args.h
+++ b/qemud/remote_dispatch_args.h
@@ -99,3 +99,5 @@
     remote_node_device_get_parent_args val_remote_node_device_get_parent_args;
     remote_node_device_num_of_caps_args val_remote_node_device_num_of_caps_args;
     remote_node_device_list_caps_args val_remote_node_device_list_caps_args;
+    remote_domain_get_seclabel_args val_remote_domain_get_seclabel_args;
+    remote_domain_get_secmodel_args val_remote_domain_get_secmodel_args;
diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h
index 3ffb164..13fbf6a 100644
--- a/qemud/remote_dispatch_prototypes.h
+++ b/qemud/remote_dispatch_prototypes.h
@@ -184,6 +184,20 @@ static int remoteDispatchDomainGetSchedulerType(
     remote_error *err,
     remote_domain_get_scheduler_type_args *args,
     remote_domain_get_scheduler_type_ret *ret);
+static int remoteDispatchDomainGetSeclabel(
+    struct qemud_server *server,
+    struct qemud_client *client,
+    virConnectPtr conn,
+    remote_error *err,
+    remote_domain_get_seclabel_args *args,
+    remote_domain_get_seclabel_ret *ret);
+static int remoteDispatchDomainGetSecmodel(
+    struct qemud_server *server,
+    struct qemud_client *client,
+    virConnectPtr conn,
+    remote_error *err,
+    remote_domain_get_secmodel_args *args,
+    remote_domain_get_secmodel_ret *ret);
 static int remoteDispatchDomainGetVcpus(
     struct qemud_server *server,
     struct qemud_client *client,
diff --git a/qemud/remote_dispatch_ret.h b/qemud/remote_dispatch_ret.h
index 563167f..1b4779f 100644
--- a/qemud/remote_dispatch_ret.h
+++ b/qemud/remote_dispatch_ret.h
@@ -86,3 +86,5 @@
     remote_node_device_get_parent_ret val_remote_node_device_get_parent_ret;
     remote_node_device_num_of_caps_ret val_remote_node_device_num_of_caps_ret;
     remote_node_device_list_caps_ret val_remote_node_device_list_caps_ret;
+    remote_domain_get_seclabel_ret val_remote_domain_get_seclabel_ret;
+    remote_domain_get_secmodel_ret val_remote_domain_get_secmodel_ret;
diff --git a/qemud/remote_dispatch_table.h b/qemud/remote_dispatch_table.h
index 60f0e1c..4e6921f 100644
--- a/qemud/remote_dispatch_table.h
+++ b/qemud/remote_dispatch_table.h
@@ -592,3 +592,13 @@
     .args_filter = (xdrproc_t) xdr_remote_node_device_list_caps_args,
     .ret_filter = (xdrproc_t) xdr_remote_node_device_list_caps_ret,
 },
+{   /* DomainGetSeclabel => 118 */
+    .fn = (dispatch_fn) remoteDispatchDomainGetSeclabel,
+    .args_filter = (xdrproc_t) xdr_remote_domain_get_seclabel_args,
+    .ret_filter = (xdrproc_t) xdr_remote_domain_get_seclabel_ret,
+},
+{   /* DomainGetSecmodel => 119 */
+    .fn = (dispatch_fn) remoteDispatchDomainGetSecmodel,
+    .args_filter = (xdrproc_t) xdr_remote_domain_get_secmodel_args,
+    .ret_filter = (xdrproc_t) xdr_remote_domain_get_secmodel_ret,
+},
diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c
index ec8e653..44aba3f 100644
--- a/qemud/remote_protocol.c
+++ b/qemud/remote_protocol.c
@@ -1166,6 +1166,52 @@ xdr_remote_domain_get_max_vcpus_ret (XDR *xdrs, remote_domain_get_max_vcpus_ret
 }
 
 bool_t
+xdr_remote_domain_get_seclabel_args (XDR *xdrs, remote_domain_get_seclabel_args *objp)
+{
+
+         if (!xdr_remote_nonnull_domain (xdrs, &objp->dom))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_domain_get_seclabel_ret (XDR *xdrs, remote_domain_get_seclabel_ret *objp)
+{
+        char **objp_cpp0 = (char **) (void *) &objp->label.label_val;
+
+         if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->label.label_len, REMOTE_SECLABEL_LABEL_MAX,
+                sizeof (char), (xdrproc_t) xdr_char))
+                 return FALSE;
+         if (!xdr_int (xdrs, &objp->enforcing))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_domain_get_secmodel_args (XDR *xdrs, remote_domain_get_secmodel_args *objp)
+{
+
+         if (!xdr_remote_nonnull_domain (xdrs, &objp->dom))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_domain_get_secmodel_ret (XDR *xdrs, remote_domain_get_secmodel_ret *objp)
+{
+        char **objp_cpp1 = (char **) (void *) &objp->doi.doi_val;
+        char **objp_cpp0 = (char **) (void *) &objp->model.model_val;
+
+         if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->model.model_len, REMOTE_SECLABEL_MODEL_MAX,
+                sizeof (char), (xdrproc_t) xdr_char))
+                 return FALSE;
+         if (!xdr_array (xdrs, objp_cpp1, (u_int *) &objp->doi.doi_len, REMOTE_SECLABEL_DOI_MAX,
+                sizeof (char), (xdrproc_t) xdr_char))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
 xdr_remote_domain_attach_device_args (XDR *xdrs, remote_domain_attach_device_args *objp)
 {
 
diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h
index bf107ae..a13e234 100644
--- a/qemud/remote_protocol.h
+++ b/qemud/remote_protocol.h
@@ -39,6 +39,9 @@ typedef remote_nonnull_string *remote_string;
 #define REMOTE_AUTH_TYPE_LIST_MAX 20
 #define REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX 65536
 #define REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX 65536
+#define REMOTE_SECLABEL_MODEL_MAX VIR_SECLABEL_MODEL_BUFLEN
+#define REMOTE_SECLABEL_LABEL_MAX VIR_SECLABEL_LABEL_BUFLEN
+#define REMOTE_SECLABEL_DOI_MAX VIR_SECLABEL_DOI_BUFLEN
 
 typedef char remote_uuid[VIR_UUID_BUFLEN];
 
@@ -638,6 +641,37 @@ struct remote_domain_get_max_vcpus_ret {
 };
 typedef struct remote_domain_get_max_vcpus_ret remote_domain_get_max_vcpus_ret;
 
+struct remote_domain_get_seclabel_args {
+        remote_nonnull_domain dom;
+};
+typedef struct remote_domain_get_seclabel_args remote_domain_get_seclabel_args;
+
+struct remote_domain_get_seclabel_ret {
+        struct {
+                u_int label_len;
+                char *label_val;
+        } label;
+        int enforcing;
+};
+typedef struct remote_domain_get_seclabel_ret remote_domain_get_seclabel_ret;
+
+struct remote_domain_get_secmodel_args {
+        remote_nonnull_domain dom;
+};
+typedef struct remote_domain_get_secmodel_args remote_domain_get_secmodel_args;
+
+struct remote_domain_get_secmodel_ret {
+        struct {
+                u_int model_len;
+                char *model_val;
+        } model;
+        struct {
+                u_int doi_len;
+                char *doi_val;
+        } doi;
+};
+typedef struct remote_domain_get_secmodel_ret remote_domain_get_secmodel_ret;
+
 struct remote_domain_attach_device_args {
         remote_nonnull_domain dom;
         remote_nonnull_string xml;
@@ -1349,6 +1383,8 @@ enum remote_procedure {
         REMOTE_PROC_NODE_DEVICE_GET_PARENT = 115,
         REMOTE_PROC_NODE_DEVICE_NUM_OF_CAPS = 116,
         REMOTE_PROC_NODE_DEVICE_LIST_CAPS = 117,
+        REMOTE_PROC_DOMAIN_GET_SECLABEL = 118,
+        REMOTE_PROC_DOMAIN_GET_SECMODEL = 119,
 };
 typedef enum remote_procedure remote_procedure;
 
@@ -1475,6 +1511,10 @@ extern  bool_t xdr_remote_domain_get_vcpus_args (XDR *, remote_domain_get_vcpus_
 extern  bool_t xdr_remote_domain_get_vcpus_ret (XDR *, remote_domain_get_vcpus_ret*);
 extern  bool_t xdr_remote_domain_get_max_vcpus_args (XDR *, remote_domain_get_max_vcpus_args*);
 extern  bool_t xdr_remote_domain_get_max_vcpus_ret (XDR *, remote_domain_get_max_vcpus_ret*);
+extern  bool_t xdr_remote_domain_get_seclabel_args (XDR *, remote_domain_get_seclabel_args*);
+extern  bool_t xdr_remote_domain_get_seclabel_ret (XDR *, remote_domain_get_seclabel_ret*);
+extern  bool_t xdr_remote_domain_get_secmodel_args (XDR *, remote_domain_get_secmodel_args*);
+extern  bool_t xdr_remote_domain_get_secmodel_ret (XDR *, remote_domain_get_secmodel_ret*);
 extern  bool_t xdr_remote_domain_attach_device_args (XDR *, remote_domain_attach_device_args*);
 extern  bool_t xdr_remote_domain_detach_device_args (XDR *, remote_domain_detach_device_args*);
 extern  bool_t xdr_remote_domain_get_autostart_args (XDR *, remote_domain_get_autostart_args*);
@@ -1680,6 +1720,10 @@ extern bool_t xdr_remote_domain_get_vcpus_args ();
 extern bool_t xdr_remote_domain_get_vcpus_ret ();
 extern bool_t xdr_remote_domain_get_max_vcpus_args ();
 extern bool_t xdr_remote_domain_get_max_vcpus_ret ();
+extern bool_t xdr_remote_domain_get_seclabel_args ();
+extern bool_t xdr_remote_domain_get_seclabel_ret ();
+extern bool_t xdr_remote_domain_get_secmodel_args ();
+extern bool_t xdr_remote_domain_get_secmodel_ret ();
 extern bool_t xdr_remote_domain_attach_device_args ();
 extern bool_t xdr_remote_domain_detach_device_args ();
 extern bool_t xdr_remote_domain_get_autostart_args ();
diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x
index 22327fd..c88fd35 100644
--- a/qemud/remote_protocol.x
+++ b/qemud/remote_protocol.x
@@ -116,6 +116,21 @@ const REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX = 65536;
  */
 const REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX = 65536;
 
+/*
+ * Maximum length of a security label model field.
+ */
+const REMOTE_SECLABEL_MODEL_MAX = VIR_SECLABEL_MODEL_BUFLEN;
+
+/*
+ * Maximum length of a security label field.
+ */
+const REMOTE_SECLABEL_LABEL_MAX = VIR_SECLABEL_LABEL_BUFLEN;
+
+/*
+ * Maximum length of a security label DOI field.
+ */
+const REMOTE_SECLABEL_DOI_MAX = VIR_SECLABEL_DOI_BUFLEN;
+
 /* UUID.  VIR_UUID_BUFLEN definition comes from libvirt.h */
 typedef opaque remote_uuid[VIR_UUID_BUFLEN];
 
@@ -618,6 +633,24 @@ struct remote_domain_get_max_vcpus_ret {
     int num;
 };
 
+struct remote_domain_get_seclabel_args {
+    remote_nonnull_domain dom;
+};
+
+struct remote_domain_get_seclabel_ret {
+    char label<REMOTE_SECLABEL_LABEL_MAX>;
+    int enforcing;
+};
+
+struct remote_domain_get_secmodel_args {
+    remote_nonnull_domain dom;
+};
+
+struct remote_domain_get_secmodel_ret {
+    char model<REMOTE_SECLABEL_MODEL_MAX>;
+    char doi<REMOTE_SECLABEL_DOI_MAX>;
+};
+
 struct remote_domain_attach_device_args {
     remote_nonnull_domain dom;
     remote_nonnull_string xml;
@@ -1224,7 +1257,10 @@ enum remote_procedure {
     REMOTE_PROC_NODE_DEVICE_DUMP_XML = 114,
     REMOTE_PROC_NODE_DEVICE_GET_PARENT = 115,
     REMOTE_PROC_NODE_DEVICE_NUM_OF_CAPS = 116,
-    REMOTE_PROC_NODE_DEVICE_LIST_CAPS = 117
+    REMOTE_PROC_NODE_DEVICE_LIST_CAPS = 117,
+
+    REMOTE_PROC_DOMAIN_GET_SECLABEL = 118,
+    REMOTE_PROC_DOMAIN_GET_SECMODEL = 119
 };
 
 /* Custom RPC structure. */
diff --git a/src/Makefile.am b/src/Makefile.am
index c32a1d4..076ef85 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -134,7 +134,7 @@ UML_DRIVER_SOURCES =						\
 NETWORK_DRIVER_SOURCES =					\
 		network_driver.h network_driver.c
 
-# And finally storage backend specific impls
+# Storage backend specific impls
 STORAGE_DRIVER_SOURCES =					\
 		storage_driver.h storage_driver.c		\
 		storage_backend.h storage_backend.c
@@ -159,6 +159,12 @@ STORAGE_DRIVER_DISK_SOURCES =					\
 STORAGE_HELPER_DISK_SOURCES =					\
 		parthelper.c
 
+# Security framework and drivers for various models
+SECLABEL_DRIVER_SOURCES =					\
+		seclabel.h seclabel.c
+
+SECLABEL_DRIVER_SELINUX_SOURCES =				\
+		seclabel_selinux.h seclabel_selinux.c
 
 NODE_DEVICE_DRIVER_SOURCES =					\
 		node_device.c node_device.h
@@ -370,6 +376,19 @@ libvirt_driver_nodedev_la_LDFLAGS += -module -avoid-version
 endif
 endif
 
+libvirt_driver_seclabel_la_SOURCES = $(SECLABEL_DRIVER_SOURCES)
+if WITH_DRIVER_MODULES
+mod_LTLIBRARIES += libvirt_driver_seclabel.la
+else
+noinst_LTLIBRARIES += libvirt_driver_seclabel.la
+endif
+if WITH_DRIVER_MODULES
+libvirt_driver_seclabel_la_LDFLAGS = -module -avoid-version
+endif
+# XXX fixme
+# if WITH_SELINUX
+libvirt_driver_seclabel_la_SOURCES += $(SECLABEL_DRIVER_SELINUX_SOURCES)
+# endif
 
 # Add all conditional sources just in case...
 EXTRA_DIST +=							\
@@ -388,8 +407,9 @@ EXTRA_DIST +=							\
 		$(STORAGE_DRIVER_DISK_SOURCES)			\
 		$(NODE_DEVICE_DRIVER_SOURCES)			\
 		$(NODE_DEVICE_DRIVER_HAL_SOURCES)		\
-		$(NODE_DEVICE_DRIVER_DEVKIT_SOURCES)
-
+		$(NODE_DEVICE_DRIVER_DEVKIT_SOURCES)		\
+		$(SECLABEL_DRIVER_SOURCES)			\
+		$(SECLABEL_DRIVER_SELINUX_SOURCES)
 
 # Empty source list - it merely links a bunch of convenience libs together
 libvirt_la_SOURCES =
diff --git a/src/capabilities.c b/src/capabilities.c
index 158873c..6a09f36 100644
--- a/src/capabilities.c
+++ b/src/capabilities.c
@@ -150,6 +150,8 @@ virCapabilitiesFree(virCapsPtr caps) {
     VIR_FREE(caps->host.migrateTrans);
 
     VIR_FREE(caps->host.arch);
+    VIR_FREE(caps->host.secModel.model);
+    VIR_FREE(caps->host.secModel.doi);
     VIR_FREE(caps);
 }
 
@@ -575,6 +577,14 @@ virCapabilitiesFormatXML(virCapsPtr caps)
         virBufferAddLit(&xml, "      </cells>\n");
         virBufferAddLit(&xml, "    </topology>\n");
     }
+
+    if (caps->host.secModel.model) {
+        virBufferAddLit(&xml, "    <secmodel>\n");
+        virBufferVSprintf(&xml, "      <model>%s</model>\n", caps->host.secModel.model);
+        virBufferVSprintf(&xml, "      <doi>%s</doi>\n", caps->host.secModel.doi);
+        virBufferAddLit(&xml, "    </secmodel>\n");
+    }
+
     virBufferAddLit(&xml, "  </host>\n\n");
 
 
diff --git a/src/capabilities.h b/src/capabilities.h
index c991ea1..6257ac3 100644
--- a/src/capabilities.h
+++ b/src/capabilities.h
@@ -78,6 +78,12 @@ struct _virCapsHostNUMACell {
     int *cpus;
 };
 
+typedef struct _virCapsHostSecModel virCapsHostSecModel;
+struct _virCapsHostSecModel {
+    char *model;
+    char *doi;
+};
+
 typedef struct _virCapsHost virCapsHost;
 typedef virCapsHost *virCapsHostPtr;
 struct _virCapsHost {
@@ -90,6 +96,7 @@ struct _virCapsHost {
     char **migrateTrans;
     int nnumaCell;
     virCapsHostNUMACellPtr *numaCell;
+    virCapsHostSecModel secModel;
 };
 
 typedef struct _virCaps virCaps;
diff --git a/src/domain_conf.c b/src/domain_conf.c
index 32ed59f..5a0d4de 100644
--- a/src/domain_conf.c
+++ b/src/domain_conf.c
@@ -359,6 +359,16 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
     VIR_FREE(def);
 }
 
+void virDomainSecLabelDefFree(virDomainDefPtr def);
+
+void virDomainSecLabelDefFree(virDomainDefPtr def)
+{
+    if (def->seclabel.model)
+        VIR_FREE(def->seclabel.model);
+    if (def->seclabel.label)
+        VIR_FREE(def->seclabel.label);
+}
+
 void virDomainDefFree(virDomainDefPtr def)
 {
     unsigned int i;
@@ -417,6 +427,8 @@ void virDomainDefFree(virDomainDefPtr def)
     VIR_FREE(def->cpumask);
     VIR_FREE(def->emulator);
 
+    virDomainSecLabelDefFree(def);
+
     VIR_FREE(def);
 }
 
@@ -1644,6 +1656,56 @@ static int virDomainLifecycleParseXML(virConnectPtr conn,
     return 0;
 }
 
+static int
+virDomainSecLabelDefParseXMLString(virConnectPtr conn,
+                                   const char *str,
+                                   int maxlen,
+                                   const char *name,
+                                   char **to,
+                                   xmlXPathContextPtr ctxt)
+{
+    char *tmp = virXPathString(conn, str, ctxt);
+
+    if (tmp != NULL) {
+        if (strlen(tmp) >= maxlen) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                 _("\'%s\' longer than %d characters"),
+                                 name, maxlen);
+            return -1;
+        }
+        *to = tmp;
+    } else {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("\'%s\' missing"), name);
+        return -1;
+    }
+    return 0;
+}
+
+static int
+virDomainSecLabelDefParseXML(virConnectPtr conn,
+                             const virDomainDefPtr def,
+                             xmlXPathContextPtr ctxt)
+{
+    if (virXPathNode(conn, "./seclabel", ctxt) == NULL)
+        return 0;
+
+    if (virDomainSecLabelDefParseXMLString(conn, "string(./seclabel/label[1])",
+                                          VIR_SECLABEL_LABEL_BUFLEN-1, "label",
+                                          &def->seclabel.label, ctxt) == -1)
+        goto error;
+
+    if (virDomainSecLabelDefParseXMLString(conn, "string(./seclabel/@model)",
+                                           VIR_SECLABEL_MODEL_BUFLEN-1, "model",
+                                           &def->seclabel.model, ctxt) == -1)
+        goto error;
+
+    return 0;
+
+error:
+    virDomainSecLabelDefFree(def);
+    return -1;
+}
 
 virDomainDeviceDefPtr virDomainDeviceDefParse(virConnectPtr conn,
                                               virCapsPtr caps,
@@ -2212,6 +2274,10 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn,
     }
     VIR_FREE(nodes);
 
+    /* analysis of security label */
+    if (virDomainSecLabelDefParseXML(conn, def, ctxt) == -1)
+        goto error;
+
     return def;
 
 no_memory:
@@ -3206,6 +3272,13 @@ char *virDomainDefFormat(virConnectPtr conn,
             goto cleanup;
 
     virBufferAddLit(&buf, "  </devices>\n");
+
+    if (def->seclabel.model) {
+        virBufferEscapeString(&buf, "  <seclabel model='%s'>\n", def->seclabel.model);
+        virBufferEscapeString(&buf, "    <label>%s</label>\n", def->seclabel.label);
+        virBufferAddLit(&buf, "  </seclabel>\n");
+    }
+
     virBufferAddLit(&buf, "</domain>\n");
 
     if (virBufferError(&buf))
diff --git a/src/domain_conf.h b/src/domain_conf.h
index 51cf6d5..aee6459 100644
--- a/src/domain_conf.h
+++ b/src/domain_conf.h
@@ -391,6 +391,14 @@ struct _virDomainOSDef {
     char *bootloaderArgs;
 };
 
+/* Security label configuration for domain */
+typedef struct _virDomainSecLabelDef virDomainSecLabelDef;
+typedef virDomainSecLabelDef *virDomainSecLabelDefPtr;
+struct _virDomainSecLabelDef {
+    char *model;        /* name of security labeling model */
+    char *label;        /* security label string */
+};
+
 #define VIR_DOMAIN_CPUMASK_LEN 1024
 
 /* Guest VM main configuration */
@@ -448,6 +456,7 @@ struct _virDomainDef {
 
     /* Only 1 */
     virDomainChrDefPtr console;
+    virDomainSecLabelDef seclabel;
 };
 
 /* Guest VM runtime state */
diff --git a/src/driver.h b/src/driver.h
index 8c394e2..393621f 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -181,6 +181,12 @@ typedef int
 typedef int
         (*virDrvDomainGetMaxVcpus)	(virDomainPtr domain);
 typedef int
+        (*virDrvDomainGetSecLabel)	(virDomainPtr domain,
+                                         virDomainSecLabelPtr seclabel);
+typedef int
+        (*virDrvDomainGetSecModel)	(virDomainPtr domain,
+                                         virDomainSecModelPtr secmodel);
+typedef int
         (*virDrvDomainAttachDevice)	(virDomainPtr domain,
                                          const char *xml);
 typedef int
@@ -361,6 +367,8 @@ struct _virDriver {
     virDrvDomainPinVcpu		domainPinVcpu;
     virDrvDomainGetVcpus		domainGetVcpus;
     virDrvDomainGetMaxVcpus		domainGetMaxVcpus;
+    virDrvDomainGetSecLabel     domainGetSecLabel;
+    virDrvDomainGetSecModel     domainGetSecModel;
     virDrvDomainDumpXML		domainDumpXML;
     virDrvListDefinedDomains	listDefinedDomains;
     virDrvNumOfDefinedDomains	numOfDefinedDomains;
diff --git a/src/libvirt.c b/src/libvirt.c
index a4a0df5..9b127e3 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -3438,6 +3438,75 @@ virDomainGetMaxVcpus(virDomainPtr domain)
     return -1;
 }
 
+/**
+ * virDomainGetSecLabel:
+ * @domain: a domain object
+ * @seclabel: pointer to a virDomainSecLabel structure
+ *
+ * Extract security label of an active domain.
+ *
+ * Returns 0 in case of success, -1 in case of failure, and -2
+ * if the operation is not supported (caller decides if that's
+ * an error).
+ */
+int
+virDomainGetSecLabel(virDomainPtr domain, virDomainSecLabelPtr seclabel)
+{
+    virConnectPtr conn;
+
+    if (!VIR_IS_CONNECTED_DOMAIN(domain)) {
+        virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+        return -1;
+    }
+
+    if (seclabel == NULL) {
+        virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return -1;
+    }
+
+    conn = domain->conn;
+
+    if (conn->driver->domainGetSecLabel)
+        return conn->driver->domainGetSecLabel(domain, seclabel);
+
+    virLibConnWarning(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -2;
+}
+
+/**
+ * virDomainGetSecModel:
+ * @domain: a domain object
+ * @secmodel: pointer to a virDomainSecModel structure
+ *
+ * Extract the security model of a hypervisor.
+ *
+ * Returns 0 in case of success, -1 in case of failure, and -2
+ * if the operation is not supported (caller decides if that's
+ * an error).
+ */
+int
+virDomainGetSecModel(virDomainPtr domain, virDomainSecModelPtr secmodel)
+{
+    virConnectPtr conn;
+
+    if (!VIR_IS_CONNECTED_DOMAIN(domain)) {
+        virLibDomainError(NULL, VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+        return -1;
+    }
+
+    if (secmodel == NULL) {
+        virLibDomainError(domain, VIR_ERR_INVALID_ARG, __FUNCTION__);
+        return -1;
+    }
+
+    conn = domain->conn;
+
+    if (conn->driver->domainGetSecModel)
+        return conn->driver->domainGetSecModel(domain, secmodel);
+
+    virLibConnWarning(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+    return -2;
+}
 
 /**
  * virDomainAttachDevice:
diff --git a/src/libvirt_sym.version.in b/src/libvirt_sym.version.in
index de0bc4a..f533c17 100644
--- a/src/libvirt_sym.version.in
+++ b/src/libvirt_sym.version.in
@@ -617,6 +617,9 @@ LIBVIRT_PRIVATE_@VERSION@ {
 	virXPathString;
 	virXMLPropString;
 
+	/* WIP */
+	virDomainGetSecLabel;
+	virDomainGetSecModel;
 
 	/* Finally everything else is totally private */
     local:
diff --git a/src/lxc_driver.c b/src/lxc_driver.c
index 12f6adc..6cd8001 100644
--- a/src/lxc_driver.c
+++ b/src/lxc_driver.c
@@ -1423,6 +1423,8 @@ static virDriver lxcDriver = {
     NULL, /* domainPinVcpu */
     NULL, /* domainGetVcpus */
     NULL, /* domainGetMaxVcpus */
+    NULL, /* domainGetSecLabel */
+    NULL, /* domainGetSecModel */
     lxcDomainDumpXML, /* domainDumpXML */
     lxcListDefinedDomains, /* listDefinedDomains */
     lxcNumDefinedDomains, /* numOfDefinedDomains */
diff --git a/src/openvz_driver.c b/src/openvz_driver.c
index 95ac7dc..bee7471 100644
--- a/src/openvz_driver.c
+++ b/src/openvz_driver.c
@@ -1279,6 +1279,8 @@ static virDriver openvzDriver = {
     NULL, /* domainPinVcpu */
     NULL, /* domainGetVcpus */
     openvzDomainGetMaxVcpus, /* domainGetMaxVcpus */
+    NULL, /* domainGetSecLabel */
+    NULL, /* domainGetSecModel */
     openvzDomainDumpXML, /* domainDumpXML */
     openvzListDefinedDomains, /* listDomains */
     openvzNumDefinedDomains, /* numOfDomains */
diff --git a/src/qemu_conf.h b/src/qemu_conf.h
index 36d09d1..736b21c 100644
--- a/src/qemu_conf.h
+++ b/src/qemu_conf.h
@@ -32,6 +32,7 @@
 #include "network_conf.h"
 #include "domain_conf.h"
 #include "domain_event.h"
+#include "seclabel.h"
 
 #define qemudDebug(fmt, ...) do {} while(0)
 
@@ -74,6 +75,8 @@ struct qemud_driver {
     virDomainEventQueuePtr domainEventQueue;
     int domainEventTimer;
     int domainEventDispatching;
+
+    virSecLabelDriverPtr secLabelDriver;
 };
 
 /* Port numbers used for KVM migration. */
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index 5f6fbd1..fa04fa3 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -68,6 +68,7 @@
 #include "memory.h"
 #include "uuid.h"
 #include "domain_conf.h"
+#include "seclabel.h"
 
 /* For storing short-lived temporary files. */
 #define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt"
@@ -182,6 +183,50 @@ qemudAutostartConfigs(struct qemud_driver *driver) {
     virConnectClose(conn);
 }
 
+static int
+qemudSecLabelInit(struct qemud_driver *qemud_drv)
+{
+    int ret;
+    const char *doi, *model;
+    virCapsPtr caps;
+    virSecLabelDriverPtr seclabel_drv;
+
+    ret = virSecLabelDriverStartup(&seclabel_drv);
+    if (ret == -1) {
+        qemudLog(QEMUD_ERR, _("Failed to start security labeling driver"));
+        return -1;
+    }
+    if (ret == -2)
+        return 0;
+
+    qemud_drv->secLabelDriver = seclabel_drv;
+    doi = virSecLabelDriverGetDOI(seclabel_drv);
+    model = virSecLabelDriverGetModel(seclabel_drv);
+
+    qemudLog(QEMUD_DEBUG, "Initialized security labeling driver \"%s\" with "
+             "DOI \"%s\".\n", model, doi);
+
+    /*
+     * Add security policy host caps now that the labeling driver is
+     * initialized.
+     */
+    caps = qemud_drv->caps;
+
+    caps->host.secModel.model = strdup(model);
+    if (!caps->host.secModel.model) {
+        qemudLog(QEMUD_ERR, _("Failed to copy secModel model: %s"), strerror(errno));
+        return -1;
+    }
+
+    caps->host.secModel.doi = strdup(doi);
+    if (!caps->host.secModel.doi) {
+        qemudLog(QEMUD_ERR, _("Failed to copy secModel DOI: %s"), strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
 /**
  * qemudStartup:
  *
@@ -253,6 +298,11 @@ qemudStartup(void) {
     if ((qemu_driver->caps = qemudCapsInit()) == NULL)
         goto out_of_memory;
 
+    if (qemudSecLabelInit(qemu_driver) < 0) {
+        qemudShutdown();
+        return -1;
+    }
+
     if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) {
         goto error;
     }
@@ -824,6 +874,15 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
     return -1;
 }
 
+static int qemudDomainSetSecLabel(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm)
+{
+    if (vm->def->seclabel.label != NULL)
+        if (driver->secLabelDriver && driver->secLabelDriver->domainSetLabel)
+            return driver->secLabelDriver->domainSetLabel(conn, driver->secLabelDriver,
+                                                          &vm->def->seclabel);
+    return 0;
+}
+
 static virDomainPtr qemudDomainLookupByName(virConnectPtr conn,
                                             const char *name);
 
@@ -924,6 +983,16 @@ static int qemudStartVMDaemon(virConnectPtr conn,
         return -1;
     }
 
+    /*
+     * Set up the security label for the domain here, before doing
+     * too much else.
+     */
+    if (qemudDomainSetSecLabel(conn, driver, vm) < 0) {
+        qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                         _("Failed to set security label"));
+        return -1;
+    }
+
     if (qemudExtractVersionInfo(emulator,
                                 NULL,
                                 &qemuCmdFlags) < 0) {
@@ -2453,7 +2522,111 @@ cleanup:
     return ret;
 }
 
+static int qemudDomainGetSecLabel(virDomainPtr dom, virDomainSecLabelPtr seclabel)
+{
+    struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
+    virDomainObjPtr vm;
+    const char *type;
+    int ret = -1;
+
+    qemuDriverLock(driver);
+    vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    qemuDriverUnlock(driver);
+
+    if (!vm) {
+        char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+        virUUIDFormat(dom->uuid, uuidstr);
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         _("no domain with matching uuid '%s'"), uuidstr);
+        goto cleanup;
+    }
+
+    if (!(type = virDomainVirtTypeToString(vm->def->virtType))) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                         _("unknown virt type in domain definition '%d'"),
+                         vm->def->virtType);
+        goto cleanup;
+    }
+
+    /*
+     * Theoretically, the pid can be replaced during this operation and
+     * return the label of a different process.  If atomicity is needed,
+     * further validation will be required.
+     */
+    if (virDomainIsActive(vm)) {
+        if (driver->secLabelDriver && driver->secLabelDriver->domainGetLabel) {
+            if (driver->secLabelDriver->domainGetLabel(dom->conn, vm, seclabel) == -1) {
+                qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                                 _("Failed to get security label"));
+                goto cleanup;
+            }
+        }
+    }
+
+    ret = 0;
+
+cleanup:
+    if (vm)
+        virDomainObjUnlock(vm);
+    return ret;
+}
+
+static int qemudDomainGetSecModel(virDomainPtr dom, virDomainSecModelPtr secmodel)
+{
+    struct qemud_driver *driver = (struct qemud_driver *)dom->conn->privateData;
+    virDomainObjPtr vm;
+    const char *type;
+    char *p;
+    int ret = -1;
+
+    qemuDriverLock(driver);
+    vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    qemuDriverUnlock(driver);
+
+    if (!vm) {
+        char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+        virUUIDFormat(dom->uuid, uuidstr);
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         _("no domain with matching uuid '%s'"), uuidstr);
+        goto cleanup;
+    }
+
+    if (!(type = virDomainVirtTypeToString(vm->def->virtType))) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                         _("unknown virt type in domain definition '%d'"),
+                         vm->def->virtType);
+        goto cleanup;
+    }
+
+    p = driver->caps->host.secModel.model;
+    if (strlen(p) >= VIR_SECLABEL_MODEL_BUFLEN-1) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                         _("security model string exceeds max %d bytes"),
+                         VIR_SECLABEL_MODEL_BUFLEN-1);
+        goto cleanup;
+    }
+    strcpy(secmodel->model, p);
+
+    p = driver->caps->host.secModel.doi;
+    if (strlen(p) >= VIR_SECLABEL_DOI_BUFLEN-1) {
+        qemudReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                         _("security DOI string exceeds max %d bytes"),
+                         VIR_SECLABEL_DOI_BUFLEN-1);
+        goto cleanup;
+    }
+    strcpy(secmodel->doi, p);
+
+    ret = 0;
+
+cleanup:
+    if (vm)
+        virDomainObjUnlock(vm);
+    return ret;
+}
 
+/* TODO: check seclabel restore */
 static int qemudDomainRestore(virConnectPtr conn,
                               const char *path) {
     struct qemud_driver *driver = conn->privateData;
@@ -4203,6 +4376,8 @@ static virDriver qemuDriver = {
     NULL, /* domainGetVcpus */
 #endif
     qemudDomainGetMaxVcpus, /* domainGetMaxVcpus */
+    qemudDomainGetSecLabel, /* domainGetSecLabel */
+    qemudDomainGetSecModel, /* domainGetSecModel */
     qemudDomainDumpXML, /* domainDumpXML */
     qemudListDefinedDomains, /* listDomains */
     qemudNumDefinedDomains, /* numOfDomains */
diff --git a/src/remote_internal.c b/src/remote_internal.c
index 1d0c5ac..99e2926 100644
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -1983,6 +1983,70 @@ remoteDomainGetMaxVcpus (virDomainPtr domain)
     return ret.num;
 }
 
+static int
+remoteDomainGetSecLabel (virDomainPtr domain, virDomainSecLabelPtr seclabel)
+{
+    remote_domain_get_seclabel_args args;
+    remote_domain_get_seclabel_ret ret;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_SECLABEL,
+              (xdrproc_t) xdr_remote_domain_get_seclabel_args, (char *)&args,
+              (xdrproc_t) xdr_remote_domain_get_seclabel_ret, (char *)&ret) == -1) {
+        return -1;
+    }
+
+    if (ret.label.label_val != NULL) {
+        if (strlen (ret.label.label_val) >= sizeof seclabel->label) {
+            errorf (domain->conn, VIR_ERR_RPC, _("security label exceeds maximum: %zd"),
+                    sizeof seclabel->label - 1);
+            return -1;
+        }
+        strcpy (seclabel->label, ret.label.label_val);
+        seclabel->enforcing = ret.enforcing;
+    }
+
+    return 0;
+}
+
+static int
+remoteDomainGetSecModel (virDomainPtr domain, virDomainSecModelPtr secmodel)
+{
+    remote_domain_get_secmodel_args args;
+    remote_domain_get_secmodel_ret ret;
+    GET_PRIVATE (domain->conn, -1);
+
+    make_nonnull_domain (&args.dom, domain);
+    memset (&ret, 0, sizeof ret);
+    if (call (domain->conn, priv, 0, REMOTE_PROC_DOMAIN_GET_SECMODEL,
+              (xdrproc_t) xdr_remote_domain_get_secmodel_args, (char *)&args,
+              (xdrproc_t) xdr_remote_domain_get_secmodel_ret, (char *)&ret) == -1) {
+        return -1;
+    }
+
+    if (ret.model.model_val != NULL) {
+        if (strlen (ret.model.model_val) >= sizeof secmodel->model) {
+            errorf (domain->conn, VIR_ERR_RPC, _("security model exceeds maximum: %zd"),
+                    sizeof secmodel->model - 1);
+            return -1;
+        }
+        strcpy (secmodel->model, ret.model.model_val);
+    }
+
+    if (ret.doi.doi_val != NULL) {
+        if (strlen (ret.doi.doi_val) >= sizeof secmodel->doi) {
+            errorf (domain->conn, VIR_ERR_RPC, _("security doi exceeds maximum: %zd"),
+                    sizeof secmodel->doi - 1);
+            return -1;
+        }
+        strcpy (secmodel->doi, ret.doi.doi_val);
+    }
+
+    return 0;
+}
+
 static char *
 remoteDomainDumpXML (virDomainPtr domain, int flags)
 {
@@ -5352,6 +5416,8 @@ static virDriver driver = {
     .domainPinVcpu = remoteDomainPinVcpu,
     .domainGetVcpus = remoteDomainGetVcpus,
     .domainGetMaxVcpus = remoteDomainGetMaxVcpus,
+    .domainGetSecLabel = remoteDomainGetSecLabel,
+    .domainGetSecModel = remoteDomainGetSecModel,
     .domainDumpXML = remoteDomainDumpXML,
     .listDefinedDomains = remoteListDefinedDomains,
     .numOfDefinedDomains = remoteNumOfDefinedDomains,
diff --git a/src/seclabel.c b/src/seclabel.c
new file mode 100644
index 0000000..854e81e
--- /dev/null
+++ b/src/seclabel.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * Authors:
+ *     James Morris <jmorris@namei.org>
+ *
+ */
+#include <config.h>
+#include <string.h>
+
+#include "virterror_internal.h"
+
+#include "seclabel.h"
+
+#if HAVE_SELINUX
+#include "seclabel_selinux.h"
+#endif
+
+static virSecLabelDriverPtr seclabel_drivers[] = {
+#ifdef HAVE_SELINUX
+    &virSELinuxSecLabelDriver,
+#endif
+};
+
+/*
+ * Probe each security labeling driver: each should perform a test to see if
+ * it should be loaded, e.g. if the currently active host security mechanism
+ * matches.  If the probe succeeds, initialize the driver and return it.
+ *
+ * Returns 0 on success, -1 on error, and -2 on no driver found (caller
+ * to determine if that's an error).
+ */
+int
+virSecLabelDriverStartup(virSecLabelDriverPtr * drv)
+{
+    unsigned int i;
+
+    /* FIXME: gcc warning when no drivers enabled */
+    for (i = 0; i < (sizeof(seclabel_drivers) / sizeof(seclabel_drivers[0])); i++) {
+        virSecLabelDriverPtr tmp = seclabel_drivers[i];
+
+        if (tmp->probe()) {
+            virSecLabelDriverInit(tmp);
+            if (tmp->open(NULL, tmp) == -1) {
+                return -1;
+            } else {
+                *drv = tmp;
+                break;
+            }
+        } else
+            return -2;
+    }
+    return 0;
+}
+
+void
+virSecLabelReportError(virConnectPtr conn, int code, const char *fmt, ...)
+{
+    va_list args;
+    char errorMessage[1024];
+
+    if (fmt) {
+        va_start(args, fmt);
+        vsnprintf(errorMessage, sizeof(errorMessage) - 1, fmt, args);
+        va_end(args);
+    } else
+        errorMessage[0] = '\0';
+
+    virRaiseError(conn, NULL, NULL, VIR_FROM_SECLABEL, code,
+                  VIR_ERR_ERROR, NULL, NULL, NULL, -1, -1, "%s",
+                  errorMessage);
+}
+
+/*
+ * Helpers
+ */
+void
+virSecLabelDriverInit(virSecLabelDriverPtr drv)
+{
+    memset(&drv->_private, 0, sizeof drv->_private);
+}
+
+int
+virSecLabelDriverSetDOI(virConnectPtr conn,
+                        virSecLabelDriverPtr drv,
+                        const char *doi)
+{
+    if (strlen(doi) >= VIR_SECLABEL_DOI_BUFLEN) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR,
+                               _("%s: DOI \'%s\' is "
+                               "longer than the maximum allowed length of %d"),
+                               __func__, doi, VIR_SECLABEL_DOI_BUFLEN - 1);
+        return -1;
+    }
+    strcpy(drv->_private.doi, doi);
+    return 0;
+}
+
+const char *
+virSecLabelDriverGetDOI(virSecLabelDriverPtr drv)
+{
+    return drv->_private.doi;
+}
+
+const char *
+virSecLabelDriverGetModel(virSecLabelDriverPtr drv)
+{
+    return drv->name;
+}
diff --git a/src/seclabel.h b/src/seclabel.h
new file mode 100644
index 0000000..0eead78
--- /dev/null
+++ b/src/seclabel.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * Authors:
+ *     James Morris <jmorris@namei.org>
+ *
+ */
+#ifndef __VIR_SECLABEL_H__
+#define __VIR_SECLABEL_H__
+
+#include "internal.h"
+#include "domain_conf.h"
+
+typedef struct _virSecLabelDriver virSecLabelDriver;
+typedef virSecLabelDriver *virSecLabelDriverPtr;
+typedef int (*virSecLabelDriverProbe) (void);
+typedef int (*virSecLabelDriverOpen) (virConnectPtr conn,
+                                      virSecLabelDriverPtr drv);
+typedef int (*virSecLabelDomainGetLabel) (virConnectPtr conn,
+                                          virDomainObjPtr vm,
+                                          virDomainSecLabelPtr sec);
+typedef int (*virSecLabelDomainSetLabel) (virConnectPtr conn,
+                                          virSecLabelDriverPtr drv,
+                                          virDomainSecLabelDefPtr secdef);
+
+struct _virSecLabelDriver {
+    const char *name;
+    virSecLabelDriverProbe probe;
+    virSecLabelDriverOpen open;
+    virSecLabelDomainGetLabel domainGetLabel;
+    virSecLabelDomainSetLabel domainSetLabel;
+
+    /*
+     * This is internally managed driver state and should only be accessed
+     * via helpers below.
+     */
+    struct {
+        char doi[VIR_SECLABEL_DOI_BUFLEN];
+    } _private;
+};
+
+/* Global methods */
+int virSecLabelDriverStartup(virSecLabelDriverPtr * drv);
+
+void
+virSecLabelReportError(virConnectPtr conn, int code, const char *fmt, ...)
+    ATTRIBUTE_FORMAT(printf, 3, 4);
+
+/* Helpers */
+void virSecLabelDriverInit(virSecLabelDriverPtr drv);
+int virSecLabelDriverSetDOI(virConnectPtr conn,
+                            virSecLabelDriverPtr drv,
+                            const char *doi);
+const char *virSecLabelDriverGetDOI(virSecLabelDriverPtr drv);
+const char *virSecLabelDriverGetModel(virSecLabelDriverPtr drv);
+
+#endif /* __VIR_SECLABEL_H__ */
diff --git a/src/seclabel_selinux.c b/src/seclabel_selinux.c
new file mode 100644
index 0000000..f3dbb8d
--- /dev/null
+++ b/src/seclabel_selinux.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * Authors:
+ *     James Morris <jmorris@namei.org>
+ *
+ * SELinux seclabel driver.
+ */
+#include <config.h>
+#include <selinux/selinux.h>
+
+#include "seclabel.h"
+#include "seclabel_selinux.h"
+
+#define SECLABEL_SELINUX_VOID_DOI       "0"
+
+static int
+SELinuxSecLabelDriverProbe(void)
+{
+    return is_selinux_enabled();
+}
+
+static int
+SELinuxSecLabelDriverOpen(virConnectPtr conn, virSecLabelDriverPtr drv)
+{
+    /*
+     * Where will the DOI come from?  SELinux configuration, or qemu
+     * configuration? For the moment, we'll just set it to "0".
+     */
+    virSecLabelDriverSetDOI(conn, drv, SECLABEL_SELINUX_VOID_DOI);
+
+    return 0;
+}
+
+static int
+SELinuxSecLabelDomainGetLabel(virConnectPtr conn,
+                              virDomainObjPtr vm, virDomainSecLabelPtr sec)
+{
+    security_context_t ctx;
+
+    if (getpidcon(vm->pid, &ctx) == -1) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR, _("%s: error calling "
+                               "getpidcon(): %s"), __func__,
+                               strerror(errno));
+        return -1;
+    }
+
+    if (strlen((char *) ctx) >= VIR_SECLABEL_LABEL_BUFLEN) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR,
+                               _("%s: security label exceeds "
+                               "maximum length: %d"), __func__,
+                               VIR_SECLABEL_LABEL_BUFLEN - 1);
+        return -1;
+    }
+
+    strcpy(sec->label, (char *) ctx);
+    free(ctx);
+
+    sec->enforcing = security_getenforce();
+    if (sec->enforcing == -1) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR, _("%s: error calling "
+                               "security_getenforce(): %s"), __func__,
+                               strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+SELinuxSecLabelDomainSetLabel(virConnectPtr conn,
+                              virSecLabelDriverPtr drv, const virDomainSecLabelDefPtr secdef)
+{
+    /* TODO: verify DOI */
+
+    if (!STREQ(drv->name, secdef->model)) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR,
+                               _("%s: security label driver mismatch: "
+                                 "\'%s\' model configured for domain, but "
+                                 "hypervisor driver is \'%s\'."),
+                                 __func__, secdef->model, drv->name);
+        return -1;
+    }
+
+    if (setexeccon(secdef->label) == -1) {
+        virSecLabelReportError(conn, VIR_ERR_ERROR,
+                               _("%s: unable to set security context "
+                               "'\%s\': %s."), __func__, secdef->label,
+                               strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+virSecLabelDriver virSELinuxSecLabelDriver = {
+    .name               = "selinux",
+    .probe              = SELinuxSecLabelDriverProbe,
+    .open               = SELinuxSecLabelDriverOpen,
+    .domainGetLabel     = SELinuxSecLabelDomainGetLabel,
+    .domainSetLabel     = SELinuxSecLabelDomainSetLabel,
+};
diff --git a/src/seclabel_selinux.h b/src/seclabel_selinux.h
new file mode 100644
index 0000000..20e575d
--- /dev/null
+++ b/src/seclabel_selinux.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * Authors:
+ *     James Morris <jmorris@namei.org>
+ *
+ */
+#ifndef __VIR_SECLABEL_SELINUX_H__
+#define __VIR_SECLABEL_SELINUX_H__
+
+extern virSecLabelDriver virSELinuxSecLabelDriver;
+
+#endif /* __VIR_SECLABEL_SELINUX_H__ */
diff --git a/src/storage_backend.c b/src/storage_backend.c
index bb24727..227817a 100644
--- a/src/storage_backend.c
+++ b/src/storage_backend.c
@@ -246,6 +246,7 @@ virStorageBackendUpdateVolInfoFD(virConnectPtr conn,
     VIR_FREE(vol->target.perms.label);
 
 #if HAVE_SELINUX
+/* XXX: make this a security driver call */
     if (fgetfilecon(fd, &filecon) == -1) {
         if (errno != ENODATA && errno != ENOTSUP) {
             virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
diff --git a/src/test.c b/src/test.c
index 257fc8a..0e27789 100644
--- a/src/test.c
+++ b/src/test.c
@@ -3270,6 +3270,8 @@ static virDriver testDriver = {
     NULL, /* domainPinVcpu */
     NULL, /* domainGetVcpus */
     NULL, /* domainGetMaxVcpus */
+    NULL, /* domainGetSecLabel */
+    NULL, /* domainGetSecModel */
     testDomainDumpXML, /* domainDumpXML */
     testListDefinedDomains, /* listDefinedDomains */
     testNumOfDefinedDomains, /* numOfDefinedDomains */
diff --git a/src/uml_driver.c b/src/uml_driver.c
index 408096e..eb56687 100644
--- a/src/uml_driver.c
+++ b/src/uml_driver.c
@@ -1855,6 +1855,8 @@ static virDriver umlDriver = {
     NULL, /* domainPinVcpu */
     NULL, /* domainGetVcpus */
     NULL, /* domainGetMaxVcpus */
+    NULL, /* domainGetSecLabel */
+    NULL, /* domainGetSecModel */
     umlDomainDumpXML, /* domainDumpXML */
     umlListDefinedDomains, /* listDomains */
     umlNumDefinedDomains, /* numOfDomains */
diff --git a/src/virsh.c b/src/virsh.c
index 1a5b42f..afadc08 100644
--- a/src/virsh.c
+++ b/src/virsh.c
@@ -958,6 +958,7 @@ static const vshCmdOptDef opts_undefine[] = {
     {NULL, 0, 0, NULL}
 };
 
+/* XXX MAC policy for defining & undefining domains ?? */
 static int
 cmdUndefine(vshControl *ctl, const vshCmd *cmd)
 {
@@ -1519,6 +1520,8 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd)
 {
     virDomainInfo info;
     virDomainPtr dom;
+    virDomainSecModel secmodel;
+    virDomainSecLabel seclabel;
     int ret = TRUE, autostart;
     unsigned int id;
     char *str, uuid[VIR_UUID_STRING_BUFLEN];
@@ -1544,6 +1547,12 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd)
         free(str);
     }
 
+    memset(&secmodel, 0, sizeof secmodel);
+    if (virDomainGetSecModel(dom, &secmodel) == 0) {
+        vshPrint(ctl, "%-15s %s\n", _("Security model:"), secmodel.model);
+        vshPrint(ctl, "%-15s %s\n", _("Security DOI:"), secmodel.doi);
+    }
+
     if (virDomainGetInfo(dom, &info) == 0) {
         vshPrint(ctl, "%-15s %s\n", _("State:"),
                  N_(vshDomainStateToString(info.state)));
@@ -1577,6 +1586,19 @@ cmdDominfo(vshControl *ctl, const vshCmd *cmd)
                  autostart ? _("enable") : _("disable") );
     }
 
+    memset(&seclabel, 0, sizeof seclabel);
+    if (virDomainGetSecLabel(dom, &seclabel) == -1)
+        ret = FALSE;
+    else {
+        /*
+         * Security labels are only valid for active
+         * domains.
+         */
+        if (seclabel.label[0] != '\0')
+            vshPrint(ctl, "%-15s %s (%s)\n",
+                     _("Security label:"), seclabel.label,
+                     seclabel.enforcing ? "enforcing" : "permissive");
+    }
     virDomainFree(dom);
     return ret;
 }
diff --git a/src/virterror.c b/src/virterror.c
index 5a0b827..88b7e18 100644
--- a/src/virterror.c
+++ b/src/virterror.c
@@ -319,6 +319,9 @@ virDefaultErrorFunc(virErrorPtr err)
         case VIR_FROM_UML:
             dom = "UML ";
             break;
+        case VIR_FROM_SECLABEL:
+            dom = "Security Labeling ";
+            break;
     }
     if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
         domain = err->dom->name;
@@ -745,6 +748,12 @@ virErrorMsg(virErrorNumber error, const char *info)
                 else
                         errmsg = _("Node device not found: %s");
                 break;
+        case VIR_ERR_NO_SECLABEL_MODEL:
+            if (info == NULL)
+                errmsg = _("Security labeling model not found");
+            else
+                errmsg = _("Security labeling model not found: %s");
+            break;
     }
     return (errmsg);
 }
diff --git a/tests/daemon-conf b/tests/daemon-conf
index 03189d5..e492846 100755
--- a/tests/daemon-conf
+++ b/tests/daemon-conf
@@ -59,6 +59,9 @@ while :; do
   # Filter out this diagnostic.
   sed '/^Cannot set group when not running as root$/d' err > k && mv k err
 
+  # Filter out this diagnostic, too.
+  sed '/^Initialized security labeling driver/d' err > k && mv k err
+
   printf '%s\n\n' "remoteReadConfigFile: $f: $param_name: $msg" > expected-err
   diff -u expected-err err || fail=1
 
