libApp/src/appinterface.c

473 lines
11 KiB
C

/* $Id$ */
/* Copyright (c) 2011-2014 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS System libApp */
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* TODO:
* - implement what's missing */
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <System.h>
#include "App/appserver.h"
#include "marshall.h"
#include "appinterface.h"
#include "../config.h"
#ifndef PREFIX
# define PREFIX "/usr/local"
#endif
#ifndef SYSCONFDIR
# define SYSCONFDIR PREFIX "/etc"
#endif
/* AppInterface */
/* private */
/* types */
/* XXX get rid of this */
#define VT_LAST VT_STRING
#define VT_COUNT (VT_LAST + 1)
#define AICT_MASK 077
#ifdef DEBUG
static const String * AICTString[VT_COUNT] =
{
"void", "bool", "int8", "uint8", "int16", "uint16", "int32", "uint32",
"int64", "uint64", "String", "Buffer", "float", "double"
};
#endif
typedef enum _AppInterfaceCallDirection
{
AICD_IN = 0000,
AICD_IN_OUT = 0100,
AICD_OUT = 0200
} AppInterfaceCallDirection;
#define AICD_MASK 0700
typedef struct _AppInterfaceCallArg
{
VariableType type;
AppInterfaceCallDirection direction;
size_t size;
} AppInterfaceCallArg;
typedef struct _AppInterfaceCall
{
char * name;
AppInterfaceCallArg type;
AppInterfaceCallArg * args;
size_t args_cnt;
void * (*func)(void *);
} AppInterfaceCall;
struct _AppInterface
{
AppTransportMode mode;
String * name;
Config * config;
AppInterfaceCall * calls;
size_t calls_cnt;
int error;
};
typedef struct _StringEnum
{
char const * string;
int value;
} StringEnum;
/* constants */
#define APPINTERFACE_CALL_PREFIX "call::"
#define APPINTERFACE_CALLBACK_PREFIX "callback::"
/* variables */
static const StringEnum _string_type[] =
{
{ "VOID", VT_NULL | AICD_IN },
{ "BOOL", VT_BOOL | AICD_IN },
{ "INT8", VT_INT8 | AICD_IN },
{ "UINT8", VT_UINT8 | AICD_IN },
{ "INT16", VT_INT16 | AICD_IN },
{ "UINT16", VT_UINT16 | AICD_IN },
{ "INT32", VT_INT32 | AICD_IN },
{ "UINT32", VT_UINT32 | AICD_IN },
{ "INT64", VT_INT64 | AICD_IN },
{ "UINT64", VT_UINT64 | AICD_IN },
{ "STRING", VT_STRING | AICD_IN },
{ "STRING_OUT", VT_STRING | AICD_OUT },
{ "BUFFER", VT_BUFFER | AICD_IN },
{ "BUFFER_OUT", VT_BUFFER | AICD_OUT },
{ "FLOAT", VT_FLOAT | AICD_IN },
{ "DOUBLE", VT_DOUBLE | AICD_IN },
{ NULL, 0 }
};
/* prototypes */
static int _string_enum(String const * string, StringEnum const * se);
/* accessors */
static AppInterfaceCall * _appinterface_get_call(AppInterface * appinterface,
char const * method);
/* functions */
/* string_enum */
/* FIXME move to string.c */
static int _string_enum(String const * string, StringEnum const * se)
{
size_t i;
if(string == NULL)
return -error_set_code(-EINVAL, "%s", strerror(EINVAL));
for(i = 0; se[i].string != NULL; i++)
if(string_compare(string, se[i].string) == 0)
return se[i].value;
return -error_set_code(1, "%s\"%s\"", "Unknown enumerated value for ",
string);
}
/* public */
/* functions */
/* appinterface_new */
AppInterface * _new_do(AppTransportMode mode, char const * app);
static int _new_foreach(char const * key, Hash * value,
AppInterface * appinterface);
static int _new_append(AppInterface * ai, VariableType type,
char const * function);
static int _new_append_arg(AppInterface * ai, char const * arg);
AppInterface * appinterface_new(AppTransportMode mode, char const * app)
{
AppInterface * ai;
Plugin * handle;
size_t i;
String * name;
if((handle = plugin_new_self()) == NULL)
return NULL;
if((ai = _new_do(mode, app)) == NULL)
return NULL;
for(i = 0; i < ai->calls_cnt; i++)
{
name = string_new_append(ai->name, "_", ai->calls[i].name,
NULL);
ai->calls[i].func = plugin_lookup(handle, name);
string_delete(name);
if(ai->calls[i].func == NULL)
{
appinterface_delete(ai);
plugin_delete(handle);
return NULL;
}
}
plugin_delete(handle);
return ai;
}
AppInterface * _new_do(AppTransportMode mode, char const * app)
{
AppInterface * appinterface;
String * pathname = NULL;
if(app == NULL)
return NULL; /* FIXME report error */
if((appinterface = object_new(sizeof(*appinterface))) == NULL)
return NULL;
appinterface->mode = mode;
appinterface->name = string_new(app);
appinterface->config = config_new();
appinterface->calls = NULL;
appinterface->calls_cnt = 0;
appinterface->error = 0;
if(appinterface->name == NULL
|| (pathname = string_new_append(
SYSCONFDIR "/AppInterface/", app,
".interface", NULL)) == NULL
|| appinterface->config == NULL
|| config_load(appinterface->config, pathname) != 0)
{
string_delete(pathname);
appinterface_delete(appinterface);
return NULL;
}
appinterface->error = 0;
hash_foreach(appinterface->config, (HashForeach)_new_foreach,
appinterface);
if(appinterface->error != 0)
{
appinterface_delete(appinterface);
return NULL;
}
return appinterface;
}
static int _new_foreach(char const * key, Hash * value,
AppInterface * appinterface)
{
String const * prefix = (appinterface->mode == ATM_SERVER)
? APPINTERFACE_CALL_PREFIX : APPINTERFACE_CALLBACK_PREFIX;
int i;
char buf[8];
int type = VT_NULL;
char const * p;
if(key == NULL || strncmp(prefix, key, string_length(prefix)) != 0)
return 0;
key += string_length(prefix);
if((p = hash_get(value, "ret")) != NULL
&& (type = _string_enum(p, _string_type)) < 0)
{
appinterface->error = error_set_code(1, "%s",
"Invalid return type");
return -appinterface->error;
}
if(_new_append(appinterface, type, key) != 0)
{
appinterface->error = 1;
return -appinterface->error;
}
for(i = 0; i < APPSERVER_MAX_ARGUMENTS; i++)
{
snprintf(buf, sizeof(buf), "arg%d", i + 1);
if((p = hash_get(value, buf)) == NULL)
break;
if(_new_append_arg(appinterface, p) != 0)
{
/* FIXME may crash here? */
appinterface->error = 1;
return -1;
}
}
return 0;
}
static int _new_append(AppInterface * ai, VariableType type,
char const * function)
{
AppInterfaceCall * p;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%d, \"%s\")\n", __func__, type, function);
#endif
if((p = realloc(ai->calls, sizeof(*p) * (ai->calls_cnt + 1))) == NULL)
return -1;
ai->calls = p;
p = &ai->calls[ai->calls_cnt];
if((p->name = string_new(function)) == NULL)
return -1;
p->type.type = type & AICT_MASK;
p->type.direction = type & AICD_MASK;
p->args = NULL;
p->args_cnt = 0;
ai->calls_cnt++;
return 0;
}
static int _new_append_arg(AppInterface * ai, char const * arg)
{
char buf[16];
char * p;
int type;
AppInterfaceCall * q;
AppInterfaceCallArg * r;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, arg);
#endif
snprintf(buf, sizeof(buf), "%s", arg);
if((p = strchr(buf, ',')) != NULL)
*p = '\0';
if((type = _string_enum(buf, _string_type)) < 0)
return -1;
q = &ai->calls[ai->calls_cnt - 1];
if((r = realloc(q->args, sizeof(*r) * (q->args_cnt + 1))) == NULL)
return error_set_code(-errno, "%s", strerror(errno));
q->args = r;
r = &q->args[q->args_cnt++];
r->type = type & AICT_MASK;
r->direction = type & AICD_MASK;
#ifdef DEBUG
fprintf(stderr, "DEBUG: type %s, direction: %d\n", AICTString[r->type],
r->direction);
#endif
return 0;
}
/* appinterface_delete */
void appinterface_delete(AppInterface * appinterface)
{
size_t i;
if(appinterface->config != NULL)
config_delete(appinterface->config);
for(i = 0; i < appinterface->calls_cnt; i++)
{
string_delete(appinterface->calls[i].name);
free(appinterface->calls[i].args);
}
free(appinterface->calls);
string_delete(appinterface->name);
object_delete(appinterface);
}
/* accessors */
/* appinterface_can_call */
static int _can_call_client(AppInterface * appinterface,
AppInterfaceCall * call, char const * name);
static int _can_call_server(AppInterface * appinterface,
AppInterfaceCall * call, char const * name);
int appinterface_can_call(AppInterface * appinterface, char const * name,
char const * method)
{
AppInterfaceCall * call;
if((call = _appinterface_get_call(appinterface, method)) == NULL)
return -1;
switch(appinterface->mode)
{
case ATM_CLIENT:
return _can_call_client(appinterface, call, name);
case ATM_SERVER:
return _can_call_server(appinterface, call, name);
}
return -error_set_code(1, "%s", "Unknown AppInterface mode");
}
static int _can_call_client(AppInterface * appinterface,
AppInterfaceCall * call, char const * name)
{
/* FIXME really implement */
return 1;
}
static int _can_call_server(AppInterface * appinterface,
AppInterfaceCall * call, char const * name)
{
int ret = 0;
String * section;
String const * allow;
String const * deny;
if((section = string_new_append(APPINTERFACE_CALL_PREFIX, call->name,
NULL)) == NULL)
return -1;
allow = config_get(appinterface->config, section, "allow");
deny = config_get(appinterface->config, section, "deny");
/* FIXME implement pattern matching */
if(allow == NULL && deny == NULL)
ret = 1;
else if(deny != NULL)
ret = (strcmp(deny, name) == 0) ? 0 : 1;
else
ret = (strcmp(allow, name) == 0) ? 1 : 0;
string_delete(section);
return ret;
}
/* appinterface_get_name */
char const * appinterface_get_app(AppInterface * appinterface)
{
return appinterface->name;
}
/* appinterface_get_args_count */
int appinterface_get_args_count(AppInterface * appinterface, size_t * count,
char const * function)
{
AppInterfaceCall * aic;
if((aic = _appinterface_get_call(appinterface, function)) == NULL)
return -1;
*count = aic->args_cnt;
return 0;
}
/* useful */
/* appinterface_callv */
int appinterface_callv(AppInterface * appinterface, App * app, void ** result,
char const * method, va_list args)
{
int ret;
AppInterfaceCall * call;
Variable * r;
if((call = _appinterface_get_call(appinterface, method)) == NULL)
return -1;
/* FIXME implement argv */
if(call->type.type == VT_NULL)
r = NULL;
else if((r = variable_new(call->type.type, NULL)) == NULL)
return -1;
if((ret = marshall_call(app, call->type.type, r, call->func,
call->args_cnt, NULL)) == 0
&& r != NULL)
{
if(result != NULL)
/* XXX return 0 anyway? */
ret = variable_get_as(r, call->type.type, *result);
variable_delete(r);
}
return ret;
}
/* appinterface_call_variablev */
int appinterface_call_variablev(AppInterface * appinterface, App * app,
Variable * result, char const * method,
size_t argc, Variable ** argv)
{
AppInterfaceCall * call;
if((call = _appinterface_get_call(appinterface, method)) == NULL)
return -1;
if(argc != call->args_cnt)
return -1;
/* FIXME implement argv */
return marshall_call(app, call->type.type, result, call->func, argc,
NULL);
}
/* private */
/* appinterface_get_call */
static AppInterfaceCall * _appinterface_get_call(AppInterface * appinterface,
String const * method)
{
size_t i;
for(i = 0; i < appinterface->calls_cnt; i++)
if(string_compare(appinterface->calls[i].name, method) == 0)
return &appinterface->calls[i];
error_set_code(1, "%s%s%s%s", "Unknown call \"", method,
"\" for interface ", appinterface->name);
return NULL;
}