diff --git a/examples/simple-tcpd.c b/examples/simple-tcpd.c index 7a8074e..923b7f4 100644 --- a/examples/simple-tcpd.c +++ b/examples/simple-tcpd.c @@ -110,7 +110,7 @@ int accept_new_client (int serverfd) EXIT_FAILURE); // adding a client - ipc_add_fd (ctx, sock_fd_client); + ipc_add_fd_switched (ctx, sock_fd_client); return sock_fd_client; } diff --git a/src/communication.c b/src/communication.c index 9f3d95f..8983b61 100644 --- a/src/communication.c +++ b/src/communication.c @@ -276,13 +276,12 @@ struct ipc_error ipc_del (struct ipc_ctx *ctx, uint32_t index) IPC_RETURN_NO_ERROR; } -// add an arbitrary file descriptor to read -struct ipc_error ipc_add_fd (struct ipc_ctx *ctx, int fd) +struct ipc_error ipc_add_fd_ (struct ipc_ctx *ctx, int fd, enum ipc_connection_type type) { T_R ((ctx == NULL), IPC_ERROR_ADD_FD__NO_PARAM_CINFOS); SECURE_DECLARATION (struct ipc_connection_info, cinfo); - cinfo.type = IPC_CONNECTION_TYPE_EXTERNAL; + cinfo.type = type; SECURE_DECLARATION (struct pollfd, pollfd); pollfd.fd = fd; @@ -291,6 +290,18 @@ struct ipc_error ipc_add_fd (struct ipc_ctx *ctx, int fd) return ipc_add (ctx, &cinfo, &pollfd); } +// add a switched file descriptor to read +struct ipc_error ipc_add_fd_switched (struct ipc_ctx *ctx, int fd) +{ + return ipc_add_fd_ (ctx, fd, IPC_CONNECTION_TYPE_SWITCHED); +} + +// add an arbitrary file descriptor to read +struct ipc_error ipc_add_fd (struct ipc_ctx *ctx, int fd) +{ + return ipc_add_fd_ (ctx, fd, IPC_CONNECTION_TYPE_EXTERNAL); +} + // remove a connection from its file descriptor struct ipc_error ipc_del_fd (struct ipc_ctx *ctx, int fd) { @@ -306,7 +317,6 @@ struct ipc_error ipc_del_fd (struct ipc_ctx *ctx, int fd) IPC_RETURN_ERROR (IPC_ERROR_DEL_FD__CANNOT_FIND_CLIENT); } - struct ipc_error handle_writing_message (struct ipc_event *event, struct ipc_ctx *ctx, uint32_t index) { int txfd = ctx->pollfd[index].fd; @@ -335,19 +345,6 @@ struct ipc_error handle_new_message (struct ipc_event *event, struct ipc_ctx *ct { SECURE_DECLARATION (struct ipc_error, ret); - { /* First test: should the message be switched? */ - ret = fd_switching (event, ctx, index); - if (ret.error_code != IPC_ERROR_FD_SWITCHING__NO_FD_RECORD) { - return ret; - } - } - - // No treatment of the socket if external socket: the libipc user should handle it himself. - if (ctx->cinfos[index].type == IPC_CONNECTION_TYPE_EXTERNAL) { - IPC_EVENT_SET (event, IPC_EVENT_TYPE_EXTRA_SOCKET, index, ctx->pollfd[index].fd, NULL); - IPC_RETURN_NO_ERROR; - } - // Listen to what they have to say (disconnection or message) // then add a client to `event`, the ipc_event structure. struct ipc_message *m = NULL; @@ -389,6 +386,20 @@ struct ipc_error handle_new_message (struct ipc_event *event, struct ipc_ctx *ct IPC_RETURN_NO_ERROR; } +struct ipc_error +handle_writing_switched_message (struct ipc_event *event, struct ipc_ctx *ctx, uint32_t index) +{ + return fd_switching_write (event, ctx, index); +} + +struct ipc_error +handle_switched_message(struct ipc_event *event, struct ipc_ctx *ctx, uint32_t index) +{ + printf ("handling message comming from a switched fd\n"); + return fd_switching_read (event, ctx, index); +} + + /* timer is in ms */ struct ipc_error ipc_wait_event (struct ipc_ctx *ctx, struct ipc_event *event, int *timer) { @@ -448,12 +459,29 @@ struct ipc_error ipc_wait_event (struct ipc_ctx *ctx, struct ipc_event *event, i return ipc_accept_add (event, ctx, i); } + // fd is switched: using callbacks for IO operations. + if (ctx->cinfos[i].type == IPC_CONNECTION_TYPE_SWITCHED) { + return handle_switched_message (event, ctx, i); + } + + // 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; + } + return handle_new_message (event, ctx, i); } // Something can be sent. if (ctx->pollfd[i].revents & POLLOUT) { ctx->pollfd[i].events &= ~POLLOUT; + + // 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); + } + return handle_writing_message (event, ctx, i); } diff --git a/src/ipc.h b/src/ipc.h index b00bae7..09d384d 100644 --- a/src/ipc.h +++ b/src/ipc.h @@ -216,8 +216,18 @@ struct ipc_error { // with the error_message string in the ipc_error structure. const char *ipc_errors_get (enum ipc_error_code e); + +enum ipc_connection_type { + IPC_CONNECTION_TYPE_IPC = 0 + , IPC_CONNECTION_TYPE_EXTERNAL = 1 + /** Messages received = new connections. */ + , IPC_CONNECTION_TYPE_SERVER = 2 + /** IO operations should go through registered callbacks. */ + , IPC_CONNECTION_TYPE_SWITCHED = 3 +}; + struct ipc_connection_info { - char type; // server, client, arbitrary fd + enum ipc_connection_type type; char *spath; // max size: PATH_MAX }; @@ -294,12 +304,6 @@ struct ipc_event { pevent->m = message_; \ }; -enum ipc_connection_types { - IPC_CONNECTION_TYPE_IPC = 0 - , IPC_CONNECTION_TYPE_EXTERNAL = 1 - , IPC_CONNECTION_TYPE_SERVER = 2 /** Messages received = new connections. */ -}; - #define IPC_EVENT_CLEAN(pevent) {\ pevent->type = IPC_EVENT_TYPE_NOT_SET;\ if (pevent->m != NULL) {\ @@ -326,7 +330,8 @@ void ipc_ctx_free (struct ipc_ctx *ctx); struct ipc_error ipc_read (const struct ipc_ctx *, uint32_t index, struct ipc_message *m); struct ipc_error ipc_write (struct ipc_ctx *, const struct ipc_message *m); -struct ipc_error fd_switching (struct ipc_event *event, struct ipc_ctx *ctx, int index); +struct ipc_error fd_switching_read (struct ipc_event *event, struct ipc_ctx *ctx, int index); +struct ipc_error fd_switching_write (struct ipc_event *event, struct ipc_ctx *ctx, int index); // store and remove only pointers on allocated structures struct ipc_error ipc_add (struct ipc_ctx *, struct ipc_connection_info *, struct pollfd *); @@ -334,6 +339,8 @@ struct ipc_error ipc_del (struct ipc_ctx *, uint32_t index); // add an arbitrary file descriptor to read struct ipc_error ipc_add_fd (struct ipc_ctx *ctx, int fd); +// add a switched file descriptor to read +struct ipc_error ipc_add_fd_switched (struct ipc_ctx *ctx, int fd); struct ipc_error ipc_del_fd (struct ipc_ctx *ctx, int fd); /*** diff --git a/src/network.c b/src/network.c index 7f5326b..a35bbfe 100644 --- a/src/network.c +++ b/src/network.c @@ -226,11 +226,11 @@ default_cb_out(int fd, struct ipc_message *m) } /** - * switching_messages allows to send messages from a fd to another. + * fd_switching_read allows to read a message from a switched fd. */ -struct ipc_error fd_switching (struct ipc_event *event, struct ipc_ctx *ctx, int index) +struct ipc_error fd_switching_read (struct ipc_event *event, struct ipc_ctx *ctx, int index) { - printf ("fd_switching\n"); + printf ("fd_switching_read\n"); // If the socket is associated to another one for ipcd: // read and write automatically and provide a new IPC_EVENT_TYPE indicating the switch. @@ -299,3 +299,91 @@ struct ipc_error fd_switching (struct ipc_event *event, struct ipc_ctx *ctx, int // 3. return IPC_ERROR_CLOSED_RECIPIENT IPC_RETURN_ERROR (IPC_ERROR_CLOSED_RECIPIENT); } + +/** + * fd_switching_write allows to read a message from a switched fd. + */ +struct ipc_error fd_switching_write (struct ipc_event *event, struct ipc_ctx *ctx, int index) +{ + printf ("fd_switching_write\n"); + + // If the socket is associated to another one for ipcd: + // read and write automatically and provide a new IPC_EVENT_TYPE indicating the switch. + T_R ((ctx->switchdb.size == 0), IPC_ERROR_FD_SWITCHING__NO_FD_RECORD); + + int output_fd = ctx->pollfd[index].fd; + struct ipc_switching sw; + struct ipc_message *m = NULL; + size_t i; + + // search for the next message to send for output_fd fd. + for (i = 0; ctx->tx.size ; i++) { + if (ctx->tx.messages[i].fd == output_fd) { + m = &ctx->tx.messages[i]; + break; + } + } + + // In case there is no message for the fd: the error will be catched. + + enum ipccb r; + int is_valid = 0; + + is_valid = ipc_switching_get_ (&ctx->switchdb, output_fd, &sw); + + T_R ((is_valid == -1), IPC_ERROR_FD_SWITCHING__NO_FD_RECORD); + + if (sw.orig == output_fd) { + if (sw.orig_in == NULL) { + r = default_cb_out (output_fd, &m); + } + else { + r = (*sw.orig_out)(output_fd, &m); + } + } + else { + if (sw.dest_in == NULL) { + r = default_cb_out (output_fd, &m); + } + else { + r = (*sw.dest_out)(output_fd, &m); + } + } + + // Freeing the message structure. + ipc_message_empty (m); + // Removing the message from the context. + ipc_messages_del (&ctx->tx, i); // remove the message indexed by i + + // Message reception OK: reading the message and put it in the list of messages to send. + if (r == IPC_CB_NO_ERROR) { + // 1. set event IPC_EVENT_TYPE_SWITCH, inform ipcd of a successful reception. + IPC_EVENT_SET (event, IPC_EVENT_TYPE_TX, index, output_fd, NULL); + // 2. IPC_RETURN_NO_ERROR + IPC_RETURN_NO_ERROR; + } + + /** + * NOTE: In any other case, the fd is, or should be closed. + */ + + // 1. close and remove both fd from switchdb + int delfd = ipc_switching_del (&ctx->switchdb, output_fd); + if (delfd >= 0) { + close (delfd); + ipc_del_fd (ctx, delfd); + } + close (output_fd); + ipc_del_fd (ctx, output_fd); + + // 2. set event (either error or disconnection) + if (r == IPC_CB_FD_CLOSING) { + IPC_EVENT_SET (event, IPC_EVENT_TYPE_DISCONNECTION, index, output_fd, NULL); + } + else { + IPC_EVENT_SET (event, IPC_EVENT_TYPE_ERROR, index, output_fd, NULL); + } + + // 3. return IPC_ERROR_CLOSED_RECIPIENT + IPC_RETURN_ERROR (IPC_ERROR_CLOSED_RECIPIENT); +} diff --git a/src/print.c b/src/print.c index 6052073..55c9145 100644 --- a/src/print.c +++ b/src/print.c @@ -19,6 +19,10 @@ void ipc_ctx_print (struct ipc_ctx *ctx) printf ("- external\n"); break; } + case IPC_CONNECTION_TYPE_SWITCHED: { + printf ("- switched\n"); + break; + } } }