* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
- * David PHAM-VAN <d.pham-van@ulteo.com> Ulteo SAS - http://www.ulteo.com
+ * David PHAM-VAN <d.pham-van@ulteo.com> Ulteo SAS - http://www.ulteo.com
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
#include <string.h>
#include <signal.h>
#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
#include <sys/socket.h>
#include <netdb.h>
#include "client.h"
#include "log.h"
+/* XML */
+#include <libxml2/libxml/parser.h>
+#include <libxml2/libxml/tree.h>
+#include <libxml2/libxml/xpath.h>
+#include <libxml2/libxml/xpathInternals.h>
+
+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;
}
+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; i<protocol_argc; i++) {
+ const char * p;
+ char * q;
+ int l = strlen(plugin->args[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;
+}
+
+void daemonize () {
+ const char *devnull = "/dev/null";
+
+ /* Fork once to ensure we aren't the process group leader */
+ int i = fork ();
+ if (i < 0) {
+ fprintf (stderr, "Unable to fork\n");
+ _exit (1);
+ }
+
+ /* Exit if we are the parent */
+ if (i > 0)
+ _exit (0); /* parent exits */
+
+ /* Start a new session */
+ setsid ();
+
+ /* Fork again so the session group leader exits */
+ i = fork ();
+ if (i < 0) {
+ fprintf (stderr, "Unable to fork\n");
+ _exit (1);
+ }
+
+ /* Exit if we are the parent */
+ if (i > 0)
+ _exit (0); /* parent exits */
+
+ if (chdir ("/") <0) {
+ fprintf (stderr, "Unable to chdir /\n");
+ _exit (1);
+ }
+
+ /* Now close all FDs and reopen the 3 stdxxx to /dev/null */
+ for (i = getdtablesize () - 1; i >= 0; i--)
+ close (i);
+
+ i = open (devnull, O_RDWR);
+ if (i == -1) {
+ fprintf (stderr, "Unable to open /dev/null\n");
+ _exit (1);
+ }
+ i = open (devnull, O_RDONLY);
+ if (i != 0) {
+ dup2 (i, 0);
+ close (i);
+ }
+ i = open (devnull, O_WRONLY);
+ if (i != 1) {
+ dup2 (i, 1);
+ close (i);
+ }
+ i = open (devnull, O_WRONLY);
+ if (i != 2) {
+ dup2 (i, 2);
+ close (i);
+ }
+}
+
+
+
int main(int argc, char* argv[]) {
/* Server */
char* pidfile = NULL;
int opt;
int foreground = 0;
+ char * xmlconfig = NULL;
/* General */
int retval;
- /* 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);
}
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]"
- " [-f foreground]"
- " [-p PIDFILE]\n", argv[0]);
+ " [-p PIDFILE]"
+ " [-f]"
+ " [-x XMLCONFIG]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
+ /* Daemonize before we start opening sockets, as this closes FDs */
+ if (!foreground)
+ daemonize();
+
+ if (pidfile != NULL) {
+ /* Attempt to open pidfile and write PID */
+ FILE* pidf = fopen(pidfile, "w");
+ if (pidf) {
+ fprintf(pidf, "%d\n", getpid());
+ fclose(pidf);
+ } else {
+ /* Warn on failure */
+ guacd_log_error("Could not write PID file: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
/* Set up logging prefix */
strncpy(log_prefix, basename(argv[0]), sizeof(log_prefix));
+ /* Open log as early as we can */
+ openlog(NULL, LOG_PID, LOG_DAEMON);
/* Get addresses for binding */
if ((retval = getaddrinfo(listen_address, listen_port, &hints, &addresses))) {
exit(EXIT_FAILURE);
}
- if (!foreground) {
- /* Fork into background */
- daemon_pid = fork();
-
- /* If error, fail */
- if (daemon_pid == -1) {
- guacd_log_error("Error forking daemon process: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- /* If parent, write PID file and exit */
- else if (daemon_pid != 0) {
-
- if (pidfile != NULL) {
-
- /* Attempt to open pidfile and write PID */
- FILE* pidf = fopen(pidfile, "w");
- if (pidf) {
- fprintf(pidf, "%d\n", daemon_pid);
- fclose(pidf);
- }
-
- /* Warn on failure */
- else {
- guacd_log_error("Could not write PID file: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- }
-
- exit(EXIT_SUCCESS);
- }
- }
-
- /* Open log */
- openlog(NULL, LOG_PID, LOG_DAEMON);
-
/* Ignore SIGPIPE */
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
guacd_log_info("Could not set handler for SIGPIPE to ignore. SIGPIPE may cause termination of the daemon.");
* particular client plugin.
*/
- child_pid = fork();
+ child_pid = (foreground>1)?0:fork();
/* If error, log */
if (child_pid == -1)
/* 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;
}
return 3;
}
+ xml_deinit();
return 0;
}