summaryrefslogtreecommitdiffstats
path: root/qarsh/qarsh_packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'qarsh/qarsh_packet.c')
-rw-r--r--qarsh/qarsh_packet.c497
1 files changed, 497 insertions, 0 deletions
diff --git a/qarsh/qarsh_packet.c b/qarsh/qarsh_packet.c
new file mode 100644
index 0000000..4fef7c0
--- /dev/null
+++ b/qarsh/qarsh_packet.c
@@ -0,0 +1,497 @@
+
+#include <string.h>
+#include <assert.h>
+#include <sys/wait.h>
+
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+
+#include "qarsh_packet.h"
+
+/* Prototypes */
+static char *get_xpath_string(xmlXPathContextPtr ctxt, const char *xpath_query);
+
+int parse_qp_hello(xmlXPathContextPtr ctxt, struct qa_packet *qp);
+int parse_qp_returncode(xmlXPathContextPtr ctxt, struct qa_packet *qp);
+int parse_qp_ack(xmlXPathContextPtr ctxt, struct qa_packet *qp);
+int parse_qp_runcmd(xmlXPathContextPtr ctxt, struct qa_packet *qp);
+int parse_qp_cmdexit(xmlXPathContextPtr ctxt, struct qa_packet *qp);
+
+void string_qp_hello(xmlNodePtr node, struct qa_packet *qp);
+void string_qp_returncode(xmlNodePtr node, struct qa_packet *qp);
+void string_qp_runcmd(xmlNodePtr node, struct qa_packet *qp);
+void string_qp_ack(xmlNodePtr node, struct qa_packet *qp);
+void string_qp_cmdexit(xmlNodePtr node, struct qa_packet *qp);
+
+void free_qp_hello(struct qa_packet *qp);
+void free_qp_returncode(struct qa_packet *qp);
+void free_qp_runcmd(struct qa_packet *qp);
+
+void dump_qp_ack(struct qa_packet *qp);
+void dump_qp_runcmd(struct qa_packet *qp);
+void dump_qp_returncode(struct qa_packet *qp);
+void dump_qp_cmdexit(struct qa_packet *qp);
+
+
+struct packet_internals {
+ char *pi_name;
+ int (*pi_parse)(xmlXPathContextPtr ctxt, struct qa_packet *qp);
+ void (*pi_string)(xmlNodePtr node, struct qa_packet *qp);
+ void (*pi_free)(struct qa_packet *qp);
+ void (*pi_dump)(struct qa_packet *qp);
+} qa_pi[] = {
+ {
+ .pi_name = "",
+ .pi_parse = NULL,
+ .pi_string = NULL,
+ .pi_free = NULL,
+ .pi_dump = NULL
+
+ }, {
+ .pi_name = "hello",
+ .pi_parse = parse_qp_hello,
+ .pi_string = string_qp_hello,
+ .pi_free = free_qp_hello
+ }, {
+ .pi_name = "returncode",
+ .pi_parse = parse_qp_returncode,
+ .pi_string = string_qp_returncode,
+ .pi_free = free_qp_returncode,
+ .pi_dump = dump_qp_returncode
+ }, {
+ .pi_name = "runcmd",
+ .pi_parse = parse_qp_runcmd,
+ .pi_string = string_qp_runcmd,
+ .pi_free = free_qp_runcmd,
+ .pi_dump = dump_qp_runcmd
+ }, {
+ .pi_name = "ack",
+ .pi_parse = parse_qp_ack,
+ .pi_string = string_qp_ack,
+ .pi_free = NULL,
+ .pi_dump = dump_qp_ack
+ }, {
+ .pi_name = "cmdexit",
+ .pi_parse = parse_qp_cmdexit,
+ .pi_string = string_qp_cmdexit,
+ .pi_free = NULL,
+ .pi_dump = dump_qp_cmdexit
+ }
+};
+
+#define QP_TYPES (sizeof qa_pi / sizeof *qa_pi)
+#define QP_NAME(n) qa_pi[n].pi_name
+
+
+/* XML Strings */
+const xmlChar *QP_QARSH_XML = (xmlChar *)"qarsh";
+const xmlChar *QP_PACKET_XML = (xmlChar *)"packet";
+const xmlChar *QP_TYPE_XML = (xmlChar *)"type";
+const xmlChar *QP_PARAM_XML = (xmlChar *)"param";
+const xmlChar *QP_NAME_XML = (xmlChar *)"name";
+const xmlChar *QP_SEQ_XML = (xmlChar *)"seq";
+
+enum qa_packet_type
+parse_packet_type(char *s)
+{
+ int i;
+ for (i = 0; i < QP_TYPES; i++) {
+ if (strcasecmp(s, QP_NAME(i)) == 0) {
+ return (enum qa_packet_type)i;
+ }
+ }
+ return QP_INVALID;
+}
+
+char *
+qp_packet_type(enum qa_packet_type t)
+{
+ return QP_NAME(t);
+}
+
+
+/*
+ * Packet parsing functions
+ */
+int
+parse_qp_hello(xmlXPathContextPtr ctxt, struct qa_packet *qp)
+{
+ qp->qp_hello.qp_greeting = get_xpath_string(ctxt,
+ "param[@name='greeting']");
+ return 0;
+}
+
+int
+parse_qp_returncode(xmlXPathContextPtr ctxt, struct qa_packet *qp)
+{
+ char *s;
+ s = get_xpath_string(ctxt, "param[@name='rc']");
+ qp->qp_returncode.qp_rc = atoi(s);
+ free(s);
+ s = get_xpath_string(ctxt, "param[@name='errno']");
+ qp->qp_returncode.qp_errno = atoi(s);
+ free(s);
+ qp->qp_returncode.qp_strerror = get_xpath_string(ctxt,
+ "param[@name='strerror']");
+ return 0;
+}
+
+int
+parse_qp_runcmd(xmlXPathContextPtr ctxt, struct qa_packet *qp)
+{
+ char *s;
+ qp->qp_runcmd.qp_cmdline = get_xpath_string(ctxt,
+ "param[@name='cmdline']");
+ s = get_xpath_string(ctxt, "param[@name='stdin']");
+ qp->qp_runcmd.qp_stdin_port = atoi(s);
+ free(s);
+ s = get_xpath_string(ctxt, "param[@name='stdout']");
+ qp->qp_runcmd.qp_stdout_port = atoi(s);
+ free(s);
+ s = get_xpath_string(ctxt, "param[@name='stderr']");
+ qp->qp_runcmd.qp_stderr_port = atoi(s);
+ free(s);
+ return 0;
+}
+
+int
+parse_qp_ack(xmlXPathContextPtr ctxt, struct qa_packet *qp)
+{
+ char *s;
+ s = get_xpath_string(ctxt, "param[@name='type']");
+ qp->qp_ack.qp_ack_type = parse_packet_type(s);
+ free(s);
+ s = get_xpath_string(ctxt, "param[@name='seq']");
+ qp->qp_ack.qp_ack_seq = atoi(s);
+ free(s);
+ return 0;
+}
+
+int
+parse_qp_cmdexit(xmlXPathContextPtr ctxt, struct qa_packet *qp)
+{
+ char *s;
+
+ s = get_xpath_string(ctxt, "param[@name='status']");
+ qp->qp_cmdexit.qp_status = atoi(s);
+ free(s);
+ return 0;
+}
+
+struct qa_packet *
+parse_packet(xmlXPathContextPtr ctxt)
+{
+ struct qa_packet *qp = NULL;
+ char *s;
+
+ qp = malloc(sizeof *qp);
+ assert(qp);
+ memset(qp, 0, sizeof *qp);
+
+ s = get_xpath_string(ctxt, "@type");
+ qp->qp_type = parse_packet_type(s);
+ free(s);
+ s = get_xpath_string(ctxt, "@seq");
+ qp->qp_seq = atoi(s);
+ free(s);
+
+ if (qa_pi[qp->qp_type].pi_parse)
+ qa_pi[qp->qp_type].pi_parse(ctxt, qp);
+ else {
+ printf("Packet type %s not implemented yet\n", s);
+ free(qp);
+ qp = NULL;
+ }
+ return qp;
+}
+
+struct qa_packet *
+parse_packets(char *buf, int n)
+{
+ xmlDocPtr doc;
+ xmlXPathContextPtr context;
+ xmlXPathObjectPtr obj = NULL;
+ struct qa_packet *qp = NULL;
+
+ doc = xmlParseMemory(buf, n);
+
+ /* If we can't parse the packet, we don't care about it */
+ if (doc == NULL) return NULL;
+
+ context = xmlXPathNewContext(doc);
+ obj = xmlXPathEvalExpression((xmlChar *)"//packet", context);
+ if (obj == NULL) {
+ printf("didn't find anything\n");
+ goto free_context;
+ }
+ if (obj->type != XPATH_NODESET || obj->nodesetval->nodeNr == 0) {
+ printf("didn't find anything\n");
+ goto free_obj;
+ }
+ if (obj->nodesetval->nodeNr > 1) {
+ printf("One packet at a time please!\n");
+ }
+ context->node = xmlXPathNodeSetItem(obj->nodesetval, 0);
+ qp = parse_packet(context);
+
+free_obj:
+ xmlXPathFreeObject(obj);
+free_context:
+ xmlXPathFreeContext(context);
+ xmlFreeDoc(doc);
+ return qp;
+}
+
+
+static char *
+get_xpath_string(xmlXPathContextPtr ctxt, const char *xpath_query)
+{
+ xmlXPathObjectPtr obj = NULL;
+ char querystr[512];
+ char *ret;
+
+ snprintf(querystr, 512, "string(%s)", xpath_query);
+ obj = xmlXPathEvalExpression((xmlChar *)querystr, ctxt);
+
+ if (obj == NULL) return NULL;
+ if (obj->type != XPATH_STRING) {
+ xmlXPathFreeObject(obj);
+ return NULL;
+ }
+
+ ret = strdup((char *)obj->stringval);
+ xmlXPathFreeObject(obj);
+ return ret;
+}
+
+/*
+ * Packet serialization functions
+ *
+ * for converting structs to XML
+ */
+xmlNodePtr
+make_param(char *name, char *value)
+{
+ xmlNodePtr param = xmlNewNode(NULL, QP_PARAM_XML);
+ xmlNewProp(param, QP_NAME_XML, (xmlChar *)name);
+ xmlNodeSetContent(param, (xmlChar *)value);
+ return param;
+}
+
+void string_qp_hello(xmlNodePtr node, struct qa_packet *qp)
+{
+ xmlAddChild(node, make_param("greeting", qp->qp_hello.qp_greeting));
+}
+void string_qp_returncode(xmlNodePtr node, struct qa_packet *qp)
+{
+ char tmpstr[32];
+
+ snprintf(tmpstr, 32, "%d", qp->qp_returncode.qp_rc);
+ xmlAddChild(node, make_param("rc", tmpstr));
+ snprintf(tmpstr, 32, "%d", qp->qp_returncode.qp_errno);
+ xmlAddChild(node, make_param("errno", tmpstr));
+ xmlAddChild(node, make_param("strerror", qp->qp_returncode.qp_strerror));
+}
+void string_qp_runcmd(xmlNodePtr node, struct qa_packet *qp)
+{
+ char tmpstr[32];
+ xmlAddChild(node, make_param("cmdline", qp->qp_runcmd.qp_cmdline));
+ snprintf(tmpstr, 32, "%d", qp->qp_runcmd.qp_stdin_port);
+ xmlAddChild(node, make_param("stdin", tmpstr));
+ snprintf(tmpstr, 32, "%d", qp->qp_runcmd.qp_stdout_port);
+ xmlAddChild(node, make_param("stdout", tmpstr));
+ snprintf(tmpstr, 32, "%d", qp->qp_runcmd.qp_stderr_port);
+ xmlAddChild(node, make_param("stderr", tmpstr));
+}
+void string_qp_ack(xmlNodePtr node, struct qa_packet *qp)
+{
+ char tmpstr[32];
+
+ xmlAddChild(node, make_param("type",
+ QP_NAME(qp->qp_ack.qp_ack_type)));
+ snprintf(tmpstr, 32, "%d", qp->qp_ack.qp_ack_seq);
+ xmlAddChild(node, make_param("seq", tmpstr));
+}
+
+void string_qp_cmdexit(xmlNodePtr node, struct qa_packet *qp)
+{
+ char tmpstr[32];
+
+ snprintf(tmpstr, 32, "%d", qp->qp_cmdexit.qp_pid);
+ xmlAddChild(node, make_param("pid", tmpstr));
+ snprintf(tmpstr, 32, "%d", qp->qp_cmdexit.qp_status);
+ xmlAddChild(node, make_param("status", tmpstr));
+}
+
+/* Must pass in a pointer, but not a malloc'ed pointer */
+char *
+qptostr(struct qa_packet *qp, char **qpstr, int *qpsize)
+{
+ xmlDocPtr doc;
+ xmlNodePtr node;
+ char tmpstr[32];
+
+ if (qp->qp_type == QP_INVALID) return NULL;
+
+ doc = xmlNewDoc((xmlChar *)XML_DEFAULT_VERSION);
+
+ /* Add <qarsh> */
+ node = xmlNewDocNode(doc, NULL, QP_QARSH_XML, NULL);
+ xmlDocSetRootElement(doc, node);
+
+ /* Add <packet type="foo"> */
+ node = xmlNewChild(node, NULL, QP_PACKET_XML, NULL);
+ xmlNewProp(node, QP_TYPE_XML, (xmlChar *)QP_NAME(qp->qp_type));
+ snprintf(tmpstr, 32, "%d", qp->qp_seq);
+ xmlNewProp(node, QP_SEQ_XML, (xmlChar *)tmpstr);
+
+ if (qa_pi[qp->qp_type].pi_string) {
+ qa_pi[qp->qp_type].pi_string(node, qp);
+ }
+ xmlDocDumpMemory(doc, (xmlChar **)qpstr, qpsize);
+ xmlFreeDoc(doc);
+ return *qpstr;
+}
+
+/*
+ * Packet construction functions
+ */
+struct qa_packet *
+make_qp_hello(char *greeting)
+{
+ struct qa_packet *qp;
+
+ qp = malloc(sizeof *qp);
+ assert(qp);
+ memset(qp, 0, sizeof *qp);
+
+ qp->qp_type = QP_HELLO;
+ qp->qp_hello.qp_greeting = strdup(greeting);
+
+ return qp;
+}
+
+struct qa_packet *
+make_qp_ack(enum qa_packet_type t, int i)
+{
+ struct qa_packet *qp;
+ qp = malloc(sizeof *qp);
+ assert(qp);
+ memset(qp, 0, sizeof *qp);
+
+ qp->qp_type = QP_ACK;
+ qp->qp_ack.qp_ack_type = t;
+ qp->qp_ack.qp_ack_seq = i;
+
+ return qp;
+}
+
+struct qa_packet *
+make_qp_runcmd(char *cmdline, int p_in, int p_out, int p_err)
+{
+ struct qa_packet *qp;
+ qp = malloc(sizeof *qp);
+ assert(qp);
+ memset(qp, 0, sizeof *qp);
+
+ qp->qp_type = QP_RUNCMD;
+ qp->qp_runcmd.qp_cmdline = strdup(cmdline);
+ qp->qp_runcmd.qp_stdin_port = p_in;
+ qp->qp_runcmd.qp_stdout_port = p_out;
+ qp->qp_runcmd.qp_stderr_port = p_err;
+
+ return qp;
+}
+
+struct qa_packet *
+make_qp_cmdexit(pid_t pid, int status)
+{
+ struct qa_packet *qp;
+ qp = malloc(sizeof *qp);
+ assert(qp);
+ memset(qp, 0, sizeof *qp);
+
+ qp->qp_type = QP_CMDEXIT;
+ qp->qp_cmdexit.qp_pid = pid;
+ qp->qp_cmdexit.qp_status = status;
+
+ return qp;
+}
+
+/*
+ * Packet deallocation functions
+ */
+#define condfree(x) if (x) { free(x); }
+void
+free_qp_hello(struct qa_packet *qp)
+{
+ condfree(qp->qp_hello.qp_greeting);
+}
+
+void
+free_qp_returncode(struct qa_packet *qp)
+{
+ condfree(qp->qp_returncode.qp_strerror);
+}
+void
+free_qp_runcmd(struct qa_packet *qp)
+{
+ condfree(qp->qp_runcmd.qp_cmdline);
+}
+
+void
+qpfree(struct qa_packet *qp)
+{
+ if (qp) {
+ if (qa_pi[qp->qp_type].pi_free) {
+ qa_pi[qp->qp_type].pi_free(qp);
+ }
+ free(qp);
+ }
+}
+#undef condfree
+
+/*
+ * Packet printing functions
+ *
+ * For printing out packets for debugging purposes
+ */
+void
+dump_qp_ack(struct qa_packet *qp)
+{
+ printf("\t%s #%d\n", QP_NAME(qp->qp_ack.qp_ack_type),
+ qp->qp_ack.qp_ack_seq);
+}
+
+void
+dump_qp_runcmd(struct qa_packet *qp)
+{
+ printf("\tcmdline: %s\n", qp->qp_runcmd.qp_cmdline);
+}
+
+void
+dump_qp_returncode(struct qa_packet *qp)
+{
+ printf("\trc: %d\n", qp->qp_returncode.qp_rc);
+}
+
+void
+dump_qp_cmdexit(struct qa_packet *qp)
+{
+ if (WIFEXITED(qp->qp_cmdexit.qp_status)) {
+ printf("\texited: %d\n", WEXITSTATUS(qp->qp_cmdexit.qp_status));
+ } else if (WIFSIGNALED(qp->qp_cmdexit.qp_status)) {
+ printf("\tsignaled: %d\n", WTERMSIG(qp->qp_cmdexit.qp_status));
+ } else {
+ printf("\tstatus: %d\n", qp->qp_cmdexit.qp_status);
+ }
+}
+
+void
+dump_qp(struct qa_packet *qp)
+{
+ printf("%s #%d\n", QP_NAME(qp->qp_type), qp->qp_seq);
+ if (qa_pi[qp->qp_type].pi_dump) {
+ qa_pi[qp->qp_type].pi_dump(qp);
+ }
+}