/* $Id$ */ /* Copyright (c) 2012-2020 Pierre Pronchery */ /* 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 . */ #include #ifdef DEBUG # include #endif #include #include #include "App/appclient.h" #include "appmessage.h" #include "apptransport.h" #include "../config.h" /* FIXME: * clarify parsing name strings (if there is a colon, there is a transport, * although possibly empty) */ #ifndef PREFIX # define PREFIX "/usr/local" #endif #ifndef LIBDIR # define LIBDIR PREFIX "/lib" #endif /* AppTransport */ /* private */ /* types */ struct _AppTransport { AppTransportMode mode; AppTransportHelper helper; String * name; /* registration */ AppClient * appclient; /* plug-in */ AppTransportPluginHelper thelper; Plugin * plugin; AppTransportPlugin * tplugin; AppTransportPluginDefinition * definition; /* acknowledgements */ AppMessageID id; }; struct _AppTransportClient { AppTransport * transport; String * name; }; /* prototypes */ /* helpers */ static int _apptransport_helper_status(AppTransport * transport, AppTransportStatus status, unsigned int code, char const * message); static AppTransportClient * _apptransport_helper_client_new( AppTransport * transport, char const * name); static void _apptransport_helper_client_delete(AppTransport * transport, AppTransportClient * client); static int _apptransport_helper_client_receive(AppTransport * transport, AppTransportClient * client, AppMessage * message); /* protected */ /* functions */ /* apptransport_new */ AppTransport * apptransport_new(AppTransportMode mode, AppTransportHelper * helper, char const * transport, char const * name, Event * event) { AppTransport * apptransport; Plugin * plugin; if((plugin = plugin_new(LIBDIR, "App", "transport", transport)) == NULL) return NULL; if((apptransport = apptransport_new_plugin(mode, helper, plugin, name, event)) == NULL) { plugin_delete(plugin); return NULL; } return apptransport; } /* apptransport_new_app */ static String * _new_app_name(AppTransportMode mode, char const * app, char const * name); static String * _new_app_transport(String ** name); AppTransport * apptransport_new_app(AppTransportMode mode, AppTransportHelper * helper, char const * app, char const * name, Event * event) { AppTransport * apptransport; String * n; String * transport; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%u, \"%s\", \"%s\")\n", __func__, mode, app, name); #endif if((n = _new_app_name(mode, app, name)) == NULL) return NULL; if((transport = _new_app_transport(&n)) == NULL) { string_delete(n); return NULL; } apptransport = apptransport_new(mode, helper, transport, n, event); string_delete(transport); string_delete(n); return apptransport; } static String * _new_app_name(AppTransportMode mode, char const * app, char const * name) { String * var; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\", \"%s\")\n", __func__, app, name); #endif if(app == NULL || app[0] == '\0') { error_set_code(1, "%s", "Invalid App"); return NULL; } if(name != NULL) return string_new(name); /* obtain the desired transport and name from the environment */ if((var = string_new_append("APPSERVER_", app, NULL)) == NULL) return NULL; name = getenv(var); #ifdef DEBUG fprintf(stderr, "DEBUG: %s() \"%s\" \"%s\"\n", __func__, var, name); #endif string_delete(var); if(name == NULL) { if(mode == ATM_CLIENT) return apptransport_lookup(app); error_set_code(1, "%s: %s", app, "Could not lookup name"); return NULL; } return string_new(name); } static String * _new_app_transport(String ** name) { String * p; String * transport; if((p = strchr(*name, ':')) == NULL) /* XXX hard-coded default value */ return string_new("tcp"); /* XXX */ *p = '\0'; transport = string_new(*name); *p = ':'; if(transport == NULL || (*name = string_new(++p)) == NULL) { string_delete(transport); return NULL; } return transport; } /* apptransport_new_plugin */ static void _new_plugin_helper(AppTransport * apptransport, AppTransportMode mode, Event * event); AppTransport * apptransport_new_plugin(AppTransportMode mode, AppTransportHelper * helper, Plugin * plugin, char const * name, Event * event) { AppTransport * apptransport; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%u, \"%s\", \"%s\", %p)\n", __func__, mode, plugin, name, (void *)event); #endif /* check the arguments */ if(plugin == NULL) { error_set_code(1, "%s", "Invalid transport plug-in"); return NULL; } if(name == NULL || event == NULL) { error_set_code(1, "%s", "Invalid arguments"); return NULL; } /* allocate the transport */ if((apptransport = object_new(sizeof(*apptransport))) == NULL) return NULL; memset(apptransport, 0, sizeof(*apptransport)); apptransport->mode = mode; if(helper != NULL) apptransport->helper = *helper; apptransport->name = string_new(name); /* initialize the plug-in helper */ _new_plugin_helper(apptransport, mode, event); /* load the transport plug-in */ apptransport->plugin = plugin; if(apptransport->name == NULL || (apptransport->definition = plugin_lookup( apptransport->plugin, "transport")) == NULL || apptransport->definition->init == NULL || apptransport->definition->destroy == NULL || (apptransport->tplugin = apptransport->definition->init( &apptransport->thelper, mode, name)) == NULL) { apptransport->plugin = NULL; apptransport_delete(apptransport); return NULL; } return apptransport; } static void _new_plugin_helper(AppTransport * apptransport, AppTransportMode mode, Event * event) { apptransport->thelper.transport = apptransport; apptransport->thelper.event = event; apptransport->thelper.status = _apptransport_helper_status; apptransport->thelper.client_new = _apptransport_helper_client_new; apptransport->thelper.client_delete = _apptransport_helper_client_delete; apptransport->thelper.client_receive = _apptransport_helper_client_receive; } /* apptransport_delete */ void apptransport_delete(AppTransport * transport) { if(transport->appclient != NULL) appclient_delete(transport->appclient); if(transport->tplugin != NULL) transport->definition->destroy(transport->tplugin); if(transport->plugin != NULL) plugin_delete(transport->plugin); if(transport->name != NULL) string_delete(transport->name); object_delete(transport); } /* accessors */ /* apptransport_get_mode */ AppTransportMode apptransport_get_mode(AppTransport * transport) { return transport->mode; } /* apptransport_get_name */ String const * apptransport_get_name(AppTransport * transport) { return transport->name; } /* apptransport_get_transport */ String const * apptransport_get_transport(AppTransport * transport) { return transport->definition->name; } /* apptransport_client_get_name */ String const * apptransport_client_get_name(AppTransportClient * client) { return client->name; } /* useful */ /* apptransport_lookup */ String * apptransport_lookup(char const * app) { const char session[] = "Session"; String * name = NULL; AppClient * appclient; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, app); #endif if(strcmp(app, session) == 0) { error_set_code(1, "%s: %s", app, "Could not lookup"); return NULL; } if((appclient = appclient_new(NULL, session, NULL)) == NULL) return NULL; appclient_call(appclient, (void **)&name, "lookup", app); appclient_delete(appclient); #ifdef DEBUG fprintf(stderr, "DEBUG: %s() => \"%s\"\n", __func__, name); #endif if(name == NULL) error_set_code(1, "%s: %s", app, "Could not lookup"); return name; } /* apptransport_client_send */ int apptransport_client_send(AppTransport * transport, AppMessage * message, int acknowledge) { if(transport->mode == ATM_CLIENT && appmessage_get_type(message) == AMT_CALL && acknowledge != 0) /* FIXME will wrap around after 2^32-1 acknowledgements */ appmessage_set_id(message, ++transport->id); return transport->definition->client_send(transport->tplugin, message); } /* apptransport_server_register */ int apptransport_server_register(AppTransport * transport, char const * app, char const * name) { int ret; int res = -1; const char session[] = "Session"; if(transport->mode != ATM_SERVER) return -error_set_code(1, "%s", "Only servers can register to sessions"); if(transport->appclient != NULL) appclient_delete(transport->appclient); if((transport->appclient = appclient_new(NULL, session, name)) == NULL) return -1; ret = appclient_call(transport->appclient, (void **)&res, "register", app, transport->name); ret = (ret == 0 && res == 0) ? 0 : -1; return ret; } /* apptransport_server_send */ int apptransport_server_send(AppTransport * transport, AppTransportClient * client, AppMessage * message) { if(transport->mode != ATM_SERVER) return -error_set_code(1, "%s", "Only servers can reply to clients"); if(transport->definition->server_send == NULL) return -error_set_code(1, "%s", "This transport does not support replies"); return transport->definition->server_send(transport->tplugin, client, message); } /* private */ /* functions */ /* helpers */ /* apptransport_helper_status */ static int _apptransport_helper_status(AppTransport * transport, AppTransportStatus status, unsigned int code, char const * message) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%u, %u, \"%s\")\n", __func__, status, code, message); #endif /* FIXME really implement */ return 0; } /* apptransport_helper_client_new */ static AppTransportClient * _apptransport_helper_client_new( AppTransport * transport, char const * name) { AppTransportClient * client; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if((client = object_new(sizeof(*client))) == NULL) return NULL; client->transport = transport; if(name != NULL) { if((client->name = string_new(name)) == NULL) { object_delete(client); return NULL; } } else client->name = NULL; return client; } /* apptransport_helper_client_delete */ static void _apptransport_helper_client_delete(AppTransport * transport, AppTransportClient * client) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif object_delete(client); } /* apptransport_helper_client_receive */ static int _apptransport_helper_client_receive(AppTransport * transport, AppTransportClient * client, AppMessage * message) { AppMessageID id; #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %u %u \"%s\"\n", __func__, appmessage_get_type(message), appmessage_get_id(message), appmessage_get_method(message)); #endif if(transport->mode != ATM_SERVER) /* XXX improve the error message */ return -error_set_code(1, "Not a server"); /* XXX check for errors? */ transport->helper.message(transport->helper.data, transport, client, message); /* check if an acknowledgement is requested */ if((id = appmessage_get_id(message)) != 0) /* XXX we can ignore errors */ if((message = appmessage_new_acknowledgement(id)) != NULL) { apptransport_server_send(transport, client, message); appmessage_delete(message); } return 0; }