[PATCH 7/9] Implement the RPC protocol for the libvirt-lxc.la library

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: "Daniel P. Berrange" <berrange@xxxxxxxxxx>

Add the infrastructure for the libvirt-lxc.la library to
the remote protocol client and daemon

Signed-off-by: Daniel P. Berrange <berrange@xxxxxxxxxx>
---
 daemon/Makefile.am         |  13 +++++-
 daemon/libvirtd.c          |  14 ++++++
 daemon/libvirtd.h          |   1 +
 daemon/remote.c            |  52 +++++++++++++++++++++
 daemon/remote.h            |   3 ++
 src/Makefile.am            |  15 +++++-
 src/remote/lxc_protocol.x  |  50 ++++++++++++++++++++
 src/remote/remote_driver.c | 112 ++++++++++++++++++++++++++++++++++++---------
 8 files changed, 235 insertions(+), 25 deletions(-)
 create mode 100644 src/remote/lxc_protocol.x

diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 18a4bca..88a27f2 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -17,7 +17,9 @@ CLEANFILES =
 
 DAEMON_GENERATED =					\
 		$(srcdir)/remote_dispatch.h		\
-		$(srcdir)/qemu_dispatch.h
+		$(srcdir)/lxc_dispatch.h		\
+		$(srcdir)/qemu_dispatch.h		\
+		$(NULL)
 
 DAEMON_SOURCES =					\
 		libvirtd.c libvirtd.h			\
@@ -25,12 +27,14 @@ DAEMON_SOURCES =					\
 		remote.c remote.h			\
 		stream.c stream.h			\
 		../src/remote/remote_protocol.c		\
+		../src/remote/lxc_protocol.c		\
 		../src/remote/qemu_protocol.c		\
 		$(DAEMON_GENERATED)
 
 DISTCLEANFILES =
 EXTRA_DIST =						\
 	remote_dispatch.h				\
+	lxc_dispatch.h					\
 	qemu_dispatch.h					\
 	libvirtd.conf					\
 	libvirtd.init.in				\
@@ -53,6 +57,7 @@ EXTRA_DIST =						\
 BUILT_SOURCES =
 
 REMOTE_PROTOCOL = $(top_srcdir)/src/remote/remote_protocol.x
+LXC_PROTOCOL = $(top_srcdir)/src/remote/lxc_protocol.x
 QEMU_PROTOCOL = $(top_srcdir)/src/remote/qemu_protocol.x
 
 $(srcdir)/remote_dispatch.h: $(srcdir)/../src/rpc/gendispatch.pl \
@@ -60,6 +65,11 @@ $(srcdir)/remote_dispatch.h: $(srcdir)/../src/rpc/gendispatch.pl \
 	$(AM_V_GEN)$(PERL) -w $(srcdir)/../src/rpc/gendispatch.pl -b remote REMOTE \
 	  $(REMOTE_PROTOCOL) > $@
 
+$(srcdir)/lxc_dispatch.h: $(srcdir)/../src/rpc/gendispatch.pl \
+		$(LXC_PROTOCOL)
+	$(AM_V_GEN)$(PERL) -w $(srcdir)/../src/rpc/gendispatch.pl -b lxc LXC \
+	  $(LXC_PROTOCOL) > $@
+
 $(srcdir)/qemu_dispatch.h: $(srcdir)/../src/rpc/gendispatch.pl \
 		$(QEMU_PROTOCOL)
 	$(AM_V_GEN)$(PERL) -w $(srcdir)/../src/rpc/gendispatch.pl -b qemu QEMU \
@@ -116,6 +126,7 @@ libvirtd_LDADD += ../src/libvirt_probes.lo
 endif
 
 libvirtd_LDADD += \
+	../src/libvirt-lxc.la \
 	../src/libvirt-qemu.la
 
 if ! WITH_DRIVER_MODULES
diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c
index fa4d129..0513312 100644
--- a/daemon/libvirtd.c
+++ b/daemon/libvirtd.c
@@ -103,6 +103,7 @@ virNetSASLContextPtr saslCtxt = NULL;
 #endif
 virNetServerProgramPtr remoteProgram = NULL;
 virNetServerProgramPtr qemuProgram = NULL;
+virNetServerProgramPtr lxcProgram = NULL;
 
 enum {
     VIR_DAEMON_ERR_NONE = 0,
@@ -1349,6 +1350,18 @@ int main(int argc, char **argv) {
         goto cleanup;
     }
 
+    if (!(lxcProgram = virNetServerProgramNew(LXC_PROGRAM,
+                                              LXC_PROTOCOL_VERSION,
+                                              lxcProcs,
+                                              lxcNProcs))) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+    if (virNetServerAddProgram(srv, lxcProgram) < 0) {
+        ret = VIR_DAEMON_ERR_INIT;
+        goto cleanup;
+    }
+
     if (!(qemuProgram = virNetServerProgramNew(QEMU_PROGRAM,
                                                QEMU_PROTOCOL_VERSION,
                                                qemuProcs,
@@ -1454,6 +1467,7 @@ int main(int argc, char **argv) {
 cleanup:
     virNetlinkEventServiceStopAll();
     virObjectUnref(remoteProgram);
+    virObjectUnref(lxcProgram);
     virObjectUnref(qemuProgram);
     virNetServerClose(srv);
     virObjectUnref(srv);
diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h
index 69a77ea..6254208 100644
--- a/daemon/libvirtd.h
+++ b/daemon/libvirtd.h
@@ -32,6 +32,7 @@
 # include <rpc/types.h>
 # include <rpc/xdr.h>
 # include "remote_protocol.h"
+# include "lxc_protocol.h"
 # include "qemu_protocol.h"
 # include "virlog.h"
 # include "virthread.h"
diff --git a/daemon/remote.c b/daemon/remote.c
index a69dc5d..a4cedbb 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -50,6 +50,7 @@
 #include "virprocess.h"
 #include "remote_protocol.h"
 #include "qemu_protocol.h"
+#include "lxc_protocol.h"
 
 
 #define VIR_FROM_THIS VIR_FROM_RPC
@@ -105,6 +106,7 @@ remoteSerializeDomainDiskErrors(virDomainDiskErrorPtr errors,
 
 #include "remote_dispatch.h"
 #include "qemu_dispatch.h"
+#include "lxc_dispatch.h"
 
 
 /* Prototypes */
@@ -4618,6 +4620,56 @@ cleanup:
     return rv;
 }
 
+static int
+lxcDispatchDomainOpenNamespace(virNetServerPtr server ATTRIBUTE_UNUSED,
+                               virNetServerClientPtr client ATTRIBUTE_UNUSED,
+                               virNetMessagePtr msg ATTRIBUTE_UNUSED,
+                               virNetMessageErrorPtr rerr,
+                               lxc_domain_open_namespace_args *args)
+{
+    int rv = -1;
+    struct daemonClientPrivate *priv =
+        virNetServerClientGetPrivateData(client);
+    int *fdlist = NULL;
+    int ret;
+    virDomainPtr dom = NULL;
+    size_t i;
+
+    if (!priv->conn) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+        goto cleanup;
+    }
+
+    if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+        goto cleanup;
+
+    ret = virDomainLxcOpenNamespace(dom,
+                                    &fdlist,
+                                    args->flags);
+    if (ret < 0)
+        goto cleanup;
+
+    /* We shouldn't have received any from the client,
+     * but in case they're playing games with us, prevent
+     * a resource leak
+     */
+    for (i = 0 ; i < msg->nfds ; i++)
+        VIR_FORCE_CLOSE(msg->fds[i]);
+    VIR_FREE(msg->fds);
+    msg->nfds = 0;
+
+    msg->fds = fdlist;
+    msg->nfds = ret;
+
+    rv = 1;
+
+cleanup:
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    virDomainFree(dom);
+    return rv;
+}
+
 /*----- Helpers. -----*/
 
 /* get_nonnull_domain and get_nonnull_network turn an on-wire
diff --git a/daemon/remote.h b/daemon/remote.h
index 493171f..d42a19e 100644
--- a/daemon/remote.h
+++ b/daemon/remote.h
@@ -32,6 +32,9 @@
 extern virNetServerProgramProc remoteProcs[];
 extern size_t remoteNProcs;
 
+extern virNetServerProgramProc lxcProcs[];
+extern size_t lxcNProcs;
+
 extern virNetServerProgramProc qemuProcs[];
 extern size_t qemuNProcs;
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 13b7e30..d0f431e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -272,19 +272,28 @@ REMOTE_DRIVER_GENERATED = \
 		$(srcdir)/remote/remote_protocol.c		\
 		$(srcdir)/remote/remote_protocol.h		\
 		$(srcdir)/remote/remote_client_bodies.h		\
+		$(srcdir)/remote/lxc_protocol.c			\
+		$(srcdir)/remote/lxc_protocol.h			\
+		$(srcdir)/remote/lxc_client_bodies.h		\
 		$(srcdir)/remote/qemu_protocol.c		\
 		$(srcdir)/remote/qemu_protocol.h		\
 		$(srcdir)/remote/qemu_client_bodies.h
 
 REMOTE_PROTOCOL = $(srcdir)/remote/remote_protocol.x
+LXC_PROTOCOL = $(srcdir)/remote/lxc_protocol.x
 QEMU_PROTOCOL = $(srcdir)/remote/qemu_protocol.x
-REMOTE_DRIVER_PROTOCOL = $(REMOTE_PROTOCOL) $(QEMU_PROTOCOL)
+REMOTE_DRIVER_PROTOCOL = $(REMOTE_PROTOCOL) $(QEMU_PROTOCOL) $(LXC_PROTOCOL)
 
 $(srcdir)/remote/remote_client_bodies.h: $(srcdir)/rpc/gendispatch.pl \
 		$(REMOTE_PROTOCOL)
 	$(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl \
 	  -k remote REMOTE $(REMOTE_PROTOCOL) > $@
 
+$(srcdir)/remote/lxc_client_bodies.h: $(srcdir)/rpc/gendispatch.pl \
+		$(LXC_PROTOCOL)
+	$(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl \
+	  -k lxc LXC $(LXC_PROTOCOL) > $@
+
 $(srcdir)/remote/qemu_client_bodies.h: $(srcdir)/rpc/gendispatch.pl \
 		$(QEMU_PROTOCOL)
 	$(AM_V_GEN)$(PERL) -w $(srcdir)/rpc/gendispatch.pl \
@@ -374,6 +383,7 @@ EXTRA_DIST += check-symfile.pl check-symsorting.pl
 
 PROTOCOL_STRUCTS = \
 	$(srcdir)/remote_protocol-structs \
+	$(srcdir)/lxc_protocol-structs \
 	$(srcdir)/qemu_protocol-structs \
 	$(srcdir)/virnetprotocol-structs \
 	$(srcdir)/virkeepaliveprotocol-structs
@@ -382,7 +392,7 @@ check-protocol: $(PROTOCOL_STRUCTS) $(PROTOCOL_STRUCTS:structs=struct)
 
 # The .o file that pdwtags parses is created as a side effect of running
 # libtool; but from make's perspective we depend on the .lo file.
-$(srcdir)/remote_protocol-struct $(srcdir)/qemu_protocol-struct: \
+$(srcdir)/remote_protocol-struct $(srcdir)/qemu_protocol-struct $(srcdir)/lxc_protocol-struct: \
 		$(srcdir)/%-struct: libvirt_driver_remote_la-%.lo
 	$(PDWTAGS)
 $(srcdir)/virnetprotocol-struct $(srcdir)/virkeepaliveprotocol-struct: \
@@ -1546,6 +1556,7 @@ tapset_DATA = libvirt_probes.stp libvirt_qemu_probes.stp libvirt_functions.stp
 RPC_PROBE_FILES = $(srcdir)/rpc/virnetprotocol.x \
 		  $(srcdir)/rpc/virkeepaliveprotocol.x \
 		  $(srcdir)/remote/remote_protocol.x \
+		  $(srcdir)/remote/lxc_protocol.x \
 		  $(srcdir)/remote/qemu_protocol.x
 
 libvirt_functions.stp: $(RPC_PROBE_FILES) $(srcdir)/rpc/gensystemtap.pl
diff --git a/src/remote/lxc_protocol.x b/src/remote/lxc_protocol.x
new file mode 100644
index 0000000..f2e0b44
--- /dev/null
+++ b/src/remote/lxc_protocol.x
@@ -0,0 +1,50 @@
+/* -*- c -*-
+ * lxc_protocol.x: private protocol for communicating between
+ *   remote_internal driver and libvirtd.  This protocol is
+ *   internal and may change at any time.
+ *
+ * Copyright (C) 2010-2012 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.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Chris Lalancette <clalance@xxxxxxxxxx>
+ */
+
+%#include "internal.h"
+%#include "remote_protocol.h"
+%#include <arpa/inet.h>
+
+/*----- Protocol. -----*/
+struct lxc_domain_open_namespace_args {
+    remote_nonnull_domain dom;
+    unsigned int flags;
+};
+
+
+/* Define the program number, protocol version and procedure numbers here. */
+const LXC_PROGRAM = 0x00068000;
+const LXC_PROTOCOL_VERSION = 1;
+
+enum lxc_procedure {
+    /* Each function must have a three-word comment.  The first word is
+     * whether gendispatch.pl handles daemon, the second whether
+     * it handles src/remote.
+     * The last argument describes priority of API. There are two accepted
+     * values: low, high; Each API that might eventually access hypervisor's
+     * monitor (and thus block) MUST fall into low priority. However, there
+     * are some exceptions to this rule, e.g. domainDestroy. Other APIs MAY
+     * be marked as high priority. If in doubt, it's safe to choose low. */
+    LXC_PROC_DOMAIN_OPEN_NAMESPACE = 1 /* skipgen skipgen priority:low */
+};
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index ae861cc..5f9b936 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -37,6 +37,7 @@
 #include "virbuffer.h"
 #include "remote_driver.h"
 #include "remote_protocol.h"
+#include "lxc_protocol.h"
 #include "qemu_protocol.h"
 #include "viralloc.h"
 #include "virutil.h"
@@ -77,6 +78,7 @@ struct private_data {
     virNetClientPtr client;
     virNetClientProgramPtr remoteProgram;
     virNetClientProgramPtr qemuProgram;
+    virNetClientProgramPtr lxcProgram;
 
     int counter; /* Serial number for RPC */
 
@@ -93,6 +95,7 @@ struct private_data {
 
 enum {
     REMOTE_CALL_QEMU              = (1 << 0),
+    REMOTE_CALL_LXC               = (1 << 1),
 };
 
 
@@ -110,10 +113,13 @@ static int call(virConnectPtr conn, struct private_data *priv,
                 unsigned int flags, int proc_nr,
                 xdrproc_t args_filter, char *args,
                 xdrproc_t ret_filter, char *ret);
-static int callWithFD(virConnectPtr conn, struct private_data *priv,
-                      unsigned int flags, int fd, int proc_nr,
-                      xdrproc_t args_filter, char *args,
-                      xdrproc_t ret_filter, char *ret);
+static int callFull(virConnectPtr conn, struct private_data *priv,
+                    unsigned int flags,
+                    int *fdin, size_t fdinlen,
+                    int **fdout, size_t *fdoutlen,
+                    int proc_nr,
+                    xdrproc_t args_filter, char *args,
+                    xdrproc_t ret_filter, char *ret);
 static int remoteAuthenticate(virConnectPtr conn, struct private_data *priv,
                               virConnectAuthPtr auth, const char *authtype);
 #if HAVE_SASL
@@ -757,6 +763,12 @@ doRemoteOpen(virConnectPtr conn,
                                                        ARRAY_CARDINALITY(remoteDomainEvents),
                                                        conn)))
         goto failed;
+    if (!(priv->lxcProgram = virNetClientProgramNew(LXC_PROGRAM,
+                                                    LXC_PROTOCOL_VERSION,
+                                                    NULL,
+                                                    0,
+                                                    NULL)))
+        goto failed;
     if (!(priv->qemuProgram = virNetClientProgramNew(QEMU_PROGRAM,
                                                      QEMU_PROTOCOL_VERSION,
                                                      NULL,
@@ -765,6 +777,7 @@ doRemoteOpen(virConnectPtr conn,
         goto failed;
 
     if (virNetClientAddProgram(priv->client, priv->remoteProgram) < 0 ||
+        virNetClientAddProgram(priv->client, priv->lxcProgram) < 0 ||
         virNetClientAddProgram(priv->client, priv->qemuProgram) < 0)
         goto failed;
 
@@ -849,6 +862,7 @@ no_memory:
 
  failed:
     virObjectUnref(priv->remoteProgram);
+    virObjectUnref(priv->lxcProgram);
     virObjectUnref(priv->qemuProgram);
     virNetClientClose(priv->client);
     virObjectUnref(priv->client);
@@ -1011,8 +1025,9 @@ doRemoteClose(virConnectPtr conn, struct private_data *priv)
     virObjectUnref(priv->client);
     priv->client = NULL;
     virObjectUnref(priv->remoteProgram);
+    virObjectUnref(priv->lxcProgram);
     virObjectUnref(priv->qemuProgram);
-    priv->remoteProgram = priv->qemuProgram = NULL;
+    priv->remoteProgram = priv->qemuProgram = priv->lxcProgram = NULL;
 
     /* Free hostname copy */
     VIR_FREE(priv->hostname);
@@ -5393,6 +5408,8 @@ remoteDomainOpenGraphics(virDomainPtr dom,
     int rv = -1;
     remote_domain_open_graphics_args args;
     struct private_data *priv = dom->conn->privateData;
+    int fdin[] = { fd };
+    size_t fdinlen = ARRAY_CARDINALITY(fdin);
 
     remoteDriverLock(priv);
 
@@ -5400,9 +5417,12 @@ remoteDomainOpenGraphics(virDomainPtr dom,
     args.idx = idx;
     args.flags = flags;
 
-    if (callWithFD(dom->conn, priv, 0, fd, REMOTE_PROC_DOMAIN_OPEN_GRAPHICS,
-                   (xdrproc_t) xdr_remote_domain_open_graphics_args, (char *) &args,
-                   (xdrproc_t) xdr_void, NULL) == -1)
+    if (callFull(dom->conn, priv, 0,
+                 fdin, fdinlen,
+                 NULL, NULL,
+                 REMOTE_PROC_DOMAIN_OPEN_GRAPHICS,
+                 (xdrproc_t) xdr_remote_domain_open_graphics_args, (char *) &args,
+                 (xdrproc_t) xdr_void, NULL) == -1)
         goto done;
 
     rv = 0;
@@ -5507,6 +5527,7 @@ done:
 }
 
 #include "remote_client_bodies.h"
+#include "lxc_client_bodies.h"
 #include "qemu_client_bodies.h"
 
 /*
@@ -5514,22 +5535,30 @@ done:
  * send that to the server and wait for reply
  */
 static int
-callWithFD(virConnectPtr conn ATTRIBUTE_UNUSED,
-           struct private_data *priv,
-           unsigned int flags,
-           int fd,
-           int proc_nr,
-           xdrproc_t args_filter, char *args,
-           xdrproc_t ret_filter, char *ret)
+callFull(virConnectPtr conn ATTRIBUTE_UNUSED,
+         struct private_data *priv,
+         unsigned int flags,
+         int *fdin,
+         size_t fdinlen,
+         int **fdout,
+         size_t *fdoutlen,
+         int proc_nr,
+         xdrproc_t args_filter, char *args,
+         xdrproc_t ret_filter, char *ret)
 {
     int rv;
-    virNetClientProgramPtr prog = flags & REMOTE_CALL_QEMU ? priv->qemuProgram : priv->remoteProgram;
+    virNetClientProgramPtr prog;
     int counter = priv->counter++;
     virNetClientPtr client = priv->client;
-    int fds[] = { fd };
-    size_t nfds = fd == -1 ? 0 : 1;
     priv->localUses++;
 
+    if (flags & REMOTE_CALL_QEMU)
+        prog = priv->qemuProgram;
+    else if (flags & REMOTE_CALL_LXC)
+        prog = priv->lxcProgram;
+    else
+        prog = priv->remoteProgram;
+
     /* Unlock, so that if we get any async events/stream data
      * while processing the RPC, we don't deadlock when our
      * callbacks for those are invoked
@@ -5539,7 +5568,8 @@ callWithFD(virConnectPtr conn ATTRIBUTE_UNUSED,
                                  client,
                                  counter,
                                  proc_nr,
-                                 nfds, nfds ? fds : NULL, NULL, NULL,
+                                 fdinlen, fdin,
+                                 fdoutlen, fdout,
                                  args_filter, args,
                                  ret_filter, ret);
     remoteDriverLock(priv);
@@ -5556,9 +5586,12 @@ call(virConnectPtr conn,
      xdrproc_t args_filter, char *args,
      xdrproc_t ret_filter, char *ret)
 {
-    return callWithFD(conn, priv, flags, -1, proc_nr,
-                      args_filter, args,
-                      ret_filter, ret);
+    return callFull(conn, priv, flags,
+                    NULL, 0,
+                    NULL, NULL,
+                    proc_nr,
+                    args_filter, args,
+                    ret_filter, ret);
 }
 
 
@@ -5834,6 +5867,40 @@ done:
     return rv;
 }
 
+
+static int
+remoteDomainLxcOpenNamespace(virDomainPtr domain,
+                             int **fdlist,
+                             unsigned int flags)
+{
+    int rv = -1;
+    lxc_domain_open_namespace_args args;
+    struct private_data *priv = domain->conn->privateData;
+    size_t nfds = 0;
+
+    remoteDriverLock(priv);
+
+    make_nonnull_domain(&args.dom, domain);
+    args.flags = flags;
+
+    *fdlist = NULL;
+
+    if (callFull(domain->conn, priv, REMOTE_CALL_LXC,
+                 NULL, 0,
+                 fdlist, &nfds,
+                 LXC_PROC_DOMAIN_OPEN_NAMESPACE,
+                 (xdrproc_t) xdr_lxc_domain_open_namespace_args, (char *) &args,
+                 (xdrproc_t) xdr_void, NULL) == -1)
+        goto done;
+
+    rv = nfds;
+
+done:
+    remoteDriverUnlock(priv);
+    return rv;
+}
+
+
 static void
 remoteDomainEventQueue(struct private_data *priv, virDomainEventPtr event)
 {
@@ -6153,6 +6220,7 @@ static virDriver remote_driver = {
     .nodeGetMemoryParameters = remoteNodeGetMemoryParameters, /* 0.10.2 */
     .nodeGetCPUMap = remoteNodeGetCPUMap, /* 1.0.0 */
     .domainFSTrim = remoteDomainFSTrim, /* 1.0.1 */
+    .domainLxcOpenNamespace = remoteDomainLxcOpenNamespace, /* 1.0.2 */
 };
 
 static virNetworkDriver network_driver = {
-- 
1.7.11.7

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list


[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]