#ifndef __IPC_H__ #define __IPC_H__ #include #include #include #include #include #include // error numbers #include #include /*** * global defaults **/ #define RUNDIR "/run/ipc/" #define PATH_MAX 4096 #define IPC_HEADER_SIZE 6 // #define __IPC_BASE_SIZE 500000 // 500 KB #define __IPC_BASE_SIZE 2000000 // 2 MB, plenty enough space for messages #define IPC_MAX_MESSAGE_SIZE __IPC_BASE_SIZE-IPC_HEADER_SIZE #define IPC_VERSION 4 #if ! defined(IPC_WITHOUT_ERRORS) && ! defined(IPC_WITH_ERRORS) #define IPC_WITH_ERRORS 2 #endif #define IPC_WITH_UNIX_SOCKETS #ifdef IPC_WITH_UNIX_SOCKETS #include "usocket.h" #endif /*** * structures and enumerations **/ enum msg_types { MSG_TYPE_SERVER_CLOSE = 0 , MSG_TYPE_ERR = 1 , MSG_TYPE_DATA = 2 , MSG_TYPE_NETWORK_LOOKUP = 3 }; /** * Event types. * In the main event loop, servers and clients can receive connections, * disconnections, errors or messages from their pairs. They also can * set a timer so the loop will allow a periodic routine (sending ping * messages for websockets, for instance). * ** * * A few other events can occur. * * Extra socket * The main loop waiting for an event can be used as an unique entry * point for socket management. libipc users can register sockets via * ipc_add_fd allowing them to trigger an event, so events unrelated * to libipc are managed the same way. * Switch * libipc can be used to create protocol-related programs, such as a * websocket proxy allowing libipc services to be accessible online. * To help those programs (with TCP-complient sockets), two sockets * can be bound together, each message coming from one end will be * automatically transfered to the other socket and a Switch event * will be triggered. * Look Up * When a client establishes a connection to a service, it asks the * ipc daemon (ipcd) to locate the service and establish a connection * to it. This is a lookup. */ enum ipc_event_type { IPC_EVENT_TYPE_NOT_SET = 0 , IPC_EVENT_TYPE_ERROR = 1 , IPC_EVENT_TYPE_EXTRA_SOCKET = 2 // Message received from a non IPC socket. , IPC_EVENT_TYPE_SWITCH = 3 // Message to send to a corresponding fd. , IPC_EVENT_TYPE_CONNECTION = 4 // New user. , IPC_EVENT_TYPE_DISCONNECTION = 5 // User disconnected. , IPC_EVENT_TYPE_MESSAGE = 6 // New message. , IPC_EVENT_TYPE_LOOKUP = 7 // Client asking for a service through ipcd. , IPC_EVENT_TYPE_TIMER = 8 // Timeout in the poll(2) function. , IPC_EVENT_TYPE_TX = 9 // Message sent. }; // For IO callbacks (switching). enum ipccb { IPC_CB_NO_ERROR = 0 // No error. A message was generated. , IPC_CB_FD_CLOSING = 1 // The fd is closing. , IPC_CB_FD_ERROR = 2 // Generic error. , IPC_CB_PARSING_ERROR = 3 // The message was read but with errors. , IPC_CB_IGNORE = 4 // The message should be ignored (protocol specific). }; /** * Error codes. * libipc tend to use unique error codes in the whole library, allowing easier debugging. */ enum ipc_error_code { IPC_ERROR_NONE = 0 , IPC_ERROR_HANDLE_MESSAGE__NOT_ENOUGH_MEMORY = 3 , IPC_ERROR_CLOSED_RECIPIENT = 4 , IPC_ERROR_SERVICE_PATH__NO_PATH = 5 , IPC_ERROR_SERVICE_PATH__NO_SERVICE_NAME = 6 , IPC_ERROR_SERVER_INIT__NO_ENVIRONMENT_PARAM = 7 , IPC_ERROR_SERVER_INIT__NO_SERVICE_PARAM = 8 , IPC_ERROR_SERVER_INIT__NO_SERVER_NAME_PARAM = 9 , IPC_ERROR_SERVER_INIT__MALLOC = 10 , IPC_ERROR_WRITE__NO_MESSAGE_PARAM = 11 , IPC_ERROR_WRITE_FD__NOT_ENOUGH_DATA = 12 , IPC_ERROR_READ__NO_MESSAGE_PARAM = 13 , IPC_ERROR_CONNECTION__NO_SERVICE_NAME = 15 , IPC_ERROR_CONNECTION__NO_ENVIRONMENT_PARAM = 16 , IPC_ERROR_ACCEPT__NO_SERVICE_PARAM = 18 , IPC_ERROR_ACCEPT__NO_CLIENT_PARAM = 19 , IPC_ERROR_HANDLE_NEW_CONNECTION__NO_CINFO_PARAM = 20 , IPC_ERROR_HANDLE_NEW_CONNECTION__NO_CINFOS_PARAM = 21 , IPC_ERROR_WAIT_EVENT__SELECT = 22 , IPC_ERROR_WAIT_EVENT__NO_CLIENTS_PARAM = 23 , IPC_ERROR_WAIT_EVENT__NO_EVENT_PARAM = 24 , IPC_ERROR_HANDLE_NEW_CONNECTION__MALLOC = 25 , IPC_ERROR_ADD__EMPTY_LIST = 26 , IPC_ERROR_ADD__NO_PARAM_CLIENTS = 27 , IPC_ERROR_ADD__NO_PARAM_CLIENT = 28 , IPC_ERROR_ADD__MALLOC = 29 , IPC_ERROR_ADD_FD__NO_PARAM_CINFOS = 30 , IPC_ERROR_ADD_FD__NOT_ENOUGH_MEMORY = 31 , IPC_ERROR_DEL_FD__NO_PARAM_CINFOS = 32 , IPC_ERROR_DEL_FD__EMPTIED_LIST = 33 , IPC_ERROR_DEL_FD__EMPTY_LIST = 34 , IPC_ERROR_DEL_FD__CANNOT_FIND_CLIENT = 35 , IPC_ERROR_CONTACT_IPCD__NO_SERVICE_NAME_PARAM = 36 , IPC_ERROR_CONTACT_IPCD__NO_SERVER_PARAM = 37 , IPC_ERROR_DEL__EMPTY_LIST = 38 , IPC_ERROR_DEL__EMPTIED_LIST = 39 , IPC_ERROR_DEL__CANNOT_FIND_CLIENT = 40 , IPC_ERROR_DEL__NO_CLIENTS_PARAM = 41 , IPC_ERROR_DEL__NO_CLIENT_PARAM = 42 , IPC_ERROR_USOCK_SEND = 43 , IPC_ERROR_USOCK_CONNECT__SOCKET = 44 , IPC_ERROR_USOCK_CONNECT__WRONG_FILE_DESCRIPTOR = 45 , IPC_ERROR_USOCK_CONNECT__EMPTY_PATH = 46 , IPC_ERROR_USOCK_CONNECT__CONNECT = 47 , IPC_ERROR_USOCK_CLOSE = 48 , IPC_ERROR_USOCK_REMOVE__UNLINK = 49 , IPC_ERROR_USOCK_REMOVE__NO_FILE = 50 , IPC_ERROR_USOCK_INIT__EMPTY_FILE_DESCRIPTOR = 51 , IPC_ERROR_USOCK_INIT__WRONG_FILE_DESCRIPTOR = 52 , IPC_ERROR_USOCK_INIT__EMPTY_PATH = 53 , IPC_ERROR_USOCK_INIT__BIND = 54 , IPC_ERROR_USOCK_INIT__LISTEN = 55 , IPC_ERROR_USOCK_ACCEPT__PATH_FILE_DESCRIPTOR = 56 , IPC_ERROR_USOCK_ACCEPT = 57 , IPC_ERROR_USOCK_RECV__NO_BUFFER = 58 , IPC_ERROR_USOCK_RECV__NO_LENGTH = 59 , IPC_ERROR_USOCK_RECV = 60 , IPC_ERROR_USOCK_RECV__UNRECOGNIZED_ERROR = 61 , IPC_ERROR_USOCK_RECV__HEAP_ALLOCATION = 62 , IPC_ERROR_USOCK_RECV__MESSAGE_SIZE = 63 , IPC_ERROR_RECEIVE_FD__NO_PARAM_FD = 64 , IPC_ERROR_RECEIVE_FD__RECVMSG = 65 , IPC_ERROR_PROVIDE_FD__SENDMSG = 66 , IPC_ERROR_MESSAGE_FORMAT_WRITE__MESSAGE_LENGTH = 67 , IPC_ERROR_MESSAGE_FORMAT__MESSAGE_SIZE = 68 , IPC_ERROR_MESSAGE_FORMAT__NO_MESSAGE_PARAM = 69 , IPC_ERROR_MESSAGE_FORMAT__INCONSISTENT_PARAMS = 70 , IPC_ERROR_MESSAGE_FORMAT__HEAP_ALLOCATION = 71 , IPC_ERROR_MESSAGE_FORMAT_WRITE__EMPTY_MESSAGE = 72 , IPC_ERROR_MESSAGE_FORMAT_WRITE__EMPTY_MSIZE = 73 , IPC_ERROR_MESSAGE_FORMAT_WRITE__EMPTY_BUFFER = 74 , IPC_ERROR_MESSAGE_FORMAT_READ__EMPTY_MESSAGE = 75 , IPC_ERROR_MESSAGE_FORMAT_READ__EMPTY_BUFFER = 76 , IPC_ERROR_MESSAGE_FORMAT_READ__PARAM_MESSAGE_SIZE = 77 , IPC_ERROR_MESSAGE_FORMAT_READ__READ_MESSAGE_SIZE = 78 , IPC_ERROR_MESSAGE_FORMAT_READ__MESSAGE_TOO_LONG = 79 , IPC_ERROR_MESSAGE_EMPTY__EMPTY_MESSAGE_LIST = 80 , IPC_ERROR_MKDIR__CANNOT_CREATE_DIR = 81 , IPC_ERROR_MKDIR__NAME_TOO_LONG = 82 , IPC_ERROR_DIR_SETUP__NOT_A_DIRECTORY = 83 , IPC_ERROR_DIR_SETUP__DIRECTORY_NOT_WRITABLE = 84 , IPC_ERROR_DIRECTORY_SETUP__PATH_PARAM = 85 , IPC_ERROR_SERVER_INIT__NOT_ENOUGH_MEMORY = 86 , IPC_ERROR_CONNECTION__NOT_ENOUGH_MEMORY = 87 , IPC_ERROR_CTX_INIT__NO_CONTEXT_PARAM = 88 , IPC_ERROR_CTX_INIT__CONTEXT_ALREADY_INIT = 89 , IPC_ERROR_ADD__MALLOC_POLLFD = 90 , IPC_ERROR_ADD_MESSAGE_TO_SEND__EMPTY_LIST = 91 , IPC_ERROR_ADD_MESSAGE_TO_SEND__MALLOC = 92 , IPC_ERROR_ADD_MESSAGE_TO_SEND__NO_PARAM_MESSAGE = 93 , IPC_ERROR_ADD_MESSAGE_TO_SEND__NO_PARAM_MESSAGES = 94 , IPC_ERROR_CONNECTION__NO_CTX = 95 , IPC_ERROR_CTX_INIT__MALLOC_CTX = 96 , IPC_ERROR_CTX_INIT__MALLOC_POLLFD = 97 , IPC_ERROR_CONTACT_IPCD__NO_FD_PARAM = 98 , IPC_ERROR_HANDLE_NEW_CONNECTION__INCONSISTENT_INDEX = 99 , IPC_ERROR_DEL_MESSAGE_TO_SEND__NO_PARAM_MESSAGES = 100 , IPC_ERROR_MESSAGE_DEL__INDEX_ERROR = 101 , IPC_ERROR_MESSAGE_DEL__EMPTY_LIST = 102 , IPC_ERROR_ADD__NO_PARAM_POLLFD = 103 , IPC_ERROR_WRITE__FD_NOT_FOUND = 104 , IPC_ERROR_ADD__NOT_ENOUGH_MEMORY = 105 , IPC_ERROR_WAIT_EVENT__POLL = 106 , IPC_ERROR_FD_SWITCHING__NO_FD_RECORD = 107 , IPC_ERROR_CLOSE_ALL__NO_CTX_PARAM = 108 , IPC_ERROR_CLOSE__NO_CTX_PARAM = 109 }; struct ipc_error { enum ipc_error_code error_code; char error_message[BUFSIZ]; }; // get explanation about an error // This only returns the generic error based on its code. // Library's users have a more meaningful insight on the 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 { enum ipc_connection_type type; short int more_to_read; char *spath; // max size: PATH_MAX }; struct ipc_message { char type; // Internal message type. char user_type; // User-defined message type. int fd; // File descriptor concerned about this message. uint32_t length; // Payload length. char *payload; }; struct ipc_messages { struct ipc_message *messages; size_t size; }; struct ipc_switching { int orig; int dest; enum ipccb (*orig_in) (int origin_fd, struct ipc_message *m, short int *more_to_read); enum ipccb (*orig_out) (int origin_fd, struct ipc_message *m); enum ipccb (*dest_in) (int origin_fd, struct ipc_message *m, short int *more_to_read); enum ipccb (*dest_out) (int origin_fd, struct ipc_message *m); }; struct ipc_switchings { struct ipc_switching *collection; size_t size; }; void ipc_message_copy (struct ipc_message *m , uint32_t fd , uint8_t type , uint8_t utype , char *payload , uint32_t paylen); struct ipc_error ipc_messages_del (struct ipc_messages *messages, uint32_t index); /** * Context of the whole networking state. */ struct ipc_ctx { /** * Keep track of connections. */ struct ipc_connection_info *cinfos; /** * List of "pollfd" structures within cinfos, so we can pass it to poll(2). */ struct pollfd *pollfd; /** * Size of the connection list. */ size_t size; /** * List of messages to send, once the fd are available. */ struct ipc_messages tx; /** * Relations between fd. */ struct ipc_switchings switchdb; }; struct ipc_event { enum ipc_event_type type; uint32_t index; int origin; void *m; // message pointer }; /*** * ipc event macros **/ #define IPC_EVENT_SET(pevent,type_,index_, origin_fd_,message_) {\ pevent->type = type_; \ pevent->index = index_; \ pevent->origin = origin_fd_; \ pevent->m = message_; \ }; #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);\ pevent->m = NULL;\ }\ }; /** * main public functions **/ struct ipc_error ipc_wait_event (struct ipc_ctx *, struct ipc_event *, int *timer); struct ipc_error ipc_server_init (struct ipc_ctx *ctx, const char *sname); struct ipc_error ipc_connection (struct ipc_ctx *ctx, const char *sname, int *fd); struct ipc_error ipc_connection_switched (struct ipc_ctx *ctx, const char *sname, int clientfd, int *serverfd); struct ipc_error ipc_close (struct ipc_ctx *ctx, uint32_t index); struct ipc_error ipc_close_all (struct ipc_ctx *ctx); 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_read_fd (int32_t fd, struct ipc_message *m); struct ipc_error ipc_write (struct ipc_ctx *, const struct ipc_message *m); 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 *); 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); /*** * message functions **/ uint32_t ipc_message_raw_serialize (char *buffer, char type, char user_type, char *message, uint32_t message_size); // used to create msg structure from buffer struct ipc_error ipc_message_format_read (struct ipc_message *m, const char *buf, size_t msize); // used to create buffer from msg structure struct ipc_error ipc_message_format_write (const struct ipc_message *m, char **buf, size_t * msize); struct ipc_error ipc_message_format (struct ipc_message *m, char type, char utype, const char *payload, size_t length); struct ipc_error ipc_message_format_data (struct ipc_message *m, char utype, const char *payload, size_t length); struct ipc_error ipc_message_format_server_close (struct ipc_message *m); struct ipc_error ipc_message_empty (struct ipc_message *m); struct ipc_error ipc_messages_add (struct ipc_messages *, const struct ipc_message *); 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\n", f, m); } break; /*** * non public functions **/ struct ipc_error ipc_write_fd (int fd, const struct ipc_message *m); struct ipc_error ipc_ctx_init (struct ipc_ctx **); struct ipc_error ipc_ctx_new_alloc (struct ipc_ctx *ctx); struct ipc_error service_path (char *path, const char *sname); struct ipc_error handle_writing_message (struct ipc_event *event, struct ipc_ctx *ctx, uint32_t index); void ipc_ctx_print (struct ipc_ctx *ctx); // Last parameter is the index for the server fd in the context structure. struct ipc_error ipc_accept_add (struct ipc_event *event, struct ipc_ctx *ctx, uint32_t index); struct ipc_error ipc_contact_ipcd (int *pfd, const char *sname); struct ipc_error service_path (char *path, const char *sname); /*** * ipcd enumerations, structures and functions **/ 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); void ipc_switching_free (struct ipc_switchings *is); void ipc_switching_callbacks_ (struct ipc_ctx *ctx, int fd , enum ipccb (*cb_in )(int fd, struct ipc_message *m, short int *more_to_read)); void ipc_switching_callbacks ( struct ipc_ctx *ctx , int fd , enum ipccb (*cb_in )(int fd, struct ipc_message *m, short int *more_to_read) , enum ipccb (*cb_out)(int fd, struct ipc_message *m)); int ipc_ctx_fd_type (struct ipc_ctx *ctx, int fd, enum ipc_connection_type type); void ipc_switching_print (struct ipc_switchings *is); struct ipc_error ipc_receive_fd (int sock, int *fd); struct ipc_error ipc_provide_fd (int sock, int fd); /*** * grooming macros **/ #define SECURE_DECLARATION(t,v) t v; memset(&v,0,sizeof(t)); #define SECURE_BUFFER_DECLARATION(type,name,size) type name[size]; memset(&name, 0, sizeof(type) * size); #define SECURE_BUFFER_HEAP_ALLOCATION(p,len,instr,r)\ { p = malloc (len); if (p == NULL) { instr; r; } ; memset(p, 0, len); } #define SECURE_BUFFER_HEAP_ALLOCATION_R(p,len,instr,r) SECURE_BUFFER_HEAP_ALLOCATION(p,len,instr, IPC_RETURN_ERROR(r); ) #define SECURE_BUFFER_HEAP_ALLOCATION_R_NULL(p,len,instr) SECURE_BUFFER_HEAP_ALLOCATION(p,len,instr, return NULL; ) #define SECURE_BUFFER_HEAP_ALLOCATION_Q(p,len,instr,r) SECURE_BUFFER_HEAP_ALLOCATION(p,len,instr, exit(r)) // Test macros, requiring the variable `enum ipc_error_code ret` // one macro to rule them all! // 1. function to test // 2. Test IPC error based (test itself) // 3. Instructions to exec on failure // 4. Return something #define TEST_IPC_T_P_I_R(function_to_test, test, instr, r) \ {\ struct ipc_error ret = function_to_test;\ if (test) {\ instr;\ r;\ }\ } // R = return r param // RR = return "ret" variable // RV = return void // Q = quit // I = additionnal instructions before returning on error #define TEST_IPC_T_I_P_Q(f,t,instr,err,r) TEST_IPC_T_P_I_R(f, t, instr, exit(r)) #define TEST_IPC_T_I_RR(f,t,instr) TEST_IPC_T_P_I_R(f, t, instr, return ret) #define TEST_IPC_I_RR(f,instr) TEST_IPC_T_P_I_R(f, ret.error_code != IPC_ERROR_NONE, instr, return ret) // Tests macros, do not require `enum ipc_error_code ret` variable // test => return error code #define T_R(t,r) if t { IPC_RETURN_ERROR(r); } #define T_R_NULL(t) if t { return NULL; } #define T_R_NOTHING(t) if t { return ; } // test => perror then return (for system functions) #define T_PERROR_R(t,m,r) if t { perror(m); return (r); } // test => perror then exit (for system functions) #define T_PERROR_Q(t,m,r) if t { perror(m); exit(r); } #define T_PERROR_RIPC(t,m,r) if t { perror(m); IPC_RETURN_ERROR(r); } #define TEST_IPC_QUIT_ON_ERROR(function_to_test,ec) {\ struct ipc_error ret = function_to_test;\ if (ret.error_code != IPC_ERROR_NONE) {\ fprintf(stderr, "%s\n", ret.error_message);\ exit(ec);\ }\ } #define TEST_IPC_RETURN_ON_ERROR_FREE(function_to_test, buffer_to_free) {\ struct ipc_error ret = function_to_test; \ if (ret.error_code != IPC_ERROR_NONE) {\ if (buffer_to_free != NULL) {\ free(buffer_to_free); \ }\ return ret;\ }\ } #define TEST_IPC_RETURN_ON_ERROR(function_to_test) {\ struct ipc_error ret = function_to_test;\ if (ret.error_code != IPC_ERROR_NONE) {\ return ret;\ }\ } #define TEST_IPC_RETURN_ON_ERROR_FREE(function_to_test, buffer_to_free) {\ struct ipc_error ret = function_to_test; \ if (ret.error_code != IPC_ERROR_NONE) {\ if (buffer_to_free != NULL) {\ free(buffer_to_free); \ }\ return ret;\ }\ } // formatted version of TEST_IPC_RR #define TEST_IPC_RR_F(function_to_test, format, ...) {\ struct ipc_error ret = function_to_test;\ if (ret.error_code != IPC_ERROR_NONE) { \ error_message_format (ret.error_message + strlen(ret.error_message) \ , "blocking error" \ , ":" __FILE__ ":" format \ , ##__VA_ARGS__ ); \ return ret;\ }\ } // TODO: ret already contains error message, append the error_message // TODO: currently, error message is not what it should be #define TEST_IPC_RR(function_to_test, err_message) \ TEST_IPC_RR_F(function_to_test, "%s", err_message) // same as TEST_IPC_RR but do not return // Also: print the error right away. #define TEST_IPC_P(function_to_test, err_message) {\ struct ipc_error ret = function_to_test;\ if (ret.error_code != IPC_ERROR_NONE) {\ error_message_format (ret.error_message + strlen(ret.error_message) \ , "non blocking error" \ , ":" __FILE__ "%s:%s" \ , err_message );\ \ fprintf (stderr, "%s\n", ret.error_message);\ }\ } #define IPC_RETURN_NO_ERROR { \ SECURE_DECLARATION (struct ipc_error, ret); \ ret.error_code = IPC_ERROR_NONE; \ return ret; \ } #define IPC_FILL_DEFAULT_ERROR_MESSAGE(error_message_ptr,error_code) { \ const char *estr = ipc_errors_get (error_code); \ snprintf(error_message_ptr, BUFSIZ, "%s", estr); \ } #define IPC_RETURN_ERROR(ec_) \ SECURE_DECLARATION(struct ipc_error, ret);\ IPC_FILL_DEFAULT_ERROR_MESSAGE(ret.error_message, ec_);\ ret.error_code = ec_;\ return ret; #define IPC_RETURN_ERROR_FORMAT(v,format,...) { \ SECURE_DECLARATION (struct ipc_error, ret); \ ret.error_code = v; \ error_message_format (ret.error_message \ , "blocking error" \ , format \ , ##__VA_ARGS__ ); \ return ret; \ } #define TEST_IPC_Q(f,e) {\ struct ipc_error ret = f; \ if (ret.error_code != IPC_ERROR_NONE) { \ fprintf(stderr, "%s", ret.error_message); \ exit(e); \ } \ } #define TEST_IPC_WAIT_EVENT_RR(f, r) { \ struct ipc_error ret = f; \ if (ret.error_code != IPC_ERROR_NONE && \ ret.error_code != IPC_ERROR_CLOSED_RECIPIENT) { \ return ret; \ } \ } #define TEST_IPC_WAIT_EVENT_Q(f, r) { \ struct ipc_error ret = f; \ if (ret.error_code != IPC_ERROR_NONE && \ ret.error_code != IPC_ERROR_CLOSED_RECIPIENT) { \ exit(r); \ } \ } #endif