diff options
Diffstat (limited to '')
-rw-r--r-- | src/libmdsserver/util.c | 867 |
1 files changed, 435 insertions, 432 deletions
diff --git a/src/libmdsserver/util.c b/src/libmdsserver/util.c index cac30ad..09d996a 100644 --- a/src/libmdsserver/util.c +++ b/src/libmdsserver/util.c @@ -49,20 +49,21 @@ static char self_exe[PATH_MAX] = {0}; * @param str The client ID string * @return The client ID integer */ -uint64_t parse_client_id(const char* str) +uint64_t +parse_client_id(const char *str) { - char client_words[22]; - char* client_high; - char* client_low; - uint64_t client; - - strcpy(client_high = client_words, str); - client_low = rawmemchr(client_words, ':'); - *client_low++ = '\0'; - client = atou64(client_high) << 32; - client |= atou64(client_low); - - return client; + char client_words[22]; + char *client_high; + char *client_low; + uint64_t client; + + strcpy(client_high = client_words, str); + client_low = rawmemchr(client_words, ':'); + *client_low++ = '\0'; + client = atou64(client_high) << 32; + client |= atou64(client_low); + + return client; } @@ -72,12 +73,11 @@ uint64_t parse_client_id(const char* str) * @param var The environment variable's name * @return The environment variable's value, `NULL` if empty or not defined */ -char* getenv_nonempty(const char* var) +char * +getenv_nonempty(const char *var) { - char* rc = getenv(var); - if ((rc == NULL) || (*rc == '\0')) - return NULL; - return rc; + char *rc = getenv(var); + return (rc && *rc) ? rc : NULL; } @@ -97,20 +97,21 @@ char* getenv_nonempty(const char* var) * * @return Zero on success, -1 on error */ -int prepare_reexec(void) +int +prepare_reexec(void) { - ssize_t len; - len = readlink(SELF_EXE, self_exe, (sizeof(self_exe) / sizeof(char)) - 1); - fail_if (len < 0); - /* ‘readlink() does not append a null byte to buf.’ */ - self_exe[len] = '\0'; - /* Handle possible race condition: file was removed. */ - if (access(self_exe, F_OK) < 0) - if (!strcmp(self_exe + (len - 10), " (deleted)")) - self_exe[len - 10] = '\0'; - return 0; - fail: - return -1; + ssize_t len; + len = readlink(SELF_EXE, self_exe, (sizeof(self_exe) / sizeof(char)) - 1); + fail_if (len < 0); + /* ‘readlink() does not append a null byte to buf.’ */ + self_exe[len] = '\0'; + /* Handle possible race condition: file was removed. */ + if (access(self_exe, F_OK) < 0) + if (!strcmp(self_exe + (len - 10), " (deleted)")) + self_exe[len - 10] = '\0'; + return 0; +fail: + return -1; } @@ -125,30 +126,30 @@ int prepare_reexec(void) * @param argv The command line arguments * @param reexeced Whether the server has previously been re-exec:ed */ -void reexec_server(int argc, char** argv, int reexeced) +void +reexec_server(int argc, char **argv, int reexeced) { - char** reexec_args; - char** reexec_args_; - int i; - - /* Re-exec the server. */ - reexec_args = alloca(((size_t)argc + 2) * sizeof(char*)); - reexec_args_ = reexec_args; - if (reexeced == 0) - { - *reexec_args_++ = *argv; - fail_if (xstrdup(*reexec_args_, "--re-exec")); - for (i = 1; i < argc; i++) - reexec_args_[i] = argv[i]; - } - else /* Don't let the --re-exec:s accumulate. */ - *reexec_args_ = *argv; - for (i = 1; i < argc; i++) - reexec_args_[i] = argv[i]; - reexec_args_[argc] = NULL; - execv(self_exe[0] ? self_exe : argv[0], reexec_args); - fail: - return; + char **reexec_args; + char **reexec_args_; + int i; + + /* Re-exec the server. */ + reexec_args = alloca(((size_t)argc + 2) * sizeof(char*)); + reexec_args_ = reexec_args; + if (!reexeced) { + *reexec_args_++ = *argv; + fail_if (xstrdup(*reexec_args_, "--re-exec")); + for (i = 1; i < argc; i++) + reexec_args_[i] = argv[i]; + } else { /* Don't let the --re-exec:s accumulate. */ + *reexec_args_ = *argv; + } + for (i = 1; i < argc; i++) + reexec_args_[i] = argv[i]; + reexec_args_[argc] = NULL; + execv(self_exe[0] ? self_exe : argv[0], reexec_args); +fail: + return; } @@ -164,17 +165,18 @@ void reexec_server(int argc, char** argv, int reexeced) * @param function The function to run when the signal is caught * @return Zero on success, -1 on error */ -int xsigaction(int signo, void (*function)(int signo)) +int +xsigaction(int signo, void (*function)(int signo)) { - struct sigaction action; - sigset_t sigset; - - sigemptyset(&sigset); - action.sa_handler = function; - action.sa_mask = sigset; - action.sa_flags = 0; - - return sigaction(signo, &action, NULL); + struct sigaction action; + sigset_t sigset; + + sigemptyset(&sigset); + action.sa_handler = function; + action.sa_mask = sigset; + action.sa_flags = 0; + + return sigaction(signo, &action, NULL); } @@ -186,34 +188,32 @@ int xsigaction(int signo, void (*function)(int signo)) * @param length The length of the message * @return The number of bytes that have been sent (even on error) */ -size_t send_message(int socket, const char* message, size_t length) +size_t +send_message(int socket, const char *message, size_t length) { - size_t block_size = length; - size_t sent = 0; - ssize_t just_sent; - - errno = 0; - while (length > 0) - if ((just_sent = send(socket, message + sent, min(block_size, length), MSG_NOSIGNAL)) < 0) - { - if (errno == EPIPE) - errno = ECONNRESET; - if (errno == EMSGSIZE) - { - block_size >>= 1; - if (block_size == 0) - return sent; - } - else - return sent; - } - else - { - sent += (size_t)just_sent; - length -= (size_t)just_sent; - } - - return sent; + size_t block_size = length; + size_t sent = 0; + ssize_t just_sent; + + errno = 0; + while (length > 0) { + if ((just_sent = send(socket, message + sent, min(block_size, length), MSG_NOSIGNAL)) < 0) { + if (errno == EPIPE) + errno = ECONNRESET; + if (errno == EMSGSIZE) { + block_size >>= 1; + if (!block_size) + return sent; + } else { + return sent; + } + } else { + sent += (size_t)just_sent; + length -= (size_t)just_sent; + } + } + + return sent; } @@ -226,20 +226,21 @@ size_t send_message(int socket, const char* message, size_t length) * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ -int strict_atoi(const char* str, int* value, int min, int max) +int +strict_atoi(const char *str, int *value, int min, int max) { - long long int r; - char* endptr; - - r = strtoll(str, &endptr, 10); - if ((*str == '\0') || isspace(*str) || - (endptr - str != (ssize_t)strlen(str)) || - (r < (long long int)min) || - (r > (long long int)max)) - return -1; - - *value = (int)r; - return 0; + long long int r; + char *endptr; + + r = strtoll(str, &endptr, 10); + if (!*str || isspace(*str) || + endptr - str != (ssize_t)strlen(str) || + r < (long long int)min || + r > (long long int)max) + return -1; + + *value = (int)r; + return 0; } @@ -252,51 +253,50 @@ int strict_atoi(const char* str, int* value, int min, int max) * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ -int strict_atoj(const char* str, intmax_t* value, intmax_t min, intmax_t max) +int +strict_atoj(const char *str, intmax_t *value, intmax_t min, intmax_t max) { - static char minstr[3 * sizeof(intmax_t) + 2] = { '\0' }; - intmax_t r = 0; - char c; - int neg = 0; - - if (*str == '-') - { - if (*minstr == '\0') - sprintf(minstr, "%j", INTMAX_MIN); - if (!strcmp(str, minstr)) - { - r = INTMAX_MIN; - goto done; + static char minstr[3 * sizeof(intmax_t) + 2] = { '\0' }; + intmax_t r = 0; + char c; + int neg = 0; + + if (*str == '-') { + if (!*minstr) + sprintf(minstr, "%j", INTMAX_MIN); + if (!strcmp(str, minstr)) { + r = INTMAX_MIN; + goto done; + } + neg = 1, str++; } - neg = 1, str++; - } - - if (*str == '\0') - return -1; - - while ((c = *str)) - if (('0' <= c) && (c <= '9')) - { - if (r > INTMAX_MAX / 10) - return -1; - else if (r == INTMAX_MAX / 10) - if ((c & 15) > INTMAX_MAX % 10) - return -1; - r = r * 10 + (c & 15); - } - else - return -1; - - if (neg) - r = -r; - - done: - - if ((r < min) || (r > max)) - return -1; - - *value = r; - return 0; + + if (!*str) + return -1; + + while ((c = *str)) { + if ('0' <= c && c <= '9') { + if (r > INTMAX_MAX / 10) { + return -1; + } else if (r == INTMAX_MAX / 10) { + if ((c & 15) > INTMAX_MAX % 10) + return -1; + } + r = r * 10 + (c & 15); + } else { + return -1; + } + } + + if (neg) + r = -r; + +done: + if (r < min || r > max) + return -1; + + *value = r; + return 0; } @@ -316,50 +316,53 @@ int strict_atoj(const char* str, intmax_t* value, intmax_t min, intmax_t max) * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ -int strict_atouj(const char* str, uintmax_t* value, uintmax_t min, uintmax_t max) +int +strict_atouj(const char *str, uintmax_t *value, uintmax_t min, uintmax_t max) { - uintmax_t r = 0; - char c; - - if (*str == '\0') - return -1; - - while ((c = *str)) - if (('0' <= c) && (c <= '9')) - { - if (r > INTMAX_MAX / 10) - return -1; - else if (r == INTMAX_MAX / 10) - if ((c & 15) > INTMAX_MAX % 10) - return -1; - r = r * 10 + (c & 15); - } - else - return -1; - - if ((r < min) || (r > max)) - return -1; - - *value = r; - return 0; + uintmax_t r = 0; + char c; + + if (!*str) + return -1; + + while ((c = *str)) { + if ('0' <= c && c <= '9') { + if (r > INTMAX_MAX / 10) { + return -1; + } else if (r == INTMAX_MAX / 10) { + if ((c & 15) > INTMAX_MAX % 10) + return -1; + } + r = r * 10 + (c & 15); + } else { + return -1; + } + } + + if (r < min || r > max) + return -1; + + *value = r; + return 0; } #if defined(__GNUC__) # pragma GCC diagnostic pop #endif -#define __strict_y(Y, TYPE, PARA_TYPE, HYPER_Y, HYPER_TYPE) \ - int strict_##Y(const char* str, TYPE* value, PARA_TYPE min, PARA_TYPE max) \ - { \ - HYPER_TYPE intermediate_value; \ - if (strict_##HYPER_Y(str, &intermediate_value, (HYPER_TYPE)min, (HYPER_TYPE)max) < 0) \ - return -1; \ - return *value = (TYPE)intermediate_value, 0; \ - } +#define __strict_y(Y, TYPE, PARA_TYPE, HYPER_Y, HYPER_TYPE)\ + int\ + strict_##Y(const char *str, TYPE *value, PARA_TYPE min, PARA_TYPE max)\ + {\ + HYPER_TYPE intermediate_value;\ + if (strict_##HYPER_Y(str, &intermediate_value, (HYPER_TYPE)min, (HYPER_TYPE)max) < 0)\ + return -1;\ + return *value = (TYPE)intermediate_value, 0;\ + } -#define __strict_x(X, TYPE, HYPER_X, HYPER_TYPE) \ - __strict_y(X, TYPE, TYPE, HYPER_X, HYPER_TYPE) +#define __strict_x(X, TYPE, HYPER_X, HYPER_TYPE)\ + __strict_y(X, TYPE, TYPE, HYPER_X, HYPER_TYPE) /** @@ -577,20 +580,20 @@ __strict_x(atou64, uint64_t, atouj, uintmax_t) * @param length The length of the buffer * @return Zero on success, -1 on error */ -int full_write(int fd, const char* buffer, size_t length) +int +full_write(int fd, const char *buffer, size_t length) { - ssize_t wrote; - while (length > 0) - { - errno = 0; - wrote = write(fd, buffer, length); - fail_if (errno && (errno != EINTR)); - length -= (size_t)max(wrote, 0); - buffer += (size_t)max(wrote, 0); - } - return 0; - fail: - return -1; + ssize_t wrote; + while (length > 0) { + errno = 0; + wrote = write(fd, buffer, length); + fail_if (errno && errno != EINTR); + length -= (size_t)max(wrote, 0); + buffer += (size_t)max(wrote, 0); + } + return 0; +fail: + return -1; } @@ -601,44 +604,44 @@ int full_write(int fd, const char* buffer, size_t length) * @param length Output parameter for the length of the file, may be `NULL` * @return The content of the file, you will need to free it. `NULL` on error. */ -char* full_read(int fd, size_t* length) +char * +full_read(int fd, size_t *length) { - char* old_buf = NULL; - size_t buffer_size = 8 << 10; - size_t buffer_ptr = 0; - char* buffer; - ssize_t got; - int saved_errno; - - if (length != NULL) - *length = 0; - - /* Allocate buffer for data. */ - fail_if (xmalloc(buffer, buffer_size, char)); - - /* Read the file. */ - for (;;) - { - /* Grow buffer if it is too small. */ - if (buffer_size == buffer_ptr) - fail_if (xxrealloc(old_buf, buffer, buffer_size <<= 1, char)); - - /* Read from the file into the buffer. */ - got = read(fd, buffer + buffer_ptr, buffer_size - buffer_ptr); - fail_if ((got < 0) && (errno != EINTR)); - if (got == 0) - break; - buffer_ptr += (size_t)got; - } - - if (length != NULL) - *length = buffer_ptr; - return buffer; - fail: - saved_errno = errno; - free(old_buf); - free(buffer); - return errno = saved_errno, NULL; + char *old_buf = NULL; + size_t buffer_size = 8 << 10; + size_t buffer_ptr = 0; + char *buffer; + ssize_t got; + int saved_errno; + + if (length) + *length = 0; + + /* Allocate buffer for data. */ + fail_if (xmalloc(buffer, buffer_size, char)); + + /* Read the file. */ + for (;;) { + /* Grow buffer if it is too small. */ + if (buffer_size == buffer_ptr) + fail_if (xxrealloc(old_buf, buffer, buffer_size <<= 1, char)); + + /* Read from the file into the buffer. */ + got = read(fd, buffer + buffer_ptr, buffer_size - buffer_ptr); + fail_if (got < 0 && errno != EINTR); + if (!got) + break; + buffer_ptr += (size_t)got; + } + + if (length) + *length = buffer_ptr; + return buffer; +fail: + saved_errno = errno; + free(old_buf); + free(buffer); + return errno = saved_errno, NULL; } @@ -650,20 +653,20 @@ char* full_read(int fd, size_t* length) * @param length The length of the message * @return Zero on success, -1 on error */ -int full_send(int socket, const char* message, size_t length) +int +full_send(int socket, const char *message, size_t length) { - size_t sent; + size_t sent; - while (length > 0) - { - sent = send_message(socket, message, length); - fail_if ((sent < length) && (errno != EINTR)); - message += sent; - length -= sent; - } - return 0; - fail: - return -1; + while (length > 0) { + sent = send_message(socket, message, length); + fail_if (sent < length && errno != EINTR); + message += sent; + length -= sent; + } + return 0; +fail: + return -1; } @@ -677,17 +680,18 @@ int full_send(int socket, const char* message, size_t length) * @param needle_n The length of `needle` * @return Whether the `haystack` begins with `needle` */ -int startswith_n(const char* haystack, const char* needle, size_t haystack_n, size_t needle_n) +int +startswith_n(const char *haystack, const char *needle, size_t haystack_n, size_t needle_n) { - size_t i; - if (haystack_n < needle_n) - return 0; - - for (i = 0; i < needle_n; i++) - if (haystack[i] != needle[i]) - return 0; - - return 1; + size_t i; + if (haystack_n < needle_n) + return 0; + + for (i = 0; i < needle_n; i++) + if (haystack[i] != needle[i]) + return 0; + + return 1; } @@ -700,30 +704,30 @@ int startswith_n(const char* haystack, const char* needle, size_t haystack_n, si * @param options See description of `options` in the documentation for `waitpid` * @return See the documentation for `waitpid` */ -pid_t uninterruptable_waitpid(pid_t pid, int* restrict status, int options) +pid_t +uninterruptable_waitpid(pid_t pid, int *restrict status, int options) { - struct timespec time_start; - struct timespec time_intr; - int intr_count = 0, have_time; - pid_t rc; - - have_time = (monotone(&time_start) >= 0); - - rewait: - rc = waitpid(pid, status, options); - if (rc == (pid_t)-1) - { - fail_if (errno != EINTR); - if (have_time && (monotone(&time_intr) >= 0)) - if (time_start.tv_sec != time_intr.tv_sec) - intr_count = 0; - if (intr_count++ < 100) - goto rewait; - /* Don't let the CPU catch fire! */ - errno = EINTR; - } - fail: - return rc; + struct timespec time_start; + struct timespec time_intr; + int intr_count = 0, have_time; + pid_t rc; + + have_time = (monotone(&time_start) >= 0); + +rewait: + rc = waitpid(pid, status, options); + if (rc == (pid_t)-1) { + fail_if (errno != EINTR); + if (have_time && monotone(&time_intr) >= 0) + if (time_start.tv_sec != time_intr.tv_sec) + intr_count = 0; + if (intr_count++ < 100) + goto rewait; + /* Don't let the CPU catch fire! */ + errno = EINTR; + } +fail: + return rc; } @@ -734,72 +738,71 @@ pid_t uninterruptable_waitpid(pid_t pid, int* restrict status, int options) * @param allow_modified_nul Whether Modified UTF-8 is allowed, which allows a two-byte encoding for NUL * @return Zero if good, -1 on encoding error */ -int verify_utf8(const char* string, int allow_modified_nul) +int +verify_utf8(const char *string, int allow_modified_nul) { - static long BYTES_TO_MIN_BITS[] = {0, 0, 8, 12, 17, 22, 37}; - static long BYTES_TO_MAX_BITS[] = {0, 7, 11, 16, 21, 26, 31}; - long bytes = 0, read_bytes = 0, bits = 0, c, character; - - /* min bits max bits - 0....... 0 7 - 110..... 10...... 8 11 - 1110.... 10...... 10...... 12 16 - 11110... 10...... 10...... 10...... 17 21 - 111110.. 10...... 10...... 10...... 10...... 22 26 - 1111110. 10...... 10...... 10...... 10...... 10...... 27 31 - */ - - while ((c = (long)(*string++))) - if (read_bytes == 0) - { - /* First byte of the character. */ - - if ((c & 0x80) == 0x00) - /* Single-byte character. */ - continue; - - if ((c & 0xC0) == 0x80) - /* Single-byte character marked as multibyte, or - a non-first byte in a multibyte character. */ - return -1; - - /* Multibyte character. */ - while ((c & 0x80)) - bytes++, c <<= 1; - read_bytes = 1; - character = c & 0x7F; - if (bytes > 6) - /* 31-bit characters can be encoded with 6-bytes, - and UTF-8 does not cover higher code points. */ - return -1; - } - else - { - /* Not first byte of the character. */ - - if ((c & 0xC0) != 0x80) - /* Beginning of new character before a - multibyte character has ended. */ - return -1; - - character = (character << 6) | (c & 0x7F); - - if (++read_bytes < bytes) - /* Not at last byte yet. */ - continue; - - /* Check that the character is not unnecessarily long. */ - while (character) - character >>= 1, bits++; - bits = ((bits == 0) && (bytes == 2) && allow_modified_nul) ? 8 : bits; - if ((bits < BYTES_TO_MIN_BITS[bytes]) || (BYTES_TO_MAX_BITS[bytes] < bits)) - return -1; - - read_bytes = bytes = bits = 0; - } - - /* Make sure we did not stop at the middle of a multibyte character. */ - return read_bytes == 0 ? 0 : -1; + static long BYTES_TO_MIN_BITS[] = {0, 0, 8, 12, 17, 22, 37}; + static long BYTES_TO_MAX_BITS[] = {0, 7, 11, 16, 21, 26, 31}; + long bytes = 0, read_bytes = 0, bits = 0, c, character; + + /* min bits max bits + 0....... 0 7 + 110..... 10...... 8 11 + 1110.... 10...... 10...... 12 16 + 11110... 10...... 10...... 10...... 17 21 + 111110.. 10...... 10...... 10...... 10...... 22 26 + 1111110. 10...... 10...... 10...... 10...... 10...... 27 31 + */ + + while ((c = (long)(*string++))) { + if (read_bytes == 0) { + /* First byte of the character. */ + + if (!(c & 0x80)) + /* Single-byte character. */ + continue; + + if ((c & 0xC0) == 0x80) + /* Single-byte character marked as multibyte, or + a non-first byte in a multibyte character. */ + return -1; + + /* Multibyte character. */ + while ((c & 0x80)) + bytes++, c <<= 1; + read_bytes = 1; + character = c & 0x7F; + if (bytes > 6) + /* 31-bit characters can be encoded with 6-bytes, + and UTF-8 does not cover higher code points. */ + return -1; + } else { + /* Not first byte of the character. */ + + if ((c & 0xC0) != 0x80) + /* Beginning of new character before a + multibyte character has ended. */ + return -1; + + character = (character << 6) | (c & 0x7F); + + if (++read_bytes < bytes) + /* Not at last byte yet. */ + continue; + + /* Check that the character is not unnecessarily long. */ + while (character) + character >>= 1, bits++; + bits = ((bits == 0) && (bytes == 2) && allow_modified_nul) ? 8 : bits; + if ((bits < BYTES_TO_MIN_BITS[bytes]) || (BYTES_TO_MAX_BITS[bytes] < bits)) + return -1; + + read_bytes = bytes = bits = 0; + } + } + + /* Make sure we did not stop at the middle of a multibyte character. */ + return read_bytes == 0 ? 0 : -1; } @@ -828,85 +831,85 @@ int verify_utf8(const char* string, int allow_modified_nul) * @param message_id The message ID of this message * @return The length of the message, zero on error */ -size_t construct_error_message(const char* restrict recv_client_id, const char* restrict recv_message_id, - const char* restrict recv_command, int custom, int errnum, - const char* restrict message, char** restrict send_buffer, - size_t* restrict send_buffer_size, uint32_t message_id) +size_t +construct_error_message(const char *restrict recv_client_id, const char *restrict recv_message_id, + const char *restrict recv_command, int custom, int errnum, + const char *restrict message, char **restrict send_buffer, + size_t *restrict send_buffer_size, uint32_t message_id) { - ssize_t part_length; - size_t length; - char* temp; - - /* Measure the maximum length of message, including NUL-termination.. */ - length = sizeof("Command: error\n" - "To: 4294967296:4294967296\n" - "In response to: 4294967296\n" - "Origin command: \n" - "Message ID: 4294967296\n" - "Error: custom \n" - "Length: \n" - "\n") / sizeof(char) + 3 * (sizeof(int)) + strlen(recv_command); - if (message != NULL) - length += (sizeof("Length: \n") / sizeof(char) - 1) + 3 * sizeof(char) + strlen(message) + 1; - - /* Ensure that the send buffer is large enough. */ - if (length > *send_buffer_size) - { - fail_if (yrealloc(temp, *send_buffer, length, char)); - *send_buffer_size = length; - } - - /* Reset `length` to indicate that the currently written - message has zero in length. */ - length = 0; - - /* Start writting the error message, begin with required - headers, but exclude the error number. */ - sprintf((*send_buffer) + length, - "Command: error\n" - "To: %s\n" - "In response to: %s\n" - "Origin command: %s\n" - "Message ID: %"PRIu32"\n" - "Error: %s%zn", - recv_client_id, recv_message_id, recv_command, - message_id, custom ? "custom" : "", - &part_length), - length += (size_t)part_length; - - /* If the error is custom and has a number we need - blank space before we write the error number. */ - if (custom && (errnum >= 0)) - (*send_buffer)[length++] = ' '; - - /* Add the error number of there is one. */ - if (errnum >= 0) - sprintf((*send_buffer) + length, "%i%zn", errnum, &part_length), - length += (size_t)part_length; - - /* End the `Error`-header line. */ - (*send_buffer)[length++] = '\n'; - - /* Add the `Length`-header if there is a description. */ - if (message != NULL) - sprintf((*send_buffer) + length, "Length: %zu\n%zn", - strlen(message) + 1, &part_length), - length += (size_t)part_length + strlen(message) + 1; - - /* Add an empty line to mark the end of headers. */ - (*send_buffer)[length++] = '\n'; - - /* Write the description if there is one. */ - if (message) - { - memcpy((*send_buffer) + length, message, strlen(message) * sizeof(char)); - length += strlen(message); - (*send_buffer)[length++] = '\n'; - } - - return length; - fail: - return 0; + ssize_t part_length; + size_t length; + char *temp; + + /* Measure the maximum length of message, including NUL-termination.. */ + length = sizeof("Command: error\n" + "To: 4294967296:4294967296\n" + "In response to: 4294967296\n" + "Origin command: \n" + "Message ID: 4294967296\n" + "Error: custom \n" + "Length: \n" + "\n") / sizeof(char) + 3 * (sizeof(int)) + strlen(recv_command); + if (message) + length += (sizeof("Length: \n") / sizeof(char) - 1) + 3 * sizeof(char) + strlen(message) + 1; + + /* Ensure that the send buffer is large enough. */ + if (length > *send_buffer_size) { + fail_if (yrealloc(temp, *send_buffer, length, char)); + *send_buffer_size = length; + } + + /* Reset `length` to indicate that the currently written + message has zero in length. */ + length = 0; + + /* Start writting the error message, begin with required + headers, but exclude the error number. */ + sprintf((*send_buffer) + length, + "Command: error\n" + "To: %s\n" + "In response to: %s\n" + "Origin command: %s\n" + "Message ID: %"PRIu32"\n" + "Error: %s%zn", + recv_client_id, recv_message_id, recv_command, + message_id, custom ? "custom" : "", + &part_length); + length += (size_t)part_length; + + /* If the error is custom and has a number we need + blank space before we write the error number. */ + if (custom && errnum >= 0) + (*send_buffer)[length++] = ' '; + + /* Add the error number of there is one. */ + if (errnum >= 0) + sprintf((*send_buffer) + length, "%i%zn", errnum, &part_length), + length += (size_t)part_length; + + /* End the `Error`-header line. */ + (*send_buffer)[length++] = '\n'; + + /* Add the `Length`-header if there is a description. */ + if (message) { + sprintf((*send_buffer) + length, "Length: %zu\n%zn", + strlen(message) + 1, &part_length); + length += (size_t)part_length + strlen(message) + 1; + } + + /* Add an empty line to mark the end of headers. */ + (*send_buffer)[length++] = '\n'; + + /* Write the description if there is one. */ + if (message) { + memcpy((*send_buffer) + length, message, strlen(message) * sizeof(char)); + length += strlen(message); + (*send_buffer)[length++] = '\n'; + } + + return length; +fail: + return 0; } @@ -936,17 +939,17 @@ size_t construct_error_message(const char* restrict recv_client_id, const char* * @param message_id The message ID of this message * @return Zero on success, -1 on error */ -int send_error(const char* restrict recv_client_id, const char* restrict recv_message_id, - const char* restrict recv_command, int custom, int errnum, const char* restrict message, - char** restrict send_buffer, size_t* restrict send_buffer_size, uint32_t message_id, - int socket_fd) +int +send_error(const char *restrict recv_client_id, const char *restrict recv_message_id, + const char *restrict recv_command, int custom, int errnum, const char *restrict message, + char **restrict send_buffer, size_t *restrict send_buffer_size, uint32_t message_id, + int socket_fd) { - size_t length; - fail_if ((length = construct_error_message(recv_client_id, recv_message_id, recv_command, custom, errnum, - message, send_buffer, send_buffer_size, message_id)) == 0); - fail_if (full_send(socket_fd, *send_buffer, length)); - return 0; - fail: - return -1; + size_t length; + fail_if (!(length = construct_error_message(recv_client_id, recv_message_id, recv_command, custom, errnum, + message, send_buffer, send_buffer_size, message_id))); + fail_if (full_send(socket_fd, *send_buffer, length)); + return 0; +fail: + return -1; } - |