diff --git a/src/communication.c b/src/communication.c index b8e8d51..0502c65 100644 --- a/src/communication.c +++ b/src/communication.c @@ -204,7 +204,7 @@ struct ipc_error ipc_close_all (struct ipc_ctx *ctx) T_R ((ctx == NULL), IPC_ERROR_CLOSE_ALL__NO_CTX_PARAM); for (size_t i = 0 ; i < ctx->size ; i++) { - TEST_IPC_P (ipc_close (ctx, i), "cannot close a connection in handle_message"); + TEST_IPC_P (ipc_close (ctx, i), "cannot close a connection in ipc_close_all"); } IPC_RETURN_NO_ERROR; @@ -526,6 +526,10 @@ struct ipc_error ipc_wait_event (struct ipc_ctx *ctx, struct ipc_event *event, i IPC_EVENT_CLEAN (event); + // By default, everything is alright. + SECURE_DECLARATION(struct ipc_error, final_return); + final_return.error_code = IPC_ERROR_NONE; + int32_t n = 0; for (size_t i = 0; i < ctx->size; i++) { @@ -533,7 +537,9 @@ struct ipc_error ipc_wait_event (struct ipc_ctx *ctx, struct ipc_event *event, i ctx->pollfd[i].events = POLLIN; } + // For each message to send… for (size_t i = 0; i < ctx->tx.size; i++) { + // … verify that its destination is available for message exchange. for (size_t y = 0; y < ctx->size; y++) { if (ctx->pollfd[y].fd == ctx->tx.messages[i].fd) { ctx->pollfd[y].events |= POLLOUT; @@ -584,7 +590,12 @@ struct ipc_error ipc_wait_event (struct ipc_ctx *ctx, struct ipc_event *event, i IPC_RETURN_NO_ERROR; } - for (size_t i = 0; i <= ctx->size; i++) { + for (size_t i = 0; i < ctx->size; i++) { + + // Whatever happens, we have the fd and the index in event. + event->index = i; + event->origin = ctx->pollfd[i].fd; + // Something to read or connection. if (ctx->pollfd[i].revents & POLLIN || ctx->cinfos[i].more_to_read == 1) { @@ -593,21 +604,25 @@ struct ipc_error ipc_wait_event (struct ipc_ctx *ctx, struct ipc_event *event, i // In case there is something to read for the server socket: new client. if (ctx->cinfos[i].type == IPC_CONNECTION_TYPE_SERVER) { - return ipc_accept_add (event, ctx, i); + final_return = ipc_accept_add (event, ctx, i); + goto wait_event_exit; } // fd is switched: using callbacks for IO operations. if (ctx->cinfos[i].type == IPC_CONNECTION_TYPE_SWITCHED) { - return handle_switched_message (event, ctx, i); + final_return = handle_switched_message (event, ctx, i); + goto wait_event_exit; } // No treatment of the socket if external socket: the libipc user should handle IO operations. if (ctx->cinfos[i].type == IPC_CONNECTION_TYPE_EXTERNAL) { IPC_EVENT_SET (event, IPC_EVENT_TYPE_EXTRA_SOCKET, i, ctx->pollfd[i].fd, NULL); - IPC_RETURN_NO_ERROR; + // Default: return no error. + goto wait_event_exit; } - return handle_new_message (event, ctx, i); + final_return = handle_new_message (event, ctx, i); + goto wait_event_exit; } // Something can be sent. @@ -616,20 +631,46 @@ struct ipc_error ipc_wait_event (struct ipc_ctx *ctx, struct ipc_event *event, i // fd is switched: using callbacks for IO operations. if (ctx->cinfos[i].type == IPC_CONNECTION_TYPE_SWITCHED) { - return handle_writing_switched_message (event, ctx, i); + final_return = handle_writing_switched_message (event, ctx, i); + goto wait_event_exit; } - return handle_writing_message (event, ctx, i); + final_return = handle_writing_message (event, ctx, i); + goto wait_event_exit; } // Disconnection. if (ctx->pollfd[i].revents & POLLHUP) { /** IPC_EVENT_SET: event, type, index, fd, message */ IPC_EVENT_SET (event, IPC_EVENT_TYPE_DISCONNECTION, i, ctx->pollfd[i].fd, NULL); - return ipc_close (ctx, i); + final_return = ipc_close (ctx, i); + goto wait_event_exit; + } + + if (ctx->pollfd[i].revents & POLLERR) { + printf ("POLLERR: PROBLEM WITH fd %d\n", ctx->pollfd[i].fd); + IPC_EVENT_SET (event, IPC_EVENT_TYPE_ERROR, i, ctx->pollfd[i].fd, NULL); + goto wait_event_exit; + } + + if (ctx->pollfd[i].revents & POLLNVAL) { + printf ("POLLNVAL: INVALID fd %d\n", ctx->pollfd[i].fd); + IPC_EVENT_SET (event, IPC_EVENT_TYPE_ERROR, i, ctx->pollfd[i].fd, NULL); + goto wait_event_exit; } } /** for loop: end of the message handling */ - IPC_RETURN_NO_ERROR; + printf ("END OF THE LOOP WITHOUT GOTO!\n"); + +wait_event_exit: + + /** TODO: tests on event, it has to be filled. */ + if (event->type == 0) { + printf ("EVENT TYPE NOT FILLED! code: %d, error_message: %s\n" + , final_return.error_code + , final_return.error_message); + } + + return final_return; } diff --git a/src/ipc.h b/src/ipc.h index 85b9a32..e16fd86 100644 --- a/src/ipc.h +++ b/src/ipc.h @@ -316,6 +316,8 @@ struct ipc_event { #define IPC_EVENT_CLEAN(pevent) {\ pevent->type = IPC_EVENT_TYPE_NOT_SET;\ + pevent->origin = 0;\ + pevent->index = 0;\ if (pevent->m != NULL) {\ ipc_message_empty (pevent->m);\ free(pevent->m);\ @@ -376,7 +378,7 @@ void ipc_messages_free (struct ipc_messages *); // Switch cases macros // print on error -#define ERROR_CASE(e,f,m) case e : { fprintf (stderr, "function %s: %s", f, m); } break; +#define ERROR_CASE(e,f,m) case e : { fprintf (stderr, "function %s: %s\n", f, m); } break; /*** * non public functions @@ -400,6 +402,7 @@ struct ipc_error service_path (char *path, const char *sname); **/ void ipc_ctx_switching_add (struct ipc_ctx *ctx, int orig, int dest); +int ipc_ctx_switching_del (struct ipc_ctx *ctx, int fd); void ipc_switching_add (struct ipc_switchings *is, int orig, int dest); int ipc_switching_del (struct ipc_switchings *is, int fd); int ipc_switching_get (struct ipc_switchings *is, int fd); diff --git a/src/network.c b/src/network.c index bca2fe2..f0579f7 100644 --- a/src/network.c +++ b/src/network.c @@ -118,6 +118,11 @@ void ipc_switching_add (struct ipc_switchings *is, int orig, int dest) // printf ("ipc_switching_add END: switchdb has %ld entries\n", is->size); } +int ipc_ctx_switching_del (struct ipc_ctx *ctx, int fd) +{ + return ipc_switching_del (&ctx->switchdb, fd); +} + int ipc_switching_del (struct ipc_switchings *is, int fd) { for (size_t i = 0; i < is->size; i++) { @@ -355,13 +360,14 @@ struct ipc_error fd_switching_read (struct ipc_event *event, struct ipc_ctx *ctx * NOTE: In any other case, the fd is, or should be closed. */ - // 1. close and remove both fd from switchdb - close (sw->dest); - ipc_del_fd (ctx, sw->dest); - // Should not close the client: it's the job of the libipc user application. - // XXX: this may be normal, but should be documented. + // 1. remove both fd from switchdb + // Client and servers should be closed by the libipc user application. + // close (sw->dest); // close (talkingfd); + + ipc_del_fd (ctx, sw->dest); ipc_del_fd (ctx, talkingfd); + ipc_switching_del (&ctx->switchdb, talkingfd); // 2. set event (either error or disconnection) diff --git a/src/usocket.c b/src/usocket.c index 2bafc06..7e7ff8d 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -24,6 +24,66 @@ struct ipc_error usock_send (const int32_t fd, const char *buf, size_t len, size { ssize_t ret = 0; ret = send (fd, buf, len, MSG_NOSIGNAL); + if (ret <= 0) + { + // TODO: Check for errno. + // Some choice could be made. + switch (errno) { + + // The receive buffer pointer(s) point outside the process's address space. + ERROR_CASE (EACCES, "usock_send", "write permission is denied"); + + // The socket is marked nonblocking and the requested operation would block. + // POSIX.1-2001 allows either error to be returned for this case, and does not + // require these constants to have the same value, so a portable application + // should check for both possibilities. + case (EWOULDBLOCK) : + ERROR_CASE (EAGAIN, "usock_send", "socket marked as nonblocking, but requested operation would block"); + + ERROR_CASE (EAGAIN, "usock_send", "socket not previously bound to an address and all ports are in use"); + + ERROR_CASE (EALREADY, "usock_send", "another Fast Open is in progress"); + + ERROR_CASE (EBADF, "usock_send", "sockfd is not a valid open file descriptor"); + + ERROR_CASE (ECONNRESET, "usock_send", "Connection reset by peer."); + + ERROR_CASE (EDESTADDRREQ, "usock_send", "socket not connection-mode, and no peer address is set."); + + ERROR_CASE (EFAULT, "usock_send", "an invalid user space address was specified for an argument"); + + // See signal(7). + ERROR_CASE (EINTR, "usock_send", "a signal occurred before any data was transmitted"); + + ERROR_CASE (EINVAL, "usock_send", "invalid argument passed"); + + // This error should not happen, and the recipient specification may be ignored. + ERROR_CASE (EISCONN, "usock_send", "connection-mode socket was already connected but a recipient was specified"); + + // The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible. + ERROR_CASE (EMSGSIZE, "usock_send", "cannot send a message of that size"); + + // This generally indicates that the interface has stopped sending, but + // may be caused by transient congestion. (Normally, this does not occur in Linux. + // Packets are just silently dropped when a device queue overflows.) + ERROR_CASE (ENOBUFS, "usock_send", "the output queue for the network interface was full"); + + ERROR_CASE (ENOMEM, "usock_send", "no memory available"); + + ERROR_CASE (ENOTCONN, "usock_send", "the socket is not connected, and no target has been given"); + + // Should not happen in libipc (watch out for libipc user application). + ERROR_CASE (ENOTSOCK, "usock_send", "the file descriptor sockfd does not refer to a socket"); + + // Should not happen in libipc. + ERROR_CASE (EOPNOTSUPP, "usock_send", "some bit in the flags argument is inappropriate for the socket type"); + + // In this case, the process will also receive a SIGPIPE unless MSG_NOSIGNAL is set. + ERROR_CASE (EPIPE, "usock_send", "the local end has been shut down on a connection oriented socket"); + + } + } + T_R ((ret <= 0), IPC_ERROR_USOCK_SEND); *sent = ret; IPC_RETURN_NO_ERROR;