blob: de4e5b45f316555d6fa1abebf8179ca6e3811939 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2005 The Regents of the University of California.
* This material was produced under U.S. Government contract W-7405-ENG-36
* for Los Alamos National Laboratory, which is operated by the University
* of California for the U.S. Department of Energy. The U.S. Government has
* rights to use, reproduce, and distribute this software. NEITHER THE
* GOVERNMENT NOR THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR
* ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified
* to produce derivative works, such modified software should be clearly
* marked, so as not to confuse it with the version available from LANL.
*
* Additionally, this program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* LA-CC 04-115
******************************************************************************/
#ifdef __gnu_linux__
#define _GNU_SOURCE
#endif /* __gnu_linux__ */
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "proxy.h"
#include "proxy_msg.h"
#include "args.h"
#include "list.h"
#include "compat.h"
#ifdef __linux__
extern int digittoint(int c);
#endif /* __linux__ */
#define ARG_SIZE 100
static char tohex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/*
* Convert a message to a packet ready to send over the wire.
*
* Note: packet length is added when the packet is transmitted.
*/
int
proxy_serialize_msg(proxy_msg *m, char **result)
{
int i;
int hdr_len;
int arg_len;
int len = 0;
char * packet;
if (m == NULL)
return -1;
/*
* Compute packet length.
*
* Header length includes leading space and separators.
* Body length include encoded strings and separators.
*/
hdr_len = MSG_ID_SIZE + MSG_TRANS_ID_SIZE + MSG_NARGS_SIZE + 3;
for (i = 0; i < m->num_args; i++) {
len += strlen(m->args[i]) + MSG_ARG_LEN_SIZE + 2;
}
/*
* Allocate packet
*/
packet = (char *)malloc(hdr_len + len + 1);
sprintf(packet, " %0*x:%0*x:%0*x",
MSG_ID_SIZE, m->msg_id & MSG_ID_MASK,
MSG_TRANS_ID_SIZE, m->trans_id & MSG_TRANS_ID_MASK,
MSG_NARGS_SIZE, m->num_args & MSG_NARGS_MASK);
len = hdr_len;
for (i = 0; i < m->num_args; i++) {
arg_len = strlen(m->args[i]);
sprintf(packet + len, " %0*x:%s",
MSG_ARG_LEN_SIZE, arg_len & MSG_ARG_LEN_MASK, m->args[i]);
len += arg_len + MSG_ARG_LEN_SIZE + 2;
}
*result = packet;
return 0;
}
void
proxy_get_data(char *str, char **data, int *len)
{
int data_len;
char ch;
char * p;
data_len = strlen(str) / 2;
*len = data_len;
*data = p = (char *)malloc(sizeof(char) * data_len);
for (; data_len > 0; data_len--) {
ch = digittoint(*str++);
ch <<= 4;
ch |= digittoint(*str++);
*p++ = ch;
}
}
void
proxy_get_int(char *str, int *val)
{
*val = (int)strtol(str, NULL, 10);
}
void
proxy_get_bitset(char *str, bitset **b)
{
*b = str_to_bitset(str);
}
/*
* Convert wire protocol to message.
*
* Packet length has already been removed.
*/
int
proxy_deserialize_msg(char *packet, int packet_len, proxy_msg **msg)
{
int i;
int msg_id;
int trans_id;
int num_args;
proxy_msg * m = NULL;
char * arg;
char * end;
if (packet == NULL || *packet != ' ' || packet_len < MSG_ID_SIZE + MSG_TRANS_ID_SIZE + MSG_NARGS_SIZE + 3) {
return -1;
}
/*
* message ID
*/
packet++; /* Skip space */
end = packet + MSG_ID_SIZE;
*end++ = '\0';
msg_id = strtol(packet, NULL, 16);
/*
* transaction ID
*/
packet = end;
end = packet + MSG_TRANS_ID_SIZE;
*end++ = '\0';
trans_id = strtol(packet, NULL, 16);
/*
* number of args
*/
packet = end;
end = packet + MSG_NARGS_SIZE;
*end++ = '\0';
num_args = strtol(packet, NULL, 16);
m = new_proxy_msg(msg_id, trans_id);
packet = end;
for (i = 0; i < num_args; i++) {
if (proxy_msg_decode_string(packet, &arg, &packet) < 0) {
free_proxy_msg(m);
return -1;
}
proxy_msg_add_string_nocopy(m, arg);
packet++; /* skip space */
}
*msg = m;
return 0;
}
/*
* Decode string argument. Returns pointer to the character after
* the end of the string in 'end'
*/
int
proxy_msg_decode_string(char *str, char **arg, char **end)
{
int arg_len;
int str_len = strlen(str);
char * ep;
char * p;
if (str_len < MSG_ARG_LEN_SIZE + 1) {
return -1;
}
ep = str + MSG_ARG_LEN_SIZE;
*ep++ = '\0';
arg_len = strtol(str, NULL, 16);
if (str_len < MSG_ARG_LEN_SIZE + arg_len + 1) {
return -1;
}
p = (char *)malloc(arg_len + 1);
memcpy(p, ep, arg_len);
p[arg_len] = '\0';
*arg = p;
*end = ep + arg_len;
return 0;
}
static void
add_arg(proxy_msg *m, char *arg, int free_arg)
{
int size = m->arg_size;
if (m->arg_size < m->num_args + 2) {
m->arg_size += ARG_SIZE;
}
if (size == 0) {
m->arg_size++; // extra space to null terminate arguments
m->args = (char **)malloc(sizeof(char *) * m->arg_size);
m->free_args = (int *)malloc(sizeof(int *) * m->arg_size);
} else if (size < m->arg_size) {
m->args = (char **)realloc(m->args, sizeof(char *) * m->arg_size);
m->free_args = (int *)realloc(m->free_args, sizeof(int *) * m->arg_size);
}
m->args[m->num_args] = arg;
m->free_args[m->num_args] = free_arg;
m->num_args++;
/*
* Make sure that args are always null terminated
*/
m->args[m->num_args] = NULL;
}
void
proxy_msg_add_data(proxy_msg *m, char *data, int len)
{
int i;
char ch;
char * arg;
char * p;
if (data == NULL) {
proxy_msg_add_string(m, "00");
return;
}
p = arg = (char *)malloc((len * 2) + 8 + 2);
/*
* Encode data
*/
for (i = 0; i < len; i++) {
ch = *data++;
*p++ = tohex[(ch >> 4) & 0xf];
*p++ = tohex[ch & 0xf];
}
*p = '\0';
proxy_msg_add_string(m, arg);
}
void
proxy_msg_add_int(proxy_msg *m, int val)
{
char * str_val;
asprintf(&str_val, "%d", val);
add_arg(m, str_val, 1);
}
void
proxy_msg_add_string(proxy_msg *m, char *val)
{
if (val == NULL) {
val = "";
}
add_arg(m, strdup(val), 1);
}
void
proxy_msg_add_string_nocopy(proxy_msg *m, char *val)
{
if (val == NULL) {
val = "";
}
add_arg(m, val, 0);
}
void
proxy_msg_add_args(proxy_msg *m, int nargs, char **args)
{
int i;
if (nargs == 0)
return;
for (i = 0; i < nargs; i++) {
add_arg(m, strdup(args[i]), 1);
}
}
void
proxy_msg_add_args_nocopy(proxy_msg *m, int nargs, char **args)
{
int i;
if (nargs == 0)
return;
for (i = 0; i < nargs; i++) {
add_arg(m, args[i], 0);
}
}
void
proxy_msg_add_keyval_int(proxy_msg *m, char *key, int val)
{
char * kv;
asprintf(&kv, "%s=%d", key, val);
add_arg(m, kv, 1);
}
void
proxy_msg_add_keyval_string(proxy_msg *m, char *key, char *val)
{
char * kv;
asprintf(&kv, "%s=%s", key, val);
add_arg(m, kv, 1);
}
void
proxy_msg_add_bitset(proxy_msg *m, bitset *b)
{
add_arg(m, bitset_to_str(b), 1);
}
void
proxy_msg_insert_bitset(proxy_msg *m, bitset *b, int idx)
{
int i;
int tmp_free;
char * tmp_arg;
if (idx < 0)
idx = 0;
/*
* First add bitset to end
*/
proxy_msg_add_bitset(m, b);
/*
* Just return if the insert location is at or past end
*/
if (idx >= m->num_args) {
return;
}
/*
* Otherwise rotate last argument into required position
*/
tmp_arg = m->args[m->num_args-1];
tmp_free = m->free_args[m->num_args-1];
for (i = m->num_args-1; i > idx; i--) {
m->args[i] = m->args[i-1];
m->free_args[i] = m->free_args[i-1];
}
m->args[idx] = tmp_arg;
m->free_args[idx] = tmp_free;
}
proxy_msg *
new_proxy_msg(int msg_id, int trans_id)
{
proxy_msg * m = (proxy_msg *)malloc(sizeof(proxy_msg));
m->msg_id = msg_id;
m->trans_id = trans_id;
m->arg_size = 0;
m->num_args = 0;
m->args = NULL;
m->free_args = NULL;
return m;
}
void
free_proxy_msg(proxy_msg *m)
{
int i;
for (i = 0; i < m->num_args; i++) {
if (m->free_args[i]) {
free(m->args[i]);
}
}
free(m->args);
free(m->free_args);
free(m);
}
/*
* Add message to list of messages waiting to be sent.
*/
void
proxy_queue_msg(List *ev_list, proxy_msg *m)
{
AddToList(ev_list, (void *)m);
}
/*
* Process any queued messages.
*/
void
proxy_process_msgs(List *msg_list, void (*callback)(proxy_msg *, void *), void *data)
{
proxy_msg * m;
if (msg_list == NULL)
return;
while ((m = (proxy_msg *)RemoveFirst(msg_list)) != NULL) {
callback(m, data);
free_proxy_msg(m);
}
}