#include #include #include #include #include #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); int parse_qp_setuser(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_kill(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_recvfile(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_sendfile(xmlXPathContextPtr ctxt, struct qa_packet *qp); int parse_qp_rstat(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 string_qp_setuser(xmlNodePtr node, struct qa_packet *qp); void string_qp_kill(xmlNodePtr node, struct qa_packet *qp); void string_qp_recvfile(xmlNodePtr node, struct qa_packet *qp); void string_qp_sendfile(xmlNodePtr node, struct qa_packet *qp); void string_qp_rstat(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 free_qp_setuser(struct qa_packet *qp); void free_qp_recvfile(struct qa_packet *qp); void free_qp_sendfile(struct qa_packet *qp); void free_qp_rstat(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); void dump_qp_setuser(struct qa_packet *qp); void dump_qp_kill(struct qa_packet *qp); void dump_qp_recvfile(struct qa_packet *qp); void dump_qp_sendfile(struct qa_packet *qp); void dump_qp_rstat(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 }, { .pi_name = "setuser", .pi_parse = parse_qp_setuser, .pi_string = string_qp_setuser, .pi_free = free_qp_setuser, .pi_dump = dump_qp_setuser }, { .pi_name = "kill", .pi_parse = parse_qp_kill, .pi_string = string_qp_kill, .pi_free = NULL, .pi_dump = dump_qp_kill }, { .pi_name = "recvfile", .pi_parse = parse_qp_recvfile, .pi_string = string_qp_recvfile, .pi_free = free_qp_recvfile, .pi_dump = dump_qp_recvfile }, { .pi_name = "sendfile", .pi_parse = parse_qp_sendfile, .pi_string = string_qp_sendfile, .pi_free = free_qp_sendfile, .pi_dump = dump_qp_sendfile }, { .pi_name = "rstat", .pi_parse = parse_qp_rstat, .pi_string = string_qp_rstat, .pi_free = free_qp_rstat, .pi_dump = dump_qp_rstat } }; #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; } int parse_qp_setuser(xmlXPathContextPtr ctxt, struct qa_packet *qp) { qp->qp_setuser.qp_user = get_xpath_string(ctxt, "param[@name='user']"); qp->qp_setuser.qp_group = get_xpath_string(ctxt, "param[@name='group']"); return 0; } int parse_qp_kill(xmlXPathContextPtr ctxt, struct qa_packet *qp) { char *s; s = get_xpath_string(ctxt, "param[@name='signal']"); qp->qp_kill.qp_sig = atoi(s); free(s); return 0; } int parse_qp_recvfile(xmlXPathContextPtr ctxt, struct qa_packet *qp) { char *s; qp->qp_recvfile.qp_path = get_xpath_string(ctxt, "param[@name='path']"); s = get_xpath_string(ctxt, "param[@name='if_port']"); qp->qp_recvfile.qp_if_port = atoi(s); free(s); s = get_xpath_string(ctxt, "param[@name='count']"); qp->qp_recvfile.qp_count = atoi(s); free(s); s = get_xpath_string(ctxt, "param[@name='mode']"); qp->qp_recvfile.qp_mode = atoi(s); free(s); return 0; } int parse_qp_sendfile(xmlXPathContextPtr ctxt, struct qa_packet *qp) { char *s; qp->qp_sendfile.qp_path = get_xpath_string(ctxt, "param[@name='path']"); s = get_xpath_string(ctxt, "param[@name='of_port']"); qp->qp_sendfile.qp_of_port = atoi(s); free(s); return 0; } int parse_qp_rstat(xmlXPathContextPtr ctxt, struct qa_packet *qp) { char *s; qp->qp_rstat.qp_path = get_xpath_string(ctxt, "param[@name='path']"); s = get_xpath_string(ctxt, "param[@name='st_mode']"); qp->qp_rstat.qp_st_mode = atoi(s); free(s); s = get_xpath_string(ctxt, "param[@name='st_uid']"); qp->qp_rstat.qp_st_uid = atoi(s); free(s); s = get_xpath_string(ctxt, "param[@name='st_gid']"); qp->qp_rstat.qp_st_gid = atoi(s); free(s); s = get_xpath_string(ctxt, "param[@name='st_size']"); qp->qp_rstat.qp_st_size = atol(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, xmlEncodeSpecialChars(NULL, (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)); } void string_qp_setuser(xmlNodePtr node, struct qa_packet *qp) { xmlAddChild(node, make_param("user", qp->qp_setuser.qp_user)); if (qp->qp_setuser.qp_group) { xmlAddChild(node, make_param("group", qp->qp_setuser.qp_group)); } } void string_qp_kill(xmlNodePtr node, struct qa_packet *qp) { char tmpstr[32]; snprintf(tmpstr, 32, "%d", qp->qp_kill.qp_sig); xmlAddChild(node, make_param("signal", tmpstr)); } void string_qp_recvfile(xmlNodePtr node, struct qa_packet *qp) { char tmpstr[32]; xmlAddChild(node, make_param("path", qp->qp_recvfile.qp_path)); snprintf(tmpstr, 32, "%d", qp->qp_recvfile.qp_if_port); xmlAddChild(node, make_param("if_port", tmpstr)); snprintf(tmpstr, 32, "%zd", qp->qp_recvfile.qp_count); xmlAddChild(node, make_param("count", tmpstr)); snprintf(tmpstr, 32, "%d", qp->qp_recvfile.qp_mode); xmlAddChild(node, make_param("mode", tmpstr)); } void string_qp_sendfile(xmlNodePtr node, struct qa_packet *qp) { char tmpstr[32]; xmlAddChild(node, make_param("path", qp->qp_sendfile.qp_path)); snprintf(tmpstr, 32, "%d", qp->qp_sendfile.qp_of_port); xmlAddChild(node, make_param("of_port", tmpstr)); } void string_qp_rstat(xmlNodePtr node, struct qa_packet *qp) { char tmpstr[32]; xmlAddChild(node, make_param("path", qp->qp_rstat.qp_path)); snprintf(tmpstr, 32, "%d", qp->qp_rstat.qp_st_mode); xmlAddChild(node, make_param("st_mode", tmpstr)); snprintf(tmpstr, 32, "%d", qp->qp_rstat.qp_st_uid); xmlAddChild(node, make_param("st_uid", tmpstr)); snprintf(tmpstr, 32, "%d", qp->qp_rstat.qp_st_gid); xmlAddChild(node, make_param("st_gid", tmpstr)); snprintf(tmpstr, 32, "%ld", qp->qp_rstat.qp_st_size); xmlAddChild(node, make_param("st_size", 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 */ node = xmlNewDocNode(doc, NULL, QP_QARSH_XML, NULL); xmlDocSetRootElement(doc, node); /* Add */ 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_returncode(int rc, int eno, char *strerr) { struct qa_packet *qp; qp = malloc(sizeof *qp); assert(qp); memset(qp, 0, sizeof *qp); qp->qp_type = QP_RETURNCODE; qp->qp_returncode.qp_rc = rc; qp->qp_returncode.qp_errno = eno; qp->qp_returncode.qp_strerror = strdup(strerr); 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; } struct qa_packet * make_qp_setuser(char *user, char *group) { struct qa_packet *qp; qp = malloc(sizeof *qp); assert(qp); memset(qp, 0, sizeof *qp); qp->qp_type = QP_SETUSER; qp->qp_setuser.qp_user = strdup(user); if (group) qp->qp_setuser.qp_group = strdup(group); return qp; } struct qa_packet * make_qp_kill(int sig) { struct qa_packet *qp; qp = malloc(sizeof *qp); assert(qp); memset(qp, 0, sizeof *qp); qp->qp_type = QP_KILL; qp->qp_kill.qp_sig = sig; return qp; } struct qa_packet * make_qp_recvfile(const char *path, int if_port, size_t count, mode_t mode) { struct qa_packet *qp; qp = malloc(sizeof *qp); assert(qp); memset(qp, 0, sizeof *qp); qp->qp_type = QP_RECVFILE; qp->qp_recvfile.qp_path = strdup(path); qp->qp_recvfile.qp_if_port = if_port; qp->qp_recvfile.qp_count = count; qp->qp_recvfile.qp_mode = mode; return qp; } struct qa_packet * make_qp_sendfile(const char *path, int of_port) { struct qa_packet *qp; qp = malloc(sizeof *qp); assert(qp); memset(qp, 0, sizeof *qp); qp->qp_type = QP_SENDFILE; qp->qp_sendfile.qp_path = strdup(path); qp->qp_sendfile.qp_of_port = of_port; return qp; } struct qa_packet * make_qp_rstat(const char *path, const struct stat *sb) { struct qa_packet *qp; qp = malloc(sizeof *qp); assert(qp); memset(qp, 0, sizeof *qp); qp->qp_type = QP_RSTAT; qp->qp_rstat.qp_path = strdup(path); if (sb) { qp->qp_rstat.qp_st_mode = sb->st_mode; qp->qp_rstat.qp_st_uid = sb->st_uid; qp->qp_rstat.qp_st_gid = sb->st_gid; qp->qp_rstat.qp_st_size = sb->st_size; } 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 free_qp_setuser(struct qa_packet *qp) { condfree(qp->qp_setuser.qp_user); condfree(qp->qp_setuser.qp_group); } void free_qp_recvfile(struct qa_packet *qp) { condfree(qp->qp_recvfile.qp_path); } void free_qp_sendfile(struct qa_packet *qp) { condfree(qp->qp_sendfile.qp_path); } void free_qp_rstat(struct qa_packet *qp) { condfree(qp->qp_rstat.qp_path); } 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_setuser(struct qa_packet *qp) { printf("\tuser: %s\n", qp->qp_setuser.qp_user); printf("\tgroup: %s\n", qp->qp_setuser.qp_group); } void dump_qp_kill(struct qa_packet *qp) { printf("\tsig: %d\n", qp->qp_kill.qp_sig); } void dump_qp_recvfile(struct qa_packet *qp) { printf("\tpath: %s\n", qp->qp_recvfile.qp_path); printf("\tif_port: %d\n", qp->qp_recvfile.qp_if_port); printf("\tcount: %zd\n", qp->qp_recvfile.qp_count); printf("\tmode: %o\n", qp->qp_recvfile.qp_mode); } void dump_qp_sendfile(struct qa_packet *qp) { printf("\tpath: %s\n", qp->qp_sendfile.qp_path); printf("\tof_port: %d\n", qp->qp_sendfile.qp_of_port); } void dump_qp_rstat(struct qa_packet *qp) { printf("\tpath: %s\n", qp->qp_rstat.qp_path); printf("\tst_mode: %o\n", qp->qp_rstat.qp_st_mode); printf("\tst_uid: %d\n", qp->qp_rstat.qp_st_uid); printf("\tst_gid: %d\n", qp->qp_rstat.qp_st_gid); printf("\tst_size: %ld\n", qp->qp_rstat.qp_st_size); } 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); } }