/* See LICENSE file for copyright and license details. */ #include "libcoopgamma.h" #include #include #include #include #include #include #include #include #include #include #include #if !defined(COOPGAMMAD) # define COOPGAMMAD "coopgammad" #endif #if defined(__clang__) # pragma clang diagnostic ignored "-Wdocumentation" # pragma clang diagnostic ignored "-Wcovered-switch-default" # pragma clang diagnostic ignored "-Wcast-align" #endif #if defined(__GNUC__) # define NAME_OF_THE_PROCESS (argv0) extern const char *argv0; #else # define NAME_OF_THE_PROCESS ("libcoopgamma") #endif #if defined(__GNUC__) # define HIDDEN __attribute__((__visibility__("hidden"))) #else # define HIDDEN #endif #define SUBBUF\ (buf ? &buf[off] : NULL) #define NNSUBBUF\ (&buf[off]) #define MARSHAL_PROLOGUE\ char *restrict buf = vbuf;\ size_t off = 0; #define UNMARSHAL_PROLOGUE\ const char *restrict buf = vbuf;\ size_t off = 0; #define MARSHAL_EPILOGUE\ return off #define UNMARSHAL_EPILOGUE\ return *np = off, LIBCOOPGAMMA_SUCCESS #define marshal_prim(datum)\ do {\ char *buf__ = (buf);\ size_t off__ = ((off) += sizeof(datum)) - sizeof(datum);\ if (buf__)\ memcpy(&buf__[off__], &(datum), sizeof(datum));\ } while (0) #define unmarshal_prim(datum)\ do {\ const char *buf__ = (buf);\ size_t off__ = ((off) += sizeof(datum)) - sizeof(datum);\ memcpy(&(datum), &buf__[off__], sizeof(datum));\ } while (0) #define marshal_version(version)\ do {\ int version__ = (version);\ marshal_prim(version__);\ } while (0) #define unmarshal_version(version)\ do {\ int version__;\ unmarshal_prim(version__);\ if (version__ < (version))\ return LIBCOOPGAMMA_INCOMPATIBLE_DOWNGRADE;\ if (version__ > (version))\ return LIBCOOPGAMMA_INCOMPATIBLE_UPGRADE;\ } while (0) #define marshal_buffer(data, n)\ ((buf ? (memcpy(&buf[off], (data), (n)), 0) : 0), off += (n)) #define unmarshal_buffer(data, n)\ do {\ (data) = malloc((n));\ if (!(data))\ return LIBCOOPGAMMA_ERRNO_SET;\ memcpy((data), &buf[off], (n));\ off += (n);\ } while (0) #define marshal_string(datum)\ do {\ char nonnull__ = (char)!!(datum);\ marshal_prim(nonnull__);\ if (nonnull__)\ marshal_buffer((datum), strlen(datum) + 1U);\ } while (0) #define unmarshal_string(datum)\ do {\ char nonnull__;\ unmarshal_prim(nonnull__);\ if (nonnull__)\ unmarshal_buffer((datum), strlen(&buf[off]) + 1U);\ else\ (datum) = NULL;\ } while (0) #define copy_errno(ctx)\ (!errno ? NULL :\ ((ctx)->error.number = (uint64_t)errno,\ (ctx)->error.custom = 0,\ (ctx)->error.server_side = 0,\ free((ctx)->error.description),\ (ctx)->error.description = NULL)) #define SYNC_CALL(send_call, recv_call, fail_return)\ libcoopgamma_async_context_t async;\ if (send_call < 0) {\ reflush:\ if (errno != EINTR)\ return fail_return;\ if (libcoopgamma_flush(ctx) < 0)\ goto reflush;\ }\ resync:\ if (libcoopgamma_synchronise(ctx, &async, (size_t)1, &(size_t){0}) < 0) {\ if (errno != EINTR && errno)\ return fail_return;\ goto resync;\ }\ return recv_call #define INTEGRAL_DEPTHS\ case LIBCOOPGAMMA_UINT8:\ case LIBCOOPGAMMA_UINT16:\ case LIBCOOPGAMMA_UINT32:\ case LIBCOOPGAMMA_UINT64: /** * Send a message to the server and wait for response * * @param resp:char** Output parameter for the response, * will be NUL-terminated * @param ctx:libcoopgamma_context_t* The state of the library * @param payload:void* Data to append to the end of the message * @param payload_size:size_t Byte-size of `payload` * @param format:string-literal Message formatting string * @param ... Message formatting arguments * * On error, the macro goes to `fail`. */ #define SEND_MESSAGE(ctx, payload, payload_size, format, ...)\ do {\ ssize_t n__;\ char *msg__;\ snprintf(NULL, (size_t)0, format "%zn", __VA_ARGS__, &n__);\ msg__ = malloc((size_t)n__ + (payload_size) + (size_t)1);\ if (!msg__)\ goto fail;\ sprintf(msg__, format, __VA_ARGS__);\ if (payload)\ memcpy(&msg__[n__], (payload), (payload_size));\ if (libcoopgamma_send_message__((ctx), msg__, (size_t)n__ + (payload_size)) < 0)\ goto fail;\ } while (0) /** * Run coopgammad with -q or -qq and return the response * * SIGCHLD must not be ignored or blocked * * @param method The adjustment method, `NULL` for automatic * @param site The site, `NULL` for automatic * @param arg "-q" or "-qq", which shall be passed to coopgammad? * @return The output of coopgammad, `NULL` on error. This * will be NUL-terminated and will not contain any * other `NUL` bytes. */ HIDDEN char *libcoopgamma_query__(const char *restrict method, const char *restrict site, const char *restrict arg); /** * Send a message to the server and wait for response * * @param ctx The state of the library * @param msg The message to send * @param n The length of `msg` * @return Zero on success, -1 on error */ HIDDEN int libcoopgamma_send_message__(libcoopgamma_context_t *restrict ctx, char *msg, size_t n); /** * Check whether the server sent an error, if so copy it to `ctx` * * This function will also reports EBADMSG if the message ID * that the message is a response to does not match the request * information, or if it is missing * * @param ctx The state of the library, must be connected * @param async Information about the request * @return 1 if the server sent an error (even indicating success), * 0 on success, -1 on failure. Information about failure * is copied `ctx`. */ HIDDEN int libcoopgamma_check_error__(libcoopgamma_context_t *restrict ctx, libcoopgamma_async_context_t *restrict async); /** * Get the next header of the inbound message * * All headers must be read before the payload is read * * @param ctx The state of the library, must be connected * @return The next header line, can never be `NULL`, * the empty string marks the end of the headers. * This memory segment must not be freed. */ HIDDEN inline char * libcoopgamma_next_header__(libcoopgamma_context_t *restrict ctx) { char *rc = ctx->inbound + ctx->inbound_tail; ctx->inbound_tail += strlen(rc) + 1U; return rc; } /** * Get the payload of the inbound message * * Calling this function marks that the inbound message * has been fully ready. You must call this function * even if you do not expect a payload * * @param ctx The state of the library, must be connected * @param n Output parameter for the size of the payload * @return The payload (not NUL-terminated), `NULL` if * there is no payload. Failure is impossible. * This memory segment must not be freed. */ HIDDEN inline char * libcoopgamma_next_payload__(libcoopgamma_context_t *restrict ctx, size_t *n) { char *rc; ctx->have_all_headers = 0; if ((*n = ctx->length)) { rc = ctx->inbound + ctx->inbound_tail; ctx->inbound_tail += *n; ctx->length = 0; return rc; } return NULL; }