libipc-old/src/communication.c

493 lines
12 KiB
C
Raw Normal View History

2019-06-03 21:25:59 +02:00
#include "ipc.h"
#include "utils.h"
#include <unistd.h>
2016-12-17 18:00:04 +01:00
#include <assert.h>
#include <stdio.h>
2019-06-03 21:25:59 +02:00
#include <errno.h> // error numbers
#include <stdlib.h>
#include <string.h>
// print structures
#include "message.h"
2016-10-28 13:58:04 +02:00
2018-11-02 21:03:54 +01:00
void service_path (char *path, const char *sname, int32_t index, int32_t version)
{
2016-12-21 01:26:47 +01:00
assert (path != NULL);
assert (sname != NULL);
2019-06-03 21:25:59 +02:00
2016-12-17 18:00:04 +01:00
memset (path, 0, PATH_MAX);
2019-06-03 21:25:59 +02:00
char * rundir = getenv ("IPC_RUNDIR");
if (rundir == NULL)
rundir = RUNDIR;
snprintf (path, PATH_MAX, "%s/%s-%d-%d", rundir, sname, index, version);
}
/*calculer le max filedescriptor*/
static int32_t get_max_fd (struct ipc_connection_infos *cinfos)
{
int32_t i;
int32_t max = 0;
for (i = 0; i < cinfos->size; i++ ) {
if (cinfos->cinfos[i]->fd > max) {
max = cinfos->cinfos[i]->fd;
}
}
return max;
2016-06-12 12:38:43 +02:00
}
2019-06-03 21:25:59 +02:00
enum ipc_errors ipc_server_init (char **env, struct ipc_connection_info *srv, const char *sname)
2016-05-26 18:27:59 +02:00
{
2019-06-03 21:25:59 +02:00
if (env == NULL)
return IPC_ERROR_SERVER_INIT__NO_ENVIRONMENT_PARAM;
2016-06-05 20:48:13 +02:00
if (srv == NULL)
2019-06-03 21:25:59 +02:00
return IPC_ERROR_SERVER_INIT__NO_SERVICE_PARAM;
2016-05-26 18:27:59 +02:00
2019-06-03 21:25:59 +02:00
if (sname == NULL)
return IPC_ERROR_SERVER_INIT__NO_SERVER_NAME_PARAM;
2016-06-12 14:41:25 +02:00
2019-06-03 21:25:59 +02:00
// TODO: loop over environment variables
// any IPC_NETWORK_* should be shared with the network service
// in order to route requests over any chosen protocol stack
// ex: IPC_NETWORK_AUDIO="tor://some.example.com/"
2016-06-12 14:41:25 +02:00
env = env;
2016-12-17 18:00:04 +01:00
// gets the service path
2019-06-03 21:25:59 +02:00
char buf [PATH_MAX];
memset (buf, 0, PATH_MAX);
service_path (buf, sname, srv->index, srv->version);
2016-06-13 09:47:19 +02:00
2019-06-03 21:25:59 +02:00
// gets the service path
if (srv->spath != NULL) {
free (srv->spath);
2018-10-08 15:18:56 +02:00
}
2019-06-03 21:25:59 +02:00
size_t s = strlen (buf);
srv->spath = malloc (s+1);
if (srv->spath == NULL) {
return IPC_ERROR_SERVER_INIT__MALLOC;
2018-10-08 15:18:56 +02:00
}
2019-06-03 21:25:59 +02:00
memcpy (srv->spath, buf, s);
srv->spath[s] = '\0'; // to be sure
2019-06-03 21:25:59 +02:00
enum ipc_errors ret = usock_init (&srv->fd, srv->spath);
if (ret != IPC_ERROR_NONE) {
handle_err ("ipc_server_init", "usock_init");
return ret;
}
2016-05-26 18:27:59 +02:00
2019-06-03 21:25:59 +02:00
return IPC_ERROR_NONE;
}
2019-06-03 21:25:59 +02:00
enum ipc_errors ipc_connection (char **env, struct ipc_connection_info *srv, const char *sname)
{
2019-06-03 21:25:59 +02:00
// TODO: loop over environment variables
// any IPC_NETWORK_* should be shared with the network service
// in order to route requests over any chosen protocol stack
// ex: IPC_NETWORK_AUDIO="tor://some.example.com/"
env = env;
2019-06-03 21:25:59 +02:00
if (env == NULL)
return IPC_ERROR_CONNECTION__NO_ENVIRONMENT_PARAM;
2016-11-03 22:44:35 +01:00
2016-12-17 18:00:04 +01:00
assert (srv != NULL);
assert (sname != NULL);
2016-10-28 13:58:04 +02:00
2016-12-17 18:00:04 +01:00
if (srv == NULL) {
2019-06-03 21:25:59 +02:00
return IPC_ERROR_CONNECTION__NO_SERVER;
2016-12-17 18:00:04 +01:00
}
2016-10-28 13:58:04 +02:00
2019-06-03 21:25:59 +02:00
if (sname == NULL) {
return IPC_ERROR_CONNECTION__NO_SERVICE_NAME;
}
2019-06-03 21:25:59 +02:00
// gets the service path
char buf [PATH_MAX];
memset (buf, 0, PATH_MAX);
service_path (buf, sname, srv->index, srv->version);
enum ipc_errors ret = usock_connect (&srv->fd, buf);
if (ret != IPC_ERROR_NONE) {
handle_err ("ipc_connection", "usock_connect ret");
return ret;
2018-10-08 15:18:56 +02:00
}
2016-10-28 13:58:04 +02:00
2019-06-03 21:25:59 +02:00
return IPC_ERROR_NONE;
}
2019-06-03 21:25:59 +02:00
enum ipc_errors ipc_server_close (struct ipc_connection_info *srv)
2016-05-26 18:27:59 +02:00
{
2019-06-03 21:25:59 +02:00
usock_close (srv->fd);
enum ipc_errors ret = usock_remove (srv->spath);
if (srv->spath != NULL) {
free (srv->spath);
srv->spath = NULL;
}
return ret;
2016-05-26 18:27:59 +02:00
}
2019-06-03 21:25:59 +02:00
enum ipc_errors ipc_close (struct ipc_connection_info *p)
{
return usock_close (p->fd);
2016-05-26 18:27:59 +02:00
}
2019-06-03 21:25:59 +02:00
enum ipc_errors ipc_accept (struct ipc_connection_info *srv, struct ipc_connection_info *p)
2016-05-26 18:27:59 +02:00
{
2019-06-03 21:25:59 +02:00
assert (srv != NULL);
assert (p != NULL);
2016-12-20 23:36:00 +01:00
2019-06-03 21:25:59 +02:00
if (srv == NULL) {
return IPC_ERROR_ACCEPT__NO_SERVICE_PARAM;
}
2016-12-20 23:36:00 +01:00
2019-06-03 21:25:59 +02:00
if (p == NULL) {
return IPC_ERROR_ACCEPT__NO_CLIENT_PARAM;
}
2017-01-19 22:07:52 +01:00
2019-06-03 21:25:59 +02:00
enum ipc_errors ret = usock_accept (srv->fd, &p->fd);
if (ret != IPC_ERROR_NONE) {
handle_err ("ipc_accept", "usock_accept");
return IPC_ERROR_ACCEPT;
}
2017-01-19 22:07:52 +01:00
2019-06-03 21:25:59 +02:00
return IPC_ERROR_NONE;
2017-01-19 22:07:52 +01:00
}
2019-06-03 21:25:59 +02:00
enum ipc_errors ipc_read (const struct ipc_connection_info *p, struct ipc_message *m)
2018-10-28 17:09:35 +01:00
{
2019-06-03 21:25:59 +02:00
return ipc_message_read (p->fd, m);
2018-10-28 17:09:35 +01:00
}
2019-06-03 21:25:59 +02:00
enum ipc_errors ipc_write (const struct ipc_connection_info *p, const struct ipc_message *m)
2016-12-22 21:48:35 +01:00
{
2019-06-03 21:25:59 +02:00
return ipc_message_write (p->fd, m);
}
2018-10-10 23:08:58 +02:00
2019-06-03 21:25:59 +02:00
enum ipc_errors handle_new_connection (struct ipc_connection_info *cinfo
, struct ipc_connection_infos *cinfos
, struct ipc_connection_info **new_client)
2018-10-10 23:08:58 +02:00
{
2019-06-03 21:25:59 +02:00
if (cinfo == NULL) {
return IPC_ERROR_HANDLE_NEW_CONNECTION__NO_CINFO_PARAM;
}
2018-10-10 23:08:58 +02:00
2019-06-03 21:25:59 +02:00
if (cinfos == NULL) {
return IPC_ERROR_HANDLE_NEW_CONNECTION__NO_CINFOS_PARAM;
2018-10-10 23:08:58 +02:00
}
2019-06-03 21:25:59 +02:00
*new_client = malloc(sizeof(struct ipc_connection_info));
if (*new_client == NULL) {
return IPC_ERROR_HANDLE_NEW_CONNECTION__MALLOC;
2018-10-10 23:08:58 +02:00
}
2019-06-03 21:25:59 +02:00
memset(*new_client, 0, sizeof(struct ipc_connection_info));
2018-10-28 17:09:35 +01:00
2019-06-03 21:25:59 +02:00
enum ipc_errors ret = ipc_accept (cinfo, *new_client);
if (ret != IPC_ERROR_NONE) {
handle_error("server_accept error");
return ret;
2018-10-28 17:09:35 +01:00
}
2019-06-03 21:25:59 +02:00
ret = ipc_add (cinfos, *new_client);
if (ret != IPC_ERROR_NONE) {
handle_error("ipc_clients_add error");
return ret;
2018-10-28 17:09:35 +01:00
}
2019-06-03 21:25:59 +02:00
return IPC_ERROR_NONE;
2018-10-28 17:09:35 +01:00
}
2019-06-03 21:25:59 +02:00
// TODO: should replace
// ipc_service_poll_event
// ipc_application_poll_event
// ipc_application_peek_event
// ipc_application_poll_event_
enum ipc_errors ipc_wait_event (struct ipc_connection_infos *cinfos
, struct ipc_connection_info *cinfo // NULL for clients
2018-10-28 17:09:35 +01:00
, struct ipc_event *event)
{
2019-06-03 21:25:59 +02:00
assert (cinfos != NULL);
if (cinfos == NULL) {
return IPC_ERROR_WAIT_EVENT__NO_CLIENTS_PARAM;
}
if (event == NULL) {
return IPC_ERROR_WAIT_EVENT__NO_EVENT_PARAM;
}
2018-10-28 17:09:35 +01:00
IPC_EVENT_CLEAN(event);
2018-11-02 21:03:54 +01:00
int32_t i, j;
2018-10-28 17:09:35 +01:00
/* master file descriptor list */
fd_set master;
fd_set readf;
/* clear the master and temp sets */
FD_ZERO(&master);
FD_ZERO(&readf);
2019-06-03 21:25:59 +02:00
/* maximum file descriptor number */
2018-10-28 17:09:35 +01:00
/* keep track of the biggest file descriptor */
2019-06-03 21:25:59 +02:00
int32_t fdmax = get_max_fd (cinfos);
/* listening socket descriptor */
int32_t listener;
if (cinfo != NULL) {
listener = cinfo->fd;
/* add the listener to the master set */
FD_SET(listener, &master);
// if listener is max fd
if (fdmax < listener)
fdmax = listener;
}
for (i=0; i < cinfos->size; i++) {
FD_SET(cinfos->cinfos[i]->fd, &master);
}
2018-10-28 17:09:35 +01:00
readf = master;
if(select(fdmax+1, &readf, NULL, NULL, NULL) == -1) {
perror("select");
2019-06-03 21:25:59 +02:00
return IPC_ERROR_WAIT_EVENT__SELECT;
2018-10-28 17:09:35 +01:00
}
for (i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &readf)) {
2019-06-03 21:25:59 +02:00
if (cinfo != NULL && i == listener) {
2018-10-28 17:09:35 +01:00
// connection
2019-06-03 21:25:59 +02:00
struct ipc_connection_info *new_client = NULL;
enum ipc_errors ret = handle_new_connection (cinfo, cinfos, &new_client);
if (ret != IPC_ERROR_NONE) {
// TODO: quit the program
return ret;
}
2018-10-28 17:09:35 +01:00
IPC_EVENT_SET (event, IPC_EVENT_TYPE_CONNECTION, NULL, new_client);
2019-06-03 21:25:59 +02:00
return IPC_ERROR_NONE;
2018-10-28 17:09:35 +01:00
} else {
2019-06-03 21:25:59 +02:00
for(j = 0; j < cinfos->size; j++) {
if(i == cinfos->cinfos[j]->fd ) {
2018-10-28 17:09:35 +01:00
// listen to what they have to say (disconnection or message)
// then add a client to `event`, the ipc_event structure
2019-06-03 21:25:59 +02:00
enum ipc_errors ret;
2018-10-28 17:09:35 +01:00
struct ipc_message *m = NULL;
m = malloc (sizeof(struct ipc_message));
if (m == NULL) {
return IPC_ERROR_NOT_ENOUGH_MEMORY;
}
memset (m, 0, sizeof (struct ipc_message));
// current talking client
2019-06-03 21:25:59 +02:00
struct ipc_connection_info *pc = cinfos->cinfos[j];
ret = ipc_read (pc, m);
if (ret != IPC_ERROR_NONE && ret != IPC_ERROR_CLOSED_RECIPIENT) {
handle_err ("ipc_wait_event", "ipc_read");
2018-10-28 17:09:35 +01:00
ipc_message_empty (m);
free (m);
IPC_EVENT_SET(event, IPC_EVENT_TYPE_ERROR, NULL, pc);
2019-06-03 21:25:59 +02:00
return ret;
2018-10-28 17:09:35 +01:00
}
2019-06-03 21:25:59 +02:00
// disconnection: close the client then delete it from cinfos
if (ret == IPC_ERROR_CLOSED_RECIPIENT) {
ret = ipc_close (pc);
if (ret != IPC_ERROR_NONE) {
handle_err( "ipc_wait_event", "ipc_close");
2018-10-28 17:09:35 +01:00
}
2019-06-03 21:25:59 +02:00
ret = ipc_del (cinfos, pc);
if (ret != IPC_ERROR_NONE) {
handle_err( "ipc_wait_event", "ipc_del");
2018-10-28 17:09:35 +01:00
}
ipc_message_empty (m);
free (m);
IPC_EVENT_SET(event, IPC_EVENT_TYPE_DISCONNECTION, NULL, pc);
// warning: do not forget to free the ipc_client structure
2019-06-03 21:25:59 +02:00
return IPC_ERROR_NONE;
2018-10-28 17:09:35 +01:00
}
2019-06-03 21:25:59 +02:00
// we received a new message
// from a client
if (pc->type == 'a') {
IPC_EVENT_SET (event, IPC_EVENT_TYPE_EXTRA_SOCKET, m, pc);
}
else {
IPC_EVENT_SET (event, IPC_EVENT_TYPE_MESSAGE, m, pc);
}
return IPC_ERROR_NONE;
2018-10-28 17:09:35 +01:00
}
}
}
}
}
2019-06-03 21:25:59 +02:00
return IPC_ERROR_NONE;
2018-10-28 17:09:35 +01:00
}
2019-06-03 21:25:59 +02:00
// store and remove only pointers on allocated structures
enum ipc_errors ipc_add (struct ipc_connection_infos *cinfos, struct ipc_connection_info *p)
2018-10-28 17:09:35 +01:00
{
2019-06-03 21:25:59 +02:00
assert(cinfos != NULL);
assert(p != NULL);
2019-06-03 21:25:59 +02:00
if (cinfos == NULL) {
return IPC_ERROR_ADD__NO_PARAM_CLIENTS;
}
2019-06-03 21:25:59 +02:00
if (p == NULL) {
return IPC_ERROR_ADD__NO_PARAM_CLIENT;
}
2019-06-03 21:25:59 +02:00
cinfos->size++;
cinfos->cinfos = realloc(cinfos->cinfos, sizeof(struct ipc_connection_info) * cinfos->size);
2019-06-03 21:25:59 +02:00
if (cinfos->cinfos == NULL) {
return IPC_ERROR_ADD__EMPTY_LIST;
}
2019-06-03 21:25:59 +02:00
cinfos->cinfos[cinfos->size - 1] = p;
return IPC_ERROR_NONE;
}
enum ipc_errors ipc_del (struct ipc_connection_infos *cinfos, struct ipc_connection_info *p)
{
assert(cinfos != NULL);
assert(p != NULL);
2019-06-03 21:25:59 +02:00
if (cinfos == NULL) {
return IPC_ERROR_DEL__NO_CLIENTS_PARAM;
}
2019-06-03 21:25:59 +02:00
if (p == NULL) {
return IPC_ERROR_DEL__NO_CLIENT_PARAM;
}
2019-06-03 21:25:59 +02:00
if (cinfos->cinfos == NULL) {
return IPC_ERROR_DEL__EMPTY_LIST;
}
2019-06-03 21:25:59 +02:00
int32_t i;
for (i = 0; i < cinfos->size; i++) {
if (cinfos->cinfos[i] == p) {
cinfos->cinfos[i] = cinfos->cinfos[cinfos->size-1];
cinfos->size--;
if (cinfos->size == 0) {
ipc_connections_free (cinfos);
}
else {
cinfos->cinfos = realloc(cinfos->cinfos, sizeof(struct ipc_connection_info) * cinfos->size);
if (cinfos->cinfos == NULL) {
return IPC_ERROR_DEL__EMPTIED_LIST;
}
}
return IPC_ERROR_NONE;
}
}
2019-06-03 21:25:59 +02:00
return IPC_ERROR_DEL__CANNOT_FIND_CLIENT;
}
2019-06-03 21:25:59 +02:00
void ipc_connections_free (struct ipc_connection_infos *cinfos)
{
if (cinfos->cinfos != NULL) {
free (cinfos->cinfos);
cinfos->cinfos = NULL;
}
cinfos->size = 0;
}
2019-06-03 21:25:59 +02:00
// TODO: should replace ipc_client_server_copy and ipc_server_client_copy
struct ipc_connection_info * ipc_connection_copy (const struct ipc_connection_info *p)
{
if (p == NULL)
return NULL;
2019-06-03 21:25:59 +02:00
struct ipc_connection_info * copy = malloc (sizeof(struct ipc_connection_info));
2019-06-03 21:25:59 +02:00
if (copy == NULL)
return NULL;
2019-06-03 21:25:59 +02:00
memset (copy, 0, sizeof (struct ipc_connection_info));
memcpy (copy, p, sizeof (struct ipc_connection_info));
2019-06-03 21:25:59 +02:00
return copy;
}
2019-06-03 21:25:59 +02:00
// TODO: should replace ipc_server_client_eq, ipc_service_eq
int8_t ipc_connection_eq (const struct ipc_connection_info *p1, const struct ipc_connection_info *p2)
{
return (p1->type == p2->type && p1->version == p2->version && p1->index == p2->index && p1->fd == p2->fd);
}
// create the client service structure
// TODO: should replace ipc_client_server_gen, ipc_server_client_gen
enum ipc_errors ipc_connection_gen (struct ipc_connection_info *cinfo
, uint32_t index, uint32_t version, int fd, char type)
{
if (cinfo == NULL) {
return IPC_ERROR_CONNECTION_GEN__NO_CINFO;
}
cinfo->type = type;
cinfo->version = version;
cinfo->index = index;
cinfo->fd = fd;
return IPC_ERROR_NONE;
}
// add an arbitrary file descriptor to read
enum ipc_errors ipc_add_fd (struct ipc_connection_infos *cinfos, int fd)
{
if (cinfos == NULL) {
return IPC_ERROR_ADD_FD__NO_PARAM_CINFOS;
}
2019-06-03 21:25:59 +02:00
struct ipc_connection_info *cinfo;
enum ipc_errors ret;
ret = ipc_connection_gen (cinfo, 0, 0, fd, 'a');
return IPC_ERROR_NONE;
2018-10-28 17:09:35 +01:00
}
2019-06-03 21:25:59 +02:00
void ipc_connection_print (struct ipc_connection_info *cinfo)
{
if (cinfo == NULL) {
return;
}
printf ("fd %d: index %d, version %d, type %c"
, cinfo->fd, cinfo->index, cinfo->version, cinfo->type);
if (cinfo->spath != NULL) {
printf (", path %s\n", cinfo->spath);
}
else {
printf ("\n");
}
}
2019-06-03 21:25:59 +02:00
void ipc_connections_print (struct ipc_connection_infos *cinfos)
{
int32_t i;
for (i = 0; i < cinfos->size; i++) {
printf("[%d] : ", i);
ipc_connection_print(cinfos->cinfos[i]);
}
}