/** * mds — A micro-display server * Copyright © 2014, 2015, 2016 Mattias Andrée (maandree@member.fsf.org) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MDS_LIBMDSSERVER_UTIL_H #define MDS_LIBMDSSERVER_UTIL_H #include #include #include #ifndef __USE_GNU static inline void *__attribute__((pure, nonnull)) rawmemchr(const void *str, int chr) { intptr_t str_address = (intptr_t)str; void *str_nonconst = (void*)str_address; char *s = str_nonconst; while ((int)*s++ != chr); return s - 1; } #endif /** * Convert a client ID string into a client ID integer * * @param str The client ID string * @return The client ID integer */ __attribute__((pure, nonnull)) uint64_t parse_client_id(const char *str); /** * Read an environment variable, but handle it as undefined if empty * * @param var The environment variable's name * @return The environment variable's value, `NULL` if empty or not defined */ __attribute__((nonnull)) char *getenv_nonempty(const char *var); /** * Prepare the server so that it can reexec into * a newer version of the executed file. * * This is required for two reasons: * 1: We cannot use argv[0] as PATH-resolution may * cause it to reexec into another pathname, and * maybe to wrong program. Additionally argv[0] * may not even refer to the program, and chdir * could also hinter its use. * 2: The kernel appends ` (deleted)` to * `/proc/self/exe` once it has been removed, * so it cannot be replaced. * * The function will should be called immediately, it * will store the content of `/proc/self/exe`. * * @return Zero on success, -1 on error */ int prepare_reexec(void); /** * Re-exec the server. * This function only returns on failure. * * If `prepare_reexec` failed or has not been called, * `argv[0]` will be used as a fallback. * * @param argc The number of elements in `argv` * @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); /** * Set up a signal trap. * This function should only be used for common mds * signals and signals that does not require and * special settings. This function may choose to add * additional behaviour depending on the signal, such * as blocking other signals. * * @param signo The signal to trap * @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)); /** * Send a message over a socket * * @param socket The file descriptor of the socket * @param message The message to send * @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); /** * A version of `atoi` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atoi(const char *str, int *value, int min, int max); /** * A version of `atoj` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atoj(const char *str, intmax_t *value, intmax_t min, intmax_t max); /** * A version of `atouj` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atouj(const char *str, uintmax_t *value, uintmax_t min, uintmax_t max); /** * A version of `atoh` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atoh(const char *str, short int *value, int min, int max); /** * A version of `atouh` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atouh(const char *str, unsigned short int *value, unsigned int min, unsigned int max); /** * A version of `atou` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atou(const char *str, unsigned int *value, unsigned int min, unsigned int max); /** * A version of `atol` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atol(const char *str, long int *value, long int min, long int max); /** * A version of `atoul` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atoul(const char *str, unsigned long int *value, unsigned long int min, unsigned long int max); /** * A version of `atoll` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atoll(const char *str, long long int *value, long long int min, long long int max); /** * A version of `atoull` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atoull(const char *str, unsigned long long int *value, unsigned long long int min, unsigned long long int max); /** * A version of `atoz` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atoz(const char *str, size_t *value, size_t min, size_t max); /** * A version of `atosz` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atosz(const char *str, ssize_t *value, ssize_t min, ssize_t max); /** * A version of `ato8` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_ato8(const char *str, int8_t *value, int min, int max); /** * A version of `atou8` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atou8(const char *str, uint8_t *value, int min, int max); /** * A version of `ato16` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_ato16(const char *str, int16_t *value, int min, int max); /** * A version of `atou16` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atou16(const char *str, uint16_t *value, unsigned int min, unsigned int max); /** * A version of `ato32` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_ato32(const char *str, int32_t *value, int32_t min, int32_t max); /** * A version of `atou32` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atou32(const char *str, uint32_t *value, uint32_t min, uint32_t max); /** * A version of `ato64` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_ato64(const char *str, int64_t *value, int64_t min, int64_t max); /** * A version of `atou64` that is strict about the syntax and bounds * * @param str The text to parse * @param value Slot in which to store the value * @param min The minimum accepted value * @param max The maximum accepted value * @return Zero on success, -1 on syntax error */ __attribute__((nonnull)) int strict_atou64(const char *str, uint64_t *value, uint64_t min, uint64_t max); /** * Send a buffer into a file and ignore interruptions * * @param fd The file descriptor * @param buffer The buffer * @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); /** * Read a file completely and ignore interruptions * * @param fd The file descriptor * @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); /** * Send a full message even if interrupted * * @param socket The file descriptor for the socket to use * @param message The message to send * @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); /** * Check whether a string begins with a specific string, * where neither of the strings are necessarily NUL-terminated * * @param haystack The string that should start with the other string * @param needle The string the first string should start with * @param haystack_n The length of `haystack` * @param needle_n The length of `needle` * @return Whether the `haystack` begins with `needle` */ __attribute__((pure, nonnull)) int startswith_n(const char *haystack, const char *needle, size_t haystack_n, size_t needle_n); /** * Wrapper around `waitpid` that never returns on an interruption unless * it is interrupted 100 times within the same second * * @param pid See description of `pid` in the documentation for `waitpid` * @param status See description of `status` in the documentation for `waitpid` * @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); /** * Check whether a NUL-terminated string is encoded in UTF-8 * * @param string The string * @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 */ __attribute__((pure, nonnull)) int verify_utf8(const char *string, int allow_modified_nul); /** * Construct an error message to be sent to a client * * @param recv_client_id The client ID attached on the message that was received, must not be `NULL` * @param recv_message_id The message ID attached on the message that was received, must not be `NULL` * @param recv_command The value of the `Command`-header on the message that was received, * must not be `NULL` * @param custom Non-zero if the error is a custom error * @param errnum The error number, `errno` should be used if the error * is not a custom error, zero should be used on success, * a negative integer, such as -1, indicates that the error * message should not include an error number, * it is not allowed to have this value be negative and * `custom` be zero at the same time * @param message The description of the error, the line feed at the end * is added automatically, `NULL` if the description should * be omitted * @param send_buffer Pointer to the buffer where the message should be stored, * it should contain the current send buffer, must not be `NULL` * @param send_buffer_size Pointer to the allocation size of `*send_buffer`, it should * contain the current size of `*send_buffer` and will be updated * with the new size, must not be `NULL` * @param message_id The message ID of this message * @return The length of the message, zero on error */ __attribute__((nonnull(1, 2, 3, 7, 8))) 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); /** * Send an error message * * @param recv_client_id The client ID attached on the message that was received, must not be `NULL` * @param recv_message_id The message ID attached on the message that was received, must not be `NULL` * @param recv_command The value of the `Command`-header on the message that was received, * must not be `NULL` * @param custom Non-zero if the error is a custom error * @param errnum The error number, `errno` should be used if the error * is not a custom error, zero should be used on success, * a negative integer, such as -1, indicates that the error * message should not include an error number, * it is not allowed to have this value be negative and * `custom` be zero at the same time * @param message The description of the error, the line feed at the end * is added automatically, `NULL` if the description should * be omitted * @param send_buffer Pointer to the buffer where the message should be stored, * it should contain the current send buffer, must not be `NULL` * @param send_buffer_size Pointer to the allocation size of `*send_buffer`, it should * contain the current size of `*send_buffer` and will be updated * with the new size, must not be `NULL` * @param message_id The message ID of this message * @param socket_fd The file descriptor of the socket * @return Zero on success, -1 on error */ __attribute__((nonnull(1, 2, 3, 7, 8))) 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); #endif