├── .editorconfig ├── .github └── workflows │ └── build-ci.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── include └── utils │ ├── arg_parser.h │ ├── assert.h │ ├── bus_server.h │ ├── byteorder.h │ ├── channel.h │ ├── circbuf.h │ ├── crc32.h │ ├── disjoint_set.h │ ├── event.h │ ├── fdutils.h │ ├── file.h │ ├── filo.h │ ├── hashmap.h │ ├── list.h │ ├── logger.h │ ├── memory.h │ ├── pcap_gen.h │ ├── procutils.h │ ├── queue.h │ ├── random.h │ ├── serial.h │ ├── slab.h │ ├── sockutils.h │ ├── stack.h │ ├── strlib.h │ ├── strutils.h │ ├── utils.h │ └── workqueue.h ├── src ├── arg_parser.c ├── bus_server.c ├── channel.c ├── circbuf.c ├── crc32.c ├── disjoint_set.c ├── event.c ├── fdutils.c ├── file.c ├── filo.c ├── hashmap.c ├── list.c ├── logger.c ├── memory.c ├── pcap_gen.c ├── procutils.c ├── queue.c ├── random.c ├── serial.c ├── slab.c ├── sockutils.c ├── stack.c ├── strlib.c ├── strutils.c ├── utils.c └── workqueue.c └── tests ├── input └── words_alpha.txt ├── test-bus-server.c ├── test-circbuf.c ├── test-filo.c ├── test-hashmap.c ├── test-procutils.c ├── test-slab.c ├── test-strlib.c ├── test-strutils.c ├── test-workqueue.c ├── test.c └── test.h /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # Unix-style newlines with a newline ending every file 4 | [*] 5 | end_of_line = lf 6 | charset = utf-8 7 | indent_style = tab 8 | indent_size = 8 9 | 10 | # 4 space indentation 11 | [*.{py,md}] 12 | indent_style = space 13 | indent_size = 4 14 | -------------------------------------------------------------------------------- /.github/workflows/build-ci.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020-2024 Siddharth Chandrasekaran 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | name: Build CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: configure 19 | run: cmake . 20 | - name: make 21 | run: make 22 | - name: make check 23 | run: make check_utils 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | tags 55 | .vscode 56 | build/ 57 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 8 | project(utils VERSION 0.1.0 LANGUAGES C) 9 | 10 | # libutils.a 11 | set(LIB_UTILS utils) 12 | file(GLOB LIB_UTILS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c) 13 | file(GLOB LIB_UTILS_INC ${CMAKE_CURRENT_SOURCE_DIR}/include/utils/*.h) 14 | add_library(${LIB_UTILS} STATIC ${LIB_UTILS_SRC} ${LIB_UTILS_INC}) 15 | target_include_directories(${LIB_UTILS} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) 16 | set_target_properties(${LIB_UTILS} PROPERTIES PUBLIC_HEADER "${LIB_UTILS_INC}") 17 | 18 | # check target 19 | set(TEST_BIN test-utils) 20 | file(GLOB TEST_SRC 21 | ${CMAKE_CURRENT_SOURCE_DIR}/tests/*.c 22 | ${CMAKE_CURRENT_SOURCE_DIR}/tests/*.h 23 | ) 24 | add_executable(${TEST_BIN} ${TEST_SRC}) 25 | target_link_libraries(${TEST_BIN} ${LIB_UTILS} pthread) 26 | target_include_directories(${TEST_BIN} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) 27 | set_target_properties(${TEST_BIN} PROPERTIES EXCLUDE_FROM_ALL TRUE) 28 | 29 | function(download_file URL FILE_PATH) 30 | message("Downloading ${FILE_PATH}..") 31 | file(DOWNLOAD ${URL} ${FILE_PATH} STATUS DOWNLOAD_RESULT) 32 | list(GET DOWNLOAD_RESULT 0 DOWNLOAD_RESULT_CODE) 33 | if(NOT DOWNLOAD_RESULT_CODE EQUAL 0) 34 | message(FATAL_ERROR "Failed downloading ${URL}! Error: ${DOWNLOAD_RESULT}.") 35 | endif() 36 | endfunction() 37 | 38 | add_custom_target(check_utils 39 | COMMAND ${CMAKE_BINARY_DIR}/${TEST_BIN} -i"${CMAKE_CURRENT_SOURCE_DIR}/tests/input" 40 | COMMAND rm ${CMAKE_BINARY_DIR}/${TEST_BIN} 41 | DEPENDS ${TEST_BIN} 42 | ) 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2021-2024 Siddharth Chandrasekaran 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | 7 | O ?= . 8 | NAME ?= utils 9 | SRC := $(wildcard src/*.c) 10 | OBJ := $(SRC:%.c=%.o) 11 | CCFLAGS ?= -Wall -Wextra -O3 12 | 13 | ifeq ($(V),) 14 | Q := @ 15 | else 16 | Q := 17 | endif 18 | 19 | .PHONY: all 20 | all: $(O)/lib$(NAME).a 21 | @echo > /dev/null 22 | 23 | $(O)/lib$(NAME).a: $(OBJ) 24 | @echo " AR $(@F)" 25 | $(Q)$(AR) -qc $@ $^ 26 | 27 | $(O)/src/%.o: src/%.c 28 | @echo " CC $<" 29 | @mkdir -p $(@D) 30 | $(Q)$(CC) -c $< $(CCFLAGS) -Iinclude/ -o $@ 31 | 32 | .PHONY: clean 33 | clean: 34 | $(Q)rm -f $(OBJ) $(O)/lib$(NAME).a 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # c-utils 2 | [![Build CI][1]][2] 3 | 4 | This repository hosts some modular, drop-in, C utilities that you can copy into 5 | your project and and kick-start your work. 6 | 7 | ## List of utils: 8 | 9 | The following methods/utils are available in this repo: 10 | 11 | - **arg_parser** - Command line argument parsing helper 12 | - **bus_server** - A broadcasting IPC server for connected clients 13 | - **channel** - A Communication protocol (uart, msgq, tcp, etc.,) abstraction layer 14 | - **circbuf** - Generic, lock-free, circular/ring buffer implementation 15 | - **disjoint_set** - Disjoint-set library 16 | - **event** - Event notifier for multi-threaded applications 17 | - **fdutils** - Collection of operations that are commonly performed on unix file descriptors 18 | - **file** - Collection of methods that operate on directories, files and paths 19 | - **filo** - First-In Last-Out (stack) implementation 20 | - **hashmap** - Hashmap/dictionary/map library 21 | - **list** - Singly and doubly linked list data library 22 | - **logger** - Logging module for C applications with log-levels, colors, and log-to-file features 23 | - **memory** - Do-or-die helper methods that allow use of mallloc/calloc/strdups without NULL checks 24 | - **procutils** - Linux process manipulation utilities 25 | - **queue** - Last-in First-out (queue) implementation 26 | - **serial** - Library to interact with uart devices 27 | - **slab** - Poor man's slab allocator for dynamic memory without using heap 28 | - **sockutils** - Collection of methods that operate on sockets 29 | - **stack** - Stack implementation using linked lists 30 | - **strlib** - A string_t type and some common methods that operate on them 31 | - **strutils** - Commonly used C string utils 32 | - **utils** - Other ad-hoc methods that don't fit anywhere else 33 | - **workqueue** - Worker thread library that waits for a job to execute 34 | 35 | ## How to use this repo? 36 | 37 | You can clone this repo and copy individual files from src/ and link it to your 38 | project. 39 | 40 | [1]: https://github.com/embedjournal/c-utils/workflows/Build%20CI/badge.svg 41 | [2]: https://github.com/embedjournal/c-utils/actions?query=workflow%3A%22Build+CI%22 42 | [3]: https://embedjournal.com/implementing-circular-buffer-embedded-c/ 43 | -------------------------------------------------------------------------------- /include/utils/arg_parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTIL_ARG_PARSER_H_ 8 | #define _UTIL_ARG_PARSER_H_ 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #define AP_HELP_SPACING 25 17 | 18 | #define AP_OPT_NOFLAG 0x00000000 19 | #define AP_OPT_REQUIRED 0x00000001 20 | #define AP_OPT_SEEN 0x10000000 21 | 22 | enum ap_type_e { 23 | AP_TYPE_CMD, 24 | AP_TYPE_BOOL, 25 | AP_TYPE_INT, 26 | AP_TYPE_STR, 27 | AP_TYPE_BOOL_HANDLER, 28 | AP_TYPE_SENTINEL 29 | }; 30 | 31 | struct ap_option { 32 | signed char short_name; 33 | const char *long_name; 34 | const char *opt_name; 35 | enum ap_type_e type; 36 | size_t offset; 37 | 38 | int flags; 39 | int (*validator)(void *data); 40 | int (*handler)(int argc, char *argv[], void *data); 41 | const char *help; 42 | void (*bool_handler)(); 43 | }; 44 | 45 | #define AP_STORE_INT(s, m) AP_TYPE_INT, (size_t)&(((s *)0)->m) 46 | #define AP_STORE_STR(s, m) AP_TYPE_STR, (size_t)&(((s *)0)->m) 47 | #define AP_STORE_BOOL(s, m) AP_TYPE_BOOL, (size_t)&(((s *)0)->m) 48 | 49 | #define AP_CMD(x, y) -1, x, NULL, AP_TYPE_CMD, 0, 0, NULL, y 50 | #define AP_ARG_BOOL(s, l, h, m) s, l, NULL, AP_TYPE_BOOL_HANDLER, 0, 0, NULL, NULL, m, h 51 | #define AP_SENTINEL { '\0', NULL, NULL, AP_TYPE_SENTINEL, 0, 0, NULL, NULL, NULL, NULL } 52 | 53 | /* dummy macros to improve readability */ 54 | #define AP_ARG(s, l, n) s, l, n 55 | #define AP_VALIDATOR(x) x, NULL 56 | #define AP_FLAGS(x) x 57 | #define AP_HELP(x) x, NULL 58 | 59 | void ap_init(const char *app_name, const char *app_desc); 60 | int ap_parse(int argc, char *argv[], struct ap_option *ap_opts, void *data); 61 | void ap_print_help(struct ap_option *ap_opts, int exit_code); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | #endif /* _UTIL_ARG_PARSER_H_ */ 68 | -------------------------------------------------------------------------------- /include/utils/assert.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTILS_ERROR_H_ 8 | #define _UTILS_ERROR_H_ 9 | 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #define __ASSERT_PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__) 18 | 19 | #define __ASSERT_LOC(test) \ 20 | __ASSERT_PRINT("ASSERTION FAIL [%s] @ %s:%d\n", \ 21 | STR(test), __FILE__, __LINE__) 22 | 23 | #define __ASSERT_MSG_INFO(fmt, ...) \ 24 | __ASSERT_PRINT("\t" fmt "\n", ##__VA_ARGS__) 25 | 26 | #define __ASSERT(test, fmt, ...) \ 27 | do { \ 28 | if (!(test)) { \ 29 | __ASSERT_LOC(test); \ 30 | __ASSERT_MSG_INFO(fmt, ##__VA_ARGS__); \ 31 | dump_trace(); \ 32 | exit(EXIT_FAILURE); \ 33 | } \ 34 | } while (0) 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | 40 | #endif /* _UTILS_ERROR_H_ */ 41 | -------------------------------------------------------------------------------- /include/utils/bus_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTILS_BUS_SERVER_H_ 8 | #define _UTILS_BUS_SERVER_H_ 9 | 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | typedef struct { 18 | int fd; 19 | int max_clients; 20 | char *path; 21 | workqueue_t wq; 22 | pthread_t thread; 23 | work_t *work; 24 | } bus_server_t; 25 | 26 | int bus_server_start(bus_server_t *s, int max_clients, const char *path); 27 | void bus_server_stop(bus_server_t *s); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif /* _UTILS_BUS_SERVER_H_ */ -------------------------------------------------------------------------------- /include/utils/byteorder.h: -------------------------------------------------------------------------------- 1 | #ifndef __BYTEORDER_H_ 2 | #define __BYTEORDER_H_ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) 11 | # if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ 12 | (defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN) || \ 13 | (defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN) || \ 14 | (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || \ 15 | (defined(__sun) && defined(__SVR4) && defined(_BIG_ENDIAN)) || \ 16 | defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ 17 | defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) || \ 18 | defined(_M_PPC) 19 | # define __BIG_ENDIAN__ 20 | # elif (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || /* gcc */\ 21 | (defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) /* linux header */ || \ 22 | (defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN) || \ 23 | (defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN) /* mingw header */ || \ 24 | (defined(__sun) && defined(__SVR4) && defined(_LITTLE_ENDIAN)) || /* solaris */ \ 25 | defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \ 26 | defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \ 27 | defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64) || /* msvc for intel processors */ \ 28 | defined(_M_ARM) /* msvc code on arm executes in little endian mode */ 29 | # define __LITTLE_ENDIAN__ 30 | # endif 31 | #endif 32 | 33 | #if !defined(__LITTLE_ENDIAN__) & !defined(__BIG_ENDIAN__) 34 | # error "UNKNOWN Platform / endianness. Configure endianness checks for this platform or set explicitly." 35 | #endif 36 | 37 | #define __bswap_16(x) ((uint16_t) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))) 38 | #define __bswap_24(x) ((uint32_t) ((((x) >> 16) & 0xff) | \ 39 | (((x)) & 0xff00) | \ 40 | (((x) & 0xff) << 16))) 41 | #define __bswap_32(x) ((uint32_t) ((((x) >> 24) & 0xff) | \ 42 | (((x) >> 8) & 0xff00) | \ 43 | (((x) & 0xff00) << 8) | \ 44 | (((x) & 0xff) << 24))) 45 | #define __bswap_48(x) ((uint64_t) ((((x) >> 40) & 0xff) | \ 46 | (((x) >> 24) & 0xff00) | \ 47 | (((x) >> 8) & 0xff0000) | \ 48 | (((x) & 0xff0000) << 8) | \ 49 | (((x) & 0xff00) << 24) | \ 50 | (((x) & 0xff) << 40))) 51 | #define __bswap_64(x) ((uint64_t) ((((x) >> 56) & 0xff) | \ 52 | (((x) >> 40) & 0xff00) | \ 53 | (((x) >> 24) & 0xff0000) | \ 54 | (((x) >> 8) & 0xff000000) | \ 55 | (((x) & 0xff000000) << 8) | \ 56 | (((x) & 0xff0000) << 24) | \ 57 | (((x) & 0xff00) << 40) | \ 58 | (((x) & 0xff) << 56))) 59 | 60 | #if defined(__LITTLE_ENDIAN__) 61 | 62 | #define sys_le16_to_cpu(val) (val) 63 | #define sys_cpu_to_le16(val) (val) 64 | #define sys_le24_to_cpu(val) (val) 65 | #define sys_cpu_to_le24(val) (val) 66 | #define sys_le32_to_cpu(val) (val) 67 | #define sys_cpu_to_le32(val) (val) 68 | #define sys_le48_to_cpu(val) (val) 69 | #define sys_cpu_to_le48(val) (val) 70 | #define sys_le64_to_cpu(val) (val) 71 | #define sys_cpu_to_le64(val) (val) 72 | #define sys_be16_to_cpu(val) __bswap_16(val) 73 | #define sys_cpu_to_be16(val) __bswap_16(val) 74 | #define sys_be24_to_cpu(val) __bswap_24(val) 75 | #define sys_cpu_to_be24(val) __bswap_24(val) 76 | #define sys_be32_to_cpu(val) __bswap_32(val) 77 | #define sys_cpu_to_be32(val) __bswap_32(val) 78 | #define sys_be48_to_cpu(val) __bswap_48(val) 79 | #define sys_cpu_to_be48(val) __bswap_48(val) 80 | #define sys_be64_to_cpu(val) __bswap_64(val) 81 | #define sys_cpu_to_be64(val) __bswap_64(val) 82 | 83 | #else 84 | 85 | #define sys_le16_to_cpu(val) __bswap_16(val) 86 | #define sys_cpu_to_le16(val) __bswap_16(val) 87 | #define sys_le24_to_cpu(val) __bswap_24(val) 88 | #define sys_cpu_to_le24(val) __bswap_24(val) 89 | #define sys_le32_to_cpu(val) __bswap_32(val) 90 | #define sys_cpu_to_le32(val) __bswap_32(val) 91 | #define sys_le48_to_cpu(val) __bswap_48(val) 92 | #define sys_cpu_to_le48(val) __bswap_48(val) 93 | #define sys_le64_to_cpu(val) __bswap_64(val) 94 | #define sys_cpu_to_le64(val) __bswap_64(val) 95 | #define sys_be16_to_cpu(val) (val) 96 | #define sys_cpu_to_be16(val) (val) 97 | #define sys_be24_to_cpu(val) (val) 98 | #define sys_cpu_to_be24(val) (val) 99 | #define sys_be32_to_cpu(val) (val) 100 | #define sys_cpu_to_be32(val) (val) 101 | #define sys_be48_to_cpu(val) (val) 102 | #define sys_cpu_to_be48(val) (val) 103 | #define sys_be64_to_cpu(val) (val) 104 | #define sys_cpu_to_be64(val) (val) 105 | 106 | #endif 107 | 108 | #ifdef __cplusplus 109 | } 110 | #endif 111 | 112 | #endif /* __BYTEORDER_H_ */ 113 | -------------------------------------------------------------------------------- /include/utils/channel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _CHANNEL_H_ 8 | #define _CHANNEL_H_ 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | enum channel_errors { 17 | CHANNEL_ERR_NONE, 18 | CHANNEL_ERR_ALREADY_OPEN, 19 | CHANNEL_ERR_OPEN_FAILED, 20 | CHANNEL_ERR_UNKNOWN_TYPE, 21 | CHANNEL_ERR_NOT_OPEN, 22 | CHANNEL_ERR_OOM, 23 | }; 24 | 25 | enum channel_type { 26 | CHANNEL_TYPE_ERR, 27 | CHANNEL_TYPE_UART, 28 | CHANNEL_TYPE_MSGQ, 29 | CHANNEL_TYPE_FIFO, 30 | CHANNEL_TYPE_UNIX_BUS, 31 | CHANNEL_TYPE_SENTINEL 32 | }; 33 | 34 | /** 35 | * @brief pointer to function that copies received bytes into buffer 36 | * @param data for use by underlying layers. channel_s::data is passed 37 | * @param buf byte array copy incoming data 38 | * @param len sizeof `buf`. Can copy utmost `len` bytes into `buf` 39 | * 40 | * @retval +ve: number of bytes copied on to `bug`. Must be <= `len` 41 | * @retval -ve on errors 42 | */ 43 | typedef int (*channel_receive_fn_t)(void *data, uint8_t *buf, int maxlen); 44 | 45 | /** 46 | * @brief pointer to function that sends byte array into some channel 47 | * @param data for use by underlying layers. channel_s::data is passed 48 | * @param buf byte array to be sent 49 | * @param len number of bytes in `buf` 50 | * 51 | * @retval +ve: number of bytes sent. must be <= `len` 52 | * @retval -ve on errors 53 | */ 54 | typedef int (*channel_send_fn_t)(void *data, uint8_t *buf, int len); 55 | 56 | /** 57 | * @brief pointer to function that drops all bytes in TX/RX fifo 58 | * @param data for use by underlying layers. channel_s::data is passed 59 | */ 60 | typedef void (*channel_flush_fn_t)(void *data); 61 | 62 | struct channel { 63 | int id; 64 | int speed; 65 | char *device; 66 | int is_server; 67 | enum channel_type type; 68 | void *data; 69 | }; 70 | 71 | struct channel_manager { 72 | int open_channels; 73 | hash_map_t channels; 74 | }; 75 | 76 | void channel_manager_init(struct channel_manager *ctx); 77 | 78 | enum channel_type channel_guess_type(const char *desc); 79 | 80 | int channel_open(struct channel_manager *ctx, enum channel_type type, char *device, 81 | int speed, int is_server); 82 | 83 | int channel_get(struct channel_manager *ctx, const char *device, 84 | int *id, void **data, 85 | channel_send_fn_t *send, 86 | channel_receive_fn_t *recv, 87 | channel_flush_fn_t *flush); 88 | 89 | int channel_close(struct channel_manager *ctx, const char *device); 90 | 91 | void channel_manager_teardown(struct channel_manager *ctx); 92 | 93 | #ifdef __cplusplus 94 | } 95 | #endif 96 | 97 | #endif /* _CHANNEL_H_ */ 98 | -------------------------------------------------------------------------------- /include/utils/circbuf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTIL_CIRCBUF_H_ 8 | #define _UTIL_CIRCBUF_H_ 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /** --- Internal methods and structures. DON'T USE --------------------------- */ 17 | typedef struct { 18 | void * const buffer; 19 | int push_count; 20 | int pop_count; 21 | int const size; 22 | int const element_size; 23 | } circbuf_t; 24 | 25 | #define __CIRCBUF_VAR_DEF(type, buf, sz) \ 26 | type buf ## _circbuf_data[sz]; \ 27 | circbuf_t buf = { \ 28 | .buffer = buf ## _circbuf_data, \ 29 | .push_count = 0, \ 30 | .pop_count = 0, \ 31 | .size = sz, \ 32 | .element_size = sizeof(type) \ 33 | }; 34 | 35 | int __circbuf_push(circbuf_t *circbuf, void *elem); 36 | int __circbuf_pop (circbuf_t *circbuf, void *elem, int read_only); 37 | int __circbuf_free_space(circbuf_t *circbuf); 38 | /* -------------------------------------------------------------------------- */ 39 | 40 | /** 41 | * Description: 42 | * Zero slots in circular buffer after a pop. 43 | */ 44 | // #define CRICBUF_CLEAN_ON_POP 45 | 46 | /** 47 | * Description: 48 | * Defines a global circular buffer `buf` of a given type and size. The type 49 | * can be native data types or user-defined data types. 50 | * 51 | * Usage: 52 | * CIRCBUF_DEF(uint8_t, byte_buf, 13); 53 | * CIRCBUF_DEF(struct foo, foo_buf, 10); 54 | */ 55 | #define CIRCBUF_DEF(type, buf, size) \ 56 | __CIRCBUF_VAR_DEF(type, buf, size) \ 57 | int buf ## _push_refd(type *pt) \ 58 | { \ 59 | return __circbuf_push(&buf, pt); \ 60 | } \ 61 | int buf ## _pop_refd(type *pt) \ 62 | { \ 63 | return __circbuf_pop(&buf, pt, 0); \ 64 | } \ 65 | int buf ## _peek_refd(type *pt) \ 66 | { \ 67 | return __circbuf_pop(&buf, pt, 1); \ 68 | } 69 | 70 | /** 71 | * Description: 72 | * Resets the circular buffer offsets to zero. Does not clean the newly freed 73 | * slots. 74 | */ 75 | #define CIRCBUF_FLUSH(buf) \ 76 | do { \ 77 | buf.push_count = 0; \ 78 | buf.pop_count = 0; \ 79 | } while(0) 80 | 81 | /** 82 | * Description: 83 | * Pushes element pointed to by `elem` at the head of circular buffer `buf`. 84 | * This is read-write method, occupancy count increases by one. 85 | * 86 | * Returns (int): 87 | * 0 - Success 88 | * -1 - Out of space 89 | */ 90 | #define CIRCBUF_PUSH(buf, elem) buf ## _push_refd(elem) 91 | 92 | /** 93 | * Description: 94 | * Copies the element at tail of circular buffer `buf` into location pointed 95 | * by `elem`. This method is read-only, does not alter occupancy status. 96 | * 97 | * Returns (int): 98 | * 0 - Success 99 | * -1 - Empty 100 | */ 101 | #define CIRCBUF_PEEK(buf, elem) buf ## _peek_refd(elem) 102 | 103 | /** 104 | * Description: 105 | * Removes the element at tail from circular buffer `buf` and makes it 106 | * available at `elem`. This is read-write method, occupancy count reduces 107 | * by one. 108 | * 109 | * Returns (int): 110 | * 0 - Success 111 | * -1 - Empty 112 | */ 113 | #define CIRCBUF_POP(buf, elem) buf ## _pop_refd(elem) 114 | 115 | /** 116 | * Description: 117 | * Returns the number of free slots in the circular buffer `buf`. 118 | * 119 | * Returns (int): 120 | * 0..N - number of slots available. 121 | */ 122 | #define CIRCBUF_FS(buf) __circbuf_free_space(&buf) 123 | 124 | #ifdef __cplusplus 125 | } 126 | #endif 127 | 128 | #endif /* _UTIL_CIRCBUF_H_ */ 129 | -------------------------------------------------------------------------------- /include/utils/crc32.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_CRC32_H_ 2 | #define _UTILS_CRC32_H_ 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | uint32_t compute_crc32(uint32_t seed, const uint8_t *buffer, size_t length); 12 | 13 | #ifdef __cplusplus 14 | } 15 | #endif 16 | 17 | #endif /* _UTILS_CRC32_H_ */ -------------------------------------------------------------------------------- /include/utils/disjoint_set.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTIL_DISJOINT_SET_H_ 8 | #define _UTIL_DISJOINT_SET_H_ 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #define DISJOINT_SET_MAX 128 15 | 16 | struct disjoint_set { 17 | int max_nodes; 18 | int rank[DISJOINT_SET_MAX]; 19 | int parent[DISJOINT_SET_MAX]; 20 | }; 21 | 22 | int disjoint_set_make(struct disjoint_set *set, int max); 23 | int disjoint_set_find(struct disjoint_set *set, int a); 24 | void disjoint_set_union(struct disjoint_set *set, int a, int b); 25 | int disjoint_set_num_roots(struct disjoint_set *set); 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif /* _UTIL_DISJOINT_SET_H_ */ 32 | -------------------------------------------------------------------------------- /include/utils/event.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | 9 | #ifndef _UTILS_EVENTS_H_ 10 | #define _UTILS_EVENTS_H_ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | typedef struct { 17 | int rfd; 18 | int wfd; 19 | bool initialized; 20 | } event_t; 21 | 22 | int event_init(event_t *e, bool active, bool blocking); 23 | void event_cleanup(event_t *e); 24 | 25 | bool event_set(event_t *e); 26 | bool event_is_set(event_t *e); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | 32 | #endif /* _UTILS_EVENTS_H_ */ 33 | -------------------------------------------------------------------------------- /include/utils/fdutils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTILS_FDUTILS_H_ 8 | #define _UTILS_FDUTILS_H_ 9 | 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | int fcntl_setfl(int fd, int flag); 18 | 19 | ssize_t read_loop(int fd, void *buf, size_t max_len); 20 | ssize_t write_loop(int fd, const void *buf, size_t len); 21 | int flush_fd(int fd); 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | 27 | #endif /* _UTILS_FDUTILS_H_ */ 28 | -------------------------------------------------------------------------------- /include/utils/file.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTIL_FILE_H_ 8 | #define _UTIL_FILE_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | int read_binary_file(const char *path, uint8_t **buf, size_t *size); 19 | int write_binary_file(const char *path, uint8_t *buf, size_t size); 20 | 21 | int file_read_all(FILE *in, char **dataptr, size_t *sizeptr); 22 | char *get_working_directory(void); 23 | char *path_join(const char *p1, const char *p2); 24 | bool file_exists(const char *path); 25 | int is_regular_file(const char *path); 26 | int path_extract(const char *path, char **dir_name, char **base_name); 27 | 28 | char **fs_path_walk(const char *root_dir); 29 | void fs_path_walk_free(char **files); 30 | void fs_path_walk_free(char **files); 31 | size_t file_size(FILE *fp); 32 | bool dir_exists(const char *path); 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | 38 | #endif /* _UTIL_FILE_H_ */ 39 | -------------------------------------------------------------------------------- /include/utils/filo.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_FILO_H_ 2 | #define _UTILS_FILO_H_ 3 | 4 | /* 5 | * Copyright (c) 2020-2021 Abhishek Arora 6 | * 7 | * SPDX-License-Identifier: Apache-2.0 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | typedef struct { 18 | void *buffer; 19 | size_t top; 20 | size_t elem_size; 21 | size_t max_size; 22 | } filo_t; 23 | 24 | #define __FILO_DEF(name, type, sz) \ 25 | type name ## _buffer[sz]; \ 26 | filo_t name = { .buffer = name ## _buffer, \ 27 | .top = 0, \ 28 | .elem_size = sizeof(type),\ 29 | .max_size = sz \ 30 | }; \ 31 | 32 | #define FILO_DEF(name, type, sz) \ 33 | __FILO_DEF(name, type, sz) \ 34 | int __## name ## _push(const type *elem) \ 35 | { \ 36 | return filo_push(&name, (void *)elem); \ 37 | } \ 38 | int __## name ## _pop(type *elem) \ 39 | { \ 40 | return filo_pop(&name, (void *)elem, true); \ 41 | } \ 42 | int __## name ## _peek(type *elem) \ 43 | { \ 44 | return filo_pop(&name, (void *)elem, false); \ 45 | } \ 46 | unsigned __## name ## _get_count(void) \ 47 | { \ 48 | return filo_get_count(&name); \ 49 | } \ 50 | void __## name ## _reset(void) \ 51 | { \ 52 | filo_reset(&name); \ 53 | } 54 | 55 | #define FILO_EXTERN(name, type) \ 56 | extern filo_t name; \ 57 | int __## name ## _push(const type *elem); \ 58 | int __## name ## _pop(type *elem); \ 59 | int __## name ## _peek(type *elem); \ 60 | unsigned __## name ## _get_count(void); \ 61 | void __## name ## _reset(void); 62 | 63 | 64 | /** 65 | * Description: 66 | * Initializes the filo_t pointed to by `pfilo`. 67 | * 68 | * Returns: None 69 | */ 70 | void filo_init(filo_t *pfilo, void *buffer, size_t elem_size, size_t max_size); 71 | 72 | /** 73 | * Description: 74 | * Resets the filo offsets to zero. Does not clean the newly freed 75 | * slots. 76 | */ 77 | void filo_reset(filo_t *pfilo); 78 | 79 | /** 80 | * Description: 81 | * Pushes element pointed to by `elem` at the top of filo `pfilo`. 82 | * This is read-write method, occupancy count increases by one. 83 | * 84 | * Returns (int): 85 | * 0 - Success 86 | * -1 - Out of space 87 | */ 88 | int filo_push(filo_t *pfilo, void *elem); 89 | 90 | /** 91 | * Description: 92 | * Pushes element pointed to by `elem` at the top of filo `name`. 93 | * This is read-write method, occupancy count increases by one. 94 | * 95 | * Returns (int): 96 | * 0 - Success 97 | * -1 - Out of space 98 | */ 99 | #define FILO_PUSH(name, elem) __## name ## _push(elem) 100 | 101 | /** 102 | * Description: 103 | * Removes (if remove is true) the element at top from filo `pfilo` and makes it 104 | * available at `elem`. This is read-write method (if remove is true, 105 | * otherwise it is just read method), occupancy count reduces by one. 106 | * 107 | * Returns (int): 108 | * 0 - Success 109 | * -1 - Empty 110 | */ 111 | int filo_pop(filo_t *pfilo, void *elem, bool remove); 112 | 113 | /** 114 | * Description: 115 | * Peeks the element at top from filo `name` and makes it 116 | * available at `elem`. 117 | * 118 | * Returns (int): 119 | * 0 - Success 120 | * -1 - Empty 121 | */ 122 | #define FILO_PEEK(name, elem) __## name ## _peek(elem) 123 | 124 | /** 125 | * Description: 126 | * Removes the element at top from filo `name` and makes it 127 | * available at `elem`. This is read-write method, 128 | * occupancy count reduces by one. 129 | * 130 | * Returns (int): 131 | * 0 - Success 132 | * -1 - Empty 133 | */ 134 | #define FILO_POP(name, elem) __## name ## _pop(elem) 135 | 136 | 137 | /** 138 | * Description: 139 | * Returns the occupancy count of filo 'pfilo'. 140 | * 141 | * Returns (int): 142 | * 0 - Success 143 | * 0..N - number of slots occupied. 144 | */ 145 | size_t filo_get_count(filo_t *pfilo); 146 | 147 | /** 148 | * Description: 149 | * Returns the occupancy count of filo 'name'. 150 | * 151 | * Returns (int): 152 | * 0 - Success 153 | * 0..N - number of slots occupied. 154 | */ 155 | #define FILO_GET_COUNT(name) __## name ## _get_count() 156 | 157 | /** 158 | * Description: 159 | * Returns the number of free slots of filo 'pfilo'. 160 | * 161 | * Returns (int): 162 | * 0 - Success 163 | * 0..N - number of slots free. 164 | */ 165 | size_t filo_get_free_space(filo_t *pfilo); 166 | 167 | /** 168 | * Description: 169 | * Dynamically allocate the filo having elem_size of 'elem_size' and maximum number of 170 | * elements 'max_size'. 171 | * 172 | * Returns (int): 173 | * 0 - Success 174 | * 0..N - number of slots occupied. 175 | */ 176 | filo_t *filo_alloc(size_t elem_size, size_t max_size); 177 | 178 | /** 179 | * Description: 180 | * Frees dynamically allocated filo 'pfilo'. 181 | */ 182 | void filo_free(filo_t *pfilo); 183 | 184 | 185 | 186 | #ifdef __cplusplus 187 | } 188 | #endif 189 | 190 | #endif /* _UTILS_FILO_H_ */ 191 | -------------------------------------------------------------------------------- /include/utils/hashmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _HASHMAP_H_ 8 | #define _HASHMAP_H_ 9 | 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | typedef struct hash_map_item_s hash_map_item_t; 18 | typedef uint32_t hash_t; 19 | 20 | struct hash_map_item_s { 21 | hash_map_item_t *next; 22 | hash_t hash; 23 | char *key; 24 | void *val; 25 | }; 26 | 27 | typedef struct { 28 | hash_map_item_t **pool; 29 | size_t capacity; 30 | size_t count; 31 | } hash_map_t; 32 | 33 | typedef void (*hash_map_callback_t)(const char *key, void *val); 34 | 35 | void hash_map_init(hash_map_t *map); 36 | void hash_map_free(hash_map_t *map, hash_map_callback_t cb); 37 | hash_t hash_map_insert(hash_map_t *map, const char *key, void *val); 38 | void *hash_map_get(hash_map_t *map, const char *key, hash_t key_hash); 39 | void *hash_map_delete(hash_map_t *map, const char *key, hash_t key_hash); 40 | 41 | /* --- iterators --- */ 42 | 43 | typedef struct { 44 | size_t pos; 45 | void *item; 46 | hash_map_t *map; 47 | } hash_map_iterator_t; 48 | 49 | void hash_map_it_init(hash_map_iterator_t *it, hash_map_t *map); 50 | int hash_map_it_next(hash_map_iterator_t *it, char **key, void **val); 51 | 52 | #define HASH_MAP_FOREACH(map, key_ref, val_ref) \ 53 | hash_map_iterator_t it; \ 54 | hash_map_it_init(&it, map); \ 55 | while (hash_map_it_next(&it, key_ref, (void **)val_ref) == 0) 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | 61 | #endif /* _HASHMAP_H_ */ 62 | -------------------------------------------------------------------------------- /include/utils/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #ifndef _UTILS_LIST_H_ 9 | #define _UTILS_LIST_H_ 10 | 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | typedef struct node_s node_t; 18 | 19 | struct node_s { 20 | node_t *next; 21 | node_t *prev; 22 | }; 23 | 24 | struct list_s { 25 | node_t *head; 26 | node_t *tail; 27 | }; 28 | 29 | typedef struct list_s list_t; 30 | 31 | #define LIST_FOREACH(list, node) \ 32 | for(node_t *node = (list)->head; node != NULL; node = node->next) 33 | 34 | void list_init(list_t *list); 35 | void list_append(list_t *list, node_t *node); 36 | void list_appendleft(list_t *list, node_t *node); 37 | int list_pop(list_t *list, node_t **node); 38 | int list_popleft(list_t *list, node_t **node); 39 | 40 | void list_remove_node(list_t *list, node_t *node); 41 | int list_remove_nodes(list_t *list, node_t *start, node_t *end); 42 | void list_insert_node(list_t *list, node_t *after, node_t *newNode); 43 | int list_insert_nodes(list_t *list, node_t *after, node_t *start, node_t *end); 44 | 45 | /*--- singly-linked list ---*/ 46 | 47 | typedef struct snode_s snode_t; 48 | 49 | struct snode_s { 50 | snode_t *next; 51 | }; 52 | 53 | struct slist_s { 54 | snode_t *head; 55 | }; 56 | 57 | typedef struct slist_s slist_t; 58 | 59 | void slist_init(slist_t *list); 60 | void slist_append(slist_t *list, snode_t *after, snode_t *node); 61 | void slist_appendleft(slist_t *list, snode_t *node); 62 | int slist_pop(slist_t *list, snode_t *after, snode_t **node); 63 | int slist_popleft(slist_t *list, snode_t **node); 64 | 65 | int slist_remove_node(slist_t *list, snode_t *node); 66 | void slist_insert_node(slist_t *list, snode_t *after, snode_t *newNode); 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | 72 | #endif /* _UTILS_LIST_H_ */ 73 | -------------------------------------------------------------------------------- /include/utils/logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #ifndef _LOGGER_H_ 9 | #define _LOGGER_H_ 10 | 11 | #include 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #define LOGGER_NAME_MAXLEN 32 19 | 20 | /** 21 | * @brief A printf() like method that will be used to write out log lines. 22 | * 23 | * @param fmt C printf() style format string. See man 3 printf 24 | * 25 | * @retval number of characters written to the log stream 26 | */ 27 | typedef int (*log_puts_fn_t)(const char *buf); 28 | 29 | /** 30 | * @brief A callback function to be used with external loggers 31 | * 32 | * @param log_level A syslog style log level. See `enum log_levels` 33 | * @param file Relative path to file which produced the log message 34 | * @param line Line number in `file` which produced the log message 35 | * @param msg The log message 36 | */ 37 | typedef void (*log_callback_fn_t)(int log_level, const char *file, 38 | unsigned long line, const char *msg); 39 | 40 | typedef struct { 41 | char name[LOGGER_NAME_MAXLEN]; 42 | const char *root_path; 43 | int log_level; 44 | uint32_t flags; 45 | FILE *file; 46 | log_puts_fn_t puts; 47 | log_callback_fn_t cb; 48 | } logger_t; 49 | 50 | enum log_levels { 51 | LOG_EMERG, 52 | LOG_ALERT, 53 | LOG_CRIT, 54 | LOG_ERR, 55 | LOG_WARNING, 56 | LOG_NOTICE, 57 | LOG_INFO, 58 | LOG_DEBUG, 59 | LOG_MAX_LEVEL 60 | }; 61 | 62 | #define LOGGER_FLAG_NONE 0x00000000 63 | #define LOGGER_FLAG_NO_COLORS 0x00000001 64 | 65 | /* Don't call this method directly. Use LOG_* macros below */ 66 | int __logger_log(logger_t *ctx, int log_level, const char *file, 67 | unsigned long line, const char *fmt, ...); 68 | 69 | int logger_init(logger_t *ctx, int log_level, const char *name, 70 | const char *root_path, log_puts_fn_t puts_fn, FILE *file, 71 | log_callback_fn_t cb, int flags); 72 | void logger_get_default(logger_t *ctx); 73 | void logger_set_default(logger_t *logger); 74 | void logger_set_log_level(logger_t *ctx, int log_level); 75 | void logger_set_put_fn(logger_t *ctx, log_puts_fn_t fn); 76 | void logger_set_file(logger_t *ctx, FILE *f); 77 | void logger_set_name(logger_t *ctx, const char *name); 78 | 79 | #ifndef USE_CUSTOM_LOGGER 80 | #define LOG_EM(...) __logger_log(NULL, LOG_EMERG, __FILE__, __LINE__, __VA_ARGS__) 81 | #define LOG_ALERT(...) __logger_log(NULL, LOG_ALERT, __FILE__, __LINE__, __VA_ARGS__) 82 | #define LOG_CRIT(...) __logger_log(NULL, LOG_CRIT, __FILE__, __LINE__, __VA_ARGS__) 83 | #define LOG_ERR(...) __logger_log(NULL, LOG_ERR, __FILE__, __LINE__, __VA_ARGS__) 84 | #define LOG_INF(...) __logger_log(NULL, LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) 85 | #define LOG_WRN(...) __logger_log(NULL, LOG_WARNING,__FILE__, __LINE__, __VA_ARGS__) 86 | #define LOG_NOT(...) __logger_log(NULL, LOG_NOTICE, __FILE__, __LINE__, __VA_ARGS__) 87 | #define LOG_DBG(...) __logger_log(NULL, LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) 88 | #endif 89 | 90 | #define LOG_PRINT(...) __logger_log(NULL, LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) 91 | 92 | #ifdef __cplusplus 93 | } 94 | #endif 95 | 96 | #endif /* _LOGGER_H_ */ 97 | -------------------------------------------------------------------------------- /include/utils/memory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTILS_MEMORY_H_ 8 | #define _UTILS_MEMORY_H_ 9 | 10 | #include 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | /** 17 | * @brief Check p to be not NULL before calling safe_free() 18 | * 19 | * @param p pointer to be free-ed 20 | */ 21 | void safe_free(void *p); 22 | 23 | /** 24 | * @brief safe_* variants of the standard alloc methods do a check on the 25 | * returned pointer and will call exit() if any of the returned NULL. The 26 | * safe_free() method will check if pointer is NULL before calling safe_free(). 27 | */ 28 | void safe_free(void *p); 29 | void *safe_malloc(size_t size); 30 | void *safe_calloc(size_t count, size_t size); 31 | void *safe_realloc(void *data, size_t size); 32 | void *safe_strdup(const char *s); 33 | void *safe_realloc_zero(void *data, size_t old_size, size_t new_size); 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | 39 | #endif /* _UTILS_MEMORY_H_ */ 40 | -------------------------------------------------------------------------------- /include/utils/pcap_gen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTILS_PCAP_GEN_H_ 8 | #define _UTILS_PCAP_GEN_H_ 9 | 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | typedef struct { 18 | FILE *file; 19 | size_t offset; 20 | void *cache; 21 | size_t num_packets; 22 | } pcap_t; 23 | 24 | pcap_t *pcap_start(char *path, uint32_t max_packet_size, uint32_t link_type); 25 | int pcap_add(pcap_t *cap, uint8_t *capture_data, uint32_t length); 26 | int pcap_stop(pcap_t *cap); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | 32 | #endif /* _UTILS_PCAP_GEN_H_ */ 33 | -------------------------------------------------------------------------------- /include/utils/procutils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTIL_PROCUTILS_H_ 8 | #define _UTIL_PROCUTILS_H_ 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /* 15 | * read_pid() - Reads PID from `file`. The file's content must just be 16 | * be PID number, as written by a call to write_pid(). On success, copies 17 | * the PID read from file into `pid`. 18 | * 19 | * Returns: 20 | * 0 - Success 21 | * -1 - Failure 22 | */ 23 | int read_pid(const char *file, int *pid); 24 | 25 | /* 26 | * write_pid() - Writes the calling process's PID to the file specified. 27 | * 28 | * Returns: 29 | * 0 - Success 30 | * -1 - Failure 31 | */ 32 | int write_pid(const char *file); 33 | 34 | /* 35 | * o_redirect() - Redirects stdout/stderr to file specified. 36 | * 37 | * Params: 38 | * mode - Bit mask. 39 | * bit-0: STDOUT 40 | * bit-1: STDERR 41 | * file - Path to log file. if null, close() is called on the 42 | * corresponding FDs (from mode above). 43 | * Examples: 44 | * o_redirect(1, "/tmp/my_log.txt"); // redirects stdout to "/tmp/my_log.txt" 45 | * o_redirect(3, "/tmp/my_log.txt"); // Same as above; but redirects stdout and strerr 46 | * o_redirect(3, NULL); // close both stdout and strerr. Same as `> /dev/null 1>&2` 47 | */ 48 | int o_redirect(int mode, const char *file); 49 | 50 | /* 51 | * pid_of() - Return the pid of "exe_name" 52 | * 53 | * Params: 54 | * exe_name - Name of the executable of which pid should be returned. 55 | * pomit_arr - Array of pid not to be considered. 56 | * arr_len - Size of pomit_arr 57 | * 58 | * * Returns: 59 | * 0 - Failed. No Process with exe_name found 60 | * > 0 - PID of the process having name "exe_name" 61 | * 62 | * Examples: 63 | * pid_of("test-utils", NULL, 0); // return the pid of the test 64 | * 65 | * Limitations: 66 | * Currently, it doesn't work with scripts or kernel threads. 67 | */ 68 | unsigned pid_of(const char* exe_name, unsigned *pomit_arr, size_t arr_len); 69 | 70 | 71 | /* 72 | * any_pid_of() - Return the pid of one of the process having "exe_name" 73 | * 74 | * Params: 75 | * exe_name - Name of the executable of which pid should be returned. 76 | * 77 | * * Returns: 78 | * 0 - Failed. No Process with exe_name found 79 | * > 0 - PID of the process having name "exe_name" 80 | * 81 | * Examples: 82 | * any_pid_of("test-utils"); // return the pid of the test 83 | * 84 | * Limitations: 85 | * Currently, it doesn't work with scripts or kernel threads. 86 | */ 87 | unsigned any_pid_of(const char* exe_name); 88 | 89 | /* 90 | * parse_proc_cmdline0() - Return the arg0 of the process having pid 'pid' 91 | * 92 | * Params: 93 | * pid - PID of the process for which arg0 will be returned 94 | */ 95 | char *parse_proc_cmdline(unsigned pid, int pos); 96 | 97 | 98 | #ifdef __cplusplus 99 | } 100 | #endif 101 | 102 | #endif /* _UTIL_PROCUTILS_H_ */ 103 | -------------------------------------------------------------------------------- /include/utils/queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #ifndef _UTILS_QUEUE_H_ 9 | #define _UTILS_QUEUE_H_ 10 | 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #define queue_node_t node_t 18 | 19 | typedef struct { 20 | list_t list; 21 | } queue_t; 22 | 23 | void queue_init(queue_t *queue); 24 | void queue_enqueue(queue_t *queue, queue_node_t *node); 25 | int queue_dequeue(queue_t *queue, queue_node_t **node); 26 | int queue_peek_last(queue_t *queue, queue_node_t **node); 27 | int queue_peek_first(queue_t *queue, queue_node_t **node); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | #endif /* _UTILS_QUEUE_H_ */ 34 | -------------------------------------------------------------------------------- /include/utils/random.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTILS_RANDOM_H_ 8 | #define _UTILS_RANDOM_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | /** 19 | * @brief Get the secure random bytes from the systems 20 | * 21 | * @param out Buffer to fill random bytes 22 | * @param len Number of random bytes to be filled to `out` 23 | * @return true if successful, false otherwise. 24 | */ 25 | bool get_random_bytes(uint8_t *out, size_t len); 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif -------------------------------------------------------------------------------- /include/utils/serial.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #ifndef _SERIAL_H_ 9 | #define _SERIAL_H_ 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | 17 | struct serial { 18 | int fd; 19 | struct termios new_termios; 20 | struct termios old_termios; 21 | }; 22 | 23 | struct serial *serial_open(const char *device, int baud, const char *mode); 24 | void serial_close(struct serial *ctx); 25 | int serial_read(struct serial *ctx, unsigned char *buf, int size); 26 | int serial_write(struct serial *ctx, unsigned char *buf, int size); 27 | void serial_flush_rx(struct serial *ctx); 28 | void serial_flush_tx(struct serial *ctx); 29 | void serial_flush(struct serial *ctx); 30 | 31 | /* line control methods */ 32 | int serial_get_dcd(struct serial *ctx); 33 | int serial_get_rng(struct serial *ctx); 34 | int serial_get_cts(struct serial *ctx); 35 | int serial_get_dsr(struct serial *ctx); 36 | void serial_assert_dtr(struct serial *ctx, int state); 37 | void serial_assert_rts(struct serial *ctx, int state); 38 | 39 | #ifdef __cplusplus 40 | } /* extern "C" */ 41 | #endif 42 | 43 | #endif /* _SERIAL_H_ */ 44 | -------------------------------------------------------------------------------- /include/utils/slab.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #ifndef _UTILS_SLAB_H_ 9 | #define _UTILS_SLAB_H_ 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | typedef struct { 21 | uint8_t *blob; 22 | size_t size; 23 | size_t count; 24 | } slab_t; 25 | 26 | /** 27 | * @brief Initializes a resource pool of slabs (out of `blob`) that are 28 | * at-least `slab_size` bytes long. 29 | * 30 | * @return -1 on errors 31 | * @return number of slabs issuable from `blob` on success 32 | */ 33 | int slab_init(slab_t *slab, size_t slab_size, 34 | uint8_t *blob, size_t blob_size); 35 | 36 | /** 37 | * @brief Allocates a slab of memory from the resource pool held at 38 | * slab_t. The allocated block is at least `size` bytes large is 39 | * guaranteed to be aligned to a power the nearest power of 2. 40 | * 41 | * @return -1 on errors 42 | * @return 0 on success 43 | */ 44 | int slab_alloc(slab_t *slab, void **block); 45 | 46 | /** 47 | * @brief Releases a slab that was previously allocated by a call 48 | * to slab_alloc(). This method can fail if the pointer passed did 49 | * not belong to the slab pool of slab_t. 50 | * 51 | * @return -1 on errors 52 | * @return 0 on success 53 | */ 54 | int slab_free(slab_t *slab, void *block); 55 | 56 | #ifdef __cplusplus 57 | } 58 | #endif 59 | 60 | #endif /* _UTILS_SLAB_H_ */ 61 | -------------------------------------------------------------------------------- /include/utils/sockutils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTILS_SOCKUTILS_H_ 8 | #define _UTILS_SOCKUTILS_H_ 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | // Provide macros to old function names so as to not break existing 15 | // users. TODO: remove it in next release. 16 | #define unix_socket_listen sock_unix_listen 17 | #define unix_socket_connect sock_unix_connect 18 | 19 | int sock_unix_listen(const char *path, int max_clients); 20 | int sock_unix_connect(const char *path); 21 | 22 | int sock_stream_connect(const char *host, int port); 23 | int sock_stream_listen(int port, int nr_clients); 24 | int sock_wait(int listening_socket_fd); 25 | int sock_shutdown(int listening_socket_fd); 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif /* _UTILS_SOCKUTILS_H_ */ -------------------------------------------------------------------------------- /include/utils/stack.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #ifndef _UTILS_STACK_H_ 9 | #define _UTILS_STACK_H_ 10 | 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | typedef struct { 18 | slist_t list; 19 | } stack_t; 20 | 21 | #define stack_node_t snode_t 22 | 23 | void stack_init(stack_t *stack); 24 | void stack_push(stack_t *stack, stack_node_t *node); 25 | int stack_pop(stack_t *stack, stack_node_t **node); 26 | int stack_get_top(stack_t *stack, stack_node_t **node); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | 32 | #endif /* _UTILS_STACK_H_ */ 33 | -------------------------------------------------------------------------------- /include/utils/strlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTIL_STRLIB_H_ 8 | #define _UTIL_STRLIB_H_ 9 | 10 | #include 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #define STRING_ALLOCATED 0x00000001 18 | 19 | typedef struct { 20 | uint32_t flags; 21 | char *buf; 22 | size_t len; 23 | size_t max_len; 24 | } string_t; 25 | 26 | /** 27 | * Description: 28 | * Defines a instance of string_t of specified length. 29 | * 30 | * Example: 31 | * GLOBAL_STRING_DEF(my_string, 100); 32 | */ 33 | #define STRING_DEF(str, length) \ 34 | char str ## _buf[length+1]={0}; \ 35 | string_t str = { \ 36 | .buf = str ## _buf, \ 37 | .len = 0, \ 38 | .max_len = length \ 39 | } 40 | 41 | /** 42 | * String Ops Modes: 43 | * 44 | * - "a" (append): bytes are appended to the existing contents of 45 | * the string. If there is not enough space in the string, an 46 | * error is returned. 47 | * 48 | * - "af" (append, fill): same as "a" but fills as many bytes as 49 | * possible into the string. 50 | * 51 | * - "c" (create/copy): bytes are copied to the beginning of the 52 | * string. If there is not enough space in the string, an 53 | * error is returned. 54 | * 55 | * - "cf" (copy, fill): same as "c" but fills as many bytes as 56 | * possible into the string. 57 | */ 58 | 59 | /** 60 | * Description: 61 | * Do formated print operations (as with printf) on string_t. 62 | * 63 | * Note: 64 | * Use `mode` as defined above in "String Ops Modes" section. 65 | * 66 | * Example: 67 | * string_printf(&my_string, "c", "Age: %d\n", 28); 68 | * // now, my_string == "Age: 28\n" 69 | */ 70 | int string_printf(string_t *s, const char *mode, const char *fmt, ...); 71 | 72 | /** 73 | * Description: 74 | * Copy contents of `buf` of length `len` into string `s`. 75 | * 76 | * Note: 77 | * Use `mode` as defined above in "String Ops Modes" section. 78 | * 79 | * Example: 80 | * string_copy(&my_string, "c", "Hello", 5); 81 | * // now, my_string == "Hello" 82 | * string_copy(&my_string, "a", " World", 6); 83 | * // now, my_string == "Hello World" 84 | */ 85 | int string_copy(string_t *s, const char *mode, const char *str, size_t len); 86 | 87 | void string_create(string_t *s, const char *buf, size_t len); 88 | void string_clone(string_t *dest, const string_t *src); 89 | void string_destroy(string_t *s); 90 | int string_resize(string_t *s, size_t new_len); 91 | int string_merge(string_t *primary, string_t *secondary); 92 | 93 | /* macro helpers */ 94 | 95 | #define str_flush(s) \ 96 | do { \ 97 | (s)->buf[0] = 0; \ 98 | (s)->len = 0; \ 99 | } while(0) 100 | #define str_const_copy(s, str) string_copy(s, "c", str, strlen(s)) 101 | #define str_const_append(s, str) string_copy(s, "a", str, strlen(str)) 102 | 103 | #ifdef __cplusplus 104 | } 105 | #endif 106 | 107 | #endif /* _UTIL_STRLIB_H_ */ 108 | -------------------------------------------------------------------------------- /include/utils/strutils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTIL_STRUTIL_H_ 8 | #define _UTIL_STRUTIL_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | /* 19 | * atohstr() - Converts an array of bytes to its hexadecimal string 20 | * representation. 21 | * 22 | * Usage: 23 | * uint8_t arr[4] = { 0xca, 0xfe, 0xba, 0xbe }; 24 | * char hstr[16]; 25 | * if (atohstr(hstr, arr, 4) == 0) { 26 | * // hstr = "cafebabe\0"; 27 | * } 28 | * 29 | * Note: 30 | * sizeof passed char *hstr, has to be atleast (2 * arr_len) + 1. 31 | */ 32 | int atohstr(char *hstr, const uint8_t *arr, const int arr_len); 33 | 34 | /* 35 | * hstrtoa() - Converts a hexadecimal string to it's value as array of 36 | * bytes. 37 | * 38 | * Usage: 39 | * uint8_t arr[4]; 40 | * const char *hstr = "cafebabe"; 41 | * if (hstrtoa(arr, hstr) == 0) { 42 | * // arr[4] = { 0xca, 0xfe, 0xba, 0xbe }; 43 | * } 44 | * 45 | * Note: 46 | * sizeof uint8_t *arr has to be atleast half of strlen(hstr) 47 | */ 48 | int hstrtoa(uint8_t *arr, const char *hstr); 49 | 50 | /* 51 | * safe_atoi() - A wrapper for atoi() that returns -ve on non number-ish 52 | * strings. This can be used to distinguish "0" from "A" as both would 53 | * return 0 by the bare atoi()'s contract. 54 | */ 55 | int safe_atoi(const char *a, int *i); 56 | 57 | /* 58 | * safe_strncpy() - A wrapper for strnlen() that guarantees null 59 | * terminated copy to dest. If strlen(src) > size, then only size-1 60 | * chars are copied to dest and a terminating '\0' is added. 61 | */ 62 | char *safe_strncpy(char* dest, const char* src, size_t size); 63 | 64 | /** 65 | * @brief rstrip(), lstrip(), strip() - String space trim methods, as defined 66 | * in python3 programming language. 67 | * 68 | * @return returns the length of the modified string. In the case of lstrip(), 69 | * if the string was not modified, -ve error is returned to indicate this. This 70 | * is done to avoid a needless O(N) strlen() call. 71 | */ 72 | int rstrip(char *str); 73 | int lstrip(char *str); 74 | int strip(char *str); 75 | 76 | /** 77 | * @brief Removes any tailing \r or \n chars. 78 | * 79 | * @return Length of modified string. 80 | */ 81 | size_t chomp(char *str); 82 | 83 | /** 84 | * @brief lstrip() without shifting chars but looses the passed pointer. This 85 | * can be used on local non-alloced refs. 86 | */ 87 | #define lstrip_soft(p) \ 88 | do { if (p) while (*p != '\0' && *p == ' ') p++; } while (0) 89 | 90 | /** 91 | * @brief Remove all occurrence of of c in str 92 | * 93 | * @param str source string 94 | * @param c char to be removed 95 | */ 96 | void remove_all(char *str, char c); 97 | 98 | /** 99 | * @brief Splits `buf` by `sep` (as defined in strtok) into char ** 100 | * and adds tailing NULL pointer into `tokens` 101 | * 102 | * @param buf source string 103 | * @param sep array of chars that mark separation in `buf` (see strtok(3)). 104 | * @param tokens allocated array of char ptrs. Must be freed by caller. 105 | * 106 | * @return -1 on errors 107 | * @return 0 on success 108 | */ 109 | int split_string(char *buf, char *sep, char ***tokens); 110 | 111 | /** 112 | * @brief Return the number of times c occurs in null terminated string s. 113 | */ 114 | int strcntchr(char *s, char c); 115 | 116 | /** 117 | * @brief Checks if string poited to by s is empty. 118 | * 119 | * This functions evaluates to true under the following conditions: 120 | * - pointer s is NULL 121 | * - s[0] == '\0' 122 | * - s has only whitespace chars followed by a '\0' 123 | * 124 | * @returns 1 when s is empty (see conditions) 125 | * @returns 0 when string has other non-whitespace chars 126 | */ 127 | int strisempty(char *s); 128 | 129 | /** 130 | * @brief Compute a 32/64 bit hash value for str upto len chars or upto the 131 | * null char. Trick: for computing hash for null terminated strings len can 132 | * be set to -ve instead of passing strlen(s) explicitly. 133 | * 134 | * @return 32-bit hash code 135 | */ 136 | uint32_t hash32_djb2(const char *str, int len); 137 | uint32_t hash32_fnv(const char *str, int len); 138 | uint64_t poly_hash(const char *str, int len); 139 | #define hash32 hash32_djb2 140 | #define hash64 poly_hash 141 | /** 142 | * @brief A strsep() clone. See `man 3 strsep` for more details. str_sep() is 143 | * cloned here because strsep() is not guaranteed to be available everywhere. 144 | */ 145 | char *str_sep(char **str, const char *sep); 146 | 147 | /** 148 | * @brief Return a count of tokens in `str` when split by chars in `sep`. A 149 | * token is as it is defined (and returned) by tokenizing method such as 150 | * strtok() or strsep(). 151 | */ 152 | size_t str_sep_count(const char *str, const char *sep); 153 | 154 | /** 155 | * @brief Transform the input null-terminated string to upper/lower case. 156 | * Only alphapbets are transformed other characters are left as-is. 157 | */ 158 | void to_upper(char *s); 159 | void to_lower(char *s); 160 | 161 | #ifdef __cplusplus 162 | } 163 | #endif 164 | 165 | #endif /* _UTIL_STRUTIL_H_ */ 166 | -------------------------------------------------------------------------------- /include/utils/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTILS_UTILS_H_ 8 | #define _UTILS_UTILS_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #ifndef NULL 19 | #define NULL ((void *)0) 20 | #endif 21 | 22 | #ifndef TRUE 23 | #define TRUE (1) 24 | #endif 25 | 26 | #ifndef FALSE 27 | #define FALSE (0) 28 | #endif 29 | 30 | #define BYTE_0(x) (uint8_t)(((x) >> 0) & 0xFF) 31 | #define BYTE_1(x) (uint8_t)(((x) >> 8) & 0xFF) 32 | #define BYTE_2(x) (uint8_t)(((x) >> 16) & 0xFF) 33 | #define BYTE_3(x) (uint8_t)(((x) >> 24) & 0xFF) 34 | 35 | #ifndef BIT 36 | #define BIT(n) (1ull << (n)) 37 | #endif 38 | #define MASK(n) (BIT((n) + 1) - 1) 39 | #define BIT_IS_SET(m, n) (bool)((m) & BIT(n)) 40 | #define BIT_SET(m, n) ((m) |= BIT(n)) 41 | #define BIT_CLEAR(m, n) ((m) &= ~BIT(n)) 42 | #define BIT_FLIP(m, n) ((m) ^= BIT(n)) 43 | #define BIT_TEST_SET(m, n) ({ BIT_IS_SET(m, n) ? false : (bool) BIT_SET(m, n) }) 44 | 45 | #define ARG_UNUSED(x) (void)(x) 46 | 47 | #define STR(x) #x 48 | #define XSTR(x) STR(x) 49 | #define STRINGIFY(x) #x 50 | 51 | #define ROUND_UP(x, y) ((x + y - 1) & ~ (y - 1)) 52 | 53 | #define MATH_MOD(a, b) (((a % b) + b) % b) 54 | 55 | #define IS_POW2(n) ((n) & ((n) - 1)) 56 | 57 | #define ARRAY_SIZEOF(x) \ 58 | (sizeof(x) / sizeof(x[0])) 59 | 60 | #define ARRAY_BASE(ptr, type, offset) \ 61 | ((char *)(ptr)) - ((sizeof(type)) * offset) 62 | 63 | #define OFFSET_OF(type, field) (size_t)(&((type *)(0))->field) 64 | 65 | #define CONTAINER_OF(ptr, type, field) \ 66 | ((type *)(((char *)(ptr)) - OFFSET_OF(type, field))) 67 | 68 | #define MAX(a,b) ({ \ 69 | __typeof__ (a) _a = (a); \ 70 | __typeof__ (b) _b = (b); \ 71 | _a > _b ? _a : _b; \ 72 | }) 73 | 74 | #define MIN(a,b) ({ \ 75 | __typeof__ (a) _a = (a); \ 76 | __typeof__ (b) _b = (b); \ 77 | _a > _b ? _b : _a; \ 78 | }) 79 | 80 | #define SWAP(a,b) { \ 81 | __typeof__ (a) _tmp; \ 82 | _tmp = a; \ 83 | b = a; \ 84 | a = _tmp; \ 85 | } 86 | 87 | #define __READ_ONCE(x) (*(volatile typeof(x) *)&(x)) 88 | #define READ_ONCE(x) ({ \ 89 | typeof(x) *__xp = &(x); \ 90 | typeof(x) __x = __READ_ONCE(*__xp); \ 91 | __x; \ 92 | }) 93 | 94 | #define WRITE_ONCE(x, val) do { \ 95 | typeof(x) *__xp = &(x); \ 96 | *(volatile typeof(x) *)__xp = (val); \ 97 | } while (0) 98 | 99 | #define ABS(x) ((x) >= 0 ? (x) : -(x)) 100 | 101 | /* config_enabled() from the kernel */ 102 | #define __IS_ENABLED1(x) __IS_ENABLED2(__XXXX ## x) 103 | #define __XXXX1 __YYYY, 104 | #define __IS_ENABLED2(y) __IS_ENABLED3(y 1, 0) 105 | #define __IS_ENABLED3(_i, val, ...) val 106 | 107 | #define IS_ENABLED(x) __IS_ENABLED1(x) 108 | 109 | /* gcc attribute shorthands */ 110 | #ifndef __fallthrough 111 | #if __GNUC__ >= 7 112 | #define __fallthrough __attribute__((fallthrough)) 113 | #else 114 | #define __fallthrough 115 | #endif 116 | #endif 117 | 118 | #ifndef __packed 119 | #define __packed __attribute__((__packed__)) 120 | #endif 121 | 122 | #ifdef __GNUC__ 123 | #define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) 124 | #endif 125 | 126 | #ifdef _MSC_VER 127 | #define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop)) 128 | #endif 129 | 130 | #ifdef __weak 131 | #undef __weak 132 | #endif 133 | 134 | #if (defined(_WIN32) || defined(_WIN64)) 135 | #include 136 | #define isatty _isatty /* from io.h */ 137 | #define fileno _fileno 138 | #define __format_printf(x, y) 139 | #define __noreturn __declspec(noreturn) 140 | #define __weak 141 | #define __unreachable() __assume(0) 142 | #define likely(p) (p) 143 | #define unlikely(p) (p) 144 | #define PATH_SEPARATOR '\\' 145 | #else 146 | #define __format_printf(x, y) __attribute__((format(printf, x, y))) 147 | #define __noreturn __attribute__((noreturn)) 148 | #define __weak __attribute__((weak)) 149 | #define __unreachable() __builtin_unreachable() 150 | #define likely(p) __builtin_expect(!!(p), 1) 151 | #define unlikely(p) __builtin_expect(!!(p), 0) 152 | #define PATH_SEPARATOR '/' 153 | #endif 154 | 155 | /** 156 | * @brief Return random number between 0 and `limit` both inclusive. 157 | * 158 | * Note: the random number generator must be pre-seeded. 159 | */ 160 | int randint(int limit); 161 | 162 | /** 163 | * @brief Rounds up 32-bit v to nearest power of 2. If v is already a power 164 | * of 2 it is returned unmodified. 165 | */ 166 | uint32_t round_up_pow2(uint32_t v); 167 | 168 | /** 169 | * @brief Retruns number of digits in a given number in its decimal form. 170 | */ 171 | int num_digits_in_number(int num); 172 | 173 | /** 174 | * @brief Dumps an array of bytes in HEX and ASCII formats for debugging. `head` 175 | * is string that is printed before the actual bytes are dumped. 176 | * 177 | * Example: 178 | * int len; 179 | * uint8_t data[MAX_LEN]; 180 | * len = get_data_from_somewhere(data, MAX_LEN); 181 | * hexdump(data, len, "Data From Somewhere"); 182 | */ 183 | void hexdump(const void *data, size_t len, const char *fmt, ...); 184 | 185 | /** 186 | * @brief Get the time in micro seconds. 187 | */ 188 | int64_t usec_now(); 189 | 190 | /** 191 | * @brief Get time elapsed in micro seconds since `last`. Used along with 192 | * usec_now(). 193 | */ 194 | int64_t usec_since(int64_t last); 195 | 196 | /** 197 | * @brief Get the time in milli seconds. 198 | */ 199 | int64_t millis_now(); 200 | 201 | /** 202 | * @brief Get time in seconds and micro_seconds 203 | */ 204 | void get_time(uint32_t *seconds, uint32_t *micro_seconds); 205 | 206 | /** 207 | * @brief Addd an ISO 8601 datetime string to `buf` for upto `size` bytes. 208 | * 209 | * Note: The format is YYYY-MM-DDThh:mm:ssZ, so size must alteast be 21. 210 | */ 211 | int add_iso8601_utc_datetime(char *buf, size_t size); 212 | 213 | /** 214 | * @brief Get time elapsed in milli seconds since `last`. Used along with 215 | * millis_now(). 216 | */ 217 | int64_t millis_since(int64_t last); 218 | 219 | /** 220 | * @brief Print the stack trace 221 | */ 222 | void dump_trace(void); 223 | 224 | static inline bool char_is_space(int c) 225 | { 226 | unsigned char d = (unsigned char)(c - 9); 227 | return (0x80001FU >> (d & 31)) & (1U >> (d >> 5)); 228 | } 229 | 230 | static inline bool char_is_digit(int c) 231 | { 232 | return (unsigned int)(('0' - 1 - c) & (c - ('9' + 1))) >> (sizeof(c) * 8 - 1); 233 | } 234 | 235 | static inline bool char_is_alpha(int c) 236 | { 237 | return (unsigned int)(('a' - 1 - (c | 32)) & ((c | 32) - ('z' + 1))) >> (sizeof(c) * 8 - 1); 238 | } 239 | 240 | inline uint8_t u8_bit_reverse(uint8_t b) 241 | { 242 | b = (((b & 0xaa) >> 1) | ((b & 0x55) << 1)); 243 | b = (((b & 0xcc) >> 2) | ((b & 0x33) << 2)); 244 | return ((b >> 4) | (b << 4)); 245 | } 246 | 247 | inline uint16_t u16_bit_reverse(uint16_t x) 248 | { 249 | x = (((x & 0xaaaa) >> 1) | ((x & 0x5555) << 1)); 250 | x = (((x & 0xcccc) >> 2) | ((x & 0x3333) << 2)); 251 | x = (((x & 0xf0f0) >> 4) | ((x & 0x0f0f) << 4)); 252 | return((x >> 8) | (x << 8)); 253 | } 254 | 255 | inline uint32_t u32_bit_reverse(uint32_t x) 256 | { 257 | x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1)); 258 | x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2)); 259 | x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4)); 260 | x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8)); 261 | return ((x >> 16) | (x << 16)); 262 | } 263 | 264 | #ifdef __cplusplus 265 | } 266 | #endif 267 | 268 | #endif /* _UTILS_UTILS_H_ */ 269 | -------------------------------------------------------------------------------- /include/utils/workqueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _UTILS_WORKQUEUE_H_ 8 | #define _UTILS_WORKQUEUE_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | typedef int (*work_fn_t) (void *); 19 | typedef void (*work_complete_fn_t) (void *work); 20 | 21 | #define WORK_ERR -1 22 | #define WORK_DONE 0 23 | #define WORK_YIELD 1 24 | 25 | enum work_status { 26 | WQ_WORK_NEW, 27 | WQ_WORK_QUEUED, 28 | WQ_WORK_IN_PROGRESS, 29 | WQ_WORK_COMPLETE, 30 | }; 31 | 32 | typedef struct { 33 | queue_node_t node; 34 | int64_t slice; 35 | enum work_status status; 36 | uint32_t requests; 37 | 38 | /* Filled by user */ 39 | void *arg; 40 | work_fn_t work_fn; 41 | work_complete_fn_t complete_fn; 42 | } work_t; 43 | 44 | typedef struct { 45 | int id; 46 | int state; 47 | pthread_t thread; 48 | event_t event; 49 | void *wq; 50 | } worker_t; 51 | 52 | typedef struct { 53 | worker_t *workers; 54 | int num_workers; 55 | queue_t backlog; 56 | int backlog_count; 57 | pthread_mutex_t backlog_lock; 58 | } workqueue_t; 59 | 60 | /** 61 | * @brief Create new workqueue with at most N workers 62 | * 63 | * @param wq work queue context 64 | * @param num_workers Max number of workers 65 | * 66 | * @return 0 Success 67 | * @return -1 Failure 68 | */ 69 | int workqueue_create(workqueue_t *wq, int num_workers); 70 | 71 | /** 72 | * @brief Add a work to work queue, next free worker thread will 73 | * pick it up 74 | * 75 | * @param wq work queue context 76 | * @param work Worker definition see work_t. 77 | * 78 | * @return 0 Success 79 | * @return -1 Failure 80 | */ 81 | int workqueue_add_work(workqueue_t *wq, work_t *work); 82 | 83 | /** 84 | * @brief Get the current backlog of the work queue 85 | * 86 | * @param wq work queue context 87 | * @return Number of work items in the work queue. 88 | */ 89 | int workqueue_backlog_count(workqueue_t *wq); 90 | 91 | /** 92 | * @brief Destroy work queue 93 | * 94 | * @param wq work queue context 95 | */ 96 | void workqueue_destroy(workqueue_t *wq); 97 | 98 | /** 99 | * @brief Request to cancel a queued work 100 | * 101 | * @param wq work queue context 102 | * @param work previously added worker pointer 103 | */ 104 | void workqueue_cancel_work(workqueue_t *wq, work_t *work); 105 | 106 | /** 107 | * @brief Check of a given work is completed or still running 108 | * 109 | * @param wq work queue context 110 | * @param work previously added worker pointer 111 | * 112 | * @return true if work is complete 113 | */ 114 | bool workqueue_work_is_complete(workqueue_t *wq, work_t *work); 115 | 116 | #ifdef __cplusplus 117 | } 118 | #endif 119 | 120 | #endif /* _UTILS_WORKQUEUE_H_ */ -------------------------------------------------------------------------------- /src/arg_parser.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #define is_lower_alpha(x) (x >= 97 && x <= 122) 18 | 19 | #define xstr(s) #s 20 | #define str(s) xstr(s) 21 | #define PAD str(AP_HELP_SPACING) 22 | 23 | const char *ap_app_name; 24 | const char *ap_app_desc; 25 | 26 | void ap_print_help(struct ap_option *ap_opts, int exit_code) 27 | { 28 | int count; 29 | char opt_str[64]; 30 | struct ap_option *ap_opt; 31 | 32 | if (ap_app_name && exit_code == 0) 33 | printf("%s - %s\n", ap_app_name, ap_app_desc); 34 | 35 | printf("\nUsage: %s [OPTIONS...] [CMD_ARGS[0] ...]\n" 36 | "\nOPTIONS:\n", ap_app_name); 37 | 38 | ap_opt = ap_opts; 39 | while (ap_opt->short_name != '\0') { 40 | if (ap_opt->short_name == -1) { 41 | ap_opt++; 42 | continue; 43 | } 44 | 45 | if (ap_opt->type == AP_TYPE_BOOL || 46 | ap_opt->type == AP_TYPE_BOOL_HANDLER) { 47 | snprintf(opt_str, 64, "%s", ap_opt->long_name); 48 | } else { 49 | snprintf(opt_str, 64, "%s <%s>", 50 | ap_opt->long_name, ap_opt->opt_name); 51 | } 52 | printf(" -%c, --%-"PAD"s %s\n", ap_opt->short_name, opt_str, 53 | ap_opt->help); 54 | ap_opt++; 55 | } 56 | printf(" -%c, --%-"PAD"s Fork to background\n", 'f', "fork"); 57 | printf(" -%c, --%-"PAD"s Prevent writing to tty\n", 'q', "quite"); 58 | printf(" -%c, --%-"PAD"s Print this help message\n", 'h', "help"); 59 | 60 | count = 0; 61 | ap_opt = ap_opts; 62 | while (ap_opt->short_name != '\0') { 63 | if (ap_opt->short_name != -1) { 64 | ap_opt++; 65 | continue; 66 | } 67 | if (count == 0) 68 | printf("\nCOMMANDS:\n"); 69 | printf(" %-"PAD"s %s\n", ap_opt->long_name, ap_opt->help); 70 | ap_opt++; count++; 71 | } 72 | 73 | exit(exit_code); 74 | } 75 | 76 | void ap_init(const char *app_name, const char *app_desc) 77 | { 78 | ap_app_name = app_name; 79 | ap_app_desc = app_desc; 80 | } 81 | 82 | int ap_parse(int argc, char *argv[], struct ap_option *ap_opts, void *data) 83 | { 84 | char **cval; 85 | char ostr[128]; 86 | struct option *opts, *opt; 87 | struct ap_option *ap_opt; 88 | int i, c, ret, *ival, pid; 89 | int opts_len = 0, cmd_len = 0, opt_idx = 0, olen = 0, do_fork = 0; 90 | int ap_opts_len = 0; 91 | 92 | ap_opt = ap_opts; 93 | while (ap_opt->short_name != '\0') { 94 | if (ap_opt->short_name != -1) 95 | opts_len++; 96 | else 97 | cmd_len++; 98 | ap_opts_len++; 99 | ap_opt++; 100 | } 101 | 102 | opts = safe_malloc(sizeof (struct option) * (opts_len + 3)); 103 | if (opts == NULL) { 104 | printf("Error: alloc error\n"); 105 | exit(-1); 106 | } 107 | 108 | i = 0; 109 | ap_opt = ap_opts; 110 | while (ap_opt->short_name != '\0') { 111 | if (ap_opt->short_name == -1) { 112 | ap_opt++; 113 | continue; 114 | } 115 | opt = opts + i++; 116 | opt->name = ap_opt->long_name; 117 | if (ap_opt->type == AP_TYPE_BOOL || 118 | ap_opt->type == AP_TYPE_BOOL_HANDLER) { 119 | opt->has_arg = no_argument; 120 | assert(olen + 2 < 128); 121 | ostr[olen++] = ap_opt->short_name; 122 | } else { 123 | opt->has_arg = required_argument; 124 | assert(olen + 3 < 128); 125 | ostr[olen++] = ap_opt->short_name; 126 | ostr[olen++] = ':'; 127 | } 128 | opt->flag = 0; 129 | opt->val = ap_opt->short_name; 130 | ap_opt++; 131 | } 132 | 133 | assert(olen + 4 < 128); 134 | 135 | /* add help and other default options */ 136 | opt = opts + i + 0; 137 | opt->name = "help"; 138 | opt->val = 'h'; 139 | ostr[olen++] = 'h'; 140 | 141 | opt = opts + i + 1; 142 | opt->name = "quite"; 143 | opt->val = 'q'; 144 | ostr[olen++] = 'q'; 145 | 146 | opt = opts + i + 2; 147 | opt->val = 'f'; 148 | ostr[olen++] = 'f'; 149 | 150 | ostr[olen] = '\0'; 151 | 152 | while ((c = getopt_long(argc, argv, ostr, opts, &opt_idx)) >= 0) { 153 | 154 | /* find c in ap_opt->short_name */ 155 | for (i = 0; i < ap_opts_len && c != ap_opts[i].short_name; i++); 156 | 157 | if (c == 'h') 158 | ap_print_help(ap_opts, 0); 159 | if (c == 'q') { 160 | close(STDOUT_FILENO); 161 | close(STDERR_FILENO); 162 | continue; 163 | } 164 | if (c == 'f') { 165 | do_fork = 1; 166 | continue; 167 | } 168 | if (c == '?') 169 | ap_print_help(ap_opts, -1); 170 | 171 | switch (ap_opts[i].type) { 172 | case AP_TYPE_BOOL: 173 | ival = (int *)((char *)data + ap_opts[i].offset); 174 | *ival = 1; 175 | break; 176 | case AP_TYPE_STR: 177 | cval = (char **)(((char *)data) + ap_opts[i].offset); 178 | *cval = safe_strdup(optarg); 179 | break; 180 | case AP_TYPE_INT: 181 | ival = (int *)((char *)data + ap_opts[i].offset); 182 | *ival = atoi(optarg); 183 | break; 184 | case AP_TYPE_BOOL_HANDLER: 185 | ap_opts[i].bool_handler(); 186 | exit(0); 187 | default: 188 | printf("Error: invalid arg type %d\n", ap_opts[i].type); 189 | exit(-1); 190 | } 191 | if (ap_opts[i].validator) { 192 | ret = ap_opts[i].validator(data); 193 | if (ret != 0) 194 | exit(-1); 195 | } 196 | ap_opts[i].flags |= AP_OPT_SEEN; 197 | } 198 | safe_free(opts); 199 | 200 | ap_opt = ap_opts; 201 | while (ap_opt->short_name != '\0') { 202 | if (ap_opt->short_name != -1) { 203 | if (ap_opt->flags & AP_OPT_REQUIRED && 204 | (ap_opt->flags & AP_OPT_SEEN) == 0U) { 205 | printf("Error: arg '%c' is mandatory\n\n", 206 | ap_opt->short_name); 207 | ap_print_help(ap_opts, -1); 208 | } 209 | } else if (argv[optind]) { 210 | if (strcmp(ap_opt->long_name, argv[optind]) == 0) { 211 | if (do_fork) { 212 | if ((pid = fork()) < 0) 213 | return -1; 214 | if (pid != 0) 215 | exit (0); 216 | } 217 | return ap_opt->handler(argc - optind - 1, 218 | argv + optind + 1, data); 219 | } 220 | } 221 | ap_opt++; 222 | } 223 | 224 | if (cmd_len) { 225 | printf("Error: unknown command '%s'\n\n", argv[optind]); 226 | ap_print_help(ap_opts, -1); 227 | } 228 | 229 | return optind; 230 | } 231 | -------------------------------------------------------------------------------- /src/bus_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | int g_message_id; 18 | uint8_t g_message_data[1024]; 19 | int g_message_data_length; 20 | pthread_mutex_t bus_global_lock; 21 | 22 | struct bus_client_data { 23 | int fd; 24 | int message_id; 25 | }; 26 | 27 | int bus_server_work_fn(void *arg) 28 | { 29 | struct bus_client_data *p = arg; 30 | int rc = -1; 31 | ssize_t ret; 32 | int bus_data_len; 33 | uint8_t bus_data[1024]; 34 | 35 | fcntl_setfl(p->fd, O_NONBLOCK); 36 | 37 | for (;;) { 38 | ret = read_loop(p->fd, bus_data, sizeof(bus_data)); 39 | if (ret < 0) { 40 | perror("read failed!"); 41 | rc = -1; 42 | break; 43 | } 44 | if (ret > 0) { 45 | p->message_id += 1; 46 | pthread_mutex_lock(&bus_global_lock); 47 | memcpy(g_message_data, bus_data, ret); 48 | g_message_data_length = (int)ret; 49 | g_message_id = p->message_id; 50 | pthread_mutex_unlock(&bus_global_lock); 51 | 52 | } 53 | if (g_message_id > p->message_id) { 54 | pthread_mutex_lock(&bus_global_lock); 55 | memcpy(bus_data, g_message_data, g_message_data_length); 56 | bus_data_len = g_message_data_length; 57 | p->message_id = g_message_id; 58 | pthread_mutex_unlock(&bus_global_lock); 59 | 60 | ret = write_loop(p->fd, bus_data, bus_data_len); 61 | if (ret < 0) { 62 | perror("write failed!"); 63 | rc = -1; 64 | break; 65 | } 66 | } 67 | } 68 | close(p->fd); 69 | free(p); 70 | return rc; 71 | } 72 | 73 | int bus_server_queue_work(bus_server_t *s, int fd) 74 | { 75 | int i; 76 | work_t *work = NULL; 77 | struct bus_client_data *client_data; 78 | 79 | for (i = 0; i < s->max_clients; i++) { 80 | work = s->work + i; 81 | if (work->status == WQ_WORK_NEW || 82 | work->status == WQ_WORK_COMPLETE) 83 | break; 84 | } 85 | 86 | if (i == s->max_clients) 87 | return -1; 88 | 89 | client_data = calloc(1, sizeof(struct bus_client_data)); 90 | client_data->fd = fd; 91 | 92 | memset(work, 0, sizeof(work_t)); 93 | work->arg = client_data; 94 | work->work_fn = bus_server_work_fn; 95 | 96 | workqueue_add_work(&s->wq, work); 97 | 98 | while (READ_ONCE(work->status) != WQ_WORK_IN_PROGRESS); 99 | 100 | return 0; 101 | } 102 | 103 | void *bus_server_serve(void *arg) 104 | { 105 | int rc, fd; 106 | bus_server_t *s = arg; 107 | struct sockaddr_un cli_addr; 108 | socklen_t len; 109 | 110 | for(;;) { 111 | fd = accept(s->fd, (struct sockaddr *)&cli_addr, &len); 112 | if (fd < 0 && (errno == EAGAIN || errno == EINTR)) 113 | continue; 114 | if (fd < 0) { 115 | perror("accept failed"); 116 | break; 117 | } 118 | rc = bus_server_queue_work(s, fd); 119 | if (rc < 0) { 120 | printf("client[%d]: workqueue full; closing.\n", fd); 121 | close(fd); 122 | } 123 | } 124 | return NULL; 125 | } 126 | 127 | int bus_server_start(bus_server_t *s, int max_clients, const char *path) 128 | { 129 | int rc; 130 | 131 | memset(s, 0, sizeof(bus_server_t)); 132 | 133 | pthread_mutex_init(&bus_global_lock, NULL); 134 | 135 | rc = workqueue_create(&s->wq, max_clients); 136 | if (rc < 0) { 137 | printf("failed to setup workqueue\n"); 138 | return -1; 139 | } 140 | 141 | s->work = calloc(max_clients, sizeof(work_t)); 142 | if (s->work == NULL) { 143 | perror("work alloc failed"); 144 | return -1; 145 | } 146 | 147 | rc = sock_unix_listen(path, max_clients); 148 | if (rc < 0) { 149 | perror("sock_unix_listen failed"); 150 | return -1; 151 | } 152 | s->fd = rc; 153 | s->path = strdup(path); 154 | s->max_clients = max_clients; 155 | 156 | rc = pthread_create(&s->thread, NULL, bus_server_serve, (void *)s); 157 | if (rc < 0) { 158 | perror("pthread_create failed"); 159 | return -1; 160 | } 161 | 162 | return 0; 163 | } 164 | 165 | void bus_server_stop(bus_server_t *s) 166 | { 167 | workqueue_destroy(&s->wq); 168 | 169 | pthread_cancel(s->thread); 170 | pthread_join(s->thread, NULL); 171 | 172 | pthread_mutex_destroy(&bus_global_lock); 173 | 174 | close(s->fd); 175 | unlink(s->path); 176 | free(s->path); 177 | } 178 | -------------------------------------------------------------------------------- /src/channel.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | struct channel_msgbuf { 25 | long mtype; /* message type, must be > 0 */ 26 | uint8_t mtext[512]; /* message data */ 27 | }; 28 | 29 | struct channel_msgq_s { 30 | int send_id; 31 | int send_msgid; 32 | int is_server; 33 | int recv_id; 34 | int recv_msgid; 35 | }; 36 | 37 | struct channel_msgbuf send_buf, recv_buf; 38 | 39 | int channel_msgq_send(void *data, uint8_t *buf, int len) 40 | { 41 | int ret; 42 | struct channel_msgq_s *ctx = data; 43 | 44 | send_buf.mtype = ctx->send_id; 45 | memcpy(&send_buf.mtext, buf, len); 46 | ret = msgsnd(ctx->send_msgid, &send_buf, len, 0); 47 | if (ret < 0 && errno == EIDRM) { 48 | printf("Error: msgq was removed externally. Exiting..\n"); 49 | exit(-1); 50 | } 51 | 52 | return len; 53 | } 54 | 55 | int channel_msgq_recv(void *data, uint8_t *buf, int max_len) 56 | { 57 | int ret; 58 | struct channel_msgq_s *ctx = data; 59 | 60 | ret = msgrcv(ctx->recv_msgid, &recv_buf, max_len, 61 | ctx->recv_id, MSG_NOERROR | IPC_NOWAIT); 62 | if (ret == 0 || (ret < 0 && errno == EAGAIN)) 63 | return 0; 64 | if (ret < 0 && errno == EIDRM) { 65 | printf("Error: msgq was removed externally. Exiting..\n"); 66 | exit(-1); 67 | } 68 | 69 | if (ret > 0) 70 | memcpy(buf, recv_buf.mtext, ret); 71 | 72 | return ret; 73 | } 74 | 75 | void channel_msgq_flush(void *data) 76 | { 77 | int ret; 78 | uint8_t buf[128]; 79 | 80 | while (1) { 81 | ret = channel_msgq_recv(data, buf, 128); 82 | if (ret <= 0) 83 | break; 84 | } 85 | } 86 | 87 | int channel_msgq_setup(void **data, struct channel *c) 88 | { 89 | key_t key; 90 | struct channel_msgq_s *ctx; 91 | 92 | ctx = malloc(sizeof(struct channel_msgq_s)); 93 | if (ctx == NULL) { 94 | printf("Failed at alloc for msgq channel\n"); 95 | return -1; 96 | } 97 | ctx->is_server = c->is_server; 98 | ctx->send_id = ctx->is_server ? 13 : 17; 99 | key = ftok(c->device, ctx->send_id); 100 | ctx->send_msgid = msgget(key, 0666 | IPC_CREAT); 101 | if (ctx->send_msgid < 0) { 102 | printf("Error: failed to create send message queue %s. ", 103 | c->device); 104 | perror(""); 105 | free(ctx); 106 | return -1; 107 | } 108 | 109 | ctx->recv_id = ctx->is_server ? 17 : 13; 110 | key = ftok(c->device, ctx->recv_id); 111 | ctx->recv_msgid = msgget(key, 0666 | IPC_CREAT); 112 | if (ctx->recv_msgid < 0) { 113 | printf("Error: failed to create recv message queue %s. ", 114 | c->device); 115 | perror(""); 116 | free(ctx); 117 | return -1; 118 | } 119 | *data = (void *)ctx; 120 | 121 | return 0; 122 | } 123 | 124 | void channel_msgq_teardown(void *data) 125 | { 126 | struct channel_msgq_s *ctx = data; 127 | 128 | if (data == NULL) 129 | return; 130 | if (ctx->is_server) { 131 | msgctl(ctx->send_msgid, IPC_RMID, NULL); 132 | msgctl(ctx->recv_msgid, IPC_RMID, NULL); 133 | } 134 | free(ctx); 135 | } 136 | 137 | int channel_uart_send(void *data, uint8_t *buf, int len) 138 | { 139 | return serial_write(data, (unsigned char *)buf, len); 140 | } 141 | 142 | int channel_uart_recv(void *data, uint8_t *buf, int maxLen) 143 | { 144 | return serial_read(data, (unsigned char *)buf, maxLen); 145 | } 146 | 147 | void channel_uart_flush(void *data) 148 | { 149 | serial_flush(data); 150 | } 151 | 152 | int channel_uart_setup(void **data, struct channel *c) 153 | { 154 | struct serial *ctx; 155 | 156 | ctx = serial_open(c->device, c->speed, "8N1"); 157 | if (ctx == NULL) { 158 | printf("Error: failed to open device %s\n", c->device); 159 | return -1; 160 | } 161 | *data = (void *)ctx; 162 | return 0; 163 | } 164 | 165 | void channel_uart_teardown(void *data) 166 | { 167 | serial_close(data); 168 | } 169 | 170 | struct channel_fifo { 171 | char *fifo0; 172 | char *fifo1; 173 | bool is_server; 174 | int rfd; 175 | int wfd; 176 | }; 177 | 178 | int channel_fifo_send(void *data, uint8_t *buf, int len) 179 | { 180 | struct channel_fifo *ctx = data; 181 | 182 | return (int)write_loop(ctx->wfd, buf, len); 183 | } 184 | 185 | int channel_fifo_recv(void *data, uint8_t *buf, int max_len) 186 | { 187 | struct channel_fifo *ctx = data; 188 | 189 | return (int)read_loop(ctx->rfd, buf, max_len); 190 | } 191 | 192 | void channel_fifo_flush(void *data) 193 | { 194 | struct channel_fifo *ctx = data; 195 | 196 | flush_fd(ctx->rfd); 197 | } 198 | 199 | int channel_fifo_setup(void **data, struct channel *c) 200 | { 201 | int rc, len; 202 | char path[128]; 203 | struct channel_fifo *ctx; 204 | 205 | len = strlen(c->device); 206 | if (len > 120) 207 | return -1; 208 | 209 | ctx = calloc(1, sizeof(struct channel_fifo)); 210 | if (ctx == NULL) 211 | return -1; 212 | ctx->is_server = c->is_server; 213 | 214 | snprintf(path, 128, "%s-0", c->device); 215 | if (ctx->is_server) { 216 | unlink(path); 217 | rc = mkfifo(path, 0666); 218 | if (rc < 0) { 219 | perror("Error: mkfifo(0)"); 220 | goto error; 221 | } 222 | } 223 | ctx->fifo0 = strdup(path); 224 | 225 | snprintf(path, 128, "%s-1", c->device); 226 | if (ctx->is_server) { 227 | unlink(path); 228 | rc = mkfifo(path, 0666); 229 | if (rc < 0) { 230 | perror("Error: mkfifo(1)"); 231 | goto error; 232 | } 233 | } 234 | ctx->fifo1 = strdup(path); 235 | 236 | if (ctx->is_server) { 237 | ctx->rfd = open(ctx->fifo0, O_RDWR); 238 | if (ctx->rfd < 0) { 239 | perror("Error: open_fifo(0, r)"); 240 | goto error; 241 | } 242 | ctx->wfd = open(ctx->fifo1, O_RDWR); 243 | if (ctx->wfd < 0) { 244 | perror("Error: open_fifo(1, w)"); 245 | goto error; 246 | } 247 | } else { 248 | ctx->rfd = open(ctx->fifo1, O_RDWR); 249 | if (ctx->rfd < 0) { 250 | perror("Error: open_fifo(1, r)"); 251 | goto error; 252 | } 253 | ctx->wfd = open(ctx->fifo0, O_RDWR); 254 | if (ctx->wfd < 0) { 255 | perror("Error: open_fifo(0, w)"); 256 | goto error; 257 | } 258 | } 259 | 260 | rc = fcntl_setfl(ctx->rfd, O_NONBLOCK); 261 | if (rc < 0) 262 | goto error; 263 | 264 | rc = fcntl_setfl(ctx->wfd, O_NONBLOCK); 265 | if (rc < 0) 266 | goto error; 267 | 268 | *data = (void *)ctx; 269 | 270 | return 0; 271 | error: 272 | if (ctx->rfd > 0) 273 | close(ctx->rfd); 274 | if (ctx->wfd > 0) 275 | close(ctx->rfd); 276 | if (ctx->fifo0) { 277 | unlink(ctx->fifo0); 278 | free(ctx->fifo0); 279 | } 280 | if (ctx->fifo1) { 281 | unlink(ctx->fifo1); 282 | free(ctx->fifo1); 283 | } 284 | free(ctx); 285 | return -1; 286 | } 287 | 288 | void channel_fifo_teardown(void *data) 289 | { 290 | struct channel_fifo *ctx = data; 291 | 292 | close(ctx->rfd); 293 | close(ctx->wfd); 294 | if (ctx->is_server) { 295 | unlink(ctx->fifo0); 296 | unlink(ctx->fifo1); 297 | } 298 | free(ctx->fifo0); 299 | free(ctx->fifo1); 300 | free(ctx); 301 | } 302 | 303 | #include 304 | #include 305 | 306 | struct channel_unix_bus { 307 | int fd; 308 | bus_server_t *bus_server; 309 | }; 310 | 311 | int channel_unix_bus_send(void *data, uint8_t *buf, int len) 312 | { 313 | struct channel_unix_bus *ctx = data; 314 | 315 | return (int)write_loop(ctx->fd, buf, len); 316 | } 317 | 318 | int channel_unix_bus_recv(void *data, uint8_t *buf, int max_len) 319 | { 320 | struct channel_unix_bus *ctx = data; 321 | 322 | return (int)read_loop(ctx->fd, buf, max_len); 323 | } 324 | 325 | void channel_unix_bus_flush(void *data) 326 | { 327 | struct channel_unix_bus *ctx = data; 328 | 329 | flush_fd(ctx->fd); 330 | } 331 | 332 | int channel_unix_bus_setup(void **data, struct channel *c) 333 | { 334 | int rc, len; 335 | struct channel_unix_bus *ctx; 336 | 337 | len = strlen(c->device); 338 | if (len > 120) 339 | return -1; 340 | 341 | ctx = calloc(1, sizeof(struct channel_unix_bus)); 342 | if (ctx == NULL) 343 | return -1; 344 | 345 | if (access(c->device, F_OK) != 0) { 346 | /* start bus server */ 347 | ctx->bus_server = calloc(1, sizeof(bus_server_t)); 348 | if (ctx->bus_server == NULL) 349 | goto error; 350 | rc = bus_server_start(ctx->bus_server, 5, c->device); 351 | if (rc < 0) 352 | goto error; 353 | } 354 | 355 | rc = sock_unix_connect(c->device); 356 | if (rc < 0) 357 | goto error; 358 | 359 | ctx->fd = rc; 360 | 361 | fcntl_setfl(ctx->fd, O_NONBLOCK); 362 | 363 | *data = ctx; 364 | return 0; 365 | error: 366 | if (ctx) { 367 | if (ctx->bus_server) { 368 | bus_server_stop(ctx->bus_server); 369 | free(ctx->bus_server); 370 | } 371 | free(ctx); 372 | } 373 | return -1; 374 | } 375 | 376 | void channel_unix_bus_teardown(void *data) 377 | { 378 | struct channel_unix_bus *ctx = data; 379 | 380 | close(ctx->fd); 381 | if (ctx->bus_server) { 382 | bus_server_stop(ctx->bus_server); 383 | free(ctx->bus_server); 384 | } 385 | free(ctx); 386 | } 387 | 388 | struct channel_ops { 389 | channel_send_fn_t send; 390 | channel_receive_fn_t receive; 391 | channel_flush_fn_t flush; 392 | int (*setup)(void **data, struct channel *c); 393 | void (*teardown)(void *data); 394 | }; 395 | 396 | struct channel_ops g_channel_ops[CHANNEL_TYPE_SENTINEL] = { 397 | [CHANNEL_TYPE_UART] = { 398 | .send = channel_uart_send, 399 | .receive = channel_uart_recv, 400 | .flush = channel_uart_flush, 401 | .setup = channel_uart_setup, 402 | .teardown = channel_uart_teardown 403 | }, 404 | [CHANNEL_TYPE_MSGQ] = { 405 | .send = channel_msgq_send, 406 | .receive = channel_msgq_recv, 407 | .flush = channel_msgq_flush, 408 | .setup = channel_msgq_setup, 409 | .teardown = channel_msgq_teardown 410 | }, 411 | [CHANNEL_TYPE_FIFO] = { 412 | .send = channel_fifo_send, 413 | .receive = channel_fifo_recv, 414 | .flush = channel_fifo_flush, 415 | .setup = channel_fifo_setup, 416 | .teardown = channel_fifo_teardown 417 | }, 418 | [CHANNEL_TYPE_UNIX_BUS] = { 419 | .send = channel_unix_bus_send, 420 | .receive = channel_unix_bus_recv, 421 | .flush = channel_unix_bus_flush, 422 | .setup = channel_unix_bus_setup, 423 | .teardown = channel_unix_bus_teardown 424 | }, 425 | }; 426 | 427 | void channel_manager_init(struct channel_manager *ctx) 428 | { 429 | hash_map_init(&ctx->channels); 430 | } 431 | 432 | enum channel_type channel_guess_type(const char *desc) 433 | { 434 | if (strcmp("uart", desc) == 0 || 435 | strcmp("usart", desc) == 0 || 436 | strcmp("serial", desc) == 0) 437 | return CHANNEL_TYPE_UART; 438 | 439 | if (strcmp("msgq", desc) == 0 || 440 | strcmp("message_queue", desc) == 0) 441 | return CHANNEL_TYPE_MSGQ; 442 | 443 | if (strcmp("fifo", desc) == 0 || 444 | strcmp("pipe", desc) == 0) 445 | return CHANNEL_TYPE_FIFO; 446 | 447 | if (strcmp("unix_bus", desc) == 0) 448 | return CHANNEL_TYPE_UNIX_BUS; 449 | 450 | return CHANNEL_TYPE_ERR; 451 | } 452 | 453 | int channel_open(struct channel_manager *ctx, enum channel_type type, char *device, 454 | int speed, int is_server) 455 | { 456 | struct channel *c; 457 | 458 | if (type <= CHANNEL_TYPE_ERR || type >= CHANNEL_TYPE_SENTINEL) 459 | return CHANNEL_ERR_UNKNOWN_TYPE; 460 | 461 | if (!device) 462 | return CHANNEL_ERR_OPEN_FAILED; 463 | 464 | if (hash_map_get(&ctx->channels, device, 0) != NULL) 465 | return CHANNEL_ERR_ALREADY_OPEN; 466 | 467 | c = calloc(1, sizeof(struct channel)); 468 | if (c == NULL) 469 | return CHANNEL_ERR_OOM; 470 | 471 | c->type = type; 472 | c->speed = speed; 473 | c->device = strdup(device); 474 | c->is_server = is_server; 475 | if (g_channel_ops[type].setup(&c->data, c)) { 476 | free(c->device); 477 | free(c); 478 | return CHANNEL_ERR_OPEN_FAILED; 479 | } 480 | 481 | if (g_channel_ops[type].flush) { 482 | g_channel_ops[type].flush(c->data); 483 | } 484 | ctx->open_channels++; 485 | c->id = ctx->open_channels; 486 | hash_map_insert(&ctx->channels, c->device, c); 487 | 488 | return CHANNEL_ERR_NONE; 489 | } 490 | 491 | int channel_get(struct channel_manager *ctx, const char *device, 492 | int *id, void **data, 493 | channel_send_fn_t *send, 494 | channel_receive_fn_t *receive, 495 | channel_flush_fn_t *flush) 496 | { 497 | struct channel *c; 498 | 499 | c = hash_map_get(&ctx->channels, device, 0); 500 | if (c == NULL) 501 | return CHANNEL_ERR_NOT_OPEN; 502 | 503 | if(id != NULL) 504 | *id = c->id; 505 | if (data != NULL) 506 | *data = c->data; 507 | if (send != NULL) 508 | *send = g_channel_ops[c->type].send; 509 | if (receive != NULL) 510 | *receive = g_channel_ops[c->type].receive; 511 | if (flush != NULL) 512 | *flush = g_channel_ops[c->type].flush; 513 | 514 | return CHANNEL_ERR_NONE; 515 | } 516 | 517 | int channel_close(struct channel_manager *ctx, const char *device) 518 | { 519 | struct channel *c; 520 | 521 | c = hash_map_get(&ctx->channels, device, 0); 522 | if (c == NULL) 523 | return CHANNEL_ERR_NOT_OPEN; 524 | 525 | g_channel_ops[c->type].teardown(c->data); 526 | hash_map_delete(&ctx->channels, device, 0); 527 | ctx->open_channels--; 528 | free(c->device); 529 | free(c); 530 | return CHANNEL_ERR_NONE; 531 | } 532 | 533 | void channel_hash_map_callback(const char *key, void *val) 534 | { 535 | struct channel *c = val; 536 | ARG_UNUSED(key); 537 | 538 | free(c->device); 539 | free(c); 540 | } 541 | 542 | void channel_manager_teardown(struct channel_manager *ctx) 543 | { 544 | char *device; 545 | struct channel *c; 546 | 547 | HASH_MAP_FOREACH(&ctx->channels, &device, &c) { 548 | channel_close(ctx, device); 549 | } 550 | 551 | hash_map_free(&ctx->channels, channel_hash_map_callback); 552 | } 553 | -------------------------------------------------------------------------------- /src/circbuf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | 9 | #include 10 | 11 | int __circbuf_pop(circbuf_t *circ_buf, void *elem, int read_only) 12 | { 13 | int total; 14 | char *tail; 15 | 16 | total = circ_buf->push_count - circ_buf->pop_count; 17 | if (total < 0) 18 | total += (2 * circ_buf->size); 19 | 20 | if (total == 0) 21 | return -1; // Empty 22 | 23 | tail = (char *)circ_buf->buffer + ((circ_buf->pop_count % circ_buf->size) 24 | * circ_buf->element_size); 25 | 26 | if (elem) 27 | memcpy(elem, tail, circ_buf->element_size); 28 | 29 | if (!read_only) { 30 | #ifdef CRICBUF_CLEAN_ON_POP 31 | memset(tail, 0, circ_buf->element_size); 32 | #endif 33 | circ_buf->pop_count++; 34 | if (circ_buf->pop_count >= (2*circ_buf->size)) 35 | circ_buf->pop_count = 0; 36 | } 37 | return 0; 38 | } 39 | 40 | int __circbuf_push(circbuf_t *circ_buf, void *elem) 41 | { 42 | int total; 43 | char *head; 44 | 45 | total = circ_buf->push_count - circ_buf->pop_count; 46 | if (total < 0) 47 | total += (2 * circ_buf->size); 48 | 49 | if (total >= circ_buf->size) 50 | return -1; // Full 51 | 52 | head = (char *)circ_buf->buffer + ( (circ_buf->push_count % circ_buf->size) 53 | * circ_buf->element_size ); 54 | memcpy(head, elem, circ_buf->element_size); 55 | circ_buf->push_count++; 56 | if (circ_buf->push_count >= (2*circ_buf->size)) 57 | circ_buf->push_count = 0; 58 | return 0; 59 | } 60 | 61 | int __circbuf_free_space(circbuf_t *circ_buf) 62 | { 63 | int total; 64 | 65 | total = circ_buf->push_count - circ_buf->pop_count; 66 | if (total < 0) 67 | total += (2 * circ_buf->size); 68 | 69 | return circ_buf->size - total; 70 | } 71 | -------------------------------------------------------------------------------- /src/disjoint_set.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | int disjoint_set_make(struct disjoint_set *set, int max_nodes) 11 | { 12 | int i; 13 | 14 | if (max_nodes >= DISJOINT_SET_MAX) 15 | return -1; 16 | 17 | set->max_nodes = max_nodes; 18 | for (i = 0; i < max_nodes; i++) { 19 | set->parent[i] = i; 20 | set->rank[i] = 0; 21 | } 22 | 23 | return 0; 24 | } 25 | 26 | int disjoint_set_find(struct disjoint_set *set, int a) 27 | { 28 | int tmp, root = a; 29 | 30 | /* find root */ 31 | while (set->parent[root] != root) 32 | root = set->parent[root]; 33 | 34 | /* path compression */ 35 | while (set->parent[a] != root) { 36 | tmp = set->parent[a]; 37 | set->parent[a] = root; 38 | a = tmp; 39 | } 40 | 41 | return root; 42 | } 43 | 44 | void disjoint_set_union(struct disjoint_set *set, int a, int b) 45 | { 46 | a = disjoint_set_find(set, a); 47 | b = disjoint_set_find(set, b); 48 | 49 | if (a == b) 50 | return; 51 | 52 | if (set->rank[a] < set->rank[b]) { 53 | int tmp; 54 | tmp = a; 55 | a = b; 56 | b = tmp; 57 | } 58 | 59 | set->parent[b] = a; 60 | if (set->rank[a] == set->rank[b]) 61 | set->rank[a] += 1; 62 | } 63 | 64 | int disjoint_set_num_roots(struct disjoint_set *set) 65 | { 66 | int i, roots = 0; 67 | 68 | for (i = 0; i < set->max_nodes; i++) { 69 | if (set->parent[i] == i) 70 | roots++; 71 | } 72 | 73 | return roots; 74 | } 75 | -------------------------------------------------------------------------------- /src/event.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | int event_init(event_t *e, bool active, bool blocking) 17 | { 18 | int fds[2]; 19 | int ret; 20 | 21 | if (pipe(fds) < 0) 22 | return -1; 23 | 24 | if (!blocking) { 25 | ret = fcntl_setfl(fds[0], O_NONBLOCK); 26 | if (ret < 0) 27 | goto error; 28 | ret = fcntl_setfl(fds[1], O_NONBLOCK); 29 | if (ret < 0) 30 | goto error; 31 | } 32 | 33 | e->rfd = fds[0]; 34 | e->wfd = fds[1]; 35 | e->initialized = true; 36 | 37 | if (active) 38 | event_set(e); 39 | 40 | return 0; 41 | error: 42 | close(fds[0]); 43 | close(fds[1]); 44 | return -1; 45 | } 46 | 47 | void event_cleanup(event_t *e) 48 | { 49 | if (!e->initialized) 50 | return; 51 | 52 | close(e->rfd); 53 | close(e->wfd); 54 | e->wfd = e->rfd = -1; 55 | e->initialized = false; 56 | } 57 | 58 | bool event_set(event_t *e) 59 | { 60 | static const uint64_t value = 1; 61 | ssize_t ret; 62 | 63 | if (!e->initialized) 64 | return false; 65 | 66 | ret = write_loop(e->wfd, &value, sizeof(value)); 67 | if (ret < 0) 68 | return false; 69 | 70 | return true; 71 | } 72 | 73 | bool event_is_set(event_t *e) 74 | { 75 | if (!e->initialized) 76 | return 0; 77 | 78 | return flush_fd(e->rfd); 79 | } 80 | -------------------------------------------------------------------------------- /src/fdutils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | 13 | int fcntl_setfl(int fd, int flag) 14 | { 15 | int flags; 16 | 17 | flags = fcntl(fd, F_GETFL); 18 | if (flags == -1) 19 | return -1; 20 | 21 | if (fcntl(fd, F_SETFL, flags | flag) == -1) 22 | return -1; 23 | 24 | return 0; 25 | } 26 | 27 | ssize_t read_loop(int fd, void *buf, size_t max_len) 28 | { 29 | ssize_t ret; 30 | 31 | do { 32 | ret = read(fd, buf, max_len); 33 | } while (ret == -1 && errno == EINTR); 34 | 35 | if (ret < 0) { 36 | if (errno != EAGAIN) 37 | return -1; 38 | ret = 0; 39 | } 40 | 41 | return ret; 42 | } 43 | 44 | ssize_t write_loop(int fd, const void *buf, size_t len) 45 | { 46 | ssize_t ret; 47 | 48 | do { 49 | ret = write(fd, buf, len); 50 | } while (ret < 0 && errno == EINTR); 51 | 52 | if (ret < 0) { 53 | if (errno != EAGAIN) 54 | return -1; 55 | ret = 0; 56 | } 57 | 58 | return ret; 59 | } 60 | 61 | int flush_fd(int fd) 62 | { 63 | int ret = 0; 64 | ssize_t len; 65 | char buf[64]; 66 | 67 | do { 68 | len = read(fd, buf, sizeof(buf)); 69 | ret |= (len > 0); 70 | } while ((len == -1 && errno == EINTR) || len == sizeof(buf)); 71 | 72 | return ret; 73 | } 74 | -------------------------------------------------------------------------------- /src/file.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | #define PATH_SEP '/' 24 | #define MAX_DIR_WALK 128 25 | 26 | size_t file_size(FILE *fp) 27 | { 28 | size_t size; 29 | 30 | fseek(fp, 0, SEEK_END); 31 | size = ftell(fp); 32 | fseek(fp, 0, SEEK_SET); 33 | return size; 34 | } 35 | 36 | int read_binary_file(const char *path, uint8_t **buf, size_t *size) 37 | { 38 | int rc = 0; 39 | FILE *fp; 40 | 41 | fp = fopen(path, "rb"); 42 | if (fp == NULL) { 43 | perror("read: file open failed!"); 44 | return -1; 45 | } 46 | *size = file_size(fp); 47 | *buf = safe_malloc(*size); 48 | if (fread(*buf, *size, 1, fp) != 1 || ferror(fp)) { 49 | perror("read failed!"); 50 | rc = -1; 51 | } 52 | fclose(fp); 53 | return rc; 54 | } 55 | 56 | int write_binary_file(const char *path, uint8_t *buf, size_t size) 57 | { 58 | int rc = 0; 59 | FILE *fp; 60 | 61 | fp = fopen(path, "wb"); 62 | if (fp == NULL) { 63 | perror("write: file open failed!"); 64 | return -1; 65 | } 66 | if (fwrite(buf, size, 1, fp) != 1) { 67 | perror("write failed!"); 68 | rc = -1; 69 | } 70 | fclose(fp); 71 | return rc; 72 | } 73 | 74 | int file_read_all(FILE *in, char **dataptr, size_t *sizeptr) 75 | { 76 | char *data = NULL; 77 | size_t n, size = 0, used = 0, chunk_size = (2 * 1024L); 78 | 79 | /* None of the parameters can be NULL. */ 80 | if (in == NULL || dataptr == NULL || sizeptr == NULL) { 81 | return -1; 82 | } 83 | 84 | /* A read error already occurred? */ 85 | if (ferror(in)) { 86 | return -1; 87 | } 88 | 89 | while (true) { 90 | if (used + chunk_size + 1 > size) { 91 | size = used + chunk_size + 1; 92 | /* size overflow check. Some ANSI C compilers may 93 | optimize this away, though. */ 94 | if (size <= used) { 95 | safe_free(data); 96 | return -1; 97 | } 98 | data = safe_realloc(data, size); 99 | } 100 | n = fread(data + used, 1, chunk_size, in); 101 | used += n; 102 | if (n < chunk_size) { 103 | break; 104 | } 105 | chunk_size *= 2; /* exponential read */ 106 | } 107 | 108 | /* check if we broke from the while due to errors/non-eof conditions */ 109 | if (ferror(in) != 0 || feof(in) == 0) { 110 | safe_free(data); 111 | return -1; 112 | } 113 | 114 | data = safe_realloc(data, used + 1); 115 | data[used] = '\0'; 116 | 117 | *dataptr = data; 118 | *sizeptr = used; 119 | 120 | return 0; 121 | } 122 | 123 | char *path_join(const char *p1, const char *p2) 124 | { 125 | char *path; 126 | size_t p1_len, p2_len; 127 | 128 | if (p2 == NULL) 129 | return NULL; 130 | if (p2[0] == PATH_SEP) 131 | return safe_strdup(p2); 132 | p1_len = (p1 == NULL) ? 0 : strlen(p1); 133 | p2_len = strlen(p2); 134 | path = safe_malloc(sizeof(char) * (p1_len + p2_len + 2)); 135 | if (p1) { 136 | memcpy(path, p1, p1_len); 137 | } 138 | if (p1_len && path[p1_len - 1] != PATH_SEP) { 139 | path[p1_len] = PATH_SEP; 140 | memcpy(path + p1_len + 1, p2, p2_len); 141 | path[p1_len + 1 + p2_len] = '\0'; 142 | } else { 143 | memcpy(path + p1_len, p2, p2_len); 144 | path[p1_len + p2_len] = '\0'; 145 | } 146 | return path; 147 | } 148 | 149 | /** 150 | * Given a root, return all files it contains. Behaves like 151 | * `find . -type f`. Note: root dir must exits! 152 | */ 153 | char **fs_path_walk(const char *root_dir) 154 | { 155 | DIR *dir; 156 | char *root; 157 | struct dirent *dir_ent; 158 | size_t nr_dirs = 0, dirs_capacity = 32, nr_files = 0, files_capacity = 32; 159 | char **files = malloc(sizeof(char *) * files_capacity); 160 | char **dirs = malloc(sizeof(char *) * files_capacity); 161 | 162 | dirs[nr_dirs++] = strdup(root_dir); 163 | while (nr_dirs > 0) { 164 | root = dirs[--nr_dirs]; 165 | dir = opendir(root); 166 | if (dir == NULL) { 167 | fprintf(stderr, "Failed to open dir %s (%s)\n", root, strerror(errno)); 168 | exit(EXIT_FAILURE); 169 | } 170 | while ((dir_ent = readdir(dir)) != NULL) { 171 | if (dir_ent->d_type != DT_DIR) { 172 | files[nr_files++] = path_join(root, dir_ent->d_name); 173 | if (nr_files == files_capacity) { 174 | files_capacity <<= 1; 175 | files = realloc(files, sizeof(char *) * files_capacity); 176 | } 177 | } 178 | else if (dir_ent->d_type == DT_DIR) { 179 | if (strcmp(dir_ent->d_name, ".") == 0 || 180 | strcmp(dir_ent->d_name, "..") == 0) 181 | continue; 182 | dirs[nr_dirs++] = path_join(root, dir_ent->d_name); 183 | if (nr_dirs == dirs_capacity) { 184 | dirs_capacity <<= 1; 185 | dirs = realloc(dirs, sizeof(char *) * dirs_capacity); 186 | } 187 | } 188 | } 189 | closedir(dir); 190 | free(root); 191 | } 192 | free(dirs); 193 | files[nr_files] = NULL; 194 | return files; 195 | } 196 | 197 | void fs_path_walk_free(char **files) 198 | { 199 | int i = 0; 200 | while (files[i] != NULL) { 201 | free(files[i]); 202 | i++; 203 | } 204 | free(files); 205 | } 206 | 207 | bool dir_exists(const char *path) 208 | { 209 | DIR* dir = opendir(path); 210 | 211 | if (dir == NULL) 212 | return false; 213 | closedir(dir); 214 | return true; 215 | } 216 | 217 | char *get_working_directory(void) 218 | { 219 | char *p; 220 | 221 | p = safe_malloc(sizeof(char) * PATH_MAX); 222 | if (getcwd(p, PATH_MAX) == NULL) { 223 | safe_free(p); 224 | return NULL; 225 | } 226 | return p; 227 | } 228 | 229 | int is_regular_file(const char *path) 230 | { 231 | struct stat path_stat; 232 | stat(path, &path_stat); 233 | return S_ISREG(path_stat.st_mode); 234 | } 235 | 236 | bool file_exists(const char *path) 237 | { 238 | return access(path, F_OK) == 0; 239 | } 240 | 241 | int path_extract(const char *path, char **dir_name, char **base_name) 242 | { 243 | int ret = -1; 244 | char *dname = NULL, *dname_copy = NULL; 245 | char *bname = NULL, *bname_copy = NULL, *rpath; 246 | 247 | if (!path || !is_regular_file(path)) 248 | return -1; 249 | 250 | if (dir_name) *dir_name = NULL; 251 | if (base_name) *base_name = NULL; 252 | 253 | rpath = safe_malloc(sizeof(char) * PATH_MAX); 254 | if (realpath(path, rpath) == NULL) { 255 | perror("realpath"); 256 | goto error; 257 | } 258 | if (dir_name) { 259 | dname_copy = safe_strdup(rpath); 260 | dname = dirname(rpath); 261 | if (dname == NULL) { 262 | perror("dirname"); 263 | goto error; 264 | } 265 | *dir_name = safe_strdup(dname); 266 | } 267 | if (base_name) { 268 | bname_copy = safe_strdup(rpath); 269 | bname = basename(bname_copy); 270 | if (bname == NULL) { 271 | perror("basename"); 272 | goto error; 273 | } 274 | *base_name = safe_strdup(bname); 275 | } 276 | ret = 0; 277 | cleanup: 278 | safe_free(rpath); 279 | safe_free(dname_copy); 280 | safe_free(bname_copy); 281 | return ret; 282 | error: 283 | if (dir_name) 284 | safe_free(*dir_name); 285 | if (base_name) 286 | safe_free(*base_name); 287 | goto cleanup; 288 | } 289 | -------------------------------------------------------------------------------- /src/filo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | 8 | void filo_init(filo_t *pfilo, void *buffer, size_t elem_size, size_t max_size) 9 | { 10 | pfilo->buffer = buffer; 11 | pfilo->elem_size = elem_size; 12 | pfilo->max_size = max_size; 13 | pfilo->top = 0; 14 | } 15 | 16 | void filo_reset(filo_t *pfilo) 17 | { 18 | pfilo->top = 0; 19 | } 20 | 21 | filo_t *filo_alloc(size_t elem_size, size_t max_size) 22 | { 23 | filo_t *pfilo = safe_malloc(sizeof(filo_t)); 24 | void *buffer = safe_malloc(elem_size * max_size); 25 | filo_init(pfilo, buffer, elem_size, max_size); 26 | return pfilo; 27 | } 28 | 29 | void filo_free(filo_t *pfilo) 30 | { 31 | safe_free(pfilo->buffer); 32 | safe_free(pfilo); 33 | } 34 | 35 | int filo_push(filo_t *pfilo, void *elem) 36 | { 37 | if (pfilo->top >= pfilo->max_size) 38 | return -1; 39 | memcpy(pfilo->buffer + (pfilo->elem_size * pfilo->top), elem, pfilo->elem_size); 40 | pfilo->top++; 41 | return 0; 42 | } 43 | 44 | int filo_pop(filo_t *pfilo, void *elem, bool remove) 45 | { 46 | if (pfilo->top == 0) 47 | return -1; 48 | memcpy(elem, pfilo->buffer + (pfilo->elem_size * (pfilo->top - 1)), pfilo->elem_size); 49 | if (remove) 50 | pfilo->top--; 51 | return 0; 52 | } 53 | 54 | size_t filo_get_count(filo_t *pfilo) 55 | { 56 | return pfilo->top; 57 | } 58 | 59 | size_t filo_get_free_space(filo_t *pfilo) 60 | { 61 | return pfilo->max_size - pfilo->top; 62 | } 63 | -------------------------------------------------------------------------------- /src/hashmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define hash_fn hash32_djb2 11 | 12 | #define HASH_MAP_BASE_SIZE 32 13 | #define HASH_MAP_DENSITY_FACTOR 0.8 14 | #define MAP_DENSITY(map) \ 15 | ((double)(map)->count / (double)(map)->capacity) 16 | #define GET_POOL_POS(map, hash) \ 17 | (hash & (map->capacity - 1)) 18 | 19 | #if 0 20 | #define hash_check_key(m, i, k) \ 21 | if (strcmp(i->key, k) != 0) { \ 22 | printf("Error: Hash collusion in %s for %s/%s\n", m, k, i->key); \ 23 | } 24 | #else 25 | #define hash_check_key(m, i, k) 26 | #endif 27 | 28 | static size_t hash_map_count(hash_map_t *map) 29 | { 30 | size_t i, count = 0; 31 | hash_map_item_t *item; 32 | 33 | for (i = 0; i < map->capacity; i++) { 34 | item = map->pool[i]; 35 | while (item) { 36 | count += 1; 37 | item = item->next; 38 | } 39 | } 40 | return count; 41 | } 42 | 43 | static void hash_map_lint(hash_map_t *map) 44 | { 45 | size_t count; 46 | 47 | count = hash_map_count(map); 48 | ARG_UNUSED(count); /* for release builds */ 49 | assert(count == map->count); 50 | } 51 | 52 | static void hash_map_rehash(hash_map_t *map) 53 | { 54 | size_t pivot, target, old_capacity; 55 | hash_map_item_t *temp, *prev, *item; 56 | 57 | old_capacity = map->capacity; 58 | map->capacity <<= 1; /* double capacity each time */ 59 | map->pool = safe_realloc_zero(map->pool, 60 | old_capacity * sizeof(hash_map_item_t *), 61 | map->capacity * sizeof(hash_map_item_t *)); 62 | 63 | /* spread out the hashes in the range [0 .. old_capacity] */ 64 | for (pivot = 0; pivot < old_capacity; pivot++) { 65 | prev = NULL; 66 | item = map->pool[pivot]; 67 | while (item != NULL) { 68 | target = GET_POOL_POS(map, item->hash); 69 | assert(target < map->capacity); 70 | if (pivot != target) { 71 | /* remove item from pivot */ 72 | if (prev != NULL) 73 | prev->next = item->next; 74 | else 75 | map->pool[pivot] = item->next; 76 | temp = item->next; 77 | /* prepend item, to target.head */ 78 | item->next = map->pool[target]; 79 | map->pool[target] = item; 80 | item = temp; 81 | } else { 82 | prev = item; 83 | item = item->next; 84 | } 85 | } 86 | } 87 | hash_map_lint(map); 88 | } 89 | 90 | void hash_map_init(hash_map_t *map) 91 | { 92 | size_t size; 93 | 94 | size = round_up_pow2(HASH_MAP_BASE_SIZE); 95 | map->pool = safe_calloc(size, sizeof(hash_map_item_t *)); 96 | map->capacity = size; 97 | map->count = 0; 98 | } 99 | 100 | void hash_map_free(hash_map_t *map, hash_map_callback_t callback) 101 | { 102 | size_t pos = 0; 103 | hash_map_item_t *item, *next; 104 | 105 | while (pos < map->capacity) { 106 | item = map->pool[pos]; 107 | while (item != NULL) { 108 | callback(item->key, item->val); 109 | next = item->next; 110 | safe_free(item->key); 111 | safe_free(item); 112 | item = next; 113 | } 114 | pos += 1; 115 | } 116 | safe_free(map->pool); 117 | map->pool = NULL; 118 | map->capacity = 0; 119 | map->capacity = 0; 120 | } 121 | 122 | hash_t hash_map_insert(hash_map_t *map, const char *key, void *val) 123 | { 124 | hash_t hash; 125 | size_t pos; 126 | hash_map_item_t *prev, *item; 127 | 128 | if (MAP_DENSITY(map) > HASH_MAP_DENSITY_FACTOR) 129 | hash_map_rehash(map); 130 | 131 | hash = hash_fn(key, -1); 132 | pos = GET_POOL_POS(map, hash); 133 | item = prev = map->pool[pos]; 134 | while (item != NULL) { 135 | if (item->hash == hash) { 136 | /* key already exists update it */ 137 | hash_check_key("insert", item, key); 138 | if (strcmp(item->key, key) == 0) { 139 | item->val = val; 140 | return item->hash; 141 | } 142 | } 143 | prev = item; 144 | item = item->next; 145 | } 146 | 147 | /* insert new key */ 148 | item = safe_malloc(sizeof(hash_map_item_t)); 149 | item->hash = hash; 150 | item->key = safe_strdup(key); 151 | item->val = val; 152 | item->next = NULL; 153 | if (prev != NULL) 154 | prev->next = item; 155 | else 156 | map->pool[pos] = item; 157 | map->count += 1; 158 | return item->hash; 159 | } 160 | 161 | void *hash_map_get(hash_map_t *map, const char *key, hash_t key_hash) 162 | { 163 | hash_t hash; 164 | size_t pos; 165 | hash_map_item_t *item; 166 | 167 | hash = key ? hash_fn(key, -1) : key_hash; 168 | pos = GET_POOL_POS(map, hash); 169 | item = map->pool[pos]; 170 | while (item != NULL) { 171 | if (item->hash == hash) { 172 | hash_check_key("get", item, key); 173 | if (strcmp(item->key, key) == 0) { 174 | return item->val; 175 | } 176 | } 177 | item = item->next; 178 | } 179 | return NULL; 180 | } 181 | 182 | void *hash_map_delete(hash_map_t *map, const char *key, hash_t key_hash) 183 | { 184 | void *val = NULL; 185 | hash_t hash; 186 | size_t pos; 187 | hash_map_item_t *prev = NULL, *item; 188 | 189 | hash = key ? hash_fn(key, -1) : key_hash; 190 | pos = GET_POOL_POS(map, hash); 191 | item = map->pool[pos]; 192 | while (item != NULL) { 193 | if (item->hash == hash) { 194 | hash_check_key("delete", item, key); 195 | if (strcmp(item->key, key) == 0) { 196 | break; 197 | } 198 | } 199 | prev = item; 200 | item = item->next; 201 | } 202 | if (item != NULL) { /* key found */ 203 | val = item->val; 204 | if (prev != NULL) 205 | prev->next = item->next; 206 | else 207 | map->pool[pos] = item->next; 208 | safe_free(item->key); 209 | safe_free(item); 210 | map->count -= 1; 211 | } 212 | return val; 213 | } 214 | 215 | void hash_map_it_init(hash_map_iterator_t *it, hash_map_t *map) 216 | { 217 | it->map = map; 218 | it->item = NULL; 219 | it->pos = 0; 220 | } 221 | 222 | int hash_map_it_next(hash_map_iterator_t *it, char **key, void **val) 223 | { 224 | hash_map_item_t *cur; 225 | 226 | if (it->item != NULL) { 227 | cur = it->item; 228 | it->item = cur->next; 229 | } else { 230 | while (it->pos < it->map->capacity && 231 | it->map->pool[it->pos] == NULL) 232 | it->pos++; 233 | if (it->pos >= it->map->capacity || 234 | it->map->pool[it->pos] == NULL) 235 | return -1; 236 | cur = it->map->pool[it->pos]; 237 | it->item = cur->next; 238 | it->pos++; 239 | } 240 | *key = cur->key; 241 | *val = cur->val; 242 | return 0; 243 | } 244 | -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | 9 | #include 10 | 11 | void list_init(list_t *list) 12 | { 13 | list->head = NULL; 14 | list->tail = NULL; 15 | } 16 | 17 | void list_append(list_t *list, node_t *node) 18 | { 19 | node->prev = list->tail; 20 | node->next = NULL; 21 | if (list->tail) { 22 | list->tail->next = node; 23 | } 24 | list->tail = node; 25 | if (!list->head) { 26 | list->head = node; 27 | } 28 | } 29 | 30 | void list_appendleft(list_t *list, node_t *node) 31 | { 32 | node->prev = NULL; 33 | node->next = list->head; 34 | if (list->head) { 35 | list->head->prev = node; 36 | } 37 | list->head = node; 38 | if (!list->tail) { 39 | list->tail = node; 40 | } 41 | } 42 | 43 | int list_pop(list_t *list, node_t **node) 44 | { 45 | if (!list->tail) { 46 | return -1; 47 | } 48 | *node = list->tail; 49 | list->tail = list->tail->prev; 50 | if (list->tail) { 51 | list->tail->next = NULL; 52 | } 53 | else { 54 | list->head = NULL; 55 | } 56 | return 0; 57 | } 58 | 59 | int list_popleft(list_t *list, node_t **node) 60 | { 61 | if (!list->head) { 62 | return -1; 63 | } 64 | *node = list->head; 65 | list->head = list->head->next; 66 | if (list->head) { 67 | list->head->prev = NULL; 68 | } 69 | else { 70 | list->tail = NULL; 71 | } 72 | return 0; 73 | } 74 | 75 | void list_remove_node(list_t *list, node_t *node) 76 | { 77 | if (node->prev == NULL) { 78 | /* remove head */ 79 | list->head = node->next; 80 | if (list->head == NULL) 81 | list->tail = NULL; 82 | else 83 | list->head->prev = NULL; 84 | } 85 | else if (node->next == NULL) { 86 | /* remove tail */ 87 | list->tail = node->prev; 88 | if (list->tail == NULL) 89 | list->head = NULL; 90 | else 91 | list->tail->next = NULL; 92 | } 93 | else { 94 | /* remove in-between */ 95 | node->next->prev = node->prev; 96 | node->prev->next = node->next; 97 | } 98 | } 99 | 100 | node_t *list_find_node(list_t *list, node_t *node) 101 | { 102 | node_t *p; 103 | 104 | p = list->head; 105 | while (p && p != node) 106 | p = p->next; 107 | return p; 108 | } 109 | 110 | bool list_check_links(node_t *p1, node_t *p2) 111 | { 112 | node_t *p1_prev, *p2_next; 113 | 114 | if (p1 == NULL || p2 == NULL) 115 | return false; 116 | 117 | if (p1 == p2) 118 | return true; 119 | 120 | p1_prev = p1->prev; 121 | p2_next = p2->next; 122 | 123 | while (p1 && p2 && p1 != p2 && p1->next != p2->prev && 124 | p1->prev == p1_prev && p2->next == p2_next) { 125 | p1_prev = p1; 126 | p1 = p1->next; 127 | 128 | p2_next = p2; 129 | p2 = p2->prev; 130 | } 131 | 132 | return (p1 && p2) && (p1 == p2 || p1->next == p2->prev); 133 | } 134 | 135 | int list_remove_nodes(list_t *list, node_t *start, node_t *end) 136 | { 137 | /* check if start in list and the range [start:end] is a valid list */ 138 | if (list_find_node(list, start) == NULL || 139 | list_check_links(start, end) == 0) 140 | return -1; 141 | 142 | if (start->prev == NULL) { 143 | /* remove from head */ 144 | list->head = end->next; 145 | if (list->head == NULL) 146 | list->head = NULL; 147 | else 148 | list->head->prev = NULL; 149 | } 150 | else if (end->next == NULL) { 151 | /* remove upto tail */ 152 | start->prev->next = NULL; 153 | list->tail = start->prev; 154 | } else { 155 | /* remove in-between */ 156 | start->prev->next = end->next; 157 | end->next->prev = start->prev; 158 | } 159 | 160 | return 0; 161 | } 162 | 163 | void list_insert_node(list_t *list, node_t *after, node_t *newNode) 164 | { 165 | node_t *next; 166 | 167 | if (after == NULL) { 168 | /* insert at head */ 169 | next = list->head; 170 | list->head = newNode; 171 | } else { 172 | next = after->next; 173 | after->next = newNode; 174 | } 175 | newNode->prev = after; 176 | newNode->next = next; 177 | next->prev = newNode; 178 | } 179 | 180 | int list_insert_nodes(list_t *list, node_t *after, node_t *start, node_t *end) 181 | { 182 | node_t *next; 183 | 184 | /* check if nodes is a valid list */ 185 | if (list_check_links(start, end) == 0) 186 | return -1; 187 | 188 | if (list->head == NULL) { 189 | /* If list is empty, the nodes becomes the list */ 190 | list->head = start; 191 | list->tail = end; 192 | } 193 | else if (after == NULL) { 194 | /* if 'after' node is not given prepend the nodes to list */ 195 | end->next = list->head; 196 | list->head = start; 197 | list->head->prev = NULL; 198 | } 199 | else { 200 | /* insert nodes into list after the 'after' node */ 201 | next = after->next; 202 | after->next = start; 203 | start->prev = after; 204 | end->next = next; 205 | if (next != NULL) 206 | next->prev = end; 207 | else 208 | list->tail = end; 209 | } 210 | 211 | return 0; 212 | } 213 | 214 | /*--- singly-linked list ---*/ 215 | 216 | void slist_init(slist_t *list) 217 | { 218 | list->head = NULL; 219 | } 220 | 221 | void slist_append(slist_t *list, snode_t *after, snode_t *node) 222 | { 223 | snode_t *p; 224 | 225 | p = after ? after : list->head; 226 | if (p == NULL) { 227 | list->head = node; 228 | } else { 229 | while (p && p->next) 230 | p = p->next; 231 | p->next = node; 232 | } 233 | node->next = NULL; 234 | } 235 | 236 | void slist_appendleft(slist_t *list, snode_t *node) 237 | { 238 | node->next = list->head; 239 | list->head = node; 240 | } 241 | 242 | int slist_pop(slist_t *list, snode_t *after, snode_t **node) 243 | { 244 | snode_t *n1, *n2; 245 | 246 | if (list->head == NULL) 247 | return -1; 248 | if (list->head->next == NULL) { 249 | *node = list->head; 250 | list->head = NULL; 251 | } else { 252 | n1 = after ? after : list->head; 253 | n2 = n1->next; 254 | while (n2 && n2->next) { 255 | n1 = n1->next; n2 = n2->next; 256 | } 257 | n1->next = NULL; 258 | *node = n2; 259 | } 260 | return 0; 261 | } 262 | 263 | int slist_popleft(slist_t *list, snode_t **node) 264 | { 265 | if (list->head == NULL) 266 | return -1; 267 | 268 | *node = list->head; 269 | list->head = list->head->next; 270 | return 0; 271 | } 272 | 273 | int slist_remove_node(slist_t *list, snode_t *node) 274 | { 275 | snode_t *prev = NULL, *cur; 276 | 277 | cur = list->head; 278 | while (cur && cur != node) { 279 | prev = cur; 280 | cur = cur->next; 281 | } 282 | if (cur == NULL) 283 | return -1; 284 | if (prev == NULL) 285 | list->head = cur->next; 286 | else 287 | prev->next = cur->next; 288 | return 0; 289 | } 290 | 291 | void slist_insert_node(slist_t *list, snode_t *after, snode_t *newNode) 292 | { 293 | if (after == NULL) { 294 | /* same as append left */ 295 | newNode->next = list->head; 296 | list->head = newNode; 297 | } else { 298 | /* assert after in list here? */ 299 | newNode->next = after->next; 300 | after->next = newNode; 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /src/logger.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | /* To disable warnings about use of strncpy and suggestions to use the more 8 | * secure variant strncpy_s in MSVC */ 9 | #define _CRT_SECURE_NO_WARNINGS 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #if (!defined(_WIN32) && !defined(_WIN64)) 18 | #include 19 | #endif 20 | 21 | #include 22 | 23 | #if (defined(_WIN32) || defined(_WIN64)) 24 | /* 25 | * For MSVC, we are expected use _write() and the type of len is unsigned int 26 | * instead of size_t in unix. 27 | */ 28 | #define write(fd, data, len) (size_t)_write((fd), (data), (unsigned int)(len)) 29 | #endif 30 | 31 | #define RED "\x1B[31m" 32 | #define GRN "\x1B[32m" 33 | #define YEL "\x1B[33m" 34 | #define BLU "\x1B[34m" 35 | #define MAG "\x1B[35m" 36 | #define CYN "\x1B[36m" 37 | #define WHT "\x1B[37m" 38 | #define RESET "\x1B[0m" 39 | 40 | logger_t default_logger = { 41 | .file = NULL, 42 | .flags = LOGGER_FLAG_NONE, 43 | .log_level = LOG_INFO, 44 | .name = "GLOBAL", 45 | .puts = puts, 46 | .root_path = NULL, 47 | .cb = NULL, 48 | }; 49 | 50 | static const char *log_level_colors[LOG_MAX_LEVEL] = { 51 | RED, RED, RED, RED, 52 | YEL, MAG, GRN, RESET 53 | }; 54 | 55 | static const char *log_level_names[LOG_MAX_LEVEL] = { 56 | "EMERG", "ALERT", "CRIT ", "ERROR", 57 | "WARN ", "NOTIC", "INFO ", "DEBUG" 58 | }; 59 | 60 | static inline void logger_log_set_color(logger_t *ctx, const char *color) 61 | { 62 | #if !defined(__BARE_METAL__) 63 | size_t ret, len; 64 | 65 | if (ctx->flags & LOGGER_FLAG_NO_COLORS) 66 | return; 67 | if (ctx->file && isatty(fileno(ctx->file))) { 68 | len = strnlen(color, 8); 69 | ret = write(fileno(ctx->file), color, len); 70 | assert(ret == len); 71 | ARG_UNUSED(ret); /* squash warning in Release builds */ 72 | } 73 | #else 74 | ARG_UNUSED(ctx); 75 | ARG_UNUSED(color); 76 | #endif 77 | } 78 | 79 | static const char *get_tstamp() 80 | { 81 | static char time_buf[24]; 82 | add_iso8601_utc_datetime(time_buf, sizeof(time_buf)); 83 | return time_buf; 84 | } 85 | 86 | static int terminate_log_line(char *buf, int len) 87 | { 88 | while (len && buf[--len] == '\0'); 89 | 90 | if (buf[len] != '\n') 91 | buf[++len] = '\n'; 92 | buf[++len] = '\0'; 93 | return len; 94 | } 95 | 96 | #define LOG_BUF_LEN 192 97 | 98 | __format_printf(5, 6) 99 | int __logger_log(logger_t *ctx, int log_level, const char *file, unsigned long line, 100 | const char *fmt, ...) 101 | { 102 | int len = 0; 103 | va_list args; 104 | char buf[LOG_BUF_LEN + 2] = {0}; /* +2 for tailing '\n' and '\0' */ 105 | 106 | if (!ctx) 107 | ctx = &default_logger; 108 | 109 | const char *filename = strrchr(file, PATH_SEPARATOR); 110 | if (filename) 111 | { 112 | file = filename + 1; 113 | } 114 | 115 | if (!ctx->cb) { 116 | if (log_level < LOG_EMERG || 117 | log_level >= LOG_MAX_LEVEL || 118 | log_level > ctx->log_level) 119 | return 0; 120 | /* print module and log_level prefix */ 121 | len = snprintf(buf, LOG_BUF_LEN, "%s: %s %11s:%-4lu [%s] ", 122 | ctx->name, get_tstamp(), file, line, 123 | log_level_names[log_level]); 124 | if (len > LOG_BUF_LEN) 125 | goto out; 126 | } 127 | 128 | /* Print the actual message */ 129 | va_start(args, fmt); 130 | len += vsnprintf(buf + len, LOG_BUF_LEN - len, fmt, args); 131 | va_end(args); 132 | if (len > LOG_BUF_LEN) 133 | goto out; 134 | 135 | out: 136 | if (len > LOG_BUF_LEN) 137 | len = LOG_BUF_LEN; 138 | 139 | /* If the log line doesn't already end with a new line, add it */ 140 | len = terminate_log_line(buf, len); 141 | 142 | if (ctx->cb) { 143 | ctx->cb(log_level, file, line, buf); 144 | } else { 145 | logger_log_set_color(ctx, log_level_colors[log_level]); 146 | if (ctx->file) 147 | fputs(buf, ctx->file); 148 | else 149 | ctx->puts(buf); 150 | logger_log_set_color(ctx, RESET); 151 | } 152 | 153 | return len; 154 | } 155 | 156 | void logger_get_default(logger_t *ctx) 157 | { 158 | memcpy(ctx, &default_logger, sizeof(default_logger)); 159 | } 160 | 161 | void logger_set_default(logger_t *logger) 162 | { 163 | default_logger = *logger; 164 | } 165 | 166 | void logger_set_log_level(logger_t *ctx, int log_level) 167 | { 168 | ctx->log_level = log_level; 169 | } 170 | 171 | void logger_set_put_fn(logger_t *ctx, log_puts_fn_t fn) 172 | { 173 | ctx->puts = fn; 174 | } 175 | 176 | void logger_set_file(logger_t *ctx, FILE *f) 177 | { 178 | ctx->file = f; 179 | } 180 | 181 | void logger_set_name(logger_t *ctx, const char *name) 182 | { 183 | if (!name) 184 | return; 185 | strncpy(ctx->name, name, LOGGER_NAME_MAXLEN); 186 | ctx->name[LOGGER_NAME_MAXLEN - 1] = '\0'; 187 | } 188 | 189 | int logger_init(logger_t *ctx, int log_level, const char *name, 190 | const char *root_path, log_puts_fn_t puts_fn, FILE *file, 191 | log_callback_fn_t cb, int flags) 192 | { 193 | if (!puts_fn && !file && !cb) 194 | return -1; 195 | 196 | ctx->log_level = log_level; 197 | ctx->root_path = root_path; 198 | ctx->puts = puts_fn; 199 | ctx->file = file; 200 | ctx->flags = flags; 201 | ctx->cb = cb; 202 | if (ctx->file || ctx->cb) 203 | ctx->flags |= LOGGER_FLAG_NO_COLORS; 204 | logger_set_name(ctx, name); 205 | return 0; 206 | } 207 | -------------------------------------------------------------------------------- /src/memory.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | static void die_oom(const char *msg, size_t count, size_t size) 14 | { 15 | fprintf(stderr, "fatal: %s() out of memory during alloc for %zu*%zu\n", 16 | msg, count, size); 17 | exit(-1); 18 | } 19 | 20 | void safe_free(void *p) 21 | { 22 | if (p != NULL) { 23 | free(p); 24 | } 25 | } 26 | 27 | void *safe_malloc(size_t size) 28 | 29 | { 30 | void *p; 31 | 32 | p = malloc(size); 33 | 34 | if (p == NULL) 35 | die_oom("malloc", 1, size); 36 | 37 | return p; 38 | } 39 | 40 | void *safe_calloc(size_t count, size_t size) 41 | { 42 | void *p; 43 | 44 | p = calloc(count, size); 45 | 46 | if (p == NULL) 47 | die_oom("calloc", count, size); 48 | 49 | return p; 50 | } 51 | 52 | void *safe_strdup(const char *s) 53 | { 54 | char *p; 55 | 56 | p = strdup(s); 57 | 58 | if (p == NULL) 59 | die_oom("strdup", 1, strlen(s)); 60 | 61 | return p; 62 | } 63 | 64 | void *safe_realloc(void *data, size_t size) 65 | { 66 | void *p; 67 | 68 | p = realloc(data, size); 69 | if (p == NULL) 70 | die_oom("realloc", 1, size); 71 | 72 | return p; 73 | } 74 | 75 | void *safe_realloc_zero(void *data, size_t old_size, size_t new_size) 76 | { 77 | void *p; 78 | 79 | assert(old_size != new_size); 80 | 81 | p = safe_realloc(data, new_size); 82 | if (new_size > old_size) { 83 | memset((unsigned char *)p + old_size, 0, new_size - old_size); 84 | } 85 | return p; 86 | } 87 | 88 | -------------------------------------------------------------------------------- /src/pcap_gen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | /** 8 | * See https://wiki.wireshark.org/Development/LibpcapFileFormat for packet 9 | * strcture documentation. 10 | */ 11 | 12 | #define PCAP_MAGIC_NUM 0xa1b2c3d4 13 | #define PCAP_VERSION_MAJOR 2 14 | #define PCAP_VERSION_MINOR 4 15 | #define PCAP_CACHE_SIZE (1 << 12) 16 | 17 | PACK(struct pcap_header { 18 | uint32_t magic_number; 19 | uint16_t version_major; 20 | uint16_t version_minor; 21 | uint32_t this_zone; 22 | int32_t sigfigs; 23 | uint32_t snap_len; 24 | uint32_t link_type; 25 | }); 26 | 27 | PACK(struct pcap_record_header { 28 | uint32_t ts_sec; 29 | uint32_t ts_usec; 30 | uint32_t incl_len; 31 | uint32_t orig_len; 32 | }); 33 | 34 | pcap_t *pcap_start(char *path, uint32_t max_packet_size, uint32_t link_type) 35 | { 36 | pcap_t *cap; 37 | struct pcap_header header; 38 | int ret = 0; 39 | 40 | cap = malloc(sizeof(*cap)); 41 | if (cap == NULL) 42 | return NULL; 43 | cap->cache = calloc(1, PCAP_CACHE_SIZE); 44 | if (cap->cache == NULL) { 45 | free(cap); 46 | return NULL; 47 | } 48 | 49 | header.magic_number = PCAP_MAGIC_NUM; 50 | header.version_major = PCAP_VERSION_MAJOR; 51 | header.version_minor = PCAP_VERSION_MINOR; 52 | header.this_zone = 0; 53 | header.sigfigs = 0; 54 | header.snap_len = max_packet_size; 55 | header.link_type = link_type; 56 | 57 | cap->offset = 0; 58 | cap->num_packets = 0; 59 | cap->file = fopen(path, "wb"); 60 | if(cap->file == NULL) { 61 | free(cap->cache); 62 | free(cap); 63 | return NULL; 64 | } 65 | 66 | ret = fwrite(&header , sizeof(header), 1, cap->file); 67 | if (!ret) { 68 | free(cap->cache); 69 | free(cap); 70 | return NULL; 71 | } 72 | return cap; 73 | } 74 | 75 | static int pcap_flush(pcap_t *cap) 76 | { 77 | if (fwrite(cap->cache, cap->offset, 1, cap->file)) { 78 | cap->offset = 0; 79 | return fflush(cap->file); 80 | } 81 | return -1; 82 | } 83 | 84 | int pcap_add(pcap_t *cap, uint8_t *capture_data, uint32_t length) 85 | { 86 | struct pcap_record_header header; 87 | uint32_t sec, usec; 88 | 89 | if (cap->offset + sizeof(header) + length > PCAP_CACHE_SIZE) { 90 | if (pcap_flush(cap)) { 91 | return -1; 92 | } 93 | } 94 | 95 | get_time(&sec, &usec); 96 | header.ts_sec = sec; 97 | header.ts_usec = usec; 98 | header.orig_len = header.incl_len = length; 99 | 100 | memcpy((char *)cap->cache + cap->offset, &header, sizeof(header)); 101 | cap->offset += sizeof(header); 102 | 103 | memcpy((char *)cap->cache + cap->offset, capture_data, length); 104 | cap->offset += length; 105 | cap->num_packets++; 106 | return 0; 107 | } 108 | 109 | int pcap_stop(pcap_t *cap) 110 | { 111 | int ret = 0; 112 | 113 | ret = pcap_flush(cap); 114 | if (ret == 0) 115 | ret = fclose(cap->file); 116 | free(cap->cache); 117 | free(cap); 118 | return ret; 119 | } 120 | -------------------------------------------------------------------------------- /src/procutils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #define MAX_PROC_CMDLINE_PATH_SZ 40 21 | #define MAX_PROC_CMDLINE_ARG_SZ 200 22 | 23 | int read_pid(const char *file, int *pid) 24 | { 25 | int pid_val, ret = 0; 26 | FILE *fd; 27 | 28 | if (file == NULL) 29 | return -1; 30 | 31 | fd = fopen(file, "r"); 32 | if (fd == NULL) 33 | return -1; 34 | if (fscanf(fd, "%d", &pid_val) != 1) { 35 | printf("Failed to read PID from file %s\n", file); 36 | ret = -1; 37 | } 38 | fclose(fd); 39 | 40 | if (pid) 41 | *pid = pid_val; 42 | return ret; 43 | } 44 | 45 | int write_pid(const char *file) 46 | { 47 | FILE *fd; 48 | char pid_str[32]; 49 | 50 | if (file == NULL) 51 | return 1; 52 | 53 | fd = fopen(file, "w"); 54 | if (fd == NULL) { 55 | perror("Error opening pid_file to write"); 56 | return -1; 57 | } 58 | snprintf(pid_str, 32, "%d\n", getpid()); 59 | fputs(pid_str, fd); 60 | fclose(fd); 61 | return 0; 62 | } 63 | 64 | int o_redirect(int mode, const char *file) 65 | { 66 | int fd = -1; 67 | int has_err = 0; 68 | 69 | if (file != NULL) { 70 | fd = open(file, O_RDWR | O_CREAT | O_APPEND, 0644); 71 | if (fd == -1) { 72 | perror("opening log_file"); 73 | return -1; 74 | } 75 | } 76 | 77 | if (mode == 3 && file == NULL) { 78 | close(fileno(stdin)); 79 | close(fileno(stdout)); 80 | close(fileno(stderr)); 81 | return 0; 82 | } 83 | 84 | if (fd != -1 && mode & 0x01) { 85 | if (dup2(fd, fileno(stdout)) == -1) { 86 | perror("cannot redirect stdout to log_file"); 87 | has_err = -1; 88 | } 89 | } 90 | 91 | if (fd != -1 && mode & 0x02) { 92 | if (dup2(fd, fileno(stderr)) == -1) { 93 | perror("cannot redirect stderr to log_file"); 94 | has_err = -1; 95 | } 96 | } 97 | 98 | return has_err; 99 | } 100 | 101 | char *parse_proc_cmdline(unsigned pid, int pos) 102 | { 103 | char buf[MAX_PROC_CMDLINE_PATH_SZ + 1]; 104 | sprintf(buf, "/proc/%u/cmdline", pid); 105 | 106 | int c = 0, ndx = 0; 107 | FILE *fd = fopen(buf, "r"); 108 | if (fd == NULL) 109 | return NULL; 110 | while (--pos > 0) { 111 | for (c = fgetc(fd); (c != EOF) && (c != 0); c = fgetc(fd)); 112 | } 113 | if (feof(fd)) { 114 | fclose(fd); 115 | return NULL; 116 | } 117 | char *arg = safe_malloc((MAX_PROC_CMDLINE_ARG_SZ + 1) * sizeof(char)); 118 | while ((c = fgetc(fd)) != EOF) { 119 | arg[ndx++] = c; 120 | if ((c == '\0') || (ndx >= MAX_PROC_CMDLINE_ARG_SZ)) 121 | break; 122 | } 123 | arg[ndx] = 0; 124 | fclose(fd); 125 | if (ndx == 1) { 126 | safe_free(arg); 127 | return NULL; 128 | } 129 | return arg; 130 | } 131 | 132 | static int pid_cmp_func(const void *a, const void *b) 133 | { 134 | return ( *(int*)a - *(int*)b ); 135 | } 136 | 137 | static int pid_bsearch_func(const void *pkey, const void *pelem) 138 | { 139 | return ( *(int*)pkey - *(int*)pelem ); 140 | } 141 | 142 | static bool cmp_arg0_basename(const char *arg0, const char *basename) 143 | { 144 | char *ptr = NULL; 145 | path_extract(arg0, NULL, &ptr); 146 | if (ptr == NULL) 147 | return false; 148 | 149 | bool found = strncmp(basename, ptr, MAX_PROC_CMDLINE_ARG_SZ) == 0; 150 | safe_free(ptr); 151 | return found; 152 | } 153 | 154 | unsigned pid_of(const char* exe_name, unsigned *pomit_arr, size_t arr_len) 155 | { 156 | char *ptr; 157 | struct dirent *ent; 158 | if (!pomit_arr && arr_len) 159 | return 0; 160 | 161 | DIR *dir = opendir("/proc"); 162 | if (dir == NULL) 163 | return 0; 164 | if (pomit_arr) 165 | qsort(pomit_arr, arr_len, sizeof(*pomit_arr), pid_cmp_func); 166 | 167 | while ((ent = readdir(dir)) != NULL) { 168 | if (strlen(ent->d_name) == 0) 169 | continue; 170 | unsigned pid = (unsigned)strtoul(ent->d_name, &ptr, 10); 171 | if (*ptr != '\0') 172 | continue; 173 | if (pomit_arr && 174 | bsearch(&pid, pomit_arr, arr_len, sizeof(*pomit_arr), pid_bsearch_func)) 175 | continue; 176 | char *arg0 = parse_proc_cmdline(pid, 1); 177 | if (arg0 == NULL) 178 | continue; 179 | if (!strcmp(arg0, "/proc/self/exe")) { 180 | safe_free(arg0); 181 | continue; 182 | } 183 | if (!cmp_arg0_basename(arg0, exe_name)) { 184 | safe_free(arg0); 185 | continue; 186 | } 187 | safe_free(arg0); 188 | closedir(dir); 189 | return pid; 190 | } 191 | closedir(dir); 192 | return 0; 193 | } 194 | 195 | unsigned any_pid_of(const char* exe_name) 196 | { 197 | return pid_of(exe_name, NULL, 0); 198 | } 199 | -------------------------------------------------------------------------------- /src/queue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #include 9 | 10 | void queue_init(queue_t *queue) 11 | { 12 | list_init(&queue->list); 13 | } 14 | 15 | void queue_enqueue(queue_t *queue, queue_node_t *node) 16 | { 17 | list_append(&queue->list, node); 18 | } 19 | 20 | int queue_dequeue(queue_t *queue, queue_node_t **node) 21 | { 22 | return list_popleft(&queue->list, node); 23 | } 24 | 25 | int queue_peek_last(queue_t *queue, queue_node_t **node) 26 | { 27 | if (queue->list.tail == NULL) 28 | return -1; 29 | 30 | *node = queue->list.tail; 31 | return 0; 32 | } 33 | 34 | int queue_peek_first(queue_t *queue, queue_node_t **node) 35 | { 36 | if (queue->list.head == NULL) 37 | return -1; 38 | 39 | *node = queue->list.head; 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /src/random.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #ifdef __linux__ 8 | #include 9 | #endif 10 | #ifdef __APPLE__ 11 | #include 12 | #ifndef MAC_OS_X_VERSION_10_12 13 | #define MAC_OS_X_VERSION_10_12 101200 14 | #endif 15 | #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12 16 | #include 17 | #endif 18 | #endif 19 | 20 | #ifndef _WIN32 21 | bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len) 22 | { 23 | ssize_t ret = 0; 24 | size_t i; 25 | int fd; 26 | 27 | if (len > 256) { 28 | errno = EOVERFLOW; 29 | return false; 30 | } 31 | 32 | #if defined(__OpenBSD__) || \ 33 | (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || \ 34 | (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) 35 | if (!getentropy(out, len)) 36 | return true; 37 | #endif 38 | 39 | #if defined(__NR_getrandom) && defined(__linux__) 40 | if (syscall(__NR_getrandom, out, len, 0) == (ssize_t)len) 41 | return true; 42 | #endif 43 | 44 | fd = open("/dev/urandom", O_RDONLY); 45 | if (fd < 0) 46 | return false; 47 | for (errno = 0, i = 0; i < len; i += ret, ret = 0) { 48 | ret = read(fd, out + i, len - i); 49 | if (ret <= 0) { 50 | ret = errno ? -errno : -EIO; 51 | break; 52 | } 53 | } 54 | close(fd); 55 | errno = -ret; 56 | return i == len; 57 | } 58 | #else 59 | #include 60 | bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len) 61 | { 62 | return RtlGenRandom(out, len); 63 | } 64 | #endif -------------------------------------------------------------------------------- /src/serial.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | /** 8 | * Note: 9 | * This code is based on Teunis van Beelen's implementation. Its API and coding 10 | * style needed a lot of work before consumption and hence warrented a full 11 | * rewrite. His implementation can be found at https://www.teuniz.net/RS-232/ 12 | * 13 | * A good explanation can be found at https://www.cmrr.umn.edu/~strupp/serial.html 14 | */ 15 | 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | struct serial *serial_open(const char *device, int baud, const char *mode) 35 | { 36 | int status, tmp; 37 | int flow_control = 0, cflags = 0, ignore_parity = 0; 38 | struct serial *ctx; 39 | 40 | switch (baud) { 41 | case 50: baud = B50; break; 42 | case 75: baud = B75; break; 43 | case 110: baud = B110; break; 44 | case 134: baud = B134; break; 45 | case 150: baud = B150; break; 46 | case 200: baud = B200; break; 47 | case 300: baud = B300; break; 48 | case 600: baud = B600; break; 49 | case 1200: baud = B1200; break; 50 | case 1800: baud = B1800; break; 51 | case 2400: baud = B2400; break; 52 | case 4800: baud = B4800; break; 53 | case 9600: baud = B9600; break; 54 | case 19200: baud = B19200; break; 55 | case 38400: baud = B38400; break; 56 | case 57600: baud = B57600; break; 57 | case 115200: baud = B115200; break; 58 | case 230400: baud = B230400; break; 59 | default: 60 | printf("invalid baudrate\n"); 61 | return NULL; 62 | } 63 | 64 | tmp = strlen(mode); 65 | if (tmp < 3 || tmp > 4) { 66 | printf("invalid mode \"%s\"\n", mode); 67 | return NULL; 68 | } 69 | 70 | if (tmp == 4 && (mode[3] == 'F' || mode[3] == 'f')) 71 | flow_control = 1; 72 | 73 | switch (mode[0]) { 74 | case '8': cflags |= CS8; break; 75 | case '7': cflags |= CS7; break; 76 | case '6': cflags |= CS6; break; 77 | case '5': cflags |= CS5; break; 78 | default: 79 | printf("invalid number of data-bits '%c'\n", mode[0]); 80 | return NULL; 81 | } 82 | 83 | if (mode[1] == 'N' || mode[1] == 'n') { /* no parity */ 84 | cflags |= IGNPAR; 85 | ignore_parity = 1; 86 | } else { /* has parity */ 87 | cflags |= PARENB; 88 | if (mode[1] == 'O' || mode[1] == 'o') 89 | cflags |= PARODD; 90 | } 91 | 92 | if (mode[2] == '2') { 93 | cflags |= CSTOPB; 94 | } 95 | 96 | ctx = safe_calloc(1, sizeof(struct serial)); 97 | ctx->fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); 98 | if (ctx->fd == -1) { 99 | perror("unable to open comport"); 100 | goto error; 101 | } 102 | 103 | if (flock(ctx->fd, LOCK_EX | LOCK_NB) != 0) { 104 | close(ctx->fd); 105 | perror("another process has locked the comport"); 106 | goto error; 107 | } 108 | 109 | if (tcgetattr(ctx->fd, &ctx->old_termios) == -1) { 110 | close(ctx->fd); 111 | perror("unable to read portsettings"); 112 | goto error; 113 | } 114 | 115 | ctx->new_termios.c_cflag = cflags | CLOCAL | CREAD; 116 | if (flow_control) 117 | ctx->new_termios.c_cflag |= CRTSCTS; 118 | ctx->new_termios.c_iflag = ignore_parity ? IGNPAR : INPCK; 119 | ctx->new_termios.c_oflag = 0; 120 | ctx->new_termios.c_lflag = 0; 121 | 122 | /* block until n bytes are received */ 123 | ctx->new_termios.c_cc[VMIN] = 0; 124 | 125 | /* block until a timer expires (n * 100 mSec.) */ 126 | ctx->new_termios.c_cc[VTIME] = 0; 127 | 128 | cfsetispeed(&ctx->new_termios, baud); 129 | cfsetospeed(&ctx->new_termios, baud); 130 | 131 | if (tcsetattr(ctx->fd, TCSANOW, &ctx->new_termios) == -1) { 132 | tcsetattr(ctx->fd, TCSANOW, &ctx->old_termios); 133 | close(ctx->fd); 134 | perror("unable to adjust portsettings"); 135 | goto error; 136 | } 137 | 138 | if (ioctl(ctx->fd, TIOCMGET, &status) == -1) { 139 | tcsetattr(ctx->fd, TCSANOW, &ctx->old_termios); 140 | perror("unable to get portstatus"); 141 | goto error; 142 | } 143 | 144 | status |= TIOCM_DTR; /* turn on DTR */ 145 | status |= TIOCM_RTS; /* turn on RTS */ 146 | 147 | if (ioctl(ctx->fd, TIOCMSET, &status) == -1) { 148 | tcsetattr(ctx->fd, TCSANOW, &ctx->old_termios); 149 | perror("unable to set portstatus"); 150 | goto error; 151 | } 152 | 153 | return ctx; 154 | 155 | error: 156 | if (ctx->fd != 0 && ctx->fd != -1) 157 | flock(ctx->fd, LOCK_UN); 158 | safe_free(ctx); 159 | return NULL; 160 | } 161 | 162 | void serial_close(struct serial *ctx) 163 | { 164 | int status; 165 | 166 | if (ctx == NULL) 167 | return; 168 | 169 | if (ioctl(ctx->fd, TIOCMGET, &status) == -1) { 170 | perror("unable to get portstatus"); 171 | } 172 | 173 | status &= ~TIOCM_DTR; /* turn off DTR */ 174 | status &= ~TIOCM_RTS; /* turn off RTS */ 175 | 176 | if (ioctl(ctx->fd, TIOCMSET, &status) == -1) { 177 | perror("unable to set portstatus"); 178 | } 179 | 180 | tcsetattr(ctx->fd, TCSANOW, &ctx->old_termios); 181 | close(ctx->fd); 182 | flock(ctx->fd, LOCK_UN); 183 | 184 | safe_free(ctx); 185 | } 186 | 187 | int serial_read(struct serial *ctx, unsigned char *buf, int size) 188 | { 189 | int n; 190 | 191 | n = read(ctx->fd, buf, size); 192 | if (n < 0) { 193 | if (errno == EAGAIN) 194 | return 0; 195 | } 196 | return n; 197 | } 198 | 199 | int serial_write(struct serial *ctx, unsigned char *buf, int size) 200 | { 201 | int n; 202 | 203 | n = write(ctx->fd, buf, size); 204 | if (n < 0) { 205 | if (errno == EAGAIN) 206 | return 0; 207 | } 208 | return n; 209 | } 210 | 211 | void serial_flush_rx(struct serial *ctx) 212 | { 213 | tcflush(ctx->fd, TCIFLUSH); 214 | } 215 | 216 | void serial_flush_tx(struct serial *ctx) 217 | { 218 | tcflush(ctx->fd, TCOFLUSH); 219 | } 220 | 221 | void serial_flush(struct serial *ctx) 222 | { 223 | tcflush(ctx->fd, TCIOFLUSH); 224 | } 225 | 226 | int serial_get_dcd(struct serial *ctx) 227 | { 228 | int status; 229 | 230 | ioctl(ctx->fd, TIOCMGET, &status); 231 | 232 | return (status & TIOCM_CAR) ? 1 : 0; 233 | } 234 | 235 | int serial_get_rng(struct serial *ctx) 236 | { 237 | int status; 238 | 239 | ioctl(ctx->fd, TIOCMGET, &status); 240 | 241 | return (status & TIOCM_RNG) ? 1 : 0; 242 | } 243 | 244 | int serial_get_cts(struct serial *ctx) 245 | { 246 | int status; 247 | 248 | ioctl(ctx->fd, TIOCMGET, &status); 249 | 250 | return (status & TIOCM_CTS) ? 1 : 0; 251 | } 252 | 253 | int serial_get_dsr(struct serial *ctx) 254 | { 255 | int status; 256 | 257 | ioctl(ctx->fd, TIOCMGET, &status); 258 | 259 | return (status & TIOCM_DSR) ? 1 : 0; 260 | } 261 | 262 | void serial_assert_dtr(struct serial *ctx, int state) 263 | { 264 | int status; 265 | 266 | if (ioctl(ctx->fd, TIOCMGET, &status) == -1) { 267 | perror("unable to get portstatus"); 268 | } 269 | 270 | if (state) 271 | status |= TIOCM_DTR; /* turn on RTS */ 272 | else 273 | status &= ~TIOCM_DTR; /* turn off RTS */ 274 | 275 | if (ioctl(ctx->fd, TIOCMSET, &status) == -1) { 276 | perror("unable to set portstatus"); 277 | } 278 | } 279 | 280 | void serial_assert_rts(struct serial *ctx, int state) 281 | { 282 | int status; 283 | 284 | if (ioctl(ctx->fd, TIOCMGET, &status) == -1) { 285 | perror("unable to get portstatus"); 286 | } 287 | 288 | if (state) 289 | status |= TIOCM_RTS; /* turn on RTS */ 290 | else 291 | status &= ~TIOCM_RTS; /* turn off RTS */ 292 | 293 | if (ioctl(ctx->fd, TIOCMSET, &status) == -1) { 294 | perror("unable to set portstatus"); 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/slab.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | PACK(struct slab_unit { 14 | uint32_t leased; 15 | uint32_t canary; 16 | uint8_t data[]; 17 | }); 18 | 19 | int slab_init(slab_t *slab, size_t slab_size, 20 | uint8_t *blob, size_t blob_size) 21 | { 22 | slab->size = ROUND_UP(slab_size, sizeof(void *)) + 23 | sizeof(struct slab_unit); 24 | 25 | if (slab->size > blob_size) 26 | return -1; 27 | 28 | slab->count = blob_size / slab->size; 29 | memset(blob, 0, blob_size); 30 | slab->blob = blob; 31 | 32 | return (int)slab->count; 33 | } 34 | 35 | int slab_alloc(slab_t *slab, void **block) 36 | { 37 | size_t i; 38 | struct slab_unit *p; 39 | 40 | for (i = 0; i < slab->count; i++) { 41 | p = (struct slab_unit *)(slab->blob + (i * slab->size)); 42 | if (!p->leased) { 43 | p->leased = true; 44 | p->canary = 0xdeadbeaf; 45 | *block = p->data; 46 | return 0; 47 | } 48 | } 49 | 50 | return -1; 51 | } 52 | 53 | int slab_free(slab_t *slab, void *block) 54 | { 55 | ARG_UNUSED(slab); 56 | struct slab_unit *p = CONTAINER_OF(block, struct slab_unit, data); 57 | 58 | if (p->canary != 0xdeadbeaf) 59 | return -1; 60 | 61 | p->leased = false; 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /src/sockutils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | int sock_unix_listen(const char *path, int max_clients) 19 | { 20 | int rc, fd; 21 | socklen_t len; 22 | struct sockaddr_un sock_serv; 23 | 24 | fd = socket(AF_UNIX, SOCK_STREAM, 0); 25 | if (fd < 0) 26 | return -1; 27 | 28 | sock_serv.sun_family = AF_UNIX; 29 | strcpy(sock_serv.sun_path, path); 30 | len = sizeof(sock_serv.sun_family) + strlen(path) + 1; 31 | unlink(path); 32 | 33 | rc = bind(fd, (const struct sockaddr *)&sock_serv, len); 34 | if (rc < 0) 35 | return -1; 36 | 37 | rc = listen(fd, max_clients); 38 | if (rc < 0) 39 | return -1; 40 | 41 | return fd; 42 | } 43 | 44 | int sock_unix_connect(const char *path) 45 | { 46 | int rc, fd; 47 | socklen_t len; 48 | struct sockaddr_un serv_addr; 49 | 50 | fd = socket(AF_UNIX, SOCK_STREAM, 0); 51 | if (fd < 0) 52 | return -1; 53 | 54 | serv_addr.sun_family = AF_UNIX; 55 | strcpy(serv_addr.sun_path, path); 56 | len = sizeof(serv_addr.sun_family) + strlen(serv_addr.sun_path) + 1; 57 | 58 | rc = connect(fd, (struct sockaddr *)&serv_addr, len); 59 | if (rc != 0) 60 | return -1; 61 | 62 | return fd; 63 | } 64 | 65 | int sock_stream_connect(const char *host, int port) 66 | { 67 | int fd; 68 | struct sockaddr_in addr = { 69 | .sin_family = AF_INET, 70 | .sin_port = htons(port) 71 | }; 72 | 73 | if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 74 | perror("Socket creation failed!"); 75 | return -1; 76 | } 77 | if (inet_pton(AF_INET, host, &addr.sin_addr) <= 0) { 78 | perror("Invalid address / Address not supported!"); 79 | return -1; 80 | } 81 | if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 82 | perror("connect failed"); 83 | return -1; 84 | } 85 | return fd; 86 | } 87 | 88 | int sock_stream_listen(int port, int nr_clients) 89 | { 90 | int fd, opt = 1; 91 | struct sockaddr_in address = { 92 | .sin_family = AF_INET, 93 | .sin_addr = { 94 | .s_addr = INADDR_ANY, 95 | }, 96 | .sin_port = htons(port), 97 | }; 98 | 99 | if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 100 | perror("socket failed"); 101 | return -1; 102 | } 103 | if (setsockopt(fd, SOL_SOCKET, 104 | SO_REUSEADDR /*| SO_REUSEPORT*/, &opt, sizeof(opt))) { 105 | perror("setsockopt failed"); 106 | return -1; 107 | } 108 | if (bind(fd, (struct sockaddr*)&address, sizeof(address)) < 0) { 109 | perror("bind failed"); 110 | return -1; 111 | } 112 | if (listen(fd, nr_clients) < 0) { 113 | perror("listen failed"); 114 | return -1; 115 | } 116 | return fd; 117 | } 118 | 119 | 120 | int sock_wait(int listening_socket_fd) 121 | { 122 | int client_fd; 123 | struct sockaddr_in addr; 124 | size_t addr_len = sizeof(addr); 125 | 126 | client_fd = accept(listening_socket_fd, (struct sockaddr *)&addr, 127 | (socklen_t *)&addr_len); 128 | if (client_fd < 0) { 129 | perror("accept failed"); 130 | return -1; 131 | } 132 | return client_fd; 133 | } 134 | 135 | int sock_shutdown(int listening_socket_fd) 136 | { 137 | return shutdown(listening_socket_fd, SHUT_RDWR); 138 | } 139 | -------------------------------------------------------------------------------- /src/stack.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | */ 7 | 8 | #include 9 | 10 | void stack_init(stack_t *stack) 11 | { 12 | slist_init(&stack->list); 13 | } 14 | 15 | void stack_push(stack_t *stack, stack_node_t *node) 16 | { 17 | slist_appendleft(&stack->list, node); 18 | } 19 | 20 | int stack_pop(stack_t *stack, stack_node_t **node) 21 | { 22 | return slist_popleft(&stack->list, node); 23 | } 24 | 25 | int stack_get_top(stack_t *stack, stack_node_t **node) 26 | { 27 | if (stack->list.head == NULL) 28 | return -1; 29 | 30 | *node = stack->list.head; 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /src/strlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | void string_create(string_t *s, const char *buf, size_t len) 16 | { 17 | s->buf = safe_malloc(sizeof(char) * (len + 1)); 18 | s->len = 0; 19 | s->max_len = len; 20 | s->flags = STRING_ALLOCATED; 21 | if (buf != NULL) { 22 | memcpy(s->buf, buf, len); 23 | s->len = len; 24 | } 25 | s->buf[len] = '\0'; /* Make sure there is always a terminating null */ 26 | } 27 | 28 | void string_clone(string_t *dest, const string_t *src) 29 | { 30 | string_create(dest, src->buf, src->len); 31 | } 32 | 33 | int string_resize(string_t *s, size_t new_len) 34 | { 35 | if (!(s->flags & STRING_ALLOCATED)) 36 | return -1; 37 | 38 | s->buf = safe_realloc(s->buf, new_len + 1); 39 | s->max_len = new_len; 40 | if (s->len > new_len) { 41 | s->len = new_len; 42 | } 43 | s->buf[new_len] = '\0'; 44 | return 0; 45 | } 46 | 47 | void string_destroy(string_t *s) 48 | { 49 | if (s->flags & STRING_ALLOCATED) 50 | safe_free(s->buf); 51 | s->buf = NULL; 52 | s->flags = 0; 53 | s->len = 0; 54 | s->max_len = 0; 55 | } 56 | 57 | int string_printf(string_t *s, const char *mode, const char *fmt, ...) 58 | { 59 | int ret, start; 60 | va_list (args); 61 | 62 | start = (mode[0] == 'a') ? s->len : 0; 63 | va_start(args, fmt); 64 | ret = vsnprintf(s->buf + start, s->max_len - start, fmt, args); 65 | va_end(args); 66 | 67 | if ((start + ret) >= (int)s->max_len) { 68 | return -1; 69 | } 70 | 71 | s->len = start + ret; 72 | s->buf[s->len] = 0; 73 | return ret; 74 | } 75 | 76 | int string_copy(string_t *s, const char *mode, const char *str, size_t len) 77 | { 78 | int start; 79 | 80 | start = (mode[0] == 'a') ? s->len : 0; 81 | if ((start + len) > s->max_len) { 82 | if (mode[1] != 'f') { 83 | return -1; 84 | } 85 | len = s->max_len - start; 86 | } 87 | memcpy(s->buf + start, str, len); 88 | s->len = start + len; 89 | s->buf[s->len] = '\0'; 90 | return len; 91 | } 92 | 93 | int string_merge(string_t *primary, string_t *secondary) 94 | { 95 | size_t target; 96 | 97 | target = primary->len + secondary->len; 98 | if (target > primary->max_len) { 99 | if (string_resize(primary, target) != 0) { 100 | return -1; 101 | } 102 | } 103 | string_copy(primary, "a", secondary->buf, secondary->len); 104 | string_destroy(secondary); 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /src/strutils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | static inline int hex2int(char ch) 14 | { 15 | if (ch >= '0' && ch <= '9') 16 | return ch - '0'; 17 | ch &= ~0x20; // to upper case. 18 | if (ch >= 'A' && ch <= 'F') 19 | return ch - 'A' + 10; 20 | return -1; 21 | } 22 | 23 | static inline char int2hex(int v) 24 | { 25 | v &= 0x0f; // consider only the lower nibble 26 | return (v >= 0 && v <= 9) ? '0' + v : 'A' + (v - 10); 27 | } 28 | 29 | int atohstr(char *hstr, const uint8_t *arr, const int arr_len) 30 | { 31 | int i; 32 | 33 | for (i = 0; i < arr_len; i++) { 34 | hstr[(i * 2) + 0] = int2hex(arr[i] >> 4); 35 | hstr[(i * 2) + 1] = int2hex(arr[i]); 36 | } 37 | hstr[i * 2] = '\0'; 38 | 39 | return 0; 40 | } 41 | 42 | int hstrtoa(uint8_t *arr, const char *hstr) 43 | { 44 | int i, len, nib1, nib2; 45 | 46 | len = strlen(hstr); 47 | if ((len & 1) == 1 || len == 0) 48 | return -1; // Must have even number of chars 49 | len = len / 2; 50 | for (i = 0; i < len; i++) { 51 | nib1 = hex2int(hstr[(i * 2) + 0]); 52 | nib2 = hex2int(hstr[(i * 2) + 1]); 53 | if (nib1 == -1 || nib2 == -1) 54 | return -1; 55 | arr[i] = nib1 << 4 | nib2; 56 | } 57 | 58 | return len; 59 | } 60 | 61 | int safe_atoi(const char *a, int *i) 62 | { 63 | int val; 64 | 65 | if (a == NULL) 66 | return -1; 67 | val = atoi(a); 68 | if (val == 0 && a[0] != '0') 69 | return -1; 70 | *i = val; 71 | return 0; 72 | } 73 | 74 | char *safe_strncpy(char* dest, const char* src, size_t size) 75 | { 76 | strncpy(dest, src, size - 1); 77 | dest[size - 1] = '\0'; 78 | return dest; 79 | } 80 | 81 | int trim_suffix(char *str, const char *suffix) 82 | { 83 | int i, j; 84 | 85 | if (!str || !suffix) 86 | return -1; 87 | 88 | i = strlen(str); 89 | j = strlen(suffix); 90 | 91 | if (j > i) 92 | return -1; 93 | 94 | while (j > 0 && str[i-1] == suffix[j-1]) { 95 | i--; j--; 96 | } 97 | 98 | return str[i] = '\0'; 99 | } 100 | 101 | int rstrip(char *str) 102 | { 103 | int i; 104 | 105 | i = strlen(str); 106 | while (i > 0 && str[i - 1] == ' ') { 107 | str[i - 1] = '\0'; 108 | i -= 1; 109 | } 110 | return i; 111 | } 112 | 113 | /* Note: can return -ve */ 114 | int lstrip(char *str) 115 | { 116 | int i = 0, j = 0; 117 | 118 | while (str[i] && str[i] == ' ') 119 | i++; 120 | if (i == 0) 121 | return -1; 122 | while (str[i]) { 123 | str[j] = str[i]; 124 | i += 1; j += 1; 125 | } 126 | str[j] = '\0'; 127 | return (j > 0) ? j - 1 : 0; 128 | } 129 | 130 | int strip(char *str) 131 | { 132 | int rs_len, ls_len; 133 | 134 | rs_len = rstrip(str); 135 | ls_len = lstrip(str); /* can be -ve */ 136 | return (ls_len > 0) ? ls_len : rs_len; 137 | } 138 | 139 | size_t chomp(char *str) 140 | { 141 | size_t i; 142 | 143 | i = strlen(str); 144 | while (i > 0 && (str[i - 1] == '\n' || str[i - 1] == '\r')) { 145 | str[i - 1] = '\0'; 146 | i -= 1; 147 | } 148 | return i; 149 | } 150 | 151 | void remove_all(char *str, char c) 152 | { 153 | int i, j = 0; 154 | 155 | for (i = 0; str[i]; i++) { 156 | if (str[i] != c) 157 | str[j++] = str[i]; 158 | } 159 | str[j] = '\0'; 160 | } 161 | 162 | int split_string(char *buf, char *sep, char ***tokens) 163 | { 164 | char *tok, *rest, **toks = NULL; 165 | size_t length = 0, size = 0; 166 | int chunk = 16; 167 | 168 | tok = strtok_r(buf, sep, &rest); 169 | while (tok != NULL) { 170 | if (length >= size) { 171 | toks = safe_realloc(toks, sizeof(char *) * (size + chunk)); 172 | size += chunk; 173 | chunk <<= 2; 174 | } 175 | toks[length] = safe_strdup(tok); 176 | length += 1; 177 | tok = strtok_r(NULL, sep, &rest); 178 | } 179 | if (toks == NULL) { 180 | return -1; 181 | } 182 | toks = safe_realloc(toks, sizeof(char *) * (length + 1)); 183 | toks[length] = NULL; 184 | *tokens = toks; 185 | return 0; 186 | } 187 | 188 | int strcntchr(char *s, char c) 189 | { 190 | int i = 0, count = 0; 191 | 192 | while (s[i]) { 193 | if (s[i] == c) 194 | count += 1; 195 | i += 1; 196 | } 197 | return count; 198 | } 199 | 200 | int strisempty(char *s) 201 | { 202 | while (s && *s != '\0' && isspace((unsigned char)*s)) 203 | s++; 204 | 205 | return s == NULL || *s == '\0'; 206 | } 207 | 208 | uint32_t hash32_djb2(const char *str, int len) 209 | { 210 | int c; 211 | uint32_t hash = 5381; 212 | 213 | while ((c = *str++) && len-- != 0) { 214 | hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ 215 | } 216 | return hash; 217 | } 218 | 219 | uint32_t hash32_fnv(const char *str, int len) 220 | { 221 | uint32_t hval = 0; 222 | unsigned char *s = (unsigned char *)str; /* unsigned string */ 223 | 224 | /* FNV-1 hash */ 225 | while (*s && len-- != 0) { 226 | /* multiply by the 32 bit FNV magic prime mod 2^32 */ 227 | hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); 228 | hval ^= (uint32_t)*s++; 229 | } 230 | return hval; 231 | } 232 | 233 | uint64_t poly_hash(const char *str, int len) 234 | { 235 | int c; 236 | const int p = 31; 237 | const int m = 1e9 + 9; 238 | uint64_t hash = 0; 239 | uint64_t p_pow = 1; 240 | 241 | while ((c = *str++) && len-- != 0) { 242 | hash = (hash + (c - 'a' + 1) * p_pow) % m; 243 | p_pow = (p_pow * p) % m; 244 | } 245 | return hash; 246 | } 247 | 248 | char *str_sep(char **str, const char *sep) 249 | { 250 | char *start, *end; 251 | 252 | if (*str == NULL || *str[0] == '\0') 253 | return *str; 254 | 255 | start = *str; 256 | while (*start && strchr(sep, *start)) 257 | start++; 258 | end = start; 259 | while (*end && !strchr(sep, *end)) 260 | end++; 261 | if (*end != '\0') 262 | *end++ = '\0'; 263 | *str = end; 264 | return start; 265 | } 266 | 267 | size_t str_sep_count(const char *str, const char *sep) 268 | { 269 | size_t count = 0; 270 | const char *p; 271 | 272 | if (str == NULL || str[0] == '\0') 273 | return 0; 274 | if (sep == NULL) 275 | return 1; 276 | 277 | p = str; 278 | while (*p) { 279 | while (*p && strchr(sep, *p)) 280 | p++; 281 | if (*p == '\0') 282 | break; 283 | count++; 284 | while(*p && !strchr(sep, *p)) 285 | p++; 286 | } 287 | return count; 288 | } 289 | 290 | void to_upper(char *s) 291 | { 292 | while (*s) { 293 | if (isalpha((unsigned char)*s)) 294 | *s &= ~0x20; 295 | s++; 296 | } 297 | } 298 | 299 | void to_lower(char *s) 300 | { 301 | while (*s) { 302 | if (isalpha((unsigned char)*s)) 303 | *s |= 0x20; 304 | s++; 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | int randint(int limit) 16 | { 17 | int r; 18 | int divisor = RAND_MAX / (limit + 1); 19 | 20 | do { 21 | r = rand() / divisor; 22 | } while (r > limit); 23 | return r; 24 | } 25 | 26 | uint32_t round_up_pow2(uint32_t v) 27 | { 28 | /* from the bit-twiddling hacks */ 29 | v -= 1; 30 | v |= v >> 1; 31 | v |= v >> 2; 32 | v |= v >> 4; 33 | v |= v >> 8; 34 | v |= v >> 16; 35 | v += 1; 36 | return v; 37 | } 38 | 39 | int num_digits_in_number(int num) 40 | { 41 | int digits = 0, n = ABS(num); 42 | 43 | while (n > 0) { 44 | digits++; 45 | n /= 10; 46 | } 47 | return digits; 48 | } 49 | 50 | __format_printf(3, 4) 51 | void hexdump(const void *p, size_t len, const char *fmt, ...) 52 | { 53 | size_t i; 54 | va_list args; 55 | char str[16 + 1] = {0}; 56 | const uint8_t *data = p; 57 | 58 | va_start(args, fmt); 59 | vprintf(fmt, args); 60 | va_end(args); 61 | 62 | printf(" [%zu] =>\n 0000 %02x ", len, data[0]); 63 | str[0] = isprint(data[0]) ? data[0] : '.'; 64 | for (i = 1; i < len; i++) { 65 | if ((i & 0x0f) == 0) { 66 | printf(" |%16s|", str); 67 | printf("\n %04zu ", i); 68 | } else if((i & 0x07) == 0) { 69 | printf(" "); 70 | } 71 | printf("%02x ", data[i]); 72 | str[i & 0x0f] = isprint(data[i]) ? data[i] : '.'; 73 | } 74 | if ((i &= 0x0f) != 0) { 75 | if (i <= 8) printf(" "); 76 | while (i < 16) { 77 | printf(" "); 78 | str[i++] = ' '; 79 | } 80 | printf(" |%16s|", str); 81 | } else { 82 | printf(" |%16s|", str); 83 | } 84 | 85 | printf("\n"); 86 | } 87 | 88 | #if (defined(_WIN32) || defined(_WIN64)) 89 | #define WIN32_LEAN_AND_MEAN 90 | #include 91 | #include // portable: uint64_t MSVC: __int64 92 | 93 | // MSVC defines this in winsock2.h!? 94 | typedef struct timeval { 95 | long tv_sec; 96 | long tv_usec; 97 | } timeval; 98 | 99 | int gettimeofday(struct timeval * tp, struct timezone * tzp) 100 | { 101 | // Note: some broken versions only have 8 trailing zero's, the correct 102 | // epoch has 9 trailing zero's This magic number is the number of 100 103 | // nanosecond intervals since January 1, 1601 (UTC) until 00:00:00 104 | // January 1, 1970 105 | static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); 106 | 107 | SYSTEMTIME system_time; 108 | FILETIME file_time; 109 | uint64_t time; 110 | 111 | GetSystemTime( &system_time ); 112 | SystemTimeToFileTime( &system_time, &file_time ); 113 | time = ((uint64_t)file_time.dwLowDateTime ) ; 114 | time += ((uint64_t)file_time.dwHighDateTime) << 32; 115 | 116 | tp->tv_sec = (long) ((time - EPOCH) / 10000000L); 117 | tp->tv_usec = (long) (system_time.wMilliseconds * 1000); 118 | return 0; 119 | } 120 | 121 | int add_iso8601_utc_datetime(char* buf, size_t size) 122 | { 123 | int r; 124 | SYSTEMTIME utcTime; 125 | GetSystemTime(&utcTime); // Get the current UTC time 126 | 127 | // Format: YYYY-MM-DDThh:mm:ssZ 128 | r = snprintf(buf, size, "%04d-%02d-%02dT%02d:%02d:%02dZ", 129 | utcTime.wYear, utcTime.wMonth, utcTime.wDay, 130 | utcTime.wHour, utcTime.wMinute, utcTime.wSecond); 131 | return r; 132 | } 133 | 134 | #elif defined(__linux__) || defined(__APPLE__) 135 | 136 | #include 137 | #include 138 | 139 | int add_iso8601_utc_datetime(char *buf, size_t size) 140 | { 141 | time_t now; 142 | struct tm timeinfo; 143 | 144 | // Format: YYYY-MM-DDThh:mm:ssZ 145 | time(&now); 146 | gmtime_r(&now, &timeinfo); 147 | 148 | return strftime(buf, size, "%Y-%m-%dT%H:%M:%SZ", &timeinfo); 149 | } 150 | 151 | #elif defined(__BARE_METAL__) 152 | 153 | #ifndef _TIMEVAL_DEFINED 154 | struct timeval { 155 | long tv_sec; // seconds since epoch 156 | long tv_usec; // microseconds 157 | }; 158 | #endif 159 | 160 | #ifndef _TIMEZONE_DEFINED 161 | struct timezone { 162 | int tz_minuteswest; // minutes west of UTC 163 | int tz_dsttime; // daylight saving time flag 164 | }; 165 | #endif 166 | 167 | int gettimeofday(struct timeval * tp, struct timezone * tzp) 168 | { 169 | ARG_UNUSED(tzp); 170 | tp->tv_sec = 0; 171 | tp->tv_usec = 0; 172 | return 0; 173 | } 174 | 175 | int add_iso8601_utc_datetime(char* buf, size_t size) { 176 | ARG_UNUSED(buf); 177 | ARG_UNUSED(size); 178 | return 0; 179 | } 180 | 181 | #else 182 | 183 | #error Platform test failed 184 | 185 | #endif 186 | 187 | int64_t usec_now() 188 | { 189 | int64_t usec; 190 | struct timeval tv; 191 | 192 | gettimeofday(&tv, NULL); 193 | usec = tv.tv_sec * 1000LL * 1000LL + tv.tv_usec; 194 | 195 | return usec; 196 | } 197 | 198 | void get_time(uint32_t *seconds, uint32_t *micro_seconds) 199 | { 200 | struct timeval tv; 201 | 202 | gettimeofday(&tv, NULL); 203 | *seconds = tv.tv_sec; 204 | *micro_seconds = tv.tv_usec; 205 | } 206 | 207 | int64_t usec_since(int64_t last) 208 | { 209 | return usec_now() - last; 210 | } 211 | 212 | int64_t millis_now() 213 | { 214 | return (int64_t)(usec_now() / 1000LL); 215 | } 216 | 217 | int64_t millis_since(int64_t last) 218 | { 219 | return millis_now() - last; 220 | } 221 | 222 | #if (defined(__linux__) || defined(__APPLE__)) && defined(__GLIBC__) 223 | #include 224 | void dump_trace(void) 225 | { 226 | char **strings; 227 | size_t i, size; 228 | void *array[1024]; 229 | 230 | size = backtrace(array, sizeof(array)); 231 | strings = backtrace_symbols(array, size); 232 | for (i = 0; i < size; i++) { 233 | printf("\t%s\n", strings[i]); 234 | } 235 | puts(""); 236 | free(strings); 237 | } 238 | #else 239 | void dump_trace(void) 240 | { 241 | puts("Stack trace unavailable!\n"); 242 | } 243 | #endif 244 | -------------------------------------------------------------------------------- /src/workqueue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | enum worker_state_e { 8 | WQ_WORKER_STATE_OFFLINE, 9 | WQ_WORKER_STATE_IDLE, 10 | WQ_WORKER_STATE_RUNNING, 11 | }; 12 | 13 | #define WQ_REQ_CANCEL_WORK BIT(0) 14 | 15 | static inline worker_t *get_worker(workqueue_t *wq, int worker_ndx) 16 | { 17 | return wq->workers + worker_ndx; 18 | } 19 | 20 | static inline void wakeup_first_free_worker(workqueue_t *wq) 21 | { 22 | int i; 23 | worker_t *w; 24 | 25 | for (i = 0; i < wq->num_workers; i++) { 26 | w = get_worker(wq, i); 27 | if (w->state == WQ_WORKER_STATE_IDLE) { 28 | event_set(&w->event); 29 | break; 30 | } 31 | } 32 | } 33 | 34 | static void complete_work(work_t *work) 35 | { 36 | work->status = WQ_WORK_COMPLETE; 37 | if (work->complete_fn) 38 | work->complete_fn(work); 39 | } 40 | 41 | static int do_work(work_t *work) 42 | { 43 | int rc; 44 | int64_t slice; 45 | 46 | slice = usec_now(); 47 | rc = work->work_fn(work->arg); 48 | work->slice += usec_since(slice); 49 | return rc; 50 | } 51 | 52 | static inline work_t *get_backlog(workqueue_t *wq) 53 | { 54 | int rc; 55 | queue_node_t *node; 56 | 57 | pthread_mutex_lock(&wq->backlog_lock); 58 | if ((rc = queue_dequeue(&wq->backlog, &node)) == 0) 59 | wq->backlog_count--; 60 | pthread_mutex_unlock(&wq->backlog_lock); 61 | 62 | if (rc != 0) 63 | return NULL; 64 | 65 | return CONTAINER_OF(node, work_t, node); 66 | } 67 | 68 | static inline void put_backlog(workqueue_t *wq, work_t *work) 69 | { 70 | pthread_mutex_lock(&wq->backlog_lock); 71 | 72 | // TODO: Make this a priority queue on work->slice 73 | queue_enqueue(&wq->backlog, &work->node); 74 | wq->backlog_count++; 75 | 76 | pthread_mutex_unlock(&wq->backlog_lock); 77 | 78 | wakeup_first_free_worker(wq); 79 | } 80 | 81 | static inline void flush_backlog(workqueue_t *wq) 82 | { 83 | work_t *work; 84 | queue_node_t *node; 85 | 86 | pthread_mutex_lock(&wq->backlog_lock); 87 | while(queue_dequeue(&wq->backlog, &node) == 0) { 88 | work = CONTAINER_OF(node, work_t, node); 89 | work->status = WQ_WORK_COMPLETE; 90 | wq->backlog_count--; 91 | } 92 | pthread_mutex_unlock(&wq->backlog_lock); 93 | } 94 | 95 | static void *workqueue_factory(void *arg) 96 | { 97 | int rc; 98 | worker_t *w = arg; 99 | work_t *work; 100 | workqueue_t *wq = w->wq; 101 | 102 | w->state = WQ_WORKER_STATE_IDLE; 103 | 104 | while (event_is_set(&w->event)) { 105 | w->state = WQ_WORKER_STATE_RUNNING; 106 | 107 | while ((work = get_backlog(wq)) != NULL) { 108 | if (work->requests & WQ_REQ_CANCEL_WORK) 109 | complete_work(work); 110 | work->status = WQ_WORK_IN_PROGRESS; 111 | 112 | rc = do_work(work); 113 | 114 | if (rc <= 0 || work->requests & WQ_REQ_CANCEL_WORK) 115 | complete_work(work); 116 | else 117 | put_backlog(wq, work); 118 | } 119 | 120 | w->state = WQ_WORKER_STATE_IDLE; 121 | } 122 | return NULL; 123 | } 124 | 125 | int workqueue_create(workqueue_t *wq, int num_workers) 126 | { 127 | int i; 128 | worker_t *w; 129 | 130 | wq->workers = calloc(num_workers, sizeof(worker_t)); 131 | if (wq->workers == NULL) 132 | return -1; 133 | 134 | wq->backlog_count = 0; 135 | queue_init(&wq->backlog); 136 | pthread_mutex_init(&wq->backlog_lock, NULL); 137 | 138 | for (i = 0; i < num_workers; i++) { 139 | w = get_worker(wq, i); 140 | w->id = i; 141 | w->wq = wq; 142 | event_init(&w->event, false, true); 143 | pthread_create(&w->thread, NULL, workqueue_factory, (void *)w); 144 | } 145 | 146 | wq->num_workers = num_workers; 147 | return 0; 148 | } 149 | 150 | int workqueue_add_work(workqueue_t *wq, work_t *work) 151 | { 152 | if (!wq || !work || !work->work_fn) 153 | return -1; 154 | work->slice = 0; 155 | work->requests = 0; 156 | work->status = WQ_WORK_QUEUED; 157 | put_backlog(wq, work); 158 | return 0; 159 | } 160 | 161 | int workqueue_backlog_count(workqueue_t *wq) 162 | { 163 | return wq->backlog_count; 164 | } 165 | 166 | void workqueue_destroy(workqueue_t *wq) 167 | { 168 | int i; 169 | worker_t *w; 170 | 171 | flush_backlog(wq); 172 | pthread_mutex_destroy(&wq->backlog_lock); 173 | 174 | for (i = 0; i < wq->num_workers; i++) { 175 | w = get_worker(wq, i); 176 | pthread_cancel(w->thread); 177 | event_cleanup(&w->event); 178 | } 179 | 180 | free(wq->workers); 181 | } 182 | 183 | void workqueue_cancel_work(workqueue_t *wq, work_t *work) 184 | { 185 | ARG_UNUSED(wq); 186 | 187 | // TODO: check if work belongs to work queue 188 | if (work) 189 | work->requests |= WQ_REQ_CANCEL_WORK; 190 | } 191 | 192 | bool workqueue_work_is_complete(workqueue_t *wq, work_t *work) 193 | { 194 | ARG_UNUSED(wq); 195 | 196 | return READ_ONCE(work->status) == WQ_WORK_COMPLETE; 197 | } 198 | -------------------------------------------------------------------------------- /tests/test-bus-server.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define TEST_SERVER_PATH "/tmp/test-utils-bus-server" 11 | #define NUM_CLIENTS 5 12 | #define TEST_MSG "12345678901234567890" 13 | #define TEST_MSG_LEN 20 14 | 15 | bus_server_t server; 16 | 17 | int bus_write_check(int *fd) 18 | { 19 | ssize_t ret; 20 | char buf[128]; 21 | int i, write_fd; 22 | 23 | write_fd = randint(NUM_CLIENTS - 1); 24 | ret = write(fd[write_fd], TEST_MSG, TEST_MSG_LEN); 25 | if (ret != TEST_MSG_LEN) { 26 | mod_printf("write %d failed!", write_fd); 27 | return -1; 28 | } 29 | 30 | for (i = 0; i < NUM_CLIENTS; i++) { 31 | if (i == write_fd) 32 | continue; 33 | ret = read(fd[i], buf, sizeof(buf)); 34 | if (ret != TEST_MSG_LEN) { 35 | mod_printf("read %d failed!", i); 36 | return -1; 37 | } 38 | if (strncmp(buf, TEST_MSG, TEST_MSG_LEN)) { 39 | mod_printf("msg check %d failed!", i); 40 | return -1; 41 | } 42 | } 43 | 44 | return 0; 45 | } 46 | 47 | int test_bus_server() 48 | { 49 | int i, rc = -1; 50 | int fd[NUM_CLIENTS]; 51 | 52 | for (i = 0; i < NUM_CLIENTS; i++) { 53 | fd[i] = sock_unix_connect(TEST_SERVER_PATH); 54 | if (fd[i] < 0) { 55 | mod_printf("connect %d failed!", i); 56 | return -1; 57 | } 58 | } 59 | 60 | for (i = 0; i < 10; i++) { 61 | if ((rc = bus_write_check(fd))) 62 | break; 63 | } 64 | 65 | for (i = 0; i < NUM_CLIENTS; i++) 66 | close(fd[i]); 67 | 68 | return rc; 69 | } 70 | 71 | TEST_DEF(bus_server) 72 | { 73 | int rc; 74 | TEST_MOD_INIT(); 75 | 76 | rc = bus_server_start(&server, NUM_CLIENTS, TEST_SERVER_PATH); 77 | if (rc < 0) { 78 | mod_printf("Failed to start bus server"); 79 | return; 80 | } 81 | mod_printf("bus server started"); 82 | 83 | 84 | TEST_MOD_EXEC( test_bus_server() ); 85 | 86 | bus_server_stop(&server); 87 | 88 | TEST_MOD_REPORT(); 89 | } 90 | -------------------------------------------------------------------------------- /tests/test-circbuf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "test.h" 14 | 15 | struct test_struct 16 | { 17 | int a; 18 | char b; 19 | }; 20 | 21 | CIRCBUF_DEF(struct test_struct, test_cb, 10); 22 | 23 | int test_boundary() 24 | { 25 | int i, iter = 0, next_write = 0, next_read = 0; 26 | struct test_struct s; 27 | 28 | CIRCBUF_FLUSH(test_cb); 29 | mod_printf("boundary test"); 30 | 31 | do { 32 | for (i = 0; i < 3; i++) { 33 | s.a = next_write++; 34 | if (CIRCBUF_PUSH(test_cb, &s)) { 35 | mod_printf("push failed at iter:%d", iter); 36 | return -1; 37 | } 38 | } 39 | for (i = 0; i < 3; i++) { 40 | if (CIRCBUF_POP(test_cb, &s)) { 41 | mod_printf("pop failed at iter:%d", iter); 42 | return -1; 43 | } 44 | if (next_read != s.a) { 45 | mod_printf("invalid data at iter:%d read:%d expected:%d", 46 | iter, s.a, next_read); 47 | return -1; 48 | } 49 | next_read++; 50 | } 51 | iter++; 52 | } while (iter < 100); 53 | 54 | return 0; 55 | } 56 | 57 | #define LIMIT 100000 58 | #define W_PROB 1.0 59 | #define R_PROB 0.01 60 | 61 | double r2() 62 | { 63 | return (double)rand() / (double)RAND_MAX; 64 | } 65 | 66 | int test_probabilistic() 67 | { 68 | struct test_struct m; 69 | int next_write = 1; 70 | int expected_read = 1; 71 | int done_writing = 0; 72 | int done_reading = 0; 73 | 74 | CIRCBUF_FLUSH(test_cb); 75 | srand(time(NULL)); 76 | 77 | mod_printf("probabilistic test"); 78 | while (1) 79 | { 80 | if ((r2() < W_PROB) && !done_writing) { 81 | m.a = next_write; 82 | if (CIRCBUF_PUSH(test_cb, &m) == 0) { 83 | next_write++; 84 | if (next_write > LIMIT) { 85 | done_writing = 1; 86 | } 87 | } 88 | } 89 | if (r2() < R_PROB) { 90 | if (CIRCBUF_POP(test_cb, &m) == 0) { 91 | if (m.a != expected_read) { 92 | mod_printf("invalid data, got %d, expected %d", 93 | m.a, expected_read); 94 | return -1; 95 | } 96 | if (m.a >= LIMIT) { 97 | done_reading = 1; 98 | } 99 | expected_read++; 100 | } 101 | } 102 | if (done_reading && done_writing) 103 | break; 104 | } 105 | return 0; 106 | } 107 | 108 | void *producer_thread(void *data) 109 | { 110 | int val = 1337; 111 | bool done = false; 112 | struct test_struct m; 113 | 114 | while (!done) { 115 | if ((r2() < W_PROB)) { 116 | m.a = val; 117 | if (CIRCBUF_PUSH(test_cb, &m) == 0) { 118 | val++; 119 | if (val > LIMIT) 120 | done = true; 121 | } 122 | } 123 | } 124 | return NULL; 125 | } 126 | 127 | void *consumer_thread(void *data) 128 | { 129 | int val = 1337; 130 | bool done = false; 131 | struct test_struct m; 132 | 133 | while (!done) { 134 | if (r2() < R_PROB) { 135 | if (CIRCBUF_POP(test_cb, &m) == 0) { 136 | if (m.a != val) { 137 | mod_printf("invalid data, got %d, expected %d", m.a, val); 138 | continue; 139 | } 140 | val++; 141 | if (m.a >= LIMIT) 142 | done = true; 143 | } 144 | } 145 | } 146 | return NULL; 147 | } 148 | 149 | int test_single_produce_single_consumer() 150 | { 151 | pthread_t producer, consumer; 152 | 153 | CIRCBUF_FLUSH(test_cb); 154 | 155 | mod_printf("single produce single consumer test"); 156 | 157 | pthread_create(&producer, NULL, producer_thread, NULL); 158 | pthread_create(&consumer, NULL, consumer_thread, NULL); 159 | 160 | pthread_join(producer, NULL); 161 | pthread_join(consumer, NULL); 162 | return 0; 163 | } 164 | 165 | TEST_DEF(circular_buffer) 166 | { 167 | TEST_MOD_INIT(); 168 | 169 | TEST_MOD_EXEC(test_boundary()); 170 | TEST_MOD_EXEC(test_probabilistic()); 171 | TEST_MOD_EXEC(test_single_produce_single_consumer()); 172 | 173 | TEST_MOD_REPORT(); 174 | } 175 | -------------------------------------------------------------------------------- /tests/test-filo.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #define NUM_TESTS 100 9 | 10 | FILO_DEF(filo_stack, int, NUM_TESTS); 11 | 12 | void do_test_filo(test_t *ctx, test_result_t *result) 13 | { 14 | int i; 15 | int total=0, pass=0; 16 | 17 | for (i = 0; i < NUM_TESTS; i++) { 18 | int elem = i + 1; 19 | if (FILO_PUSH(filo_stack, &elem) == 0) 20 | pass++; 21 | else 22 | break; 23 | total++; 24 | } 25 | 26 | if (total != pass) 27 | goto exit; 28 | 29 | for (i = 0; i < NUM_TESTS; i++) { 30 | int elem = 0; 31 | if ((FILO_POP(filo_stack, &elem) == 0) && (elem == (NUM_TESTS - i))) 32 | pass++; 33 | else 34 | break; 35 | total++; 36 | } 37 | 38 | exit: 39 | result->pass = pass; 40 | result->total = total; 41 | } 42 | -------------------------------------------------------------------------------- /tests/test-hashmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "test.h" 7 | 8 | struct test_hashmap { 9 | size_t off; 10 | char *key; 11 | }; 12 | 13 | struct test_hashmap *new_test_hashmap_value(size_t offset, const char *key) 14 | { 15 | struct test_hashmap *p; 16 | 17 | p = safe_malloc(sizeof(struct test_hashmap)); 18 | p->off = offset; 19 | p->key = safe_strdup(key); 20 | return p; 21 | } 22 | 23 | void free_test_hashmap_value(const char *key, void *val) 24 | { 25 | struct test_hashmap *p = val; 26 | 27 | ARG_UNUSED(key); 28 | safe_free(p->key); 29 | safe_free(p); 30 | } 31 | 32 | #define SEP "\n\r \t" 33 | 34 | int test_dict_insert(hash_map_t *map, const char * const *words, size_t count) 35 | { 36 | size_t ndx = 0; 37 | struct test_hashmap *p; 38 | 39 | while (ndx < count) { 40 | p = new_test_hashmap_value(ndx, words[ndx]); 41 | hash_map_insert(map, words[ndx], p); 42 | ndx++; 43 | } 44 | mod_printf("Inserted %zu items", ndx); 45 | return 0; 46 | } 47 | 48 | int test_dict_iterator(hash_map_t *map, const char * const *words, size_t count) 49 | { 50 | size_t ndx = 0, mismatch = 0; 51 | struct test_hashmap *val; 52 | char *key; 53 | 54 | HASH_MAP_FOREACH(map, &key, &val) { 55 | if (strcmp(val->key, words[val->off])) { 56 | mismatch += 1; 57 | mod_printf("Err: word mismatch '%s/%s' off: %zu", 58 | words[ndx], val->key, val->off); 59 | } 60 | ndx++; 61 | } 62 | if (ndx != count || mismatch) 63 | return -1; 64 | mod_printf("Iterated over %zu items", ndx); 65 | return 0; 66 | } 67 | 68 | int test_dict_delete(hash_map_t *map, const char * const *words, size_t count) 69 | { 70 | size_t ndx = 0, not_found = 0; 71 | struct test_hashmap *p; 72 | 73 | while (ndx < count) { 74 | p = hash_map_delete(map, words[ndx], 0); 75 | if (p == NULL) { 76 | not_found += 1; 77 | mod_printf("word '%s' not found", words[ndx]); 78 | } else { 79 | if (p->off != ndx || strcmp(p->key, words[ndx])) { 80 | not_found += 1; 81 | mod_printf("Err: word '%s/%s' off: %zu", 82 | words[ndx], p->key, ndx); 83 | } 84 | free_test_hashmap_value(NULL, p); 85 | } 86 | ndx++; 87 | } 88 | mod_printf("Deleted %zu items", ndx); 89 | return not_found * -1; 90 | } 91 | 92 | size_t test_build_word_list(char *buf, const char *const **word_list) 93 | { 94 | size_t count, ndx = 0; 95 | char *word, *state, **words; 96 | 97 | count = str_sep_count(buf, "\n\r \t"); 98 | words = safe_malloc(sizeof(char *) * count); 99 | 100 | word = strtok_r(buf, SEP, &state); 101 | while (ndx < count && word != NULL) { 102 | words[ndx] = word; 103 | word = strtok_r(NULL, SEP, &state); 104 | ndx += 1; 105 | } 106 | *word_list = (const char *const *)words; 107 | return count; 108 | } 109 | 110 | TEST_DEF(hashmap) 111 | { 112 | char *buf; 113 | const char * const *words; 114 | size_t size, count; 115 | hash_map_t map; 116 | TEST_MOD_INIT(); 117 | 118 | TEST_MOD_READ_FILE("words_alpha.txt", &buf, &size); 119 | count = test_build_word_list(buf, &words); 120 | 121 | hash_map_init(&map); 122 | 123 | TEST_MOD_EXEC( test_dict_insert(&map, words, count) ); 124 | TEST_MOD_EXEC( test_dict_iterator(&map, words, count) ); 125 | TEST_MOD_EXEC( test_dict_delete(&map, words, count) ); 126 | TEST_MOD_EXEC( map.count ); 127 | 128 | TEST_MOD_EXEC( test_dict_insert(&map, words, count) ); 129 | hash_map_free(&map, free_test_hashmap_value); 130 | 131 | safe_free(buf); 132 | safe_free((void *)words); 133 | TEST_MOD_REPORT(); 134 | } 135 | -------------------------------------------------------------------------------- /tests/test-procutils.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int test_pid_of(void) 9 | { 10 | FILE *pfile = fopen("/proc/self/status", "r"); 11 | if (pfile == NULL) 12 | return -ENOENT; 13 | 14 | char filename[200] = { 0 }; 15 | fscanf(pfile, "Name: %199s", filename); 16 | fclose(pfile); 17 | 18 | int parsed_pid = any_pid_of(filename); 19 | int get_pid = getpid(); 20 | 21 | return parsed_pid == get_pid ? 0 : -1; 22 | } 23 | 24 | TEST_DEF(procutils) 25 | { 26 | int rc; 27 | TEST_MOD_INIT(); 28 | 29 | rc = test_pid_of(); 30 | TEST_MOD_EXEC(rc && rc != -ENOENT); 31 | 32 | TEST_MOD_REPORT(); 33 | } 34 | -------------------------------------------------------------------------------- /tests/test-slab.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | 3 | #include 4 | 5 | struct test_slab_blocks 6 | { 7 | int test; 8 | long long long_test; 9 | }; 10 | 11 | #define TEST_SLAB_COUNT 10 12 | 13 | int test_slab_alloc_free() 14 | { 15 | int i = 0; 16 | struct test_slab_blocks *p[TEST_SLAB_COUNT + 1]; 17 | uint8_t slab_space[(sizeof(struct test_slab_blocks) + 8) * TEST_SLAB_COUNT]; 18 | slab_t slab; 19 | 20 | slab_init(&slab, sizeof(struct test_slab_blocks), 21 | slab_space, sizeof(slab_space)); 22 | 23 | while (i < TEST_SLAB_COUNT) { 24 | if (slab_alloc(&slab, (void **)&p[i])) 25 | return -1; 26 | i++; 27 | } 28 | 29 | if (slab_alloc(&slab, (void **)&p[i]) == 0) 30 | return -1; 31 | 32 | while (--i < 0) { 33 | if (slab_free(&slab, p[i])) 34 | return -1; 35 | } 36 | 37 | if (slab_free(&slab, (void **)&p[0]) == 0) 38 | return -1; 39 | 40 | return 0; 41 | } 42 | 43 | TEST_DEF(slab) 44 | { 45 | TEST_MOD_INIT(); 46 | 47 | TEST_MOD_EXEC( test_slab_alloc_free() ); 48 | 49 | TEST_MOD_REPORT(); 50 | } 51 | -------------------------------------------------------------------------------- /tests/test-strlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "test.h" 15 | 16 | STRING_DEF(test_str, 8); 17 | 18 | void test_print_info_string(const char *msg, string_t *s) 19 | { 20 | printf("\t[%s] s->len: %zu s->max_len: %zu s->buf: %s\n", 21 | msg, s->len, s->max_len, s->buf); 22 | } 23 | 24 | int test_check_str(string_t *s, const char *str) 25 | { 26 | if (strcmp(s->buf, str) != 0) 27 | return -1; 28 | 29 | return 0; 30 | } 31 | 32 | int test_str_printf() 33 | { 34 | int ret; 35 | 36 | ret = string_printf(&test_str, "c", "age: %d", 10); 37 | if (ret <= 0 || test_check_str(&test_str, "age: 10")) { 38 | printf("\tret: %d", ret); 39 | test_print_info_string("string_printf: create", &test_str); 40 | return -1; 41 | } 42 | 43 | ret = string_printf(&test_str, "a", " id: %d", 101); 44 | if (ret != -1) { 45 | printf("\tret: %d", ret); 46 | test_print_info_string("string_printf: create", &test_str); 47 | return -1; 48 | } 49 | 50 | return 0; 51 | } 52 | 53 | int test_str_copy() 54 | { 55 | int ret; 56 | 57 | ret = string_copy(&test_str, "c", "1234567890", 10); 58 | if (ret != -1) { 59 | test_print_info_string("Create excess", &test_str); 60 | return -1; 61 | } 62 | 63 | ret = string_copy(&test_str, "cf", "1234567890", 10); 64 | if (ret != 8) { 65 | test_print_info_string("Create fill", &test_str); 66 | return -1; 67 | } 68 | 69 | ret = string_copy(&test_str, "c", "1234567", 7); 70 | if (ret != 7) { 71 | test_print_info_string("Create", &test_str); 72 | return -1; 73 | } 74 | 75 | ret = string_copy(&test_str, "a", "1", 1); 76 | if (ret != 1) { 77 | test_print_info_string("Append", &test_str); 78 | return -1; 79 | } 80 | 81 | ret = string_copy(&test_str, "a", "123", 3); 82 | if (ret != -1) { 83 | test_print_info_string("Append after full", &test_str); 84 | return -1; 85 | } 86 | 87 | return 0; 88 | } 89 | 90 | TEST_DEF(strlib) 91 | { 92 | TEST_MOD_INIT(); 93 | 94 | TEST_MOD_EXEC(test_str_copy()); 95 | TEST_MOD_EXEC(test_str_printf()); 96 | 97 | TEST_MOD_REPORT(); 98 | } 99 | -------------------------------------------------------------------------------- /tests/test-strutils.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | #include 4 | 5 | int _test_str_sep_do(char *input, char *sep, int toks_len, char *toks[], bool outcome) 6 | { 7 | int len = 0; 8 | char *p, *tok, buf[64+1] = {0}; 9 | 10 | strncpy(buf, input, 64); 11 | p = buf; 12 | 13 | while (len < toks_len && (tok = str_sep(&p, sep))) { 14 | if (strcmp(tok, toks[len]) != 0) { 15 | if (outcome) { 16 | mod_printf("strsep failed: " 17 | "tok: '%s', res: '%s'", tok, p); 18 | } 19 | return -1; 20 | } 21 | len++; 22 | } 23 | return len == toks_len ? 0 : -1; 24 | } 25 | 26 | #define _test_sep_pos(i,s,t) if (_test_str_sep_do(i,s,ARRAY_SIZEOF(t),t,true)) break; 27 | #define _test_sep_neg(i,s,t) if (_test_str_sep_do(i,s,ARRAY_SIZEOF(t),t,false) == 0) break; 28 | int test_str_sep() 29 | { 30 | int retval = -1; 31 | char *empty = "", *tc01[] = { "1", "2", "3", "4", "5" }; 32 | 33 | do { 34 | _test_sep_pos("1,2,3,4,5", ",", tc01); 35 | _test_sep_pos(",, 1 ,2 , ,3 , 4,5 ", ", ", tc01); 36 | _test_sep_pos("1,,,2 3,,,4 5", ", ", tc01); 37 | _test_sep_neg("0,1,,,2 3,,,4 5", ", ", tc01); 38 | _test_sep_neg("1,,,2 3,,,4 5", ",", tc01); 39 | _test_sep_neg("A,,,2,3,,,4 5", ",", tc01); 40 | _test_sep_neg("1,,,2,3,,,", ",", tc01); 41 | retval = 0; 42 | } while (0); 43 | return retval; 44 | } 45 | 46 | int test_str_sep_count() 47 | { 48 | if (str_sep_count("1,2,3,4,5", ",") != 5) 49 | return -1; 50 | if (str_sep_count(" 1 2, 3,,,,4,5 ", ", ") != 5) 51 | return -1; 52 | if (str_sep_count("1, 2,,,3 ,4 ,5 ", ", ") != 5) 53 | return -1; 54 | if (str_sep_count(",,,,,, 1 2 3 4 5", " ,") != 5) 55 | return -1; 56 | if (str_sep_count("1 2 3 4 5", ",") != 1) 57 | return -1; 58 | return 0; 59 | } 60 | 61 | TEST_DEF(strutils) 62 | { 63 | TEST_MOD_INIT(); 64 | 65 | TEST_MOD_EXEC(test_str_sep()); 66 | TEST_MOD_EXEC(test_str_sep_count()); 67 | 68 | TEST_MOD_REPORT(); 69 | } 70 | -------------------------------------------------------------------------------- /tests/test-workqueue.c: -------------------------------------------------------------------------------- 1 | #include "test.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | struct test_work_data { 9 | int job_id; 10 | int runs; 11 | }; 12 | 13 | int num_complete; 14 | pthread_mutex_t test_lock; 15 | 16 | int test_work_runner(void *arg) 17 | { 18 | struct test_work_data *d = arg; 19 | 20 | usleep(1000); 21 | 22 | if (++d->runs == 5) { 23 | pthread_mutex_lock(&test_lock); 24 | num_complete++; 25 | pthread_mutex_unlock(&test_lock); 26 | return WORK_DONE; 27 | } 28 | 29 | return WORK_YIELD; 30 | } 31 | 32 | #define NUM_JOBS 20 33 | #define NUM_WORKERS 5 34 | 35 | int test_workqueue() 36 | { 37 | int i; 38 | workqueue_t wq; 39 | work_t work[NUM_JOBS] = {0}; 40 | struct test_work_data data[NUM_JOBS] = {0}; 41 | 42 | pthread_mutex_init(&test_lock, NULL); 43 | 44 | workqueue_create(&wq, NUM_WORKERS); 45 | mod_printf("Created %d workers", NUM_WORKERS); 46 | 47 | for (i = 0; i < NUM_JOBS; i++) { 48 | data[i].job_id = i; 49 | work[i].work_fn = test_work_runner; 50 | work[i].arg = &data[i]; 51 | workqueue_add_work(&wq, &work[i]); 52 | } 53 | 54 | while (num_complete != NUM_JOBS); 55 | mod_printf("Completed %d jobs", NUM_JOBS); 56 | 57 | workqueue_destroy(&wq); 58 | pthread_mutex_destroy(&test_lock); 59 | return 0; 60 | } 61 | 62 | TEST_DEF(workqueue) 63 | { 64 | TEST_MOD_INIT(); 65 | 66 | TEST_MOD_EXEC( test_workqueue() ); 67 | 68 | TEST_MOD_REPORT(); 69 | } 70 | -------------------------------------------------------------------------------- /tests/test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "test.h" 17 | 18 | #define DATE_TIME_STR_LEN 64 19 | 20 | char date_time[DATE_TIME_STR_LEN]; 21 | 22 | TEST_DEF(circular_buffer); 23 | TEST_DEF(strlib); 24 | TEST_DEF(hashmap); 25 | TEST_DEF(strutils); 26 | TEST_DEF(filo); 27 | TEST_DEF(slab); 28 | TEST_DEF(procutils); 29 | TEST_DEF(workqueue); 30 | TEST_DEF(bus_server); 31 | 32 | test_module_t c_utils_test_modules[] = { 33 | TEST_MOD(circular_buffer), 34 | TEST_MOD(strlib), 35 | TEST_MOD(hashmap), 36 | TEST_MOD(strutils), 37 | TEST_MOD(filo), 38 | TEST_MOD(slab), 39 | TEST_MOD(procutils), 40 | TEST_MOD(workqueue), 41 | TEST_MOD(bus_server), 42 | TEST_MOD_SENTINEL, 43 | }; 44 | 45 | void test_execute(test_t *t, test_module_t *tm) 46 | { 47 | test_result_t result; 48 | 49 | printf("Testing '%s'\n", tm->name); 50 | tm->runner(t, &result); 51 | mod_printf("Result: name:'%s' total:%d pass:%d\n", 52 | tm->name, result.total, result.pass); 53 | t->pass += result.pass; 54 | t->total += result.total; 55 | } 56 | 57 | char *time_string(time_t *t) 58 | { 59 | strftime(date_time, DATE_TIME_STR_LEN, "%d/%m/%y-%H:%M:%S %z", localtime(t)); 60 | return date_time; 61 | } 62 | 63 | static const char *TEST_HELP_TEXT = 64 | "test-bin [OPTIONS]\n" 65 | "\n" 66 | "OPTIONS:\n" 67 | " -i Input files directory\n" 68 | " -h Print this help text\n" 69 | "\n"; 70 | 71 | void process_cli_opts(test_t *ctx, int argc, char *argv[]) 72 | { 73 | int c; 74 | int opt_ndx; 75 | static struct option opts[] = { 76 | { "inputdir", required_argument, NULL, 'i' }, 77 | { "help", no_argument, NULL, 'h' }, 78 | { NULL, 0, NULL, 0 } 79 | }; 80 | const char *opt_str = 81 | /* no_argument */ "h" 82 | /* required_argument */ "i:" 83 | ; 84 | while ((c = getopt_long(argc, argv, opt_str, opts, &opt_ndx)) >= 0) { 85 | switch (c) { 86 | case 'i': 87 | if (ctx->inputdir) { 88 | printf("Cannot pass multiple --inputdir"); 89 | exit(-1); 90 | } 91 | ctx->inputdir = safe_strdup(optarg); 92 | break; 93 | case 'h': 94 | printf("%s", TEST_HELP_TEXT); 95 | exit(0); 96 | break; 97 | case '?': 98 | default: 99 | printf("%s", TEST_HELP_TEXT); 100 | exit(-1); 101 | } 102 | } 103 | 104 | argc -= optind; 105 | argv += optind; 106 | 107 | if (argc != 0) { 108 | printf("Invlaid arguments found\n"); 109 | exit(-1); 110 | } 111 | } 112 | 113 | void test_read_input_file(test_t *ctx, char *filename, char **buf, size_t *size) 114 | { 115 | char *path; 116 | FILE *fd; 117 | 118 | path = path_join(ctx->inputdir, filename); 119 | if ((fd = fopen(path, "r")) == NULL) { 120 | printf("Failed to open %s\n", path); 121 | exit(-1); 122 | } 123 | 124 | if (file_read_all(fd, buf, size)) { 125 | printf("Failed to read contents of %s\n", path); 126 | exit(-1); 127 | } 128 | 129 | fclose(fd); 130 | safe_free(path); 131 | } 132 | 133 | int main(int argc, char *argv[]) 134 | { 135 | test_module_t *tm; 136 | test_t test; 137 | 138 | memset(&test, 0, sizeof(test_t)); 139 | process_cli_opts(&test, argc, argv); 140 | 141 | if (!test.inputdir) 142 | test.inputdir = safe_strdup("../tests/input"); 143 | 144 | // Display test header 145 | printf("\n"); 146 | printf("------------------------------------------\n"); 147 | printf(" goToMain C Unit Test Framework \n"); 148 | printf("------------------------------------------\n"); 149 | 150 | // Run tests 151 | test.start_time = time(NULL); 152 | tm = &(c_utils_test_modules[0]); 153 | while (tm->name != NULL) { 154 | test_execute(&test, tm); 155 | tm++; 156 | } 157 | test.end_time = time(NULL); 158 | 159 | // Test summary 160 | printf("------------------------------------------\n"); 161 | printf(" Test Summary \n"); 162 | printf("------------------------------------------\n"); 163 | printf("Start Time: %s\n", time_string(&test.start_time)); 164 | printf("End Time: %s\n", time_string(&test.end_time)); 165 | printf("Executed: %d\n", test.total); 166 | printf("Successful: %d\n", test.pass); 167 | printf("Result: %s\n\n", (test.total == test.pass) ? "PASS" : "FAIL"); 168 | return 0; 169 | } 170 | -------------------------------------------------------------------------------- /tests/test.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2024 Siddharth Chandrasekaran 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | #ifndef _TEST_H_ 8 | #define _TEST_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #define mod_printf(fmt, ...) fprintf(stdout," - "fmt"\n", ## __VA_ARGS__) 19 | 20 | #define TEST_DEF(m) void do_test_ ## m (test_t *ctx, test_result_t *res) 21 | #define TEST_MOD_INIT() test_result_t _test = { 0 , 0 } 22 | #define TEST_MOD_EXEC(f) if (f == 0) _test.pass++; _test.total++; 23 | #define TEST_MOD_REPORT() res->pass = _test.pass; res->total = _test.total; 24 | #define TEST_MOD(m) { STR(m), do_test_ ## m } 25 | #define TEST_MOD_SENTINEL { NULL, NULL } 26 | 27 | #define TEST_MOD_READ_FILE(f, b, s) test_read_input_file(ctx, f, b, s); 28 | 29 | typedef struct 30 | { 31 | int total; 32 | int pass; 33 | } test_result_t; 34 | 35 | typedef struct { 36 | char *inputdir; 37 | time_t start_time; 38 | time_t end_time; 39 | int total; 40 | int pass; 41 | } test_t; 42 | 43 | typedef struct { 44 | const char *name; 45 | void (*runner)(test_t *ctx, test_result_t *result); 46 | } test_module_t; 47 | 48 | void test_read_input_file(test_t *ctx, char *filename, char **buf, size_t *size); 49 | 50 | #endif 51 | --------------------------------------------------------------------------------