#include "ipc.h" #include "utils.h" #include #include #include #include // error numbers #include #include // print structures #include "message.h" void service_path (char *path, const char *sname, int32_t index, int32_t version) { assert (path != NULL); assert (sname != NULL); memset (path, 0, PATH_MAX); 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; } enum ipc_errors ipc_server_init (char **env, struct ipc_connection_info *srv, const char *sname) { if (env == NULL) return IPC_ERROR_SERVER_INIT__NO_ENVIRONMENT_PARAM; if (srv == NULL) return IPC_ERROR_SERVER_INIT__NO_SERVICE_PARAM; if (sname == NULL) return IPC_ERROR_SERVER_INIT__NO_SERVER_NAME_PARAM; // 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; // gets the service path char buf [PATH_MAX]; memset (buf, 0, PATH_MAX); service_path (buf, sname, srv->index, srv->version); // gets the service path if (srv->spath != NULL) { free (srv->spath); } size_t s = strlen (buf); srv->spath = malloc (s+1); if (srv->spath == NULL) { return IPC_ERROR_SERVER_INIT__MALLOC; } memcpy (srv->spath, buf, s); srv->spath[s] = '\0'; // to be sure enum ipc_errors ret = usock_init (&srv->fd, srv->spath); if (ret != IPC_ERROR_NONE) { handle_err ("ipc_server_init", "usock_init"); return ret; } return IPC_ERROR_NONE; } enum ipc_errors ipc_connection (char **env, struct ipc_connection_info *srv, const char *sname) { // 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; if (env == NULL) return IPC_ERROR_CONNECTION__NO_ENVIRONMENT_PARAM; assert (srv != NULL); assert (sname != NULL); if (srv == NULL) { return IPC_ERROR_CONNECTION__NO_SERVER; } if (sname == NULL) { return IPC_ERROR_CONNECTION__NO_SERVICE_NAME; } // 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; } return IPC_ERROR_NONE; } enum ipc_errors ipc_server_close (struct ipc_connection_info *srv) { usock_close (srv->fd); enum ipc_errors ret = usock_remove (srv->spath); if (srv->spath != NULL) { free (srv->spath); srv->spath = NULL; } return ret; } enum ipc_errors ipc_close (struct ipc_connection_info *p) { return usock_close (p->fd); } enum ipc_errors ipc_accept (struct ipc_connection_info *srv, struct ipc_connection_info *p) { assert (srv != NULL); assert (p != NULL); if (srv == NULL) { return IPC_ERROR_ACCEPT__NO_SERVICE_PARAM; } if (p == NULL) { return IPC_ERROR_ACCEPT__NO_CLIENT_PARAM; } 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; } return IPC_ERROR_NONE; } enum ipc_errors ipc_read (const struct ipc_connection_info *p, struct ipc_message *m) { return ipc_message_read (p->fd, m); } enum ipc_errors ipc_write (const struct ipc_connection_info *p, const struct ipc_message *m) { return ipc_message_write (p->fd, m); } enum ipc_errors handle_new_connection (struct ipc_connection_info *cinfo , struct ipc_connection_infos *cinfos , struct ipc_connection_info **new_client) { if (cinfo == NULL) { return IPC_ERROR_HANDLE_NEW_CONNECTION__NO_CINFO_PARAM; } if (cinfos == NULL) { return IPC_ERROR_HANDLE_NEW_CONNECTION__NO_CINFOS_PARAM; } *new_client = malloc(sizeof(struct ipc_connection_info)); if (*new_client == NULL) { return IPC_ERROR_HANDLE_NEW_CONNECTION__MALLOC; } memset(*new_client, 0, sizeof(struct ipc_connection_info)); enum ipc_errors ret = ipc_accept (cinfo, *new_client); if (ret != IPC_ERROR_NONE) { handle_error("server_accept error"); return ret; } ret = ipc_add (cinfos, *new_client); if (ret != IPC_ERROR_NONE) { handle_error("ipc_clients_add error"); return ret; } return IPC_ERROR_NONE; } // 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 , struct ipc_event *event) { 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; } IPC_EVENT_CLEAN(event); int32_t i, j; /* master file descriptor list */ fd_set master; fd_set readf; /* clear the master and temp sets */ FD_ZERO(&master); FD_ZERO(&readf); /* maximum file descriptor number */ /* keep track of the biggest file descriptor */ 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); } readf = master; if(select(fdmax+1, &readf, NULL, NULL, NULL) == -1) { perror("select"); return IPC_ERROR_WAIT_EVENT__SELECT; } for (i = 0; i <= fdmax; i++) { if (FD_ISSET(i, &readf)) { if (cinfo != NULL && i == listener) { // connection 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; } IPC_EVENT_SET (event, IPC_EVENT_TYPE_CONNECTION, NULL, new_client); return IPC_ERROR_NONE; } else { for(j = 0; j < cinfos->size; j++) { if(i == cinfos->cinfos[j]->fd ) { // listen to what they have to say (disconnection or message) // then add a client to `event`, the ipc_event structure enum ipc_errors ret; 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 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"); ipc_message_empty (m); free (m); IPC_EVENT_SET(event, IPC_EVENT_TYPE_ERROR, NULL, pc); return ret; } // 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"); } ret = ipc_del (cinfos, pc); if (ret != IPC_ERROR_NONE) { handle_err( "ipc_wait_event", "ipc_del"); } 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 return IPC_ERROR_NONE; } // 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; } } } } } return IPC_ERROR_NONE; } // store and remove only pointers on allocated structures enum ipc_errors ipc_add (struct ipc_connection_infos *cinfos, struct ipc_connection_info *p) { assert(cinfos != NULL); assert(p != NULL); if (cinfos == NULL) { return IPC_ERROR_ADD__NO_PARAM_CLIENTS; } if (p == NULL) { return IPC_ERROR_ADD__NO_PARAM_CLIENT; } cinfos->size++; cinfos->cinfos = realloc(cinfos->cinfos, sizeof(struct ipc_connection_info) * cinfos->size); if (cinfos->cinfos == NULL) { return IPC_ERROR_ADD__EMPTY_LIST; } 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); if (cinfos == NULL) { return IPC_ERROR_DEL__NO_CLIENTS_PARAM; } if (p == NULL) { return IPC_ERROR_DEL__NO_CLIENT_PARAM; } if (cinfos->cinfos == NULL) { return IPC_ERROR_DEL__EMPTY_LIST; } 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; } } return IPC_ERROR_DEL__CANNOT_FIND_CLIENT; } void ipc_connections_free (struct ipc_connection_infos *cinfos) { if (cinfos->cinfos != NULL) { free (cinfos->cinfos); cinfos->cinfos = NULL; } cinfos->size = 0; } // 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; struct ipc_connection_info * copy = malloc (sizeof(struct ipc_connection_info)); if (copy == NULL) return NULL; memset (copy, 0, sizeof (struct ipc_connection_info)); memcpy (copy, p, sizeof (struct ipc_connection_info)); return copy; } // 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; } struct ipc_connection_info *cinfo; enum ipc_errors ret; ret = ipc_connection_gen (cinfo, 0, 0, fd, 'a'); return IPC_ERROR_NONE; } 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"); } } 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]); } }