| #ifndef H_LIBSOCK_H |
| #define H_LIBSOCK_H |
| /* |
| Copyright: Boaz Segev, 2016-2017 |
| License: MIT |
| |
| Feel free to copy, use and enjoy according to the license provided. |
| */ |
| #define LIB_SOCK_VERSION_MAJOR 0 |
| #define LIB_SOCK_VERSION_MINOR 4 |
| #define LIB_SOCK_VERSION_PATCH 0 |
| |
| #ifndef LIB_SOCK_MAX_CAPACITY |
| /** The maximum `fd` value `sock.h` should support. */ |
| #define LIB_SOCK_MAX_CAPACITY 4194304 |
| #endif |
| /** \file |
| * The `sock.h` is a non-blocking socket helper library, using a user level |
| * buffer, non-blocking sockets and some helper functions. |
| * |
| * This library is great when using it alongside `evio.h`. |
| * |
| * The library is designed to be thread safe, but not fork safe - mostly since |
| * sockets, except listenning sockets, shouldn't be shared among processes. |
| * |
| * Socket connections accepted or created using this library will use the |
| * TCP_NODELAY option by default. |
| * |
| * Non TCP/IP stream sockets and file descriptors (i.e., unix sockets) can be |
| * safely used with this library. However, file descriptors that can't use the |
| * `read` or `write` system calls MUST set correct Read / Write hooks or they |
| * will fail. |
| */ |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #ifndef UNUSED_FUNC |
| #define UNUSED_FUNC __attribute__((unused)) |
| #endif |
| |
| /* ***************************************************************************** |
| A simple, predictable UUID for file-descriptors, for collision prevention |
| */ |
| #ifndef sock_uuid2fd |
| #define sock_uuid2fd(uuid) ((intptr_t)((uintptr_t)uuid >> 8)) |
| #endif |
| |
| /* ***************************************************************************** |
| C++ extern |
| */ |
| #if defined(__cplusplus) |
| extern "C" { |
| #endif |
| |
| /* ***************************************************************************** |
| Process wide and helper sock_API. |
| */ |
| |
| /** |
| * Sets a socket to non blocking state. |
| * |
| * This function is called automatically for the new socket, when using |
| * `sock_accept` or `sock_connect`. |
| */ |
| int sock_set_non_block(int fd); |
| |
| /** |
| Gets the maximum number of file descriptors this process can be allowed to |
| access (== maximum fd value + 1). |
| |
| If the "soft" limit is lower then the "hard" limit, the process's limits will be |
| extended to the allowed "hard" limit. |
| */ |
| ssize_t sock_max_capacity(void); |
| |
| /* ***************************************************************************** |
| The main sock_API. |
| */ |
| |
| /** |
| * Opens a listening non-blocking socket. Return's the socket's UUID. |
| * |
| * Returns -1 on error. Returns a valid socket (non-random) UUID. |
| * |
| * UUIDs with values less then -1 are valid values, depending on the system's |
| * byte-ordering. |
| * |
| * Socket UUIDs are predictable and shouldn't be used outside the local system. |
| * They protect against connection mixups on concurrent systems (i.e. when |
| * saving client data for "broadcasting" or when an old client task is preparing |
| * a response in the background while a disconnection and a new connection occur |
| * on the same `fd`). |
| */ |
| intptr_t sock_listen(const char *address, const char *port); |
| |
| /** |
| * `sock_accept` accepts a new socket connection from the listening socket |
| * `server_fd`, allowing the use of `sock_` functions with this new file |
| * descriptor. |
| |
| * When using `libreact`, remember to call `int reactor_add(intptr_t uuid);` to |
| * listen for events. |
| * |
| * Returns -1 on error. Returns a valid socket (non-random) UUID. |
| * |
| * Socket UUIDs are predictable and shouldn't be used outside the local system. |
| * They protect against connection mixups on concurrent systems (i.e. when saving |
| * client data for "broadcasting" or when an old client task is preparing a |
| * response in the background while a disconnection and a new connection occur on |
| * the same `fd`). |
| */ |
| intptr_t sock_accept(intptr_t srv_uuid); |
| |
| /** |
| * `sock_connect` is similar to `sock_accept` but should be used to initiate a |
| * client connection to the address requested. |
| * |
| * Returns -1 on error. Returns a valid socket (non-random) UUID. |
| * |
| * Socket UUIDs are predictable and shouldn't be used outside the local system. |
| * They protect against connection mixups on concurrent systems (i.e. when |
| * saving client data for "broadcasting" or when an old client task is preparing |
| * a response in the background while a disconnection and a new connection occur |
| * on the same `fd`). |
| * |
| * When using `evio`, remember to call `int evio_add(sock_fd2uuid(uuid), |
| * (void*)uuid);` to listen for events. |
| * |
| * NOTICE: |
| * |
| * This function is non-blocking, meaning that the connection probably wasn't |
| * established by the time the function returns (this prevents the function from |
| * hanging while waiting for a network timeout). |
| * |
| * Use select, poll, `evio` or other solutions to review the connection state |
| * before attempting to write to the socket. |
| */ |
| intptr_t sock_connect(char *address, char *port); |
| |
| /** |
| * `sock_open` takes an existing file descriptor `fd` and initializes it's status |
| * as open and available for `sock_API` calls, returning a valid UUID. |
| * |
| * This will reinitialize the data (user buffer etc') for the file descriptor |
| * provided, calling the `sock_on_close` callback if the `fd` was previously |
| * marked as used. |
| |
| * When using `evio`, remember to call `int evio_add(sock_fd2uuid(uuid), |
| * (void*)uuid);` to listen for events. |
| * |
| * Returns -1 on error. Returns a valid socket (non-random) UUID. |
| * |
| * Socket UUIDs are predictable and shouldn't be used outside the local system. |
| * They protect against connection mixups on concurrent systems (i.e. when saving |
| * client data for "broadcasting" or when an old client task is preparing a |
| * response in the background while a disconnection and a new connection occur on |
| * the same `fd`). |
| */ |
| intptr_t sock_open(int fd); |
| |
| /** |
| * Returns 1 if the uuid refers to a valid and open, socket. |
| * |
| * Returns 0 if not. |
| */ |
| int sock_isvalid(intptr_t uuid); |
| |
| /** The return type for the `sock_peer_addr` function. */ |
| typedef struct { |
| uint32_t addrlen; |
| struct sockaddr *addr; |
| } sock_peer_addr_s; |
| /** |
| * Returns the information available about the socket's peer address. |
| * |
| * If no information is available, the struct will be initialized with zero |
| * (`addr == NULL`). |
| * The information is only available when the socket was accepted using |
| * `sock_accept` or opened using `sock_connect`. |
| */ |
| sock_peer_addr_s sock_peer_addr(intptr_t uuid); |
| |
| /** |
| * `sock_fd2uuid` takes an existing file decriptor `fd` and returns it's active |
| * `uuid`. |
| |
| * If the file descriptor is marked as closed (wasn't opened / registered with |
| * `libsock`) the function returns -1; |
| * |
| * If the file descriptor was closed remotely (or not using `libsock`), a false |
| * positive will be possible. This is not an issue, since the use of an invalid |
| fd |
| * will result in the registry being updated and the fd being closed. |
| * |
| * Returns -1 on error. Returns a valid socket (non-random) UUID. |
| */ |
| intptr_t sock_fd2uuid(int fd); |
| |
| /** |
| * OVERRIDABLE: |
| * |
| * "Touches" a socket connection. |
| * |
| * This is a place holder for an optional callback for systems that apply |
| * timeout reviews. |
| * |
| * `sock` supplies a default implementation (that does nothing) is cases where a |
| * callback wasn't defined. |
| */ |
| void sock_touch(intptr_t uuid); |
| |
| /** |
| * OVERRIDABLE:. |
| * |
| * This is a place holder for an optional callback for when the socket is closed |
| * locally. |
| * |
| * Notice that calling `sock_close()` won't close the socket before all the data |
| * in the buffer is sent. This function will be called only one the connection |
| * is actually closed. |
| * |
| * `sock` supplies a default implementation (that does nothing) is cases where a |
| * callback wasn't defined. |
| */ |
| void sock_on_close(intptr_t uuid); |
| |
| /** |
| * `sock_read` attempts to read up to count bytes from the socket into the |
| * buffer starting at buf. |
| * |
| * `sock_read`'s return values are wildly different then the native return |
| * values and they aim at making far simpler sense. |
| * |
| * `sock_read` returns the number of bytes read (0 is a valid return value which |
| * simply means that no bytes were read from the buffer). |
| * |
| * On a connection error (NOT EAGAIN or EWOULDBLOCK), signal interrupt, or when |
| * the connection was closed, `sock_read` returns -1. |
| * |
| * The value 0 is the valid value indicating no data was read. |
| * |
| * Data might be available in the kernel's buffer while it is not available to |
| * be read using `sock_read` (i.e., when using a transport layer, such as TLS). |
| */ |
| ssize_t sock_read(intptr_t uuid, void *buf, size_t count); |
| |
| typedef struct { |
| /** The fsocket uuid for sending data. */ |
| intptr_t uuid; |
| union { |
| /** The in-memory data to be sent. */ |
| const void *buffer; |
| /** The data to be sent, if this is a file. */ |
| const intptr_t data_fd; |
| }; |
| union { |
| /** |
| * This deallocation callback will be called when the packet is finished |
| * with the buffer if the `move` flags is set. |
| * |
| * If no deallocation callback is `free` will be used. |
| * |
| * Note: `sock` library functions MUST NEVER be called by a callback, or a |
| * deadlock might occur. |
| */ |
| void (*dealloc)(void *buffer); |
| /** |
| * This is an alternative deallocation callback accessor (same memory space |
| * as `dealloc`) for conveniently setting the file `close` callback. |
| * |
| * Note: `sock` library functions MUST NEVER be called by a callback, or a |
| * deadlock might occur. |
| */ |
| void (*close)(intptr_t fd); |
| }; |
| /** The length (size) of the buffer, or the amount of data to be sent from the |
| * file descriptor. |
| */ |
| uintptr_t length; |
| /** Starting point offset from the buffer or file descriptor's beginning. */ |
| intptr_t offset; |
| /** The packet will be sent as soon as possible. */ |
| unsigned urgent : 1; |
| /** |
| * The buffer contains the value of a file descriptor (`int`). i.e.: |
| * `.data_fd = fd` or `.buffer = (void*)fd;` |
| */ |
| unsigned is_fd : 1; |
| /** |
| * The buffer **points** to a file descriptor (`int`): `.buffer = (void*)&fd;` |
| * |
| * In the case the `dealloc` function will be called, allowing both closure |
| * and deallocation of the `int` pointer. |
| * |
| * This feature can be used for file reference counting, such as implemented |
| * by the `fio_file_cache` service. |
| */ |
| unsigned is_pfd : 1; |
| /** for internal use */ |
| unsigned rsv : 1; |
| } sock_write_info_s; |
| |
| void SOCK_DEALLOC_NOOP(void *arg); |
| #define SOCK_CLOSE_NOOP ((void (*)(intptr_t))SOCK_DEALLOC_NOOP) |
| |
| /** |
| * `sock_write2_fn` is the actual function behind the macro `sock_write2`. |
| */ |
| ssize_t sock_write2_fn(sock_write_info_s options); |
| |
| /** |
| * `sock_write2` is similar to `sock_write`, except special properties can be |
| * set. |
| * |
| * On error, -1 will be returned. Otherwise returns 0. All the bytes are |
| * transferred to the socket's user level buffer. |
| */ |
| #define sock_write2(...) sock_write2_fn((sock_write_info_s){__VA_ARGS__}) |
| /** |
| * `sock_write` copies `legnth` data from the buffer and schedules the data to |
| * be sent over the socket. |
| * |
| * The data isn't necessarily written to the socket and multiple calls to |
| * `sock_flush` might be required before all the data is actually sent. |
| * |
| * On error, -1 will be returned. Otherwise returns 0. All the bytes are |
| * transferred to the socket's user level buffer. |
| * |
| * Returns the same values as `sock_write2`. |
| */ |
| // ssize_t sock_write(uintptr_t uuid, void *buffer, size_t legnth); |
| UNUSED_FUNC static inline ssize_t |
| sock_write(const intptr_t uuid, const void *buffer, const size_t length) { |
| if (!length || !buffer) |
| return 0; |
| void *cpy = malloc(length); |
| if (!cpy) |
| return -1; |
| memcpy(cpy, buffer, length); |
| return sock_write2(.uuid = uuid, .buffer = cpy, .length = length); |
| } |
| |
| /** |
| * Sends data from a file as if it were a single atomic packet (sends up to |
| * length bytes or until EOF is reached). |
| * |
| * Once the file was sent, the `source_fd` will be closed using `close`. |
| * |
| * The file will be buffered to the socket chunk by chunk, so that memory |
| * consumption is capped. The system's `sendfile` might be used if conditions |
| * permit. |
| * |
| * `offset` dictates the starting point for te data to be sent and length sets |
| * the maximum amount of data to be sent. |
| * |
| * Returns -1 and closes the file on error. Returns 0 on success. |
| */ |
| UNUSED_FUNC static inline ssize_t |
| sock_sendfile(intptr_t uuid, intptr_t source_fd, off_t offset, size_t length) { |
| return sock_write2(.uuid = uuid, .buffer = (void *)(source_fd), |
| .length = length, .is_fd = 1, .offset = offset); |
| } |
| |
| /** |
| * `sock_close` marks the connection for disconnection once all the data was |
| * sent. The actual disconnection will be managed by the `sock_flush` function. |
| * |
| * `sock_flash` will automatically be called. |
| */ |
| void sock_close(intptr_t uuid); |
| /** |
| * `sock_force_close` closes the connection immediately, without adhering to any |
| * protocol restrictions and without sending any remaining data in the |
| * connection buffer. |
| */ |
| void sock_force_close(intptr_t uuid); |
| |
| /* ***************************************************************************** |
| Direct user level buffer API. |
| */ |
| |
| /** |
| * `sock_flush` writes the data in the internal buffer to the underlying file |
| * descriptor and closes the underlying fd once it's marked for closure (and all |
| * the data was sent). |
| * |
| * Return value: 0 will be returned on success and -1 will be returned on an |
| * error or when the connection is closed. |
| */ |
| ssize_t sock_flush(intptr_t uuid); |
| /** |
| * `sock_flush_strong` performs the same action as `sock_flush` but returns only |
| * after all the data was sent. This is a "busy" wait, polling isn't performed. |
| */ |
| void sock_flush_strong(intptr_t uuid); |
| /** |
| * Calls `sock_flush` for each file descriptor that's buffer isn't empty. |
| */ |
| void sock_flush_all(void); |
| |
| /** |
| * Returns TRUE (non 0) if there is data waiting to be written to the socket in |
| * the user-land buffer. |
| */ |
| int sock_has_pending(intptr_t uuid); |
| |
| /* ***************************************************************************** |
| TLC - Transport Layer Callback. |
| */ |
| |
| /** |
| * The following struct is used for setting a the read/write hooks that will |
| * replace the default system calls to `recv` and `write`. |
| * |
| * Note: `sock` library functions MUST NEVER be called by any callback, or a |
| * deadlock might occur. |
| */ |
| typedef struct sock_rw_hook_s { |
| /** |
| * Implement reading from a file descriptor. Should behave like the file |
| * system `read` call, including the setup or errno to EAGAIN / EWOULDBLOCK. |
| * |
| * Note: `sock` library functions MUST NEVER be called by any callback, or a |
| * deadlock might occur. |
| */ |
| ssize_t (*read)(intptr_t uuid, void *udata, void *buf, size_t count); |
| /** |
| * Implement writing to a file descriptor. Should behave like the file system |
| * `write` call. |
| * |
| * Note: `sock` library functions MUST NEVER be called by any callback, or a |
| * deadlock might occur. |
| */ |
| ssize_t (*write)(intptr_t uuid, void *udata, const void *buf, size_t count); |
| /** |
| * When implemented, this function will be called to flush any data remaining |
| * in the internal buffer. |
| * |
| * The function should return the number of bytes remaining in the internal |
| * buffer (0 is a valid response) on -1 (on error). |
| * |
| * It is important thet the `flush` function write to the underlying fd until |
| * the writing operation returns -1 with EWOULDBLOCK or all the data was |
| * written. |
| * |
| * Note: `sock` library functions MUST NEVER be called by any callback, or a |
| * deadlock might occur. |
| */ |
| ssize_t (*flush)(intptr_t uuid, void *udata); |
| /** |
| * The `on_close` callback is called when the socket is closed, allowing for |
| * dynamic sock_rw_hook_s memory management. |
| * |
| * The `on_close` callback should manage is own thread safety mechanism, if |
| * required. |
| * |
| * Note: `sock` library functions MUST NEVER be called by any callback, or a |
| * deadlock might occur. |
| * */ |
| void (*on_close)(intptr_t uuid, struct sock_rw_hook_s *rw_hook, void *udata); |
| } sock_rw_hook_s; |
| |
| /* ***************************************************************************** |
| RW hooks implementation |
| */ |
| |
| /** Sets a socket hook state (a pointer to the struct). */ |
| int sock_rw_hook_set(intptr_t uuid, sock_rw_hook_s *rw_hooks, void *udata); |
| |
| /** Gets a socket hook state (a pointer to the struct). */ |
| struct sock_rw_hook_s *sock_rw_hook_get(intptr_t uuid); |
| |
| /** Returns the socket's udata associated with the read/write hook. */ |
| void *sock_rw_udata(intptr_t uuid); |
| |
| /** The default Read/Write hooks used for system Read/Write (udata == NULL). */ |
| extern const sock_rw_hook_s SOCK_DEFAULT_HOOKS; |
| |
| /* ***************************************************************************** |
| test |
| */ |
| #ifdef DEBUG |
| void sock_libtest(void); |
| #endif |
| |
| /* ***************************************************************************** |
| C++ extern |
| */ |
| #if defined(__cplusplus) |
| } |
| #endif |
| |
| #endif /* H_LIBSOCK_H */ |