From f21c016ee7798bab4f15cca8350f71917bd8f746 Mon Sep 17 00:00:00 2001 From: Alex Bligh Date: Mon, 10 Sep 2012 20:40:32 +0100 Subject: [PATCH] Add provision for XML parameters on the command line avoiding negotiation with client --- Makefile.am | 3 + configure.in | 2 + src/daemon.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 194 insertions(+), 5 deletions(-) diff --git a/Makefile.am b/Makefile.am index 4075eb7..35a5e18 100644 --- a/Makefile.am +++ b/Makefile.am @@ -39,6 +39,9 @@ initdir = @init_dir@ AM_CFLAGS = -Werror -Wall -pedantic -Iinclude +guacd_CFLAGS = $(AM_CFLAGS) $(XML_CFLAGS) +guacd_LDFLAGS = $(XML_LIBS) + sbin_PROGRAMS = guacd init_SCRIPTS = init.d/guacd man_MANS = man/guacd.8 diff --git a/configure.in b/configure.in index ee51452..19775c2 100644 --- a/configure.in +++ b/configure.in @@ -52,6 +52,8 @@ AC_CHECK_HEADERS([netinet/in.h stdlib.h string.h sys/socket.h syslog.h unistd.h AC_FUNC_MALLOC AC_CHECK_FUNCS([memset socket strerror fork]) +PKG_CHECK_MODULES(XML, libxml-2.0) + # Options AC_ARG_WITH(init_dir, [AS_HELP_STRING([--with-init-dir=], diff --git a/src/daemon.c b/src/daemon.c index 5435865..f952d67 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,71 @@ #include "client.h" #include "log.h" +/* XML */ +#include +#include +#include +#include + +void xml_init () { + /* check the version. This calls xmlInitParser() */ + /* braces to stop indent getting confused */ + {LIBXML_TEST_VERSION} +} + +void xml_deinit () { + xmlCleanupParser (); +} + +xmlNodePtr xml_get_node (xmlDoc * pDoc, const char *xpathexpr) { + xmlChar *xpath_expr = (xmlChar *) xpathexpr; + xmlXPathContextPtr xpathCtx = NULL; + xmlXPathObjectPtr xpathObj = NULL; + xmlNodeSetPtr nodeSet = NULL; + int size; + xmlNodePtr myNode = NULL; + + /* Create xpath evaluation context */ + if (NULL == (xpathCtx = xmlXPathNewContext (pDoc))) + return NULL; + + /* Evaluate xpath expression */ + if (NULL == (xpathObj = xmlXPathEvalExpression (xpath_expr, xpathCtx))) { + xmlXPathFreeContext (xpathCtx); + return NULL; + } + + nodeSet = xpathObj->nodesetval; + size = (nodeSet) ? nodeSet->nodeNr : 0; + if (size == 1) + myNode = nodeSet->nodeTab[0]; + + xmlXPathFreeObject (xpathObj); + xmlXPathFreeContext (xpathCtx); + return myNode; +} + +char * xml_get_string (xmlDoc * pDoc, char *xpathexpr) { + xmlNodePtr config_node = NULL; + xmlChar *propval = NULL; + + /* Find the node in question beneath the config node */ + if (NULL == (config_node = xml_get_node (pDoc, xpathexpr))) + return NULL; + + /* Find the property attached to that node; if it's not there, return 0 */ + if (NULL == (propval = xmlNodeGetContent (config_node))) + return NULL; + + /* We would like to just return propval here, but that's an xmlChar * allocated by + * libxml, and thus the caller can't just free() it - it would need to be xmlFree()'d. + * so we'll fiddle around and generate our own copy allocated with libc + */ + char *value = strdup ((char *) propval); + xmlFree (propval); /* as xmlGetProp makes a copy of the string */ + return value; /* caller's responsibility to free() this */ +} + void guacd_handle_connection(int fd) { guac_client* client; @@ -174,6 +240,113 @@ void guacd_handle_connection(int fd) { } +void guacd_handle_connection_xml(int fd, char* xmlconfig) { + + guac_client* client = NULL; + guac_client_plugin* plugin = NULL; + char ** protocol_argv = NULL; + int protocol_argc = 0; + xmlDoc * pDoc = NULL; + char * protocol = NULL; + guac_socket* socket = NULL; + + if (NULL == (socket = guac_socket_open(fd))) { + guacd_log_guac_error("Could not open socket"); + goto error; + } + + if (NULL == (pDoc = xmlParseMemory (xmlconfig, strlen(xmlconfig)))) { + guacd_log_guac_error("Could not parse XML"); + goto error; + } + + if (NULL == (protocol = xml_get_string(pDoc, "/params/protocol"))) { + guacd_log_guac_error("Could not parse XML"); + goto error; + } + + guacd_log_info("Opening protocol '%s'", protocol); + + /* Get plugin from protocol in select */ + if (NULL == (plugin = guac_client_plugin_open(protocol))) { + guacd_log_guac_error("Error loading client plugin"); + goto error; + } + + /* Now parse protocol strings */ + const char ** arg; + const char * params = "/params/"; + int lparams = strlen(params); + for (arg = plugin->args; *arg && **arg; arg++) + protocol_argc++; + if (NULL == (protocol_argv = calloc(sizeof(char *), protocol_argc+1))) { + guacd_log_guac_error("Cannot allocate protocol arguments"); + goto error; + } + + int i; + for (i=0; iargs[i]); + char * argname = malloc(lparams+l+1); + if (!argname) { + guacd_log_guac_error("Error duplicating argument list"); + goto error; + } + strncpy(argname, params, lparams); + /* replace non-alpha characters by '_' for XML */ + for (p = plugin->args[i], q = argname+lparams; *p; p++, q++) + *q = isalnum(*p)?*p:'_'; + *q='\0'; + char * value = xml_get_string(pDoc, argname); + if (!value) + value = strdup(""); + guacd_log_info("Argument '%s' set to '%s'", plugin->args[i], value); + protocol_argv[i]=value; + } + + guacd_log_info("Starting protocol %s, %d arguments", protocol, protocol_argc); + + /* Load and init client */ + if (NULL == (client = guac_client_plugin_get_client(plugin, socket, + protocol_argc, protocol_argv, + guacd_client_log_info, guacd_client_log_error))) { + guacd_log_guac_error("Error instantiating client"); + goto error; + } + + /* Start client threads */ + guacd_log_info("Starting client"); + if (guacd_client_start(client)) + guacd_log_error("Client finished abnormally"); + else + guacd_log_info("Client finished normally"); + + error: + /* Clean up */ + if (client) + guac_client_free(client); + + if (plugin && guac_client_plugin_close(plugin)) + guacd_log_error("Error closing client plugin"); + + if (protocol_argv) { + char **parg; + for (parg = protocol_argv ; *parg; parg++) + free(*parg); + free(protocol_argv); + } + if (pDoc) + xmlFreeDoc(pDoc); + if (protocol) + free (protocol); + if (socket) + guac_socket_close(socket); + + return; +} + int main(int argc, char* argv[]) { /* Server */ @@ -201,6 +374,7 @@ int main(int argc, char* argv[]) { char* pidfile = NULL; int opt; int foreground = 0; + char * xmlconfig = NULL; /* General */ int retval; @@ -208,8 +382,10 @@ int main(int argc, char* argv[]) { /* Daemon Process */ pid_t daemon_pid; + xml_init(); + /* Parse arguments */ - while ((opt = getopt(argc, argv, "l:b:p:f")) != -1) { + while ((opt = getopt(argc, argv, "l:b:p:x:f")) != -1) { if (opt == 'l') { listen_port = strdup(optarg); } @@ -217,18 +393,22 @@ int main(int argc, char* argv[]) { listen_address = strdup(optarg); } else if (opt == 'f') { - foreground = 1; + foreground++; } else if (opt == 'p') { pidfile = strdup(optarg); } + else if (opt == 'x') { + xmlconfig = strdup (optarg); + } else { fprintf(stderr, "USAGE: %s" " [-l LISTENPORT]" " [-b LISTENADDRESS]" " [-p PIDFILE]" - " [-f]\n", argv[0]); + " [-f]" + " [-x XMLCONFIG]\n", argv[0]); exit(EXIT_FAILURE); } @@ -384,7 +564,7 @@ int main(int argc, char* argv[]) { * particular client plugin. */ - child_pid = fork(); + child_pid = (foreground>1)?0:fork(); /* If error, log */ if (child_pid == -1) @@ -392,7 +572,10 @@ int main(int argc, char* argv[]) { /* If child, start client, and exit when finished */ else if (child_pid == 0) { - guacd_handle_connection(connected_socket_fd); + if (xmlconfig) + guacd_handle_connection_xml(connected_socket_fd, xmlconfig); + else + guacd_handle_connection(connected_socket_fd); close(connected_socket_fd); return 0; } @@ -410,6 +593,7 @@ int main(int argc, char* argv[]) { return 3; } + xml_deinit(); return 0; } -- 1.7.10.4