blob: e9895fb3585e9f0fb7f2a6f96da980258d3b9507 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 Wind River Systems, Inc. and others.
* All rights reserved. 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
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* TCF communication protocol.
* This module handles registration of command and event handlers.
* It is called when new messages are received and will dispatch
* messages to the appropriate handler. It has no knowledge of what transport
* protocol is used and what services do.
*/
#include <stdlib.h>
#include <string.h>
#include "protocol.h"
#include "trace.h"
#include "exceptions.h"
#include "json.h"
#include "myalloc.h"
static const char * LOCATOR = "Locator";
struct ServiceInfo {
const char * name;
struct ServiceInfo * next;
};
typedef struct ServiceInfo ServiceInfo;
struct MessageHandlerInfo {
char type;
ServiceInfo * service;
const char * name;
MessageHandler handler;
struct MessageHandlerInfo * next;
};
typedef struct MessageHandlerInfo MessageHandlerInfo;
#define MESSAGE_HASH_SIZE 127
static MessageHandlerInfo * message_handlers[MESSAGE_HASH_SIZE];
static ServiceInfo * services = NULL;
int congestion_level = 0;
static void read_stringz(InputStream * inp, char * str, size_t size) {
unsigned len = 0;
for (;;) {
int ch = inp->read(inp);
if (ch <= 0) break;
if (len < size - 1) str[len] = ch;
len++;
}
str[len] = 0;
}
static int message_hash(const char * service, const char * name) {
int i;
int h = 0;
for (i = 0; service[i]; i++) h += service[i];
for (i = 0; name[i]; i++) h += name[i];
h = h + h / MESSAGE_HASH_SIZE;
return h % MESSAGE_HASH_SIZE;
}
static MessageHandlerInfo * find_message_handler(char type, char * service, char * name) {
MessageHandlerInfo * c = message_handlers[message_hash(service, name)];
while (c != NULL) {
if (c->type == type && !strcmp(c->service->name, service) && !strcmp(c->name, name)) return c;
c = c->next;
}
trace(LOG_ALWAYS, "Unsupported TCF message: %c %s %s ...", type, service, name);
exception(ERR_PROTOCOL);
return NULL;
}
void handle_protocol_message(InputStream * inp, OutputStream * out) {
char type[8];
char token[256];
char service[256];
char name[256];
read_stringz(inp, type, sizeof(type));
if (strlen(type) != 1) {
trace(LOG_ALWAYS, "Invalid TCF message: %s ...", type);
exception(ERR_PROTOCOL);
}
else if (type[0] == 'C') {
Trap trap;
MessageHandlerInfo * c;
read_stringz(inp, token, sizeof(token));
read_stringz(inp, service, sizeof(service));
read_stringz(inp, name, sizeof(name));
trace(LOG_PROTOCOL, "Command: C %s %s %s ...", token, service, name);
c = find_message_handler('C', service, name);
if (set_trap(&trap)) {
c->handler(token, inp, out);
clear_trap(&trap);
}
else {
trace(LOG_ALWAYS, "Exception handling command %s.%s: %d %s",
service, name, trap.error, errno_to_str(trap.error));
exception(trap.error);
}
}
else if (type[0] == 'E') {
Trap trap;
MessageHandlerInfo * c;
read_stringz(inp, service, sizeof(service));
read_stringz(inp, name, sizeof(name));
trace(LOG_PROTOCOL, "Event: E %s %s ...", service, name);
c = find_message_handler('E', service, name);
if (set_trap(&trap)) {
c->handler(NULL, inp, out);
clear_trap(&trap);
}
else {
trace(LOG_ALWAYS, "Exception handling event %s.%s: %d %s",
service, name, trap.error, errno_to_str(trap.error));
exception(trap.error);
}
}
else if (type[0] == 'F') {
int n = 0;
int s = 0;
int ch = inp->read(inp);
if (ch == '-') {
s = 1;
ch = inp->read(inp);
}
while (ch >= '0' && ch <= '9') {
n = n * 10 + (ch - '0');
ch = inp->read(inp);
}
if (ch != MARKER_EOM) exception(ERR_PROTOCOL);
congestion_level = n;
}
else {
trace(LOG_ALWAYS, "Invalid TCF message: %s ...", type);
exception(ERR_PROTOCOL);
}
}
static void add_message_handler(const char type, const char * service, const char * name, MessageHandler handler) {
int h = message_hash(service, name);
ServiceInfo * s = services;
MessageHandlerInfo * c = (MessageHandlerInfo *)loc_alloc(sizeof(MessageHandlerInfo));
while (s != NULL && strcmp(s->name, service) != 0) s = s->next;
if (s == NULL) {
s = (ServiceInfo *)loc_alloc(sizeof(ServiceInfo));
s->name = service;
s->next = services;
services = s;
}
c->type = type;
c->service = s;
c->name = name;
c->handler = handler;
c->next = message_handlers[h];
message_handlers[h] = c;
}
void add_command_handler(const char * service, const char * name, MessageHandler handler) {
add_message_handler('C', service, name, handler);
}
void add_event_handler(const char * service, const char * name, MessageHandler handler) {
add_message_handler('E', service, name, handler);
}
void send_hello_message(OutputStream * out) {
ServiceInfo * s = services;
write_stringz(out, "E");
write_stringz(out, LOCATOR);
write_stringz(out, "Hello");
out->write(out, '[');
while (s) {
if (s != services) out->write(out, ',');
json_write_string(out, s->name);
s = s->next;
}
out->write(out, ']');
out->write(out, 0);
out->write(out, MARKER_EOM);
}
static void command_sync(char * token, InputStream * inp, OutputStream * out) {
if (inp->read(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, 0);
out->write(out, MARKER_EOM);
}
void ini_protocol(void) {
add_command_handler(LOCATOR, "sync", command_sync);
}