From 64f1977eb2016788589ab40f60b244d0268f7c02 Mon Sep 17 00:00:00 2001 From: Philippe PITTOLI Date: Sun, 28 Oct 2018 17:09:35 +0100 Subject: [PATCH] ipc.h, event, server loop() --- core/communication.c | 165 ++++++++++++++++++++++++++---- core/communication.h | 4 + core/error.h | 7 +- core/event.h | 35 +++++++ core/ipc.h | 15 +++ core/usocket.c | 1 - pong/app/pong.c | 1 - pong/app/pongd.c | 232 ++++++++++++++++++------------------------- 8 files changed, 304 insertions(+), 156 deletions(-) create mode 100644 core/event.h create mode 100644 core/ipc.h diff --git a/core/communication.c b/core/communication.c index 02b158f..6e6eaa4 100644 --- a/core/communication.c +++ b/core/communication.c @@ -1,6 +1,7 @@ #include "communication.h" #include "utils.h" #include "error.h" +#include "event.h" #include #include @@ -126,9 +127,8 @@ int ipc_application_write (struct ipc_service *srv, const struct ipc_message *m) /*calculer le max filedescriptor*/ -static int getMaxFd(struct ipc_clients *clients) +static int get_max_fd_from_ipc_clients_ (struct ipc_clients *clients) { - int i; int max = 0; @@ -141,6 +141,20 @@ static int getMaxFd(struct ipc_clients *clients) return max; } +static int get_max_fd_from_ipc_services_ (struct ipc_services *services) +{ + int i; + int max = 0; + + for (i = 0; i < services->size; i++ ) { + if (services->services[i]->service_fd > max) { + max = services->services[i]->service_fd; + } + } + + return max; +} + /* * ipc_server_select prend en parametre * * un tableau de client qu'on écoute @@ -182,7 +196,7 @@ int ipc_server_select (struct ipc_clients *clients, struct ipc_service *srv } /* keep track of the biggest file descriptor */ - fdmax = getMaxFd(clients) > srv->service_fd ? getMaxFd(clients) : srv->service_fd; + fdmax = get_max_fd_from_ipc_clients_ (clients) > srv->service_fd ? get_max_fd_from_ipc_clients_ (clients) : srv->service_fd; // printf ("loop ipc_server_select main_loop\n"); readf = master; @@ -211,21 +225,6 @@ int ipc_server_select (struct ipc_clients *clients, struct ipc_service *srv return 0; } -/*calculer le max filedescriptor*/ -static int getMaxFdServices(struct ipc_services *services) -{ - int i; - int max = 0; - - for (i = 0; i < services->size; i++ ) { - if (services->services[i]->service_fd > max) { - max = services->services[i]->service_fd; - } - } - - return max; -} - /* * ipc_application_select prend en parametre * * un tableau de server qu'on écoute @@ -261,7 +260,7 @@ int ipc_application_select (struct ipc_services *services, struct ipc_services * } /* keep track of the biggest file descriptor */ - fdmax = getMaxFdServices(services); + fdmax = get_max_fd_from_ipc_services_ (services); // printf ("loop ipc_server_select main_loop\n"); readf = master; @@ -285,3 +284,131 @@ int ipc_application_select (struct ipc_services *services, struct ipc_services * return 0; } + +int handle_new_connection (struct ipc_service *srv + , struct ipc_clients *clients + , struct ipc_client **new_client) +{ + *new_client = malloc(sizeof(struct ipc_client)); + memset(*new_client, 0, sizeof(struct ipc_client)); + + if (ipc_server_accept (srv, *new_client) < 0) { + handle_error("server_accept < 0"); + return 1; + } else { + printf("new connection\n"); + } + + if (ipc_client_add (clients, *new_client) < 0) { + handle_error("ipc_client_add < 0"); + return 1; + } + + return 0; +} + +int ipc_service_loop (struct ipc_clients *clients, struct ipc_service *srv + , struct ipc_event *event) +{ + assert (clients != NULL); + + IPC_EVENT_CLEAN(event); + + int i, j; + /* master file descriptor list */ + fd_set master; + fd_set readf; + + /* maximum file descriptor number */ + int fdmax; + /* listening socket descriptor */ + int listener = srv->service_fd; + + /* clear the master and temp sets */ + FD_ZERO(&master); + FD_ZERO(&readf); + /* add the listener to the master set */ + FD_SET(listener, &master); + + for (i=0; i < clients->size; i++) { + FD_SET(clients->clients[i]->proc_fd, &master); + } + + /* keep track of the biggest file descriptor */ + fdmax = get_max_fd_from_ipc_clients_ (clients) > srv->service_fd ? get_max_fd_from_ipc_clients_ (clients) : srv->service_fd; + + // printf ("loop ipc_server_select main_loop\n"); + readf = master; + if(select(fdmax+1, &readf, NULL, NULL, NULL) == -1) { + perror("select"); + return -1; + } + + /*run through the existing connections looking for data to be read*/ + for (i = 0; i <= fdmax; i++) { + // printf ("loop ipc_server_select inner loop\n"); + if (FD_ISSET(i, &readf)) { + if (i == listener) { + // connection + struct ipc_client *new_client = NULL; + handle_new_connection (srv, clients, &new_client); + IPC_EVENT_SET (event, IPC_EVENT_TYPE_CONNECTION, NULL, new_client); + return 0; + } else { + for(j = 0; j < clients->size; j++) { + if(i == clients->clients[j]->proc_fd ) { + // listen to what they have to say (disconnection or message) + // then add a client to `event`, the ipc_event structure + int ret = 0; + 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_client *pc = clients->clients[j]; + ret = ipc_server_read (pc, m); + if (ret < 0) { + handle_err ("ipc_service_loop", "ipc_server_read < 0"); + ipc_message_empty (m); + free (m); + + IPC_EVENT_SET(event, IPC_EVENT_TYPE_ERROR, NULL, pc); + return IPC_ERROR_READ; + } + + // disconnection: close the client then delete it from clients + if (ret == 1) { + if (ipc_server_close_client (pc) < 0) { + handle_err( "ipc_service_loop", "ipc_server_close_client < 0"); + } + if (ipc_client_del (clients, pc) < 0) { + handle_err( "ipc_service_loop", "ipc_client_del < 0"); + } + 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 0; + } + + // we received a new message from a client + IPC_EVENT_SET (event, IPC_EVENT_TYPE_MESSAGE, m, pc); + return 0; + } + } + } + } + } + + return 0; +} + +int ipc_application_loop (struct ipc_services *services) +{ + return 0; +} diff --git a/core/communication.h b/core/communication.h index bc6e2a7..6c614e0 100644 --- a/core/communication.h +++ b/core/communication.h @@ -8,6 +8,7 @@ #include "message.h" #include "client.h" +#include "event.h" #define COMMUNICATION_VERSION 1 @@ -32,6 +33,9 @@ int ipc_server_write (const struct ipc_client *, const struct ipc_message *m); int ipc_server_select (struct ipc_clients * clients, struct ipc_service *srv , struct ipc_clients *active_clients, int *new_connection); +int ipc_service_loop (struct ipc_clients *clients, struct ipc_service *srv + , struct ipc_event *event); + // APPLICATION // Initialize connection with unix socket diff --git a/core/error.h b/core/error.h index dcbb98d..41b2c6b 100644 --- a/core/error.h +++ b/core/error.h @@ -3,8 +3,11 @@ #include "logger.h" -#define IPC_ERROR_NOT_ENOUGH_MEMORY 100 -#define IPC_ERROR_WRONG_PARAMETERS 101 +enum ipc_errors { + IPC_ERROR_NOT_ENOUGH_MEMORY + , IPC_ERROR_WRONG_PARAMETERS + , IPC_ERROR_READ +}; // #define IPC_WITH_ERRORS 3 diff --git a/core/event.h b/core/event.h new file mode 100644 index 0000000..e12d95b --- /dev/null +++ b/core/event.h @@ -0,0 +1,35 @@ +#ifndef __IPC_EVENT__ +#define __IPC_EVENT__ + +#include "message.h" + +enum ipc_event_type { + IPC_EVENT_TYPE_NOT_SET + , IPC_EVENT_TYPE_ERROR + , IPC_EVENT_TYPE_CONNECTION + , IPC_EVENT_TYPE_DISCONNECTION + , IPC_EVENT_TYPE_MESSAGE +}; + +struct ipc_event { + enum ipc_event_type type; + void* origin; // currently used as an client or service pointer + void* m; // message pointer +}; + +#define IPC_EVENT_SET(pevent,type_,message_,origin_) {\ + pevent->type = type_; \ + pevent->m = message_; \ + pevent->origin = origin_; \ +}; + +#define IPC_EVENT_CLEAN(pevent) {\ + pevent->type = IPC_EVENT_TYPE_NOT_SET;\ + if (pevent->m != NULL) {\ + ipc_message_empty (pevent->m);\ + free(pevent->m);\ + pevent->m = NULL;\ + }\ +}; + +#endif diff --git a/core/ipc.h b/core/ipc.h new file mode 100644 index 0000000..80e0337 --- /dev/null +++ b/core/ipc.h @@ -0,0 +1,15 @@ +#ifndef __IPC_H__ +#define __IPC_H__ + +#include "client.h" +#include "communication.h" +#include "error.h" +#include "event.h" +#include "ipc.h" +#include "logger.h" +#include "message.h" +#include "queue.h" +#include "usocket.h" +#include "utils.h" + +#endif diff --git a/core/usocket.c b/core/usocket.c index cf45b4c..ce15cb6 100644 --- a/core/usocket.c +++ b/core/usocket.c @@ -57,7 +57,6 @@ int usock_recv (const int fd, char **buf, ssize_t *len) memcpy (&msize, *buf + 1, sizeof msize); } } - printf("msize = %u\n", msize); assert (msize < IPC_MAX_MESSAGE_SIZE); msize_read += ret - IPC_HEADER_SIZE; diff --git a/pong/app/pong.c b/pong/app/pong.c index c4b747f..76b27e1 100644 --- a/pong/app/pong.c +++ b/pong/app/pong.c @@ -45,7 +45,6 @@ void non_interactive (char *env[]) printf ("msg recv: %s\n", m.payload); ipc_message_empty (&m); - sleep(10); if (ipc_application_close (&srv) < 0) { handle_err("main", "application_close < 0"); exit (EXIT_FAILURE); diff --git a/pong/app/pongd.c b/pong/app/pongd.c index 4c69d82..2fde110 100644 --- a/pong/app/pongd.c +++ b/pong/app/pongd.c @@ -1,6 +1,5 @@ -#include "../../core/communication.h" -#include "../../core/client.h" -#include "../../core/error.h" +#include "../../core/ipc.h" +#include #include #include @@ -11,147 +10,110 @@ int cpt = 0; struct ipc_service *srv = 0; +struct ipc_clients *clients; -void handle_new_connection (struct ipc_clients *clients) -{ - struct ipc_client *p = malloc(sizeof(struct ipc_client)); - memset(p, 0, sizeof(struct ipc_client)); - - if (ipc_server_accept (srv, p) < 0) { - handle_error("server_accept < 0"); - } else { - printf("new connection\n"); - } - - if (ipc_client_add (clients, p) < 0) { - handle_error("ipc_client_add < 0"); - } - - cpt++; - printf ("%d client(s)\n", cpt); -} - -void handle_new_msg (struct ipc_clients *clients, struct ipc_clients *clients_talking) -{ - int ret = 0; - struct ipc_message m; - memset (&m, 0, sizeof (struct ipc_message)); - int i = 0; - for (i = 0; i < clients_talking->size; i++) { - // printf ("loop handle_new_msg\n"); - - // current talking client - struct ipc_client *pc = clients_talking->clients[i]; - - ret = ipc_server_read (pc, &m); - if (ret < 0) { - handle_error("server_read < 0"); - } - - // close the client then delete it from clients - if (ret == 1) { - cpt--; - printf ("disconnection => %d client(s) remaining\n", cpt); - - if (ipc_server_close_client (pc) < 0) { - handle_err( "handle_new_msg", "server_close_client < 0"); - } - if (ipc_client_del (clients, pc) < 0) { - handle_err( "handle_new_msg", "ipc_client_del < 0"); - } - if (ipc_client_del (clients_talking, pc) < 0) { - handle_err( "handle_new_msg", "ipc_client_del < 0"); - } - i--; - - // free the ipc_client structure - free (pc); - continue; - } - - if (m.type == MSG_TYPE_SERVER_CLOSE) { - // free remaining clients - for (int y = 0; y < clients->size ; y++) { - struct ipc_client *cli = clients->clients[y]; - // TODO: replace with specific ipc_client_empty function - if (cli != NULL) - free (cli); - clients->clients[y] = NULL; - } - - ipc_clients_free (clients); - ipc_clients_free (clients_talking); - - if (ipc_server_close (srv) < 0) { - handle_error("server_close < 0"); - } - - ipc_message_empty (&m); - free (srv); - exit (0); - } - - if (m.length > 0) { - printf ("new message : %.*s\n", m.length, m.payload); - } - if (ipc_server_write (pc, &m) < 0) { - handle_err( "handle_new_msg", "server_write < 0"); - } - - // empty the message structure - ipc_message_empty (&m); - memset (&m, 0, sizeof m); - } -} - -/* - * main loop - * - * accept new application connections - * read a message and send it back - * close a connection if MSG_TYPE_CLOSE received - */ void main_loop () { int ret = 0; - struct ipc_clients clients; - memset(&clients, 0, sizeof(struct ipc_clients)); + clients = malloc (sizeof (struct ipc_clients)); + memset(clients, 0, sizeof(struct ipc_clients)); - struct ipc_clients clients_talking; - memset(&clients_talking, 0, sizeof(struct ipc_clients)); + struct ipc_event event; + memset(&event, 0, sizeof (struct ipc_event)); + event.type = IPC_EVENT_TYPE_NOT_SET; while(1) { - int new_connection = 0; - ret = ipc_server_select (&clients, srv, &clients_talking, &new_connection); - if (ret < 0) { - handle_error("ipc_server_select < 0"); + // ipc_service_loop provides one event at a time + // warning: event->m is free'ed if not NULL + ret = ipc_service_loop (clients, srv, &event); + if (ret != 0) { + handle_error("ipc_service_loop != 0"); + // the application will shut down, and close the service + if (ipc_server_close (srv) < 0) { + handle_error("ipc_server_close < 0"); + } + exit (EXIT_FAILURE); } - if (new_connection) { - handle_new_connection (&clients); - } + switch (event.type) { + case IPC_EVENT_TYPE_CONNECTION: + { + cpt++; + printf ("connection: %d clients connected\n", cpt); + printf ("new client has the fd %d\n", ((struct ipc_client*) event.origin)->proc_fd); + }; + break; + case IPC_EVENT_TYPE_DISCONNECTION: + { + cpt--; + printf ("disconnection: %d clients remaining\n", cpt); - if (clients_talking.size > 0) { - handle_new_msg (&clients, &clients_talking); - } - ipc_clients_free (&clients_talking); + // free the ipc_client structure + free (event.origin); + }; + break; + case IPC_EVENT_TYPE_MESSAGE: + { + struct ipc_message *m = event.m; + if (m->length > 0) { + printf ("message received (type %d): %.*s\n", m->type, m->length, m->payload); + } + if (ipc_server_write (event.origin, m) < 0) { + handle_err( "handle_new_msg", "server_write < 0"); + } + }; + break; + case IPC_EVENT_TYPE_ERROR: + { + fprintf (stderr, "a problem happened with client %d\n" + , ((struct ipc_client*) event.origin)->proc_fd); + }; + break; + default : + { + fprintf (stderr, "there must be a problem, event not set\n"); + }; + } } + // should never go there exit (1); } +void exit_program(int signal) +{ + printf("Quitting, signal: %d\n", signal); + + // free remaining clients + for (int i = 0; i < clients->size ; i++) { + struct ipc_client *cli = clients->clients[i]; + // TODO: replace with specific ipc_client_empty function + if (cli != NULL) { + // ipc_client_empty (cli); + free (cli); + } + clients->clients[i] = NULL; + } + + ipc_clients_free (clients); + free (clients); + + + // the application will shut down, and close the service + if (ipc_server_close (srv) < 0) { + handle_error("ipc_server_close < 0"); + } + free (srv); + + exit(EXIT_SUCCESS); +} + /* - * service ping-pong - * - * 1. creates the unix socket /run/ipc/.sock, then listens - * 2. listen for new clients - * 3. then accept a new client, and send back everything it sends - * 4. close any client that closes its socket - * - * and finally, stop the program once a client sends a SERVER CLOSE command + * service ping-pong: send back everything sent by the clients + * stop the program on SIGTERM, SIGALRM, SIGUSR{1,2}, SIGHUP signals */ int main(int argc, char * argv[], char **env) @@ -159,6 +121,8 @@ int main(int argc, char * argv[], char **env) argc = argc; // warnings argv = argv; // warnings + printf ("pid = %d\n", getpid ()); + srv = malloc (sizeof (struct ipc_service)); if (srv == NULL) { exit (1); @@ -170,20 +134,22 @@ int main(int argc, char * argv[], char **env) // unlink("/tmp/ipc/pongd-0-0"); if (ipc_server_init (env, srv, PONGD_SERVICE_NAME) < 0) { - handle_error("server_init < 0"); + handle_error("ipc_server_init < 0"); return EXIT_FAILURE; } printf ("Listening on %s.\n", srv->spath); printf("MAIN: server created\n" ); - // the service will loop until the end of time, a specific message, a signal + signal (SIGHUP, exit_program); + signal (SIGALRM, exit_program); + signal (SIGUSR1, exit_program); + signal (SIGUSR2, exit_program); + signal (SIGTERM, exit_program); + + // the service will loop until the end of time, or a signal main_loop (); - // the application will shut down, and remove the service named pipe - if (ipc_server_close (srv) < 0) { - handle_error("server_close < 0"); - } - - return EXIT_SUCCESS; + // main_loop should not return + return EXIT_FAILURE; }