├── debian ├── source │ └── format ├── lintian-overrides ├── libubox-utils.install ├── libubox.install ├── libubox-dev.install ├── generate-changelog.sh ├── rules ├── copyright └── control ├── tests ├── fuzz │ ├── corpus │ │ ├── c1dfd96eea8cc2b62785275bca38ac261256e278 │ │ ├── c42ac1c46f1d4e211c735cc7dfad4ff8391110e9 │ │ ├── crash-75b146c4e6fac64d3e62236b27c64b50657bab2a │ │ ├── crash-a3585b70f1c7ffbdec10f6dadc964336118485c4 │ │ ├── crash-b3585b70f1c7ffbdec10f6dadc964336118485c4 │ │ ├── crash-1b8fb1be45db3aff7699100f497fb74138f3df4f │ │ ├── crash-333757b203a44751d3535f24b05f467183a96d09 │ │ ├── crash-e0f8ecc694d96a09a1fced27b2a0838b670d34a0 │ │ ├── crash-df9d1243057b27bbad6211e5a23d1cb699028aa2 │ │ ├── crash-98595faa58ba01d85ba4fd0b109cd3d490b45795 │ │ ├── valid-blobmsg.bin │ │ ├── 71520a5c4b5ca73903216857abbad54a8002d44a │ │ ├── crash-4c4d2c3c9ade5da9347534e290305c3b9760f627 │ │ ├── crash-5e9937b197c88bf4e7b7ee2612456cad4cb83f5b │ │ ├── crash-813f3e68661da09c26d4a87dbb9d5099e92be50f │ │ ├── crash-d0f3aa7d60a094b021f635d4edb7807c055a4ea1 │ │ └── crash-e2fd5ecb3b37926743256f1083f47a07c39e10c2 │ ├── CMakeLists.txt │ └── test-fuzz.c ├── cram │ ├── inputs │ │ ├── invalid.ucert │ │ ├── key-build.ucert │ │ ├── signature.ucert │ │ ├── procd-instance-nlbwmon.bin │ │ └── json-script.json │ ├── test_blobmsg_check_array.t │ ├── test_blob_buflen.t │ ├── test_blobmsg_procd_instance.t │ ├── CMakeLists.txt │ ├── test_base64.t │ ├── test_runqueue.t │ ├── test_avl.t │ ├── test_list.t │ ├── test_blobmsg_types.t │ ├── test_json_script.t │ ├── test_blobmsg_parse.t │ └── test_blob_parse.t ├── test-b64_decode.c ├── test-b64_encode.c ├── shunit2 │ └── CMakeLists.txt ├── CMakeLists.txt ├── test-blob-buflen.c ├── test-b64.c ├── test-blobmsg-parse.c ├── test-json-script.c ├── test-avl.c ├── test-list.c ├── test-blobmsg-procd-instance.c ├── test-blobmsg_check_array.c ├── test-blob-parse.c ├── test-runqueue.c └── test-blobmsg.c ├── assert.h ├── examples ├── uloop_pid_test.sh ├── CMakeLists.txt ├── json_script-example.json ├── json_script-example.c ├── uloop-example.lua ├── ustream-example.c └── json_script-tests.sh ├── .gitlab-ci.yml ├── .github └── workflows │ ├── scripts │ └── ci_helpers.sh │ ├── formal.yml │ └── codeql.yml ├── .gitignore ├── avl-cmp.h ├── lua └── CMakeLists.txt ├── avl-cmp.c ├── ulog.h ├── udebug-priv.h ├── usock.h ├── udebug-proto.h ├── scripts └── devel-build.sh ├── safe_list.h ├── md5.h ├── vlist.c ├── blobmsg_json.h ├── kvlist.h ├── kvlist.c ├── safe_list.c ├── vlist.h ├── CMakeLists.txt ├── runqueue.h ├── uloop.h ├── ustream-fd.c ├── json_script.h ├── ulog.c ├── uloop-epoll.c ├── utils.c ├── uloop-kqueue.c ├── udebug.h ├── ustream.h ├── list.h ├── runqueue.c ├── usock.c └── sh └── jshn.sh /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/lintian-overrides: -------------------------------------------------------------------------------- 1 | no-manual-page 2 | -------------------------------------------------------------------------------- /tests/fuzz/corpus/c1dfd96eea8cc2b62785275bca38ac261256e278: -------------------------------------------------------------------------------- 1 | 6 -------------------------------------------------------------------------------- /tests/fuzz/corpus/c42ac1c46f1d4e211c735cc7dfad4ff8391110e9: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /debian/libubox-utils.install: -------------------------------------------------------------------------------- 1 | usr/bin/jshn 2 | usr/share/libubox 3 | -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-75b146c4e6fac64d3e62236b27c64b50657bab2a: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-a3585b70f1c7ffbdec10f6dadc964336118485c4: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-b3585b70f1c7ffbdec10f6dadc964336118485c4: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-1b8fb1be45db3aff7699100f497fb74138f3df4f: -------------------------------------------------------------------------------- 1 | 2 | A -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-333757b203a44751d3535f24b05f467183a96d09: -------------------------------------------------------------------------------- 1 | 2 | 6 -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-e0f8ecc694d96a09a1fced27b2a0838b670d34a0: -------------------------------------------------------------------------------- 1 | 5% -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-df9d1243057b27bbad6211e5a23d1cb699028aa2: -------------------------------------------------------------------------------- 1 |   -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-98595faa58ba01d85ba4fd0b109cd3d490b45795: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |  -------------------------------------------------------------------------------- /debian/libubox.install: -------------------------------------------------------------------------------- 1 | usr/lib/libubox.so.* 2 | usr/lib/libblobmsg_json.so.* 3 | usr/lib/libjson_script.so.* 4 | -------------------------------------------------------------------------------- /tests/cram/inputs/invalid.ucert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwrt/libubox/master/tests/cram/inputs/invalid.ucert -------------------------------------------------------------------------------- /tests/cram/inputs/key-build.ucert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwrt/libubox/master/tests/cram/inputs/key-build.ucert -------------------------------------------------------------------------------- /tests/cram/inputs/signature.ucert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwrt/libubox/master/tests/cram/inputs/signature.ucert -------------------------------------------------------------------------------- /tests/fuzz/corpus/valid-blobmsg.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwrt/libubox/master/tests/fuzz/corpus/valid-blobmsg.bin -------------------------------------------------------------------------------- /tests/test-b64_decode.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | int main() 4 | { 5 | b64_decode("Zg==", NULL, 2); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/test-b64_encode.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | int main() 4 | { 5 | b64_encode("foo", 3, NULL, 2); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/cram/inputs/procd-instance-nlbwmon.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwrt/libubox/master/tests/cram/inputs/procd-instance-nlbwmon.bin -------------------------------------------------------------------------------- /assert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef NDEBUG 4 | #undef NDEBUG 5 | #include 6 | #define NDEBUG 7 | #else 8 | #include 9 | #endif 10 | -------------------------------------------------------------------------------- /examples/uloop_pid_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo $0 $* 4 | echo Environment: 5 | env 6 | 7 | sleep 2 8 | 9 | echo "stopping child" 10 | 11 | exit 5 12 | -------------------------------------------------------------------------------- /tests/fuzz/corpus/71520a5c4b5ca73903216857abbad54a8002d44a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwrt/libubox/master/tests/fuzz/corpus/71520a5c4b5ca73903216857abbad54a8002d44a -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-4c4d2c3c9ade5da9347534e290305c3b9760f627: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwrt/libubox/master/tests/fuzz/corpus/crash-4c4d2c3c9ade5da9347534e290305c3b9760f627 -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-5e9937b197c88bf4e7b7ee2612456cad4cb83f5b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwrt/libubox/master/tests/fuzz/corpus/crash-5e9937b197c88bf4e7b7ee2612456cad4cb83f5b -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-813f3e68661da09c26d4a87dbb9d5099e92be50f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwrt/libubox/master/tests/fuzz/corpus/crash-813f3e68661da09c26d4a87dbb9d5099e92be50f -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-d0f3aa7d60a094b021f635d4edb7807c055a4ea1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwrt/libubox/master/tests/fuzz/corpus/crash-d0f3aa7d60a094b021f635d4edb7807c055a4ea1 -------------------------------------------------------------------------------- /tests/fuzz/corpus/crash-e2fd5ecb3b37926743256f1083f47a07c39e10c2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openwrt/libubox/master/tests/fuzz/corpus/crash-e2fd5ecb3b37926743256f1083f47a07c39e10c2 -------------------------------------------------------------------------------- /debian/libubox-dev.install: -------------------------------------------------------------------------------- 1 | usr/include/libubox 2 | usr/lib/libubox.so 3 | usr/lib/libubox.a 4 | usr/lib/libblobmsg_json.so 5 | usr/lib/libblobmsg_json.a 6 | usr/lib/libjson_script.so 7 | -------------------------------------------------------------------------------- /tests/shunit2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_TEST( 2 | NAME shunit2 3 | COMMAND tests.sh 4 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 5 | ) 6 | 7 | SET_PROPERTY(TEST shunit2 APPEND PROPERTY ENVIRONMENT "TEST_JSON_SCRIPT=$") 8 | -------------------------------------------------------------------------------- /tests/cram/test_blobmsg_check_array.t: -------------------------------------------------------------------------------- 1 | check that blobmsg_check_array() is producing expected results: 2 | 3 | $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" 4 | 5 | $ test-blobmsg_check_array 6 | Process array_a: entry 0 7 | array_b contains string: 1 8 | blobmsg_check_array() test passed 9 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | variables: 2 | CI_ENABLE_UNIT_TESTING: 1 3 | CI_TARGET_BUILD_DEPENDS: libubox 4 | CI_CMAKE_EXTRA_BUILD_ARGS: -DLUAPATH=/usr/lib/lua 5 | 6 | include: 7 | - remote: https://gitlab.com/ynezz/openwrt-ci/raw/master/openwrt-ci/gitlab/main.yml 8 | - remote: https://gitlab.com/ynezz/openwrt-ci/raw/master/openwrt-ci/gitlab/pipeline.yml 9 | -------------------------------------------------------------------------------- /tests/cram/test_blob_buflen.t: -------------------------------------------------------------------------------- 1 | check that blob buffer cannot exceed maximum buffer length: 2 | 3 | $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" 4 | 5 | $ valgrind --quiet --leak-check=full test-blob-buflen 6 | SUCCESS: failed to allocate attribute 7 | 8 | $ test-blob-buflen-san 9 | SUCCESS: failed to allocate attribute 10 | -------------------------------------------------------------------------------- /.github/workflows/scripts/ci_helpers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | color_out() { 4 | printf "\e[0;$1m%s\e[0;0m\n" "$2" 5 | } 6 | 7 | success() { 8 | color_out 32 "$1" 9 | } 10 | 11 | info() { 12 | color_out 36 "$1" 13 | } 14 | 15 | err() { 16 | color_out 31 "$1" 17 | } 18 | 19 | warn() { 20 | color_out 33 "$1" 21 | } 22 | 23 | err_die() { 24 | err "$1" 25 | exit 1 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | CMakeCache.txt 3 | CMakeFiles 4 | *.cmake 5 | *.a 6 | *.so 7 | *.dylib 8 | build 9 | install_manifest.txt 10 | jshn 11 | *-example 12 | tests.* 13 | obj-* 14 | debian/.debhelper/ 15 | debian/debhelper-build-stamp 16 | debian/*.debhelper.log 17 | debian/*.substvars 18 | debian/files 19 | debian/libubox/ 20 | debian/libubox-dev/ 21 | debian/libubox-utils/ 22 | debian/changelog 23 | debian/tmp/ 24 | -------------------------------------------------------------------------------- /tests/cram/test_blobmsg_procd_instance.t: -------------------------------------------------------------------------------- 1 | check that blobmsg parsing/checking would produce expected results in procd: 2 | 3 | $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" 4 | $ export INPUTS="$TESTDIR/inputs" 5 | 6 | $ for blob in $(LC_ALL=C find $INPUTS -type f | sort ); do 7 | > valgrind --quiet --leak-check=full test-blobmsg-procd-instance $blob; \ 8 | > test-blobmsg-procd-instance-san $blob; \ 9 | > done 10 | procd-instance-nlbwmon.bin: OK 11 | procd-instance-nlbwmon.bin: OK 12 | -------------------------------------------------------------------------------- /debian/generate-changelog.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd "$(dirname "$0")/.." 4 | 5 | COMMIT_DATE=$(git log -1 --format='%cd' --date=format:'%Y%m%d' 2>/dev/null || echo '00000000') 6 | COMMIT_HASH=$(git log -1 --format='%h' 2>/dev/null || echo 'unknown') 7 | COMMIT_TIMESTAMP=$(git log -1 --format='%cd' --date=rfc2822 2>/dev/null || date -R) 8 | 9 | cat > debian/changelog < ${COMMIT_TIMESTAMP} 15 | EOF 16 | -------------------------------------------------------------------------------- /.github/workflows/formal.yml: -------------------------------------------------------------------------------- 1 | name: Test Formalities 2 | 3 | on: 4 | pull_request: 5 | 6 | permissions: 7 | contents: read 8 | pull-requests: write 9 | 10 | jobs: 11 | build: 12 | name: Test Formalities 13 | uses: openwrt/actions-shared-workflows/.github/workflows/formal.yml@main 14 | # with: 15 | # # Post formality check summaries to the PR. 16 | # # Repo's permissions need to be updated for actions to modify PRs: 17 | # # https://docs.github.com/en/rest/issues/comments?apiVersion=2022-11-28#create-an-issue-comment 18 | # post_comment: true 19 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | IF (BUILD_EXAMPLES) 2 | PROJECT(ubox-examples C) 3 | ADD_DEFINITIONS(-O1 -Wall -Werror --std=gnu99 -g3) 4 | 5 | INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) 6 | LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) 7 | 8 | FIND_LIBRARY(json NAMES json-c json) 9 | 10 | ADD_EXECUTABLE(ustream-example ustream-example.c) 11 | TARGET_LINK_LIBRARIES(ustream-example ubox) 12 | 13 | ADD_EXECUTABLE(json_script-example json_script-example.c) 14 | TARGET_LINK_LIBRARIES(json_script-example ubox blobmsg_json json_script ${json}) 15 | ENDIF() 16 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | export DEB_BUILD_MAINT_OPTIONS = hardening=+all 4 | 5 | BUILD_DIR = obj-$(shell dpkg-architecture -qDEB_HOST_MULTIARCH) 6 | 7 | %: 8 | dh $@ --builddirectory=$(BUILD_DIR) 9 | 10 | override_dh_auto_clean: 11 | dh_auto_clean --builddirectory=$(BUILD_DIR) 12 | rm -rf $(BUILD_DIR) 13 | 14 | override_dh_auto_configure: 15 | cmake -S . -B $(BUILD_DIR) \ 16 | -DCMAKE_INSTALL_PREFIX=/usr \ 17 | -DCMAKE_BUILD_TYPE=None \ 18 | -DCMAKE_VERBOSE_MAKEFILE=ON \ 19 | -DABIVERSION=$(shell dpkg-parsechangelog | sed -rne 's/^Version: 0\.0\.([0-9]+).*$$/\1/p') \ 20 | -DBUILD_LUA=OFF \ 21 | -DBUILD_EXAMPLES=OFF 22 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ADD_SUBDIRECTORY(cram) 2 | ADD_SUBDIRECTORY(shunit2) 3 | 4 | MACRO(ADD_UNIT_TEST name) 5 | ADD_EXECUTABLE(${name} ${name}.c) 6 | TARGET_LINK_LIBRARIES(${name} ubox blobmsg_json json_script ${json}) 7 | TARGET_INCLUDE_DIRECTORIES(${name} PRIVATE ${PROJECT_SOURCE_DIR}) 8 | ENDMACRO(ADD_UNIT_TEST) 9 | 10 | FILE(GLOB test_cases "test-*.c") 11 | FOREACH(test_case ${test_cases}) 12 | GET_FILENAME_COMPONENT(test_case ${test_case} NAME_WE) 13 | ADD_UNIT_TEST(${test_case}) 14 | ADD_UNIT_TEST_SAN(${test_case}) 15 | ENDFOREACH(test_case) 16 | 17 | IF(CMAKE_C_COMPILER_ID STREQUAL "Clang") 18 | ADD_SUBDIRECTORY(fuzz) 19 | ENDIF() 20 | -------------------------------------------------------------------------------- /tests/fuzz/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | FILE(GLOB test_cases "test-*.c") 2 | 3 | MACRO(ADD_FUZZER_TEST name) 4 | ADD_EXECUTABLE(${name} ${name}.c) 5 | TARGET_COMPILE_OPTIONS(${name} PRIVATE -g -O1 -fno-omit-frame-pointer -fsanitize=fuzzer,address,leak,undefined) 6 | TARGET_INCLUDE_DIRECTORIES(${name} PRIVATE ${PROJECT_SOURCE_DIR}) 7 | TARGET_LINK_OPTIONS(${name} PRIVATE -stdlib=libc++ -fsanitize=fuzzer,address,leak,undefined) 8 | TARGET_LINK_LIBRARIES(${name} ubox blobmsg_json json_script ${json}) 9 | ADD_TEST( 10 | NAME ${name} 11 | COMMAND ${name} -max_len=256 -timeout=10 -max_total_time=300 ${CMAKE_CURRENT_SOURCE_DIR}/corpus 12 | ) 13 | ENDMACRO(ADD_FUZZER_TEST) 14 | 15 | FOREACH(test_case ${test_cases}) 16 | GET_FILENAME_COMPONENT(test_case ${test_case} NAME_WE) 17 | ADD_FUZZER_TEST(${test_case}) 18 | ENDFOREACH(test_case) 19 | -------------------------------------------------------------------------------- /tests/cram/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | FIND_PACKAGE(PythonInterp 3 REQUIRED) 2 | FILE(GLOB test_cases "test_*.t") 3 | 4 | SET(PYTHON_VENV_DIR "${CMAKE_CURRENT_BINARY_DIR}/.venv") 5 | SET(PYTHON_VENV_PIP "${PYTHON_VENV_DIR}/bin/pip") 6 | SET(PYTHON_VENV_CRAM "${PYTHON_VENV_DIR}/bin/cram") 7 | 8 | ADD_CUSTOM_COMMAND( 9 | OUTPUT ${PYTHON_VENV_CRAM} 10 | COMMAND ${PYTHON_EXECUTABLE} -m venv ${PYTHON_VENV_DIR} 11 | COMMAND ${PYTHON_VENV_PIP} install cram 12 | ) 13 | ADD_CUSTOM_TARGET(prepare-cram-venv ALL DEPENDS ${PYTHON_VENV_CRAM}) 14 | 15 | ADD_TEST( 16 | NAME cram 17 | COMMAND ${PYTHON_VENV_CRAM} ${test_cases} 18 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 19 | ) 20 | 21 | SET_PROPERTY(TEST cram APPEND PROPERTY ENVIRONMENT "JSHN=$") 22 | SET_PROPERTY(TEST cram APPEND PROPERTY ENVIRONMENT "TEST_BIN_DIR=$") 23 | -------------------------------------------------------------------------------- /tests/test-blob-buflen.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "blobmsg.h" 4 | 5 | /* chunks of 64KB to be added to blob-buffer */ 6 | #define BUFF_SIZE 0x10000 7 | /* exceed maximum blob buff-length */ 8 | #define BUFF_CHUNKS (((BLOB_ATTR_LEN_MASK + 1) / BUFF_SIZE) + 1) 9 | 10 | int main(int argc, char **argv) 11 | { 12 | int i; 13 | static struct blob_buf buf; 14 | blobmsg_buf_init(&buf); 15 | int prev_len = buf.buflen; 16 | 17 | for (i = 0; i < BUFF_CHUNKS; i++) { 18 | struct blob_attr *attr = blob_new(&buf, 0, BUFF_SIZE); 19 | if (!attr) { 20 | fprintf(stderr, "SUCCESS: failed to allocate attribute\n"); 21 | break; 22 | } 23 | if (prev_len < buf.buflen) { 24 | prev_len = buf.buflen; 25 | continue; 26 | } 27 | fprintf(stderr, "ERROR: buffer length did not increase\n"); 28 | return -1; 29 | } 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: libubox 3 | Source: https://git.openwrt.org/project/libubox.git 4 | 5 | Files: * 6 | Copyright: 2010- Felix Fietkau 7 | License: ISC 8 | 9 | Permission to use, copy, modify, and/or distribute this software for any 10 | purpose with or without fee is hereby granted, provided that the above 11 | copyright notice and this permission notice appear in all copies. 12 | . 13 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | -------------------------------------------------------------------------------- /avl-cmp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #ifndef __AVL_CMP_H 17 | #define __AVL_CMP_H 18 | 19 | int avl_strcmp(const void *k1, const void *k2, void *ptr); 20 | int avl_blobcmp(const void *k1, const void *k2, void *ptr); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /tests/test-b64.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "utils.h" 6 | 7 | #define BUF_LEN 255 8 | 9 | static void test_b64_encode(const char *src) 10 | { 11 | char *dst = malloc(BUF_LEN+1); 12 | int r = b64_encode(src, strlen(src), dst, BUF_LEN); 13 | fprintf(stdout, "%d %s\n", r, dst); 14 | free(dst); 15 | } 16 | 17 | static void test_b64_decode(const char *src) 18 | { 19 | char *dst = malloc(BUF_LEN+1); 20 | int r = b64_decode(src, dst, BUF_LEN); 21 | fprintf(stdout, "%d %s\n", r, dst); 22 | free(dst); 23 | } 24 | 25 | int main() 26 | { 27 | test_b64_encode(""); 28 | test_b64_encode("f"); 29 | test_b64_encode("fo"); 30 | test_b64_encode("foo"); 31 | test_b64_encode("foob"); 32 | test_b64_encode("fooba"); 33 | test_b64_encode("foobar"); 34 | 35 | test_b64_decode(""); 36 | test_b64_decode("Zg=="); 37 | test_b64_decode("Zm8="); 38 | test_b64_decode("Zm9v"); 39 | test_b64_decode("Zm9vYg=="); 40 | test_b64_decode("Zm9vYmE="); 41 | test_b64_decode("Zm9vYmFy"); 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | schedule: 9 | - cron: '31 12 * * 1' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze (c-cpp) 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 360 16 | permissions: 17 | security-events: write 18 | 19 | steps: 20 | - name: Install Dependencies 21 | run: | 22 | sudo apt update && sudo apt install cmake make gcc pkg-config python3 libjson-c-dev lua5.1 liblua5.1-0-dev 23 | 24 | - name: Checkout repository 25 | uses: actions/checkout@v4 26 | 27 | # Initializes the CodeQL tools for scanning. 28 | - name: Initialize CodeQL 29 | uses: github/codeql-action/init@v3 30 | with: 31 | languages: c-cpp 32 | queries: security-and-quality 33 | 34 | - name: Install libubox 35 | run: | 36 | cmake -DBUILD_EXAMPLES=OFF 37 | make 38 | sudo make install 39 | 40 | - name: Perform CodeQL Analysis 41 | uses: github/codeql-action/analyze@v3 42 | with: 43 | category: "/language:c-cpp" 44 | -------------------------------------------------------------------------------- /examples/json_script-example.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ "exec", "%EXECVAR%", "/%%/" ], 3 | [ "if", 4 | [ "eq", "EQVAR", "eqval" ], 5 | [ "exec_if", "%VAR%", "%%", "jk" ] 6 | ], 7 | [ "case", "CASEVAR", { 8 | "caseval0": ["cmd_case_0", "cmd_case_arg0", "case_cmd_arg1"], 9 | "caseval1": ["cmd_case_1", "cmd_case_arg0", "case_cmd_arg1"] 10 | } ], 11 | 12 | [ "if", 13 | [ "and", [ "eq", "EQVAR", "eqval" ], 14 | [ "has", "HASVAR" ], 15 | [ "regex", "REGEXVAR0", "regexval" ], 16 | [ "regex", "REGEXVAR1", [ "regexval10", "regexval11" ] ], 17 | [ "not", [ "eq", "NOTEQVAR", "noteqval" ] ] ], 18 | [ "exec_if_and", "%ANDVAR%" ] 19 | ], 20 | 21 | [ "if", 22 | [ "or", [ "eq", "EQVAR", "eqval" ], 23 | [ "has", "HASVAR" ], 24 | [ "regex", "REGEXVAR0", "regexval" ], 25 | [ "regex", "REGEXVAR1", [ "regexval10", "regexval11" ] ], 26 | [ "not", [ "eq", "NOTEQVAR", "noteqval" ] ] ], 27 | [ "exec_if_or", "%ORVAR%" ] 28 | ], 29 | 30 | [ "if", 31 | [ "isdir", "%ISDIRVAR%" ], 32 | [ "exec_isdir", "%ISDIRVAR%" ] 33 | ], 34 | 35 | [ "return", "foobar" ], 36 | 37 | [ "exec_non_reachable", "Arghhh" ] 38 | ] 39 | -------------------------------------------------------------------------------- /tests/cram/inputs/json-script.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ "exec", "%EXECVAR%", "/%%/" ], 3 | [ "if", 4 | [ "eq", "EQVAR", "eqval" ], 5 | [ "exec_if", "%VAR%", "%%", "jk" ] 6 | ], 7 | [ "case", "CASEVAR", { 8 | "caseval0": ["cmd_case_0", "cmd_case_arg0", "case_cmd_arg1"], 9 | "caseval1": ["cmd_case_1", "cmd_case_arg0", "case_cmd_arg1"] 10 | } ], 11 | 12 | [ "if", 13 | [ "and", [ "eq", "EQVAR", "eqval" ], 14 | [ "has", "HASVAR" ], 15 | [ "regex", "REGEXVAR0", "regexval" ], 16 | [ "regex", "REGEXVAR1", [ "regexval10", "regexval11" ] ], 17 | [ "not", [ "eq", "NOTEQVAR", "noteqval" ] ] ], 18 | [ "exec_if_and", "%ANDVAR%" ] 19 | ], 20 | 21 | [ "if", 22 | [ "or", [ "eq", "EQVAR", "eqval" ], 23 | [ "has", "HASVAR" ], 24 | [ "regex", "REGEXVAR0", "regexval" ], 25 | [ "regex", "REGEXVAR1", [ "regexval10", "regexval11" ] ], 26 | [ "not", [ "eq", "NOTEQVAR", "noteqval" ] ] ], 27 | [ "exec_if_or", "%ORVAR%" ] 28 | ], 29 | 30 | [ "if", 31 | [ "isdir", "%ISDIRVAR%" ], 32 | [ "exec_isdir", "%ISDIRVAR%" ] 33 | ], 34 | 35 | [ "return", "foobar" ], 36 | 37 | [ "exec_non_reachable", "Arghhh" ] 38 | ] 39 | -------------------------------------------------------------------------------- /lua/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT(uloop C) 2 | 3 | SET(CMAKE_INSTALL_PREFIX /) 4 | 5 | IF(NOT LUA_CFLAGS) 6 | pkg_search_module(LUA lua5.1 lua-5.1) 7 | ENDIF() 8 | 9 | ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -I.. ${LUA_CFLAGS}) 10 | LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) 11 | 12 | IF(APPLE) 13 | SET(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") 14 | ENDIF(APPLE) 15 | 16 | IF(NOT LUAPATH) 17 | EXECUTE_PROCESS( 18 | COMMAND lua -e "for k in string.gmatch(package.cpath .. \";\", \"([^;]+)/..so;\") do if k:sub(1,1) == \"/\" then print(k) break end end" 19 | OUTPUT_VARIABLE LUAPATH 20 | RESULT_VARIABLE LUA_CHECK_RES 21 | OUTPUT_STRIP_TRAILING_WHITESPACE 22 | ) 23 | 24 | IF(BUILD_LUA) 25 | IF(NOT ${LUA_CHECK_RES} EQUAL 0 OR "${LUAPATH}" EQUAL "") 26 | MESSAGE(SEND_ERROR "Lua was not found on your system") 27 | ENDIF() 28 | ENDIF() 29 | ENDIF() 30 | 31 | IF(BUILD_LUA) 32 | ADD_LIBRARY(uloop_lua MODULE uloop.c) 33 | SET_TARGET_PROPERTIES(uloop_lua PROPERTIES 34 | OUTPUT_NAME uloop 35 | PREFIX "" 36 | ) 37 | TARGET_LINK_LIBRARIES(uloop_lua ubox) 38 | 39 | INSTALL(TARGETS uloop_lua 40 | LIBRARY DESTINATION ${LUAPATH} 41 | ) 42 | ENDIF() 43 | -------------------------------------------------------------------------------- /tests/cram/test_base64.t: -------------------------------------------------------------------------------- 1 | set test bin path: 2 | 3 | $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" 4 | 5 | check that base64 is producing expected results: 6 | 7 | $ valgrind --quiet --leak-check=full test-b64 8 | 0 9 | 4 Zg== 10 | 4 Zm8= 11 | 4 Zm9v 12 | 8 Zm9vYg== 13 | 8 Zm9vYmE= 14 | 8 Zm9vYmFy 15 | 0 16 | 1 f 17 | 2 fo 18 | 3 foo 19 | 4 foob 20 | 5 fooba 21 | 6 foobar 22 | 23 | $ test-b64-san 24 | 0 25 | 4 Zg== 26 | 4 Zm8= 27 | 4 Zm9v 28 | 8 Zm9vYg== 29 | 8 Zm9vYmE= 30 | 8 Zm9vYmFy 31 | 0 32 | 1 f 33 | 2 fo 34 | 3 foo 35 | 4 foob 36 | 5 fooba 37 | 6 foobar 38 | 39 | check that b64_encode and b64_decode assert invalid input 40 | 41 | $ alias check="grep Assertion output.log | sed 's;.*\(b64_.*code\).*\(Assertion.*$\);\1: \2;' | LC_ALL=C sort" 42 | 43 | $ test-b64_decode > output.log 2>&1; check 44 | b64_decode: Assertion `dest && targsize > 0' failed. 45 | 46 | $ test-b64_encode > output.log 2>&1; check 47 | b64_encode: Assertion `dest && targsize > 0' failed. 48 | 49 | $ test-b64_decode-san > output.log 2>&1; check 50 | b64_decode: Assertion `dest && targsize > 0' failed. 51 | 52 | $ test-b64_encode-san > output.log 2>&1; check 53 | b64_encode: Assertion `dest && targsize > 0' failed. 54 | -------------------------------------------------------------------------------- /avl-cmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #include 17 | #include "avl-cmp.h" 18 | #include "blob.h" 19 | 20 | static inline int _min(int v1, int v2) 21 | { 22 | return v1 < v2 ? v1 : v2; 23 | } 24 | 25 | int 26 | avl_strcmp(const void *k1, const void *k2, void *ptr) 27 | { 28 | return strcmp(k1, k2); 29 | } 30 | 31 | int 32 | avl_blobcmp(const void *k1, const void *k2, void *ptr) 33 | { 34 | int len = _min(blob_raw_len(k1), blob_raw_len(k2)); 35 | 36 | return memcmp(k1, k2, len); 37 | } 38 | -------------------------------------------------------------------------------- /tests/cram/test_runqueue.t: -------------------------------------------------------------------------------- 1 | check that runqueue is producing expected results: 2 | 3 | $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" 4 | $ valgrind --quiet --leak-check=full test-runqueue 5 | [1/1] start 'sleep 1' (killer) 6 | [1/1] killing process (killer) 7 | [0/1] finish 'sleep 1' (killer) 8 | [0/1] finish 'sleep 1' (killer) 9 | [0/1] finish 'sleep 1' (killer) 10 | [1/1] start 'sleep 1' (sleeper) 11 | [1/1] cancel 'sleep 1' (sleeper) 12 | [0/1] finish 'sleep 1' (sleeper) 13 | [1/1] start 'sleep 1' (sleeper) 14 | [1/1] cancel 'sleep 1' (sleeper) 15 | [0/1] finish 'sleep 1' (sleeper) 16 | [1/1] start 'sleep 1' (sleeper) 17 | [1/1] cancel 'sleep 1' (sleeper) 18 | [0/1] finish 'sleep 1' (sleeper) 19 | All done! 20 | 21 | $ test-runqueue-san 22 | [1/1] start 'sleep 1' (killer) 23 | [1/1] killing process (killer) 24 | [0/1] finish 'sleep 1' (killer) 25 | [0/1] finish 'sleep 1' (killer) 26 | [0/1] finish 'sleep 1' (killer) 27 | [1/1] start 'sleep 1' (sleeper) 28 | [1/1] cancel 'sleep 1' (sleeper) 29 | [0/1] finish 'sleep 1' (sleeper) 30 | [1/1] start 'sleep 1' (sleeper) 31 | [1/1] cancel 'sleep 1' (sleeper) 32 | [0/1] finish 'sleep 1' (sleeper) 33 | [1/1] start 'sleep 1' (sleeper) 34 | [1/1] cancel 'sleep 1' (sleeper) 35 | [0/1] finish 'sleep 1' (sleeper) 36 | All done! 37 | -------------------------------------------------------------------------------- /tests/cram/test_avl.t: -------------------------------------------------------------------------------- 1 | check that avl is producing expected results: 2 | 3 | $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" 4 | $ valgrind --quiet --leak-check=full test-avl 5 | test_basics: insert: 0=zero 0=one 0=two 0=three 0=four 0=five 0=six 0=seven 0=eight 0=nine 0=ten 0=eleven 0=twelve 6 | test_basics: insert duplicate: -1=zero -1=one -1=two -1=three -1=four -1=five -1=six -1=seven -1=eight -1=nine -1=ten -1=eleven -1=twelve 7 | test_basics: first=eight last=zero 8 | test_basics: for each element: eight eleven five four nine one seven six ten three twelve two zero 9 | test_basics: delete 'one' element 10 | test_basics: for each element reverse: zero two twelve three ten six seven nine four five eleven eight 11 | test_basics: delete all elements 12 | 13 | $ test-avl-san 14 | test_basics: insert: 0=zero 0=one 0=two 0=three 0=four 0=five 0=six 0=seven 0=eight 0=nine 0=ten 0=eleven 0=twelve 15 | test_basics: insert duplicate: -1=zero -1=one -1=two -1=three -1=four -1=five -1=six -1=seven -1=eight -1=nine -1=ten -1=eleven -1=twelve 16 | test_basics: first=eight last=zero 17 | test_basics: for each element: eight eleven five four nine one seven six ten three twelve two zero 18 | test_basics: delete 'one' element 19 | test_basics: for each element reverse: zero two twelve three ten six seven nine four five eleven eight 20 | test_basics: delete all elements 21 | -------------------------------------------------------------------------------- /ulog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ulog - simple logging functions 3 | * 4 | * Copyright (C) 2015 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __LIBUBOX_ULOG_H 20 | #define __LIBUBOX_ULOG_H 21 | 22 | #include 23 | 24 | #include "udebug.h" 25 | 26 | enum { 27 | ULOG_KMSG = (1 << 0), 28 | ULOG_SYSLOG = (1 << 1), 29 | ULOG_STDIO = (1 << 2) 30 | }; 31 | 32 | void ulog_open(int channels, int facility, const char *ident); 33 | void ulog_udebug(struct udebug_buf *udb); 34 | void ulog_close(void); 35 | 36 | void ulog_threshold(int threshold); 37 | 38 | void ulog(int priority, const char *fmt, ...) 39 | __attribute__ ((format (printf, 2, 3))); 40 | 41 | #define ULOG_INFO(fmt, ...) ulog(LOG_INFO, fmt, ## __VA_ARGS__) 42 | #define ULOG_NOTE(fmt, ...) ulog(LOG_NOTICE, fmt, ## __VA_ARGS__) 43 | #define ULOG_WARN(fmt, ...) ulog(LOG_WARNING, fmt, ## __VA_ARGS__) 44 | #define ULOG_ERR(fmt, ...) ulog(LOG_ERR, fmt, ## __VA_ARGS__) 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /udebug-priv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * udebug - debug ring buffer library 3 | * 4 | * Copyright (C) 2023 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #ifndef __UDEBUG_UTIL_H 19 | #define __UDEBUG_UTIL_H 20 | 21 | #include "blobmsg.h" 22 | #include "udebug.h" 23 | #include "udebug-proto.h" 24 | 25 | #define UDEBUG_TIMEOUT 1000 26 | 27 | __hidden int udebug_buf_open(struct udebug_buf *buf, int fd, uint32_t ring_size, uint32_t data_size); 28 | __hidden struct udebug_client_msg * 29 | udebug_send_and_wait(struct udebug *ctx, struct udebug_client_msg *msg, int *rfd); 30 | 31 | static inline int32_t u32_sub(uint32_t a, uint32_t b) 32 | { 33 | return a - b; 34 | } 35 | 36 | static inline int32_t u32_max(uint32_t a, uint32_t b) 37 | { 38 | return u32_sub(a, b) > 0 ? a : b; 39 | } 40 | 41 | static inline void u32_set(void *ptr, uint32_t val) 42 | { 43 | volatile uint32_t *v = ptr; 44 | *v = val; 45 | } 46 | 47 | static inline uint32_t u32_get(void *ptr) 48 | { 49 | volatile uint32_t *v = ptr; 50 | return *v; 51 | } 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /tests/test-blobmsg-parse.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "blobmsg.h" 7 | 8 | enum { 9 | FOO_MESSAGE, 10 | FOO_LIST, 11 | FOO_TESTDATA, 12 | __FOO_MAX 13 | }; 14 | 15 | static const struct blobmsg_policy foo_policy[] = { 16 | [FOO_MESSAGE] = { 17 | .name = "message", 18 | .type = BLOBMSG_TYPE_STRING, 19 | }, 20 | [FOO_LIST] = { 21 | .name = "list", 22 | .type = BLOBMSG_TYPE_ARRAY, 23 | }, 24 | [FOO_TESTDATA] = { 25 | .name = "testdata", 26 | .type = BLOBMSG_TYPE_TABLE, 27 | }, 28 | }; 29 | 30 | static void dump_result(const char *fn, int r, const char *filename, struct blob_attr **tb) 31 | { 32 | fprintf(stdout, "%s: %s: %c%c%c (%d)\n", basename((char *) filename), fn, 33 | tb[FOO_MESSAGE] ? 'M' : '.', 34 | tb[FOO_LIST] ? 'L' : '.', 35 | tb[FOO_TESTDATA] ? 'T' : '.', 36 | r); 37 | } 38 | 39 | static void test_blobmsg(const char *filename) 40 | { 41 | #define BUF_LEN 256 42 | int r = 0; 43 | size_t len = 0; 44 | FILE *fd = NULL; 45 | char *buf = NULL; 46 | struct blob_attr *tb[__FOO_MAX]; 47 | 48 | fd = fopen(filename, "r"); 49 | if (!fd) { 50 | fprintf(stderr, "unable to open %s\n", filename); 51 | return; 52 | } 53 | 54 | buf = malloc(BUF_LEN+1); 55 | if (!buf) 56 | return; 57 | 58 | len = fread(buf, 1, BUF_LEN, fd); 59 | fclose(fd); 60 | 61 | r = blobmsg_parse(foo_policy, ARRAY_SIZE(foo_policy), tb, buf, len); 62 | dump_result("blobmsg_parse", r, filename, tb); 63 | 64 | r = blobmsg_parse_array(foo_policy, ARRAY_SIZE(foo_policy), tb, buf, len); 65 | dump_result("blobmsg_parse_array", r, filename, tb); 66 | 67 | free(buf); 68 | } 69 | 70 | int main(int argc, char *argv[]) 71 | { 72 | if (argc != 2) { 73 | fprintf(stderr, "Usage: %s \n", argv[0]); 74 | return 3; 75 | } 76 | 77 | test_blobmsg(argv[1]); 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /usock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usock - socket helper functions 3 | * 4 | * Copyright (C) 2010 Steven Barth 5 | * Copyright (C) 2011-2012 Felix Fietkau 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | #ifndef USOCK_H_ 20 | #define USOCK_H_ 21 | 22 | #define USOCK_TCP 0 23 | #define USOCK_UDP 1 24 | 25 | #define USOCK_SERVER 0x0100 26 | #define USOCK_NOCLOEXEC 0x0200 27 | #define USOCK_NONBLOCK 0x0400 28 | #define USOCK_NUMERIC 0x0800 29 | #define USOCK_IPV6ONLY 0x2000 30 | #define USOCK_IPV4ONLY 0x4000 31 | #define USOCK_UNIX 0x8000 32 | 33 | const char *usock_port(int port); 34 | int usock(int type, const char *host, const char *service); 35 | int usock_inet_timeout(int type, const char *host, const char *service, 36 | void *addr, int timeout); 37 | static inline int 38 | usock_inet(int type, const char *host, const char *service, void *addr) 39 | { 40 | return usock_inet_timeout(type, host, service, addr, -1); 41 | } 42 | 43 | /** 44 | * Wait for a socket to become ready. 45 | * 46 | * This may be useful for users of USOCK_NONBLOCK to wait (with a timeout) 47 | * for a socket. 48 | * 49 | * @param fd file descriptor of socket 50 | * @param msecs timeout in microseconds 51 | */ 52 | int usock_wait_ready(int fd, int msecs); 53 | 54 | #endif /* USOCK_H_ */ 55 | -------------------------------------------------------------------------------- /udebug-proto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * udebug - debug ring buffer library 3 | * 4 | * Copyright (C) 2023 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #ifndef __UDEBUG_PROTO_H 19 | #define __UDEBUG_PROTO_H 20 | 21 | #include "udebug.h" 22 | 23 | struct udebug_hdr { 24 | uint32_t ring_size; 25 | uint32_t data_size; 26 | 27 | uint32_t format; 28 | uint32_t sub_format; 29 | 30 | uintptr_t flags[8 / sizeof(uintptr_t)]; 31 | uintptr_t notify; 32 | 33 | uint32_t head_hi; 34 | uint32_t head; 35 | uint32_t data_head; 36 | uint32_t data_used; 37 | }; 38 | 39 | enum udebug_client_msg_type { 40 | CL_MSG_RING_ADD, 41 | CL_MSG_RING_REMOVE, 42 | CL_MSG_RING_NOTIFY, 43 | CL_MSG_GET_HANDLE, 44 | CL_MSG_RING_GET, 45 | CL_MSG_ERROR, 46 | }; 47 | 48 | struct udebug_client_msg { 49 | uint8_t type; 50 | uint8_t _pad[3]; 51 | uint32_t id; 52 | union { 53 | struct { 54 | uint32_t ring_size, data_size; 55 | }; 56 | uint32_t notify_mask; 57 | }; 58 | } __attribute__((packed, aligned(4))); 59 | 60 | static inline struct udebug_ptr * 61 | udebug_ring_ptr(struct udebug_hdr *hdr, uint32_t idx) 62 | { 63 | struct udebug_ptr *ring = (struct udebug_ptr *)&hdr[1]; 64 | return &ring[idx & (hdr->ring_size - 1)]; 65 | } 66 | 67 | static inline void *udebug_buf_ptr(struct udebug_buf *buf, uint32_t ofs) 68 | { 69 | return buf->data + (ofs & (buf->data_size - 1)); 70 | } 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /scripts/devel-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euxo pipefail 4 | cd "${0%/*}" 5 | cd .. 6 | 7 | # Sanity checks 8 | if [ ! -e "CMakeLists.txt" ] || [ ! -e "blob.c" ]; then 9 | echo "libubox checkout not found" >&2 10 | exit 1 11 | fi 12 | 13 | if [ $# -eq 0 ]; then 14 | BUILD_ARGS="-DBUILD_LUA=ON -DBUILD_EXAMPLES=ON -DUNIT_TESTING=ON" 15 | else 16 | BUILD_ARGS="$@" 17 | fi 18 | 19 | # Create build dirs 20 | LIBUBOXDIR="$(pwd)" 21 | BUILDDIR="${LIBUBOXDIR}/build" 22 | DEPSDIR="${BUILDDIR}/depends" 23 | [ -e "${BUILDDIR}" ] || mkdir "${BUILDDIR}" 24 | [ -e "${DEPSDIR}" ] || mkdir "${DEPSDIR}" 25 | 26 | # Prepare env 27 | export LD_LIBRARY_PATH="${BUILDDIR}/lib:${LD_LIBRARY_PATH:-}" 28 | export PATH="${BUILDDIR}/bin:${PATH:-}" 29 | 30 | # Download deps 31 | cd "${DEPSDIR}" 32 | [ -e "json-c" ] || git clone https://github.com/json-c/json-c.git 33 | if [ ! -e "lua" ]; then 34 | mkdir -p lua 35 | wget -qO- https://www.lua.org/ftp/lua-5.1.5.tar.gz | \ 36 | tar zxvf - -C lua --strip-components=1 37 | sed -i '/#define LUA_USE_READLINE/d' ./lua/src/luaconf.h 38 | sed -i 's/ -lreadline -lhistory -lncurses//g' ./lua/src/Makefile 39 | fi 40 | 41 | # Build lua 42 | cd "${DEPSDIR}/lua" 43 | make linux install \ 44 | INSTALL_TOP="${BUILDDIR}" 45 | 46 | # Build json-c 47 | cd "${DEPSDIR}/json-c" 48 | cmake \ 49 | -S . \ 50 | -B . \ 51 | -DCMAKE_PREFIX_PATH="${BUILDDIR}" \ 52 | -DBUILD_SHARED_LIBS=ON \ 53 | -DBUILD_STATIC_LIBS=OFF \ 54 | -DDISABLE_EXTRA_LIBS=ON \ 55 | -DBUILD_TESTING=OFF \ 56 | --install-prefix "${BUILDDIR}" 57 | make 58 | make install 59 | 60 | # Build libubox 61 | cd "${LIBUBOXDIR}" 62 | cmake \ 63 | -S . \ 64 | -B "${BUILDDIR}" \ 65 | -DCMAKE_PREFIX_PATH="${BUILDDIR}" \ 66 | -DLUAPATH=${BUILDDIR}/lib/lua \ 67 | --install-prefix "${BUILDDIR}" \ 68 | ${BUILD_ARGS} 69 | make -C "${BUILDDIR}" 70 | make -C "${BUILDDIR}" install 71 | 72 | # Test libubox 73 | make -C "${BUILDDIR}" test CTEST_OUTPUT_ON_FAILURE=1 74 | 75 | set +x 76 | echo "✅ Success - the libubox library is available at ${BUILDDIR}" 77 | echo "👷 You can rebuild libubox by running 'make -C build'" 78 | 79 | exit 0 80 | -------------------------------------------------------------------------------- /tests/test-json-script.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include "blobmsg.h" 6 | #include "blobmsg_json.h" 7 | #include "json_script.h" 8 | 9 | struct json_script_ctx jctx; 10 | struct blob_buf b_vars; 11 | struct blob_buf b_script; 12 | 13 | static void handle_command(struct json_script_ctx *ctx, const char *name, 14 | struct blob_attr *data, struct blob_attr *vars) 15 | { 16 | struct blob_attr *cur; 17 | size_t rem; 18 | 19 | fprintf(stdout, "%s", name); 20 | blobmsg_for_each_attr(cur, data, rem) 21 | fprintf(stdout, " %s", (char *) blobmsg_data(cur)); 22 | fprintf(stdout, "\n"); 23 | } 24 | 25 | static struct json_script_file * 26 | handle_file(struct json_script_ctx *ctx, const char *filename) 27 | { 28 | json_object *obj; 29 | 30 | obj = json_object_from_file(filename); 31 | if (!obj) { 32 | fprintf(stderr, "load JSON data from %s failed.\n", filename); 33 | return NULL; 34 | } 35 | 36 | blob_buf_init(&b_script, 0); 37 | blobmsg_add_json_element(&b_script, "", obj); 38 | json_object_put(obj); 39 | 40 | return json_script_file_from_blobmsg(filename, 41 | blob_data(b_script.head), blob_len(b_script.head)); 42 | } 43 | 44 | static void usage(const char *prog, int exit_code) 45 | { 46 | fprintf(stderr, "Usage: %s [VARNAME=value] \n", prog); 47 | exit(exit_code); 48 | } 49 | 50 | int main(int argc, char *argv[]) 51 | { 52 | int i; 53 | char *file = NULL; 54 | const char *prog = argv[0]; 55 | 56 | blobmsg_buf_init(&b_vars); 57 | blobmsg_buf_init(&b_script); 58 | 59 | json_script_init(&jctx); 60 | jctx.handle_command = handle_command; 61 | jctx.handle_file = handle_file; 62 | 63 | for (i = 1; i < argc; i++) { 64 | char *sep = strchr(argv[i], '='); 65 | if (sep) { 66 | *sep = '\0'; 67 | blobmsg_add_string(&b_vars, argv[i], sep + 1); 68 | } else if (!file) { 69 | file = argv[i]; 70 | } else { 71 | usage(prog, -1); 72 | } 73 | } 74 | if (i < argc || !file) 75 | usage(prog, -2); 76 | 77 | json_script_run(&jctx, file, b_vars.head); 78 | 79 | json_script_free(&jctx); 80 | blob_buf_free(&b_script); 81 | blob_buf_free(&b_vars); 82 | 83 | return 0; 84 | } 85 | -------------------------------------------------------------------------------- /examples/json_script-example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include "blobmsg.h" 6 | #include "blobmsg_json.h" 7 | #include "json_script.h" 8 | 9 | struct json_script_ctx jctx; 10 | struct blob_buf b_vars; 11 | struct blob_buf b_script; 12 | 13 | static void handle_command(struct json_script_ctx *ctx, const char *name, 14 | struct blob_attr *data, struct blob_attr *vars) 15 | { 16 | struct blob_attr *cur; 17 | size_t rem; 18 | 19 | fprintf(stdout, "%s", name); 20 | blobmsg_for_each_attr(cur, data, rem) 21 | fprintf(stdout, " %s", (char *) blobmsg_data(cur)); 22 | fprintf(stdout, "\n"); 23 | } 24 | 25 | static struct json_script_file * 26 | handle_file(struct json_script_ctx *ctx, const char *filename) 27 | { 28 | json_object *obj; 29 | 30 | obj = json_object_from_file(filename); 31 | if (!obj) { 32 | fprintf(stderr, "load JSON data from %s failed.\n", filename); 33 | return NULL; 34 | } 35 | 36 | blob_buf_init(&b_script, 0); 37 | blobmsg_add_json_element(&b_script, "", obj); 38 | json_object_put(obj); 39 | 40 | return json_script_file_from_blobmsg(filename, 41 | blob_data(b_script.head), blob_len(b_script.head)); 42 | } 43 | 44 | static void usage(const char *prog, int exit_code) 45 | { 46 | fprintf(stderr, "Usage: %s [VARNAME=value] \n", prog); 47 | exit(exit_code); 48 | } 49 | 50 | int main(int argc, char *argv[]) 51 | { 52 | int i; 53 | char *file = NULL; 54 | const char *prog = argv[0]; 55 | 56 | blobmsg_buf_init(&b_vars); 57 | blobmsg_buf_init(&b_script); 58 | 59 | json_script_init(&jctx); 60 | jctx.handle_command = handle_command; 61 | jctx.handle_file = handle_file; 62 | 63 | for (i = 1; i < argc; i++) { 64 | char *sep = strchr(argv[i], '='); 65 | if (sep) { 66 | *sep = '\0'; 67 | blobmsg_add_string(&b_vars, argv[i], sep + 1); 68 | } else if (!file) { 69 | file = argv[i]; 70 | } else { 71 | usage(prog, -1); 72 | } 73 | } 74 | if (i < argc || !file) 75 | usage(prog, -2); 76 | 77 | json_script_run(&jctx, file, b_vars.head); 78 | 79 | json_script_free(&jctx); 80 | blob_buf_free(&b_script); 81 | blob_buf_free(&b_vars); 82 | 83 | return 0; 84 | } 85 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: libubox 2 | Maintainer: Felix Fietkau 3 | Section: libs 4 | Priority: optional 5 | Standards-Version: 4.5.0 6 | Rules-Requires-Root: no 7 | Build-Depends: debhelper-compat (= 12), cmake, pkgconf, libjson-c-dev 8 | Homepage: https://git.openwrt.org/project/libubox.git 9 | Vcs-Browser: https://git.openwrt.org/project/libubox.git 10 | Vcs-Git: https://git.openwrt.org/project/libubox.git 11 | 12 | Package: libubox 13 | Architecture: any 14 | Multi-Arch: same 15 | Pre-Depends: ${misc:Pre-Depends} 16 | Depends: ${shlibs:Depends}, ${misc:Depends} 17 | Section: libs 18 | Description: OpenWrt utility library 19 | Libubox is a utility library used by OpenWrt. It provides various data 20 | structures (AVL tree, linked lists, blob format, key-value lists) and 21 | utilities (event loop, socket helpers, base64 encoding, MD5 hashing, 22 | logging) commonly used in OpenWrt. 23 | . 24 | This package contains the shared libraries: libubox, libblobmsg_json, 25 | and libjson_script. 26 | 27 | Package: libubox-dev 28 | Architecture: any 29 | Multi-Arch: same 30 | Depends: libubox (= ${binary:Version}), libc6-dev|libc-dev, libjson-c-dev, ${misc:Depends} 31 | Section: libdevel 32 | Description: Development files for libubox 33 | Libubox is a utility library used by OpenWrt. It provides various data 34 | structures (AVL tree, linked lists, blob format, key-value lists) and 35 | utilities (event loop, socket helpers, base64 encoding, MD5 hashing, 36 | logging) commonly used in OpenWrt. 37 | . 38 | This package contains the development files (headers, static libraries, 39 | and symlinks) for libubox. 40 | 41 | Package: libubox-utils 42 | Architecture: any 43 | Multi-Arch: foreign 44 | Depends: libubox (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends} 45 | Description: Utility programs for libubox 46 | Libubox is a utility library used by OpenWrt. It provides various data 47 | structures (AVL tree, linked lists, blob format, key-value lists) and 48 | utilities (event loop, socket helpers, base64 encoding, MD5 hashing, 49 | logging) commonly used in OpenWrt. 50 | . 51 | This package contains the jshn utility for JSON shell integration and 52 | helper shell scripts. 53 | -------------------------------------------------------------------------------- /safe_list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * safe_list - linked list protected against recursive iteration with deletes 3 | * 4 | * Copyright (C) 2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /* 20 | * Use this linked list implementation as a replacement for list.h if you 21 | * want to allow deleting arbitrary list entries from within one or more 22 | * recursive iterator calling context 23 | */ 24 | 25 | #ifndef __LIBUBOX_SAFE_LIST_H 26 | #define __LIBUBOX_SAFE_LIST_H 27 | 28 | #include 29 | #include "list.h" 30 | #include "utils.h" 31 | 32 | struct safe_list; 33 | struct safe_list_iterator; 34 | 35 | struct safe_list { 36 | struct list_head list; 37 | struct safe_list_iterator *i; 38 | }; 39 | 40 | int safe_list_for_each(struct safe_list *list, 41 | int (*cb)(void *ctx, struct safe_list *list), 42 | void *ctx); 43 | 44 | void safe_list_add(struct safe_list *list, struct safe_list *head); 45 | void safe_list_add_first(struct safe_list *list, struct safe_list *head); 46 | void safe_list_del(struct safe_list *list); 47 | 48 | #define INIT_SAFE_LIST(_head) \ 49 | do { \ 50 | INIT_LIST_HEAD(_head.list); \ 51 | (_head)->i = NULL; \ 52 | } while (0) 53 | 54 | #define SAFE_LIST_INIT(_name) { LIST_HEAD_INIT(_name.list), NULL } 55 | #define SAFE_LIST(_name) struct safe_list _name = SAFE_LIST_INIT(_name) 56 | 57 | static inline bool safe_list_empty(struct safe_list *head) 58 | { 59 | return list_empty(&head->list); 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /tests/test-avl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "avl.h" 6 | #include "avl-cmp.h" 7 | #include "utils.h" 8 | 9 | #define OUT(fmt, ...) do { \ 10 | fprintf(stdout, "%s: " fmt, __func__, ## __VA_ARGS__); \ 11 | } while (0); 12 | 13 | struct node { 14 | struct avl_node avl; 15 | }; 16 | 17 | static void test_basics() 18 | { 19 | size_t i; 20 | struct avl_tree t; 21 | struct node *temp; 22 | struct node *elem; 23 | struct node *last; 24 | struct node *first; 25 | const char *vals[] = { 26 | "zero", "one", "two", "three", "four", "five", "six", 27 | "seven", "eight", "nine", "ten", "eleven", "twelve" 28 | }; 29 | 30 | avl_init(&t, avl_strcmp, false, NULL); 31 | 32 | OUT("insert: "); 33 | for (i=0; iavl.key = vals[i]; 36 | 37 | int r = avl_insert(&t, &n->avl); 38 | fprintf(stdout, "%d=%s ", r, (char *)n->avl.key); 39 | } 40 | fprintf(stdout, "\n"); 41 | 42 | OUT("insert duplicate: "); 43 | for (i=0; iavl.key = vals[i]; 46 | 47 | int r = avl_insert(&t, &n->avl); 48 | fprintf(stdout, "%d=%s ", r, (char *)n->avl.key); 49 | 50 | if (r) 51 | free(n); 52 | } 53 | fprintf(stdout, "\n"); 54 | 55 | first = avl_first_element(&t, first, avl); 56 | last = avl_last_element(&t, last, avl); 57 | OUT("first=%s last=%s\n", (char*)first->avl.key, (char*)last->avl.key); 58 | 59 | OUT("for each element: "); 60 | avl_for_each_element(&t, elem, avl) { 61 | fprintf(stdout, "%s ", (char*)elem->avl.key); 62 | } 63 | fprintf(stdout, "\n"); 64 | 65 | OUT("delete 'one' element\n"); 66 | elem = avl_find_element(&t, "one", elem, avl); 67 | avl_delete(&t, &elem->avl); 68 | free(elem); 69 | 70 | OUT("for each element reverse: "); 71 | avl_for_each_element_reverse(&t, elem, avl) { 72 | fprintf(stdout, "%s ", (char*)elem->avl.key); 73 | } 74 | fprintf(stdout, "\n"); 75 | 76 | OUT("delete all elements\n"); 77 | avl_for_each_element_safe(&t, elem, avl, temp) { 78 | avl_delete(&t, &elem->avl); 79 | free(elem); 80 | } 81 | } 82 | 83 | int main() 84 | { 85 | test_basics(); 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /md5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | /* 17 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. 18 | * MD5 Message-Digest Algorithm (RFC 1321). 19 | * 20 | * Homepage: 21 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 22 | * 23 | * Author: 24 | * Alexander Peslyak, better known as Solar Designer 25 | * 26 | * This software was written by Alexander Peslyak in 2001. No copyright is 27 | * claimed, and the software is hereby placed in the public domain. 28 | * In case this attempt to disclaim copyright and place the software in the 29 | * public domain is deemed null and void, then the software is 30 | * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the 31 | * general public under the following terms: 32 | * 33 | * Redistribution and use in source and binary forms, with or without 34 | * modification, are permitted. 35 | * 36 | * There's ABSOLUTELY NO WARRANTY, express or implied. 37 | * 38 | * See md5.c for more information. 39 | */ 40 | 41 | #ifndef _LIBUBOX_MD5_H 42 | #define _LIBUBOX_MD5_H 43 | 44 | #include 45 | #include 46 | 47 | typedef struct md5_ctx { 48 | uint32_t lo, hi; 49 | uint32_t a, b, c, d; 50 | unsigned char buffer[64]; 51 | } md5_ctx_t; 52 | 53 | extern void md5_begin(md5_ctx_t *ctx); 54 | extern void md5_hash(const void *data, size_t length, md5_ctx_t *ctx); 55 | extern void md5_end(void *resbuf, md5_ctx_t *ctx); 56 | int md5sum(const char *file, void *md5_buf); 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /vlist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #include "vlist.h" 17 | 18 | void 19 | vlist_init(struct vlist_tree *tree, avl_tree_comp cmp, vlist_update_cb update) 20 | { 21 | tree->update = update; 22 | tree->version = 1; 23 | 24 | avl_init(&tree->avl, cmp, 0, tree); 25 | } 26 | 27 | void 28 | vlist_delete(struct vlist_tree *tree, struct vlist_node *node) 29 | { 30 | if (!tree->no_delete) 31 | avl_delete(&tree->avl, &node->avl); 32 | tree->update(tree, NULL, node); 33 | } 34 | 35 | void 36 | vlist_add(struct vlist_tree *tree, struct vlist_node *node, const void *key) 37 | { 38 | struct vlist_node *old_node = NULL; 39 | struct avl_node *anode; 40 | 41 | node->avl.key = key; 42 | node->version = tree->version; 43 | 44 | anode = avl_find(&tree->avl, key); 45 | if (anode) { 46 | old_node = container_of(anode, struct vlist_node, avl); 47 | if (tree->keep_old || tree->no_delete) { 48 | old_node->version = tree->version; 49 | goto update_only; 50 | } 51 | 52 | avl_delete(&tree->avl, anode); 53 | } 54 | 55 | avl_insert(&tree->avl, &node->avl); 56 | 57 | update_only: 58 | tree->update(tree, node, old_node); 59 | } 60 | 61 | void 62 | vlist_flush(struct vlist_tree *tree) 63 | { 64 | struct vlist_node *node, *tmp; 65 | 66 | avl_for_each_element_safe(&tree->avl, node, avl, tmp) { 67 | if ((node->version == tree->version || node->version == -1) && 68 | tree->version != -1) 69 | continue; 70 | 71 | vlist_delete(tree, node); 72 | } 73 | } 74 | 75 | void 76 | vlist_flush_all(struct vlist_tree *tree) 77 | { 78 | tree->version = -1; 79 | vlist_flush(tree); 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /blobmsg_json.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010-2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #ifndef __BLOBMSG_JSON_H 17 | #define __BLOBMSG_JSON_H 18 | 19 | struct json_object; 20 | 21 | #include 22 | #include "blobmsg.h" 23 | 24 | bool blobmsg_add_object(struct blob_buf *b, struct json_object *obj); 25 | bool blobmsg_add_json_element(struct blob_buf *b, const char *name, struct json_object *obj); 26 | bool blobmsg_add_json_from_string(struct blob_buf *b, const char *str); 27 | bool blobmsg_add_json_from_file(struct blob_buf *b, const char *file); 28 | 29 | typedef const char *(*blobmsg_json_format_t)(void *priv, struct blob_attr *attr); 30 | 31 | char *blobmsg_format_json_with_cb(struct blob_attr *attr, bool list, 32 | blobmsg_json_format_t cb, void *priv, 33 | int indent); 34 | 35 | static inline char *blobmsg_format_json(struct blob_attr *attr, bool list) 36 | { 37 | return blobmsg_format_json_with_cb(attr, list, NULL, NULL, -1); 38 | } 39 | 40 | static inline char *blobmsg_format_json_indent(struct blob_attr *attr, bool list, int indent) 41 | { 42 | return blobmsg_format_json_with_cb(attr, list, NULL, NULL, indent); 43 | } 44 | 45 | char *blobmsg_format_json_value_with_cb(struct blob_attr *attr, 46 | blobmsg_json_format_t cb, void *priv, 47 | int indent); 48 | 49 | static inline char *blobmsg_format_json_value(struct blob_attr *attr) 50 | { 51 | return blobmsg_format_json_value_with_cb(attr, NULL, NULL, -1); 52 | } 53 | 54 | static inline char *blobmsg_format_json_value_indent(struct blob_attr *attr, int indent) 55 | { 56 | return blobmsg_format_json_value_with_cb(attr, NULL, NULL, indent); 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /tests/cram/test_list.t: -------------------------------------------------------------------------------- 1 | check that list is producing expected results: 2 | 3 | $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" 4 | $ valgrind --quiet --leak-check=full test-list 5 | init_list: list_empty: yes 6 | init_list: list_add_tail: zero one two three four five six seven eight nine ten eleven twelve 7 | init_list: list_empty: no 8 | test_basics: first=zero last=twelve 9 | test_basics: 'zero' is first, yes 10 | test_basics: 'twelve' is last, yes 11 | test_basics: removing 'twelve' and 'zero' 12 | test_basics: first=one last=eleven 13 | test_basics: 'one' is first, yes 14 | test_basics: 'eleven' is last, yes 15 | test_basics: moving 'one' to the tail 16 | test_basics: first=two last=one 17 | test_basics: 'two' is first, yes 18 | test_basics: 'one' is last, yes 19 | test_basics: list_for_each_entry: two three four five six seven eight nine ten eleven one 20 | test_basics: list_for_each_entry_reverse: one eleven ten nine eight seven six five four three two 21 | test_basics: delete all entries 22 | test_basics: list_empty: yes 23 | init_list: list_empty: yes 24 | init_list: list_add_tail: zero one two three four five six seven eight nine ten eleven twelve 25 | init_list: list_empty: no 26 | test_while_list_empty: delete all entries 27 | test_while_list_empty: list_empty: yes 28 | 29 | $ test-list-san 30 | init_list: list_empty: yes 31 | init_list: list_add_tail: zero one two three four five six seven eight nine ten eleven twelve 32 | init_list: list_empty: no 33 | test_basics: first=zero last=twelve 34 | test_basics: 'zero' is first, yes 35 | test_basics: 'twelve' is last, yes 36 | test_basics: removing 'twelve' and 'zero' 37 | test_basics: first=one last=eleven 38 | test_basics: 'one' is first, yes 39 | test_basics: 'eleven' is last, yes 40 | test_basics: moving 'one' to the tail 41 | test_basics: first=two last=one 42 | test_basics: 'two' is first, yes 43 | test_basics: 'one' is last, yes 44 | test_basics: list_for_each_entry: two three four five six seven eight nine ten eleven one 45 | test_basics: list_for_each_entry_reverse: one eleven ten nine eight seven six five four three two 46 | test_basics: delete all entries 47 | test_basics: list_empty: yes 48 | init_list: list_empty: yes 49 | init_list: list_add_tail: zero one two three four five six seven eight nine ten eleven twelve 50 | init_list: list_empty: no 51 | test_while_list_empty: delete all entries 52 | test_while_list_empty: list_empty: yes 53 | -------------------------------------------------------------------------------- /kvlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * kvlist - simple key/value store 3 | * 4 | * Copyright (C) 2014 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #ifndef __LIBUBOX_KVLIST_H 19 | #define __LIBUBOX_KVLIST_H 20 | 21 | #include "avl-cmp.h" 22 | #include "avl.h" 23 | 24 | struct kvlist { 25 | struct avl_tree avl; 26 | 27 | int (*get_len)(struct kvlist *kv, const void *data); 28 | }; 29 | 30 | struct kvlist_node { 31 | struct avl_node avl; 32 | 33 | char data[0] __attribute__((aligned(4))); 34 | }; 35 | 36 | #define KVLIST_INIT(_name, _get_len) \ 37 | { \ 38 | .avl = AVL_TREE_INIT(_name.avl, avl_strcmp, false, NULL), \ 39 | .get_len = _get_len \ 40 | } 41 | 42 | #define KVLIST(_name, _get_len) \ 43 | struct kvlist _name = KVLIST_INIT(_name, _get_len) 44 | 45 | #define __ptr_to_kv(_ptr) container_of(((char *) (_ptr)), struct kvlist_node, data[0]) 46 | #define __avl_list_to_kv(_l) container_of(_l, struct kvlist_node, avl.list) 47 | 48 | #define kvlist_for_each(kv, name, value) \ 49 | for (value = (void *) __avl_list_to_kv((kv)->avl.list_head.next)->data, \ 50 | name = (const char *) __ptr_to_kv(value)->avl.key, (void) name; \ 51 | &__ptr_to_kv(value)->avl.list != &(kv)->avl.list_head; \ 52 | value = (void *) (__avl_list_to_kv(__ptr_to_kv(value)->avl.list.next))->data, \ 53 | name = (const char *) __ptr_to_kv(value)->avl.key) 54 | 55 | void kvlist_init(struct kvlist *kv, int (*get_len)(struct kvlist *kv, const void *data)); 56 | void kvlist_free(struct kvlist *kv); 57 | void *kvlist_get(struct kvlist *kv, const char *name); 58 | bool kvlist_set(struct kvlist *kv, const char *name, const void *data); 59 | bool kvlist_delete(struct kvlist *kv, const char *name); 60 | 61 | int kvlist_strlen(struct kvlist *kv, const void *data); 62 | int kvlist_blob_len(struct kvlist *kv, const void *data); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /tests/cram/test_blobmsg_types.t: -------------------------------------------------------------------------------- 1 | check that blobmsg is producing expected results: 2 | 3 | $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" 4 | 5 | $ valgrind --quiet --leak-check=full test-blobmsg-types 6 | [*] blobmsg dump: 7 | string: Hello, world! 8 | int64_max: 9223372036854775807 9 | int64_min: -9223372036854775808 10 | int32_max: 2147483647 11 | int32_min: -2147483648 12 | int16_max: 32767 13 | int16_min: -32768 14 | int8_max: 127 15 | int8_min: -128 16 | double_max: 1.797693e+308 17 | double_min: 2.225074e-308 18 | [*] blobmsg dump cast_u64: 19 | string: Hello, world! 20 | int64_max: 9223372036854775807 21 | int64_min: 9223372036854775808 22 | int32_max: 2147483647 23 | int32_min: 2147483648 24 | int16_max: 32767 25 | int16_min: 32768 26 | int8_max: 127 27 | int8_min: 128 28 | double_max: 1.797693e+308 29 | double_min: 2.225074e-308 30 | [*] blobmsg dump cast_s64: 31 | string: Hello, world! 32 | int64_max: 9223372036854775807 33 | int64_min: -9223372036854775808 34 | int32_max: 2147483647 35 | int32_min: -2147483648 36 | int16_max: 32767 37 | int16_min: -32768 38 | int8_max: 127 39 | int8_min: -128 40 | double_max: 1.797693e+308 41 | double_min: 2.225074e-308 42 | 43 | [*] blobmsg to json: {"string":"Hello, world!","int64_max":9223372036854775807,"int64_min":-9223372036854775808,"int32_max":2147483647,"int32_min":-2147483648,"int16_max":32767,"int16_min":-32768,"int8_max":true,"int8_min":true,"double_max":179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000,"double_min":0.000000} 44 | 45 | [*] blobmsg from json: 46 | string: Hello, world! 47 | int64_max: 9223372036854775807 48 | int64_min: -9223372036854775808 49 | int32_max: 2147483647 50 | int32_min: -2147483648 51 | int16_max: 32767 52 | int16_min: -32768 53 | int8_max: 1 54 | int8_min: 1 55 | double_max: 1.797693e+308 56 | double_min: 0.000000e+00 57 | 58 | [*] blobmsg from json/cast_u64: 59 | string: Hello, world! 60 | int64_max: 9223372036854775807 61 | int64_min: 9223372036854775808 62 | int32_max: 2147483647 63 | int32_min: 2147483648 64 | int16_max: 32767 65 | int16_min: 4294934528 66 | int8_max: 1 67 | int8_min: 1 68 | double_max: 1.797693e+308 69 | double_min: 0.000000e+00 70 | 71 | [*] blobmsg from json/cast_s64: 72 | string: Hello, world! 73 | int64_max: 9223372036854775807 74 | int64_min: -9223372036854775808 75 | int32_max: 2147483647 76 | int32_min: -2147483648 77 | int16_max: 32767 78 | int16_min: -32768 79 | int8_max: 1 80 | int8_min: 1 81 | double_max: 1.797693e+308 82 | double_min: 0.000000e+00 83 | -------------------------------------------------------------------------------- /kvlist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * kvlist - simple key/value store 3 | * 4 | * Copyright (C) 2014 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #include 19 | #include 20 | 21 | #include "utils.h" 22 | #include "avl-cmp.h" 23 | #include "blob.h" 24 | 25 | #include "kvlist.h" 26 | 27 | int kvlist_strlen(struct kvlist *kv, const void *data) 28 | { 29 | return strlen(data) + 1; 30 | } 31 | 32 | int kvlist_blob_len(struct kvlist *kv, const void *data) 33 | { 34 | return blob_pad_len(data); 35 | } 36 | 37 | void kvlist_init(struct kvlist *kv, int (*get_len)(struct kvlist *kv, const void *data)) 38 | { 39 | avl_init(&kv->avl, avl_strcmp, false, NULL); 40 | kv->get_len = get_len; 41 | } 42 | 43 | static struct kvlist_node *__kvlist_get(struct kvlist *kv, const char *name) 44 | { 45 | struct kvlist_node *node; 46 | 47 | return avl_find_element(&kv->avl, name, node, avl); 48 | } 49 | 50 | void *kvlist_get(struct kvlist *kv, const char *name) 51 | { 52 | struct kvlist_node *node; 53 | 54 | node = __kvlist_get(kv, name); 55 | if (!node) 56 | return NULL; 57 | 58 | return node->data; 59 | } 60 | 61 | bool kvlist_delete(struct kvlist *kv, const char *name) 62 | { 63 | struct kvlist_node *node; 64 | 65 | node = __kvlist_get(kv, name); 66 | if (node) { 67 | avl_delete(&kv->avl, &node->avl); 68 | free(node); 69 | } 70 | 71 | return !!node; 72 | } 73 | 74 | bool kvlist_set(struct kvlist *kv, const char *name, const void *data) 75 | { 76 | struct kvlist_node *node; 77 | char *name_buf; 78 | int len = kv->get_len(kv, data); 79 | 80 | node = calloc_a(sizeof(struct kvlist_node) + len, 81 | &name_buf, strlen(name) + 1); 82 | if (!node) 83 | return false; 84 | 85 | kvlist_delete(kv, name); 86 | 87 | memcpy(node->data, data, len); 88 | 89 | node->avl.key = strcpy(name_buf, name); 90 | avl_insert(&kv->avl, &node->avl); 91 | 92 | return true; 93 | } 94 | 95 | void kvlist_free(struct kvlist *kv) 96 | { 97 | struct kvlist_node *node, *tmp; 98 | 99 | avl_remove_all_elements(&kv->avl, node, avl, tmp) 100 | free(node); 101 | } 102 | -------------------------------------------------------------------------------- /examples/uloop-example.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local socket = require "socket" 4 | 5 | local uloop = require("uloop") 6 | uloop.init() 7 | 8 | local udp = socket.udp() 9 | udp:settimeout(0) 10 | udp:setsockname('*', 8080) 11 | 12 | -- timer example 1 (will run repeatedly) 13 | local timer 14 | function t() 15 | print("1000 ms timer run"); 16 | timer:set(1000) 17 | end 18 | timer = uloop.timer(t) 19 | timer:set(1000) 20 | 21 | -- timer example 2 (will run once) 22 | uloop.timer(function() print("2000 ms timer run"); end, 2000) 23 | 24 | -- timer example 3 (will never run) 25 | uloop.timer(function() print("3000 ms timer run"); end, 3000):cancel() 26 | 27 | -- periodic interval timer 28 | local intv 29 | intv = uloop.interval(function() 30 | print(string.format("Interval expiration #%d - %dms until next expiration", 31 | intv:expirations(), intv:remaining())) 32 | 33 | -- after 5 expirations, lower interval to 500ms 34 | if intv:expirations() >= 5 then 35 | intv:set(500) 36 | end 37 | 38 | -- cancel after 10 expirations 39 | if intv:expirations() >= 10 then 40 | intv:cancel() 41 | end 42 | end, 1000) 43 | 44 | -- process 45 | function p1(r) 46 | print("Process 1 completed") 47 | print(r) 48 | end 49 | 50 | function p2(r) 51 | print("Process 2 completed") 52 | print(r) 53 | end 54 | 55 | uloop.timer( 56 | function() 57 | uloop.process("uloop_pid_test.sh", {"foo", "bar"}, {"PROCESS=1"}, p1) 58 | end, 1000 59 | ) 60 | uloop.timer( 61 | function() 62 | uloop.process("uloop_pid_test.sh", {"foo", "bar"}, {"PROCESS=2"}, p2) 63 | end, 2000 64 | ) 65 | 66 | -- SIGINT handler 67 | uloop.signal(function(signo) 68 | print(string.format("Terminating on SIGINT (#%d)!", signo)) 69 | 70 | -- end uloop to terminate program 71 | uloop.cancel() 72 | end, uloop.SIGINT) 73 | 74 | local sig 75 | sig = uloop.signal(function(signo) 76 | print(string.format("Got SIGUSR2 (#%d)!", signo)) 77 | 78 | -- remove signal handler, next SIGUSR2 will terminate program 79 | sig:delete() 80 | end, uloop.SIGUSR2) 81 | 82 | -- Keep udp_ev reference, events will be gc'd, even if the callback is still referenced 83 | -- .delete will manually untrack. 84 | udp_ev = uloop.fd_add(udp, function(ufd, events) 85 | local words, msg_or_ip, port_or_nil = ufd:receivefrom() 86 | print('Recv UDP packet from '..msg_or_ip..':'..port_or_nil..' : '..words) 87 | if words == "Stop!" then 88 | udp_ev:delete() 89 | end 90 | end, uloop.ULOOP_READ) 91 | 92 | udp_count = 0 93 | udp_send_timer = uloop.timer( 94 | function() 95 | local s = socket.udp() 96 | local words 97 | if udp_count > 3 then 98 | words = "Stop!" 99 | udp_send_timer:cancel() 100 | else 101 | words = 'Hello!' 102 | udp_send_timer:set(1000) 103 | end 104 | print('Send UDP packet to 127.0.0.1:8080 :'..words) 105 | s:sendto(words, '127.0.0.1', 8080) 106 | s:close() 107 | 108 | udp_count = udp_count + 1 109 | end, 3000 110 | ) 111 | 112 | uloop.run() 113 | 114 | -------------------------------------------------------------------------------- /tests/fuzz/test-fuzz.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "blob.h" 7 | #include "blobmsg.h" 8 | 9 | #define BLOBMSG_TYPE_TROUBLE INT_MAX 10 | 11 | static void fuzz_blobmsg_parse(const uint8_t *data, size_t size) 12 | { 13 | enum { 14 | FOO_MESSAGE, 15 | FOO_LIST, 16 | FOO_TESTDATA, 17 | __FOO_MAX 18 | }; 19 | 20 | static const int blobmsg_type[] = { 21 | BLOBMSG_TYPE_UNSPEC, 22 | BLOBMSG_TYPE_ARRAY, 23 | BLOBMSG_TYPE_TABLE, 24 | BLOBMSG_TYPE_STRING, 25 | BLOBMSG_TYPE_INT64, 26 | BLOBMSG_TYPE_INT32, 27 | BLOBMSG_TYPE_INT16, 28 | BLOBMSG_TYPE_INT8, 29 | BLOBMSG_TYPE_DOUBLE, 30 | BLOBMSG_TYPE_TROUBLE, 31 | }; 32 | 33 | static const struct blobmsg_policy foo_policy[] = { 34 | [FOO_MESSAGE] = { 35 | .name = "message", 36 | .type = BLOBMSG_TYPE_STRING, 37 | }, 38 | [FOO_LIST] = { 39 | .name = "list", 40 | .type = BLOBMSG_TYPE_ARRAY, 41 | }, 42 | [FOO_TESTDATA] = { 43 | .name = "testdata", 44 | .type = BLOBMSG_TYPE_TABLE, 45 | }, 46 | }; 47 | 48 | struct blob_attr *tb[__FOO_MAX]; 49 | 50 | blobmsg_parse(foo_policy, __FOO_MAX, tb, (uint8_t *)data, size); 51 | blobmsg_parse_array(foo_policy, __FOO_MAX, tb, (uint8_t *)data, size); 52 | 53 | blobmsg_check_attr_len((struct blob_attr *)data, false, size); 54 | blobmsg_check_attr_len((struct blob_attr *)data, true, size); 55 | 56 | for (size_t i=0; i < ARRAY_SIZE(blobmsg_type); i++) { 57 | blobmsg_check_array_len((struct blob_attr *)data, blobmsg_type[i], size); 58 | blobmsg_check_attr_list_len((struct blob_attr *)data, blobmsg_type[i], size); 59 | } 60 | } 61 | 62 | static void fuzz_blob_parse(const uint8_t *data, size_t size) 63 | { 64 | enum { 65 | FOO_ATTR_NESTED, 66 | FOO_ATTR_BINARY, 67 | FOO_ATTR_STRING, 68 | FOO_ATTR_INT8, 69 | FOO_ATTR_INT16, 70 | FOO_ATTR_INT32, 71 | FOO_ATTR_INT64, 72 | FOO_ATTR_DOUBLE, 73 | __FOO_ATTR_MAX 74 | }; 75 | 76 | 77 | static const struct blob_attr_info foo_policy[__FOO_ATTR_MAX] = { 78 | [FOO_ATTR_NESTED] = { .type = BLOB_ATTR_NESTED }, 79 | [FOO_ATTR_BINARY] = { .type = BLOB_ATTR_BINARY }, 80 | [FOO_ATTR_STRING] = { .type = BLOB_ATTR_STRING }, 81 | [FOO_ATTR_INT8] = { .type = BLOB_ATTR_INT8 }, 82 | [FOO_ATTR_INT16] = { .type = BLOB_ATTR_INT16 }, 83 | [FOO_ATTR_INT32] = { .type = BLOB_ATTR_INT32 }, 84 | [FOO_ATTR_INT64] = { .type = BLOB_ATTR_INT64 }, 85 | [FOO_ATTR_DOUBLE] = { .type = BLOB_ATTR_DOUBLE }, 86 | }; 87 | 88 | struct blob_attr *foo[__FOO_ATTR_MAX]; 89 | struct blob_attr *buf = (struct blob_attr *)data; 90 | 91 | blob_parse_untrusted(buf, size, foo, foo_policy, __FOO_ATTR_MAX); 92 | } 93 | 94 | int LLVMFuzzerTestOneInput(const uint8_t *input, size_t size) 95 | { 96 | uint8_t *data; 97 | 98 | data = malloc(size); 99 | if (!data) 100 | return -1; 101 | 102 | memcpy(data, input, size); 103 | fuzz_blob_parse(data, size); 104 | fuzz_blobmsg_parse(data, size); 105 | free(data); 106 | 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /tests/cram/test_json_script.t: -------------------------------------------------------------------------------- 1 | set test bin path: 2 | 3 | $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" 4 | $ export TEST_INPUTS="$TESTDIR/inputs" 5 | $ alias js="valgrind --quiet --leak-check=full test-json-script" 6 | $ alias js-san="test-json-script-san" 7 | 8 | check that json-script is producing expected results: 9 | 10 | $ js 11 | Usage: test-json-script [VARNAME=value] 12 | [254] 13 | 14 | $ js-san 15 | Usage: test-json-script-san [VARNAME=value] 16 | [254] 17 | 18 | $ echo '}' > test.json; js test.json 19 | load JSON data from test.json failed. 20 | 21 | $ echo '}' > test.json; js-san test.json 22 | load JSON data from test.json failed. 23 | 24 | $ js nada.json 2>&1 | grep load.*failed 25 | load JSON data from nada.json failed. 26 | 27 | $ js-san nada.json 2>&1 | grep load.*failed 28 | load JSON data from nada.json failed. 29 | 30 | $ echo '[ [ ] [ ] ]' > test.json; js test.json 31 | load JSON data from test.json failed. 32 | 33 | $ echo '[ [ ] [ ] ]' > test.json; js-san test.json 34 | load JSON data from test.json failed. 35 | 36 | check example json-script: 37 | 38 | $ js $TEST_INPUTS/json-script.json 39 | exec /%/ 40 | exec_if_or 41 | 42 | $ js-san $TEST_INPUTS/json-script.json 43 | exec /%/ 44 | exec_if_or 45 | 46 | $ js EXECVAR=meh ORVAR=meep $TEST_INPUTS/json-script.json 47 | exec meh /%/ 48 | exec_if_or meep 49 | 50 | $ js-san EXECVAR=meh ORVAR=meep $TEST_INPUTS/json-script.json 51 | exec meh /%/ 52 | exec_if_or meep 53 | 54 | check has expression: 55 | 56 | $ echo ' 57 | > [ 58 | > [ "if", 59 | > [ "has", "VAR" ], 60 | > [ "echo", "bar" ], 61 | > [ "echo", "baz" ] 62 | > ] 63 | > ]' > test.json 64 | 65 | $ js VAR=foo test.json 66 | echo bar 67 | 68 | $ js-san VAR=foo test.json 69 | echo bar 70 | 71 | $ js VAR=bar test.json 72 | echo bar 73 | 74 | $ js-san VAR=bar test.json 75 | echo bar 76 | 77 | $ js test.json 78 | echo baz 79 | 80 | $ js-san test.json 81 | echo baz 82 | 83 | check eq expression: 84 | 85 | $ echo ' 86 | > [ 87 | > [ "if", 88 | > [ "eq", "VAR", "bar" ], 89 | > [ "echo", "foo" ], 90 | > [ "echo", "baz" ] 91 | > ] 92 | > ]' > test.json 93 | 94 | $ js VAR=bar test.json 95 | echo foo 96 | 97 | $ js-san VAR=bar test.json 98 | echo foo 99 | 100 | $ js VAR=xxx test.json 101 | echo baz 102 | 103 | $ js-san VAR=xxx test.json 104 | echo baz 105 | 106 | $ js test.json 107 | echo baz 108 | 109 | $ js-san test.json 110 | echo baz 111 | 112 | check regex single expression: 113 | 114 | $ echo ' 115 | > [ 116 | > [ "if", 117 | > [ "regex", "VAR", ".ell." ], 118 | > [ "echo", "bar" ], 119 | > [ "echo", "baz" ] 120 | > ] 121 | > ]' > test.json 122 | 123 | $ js VAR=hello test.json 124 | echo bar 125 | 126 | $ js-san VAR=hello test.json 127 | echo bar 128 | 129 | $ js VAR=.ell. test.json 130 | echo bar 131 | 132 | $ js-san VAR=.ell. test.json 133 | echo bar 134 | 135 | $ js test.json 136 | echo baz 137 | 138 | $ js-san test.json 139 | echo baz 140 | 141 | $ js VAR= test.json 142 | echo baz 143 | 144 | $ js-san VAR= test.json 145 | echo baz 146 | 147 | $ js VAR=hell test.json 148 | echo baz 149 | 150 | $ js-san VAR=hell test.json 151 | echo baz 152 | -------------------------------------------------------------------------------- /safe_list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * safe_list - linked list protected against recursive iteration with deletes 3 | * 4 | * Copyright (C) 2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "safe_list.h" 20 | 21 | struct safe_list_iterator { 22 | struct safe_list_iterator **head; 23 | struct safe_list_iterator *next_i; 24 | struct safe_list *next; 25 | }; 26 | 27 | static void 28 | __safe_list_set_iterator(struct safe_list *list, 29 | struct safe_list_iterator *i) 30 | { 31 | struct safe_list_iterator *next_i; 32 | struct safe_list *next; 33 | 34 | next = list_entry(list->list.next, struct safe_list, list); 35 | next_i = next->i; 36 | 37 | next->i = i; 38 | i->next = next; 39 | i->head = &next->i; 40 | 41 | i->next_i = next_i; 42 | if (next_i) 43 | next_i->head = &i->next_i; 44 | } 45 | 46 | static void 47 | __safe_list_del_iterator(struct safe_list_iterator *i) 48 | { 49 | *i->head = i->next_i; 50 | if (i->next_i) 51 | i->next_i->head = i->head; 52 | } 53 | 54 | static void 55 | __safe_list_move_iterator(struct safe_list *list, 56 | struct safe_list_iterator *i) 57 | { 58 | __safe_list_del_iterator(i); 59 | __safe_list_set_iterator(list, i); 60 | } 61 | 62 | int safe_list_for_each(struct safe_list *head, 63 | int (*cb)(void *ctx, struct safe_list *list), 64 | void *ctx) 65 | { 66 | struct safe_list_iterator i; 67 | struct safe_list *cur; 68 | int ret = 0; 69 | 70 | for (cur = list_entry(head->list.next, struct safe_list, list), 71 | __safe_list_set_iterator(cur, &i); 72 | cur != head; 73 | cur = i.next, __safe_list_move_iterator(cur, &i)) { 74 | ret = cb(ctx, cur); 75 | if (ret) 76 | break; 77 | } 78 | 79 | __safe_list_del_iterator(&i); 80 | return ret; 81 | } 82 | 83 | void safe_list_add(struct safe_list *list, struct safe_list *head) 84 | { 85 | list->i = NULL; 86 | list_add_tail(&list->list, &head->list); 87 | } 88 | 89 | void safe_list_add_first(struct safe_list *list, struct safe_list *head) 90 | { 91 | list->i = NULL; 92 | list_add(&list->list, &head->list); 93 | } 94 | 95 | void safe_list_del(struct safe_list *list) 96 | { 97 | struct safe_list_iterator *i, *next_i, **tail; 98 | struct safe_list *next; 99 | 100 | next = list_entry(list->list.next, struct safe_list, list); 101 | list_del(&list->list); 102 | 103 | if (!list->i) 104 | return; 105 | 106 | next_i = next->i; 107 | tail = &next->i; 108 | 109 | for (i = list->i; i; i = i->next_i) { 110 | tail = &i->next_i; 111 | i->next = next; 112 | } 113 | 114 | next->i = list->i; 115 | list->i->head = &next->i; 116 | *tail = next_i; 117 | if (next_i) 118 | next_i->head = tail; 119 | 120 | list->i = NULL; 121 | } 122 | -------------------------------------------------------------------------------- /vlist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef __LIBUBOX_VLIST_H 18 | #define __LIBUBOX_VLIST_H 19 | 20 | #include "avl.h" 21 | 22 | struct vlist_tree; 23 | struct vlist_node; 24 | 25 | typedef void (*vlist_update_cb)(struct vlist_tree *tree, 26 | struct vlist_node *node_new, 27 | struct vlist_node *node_old); 28 | 29 | struct vlist_tree { 30 | struct avl_tree avl; 31 | 32 | vlist_update_cb update; 33 | bool keep_old; 34 | bool no_delete; 35 | 36 | int version; 37 | }; 38 | 39 | struct vlist_node { 40 | struct avl_node avl; 41 | int version; 42 | }; 43 | 44 | #define VLIST_TREE_INIT(_name, _comp, _update, _keep_old, _no_delete) \ 45 | { \ 46 | .avl = AVL_TREE_INIT(_name.avl, _comp, false, NULL), \ 47 | .update = _update, \ 48 | .version = 1, \ 49 | .keep_old = _keep_old, \ 50 | .no_delete = _no_delete, \ 51 | } 52 | 53 | #define VLIST_TREE(_name, ...) \ 54 | struct vlist_tree _name = \ 55 | VLIST_TREE_INIT(_name, __VA_ARGS__) 56 | 57 | void vlist_init(struct vlist_tree *tree, avl_tree_comp cmp, vlist_update_cb update); 58 | 59 | #define vlist_find(tree, name, element, node_member) \ 60 | avl_find_element(&(tree)->avl, name, element, node_member.avl) 61 | 62 | static inline void vlist_update(struct vlist_tree *tree) 63 | { 64 | tree->version++; 65 | } 66 | 67 | void vlist_add(struct vlist_tree *tree, struct vlist_node *node, const void *key); 68 | void vlist_delete(struct vlist_tree *tree, struct vlist_node *node); 69 | void vlist_flush(struct vlist_tree *tree); 70 | void vlist_flush_all(struct vlist_tree *tree); 71 | 72 | #define vlist_for_each_element(tree, element, node_member) \ 73 | avl_for_each_element(&(tree)->avl, element, node_member.avl) 74 | 75 | #define vlist_for_each_element_safe(tree, element, node_member, ptr) \ 76 | avl_for_each_element_safe(&(tree)->avl, element, node_member.avl, ptr) 77 | 78 | #define vlist_for_each_element_reverse(tree, element, node_member) \ 79 | avl_for_each_element_reverse(&(tree)->avl, element, node_member.avl) 80 | 81 | #define vlist_for_first_to_element(tree, last, element, node_member) \ 82 | avl_for_element_range(avl_first_element(&(tree)->avl, element, node_member.avl), last, element, node_member.avl) 83 | 84 | #define vlist_for_first_to_element_reverse(tree, last, element, node_member) \ 85 | avl_for_element_range_reverse(avl_first_element(&(tree)->avl, element, node_member.avl), last, element, node_member.avl) 86 | 87 | #define vlist_for_element_to_last(tree, first, element, node_member) \ 88 | avl_for_element_range(first, avl_last_element(&(tree)->avl, element, node_member.avl), element, node_member.avl) 89 | 90 | #define vlist_for_element_to_last_reverse(tree, first, element, node_member) \ 91 | avl_for_element_range_reverse(first, avl_last_element(&(tree)->avl, element, node_member.avl), element, node_member.avl) 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /tests/test-list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "list.h" 6 | #include "utils.h" 7 | 8 | struct item { 9 | const char *name; 10 | struct list_head list; 11 | }; 12 | 13 | #define OUT(fmt, ...) do { \ 14 | fprintf(stdout, "%s: " fmt, __func__, ## __VA_ARGS__); \ 15 | } while (0); 16 | 17 | static void init_list(struct list_head *list) 18 | { 19 | const char *vals[] = { 20 | "zero", "one", "two", "three", "four", "five", "six", 21 | "seven", "eight", "nine", "ten", "eleven", "twelve" 22 | }; 23 | 24 | OUT("list_empty: %s\n", list_empty(list) ? "yes" : "no"); 25 | OUT("list_add_tail: "); 26 | for (size_t i=0; iname = vals[i]; 29 | list_add_tail(&e->list, list); 30 | fprintf(stdout, "%s ", vals[i]); 31 | } 32 | fprintf(stdout, "\n"); 33 | OUT("list_empty: %s\n", list_empty(list) ? "yes" : "no"); 34 | } 35 | 36 | static void test_basics() 37 | { 38 | struct item *tmp; 39 | struct item *item; 40 | struct item *last; 41 | struct item *first; 42 | struct list_head test_list = LIST_HEAD_INIT(test_list); 43 | 44 | init_list(&test_list); 45 | 46 | first = list_first_entry(&test_list, struct item, list); 47 | last = list_last_entry(&test_list, struct item, list); 48 | OUT("first=%s last=%s\n", first->name, last->name); 49 | OUT("'zero' is first, %s\n", list_is_first(&first->list, &test_list) ? "yes" : "no"); 50 | OUT("'twelve' is last, %s\n", list_is_last(&last->list, &test_list) ? "yes" : "no"); 51 | 52 | OUT("removing 'twelve' and 'zero'\n"); 53 | list_del(&first->list); 54 | list_del(&last->list); 55 | free(first); 56 | free(last); 57 | 58 | first = list_first_entry(&test_list, struct item, list); 59 | last = list_last_entry(&test_list, struct item, list); 60 | 61 | if (!first || !last) 62 | return; 63 | 64 | OUT("first=%s last=%s\n", first->name, last->name); 65 | OUT("'one' is first, %s\n", list_is_first(&first->list, &test_list) ? "yes" : "no"); 66 | OUT("'eleven' is last, %s\n", list_is_last(&last->list, &test_list) ? "yes" : "no"); 67 | 68 | OUT("moving 'one' to the tail\n"); 69 | list_move_tail(&first->list, &test_list); 70 | first = list_first_entry(&test_list, struct item, list); 71 | last = list_last_entry(&test_list, struct item, list); 72 | OUT("first=%s last=%s\n", first->name, last->name); 73 | OUT("'two' is first, %s\n", list_is_first(&first->list, &test_list) ? "yes" : "no"); 74 | OUT("'one' is last, %s\n", list_is_last(&last->list, &test_list) ? "yes" : "no"); 75 | 76 | OUT("list_for_each_entry: "); 77 | list_for_each_entry(item, &test_list, list) { 78 | fprintf(stdout, "%s ", item->name); 79 | } 80 | fprintf(stdout, "\n"); 81 | 82 | OUT("list_for_each_entry_reverse: "); 83 | list_for_each_entry_reverse(item, &test_list, list) { 84 | fprintf(stdout, "%s ", item->name); 85 | } 86 | fprintf(stdout, "\n"); 87 | 88 | OUT("delete all entries\n"); 89 | list_for_each_entry_safe(item, tmp, &test_list, list) { 90 | list_del(&item->list); 91 | free(item); 92 | } 93 | OUT("list_empty: %s\n", list_empty(&test_list) ? "yes" : "no"); 94 | } 95 | 96 | static void test_while_list_empty() 97 | { 98 | struct item *first; 99 | struct list_head test_list = LIST_HEAD_INIT(test_list); 100 | 101 | init_list(&test_list); 102 | 103 | OUT("delete all entries\n"); 104 | while (!list_empty(&test_list)) { 105 | first = list_first_entry(&test_list, struct item, list); 106 | list_del(&first->list); 107 | free(first); 108 | } 109 | OUT("list_empty: %s\n", list_empty(&test_list) ? "yes" : "no"); 110 | } 111 | 112 | int main() 113 | { 114 | test_basics(); 115 | test_while_list_empty(); 116 | return 0; 117 | } 118 | -------------------------------------------------------------------------------- /examples/ustream-example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "ustream.h" 11 | #include "uloop.h" 12 | #include "usock.h" 13 | 14 | static struct uloop_fd server; 15 | static const char *port = "10000"; 16 | struct client *next_client = NULL; 17 | 18 | struct client { 19 | struct sockaddr_in sin; 20 | 21 | struct ustream_fd s; 22 | int ctr; 23 | }; 24 | 25 | static void client_read_cb(struct ustream *s, int bytes) 26 | { 27 | struct client *cl = container_of(s, struct client, s.stream); 28 | struct ustream_buf *buf = s->r.head; 29 | char *newline, *str; 30 | 31 | do { 32 | str = ustream_get_read_buf(s, NULL); 33 | if (!str) 34 | break; 35 | 36 | newline = strchr(buf->data, '\n'); 37 | if (!newline) 38 | break; 39 | 40 | *newline = 0; 41 | ustream_printf(s, "%s\n", str); 42 | ustream_consume(s, newline + 1 - str); 43 | cl->ctr += newline + 1 - str; 44 | } while(1); 45 | 46 | if (s->w.data_bytes > 256 && !ustream_read_blocked(s)) { 47 | fprintf(stderr, "Block read, bytes: %d\n", s->w.data_bytes); 48 | ustream_set_read_blocked(s, true); 49 | } 50 | } 51 | 52 | static void client_close(struct ustream *s) 53 | { 54 | struct client *cl = container_of(s, struct client, s.stream); 55 | 56 | fprintf(stderr, "Connection closed\n"); 57 | ustream_free(s); 58 | close(cl->s.fd.fd); 59 | free(cl); 60 | } 61 | 62 | static void client_notify_write(struct ustream *s, int bytes) 63 | { 64 | fprintf(stderr, "Wrote %d bytes, pending: %d\n", bytes, s->w.data_bytes); 65 | 66 | if (s->w.data_bytes < 128 && ustream_read_blocked(s)) { 67 | fprintf(stderr, "Unblock read\n"); 68 | ustream_set_read_blocked(s, false); 69 | } 70 | } 71 | 72 | static void client_notify_state(struct ustream *s) 73 | { 74 | struct client *cl = container_of(s, struct client, s.stream); 75 | 76 | if (!s->eof) 77 | return; 78 | 79 | fprintf(stderr, "eof!, pending: %d, total: %d\n", s->w.data_bytes, cl->ctr); 80 | if (!s->w.data_bytes) 81 | return client_close(s); 82 | 83 | } 84 | 85 | static void server_cb(struct uloop_fd *fd, unsigned int events) 86 | { 87 | struct client *cl; 88 | unsigned int sl = sizeof(struct sockaddr_in); 89 | int sfd; 90 | 91 | if (!next_client) 92 | next_client = calloc(1, sizeof(*next_client)); 93 | 94 | cl = next_client; 95 | sfd = accept(server.fd, (struct sockaddr *) &cl->sin, &sl); 96 | if (sfd < 0) { 97 | fprintf(stderr, "Accept failed\n"); 98 | return; 99 | } 100 | 101 | cl->s.stream.string_data = true; 102 | cl->s.stream.notify_read = client_read_cb; 103 | cl->s.stream.notify_state = client_notify_state; 104 | cl->s.stream.notify_write = client_notify_write; 105 | ustream_fd_init(&cl->s, sfd); 106 | next_client = NULL; 107 | fprintf(stderr, "New connection\n"); 108 | } 109 | 110 | static int run_server(void) 111 | { 112 | 113 | server.cb = server_cb; 114 | server.fd = usock(USOCK_TCP | USOCK_SERVER | USOCK_IPV4ONLY | USOCK_NUMERIC, "127.0.0.1", port); 115 | if (server.fd < 0) { 116 | perror("usock"); 117 | return 1; 118 | } 119 | 120 | uloop_init(); 121 | uloop_fd_add(&server, ULOOP_READ); 122 | uloop_run(); 123 | 124 | return 0; 125 | } 126 | 127 | static int usage(const char *name) 128 | { 129 | fprintf(stderr, "Usage: %s -p \n", name); 130 | return 1; 131 | } 132 | 133 | int main(int argc, char **argv) 134 | { 135 | int ch; 136 | 137 | while ((ch = getopt(argc, argv, "p:")) != -1) { 138 | switch(ch) { 139 | case 'p': 140 | port = optarg; 141 | break; 142 | default: 143 | return usage(argv[0]); 144 | } 145 | } 146 | 147 | return run_server(); 148 | } 149 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | INCLUDE(CheckLibraryExists) 3 | INCLUDE(CheckFunctionExists) 4 | 5 | PROJECT(ubox C) 6 | 7 | ADD_DEFINITIONS(-Wall -Werror) 8 | IF(CMAKE_C_COMPILER_VERSION VERSION_GREATER 6) 9 | ADD_DEFINITIONS(-Wextra -Werror=implicit-function-declaration) 10 | ADD_DEFINITIONS(-Wformat -Werror=format-security -Werror=format-nonliteral) 11 | ENDIF() 12 | ADD_DEFINITIONS(-Os -std=gnu99 -g3 -Wmissing-declarations -Wno-unused-parameter) 13 | 14 | OPTION(BUILD_LUA "build Lua plugin" ON) 15 | OPTION(BUILD_EXAMPLES "build examples" ON) 16 | 17 | INCLUDE(FindPkgConfig) 18 | PKG_SEARCH_MODULE(JSONC json-c REQUIRED) 19 | INCLUDE_DIRECTORIES(${JSONC_INCLUDE_DIRS}) 20 | 21 | SET(SOURCES avl.c avl-cmp.c blob.c blobmsg.c uloop.c usock.c ustream.c ustream-fd.c vlist.c utils.c safe_list.c runqueue.c md5.c kvlist.c ulog.c base64.c udebug.c udebug-remote.c) 22 | 23 | ADD_LIBRARY(ubox SHARED ${SOURCES}) 24 | ADD_LIBRARY(ubox-static STATIC ${SOURCES}) 25 | SET_TARGET_PROPERTIES(ubox-static PROPERTIES OUTPUT_NAME ubox) 26 | 27 | SET(LIBS) 28 | CHECK_FUNCTION_EXISTS(clock_gettime HAVE_GETTIME) 29 | CHECK_FUNCTION_EXISTS(shm_open HAVE_SHM) 30 | IF(NOT HAVE_GETTIME OR NOT HAVE_SHM) 31 | CHECK_LIBRARY_EXISTS(rt clock_gettime "" NEED_GETTIME) 32 | CHECK_LIBRARY_EXISTS(rt shm_open "" NEED_SHM) 33 | IF(NEED_GETTIME OR NEED_SHM) 34 | TARGET_LINK_LIBRARIES(ubox rt) 35 | ENDIF() 36 | ENDIF() 37 | 38 | FILE(GLOB headers *.h) 39 | LIST(FILTER headers EXCLUDE REGEX "-priv.h$" ) 40 | INSTALL(FILES ${headers} 41 | DESTINATION include/libubox 42 | ) 43 | INSTALL(TARGETS ubox ubox-static 44 | ARCHIVE DESTINATION lib 45 | LIBRARY DESTINATION lib 46 | ) 47 | 48 | ADD_SUBDIRECTORY(lua) 49 | ADD_SUBDIRECTORY(examples) 50 | 51 | MACRO(ADD_UNIT_TEST_SAN name) 52 | ADD_EXECUTABLE(${name}-san ${name}.c) 53 | TARGET_COMPILE_OPTIONS(${name}-san PRIVATE -g -fno-omit-frame-pointer -fsanitize=undefined,address,leak -fno-sanitize-recover=all) 54 | TARGET_LINK_OPTIONS(${name}-san PRIVATE -fsanitize=undefined,address,leak) 55 | TARGET_LINK_LIBRARIES(${name}-san ubox blobmsg_json json_script ${json}) 56 | TARGET_INCLUDE_DIRECTORIES(${name}-san PRIVATE ${PROJECT_SOURCE_DIR}) 57 | ENDMACRO(ADD_UNIT_TEST_SAN) 58 | 59 | IF(UNIT_TESTING) 60 | ENABLE_TESTING() 61 | ADD_SUBDIRECTORY(tests) 62 | ENDIF() 63 | 64 | find_library(json NAMES json-c) 65 | IF(EXISTS ${json}) 66 | ADD_LIBRARY(blobmsg_json SHARED blobmsg_json.c) 67 | TARGET_LINK_LIBRARIES(blobmsg_json ubox ${json}) 68 | 69 | ADD_LIBRARY(blobmsg_json-static STATIC blobmsg_json.c) 70 | SET_TARGET_PROPERTIES(blobmsg_json-static 71 | PROPERTIES OUTPUT_NAME blobmsg_json) 72 | 73 | IF(UNIT_TESTING) 74 | ADD_UNIT_TEST_SAN(jshn) 75 | ENDIF(UNIT_TESTING) 76 | 77 | ADD_EXECUTABLE(jshn jshn.c) 78 | TARGET_LINK_LIBRARIES(jshn blobmsg_json ${json}) 79 | 80 | ADD_LIBRARY(json_script SHARED json_script.c) 81 | TARGET_LINK_LIBRARIES(json_script ubox) 82 | 83 | INSTALL(TARGETS blobmsg_json blobmsg_json-static jshn json_script 84 | ARCHIVE DESTINATION lib 85 | LIBRARY DESTINATION lib 86 | RUNTIME DESTINATION bin 87 | ) 88 | 89 | FILE(GLOB scripts sh/*.sh) 90 | INSTALL(FILES ${scripts} 91 | DESTINATION share/libubox 92 | ) 93 | 94 | ENDIF() 95 | 96 | IF(ABIVERSION) 97 | SET_TARGET_PROPERTIES(ubox PROPERTIES VERSION ${ABIVERSION}) 98 | SET_TARGET_PROPERTIES(json_script PROPERTIES VERSION ${ABIVERSION}) 99 | SET_TARGET_PROPERTIES(blobmsg_json PROPERTIES VERSION ${ABIVERSION}) 100 | ENDIF() 101 | 102 | ADD_CUSTOM_TARGET(debian 103 | COMMAND ${CMAKE_COMMAND} -E echo "Generating debian/changelog from git..." 104 | COMMAND ${CMAKE_SOURCE_DIR}/debian/generate-changelog.sh 105 | COMMAND dpkg-buildpackage -b -uc -us 106 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 107 | COMMENT "Building Debian package" 108 | ) 109 | -------------------------------------------------------------------------------- /tests/test-blobmsg-procd-instance.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "blobmsg.h" 7 | #include "utils.h" 8 | 9 | enum { 10 | INSTANCE_ATTR_COMMAND, 11 | INSTANCE_ATTR_ENV, 12 | INSTANCE_ATTR_DATA, 13 | INSTANCE_ATTR_NETDEV, 14 | INSTANCE_ATTR_FILE, 15 | INSTANCE_ATTR_TRIGGER, 16 | INSTANCE_ATTR_RESPAWN, 17 | INSTANCE_ATTR_NICE, 18 | INSTANCE_ATTR_LIMITS, 19 | INSTANCE_ATTR_WATCH, 20 | INSTANCE_ATTR_ERROR, 21 | INSTANCE_ATTR_USER, 22 | INSTANCE_ATTR_GROUP, 23 | INSTANCE_ATTR_STDOUT, 24 | INSTANCE_ATTR_STDERR, 25 | INSTANCE_ATTR_NO_NEW_PRIVS, 26 | INSTANCE_ATTR_JAIL, 27 | INSTANCE_ATTR_TRACE, 28 | INSTANCE_ATTR_SECCOMP, 29 | INSTANCE_ATTR_PIDFILE, 30 | INSTANCE_ATTR_RELOADSIG, 31 | INSTANCE_ATTR_TERMTIMEOUT, 32 | INSTANCE_ATTR_FACILITY, 33 | __INSTANCE_ATTR_MAX 34 | }; 35 | 36 | static const struct blobmsg_policy instance_attr[__INSTANCE_ATTR_MAX] = { 37 | [INSTANCE_ATTR_COMMAND] = { "command", BLOBMSG_TYPE_ARRAY }, 38 | [INSTANCE_ATTR_ENV] = { "env", BLOBMSG_TYPE_TABLE }, 39 | [INSTANCE_ATTR_DATA] = { "data", BLOBMSG_TYPE_TABLE }, 40 | [INSTANCE_ATTR_NETDEV] = { "netdev", BLOBMSG_TYPE_ARRAY }, 41 | [INSTANCE_ATTR_FILE] = { "file", BLOBMSG_TYPE_ARRAY }, 42 | [INSTANCE_ATTR_TRIGGER] = { "triggers", BLOBMSG_TYPE_ARRAY }, 43 | [INSTANCE_ATTR_RESPAWN] = { "respawn", BLOBMSG_TYPE_ARRAY }, 44 | [INSTANCE_ATTR_NICE] = { "nice", BLOBMSG_TYPE_INT32 }, 45 | [INSTANCE_ATTR_LIMITS] = { "limits", BLOBMSG_TYPE_TABLE }, 46 | [INSTANCE_ATTR_WATCH] = { "watch", BLOBMSG_TYPE_ARRAY }, 47 | [INSTANCE_ATTR_ERROR] = { "error", BLOBMSG_TYPE_ARRAY }, 48 | [INSTANCE_ATTR_USER] = { "user", BLOBMSG_TYPE_STRING }, 49 | [INSTANCE_ATTR_GROUP] = { "group", BLOBMSG_TYPE_STRING }, 50 | [INSTANCE_ATTR_STDOUT] = { "stdout", BLOBMSG_TYPE_BOOL }, 51 | [INSTANCE_ATTR_STDERR] = { "stderr", BLOBMSG_TYPE_BOOL }, 52 | [INSTANCE_ATTR_NO_NEW_PRIVS] = { "no_new_privs", BLOBMSG_TYPE_BOOL }, 53 | [INSTANCE_ATTR_JAIL] = { "jail", BLOBMSG_TYPE_TABLE }, 54 | [INSTANCE_ATTR_TRACE] = { "trace", BLOBMSG_TYPE_BOOL }, 55 | [INSTANCE_ATTR_SECCOMP] = { "seccomp", BLOBMSG_TYPE_STRING }, 56 | [INSTANCE_ATTR_PIDFILE] = { "pidfile", BLOBMSG_TYPE_STRING }, 57 | [INSTANCE_ATTR_RELOADSIG] = { "reload_signal", BLOBMSG_TYPE_INT32 }, 58 | [INSTANCE_ATTR_TERMTIMEOUT] = { "term_timeout", BLOBMSG_TYPE_INT32 }, 59 | [INSTANCE_ATTR_FACILITY] = { "facility", BLOBMSG_TYPE_STRING }, 60 | }; 61 | 62 | static void test_blobmsg_procd_instance(const char *filename) 63 | { 64 | #define BUF_LEN 2048 65 | int r = 0; 66 | size_t len = 0; 67 | FILE *fd = NULL; 68 | char *buf = NULL; 69 | struct blob_attr *tb[__INSTANCE_ATTR_MAX]; 70 | const char *fname = basename((char *) filename); 71 | 72 | fd = fopen(filename, "r"); 73 | if (!fd) { 74 | fprintf(stderr, "unable to open %s\n", fname); 75 | return; 76 | } 77 | 78 | buf = malloc(BUF_LEN+1); 79 | if (!buf) 80 | return; 81 | 82 | len = fread(buf, 1, BUF_LEN, fd); 83 | fclose(fd); 84 | 85 | r = blobmsg_parse(instance_attr, __INSTANCE_ATTR_MAX, tb, buf, len); 86 | if (r) 87 | goto out; 88 | 89 | if (!tb[INSTANCE_ATTR_COMMAND] || !tb[INSTANCE_ATTR_NICE] || !tb[INSTANCE_ATTR_STDERR]) 90 | goto out; 91 | 92 | if (!blobmsg_check_attr_list(tb[INSTANCE_ATTR_COMMAND], BLOBMSG_TYPE_STRING)) 93 | goto out; 94 | 95 | if (blobmsg_get_u32(tb[INSTANCE_ATTR_NICE]) != 19) 96 | goto out; 97 | 98 | if (!blobmsg_get_bool(tb[INSTANCE_ATTR_STDERR])) 99 | goto out; 100 | 101 | fprintf(stderr, "%s: OK\n", fname); 102 | out: 103 | free(buf); 104 | } 105 | 106 | int main(int argc, char *argv[]) 107 | { 108 | if (argc != 2) { 109 | fprintf(stderr, "Usage: %s \n", argv[0]); 110 | return 3; 111 | } 112 | 113 | test_blobmsg_procd_instance(argv[1]); 114 | 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /tests/test-blobmsg_check_array.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "blobmsg.h" 4 | 5 | /* 6 | * This test tests a blob of this form... 7 | * 8 | { 9 | "array_a" : [ 10 | { 11 | "array_b": [ 12 | "1" 13 | ] 14 | } 15 | ] 16 | } 17 | * 18 | */ 19 | 20 | 21 | enum { 22 | ARRAY_A = 0, 23 | ARRAY_B = 0, 24 | }; 25 | 26 | static char const array_a[] = "array_a"; 27 | static char const array_b[] = "array_b"; 28 | 29 | static const struct blobmsg_policy pol_a[] = { 30 | [ARRAY_A] = { 31 | .name = array_a, 32 | .type = BLOBMSG_TYPE_ARRAY 33 | } 34 | }; 35 | 36 | static const struct blobmsg_policy pol_b[] = { 37 | [ARRAY_B] = { 38 | .name = array_b, 39 | .type = BLOBMSG_TYPE_ARRAY 40 | } 41 | }; 42 | 43 | #ifndef ARRAY_SIZE 44 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 45 | #endif 46 | 47 | static int 48 | check_table_a_entries(struct blob_attr *attr) 49 | { 50 | struct blob_attr *cur; 51 | size_t rem; 52 | int entry_number = 0; 53 | 54 | blobmsg_for_each_attr(cur, attr, rem) { 55 | int failed = 0; 56 | 57 | fprintf(stderr, "Process %s: entry %d\n", array_a, entry_number); 58 | 59 | struct blob_attr *tb[ARRAY_SIZE(pol_b)]; 60 | 61 | if (blobmsg_parse(pol_b, ARRAY_SIZE(pol_b), tb, 62 | blobmsg_data(cur), blobmsg_data_len(cur)) != 0) { 63 | fprintf(stderr, "Policy %s parse failed\n", array_b); 64 | return -1; 65 | } 66 | 67 | if (tb[ARRAY_B] == NULL) { 68 | fprintf(stderr, "%s not found\n", array_b); 69 | return -1; 70 | } 71 | 72 | /* 73 | * This is the test that fails when blobmsg_check_array() passes the 74 | * length obtained by blob_len(attr). 75 | * It succeeds when blobmsg_check_array() uses blob_len(attr), which is 76 | * equivalent to the origianl code, pre the length check changes. 77 | */ 78 | if (blobmsg_check_array(tb[ARRAY_B], BLOBMSG_TYPE_STRING) < 0) { 79 | fprintf(stderr, "Failed blobmsg_check_array() (STRING) on %s\n", 80 | array_b); 81 | failed = 1; 82 | } 83 | 84 | /* 85 | * Continue outputting the strings even though the test above might 86 | * have failed. 87 | * This will show that the array does actually contain the expected 88 | * string. 89 | */ 90 | 91 | struct blob_attr *cur2; 92 | size_t rem2; 93 | 94 | blobmsg_for_each_attr(cur2, tb[ARRAY_B], rem2) { 95 | fprintf(stderr, "%s contains string: %s\n", 96 | array_b, blobmsg_get_string(cur2)); 97 | } 98 | 99 | 100 | entry_number++; 101 | 102 | if (failed) 103 | return -1; 104 | } 105 | 106 | return 0; 107 | } 108 | 109 | static int 110 | check_message(struct blob_buf *buf) 111 | { 112 | struct blob_attr *tb[ARRAY_SIZE(pol_a)]; 113 | 114 | if (blobmsg_parse(pol_a, ARRAY_SIZE(pol_a), tb, 115 | blob_data(buf->head), blobmsg_data_len(buf->head)) != 0) { 116 | fprintf(stderr, "Policy %s parse failed\n", array_a); 117 | return -1; 118 | } 119 | 120 | if (tb[ARRAY_A] == NULL) { 121 | fprintf(stderr, "%s not found\n", array_a); 122 | return -1; 123 | } 124 | 125 | int const result = blobmsg_check_array(tb[ARRAY_A], BLOBMSG_TYPE_TABLE); 126 | 127 | if (result < 0) { 128 | fprintf(stderr, "Failed blobmsg_check_array() (TABLE) on %s (%d)\n", 129 | array_a, result); 130 | return -1; 131 | } 132 | 133 | return check_table_a_entries(tb[ARRAY_A]); 134 | } 135 | 136 | static void 137 | fill_message(struct blob_buf * const buf) 138 | { 139 | void * const tbl_a = blobmsg_open_array(buf, "array_a"); 140 | void * const obj = blobmsg_open_table(buf, NULL); 141 | 142 | void * const tbl_b = blobmsg_open_array(buf, "array_b"); 143 | 144 | blobmsg_add_string(buf, NULL, "1"); 145 | 146 | blobmsg_close_array(buf, tbl_b); 147 | 148 | blobmsg_close_table(buf, obj); 149 | 150 | blobmsg_close_array(buf, tbl_a); 151 | } 152 | 153 | int main(int argc, char **argv) 154 | { 155 | int result; 156 | static struct blob_buf buf; 157 | 158 | blobmsg_buf_init(&buf); 159 | fill_message(&buf); 160 | 161 | result = check_message(&buf); 162 | if (result == 0) 163 | fprintf(stderr, "blobmsg_check_array() test passed\n"); 164 | 165 | if (buf.buf != NULL) 166 | free(buf.buf); 167 | 168 | return result ? EXIT_FAILURE : EXIT_SUCCESS; 169 | } 170 | -------------------------------------------------------------------------------- /runqueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * runqueue.c - a simple task queueing/completion tracking helper 3 | * 4 | * Copyright (C) 2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __LIBUBOX_RUNQUEUE_H 20 | #define __LIBUBOX_RUNQUEUE_H 21 | 22 | #include "list.h" 23 | #include "safe_list.h" 24 | #include "uloop.h" 25 | 26 | struct runqueue; 27 | struct runqueue_task; 28 | struct runqueue_task_type; 29 | 30 | struct runqueue { 31 | struct safe_list tasks_active; 32 | struct safe_list tasks_inactive; 33 | struct uloop_timeout timeout; 34 | 35 | int running_tasks; 36 | int max_running_tasks; 37 | bool stopped; 38 | bool empty; 39 | 40 | /* called when the runqueue is emptied */ 41 | void (*empty_cb)(struct runqueue *q); 42 | }; 43 | 44 | struct runqueue_task_type { 45 | const char *name; 46 | 47 | /* 48 | * called when a task is requested to run 49 | * 50 | * The task is removed from the list before this callback is run. It 51 | * can re-arm itself using runqueue_task_add. 52 | */ 53 | void (*run)(struct runqueue *q, struct runqueue_task *t); 54 | 55 | /* 56 | * called to request cancelling a task 57 | * 58 | * int type is used as an optional hint for the method to be used when 59 | * cancelling the task, e.g. a signal number for processes. The cancel 60 | * callback should call runqueue_task_complete when done. 61 | */ 62 | void (*cancel)(struct runqueue *q, struct runqueue_task *t, int type); 63 | 64 | /* 65 | * called to kill a task. must not make any calls to runqueue_task_complete, 66 | * which will be called after this returns. 67 | */ 68 | void (*kill)(struct runqueue *q, struct runqueue_task *t); 69 | }; 70 | 71 | struct runqueue_task { 72 | struct safe_list list; 73 | const struct runqueue_task_type *type; 74 | struct runqueue *q; 75 | 76 | void (*complete)(struct runqueue *q, struct runqueue_task *t); 77 | 78 | struct uloop_timeout timeout; 79 | int run_timeout; 80 | int cancel_timeout; 81 | int cancel_type; 82 | 83 | bool queued; 84 | bool running; 85 | bool cancelled; 86 | }; 87 | 88 | struct runqueue_process { 89 | struct runqueue_task task; 90 | struct uloop_process proc; 91 | }; 92 | 93 | #define RUNQUEUE_INIT(_name, _max_running) { \ 94 | .tasks_active = SAFE_LIST_INIT(_name.tasks_active), \ 95 | .tasks_inactive = SAFE_LIST_INIT(_name.tasks_inactive), \ 96 | .max_running_tasks = _max_running \ 97 | } 98 | 99 | #define RUNQUEUE(_name, _max_running) \ 100 | struct runqueue _name = RUNQUEUE_INIT(_name, _max_running) 101 | 102 | void runqueue_init(struct runqueue *q); 103 | void runqueue_cancel(struct runqueue *q); 104 | void runqueue_cancel_active(struct runqueue *q); 105 | void runqueue_cancel_pending(struct runqueue *q); 106 | void runqueue_kill(struct runqueue *q); 107 | 108 | void runqueue_stop(struct runqueue *q); 109 | void runqueue_resume(struct runqueue *q); 110 | 111 | void runqueue_task_add(struct runqueue *q, struct runqueue_task *t, bool running); 112 | void runqueue_task_add_first(struct runqueue *q, struct runqueue_task *t, bool running); 113 | void runqueue_task_complete(struct runqueue_task *t); 114 | 115 | void runqueue_task_cancel(struct runqueue_task *t, int type); 116 | void runqueue_task_kill(struct runqueue_task *t); 117 | 118 | void runqueue_process_add(struct runqueue *q, struct runqueue_process *p, pid_t pid); 119 | 120 | /* to be used only from runqueue_process callbacks */ 121 | void runqueue_process_cancel_cb(struct runqueue *q, struct runqueue_task *t, int type); 122 | void runqueue_process_kill_cb(struct runqueue *q, struct runqueue_task *t); 123 | 124 | #endif 125 | -------------------------------------------------------------------------------- /uloop.h: -------------------------------------------------------------------------------- 1 | /* 2 | * uloop - event loop implementation 3 | * 4 | * Copyright (C) 2010-2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #ifndef _ULOOP_H__ 19 | #define _ULOOP_H__ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #if defined(__APPLE__) || defined(__FreeBSD__) 28 | #define USE_KQUEUE 29 | #else 30 | #define USE_EPOLL 31 | #endif 32 | 33 | #include "list.h" 34 | 35 | struct uloop_fd; 36 | struct uloop_timeout; 37 | struct uloop_process; 38 | struct uloop_interval; 39 | struct uloop_signal; 40 | 41 | typedef void (*uloop_fd_handler)(struct uloop_fd *u, unsigned int events); 42 | typedef void (*uloop_timeout_handler)(struct uloop_timeout *t); 43 | typedef void (*uloop_process_handler)(struct uloop_process *c, int ret); 44 | typedef void (*uloop_interval_handler)(struct uloop_interval *t); 45 | typedef void (*uloop_signal_handler)(struct uloop_signal *s); 46 | 47 | #define ULOOP_READ (1 << 0) 48 | #define ULOOP_WRITE (1 << 1) 49 | #define ULOOP_EDGE_TRIGGER (1 << 2) 50 | #define ULOOP_BLOCKING (1 << 3) 51 | 52 | #define ULOOP_EVENT_MASK (ULOOP_READ | ULOOP_WRITE) 53 | 54 | /* internal flags */ 55 | #define ULOOP_EVENT_BUFFERED (1 << 4) 56 | #ifdef USE_KQUEUE 57 | #define ULOOP_EDGE_DEFER (1 << 5) 58 | #endif 59 | 60 | #define ULOOP_ERROR_CB (1 << 6) 61 | 62 | struct uloop_fd 63 | { 64 | uloop_fd_handler cb; 65 | int fd; 66 | bool eof; 67 | bool error; 68 | bool registered; 69 | uint8_t flags; 70 | }; 71 | 72 | struct uloop_timeout 73 | { 74 | struct list_head list; 75 | bool pending; 76 | 77 | uloop_timeout_handler cb; 78 | struct timeval time; 79 | }; 80 | 81 | struct uloop_process 82 | { 83 | struct list_head list; 84 | bool pending; 85 | 86 | uloop_process_handler cb; 87 | pid_t pid; 88 | }; 89 | 90 | struct uloop_interval 91 | { 92 | uloop_interval_handler cb; 93 | uint64_t expirations; 94 | 95 | union { 96 | struct uloop_fd ufd; 97 | struct { 98 | int64_t fired; 99 | unsigned int msecs; 100 | } time; 101 | } priv; 102 | }; 103 | 104 | struct uloop_signal 105 | { 106 | struct list_head list; 107 | struct sigaction orig; 108 | bool pending; 109 | 110 | uloop_signal_handler cb; 111 | int signo; 112 | }; 113 | 114 | extern bool uloop_cancelled; 115 | extern bool uloop_handle_sigchld; 116 | extern uloop_fd_handler uloop_fd_set_cb; 117 | 118 | int uloop_fd_add(struct uloop_fd *sock, unsigned int flags); 119 | int uloop_fd_delete(struct uloop_fd *sock); 120 | 121 | int uloop_get_next_timeout(void); 122 | int uloop_timeout_add(struct uloop_timeout *timeout); 123 | int uloop_timeout_set(struct uloop_timeout *timeout, int msecs); 124 | int uloop_timeout_cancel(struct uloop_timeout *timeout); 125 | int uloop_timeout_remaining(struct uloop_timeout *timeout) __attribute__((deprecated("use uloop_timeout_remaining64"))); 126 | int64_t uloop_timeout_remaining64(struct uloop_timeout *timeout); 127 | 128 | int uloop_process_add(struct uloop_process *p); 129 | int uloop_process_delete(struct uloop_process *p); 130 | 131 | int uloop_interval_set(struct uloop_interval *timer, unsigned int msecs); 132 | int uloop_interval_cancel(struct uloop_interval *timer); 133 | int64_t uloop_interval_remaining(struct uloop_interval *timer); 134 | 135 | int uloop_signal_add(struct uloop_signal *s); 136 | int uloop_signal_delete(struct uloop_signal *s); 137 | 138 | bool uloop_cancelling(void); 139 | 140 | static inline void uloop_end(void) 141 | { 142 | uloop_cancelled = true; 143 | } 144 | 145 | int uloop_init(void); 146 | int uloop_run_timeout(int timeout); 147 | static inline int uloop_run(void) 148 | { 149 | return uloop_run_timeout(-1); 150 | } 151 | void uloop_done(void); 152 | 153 | #endif 154 | -------------------------------------------------------------------------------- /tests/test-blob-parse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Based on certificate dump functionality from ucert.c: 3 | * 4 | * Copyright (C) 2018 Daniel Golle 5 | * SPDX-License-Identifier: GPL-3.0 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "blob.h" 15 | #include "list.h" 16 | #include "blobmsg_json.h" 17 | 18 | #define CERT_BUF_LEN 4096 19 | 20 | /* 21 | * ucert structure 22 | * | BLOB | 23 | * | SIGNATURE | PAYLOAD | 24 | * | |[ BLOBMSG CONTAINER ]| 25 | * | |[[T,i,v,e,f,pubkey ]]| 26 | */ 27 | enum cert_attr { 28 | CERT_ATTR_SIGNATURE, 29 | CERT_ATTR_PAYLOAD, 30 | CERT_ATTR_MAX 31 | }; 32 | 33 | static const struct blob_attr_info cert_policy[CERT_ATTR_MAX] = { 34 | [CERT_ATTR_SIGNATURE] = { .type = BLOB_ATTR_BINARY }, 35 | [CERT_ATTR_PAYLOAD] = { .type = BLOB_ATTR_NESTED }, 36 | }; 37 | 38 | enum cert_cont_attr { 39 | CERT_CT_ATTR_PAYLOAD, 40 | CERT_CT_ATTR_MAX 41 | }; 42 | 43 | enum cert_payload_attr { 44 | CERT_PL_ATTR_CERTTYPE, 45 | CERT_PL_ATTR_CERTID, 46 | CERT_PL_ATTR_VALIDFROMTIME, 47 | CERT_PL_ATTR_EXPIRETIME, 48 | CERT_PL_ATTR_PUBKEY, 49 | CERT_PL_ATTR_KEY_FINGERPRINT, 50 | CERT_PL_ATTR_MAX 51 | }; 52 | 53 | enum certtype_id { 54 | CERTTYPE_UNSPEC, 55 | CERTTYPE_AUTH, 56 | CERTTYPE_REVOKE 57 | }; 58 | 59 | /* list to store certificate chain at runtime */ 60 | struct cert_object { 61 | struct list_head list; 62 | struct blob_attr *cert[CERT_ATTR_MAX]; 63 | }; 64 | 65 | static int cert_load(const char *certfile, struct list_head *chain) 66 | { 67 | FILE *f; 68 | struct blob_attr *certtb[CERT_ATTR_MAX]; 69 | struct blob_attr *bufpt; 70 | struct cert_object *cobj; 71 | char *filebuf = NULL; 72 | int ret = 0, pret = 0; 73 | size_t len, pos = 0; 74 | 75 | f = fopen(certfile, "r"); 76 | if (!f) 77 | return 1; 78 | 79 | filebuf = malloc(CERT_BUF_LEN+1); 80 | if (!filebuf) 81 | return 1; 82 | 83 | len = fread(filebuf, 1, CERT_BUF_LEN, f); 84 | if (len < 64) { 85 | free(filebuf); 86 | return 1; 87 | } 88 | 89 | ret = ferror(f) || !feof(f); 90 | fclose(f); 91 | if (ret) { 92 | free(filebuf); 93 | return 1; 94 | } 95 | 96 | bufpt = (struct blob_attr *)filebuf; 97 | do { 98 | pret = blob_parse_untrusted(bufpt, len, certtb, cert_policy, CERT_ATTR_MAX); 99 | if (pret <= 0) 100 | /* no attributes found */ 101 | break; 102 | 103 | if (pos + blob_pad_len(bufpt) > len) 104 | /* blob exceeds filebuffer */ 105 | break; 106 | else 107 | pos += blob_pad_len(bufpt); 108 | 109 | if (!certtb[CERT_ATTR_SIGNATURE]) 110 | /* no signature -> drop */ 111 | break; 112 | 113 | cobj = calloc(1, sizeof(*cobj)); 114 | cobj->cert[CERT_ATTR_SIGNATURE] = blob_memdup(certtb[CERT_ATTR_SIGNATURE]); 115 | if (certtb[CERT_ATTR_PAYLOAD]) 116 | cobj->cert[CERT_ATTR_PAYLOAD] = blob_memdup(certtb[CERT_ATTR_PAYLOAD]); 117 | 118 | list_add_tail(&cobj->list, chain); 119 | ret += pret; 120 | /* repeat parsing while there is still enough remaining data in buffer */ 121 | } while(len > pos + sizeof(struct blob_attr) && (bufpt = blob_next(bufpt))); 122 | 123 | free(filebuf); 124 | return (ret <= 0); 125 | } 126 | 127 | /* dump single chain element to console */ 128 | static void cert_dump_blob(struct blob_attr *cert[CERT_ATTR_MAX]) 129 | { 130 | int i; 131 | char *json = NULL; 132 | 133 | for (i = 0; i < CERT_ATTR_MAX; i++) { 134 | struct blob_attr *v = cert[i]; 135 | 136 | if (!v) 137 | continue; 138 | 139 | switch(cert_policy[i].type) { 140 | case BLOB_ATTR_BINARY: 141 | fprintf(stdout, "signature:\n---\n%s---\n", (char *) blob_data(v)); 142 | break; 143 | case BLOB_ATTR_NESTED: 144 | json = blobmsg_format_json_indent(blob_data(v), false, 0); 145 | if (!json) 146 | continue; 147 | 148 | fprintf(stdout, "payload:\n---\n%s\n---\n", json); 149 | free(json); 150 | break; 151 | } 152 | } 153 | } 154 | 155 | static int cert_dump(const char *certfile) 156 | { 157 | struct cert_object *cobj; 158 | static LIST_HEAD(certchain); 159 | unsigned int count = 0; 160 | 161 | if (cert_load(certfile, &certchain)) { 162 | fprintf(stderr, "cannot parse cert %s\n", basename((char *) certfile)); 163 | return 1; 164 | } 165 | 166 | list_for_each_entry(cobj, &certchain, list) { 167 | fprintf(stdout, "=== CHAIN ELEMENT %02u ===\n", ++count); 168 | cert_dump_blob(cobj->cert); 169 | } 170 | 171 | return 0; 172 | } 173 | 174 | int main(int argc, char *argv[]) 175 | { 176 | if (argc != 2) { 177 | fprintf(stderr, "Usage: %s \n", argv[0]); 178 | return 3; 179 | } 180 | 181 | cert_dump(argv[1]); 182 | 183 | return 0; 184 | } 185 | -------------------------------------------------------------------------------- /ustream-fd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ustream - library for stream buffer management 3 | * 4 | * Copyright (C) 2012 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include "ustream.h" 23 | 24 | static void ustream_fd_set_uloop(struct ustream *s, bool write) 25 | { 26 | struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); 27 | struct ustream_buf *buf; 28 | unsigned int flags = ULOOP_EDGE_TRIGGER | ULOOP_ERROR_CB; 29 | 30 | if (!s->read_blocked && !s->eof) 31 | flags |= ULOOP_READ; 32 | 33 | buf = s->w.head; 34 | if (write || (buf && s->w.data_bytes && !s->write_error)) 35 | flags |= ULOOP_WRITE; 36 | 37 | uloop_fd_add(&sf->fd, flags); 38 | } 39 | 40 | static void ustream_fd_set_read_blocked(struct ustream *s) 41 | { 42 | ustream_fd_set_uloop(s, false); 43 | } 44 | 45 | static void ustream_fd_read_pending(struct ustream_fd *sf, bool *more) 46 | { 47 | struct ustream *s = &sf->stream; 48 | int buflen = 0; 49 | ssize_t len; 50 | char *buf; 51 | 52 | do { 53 | if (s->read_blocked) 54 | break; 55 | 56 | buf = ustream_reserve(s, 1, &buflen); 57 | if (!buf) 58 | break; 59 | 60 | len = read(sf->fd.fd, buf, buflen); 61 | if (len < 0) { 62 | if (errno == EINTR) 63 | continue; 64 | 65 | if (errno == EAGAIN || errno == ENOTCONN) 66 | return; 67 | 68 | len = 0; 69 | } 70 | 71 | if (!len) { 72 | if (!s->eof) 73 | ustream_state_change(s); 74 | s->eof = true; 75 | ustream_fd_set_uloop(s, false); 76 | return; 77 | } 78 | 79 | ustream_fill_read(s, len); 80 | *more = true; 81 | } while (1); 82 | } 83 | 84 | static int ustream_fd_write(struct ustream *s, const char *buf, int buflen, bool more) 85 | { 86 | struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); 87 | ssize_t ret = 0, len; 88 | 89 | if (!buflen) 90 | return 0; 91 | 92 | while (buflen) { 93 | len = write(sf->fd.fd, buf, buflen); 94 | 95 | if (len < 0) { 96 | if (errno == EINTR) 97 | continue; 98 | 99 | if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOTCONN) 100 | break; 101 | 102 | return -1; 103 | } 104 | 105 | ret += len; 106 | buf += len; 107 | buflen -= len; 108 | } 109 | 110 | if (buflen) 111 | ustream_fd_set_uloop(s, true); 112 | 113 | return ret; 114 | } 115 | 116 | static bool __ustream_fd_poll(struct ustream_fd *sf, unsigned int events) 117 | { 118 | struct ustream *s = &sf->stream; 119 | bool more = false; 120 | 121 | if (events & ULOOP_READ) 122 | ustream_fd_read_pending(sf, &more); 123 | 124 | if (events & ULOOP_WRITE) { 125 | bool no_more = ustream_write_pending(s); 126 | if (no_more) 127 | ustream_fd_set_uloop(s, false); 128 | } 129 | 130 | if (sf->fd.error && !s->write_error) { 131 | ustream_state_change(s); 132 | s->write_error = true; 133 | ustream_fd_set_uloop(s, false); 134 | } 135 | 136 | return more; 137 | } 138 | 139 | static bool ustream_fd_poll(struct ustream *s) 140 | { 141 | struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); 142 | 143 | return __ustream_fd_poll(sf, ULOOP_READ | ULOOP_WRITE); 144 | } 145 | 146 | static void ustream_uloop_cb(struct uloop_fd *fd, unsigned int events) 147 | { 148 | struct ustream_fd *sf = container_of(fd, struct ustream_fd, fd); 149 | 150 | __ustream_fd_poll(sf, events); 151 | } 152 | 153 | static void ustream_fd_free(struct ustream *s) 154 | { 155 | struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); 156 | 157 | uloop_fd_delete(&sf->fd); 158 | } 159 | 160 | void ustream_fd_init(struct ustream_fd *sf, int fd) 161 | { 162 | struct ustream *s = &sf->stream; 163 | 164 | ustream_init_defaults(s); 165 | 166 | sf->fd.fd = fd; 167 | sf->fd.cb = ustream_uloop_cb; 168 | s->set_read_blocked = ustream_fd_set_read_blocked; 169 | s->write = ustream_fd_write; 170 | s->free = ustream_fd_free; 171 | s->poll = ustream_fd_poll; 172 | ustream_fd_set_uloop(s, false); 173 | } 174 | -------------------------------------------------------------------------------- /tests/test-runqueue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * runqueue-example.c 3 | * 4 | * Copyright (C) 2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "uloop.h" 25 | #include "runqueue.h" 26 | 27 | static struct runqueue q; 28 | 29 | struct sleeper { 30 | int val; 31 | bool kill; 32 | struct uloop_timeout t; 33 | struct runqueue_process proc; 34 | }; 35 | 36 | static void q_empty(struct runqueue *q) 37 | { 38 | fprintf(stderr, "All done!\n"); 39 | uloop_end(); 40 | } 41 | 42 | static const char* sleeper_type(struct sleeper *s) 43 | { 44 | return s->kill ? "killer" : "sleeper"; 45 | } 46 | 47 | static void q_sleep_run(struct runqueue *q, struct runqueue_task *t) 48 | { 49 | struct sleeper *s = container_of(t, struct sleeper, proc.task); 50 | char str[32]; 51 | pid_t pid; 52 | 53 | fprintf(stderr, "[%d/%d] start 'sleep %d' (%s)\n", q->running_tasks, 54 | q->max_running_tasks, s->val, sleeper_type(s)); 55 | 56 | pid = fork(); 57 | if (pid < 0) 58 | return; 59 | 60 | if (pid) { 61 | runqueue_process_add(q, &s->proc, pid); 62 | return; 63 | } 64 | 65 | sprintf(str, "%d", s->val); 66 | execlp("sleep", "sleep", str, NULL); 67 | exit(1); 68 | } 69 | 70 | static void q_sleep_cancel(struct runqueue *q, struct runqueue_task *t, int type) 71 | { 72 | struct sleeper *s = container_of(t, struct sleeper, proc.task); 73 | 74 | fprintf(stderr, "[%d/%d] cancel 'sleep %d' (%s)\n", q->running_tasks, 75 | q->max_running_tasks, s->val, sleeper_type(s)); 76 | runqueue_process_cancel_cb(q, t, type); 77 | } 78 | 79 | static void q_sleep_complete(struct runqueue *q, struct runqueue_task *p) 80 | { 81 | struct sleeper *s = container_of(p, struct sleeper, proc.task); 82 | 83 | fprintf(stderr, "[%d/%d] finish 'sleep %d' (%s) \n", q->running_tasks, 84 | q->max_running_tasks, s->val, sleeper_type(s)); 85 | free(s); 86 | } 87 | 88 | static void my_runqueue_process_kill_cb(struct runqueue *q, struct runqueue_task *p) 89 | { 90 | struct sleeper *s = container_of(p, struct sleeper, proc.task); 91 | 92 | fprintf(stderr, "[%d/%d] killing process (%s)\n", q->running_tasks, 93 | q->max_running_tasks, sleeper_type(s)); 94 | runqueue_process_kill_cb(q, p); 95 | } 96 | 97 | static void timer_cb(struct uloop_timeout *t) 98 | { 99 | struct sleeper *s = container_of(t, struct sleeper, t); 100 | if (s->kill) 101 | runqueue_task_kill(&s->proc.task); 102 | } 103 | 104 | static struct sleeper* create_sleeper(int val, const struct runqueue_task_type *type, bool kill) 105 | { 106 | struct sleeper *s = calloc(1, sizeof(*s)); 107 | s->kill = kill; 108 | s->t.cb = timer_cb; 109 | s->proc.task.type = type; 110 | s->proc.task.run_timeout = 500; 111 | s->proc.task.complete = q_sleep_complete; 112 | s->val = val; 113 | 114 | return s; 115 | } 116 | 117 | static void add_sleeper(int val) 118 | { 119 | static const struct runqueue_task_type sleeper_type = { 120 | .run = q_sleep_run, 121 | .cancel = q_sleep_cancel, 122 | .kill = runqueue_process_kill_cb, 123 | }; 124 | 125 | static const struct runqueue_task_type killer_type = { 126 | .run = q_sleep_run, 127 | .cancel = q_sleep_cancel, 128 | .kill = my_runqueue_process_kill_cb, 129 | }; 130 | 131 | struct sleeper *k = create_sleeper(val, &killer_type, true); 132 | uloop_timeout_set(&k->t, 10); 133 | uloop_timeout_add(&k->t); 134 | runqueue_task_add(&q, &k->proc.task, false); 135 | 136 | struct sleeper *s = create_sleeper(val, &sleeper_type, false); 137 | runqueue_task_add(&q, &s->proc.task, false); 138 | } 139 | 140 | int main(int argc, char **argv) 141 | { 142 | uloop_init(); 143 | 144 | runqueue_init(&q); 145 | q.empty_cb = q_empty; 146 | q.max_running_tasks = 1; 147 | 148 | if (argc > 1) 149 | q.max_running_tasks = atoi(argv[1]); 150 | 151 | add_sleeper(1); 152 | add_sleeper(1); 153 | add_sleeper(1); 154 | 155 | uloop_run(); 156 | uloop_done(); 157 | 158 | return 0; 159 | } 160 | -------------------------------------------------------------------------------- /json_script.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Felix Fietkau 3 | * 4 | * Permission to use, copy, modify, and/or distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | #ifndef __JSON_SCRIPT_H 17 | #define __JSON_SCRIPT_H 18 | 19 | #include "avl.h" 20 | #include "blob.h" 21 | #include "blobmsg.h" 22 | #include "utils.h" 23 | 24 | struct json_script_file; 25 | 26 | struct json_script_ctx { 27 | struct avl_tree files; 28 | struct blob_buf buf; 29 | 30 | uint32_t run_seq; 31 | bool abort; 32 | 33 | /* 34 | * handle_command: handle a command that was not recognized by the 35 | * json_script core (required) 36 | * 37 | * @cmd: blobmsg container of the processed command 38 | * @vars: blobmsg container of current run variables 39 | */ 40 | void (*handle_command)(struct json_script_ctx *ctx, const char *name, 41 | struct blob_attr *cmd, struct blob_attr *vars); 42 | 43 | /* 44 | * handle_expr: handle an expression that was not recognized by the 45 | * json_script core (optional) 46 | * 47 | * @expr: blobmsg container of the processed expression 48 | * @vars: blobmsg container of current runtime variables 49 | */ 50 | int (*handle_expr)(struct json_script_ctx *ctx, const char *name, 51 | struct blob_attr *expr, struct blob_attr *vars); 52 | 53 | /* 54 | * handle_var - look up a variable that's not part of the runtime 55 | * variable set (optional) 56 | */ 57 | const char *(*handle_var)(struct json_script_ctx *ctx, const char *name, 58 | struct blob_attr *vars); 59 | 60 | /* 61 | * handle_file - load a file by filename (optional) 62 | * 63 | * in case of wildcards, it can return a chain of json_script files 64 | * linked via the ::next pointer. Only the first json_script file is 65 | * added to the tree. 66 | */ 67 | struct json_script_file *(*handle_file)(struct json_script_ctx *ctx, 68 | const char *name); 69 | 70 | /* 71 | * handle_error - handle a processing error in a command or expression 72 | * (optional) 73 | * 74 | * @msg: error message 75 | * @context: source file context of the error (blobmsg container) 76 | */ 77 | void (*handle_error)(struct json_script_ctx *ctx, const char *msg, 78 | struct blob_attr *context); 79 | }; 80 | 81 | struct json_script_file { 82 | struct avl_node avl; 83 | struct json_script_file *next; 84 | 85 | unsigned int seq; 86 | struct blob_attr data[]; 87 | }; 88 | 89 | void json_script_init(struct json_script_ctx *ctx); 90 | void json_script_free(struct json_script_ctx *ctx); 91 | 92 | /* 93 | * json_script_run - run a json script with a set of runtime variables 94 | * 95 | * @filename: initial filename to run 96 | * @vars: blobmsg container of the current runtime variables 97 | */ 98 | void json_script_run(struct json_script_ctx *ctx, const char *filename, 99 | struct blob_attr *vars); 100 | 101 | void json_script_run_file(struct json_script_ctx *ctx, struct json_script_file *file, 102 | struct blob_attr *vars); 103 | 104 | /* 105 | * json_script_abort - abort current json script run 106 | * 107 | * to be called from a script context callback 108 | */ 109 | static inline void 110 | json_script_abort(struct json_script_ctx *ctx) 111 | { 112 | ctx->abort = true; 113 | } 114 | 115 | /* 116 | * json_script_eval_string - evaluate a string and store the result 117 | * 118 | * Can be used to process variable references outside of a script 119 | * in a same way that they would be interpreted in the script context. 120 | */ 121 | int json_script_eval_string(struct json_script_ctx *ctx, struct blob_attr *vars, 122 | struct blob_buf *buf, const char *name, 123 | const char *pattern); 124 | 125 | struct json_script_file * 126 | json_script_file_from_blobmsg(const char *name, void *data, int len); 127 | 128 | /* 129 | * json_script_find_var - helper function to find a runtime variable from 130 | * the list passed by json_script user. 131 | * It is intended to be used by the .handle_var callback 132 | */ 133 | const char *json_script_find_var(struct json_script_ctx *ctx, struct blob_attr *vars, 134 | const char *name); 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /ulog.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ulog - simple logging functions 3 | * 4 | * Copyright (C) 2015 Jo-Philipp Wich 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include "ulog.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | static int _ulog_channels = -1; 28 | static int _ulog_facility = -1; 29 | static int _ulog_threshold = LOG_DEBUG; 30 | static int _ulog_initialized = 0; 31 | static const char *_ulog_ident = NULL; 32 | static struct udebug_buf *udb = NULL; 33 | 34 | static const char *ulog_default_ident(void) 35 | { 36 | FILE *self; 37 | static char line[64]; 38 | char *p = NULL; 39 | char *sbuf; 40 | 41 | if ((self = fopen("/proc/self/status", "r")) != NULL) { 42 | while (fgets(line, sizeof(line), self)) { 43 | if (!strncmp(line, "Name:", 5)) { 44 | strtok_r(line, "\t\n", &sbuf); 45 | p = strtok_r(NULL, "\t\n", &sbuf); 46 | break; 47 | } 48 | } 49 | fclose(self); 50 | } 51 | 52 | return p; 53 | } 54 | 55 | static void ulog_defaults(void) 56 | { 57 | char *env; 58 | 59 | if (_ulog_initialized) 60 | return; 61 | 62 | env = getenv("PREINIT"); 63 | 64 | if (_ulog_channels < 0) { 65 | if (env && !strcmp(env, "1")) 66 | _ulog_channels = ULOG_KMSG; 67 | else if (isatty(1)) 68 | _ulog_channels = ULOG_STDIO; 69 | else 70 | _ulog_channels = ULOG_SYSLOG; 71 | } 72 | 73 | if (_ulog_facility < 0) { 74 | if (env && !strcmp(env, "1")) 75 | _ulog_facility = LOG_DAEMON; 76 | else if (isatty(1)) 77 | _ulog_facility = LOG_USER; 78 | else 79 | _ulog_facility = LOG_DAEMON; 80 | } 81 | 82 | if (_ulog_ident == NULL && _ulog_channels != ULOG_STDIO) 83 | _ulog_ident = ulog_default_ident(); 84 | 85 | if (_ulog_channels & ULOG_SYSLOG) 86 | openlog(_ulog_ident, 0, _ulog_facility); 87 | 88 | _ulog_initialized = 1; 89 | } 90 | 91 | __attribute__((format(printf, 2, 0))) 92 | static void ulog_kmsg(int priority, const char *fmt, va_list ap) 93 | { 94 | FILE *kmsg; 95 | 96 | if ((kmsg = fopen("/dev/kmsg", "r+")) != NULL) { 97 | fprintf(kmsg, "<%u>", priority); 98 | 99 | if (_ulog_ident) 100 | fprintf(kmsg, "%s: ", _ulog_ident); 101 | 102 | vfprintf(kmsg, fmt, ap); 103 | fclose(kmsg); 104 | } 105 | } 106 | 107 | __attribute__((format(printf, 2, 0))) 108 | static void ulog_stdio(int priority, const char *fmt, va_list ap) 109 | { 110 | FILE *out = stderr; 111 | 112 | if (_ulog_ident) 113 | fprintf(out, "%s: ", _ulog_ident); 114 | 115 | vfprintf(out, fmt, ap); 116 | } 117 | 118 | __attribute__((format(printf, 2, 0))) 119 | static void ulog_syslog(int priority, const char *fmt, va_list ap) 120 | { 121 | vsyslog(priority, fmt, ap); 122 | } 123 | 124 | void ulog_udebug(struct udebug_buf *_udb) 125 | { 126 | udb = _udb; 127 | } 128 | 129 | void ulog_open(int channels, int facility, const char *ident) 130 | { 131 | ulog_close(); 132 | 133 | _ulog_channels = channels; 134 | _ulog_facility = facility; 135 | _ulog_ident = ident; 136 | } 137 | 138 | void ulog_close(void) 139 | { 140 | if (!_ulog_initialized) 141 | return; 142 | 143 | if (_ulog_channels & ULOG_SYSLOG) 144 | closelog(); 145 | 146 | _ulog_initialized = 0; 147 | } 148 | 149 | void ulog_threshold(int threshold) 150 | { 151 | _ulog_threshold = threshold; 152 | } 153 | 154 | void ulog(int priority, const char *fmt, ...) 155 | { 156 | va_list ap; 157 | 158 | if (udb) { 159 | va_start(ap, fmt); 160 | udebug_entry_init(udb); 161 | udebug_entry_vprintf(udb, fmt, ap); 162 | udebug_entry_add(udb); 163 | va_end(ap); 164 | } 165 | 166 | if (priority > _ulog_threshold) 167 | return; 168 | 169 | ulog_defaults(); 170 | 171 | if (_ulog_channels & ULOG_KMSG) 172 | { 173 | va_start(ap, fmt); 174 | ulog_kmsg(priority, fmt, ap); 175 | va_end(ap); 176 | } 177 | 178 | if (_ulog_channels & ULOG_STDIO) 179 | { 180 | va_start(ap, fmt); 181 | ulog_stdio(priority, fmt, ap); 182 | va_end(ap); 183 | } 184 | 185 | if (_ulog_channels & ULOG_SYSLOG) 186 | { 187 | va_start(ap, fmt); 188 | ulog_syslog(priority, fmt, ap); 189 | va_end(ap); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /uloop-epoll.c: -------------------------------------------------------------------------------- 1 | /* 2 | * uloop - event loop implementation 3 | * 4 | * Copyright (C) 2010-2016 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | /** 20 | * FIXME: uClibc < 0.9.30.3 does not define EPOLLRDHUP for Linux >= 2.6.17 21 | */ 22 | #ifndef EPOLLRDHUP 23 | #define EPOLLRDHUP 0x2000 24 | #endif 25 | 26 | static int uloop_init_pollfd(void) 27 | { 28 | if (poll_fd >= 0) 29 | return 0; 30 | 31 | poll_fd = epoll_create(32); 32 | if (poll_fd < 0) 33 | return -1; 34 | 35 | fcntl(poll_fd, F_SETFD, fcntl(poll_fd, F_GETFD) | FD_CLOEXEC); 36 | return 0; 37 | } 38 | 39 | static int register_poll(struct uloop_fd *fd, unsigned int flags) 40 | { 41 | struct epoll_event ev; 42 | int op = fd->registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; 43 | 44 | memset(&ev, 0, sizeof(struct epoll_event)); 45 | 46 | if (flags & ULOOP_READ) 47 | ev.events |= EPOLLIN | EPOLLRDHUP; 48 | 49 | if (flags & ULOOP_WRITE) 50 | ev.events |= EPOLLOUT; 51 | 52 | if (flags & ULOOP_EDGE_TRIGGER) 53 | ev.events |= EPOLLET; 54 | 55 | ev.data.ptr = fd; 56 | 57 | return epoll_ctl(poll_fd, op, fd->fd, &ev); 58 | } 59 | 60 | static struct epoll_event events[ULOOP_MAX_EVENTS]; 61 | 62 | static int __uloop_fd_delete(struct uloop_fd *sock) 63 | { 64 | sock->flags = 0; 65 | return epoll_ctl(poll_fd, EPOLL_CTL_DEL, sock->fd, 0); 66 | } 67 | 68 | static int uloop_fetch_events(int timeout) 69 | { 70 | int n, nfds; 71 | 72 | nfds = epoll_wait(poll_fd, events, ARRAY_SIZE(events), timeout); 73 | for (n = 0; n < nfds; ++n) { 74 | struct uloop_fd_event *cur = &cur_fds[n]; 75 | struct uloop_fd *u = events[n].data.ptr; 76 | unsigned int ev = 0; 77 | 78 | cur->fd = u; 79 | if (!u) 80 | continue; 81 | 82 | if (events[n].events & (EPOLLERR|EPOLLHUP)) { 83 | u->error = true; 84 | if (!(u->flags & ULOOP_ERROR_CB)) 85 | uloop_fd_delete(u); 86 | } 87 | 88 | if(!(events[n].events & (EPOLLRDHUP|EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP))) { 89 | cur->fd = NULL; 90 | continue; 91 | } 92 | 93 | if(events[n].events & EPOLLRDHUP) 94 | u->eof = true; 95 | 96 | if(events[n].events & EPOLLIN) 97 | ev |= ULOOP_READ; 98 | 99 | if(events[n].events & EPOLLOUT) 100 | ev |= ULOOP_WRITE; 101 | 102 | cur->events = ev; 103 | } 104 | 105 | return nfds; 106 | } 107 | 108 | static void dispatch_timer(struct uloop_fd *u, unsigned int events) 109 | { 110 | if (!(events & ULOOP_READ)) 111 | return; 112 | 113 | uint64_t fired; 114 | 115 | if (read(u->fd, &fired, sizeof(fired)) != sizeof(fired)) 116 | return; 117 | 118 | struct uloop_interval *tm = container_of(u, struct uloop_interval, priv.ufd); 119 | 120 | tm->expirations += fired; 121 | tm->cb(tm); 122 | } 123 | 124 | static int timer_register(struct uloop_interval *tm, unsigned int msecs) 125 | { 126 | if (!tm->priv.ufd.registered) { 127 | int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC|TFD_NONBLOCK); 128 | 129 | if (fd == -1) 130 | return -1; 131 | 132 | tm->priv.ufd.fd = fd; 133 | tm->priv.ufd.cb = dispatch_timer; 134 | } 135 | 136 | struct itimerspec spec = { 137 | .it_value = { 138 | .tv_sec = msecs / 1000, 139 | .tv_nsec = (msecs % 1000) * 1000000 140 | }, 141 | .it_interval = { 142 | .tv_sec = msecs / 1000, 143 | .tv_nsec = (msecs % 1000) * 1000000 144 | } 145 | }; 146 | 147 | if (timerfd_settime(tm->priv.ufd.fd, 0, &spec, NULL) == -1) 148 | goto err; 149 | 150 | if (uloop_fd_add(&tm->priv.ufd, ULOOP_READ) == -1) 151 | goto err; 152 | 153 | return 0; 154 | 155 | err: 156 | uloop_fd_delete(&tm->priv.ufd); 157 | close(tm->priv.ufd.fd); 158 | memset(&tm->priv.ufd, 0, sizeof(tm->priv.ufd)); 159 | 160 | return -1; 161 | } 162 | 163 | static int timer_remove(struct uloop_interval *tm) 164 | { 165 | int ret = __uloop_fd_delete(&tm->priv.ufd); 166 | 167 | if (ret == 0) { 168 | close(tm->priv.ufd.fd); 169 | memset(&tm->priv.ufd, 0, sizeof(tm->priv.ufd)); 170 | } 171 | 172 | return ret; 173 | } 174 | 175 | static int64_t timer_next(struct uloop_interval *tm) 176 | { 177 | struct itimerspec spec; 178 | 179 | if (!tm->priv.ufd.registered) 180 | return -1; 181 | 182 | if (timerfd_gettime(tm->priv.ufd.fd, &spec) == -1) 183 | return -1; 184 | 185 | return spec.it_value.tv_sec * 1000 + spec.it_value.tv_nsec / 1000000; 186 | } 187 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * utils - misc libubox utility functions 3 | * 4 | * Copyright (C) 2012 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "utils.h" 26 | 27 | #define foreach_arg(_arg, _addr, _len, _first_addr, _first_len) \ 28 | for (_addr = (_first_addr), _len = (_first_len); \ 29 | _addr; \ 30 | _addr = va_arg(_arg, void **), _len = _addr ? va_arg(_arg, size_t) : 0) 31 | 32 | #define C_PTR_ALIGN (sizeof(size_t)) 33 | #define C_PTR_MASK (-C_PTR_ALIGN) 34 | 35 | void *__calloc_a(size_t len, ...) 36 | { 37 | va_list ap, ap1; 38 | void *ret; 39 | void **cur_addr; 40 | size_t cur_len; 41 | int alloc_len = 0; 42 | char *ptr; 43 | 44 | va_start(ap, len); 45 | 46 | va_copy(ap1, ap); 47 | foreach_arg(ap1, cur_addr, cur_len, &ret, len) 48 | alloc_len += (cur_len + C_PTR_ALIGN - 1 ) & C_PTR_MASK; 49 | va_end(ap1); 50 | 51 | ptr = calloc(1, alloc_len); 52 | if (!ptr) { 53 | va_end(ap); 54 | return NULL; 55 | } 56 | 57 | alloc_len = 0; 58 | foreach_arg(ap, cur_addr, cur_len, &ret, len) { 59 | *cur_addr = &ptr[alloc_len]; 60 | alloc_len += (cur_len + C_PTR_ALIGN - 1) & C_PTR_MASK; 61 | } 62 | va_end(ap); 63 | 64 | return ret; 65 | } 66 | 67 | #ifdef LIBUBOX_COMPAT_CLOCK_GETTIME 68 | #include /* host_get_clock_service() */ 69 | #include /* mach_port_deallocate() */ 70 | #include /* mach_host_self(), mach_task_self() */ 71 | #include /* clock_get_time() */ 72 | 73 | static clock_serv_t clock_realtime; 74 | static clock_serv_t clock_monotonic; 75 | 76 | static void __constructor clock_name_init(void) 77 | { 78 | mach_port_t host_self = mach_host_self(); 79 | 80 | host_get_clock_service(host_self, CLOCK_REALTIME, &clock_realtime); 81 | host_get_clock_service(host_self, CLOCK_MONOTONIC, &clock_monotonic); 82 | } 83 | 84 | static void __destructor clock_name_dealloc(void) 85 | { 86 | mach_port_t self = mach_task_self(); 87 | 88 | mach_port_deallocate(self, clock_realtime); 89 | mach_port_deallocate(self, clock_monotonic); 90 | } 91 | 92 | int clock_gettime(int type, struct timespec *tv) 93 | { 94 | int retval = -1; 95 | mach_timespec_t mts; 96 | 97 | switch (type) { 98 | case CLOCK_REALTIME: 99 | retval = clock_get_time(clock_realtime, &mts); 100 | break; 101 | case CLOCK_MONOTONIC: 102 | retval = clock_get_time(clock_monotonic, &mts); 103 | break; 104 | default: 105 | goto out; 106 | } 107 | 108 | tv->tv_sec = mts.tv_sec; 109 | tv->tv_nsec = mts.tv_nsec; 110 | out: 111 | return retval; 112 | } 113 | 114 | #endif 115 | 116 | void *cbuf_alloc(unsigned int order) 117 | { 118 | char path[] = "/tmp/cbuf-XXXXXX"; 119 | unsigned long size = cbuf_size(order); 120 | void *ret = NULL; 121 | int fd; 122 | 123 | fd = mkstemp(path); 124 | if (fd < 0) 125 | return NULL; 126 | 127 | if (unlink(path)) 128 | goto close; 129 | 130 | if (ftruncate(fd, cbuf_size(order))) 131 | goto close; 132 | 133 | #ifndef MAP_ANONYMOUS 134 | #define MAP_ANONYMOUS MAP_ANON 135 | #endif 136 | 137 | ret = mmap(NULL, size * 2, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); 138 | if (ret == MAP_FAILED) { 139 | ret = NULL; 140 | goto close; 141 | } 142 | 143 | if (mmap(ret, size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, 144 | fd, 0) != ret || 145 | mmap(ret + size, size, PROT_READ | PROT_WRITE, 146 | MAP_FIXED | MAP_SHARED, fd, 0) != ret + size) { 147 | munmap(ret, size * 2); 148 | ret = NULL; 149 | } 150 | 151 | close: 152 | close(fd); 153 | return ret; 154 | } 155 | 156 | void cbuf_free(void *ptr, unsigned int order) 157 | { 158 | munmap(ptr, cbuf_size(order) * 2); 159 | } 160 | 161 | int mkdir_p(char *dir, mode_t mask) 162 | { 163 | char *l; 164 | int ret; 165 | 166 | ret = mkdir(dir, mask); 167 | if (!ret || errno == EEXIST) 168 | return 0; 169 | 170 | if (ret && (errno != ENOENT)) 171 | return -1; 172 | 173 | l = strrchr(dir, '/'); 174 | if (!l || l == dir) 175 | return -1; 176 | 177 | *l = '\0'; 178 | 179 | if (mkdir_p(dir, mask)) 180 | return -1; 181 | 182 | *l = '/'; 183 | 184 | ret = mkdir(dir, mask); 185 | if (!ret || errno == EEXIST) 186 | return 0; 187 | else 188 | return -1; 189 | } 190 | -------------------------------------------------------------------------------- /tests/test-blobmsg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "blobmsg.h" 8 | #include "blobmsg_json.h" 9 | 10 | static const char *indent_str = "\t\t\t\t\t\t\t\t\t\t\t\t\t"; 11 | 12 | #define indent_printf(indent, ...) do { \ 13 | if (indent > 0) \ 14 | fwrite(indent_str, indent, 1, stderr); \ 15 | fprintf(stderr, __VA_ARGS__); \ 16 | } while(0) 17 | 18 | static void dump_attr_data(struct blob_attr *data, int indent, int next_indent); 19 | 20 | static void 21 | dump_table(struct blob_attr *head, size_t len, int indent, bool array) 22 | { 23 | struct blob_attr *attr; 24 | struct blobmsg_hdr *hdr; 25 | 26 | indent_printf(indent, "{\n"); 27 | __blob_for_each_attr(attr, head, len) { 28 | hdr = blob_data(attr); 29 | if (!array) 30 | indent_printf(indent + 1, "%s : ", hdr->name); 31 | dump_attr_data(attr, 0, indent + 1); 32 | } 33 | indent_printf(indent, "}\n"); 34 | } 35 | 36 | static void dump_attr_data(struct blob_attr *data, int indent, int next_indent) 37 | { 38 | int type = blobmsg_type(data); 39 | switch(type) { 40 | case BLOBMSG_TYPE_STRING: 41 | indent_printf(indent, "%s (str)\n", blobmsg_get_string(data)); 42 | break; 43 | case BLOBMSG_TYPE_INT8: 44 | indent_printf(indent, "%d (i8)\n", (int8_t) blobmsg_get_u8(data)); 45 | break; 46 | case BLOBMSG_TYPE_INT16: 47 | indent_printf(indent, "%d (i16)\n", (int16_t) blobmsg_get_u16(data)); 48 | break; 49 | case BLOBMSG_TYPE_INT32: 50 | indent_printf(indent, "%d (i32)\n", (int32_t) blobmsg_get_u32(data)); 51 | break; 52 | case BLOBMSG_TYPE_INT64: 53 | indent_printf(indent, "%"PRId64" (i64)\n", (int64_t) blobmsg_get_u64(data)); 54 | break; 55 | case BLOBMSG_TYPE_DOUBLE: 56 | indent_printf(indent, "%lf (dbl)\n", blobmsg_get_double(data)); 57 | break; 58 | case BLOBMSG_TYPE_TABLE: 59 | case BLOBMSG_TYPE_ARRAY: 60 | if (!indent) 61 | indent_printf(indent, "\n"); 62 | dump_table(blobmsg_data(data), blobmsg_data_len(data), 63 | next_indent, type == BLOBMSG_TYPE_ARRAY); 64 | break; 65 | } 66 | } 67 | 68 | enum { 69 | FOO_MESSAGE, 70 | FOO_LIST, 71 | FOO_TESTDATA 72 | }; 73 | 74 | static const struct blobmsg_policy pol[] = { 75 | [FOO_MESSAGE] = { 76 | .name = "message", 77 | .type = BLOBMSG_TYPE_STRING, 78 | }, 79 | [FOO_LIST] = { 80 | .name = "list", 81 | .type = BLOBMSG_TYPE_ARRAY, 82 | }, 83 | [FOO_TESTDATA] = { 84 | .name = "testdata", 85 | .type = BLOBMSG_TYPE_TABLE, 86 | }, 87 | }; 88 | 89 | #ifndef ARRAY_SIZE 90 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 91 | #endif 92 | 93 | static void dump_message(struct blob_buf *buf) 94 | { 95 | struct blob_attr *tb[ARRAY_SIZE(pol)]; 96 | 97 | if (blobmsg_parse(pol, ARRAY_SIZE(pol), tb, blob_data(buf->head), blob_len(buf->head)) != 0) { 98 | fprintf(stderr, "Parse failed\n"); 99 | return; 100 | } 101 | if (tb[FOO_MESSAGE]) 102 | fprintf(stderr, "Message: %s\n", (char *) blobmsg_data(tb[FOO_MESSAGE])); 103 | 104 | if (tb[FOO_LIST]) { 105 | fprintf(stderr, "List: "); 106 | dump_table(blobmsg_data(tb[FOO_LIST]), blobmsg_data_len(tb[FOO_LIST]), 0, true); 107 | } 108 | if (tb[FOO_TESTDATA]) { 109 | fprintf(stderr, "Testdata: "); 110 | dump_table(blobmsg_data(tb[FOO_TESTDATA]), blobmsg_data_len(tb[FOO_TESTDATA]), 0, false); 111 | } 112 | } 113 | 114 | static void 115 | fill_message(struct blob_buf *buf) 116 | { 117 | void *tbl; 118 | 119 | blobmsg_add_string(buf, "message", "Hello, world!"); 120 | 121 | tbl = blobmsg_open_table(buf, "testdata"); 122 | blobmsg_add_double(buf, "dbl-min", DBL_MIN); 123 | blobmsg_add_double(buf, "dbl-max", DBL_MAX); 124 | blobmsg_add_u8(buf, "foo", 0); 125 | blobmsg_add_u8(buf, "poo", 100); 126 | blobmsg_add_u8(buf, "moo-min", INT8_MIN); 127 | blobmsg_add_u8(buf, "moo-max", INT8_MAX); 128 | blobmsg_add_u16(buf, "bar-min", INT16_MIN); 129 | blobmsg_add_u16(buf, "bar-max", INT16_MAX); 130 | blobmsg_add_u32(buf, "baz-min", INT32_MIN); 131 | blobmsg_add_u32(buf, "baz-max", INT32_MAX); 132 | blobmsg_add_u64(buf, "taz-min", INT64_MIN); 133 | blobmsg_add_u64(buf, "taz-max", INT64_MAX); 134 | blobmsg_add_string(buf, "world", "2"); 135 | blobmsg_close_table(buf, tbl); 136 | 137 | tbl = blobmsg_open_array(buf, "list"); 138 | blobmsg_add_u8(buf, NULL, 0); 139 | blobmsg_add_u8(buf, NULL, 100); 140 | blobmsg_add_u8(buf, NULL, INT8_MIN); 141 | blobmsg_add_u8(buf, NULL, INT8_MAX); 142 | blobmsg_add_u16(buf, NULL, INT16_MIN); 143 | blobmsg_add_u16(buf, NULL, INT16_MAX); 144 | blobmsg_add_u32(buf, NULL, INT32_MIN); 145 | blobmsg_add_u32(buf, NULL, INT32_MAX); 146 | blobmsg_add_u64(buf, NULL, INT64_MIN); 147 | blobmsg_add_u64(buf, NULL, INT64_MAX); 148 | blobmsg_add_double(buf, NULL, DBL_MIN); 149 | blobmsg_add_double(buf, NULL, DBL_MAX); 150 | blobmsg_close_table(buf, tbl); 151 | } 152 | 153 | int main(int argc, char **argv) 154 | { 155 | char *json = NULL; 156 | static struct blob_buf buf; 157 | 158 | blobmsg_buf_init(&buf); 159 | fill_message(&buf); 160 | fprintf(stderr, "[*] blobmsg dump:\n"); 161 | dump_message(&buf); 162 | 163 | json = blobmsg_format_json(buf.head, true); 164 | if (!json) 165 | exit(EXIT_FAILURE); 166 | 167 | fprintf(stderr, "\n[*] blobmsg to json: %s\n", json); 168 | 169 | blobmsg_buf_init(&buf); 170 | if (!blobmsg_add_json_from_string(&buf, json)) 171 | exit(EXIT_FAILURE); 172 | 173 | fprintf(stderr, "\n[*] blobmsg from json:\n"); 174 | dump_message(&buf); 175 | 176 | if (buf.buf) 177 | free(buf.buf); 178 | free(json); 179 | 180 | return 0; 181 | } 182 | -------------------------------------------------------------------------------- /tests/cram/test_blobmsg_parse.t: -------------------------------------------------------------------------------- 1 | check that blobmsg_parse is producing expected results: 2 | 3 | $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" 4 | $ export FUZZ_CORPUS="$TESTDIR/../fuzz/corpus" 5 | 6 | $ for blob in $(LC_ALL=C find $FUZZ_CORPUS -type f | sort ); do 7 | > valgrind --quiet --leak-check=full test-blobmsg-parse $blob; \ 8 | > test-blobmsg-parse-san $blob; \ 9 | > done 10 | 71520a5c4b5ca73903216857abbad54a8002d44a: blobmsg_parse: ... (0) 11 | 71520a5c4b5ca73903216857abbad54a8002d44a: blobmsg_parse_array: ... (0) 12 | 71520a5c4b5ca73903216857abbad54a8002d44a: blobmsg_parse: ... (0) 13 | 71520a5c4b5ca73903216857abbad54a8002d44a: blobmsg_parse_array: ... (0) 14 | c1dfd96eea8cc2b62785275bca38ac261256e278: blobmsg_parse: ... (0) 15 | c1dfd96eea8cc2b62785275bca38ac261256e278: blobmsg_parse_array: ... (0) 16 | c1dfd96eea8cc2b62785275bca38ac261256e278: blobmsg_parse: ... (0) 17 | c1dfd96eea8cc2b62785275bca38ac261256e278: blobmsg_parse_array: ... (0) 18 | c42ac1c46f1d4e211c735cc7dfad4ff8391110e9: blobmsg_parse: ... (0) 19 | c42ac1c46f1d4e211c735cc7dfad4ff8391110e9: blobmsg_parse_array: ... (0) 20 | c42ac1c46f1d4e211c735cc7dfad4ff8391110e9: blobmsg_parse: ... (0) 21 | c42ac1c46f1d4e211c735cc7dfad4ff8391110e9: blobmsg_parse_array: ... (0) 22 | crash-1b8fb1be45db3aff7699100f497fb74138f3df4f: blobmsg_parse: ... (0) 23 | crash-1b8fb1be45db3aff7699100f497fb74138f3df4f: blobmsg_parse_array: ... (0) 24 | crash-1b8fb1be45db3aff7699100f497fb74138f3df4f: blobmsg_parse: ... (0) 25 | crash-1b8fb1be45db3aff7699100f497fb74138f3df4f: blobmsg_parse_array: ... (0) 26 | crash-333757b203a44751d3535f24b05f467183a96d09: blobmsg_parse: ... (0) 27 | crash-333757b203a44751d3535f24b05f467183a96d09: blobmsg_parse_array: ... (0) 28 | crash-333757b203a44751d3535f24b05f467183a96d09: blobmsg_parse: ... (0) 29 | crash-333757b203a44751d3535f24b05f467183a96d09: blobmsg_parse_array: ... (0) 30 | crash-4c4d2c3c9ade5da9347534e290305c3b9760f627: blobmsg_parse: ... (-1) 31 | crash-4c4d2c3c9ade5da9347534e290305c3b9760f627: blobmsg_parse_array: ... (-1) 32 | crash-4c4d2c3c9ade5da9347534e290305c3b9760f627: blobmsg_parse: ... (-1) 33 | crash-4c4d2c3c9ade5da9347534e290305c3b9760f627: blobmsg_parse_array: ... (-1) 34 | crash-5e9937b197c88bf4e7b7ee2612456cad4cb83f5b: blobmsg_parse: ... (-1) 35 | crash-5e9937b197c88bf4e7b7ee2612456cad4cb83f5b: blobmsg_parse_array: ... (-1) 36 | crash-5e9937b197c88bf4e7b7ee2612456cad4cb83f5b: blobmsg_parse: ... (-1) 37 | crash-5e9937b197c88bf4e7b7ee2612456cad4cb83f5b: blobmsg_parse_array: ... (-1) 38 | crash-75b146c4e6fac64d3e62236b27c64b50657bab2a: blobmsg_parse: ... (-1) 39 | crash-75b146c4e6fac64d3e62236b27c64b50657bab2a: blobmsg_parse_array: ... (-1) 40 | crash-75b146c4e6fac64d3e62236b27c64b50657bab2a: blobmsg_parse: ... (-1) 41 | crash-75b146c4e6fac64d3e62236b27c64b50657bab2a: blobmsg_parse_array: ... (-1) 42 | crash-813f3e68661da09c26d4a87dbb9d5099e92be50f: blobmsg_parse: ... (-1) 43 | crash-813f3e68661da09c26d4a87dbb9d5099e92be50f: blobmsg_parse_array: ... (-1) 44 | crash-813f3e68661da09c26d4a87dbb9d5099e92be50f: blobmsg_parse: ... (-1) 45 | crash-813f3e68661da09c26d4a87dbb9d5099e92be50f: blobmsg_parse_array: ... (-1) 46 | crash-98595faa58ba01d85ba4fd0b109cd3d490b45795: blobmsg_parse: ... (0) 47 | crash-98595faa58ba01d85ba4fd0b109cd3d490b45795: blobmsg_parse_array: ... (0) 48 | crash-98595faa58ba01d85ba4fd0b109cd3d490b45795: blobmsg_parse: ... (0) 49 | crash-98595faa58ba01d85ba4fd0b109cd3d490b45795: blobmsg_parse_array: ... (0) 50 | crash-a3585b70f1c7ffbdec10f6dadc964336118485c4: blobmsg_parse: ... (-1) 51 | crash-a3585b70f1c7ffbdec10f6dadc964336118485c4: blobmsg_parse_array: ... (-1) 52 | crash-a3585b70f1c7ffbdec10f6dadc964336118485c4: blobmsg_parse: ... (-1) 53 | crash-a3585b70f1c7ffbdec10f6dadc964336118485c4: blobmsg_parse_array: ... (-1) 54 | crash-b3585b70f1c7ffbdec10f6dadc964336118485c4: blobmsg_parse: ... (-1) 55 | crash-b3585b70f1c7ffbdec10f6dadc964336118485c4: blobmsg_parse_array: ... (-1) 56 | crash-b3585b70f1c7ffbdec10f6dadc964336118485c4: blobmsg_parse: ... (-1) 57 | crash-b3585b70f1c7ffbdec10f6dadc964336118485c4: blobmsg_parse_array: ... (-1) 58 | crash-d0f3aa7d60a094b021f635d4edb7807c055a4ea1: blobmsg_parse: ... (0) 59 | crash-d0f3aa7d60a094b021f635d4edb7807c055a4ea1: blobmsg_parse_array: ... (0) 60 | crash-d0f3aa7d60a094b021f635d4edb7807c055a4ea1: blobmsg_parse: ... (0) 61 | crash-d0f3aa7d60a094b021f635d4edb7807c055a4ea1: blobmsg_parse_array: ... (0) 62 | crash-df9d1243057b27bbad6211e5a23d1cb699028aa2: blobmsg_parse: ... (-1) 63 | crash-df9d1243057b27bbad6211e5a23d1cb699028aa2: blobmsg_parse_array: ... (0) 64 | crash-df9d1243057b27bbad6211e5a23d1cb699028aa2: blobmsg_parse: ... (-1) 65 | crash-df9d1243057b27bbad6211e5a23d1cb699028aa2: blobmsg_parse_array: ... (0) 66 | crash-e0f8ecc694d96a09a1fced27b2a0838b670d34a0: blobmsg_parse: ... (0) 67 | crash-e0f8ecc694d96a09a1fced27b2a0838b670d34a0: blobmsg_parse_array: ... (0) 68 | crash-e0f8ecc694d96a09a1fced27b2a0838b670d34a0: blobmsg_parse: ... (0) 69 | crash-e0f8ecc694d96a09a1fced27b2a0838b670d34a0: blobmsg_parse_array: ... (0) 70 | crash-e2fd5ecb3b37926743256f1083f47a07c39e10c2: blobmsg_parse: ... (-1) 71 | crash-e2fd5ecb3b37926743256f1083f47a07c39e10c2: blobmsg_parse_array: ... (-1) 72 | crash-e2fd5ecb3b37926743256f1083f47a07c39e10c2: blobmsg_parse: ... (-1) 73 | crash-e2fd5ecb3b37926743256f1083f47a07c39e10c2: blobmsg_parse_array: ... (-1) 74 | valid-blobmsg.bin: blobmsg_parse: MLT (0) 75 | valid-blobmsg.bin: blobmsg_parse_array: MLT (0) 76 | valid-blobmsg.bin: blobmsg_parse: MLT (0) 77 | valid-blobmsg.bin: blobmsg_parse_array: MLT (0) 78 | -------------------------------------------------------------------------------- /uloop-kqueue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * uloop - event loop implementation 3 | * 4 | * Copyright (C) 2010-2016 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | static int uloop_init_pollfd(void) 19 | { 20 | struct timespec timeout = { 0, 0 }; 21 | struct kevent ev = {}; 22 | 23 | if (poll_fd >= 0) 24 | return 0; 25 | 26 | poll_fd = kqueue(); 27 | if (poll_fd < 0) 28 | return -1; 29 | 30 | EV_SET(&ev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); 31 | kevent(poll_fd, &ev, 1, NULL, 0, &timeout); 32 | 33 | return 0; 34 | } 35 | 36 | 37 | static uint16_t get_flags(unsigned int flags, unsigned int mask) 38 | { 39 | uint16_t kflags = 0; 40 | 41 | if (!(flags & mask)) 42 | return EV_DELETE; 43 | 44 | kflags = EV_ADD; 45 | if (flags & ULOOP_EDGE_TRIGGER) 46 | kflags |= EV_CLEAR; 47 | 48 | return kflags; 49 | } 50 | 51 | static struct kevent events[ULOOP_MAX_EVENTS]; 52 | 53 | static int register_kevent(struct uloop_fd *fd, unsigned int flags) 54 | { 55 | struct timespec timeout = { 0, 0 }; 56 | struct kevent ev[2]; 57 | int nev = 0; 58 | unsigned int fl = 0; 59 | unsigned int changed; 60 | uint16_t kflags; 61 | 62 | if (flags & ULOOP_EDGE_DEFER) 63 | flags &= ~ULOOP_EDGE_TRIGGER; 64 | 65 | changed = flags ^ fd->flags; 66 | if (changed & ULOOP_EDGE_TRIGGER) 67 | changed |= flags; 68 | 69 | if (!changed) 70 | return 0; 71 | 72 | if (changed & ULOOP_READ) { 73 | kflags = get_flags(flags, ULOOP_READ); 74 | EV_SET(&ev[nev++], fd->fd, EVFILT_READ, kflags, 0, 0, fd); 75 | } 76 | 77 | if (changed & ULOOP_WRITE) { 78 | kflags = get_flags(flags, ULOOP_WRITE); 79 | EV_SET(&ev[nev++], fd->fd, EVFILT_WRITE, kflags, 0, 0, fd); 80 | } 81 | 82 | if (!flags) 83 | fl |= EV_DELETE; 84 | 85 | if (kevent(poll_fd, ev, nev, NULL, fl, &timeout) == -1) 86 | return -1; 87 | 88 | return 0; 89 | } 90 | 91 | static int register_poll(struct uloop_fd *fd, unsigned int flags) 92 | { 93 | if (flags & ULOOP_EDGE_TRIGGER) 94 | flags |= ULOOP_EDGE_DEFER; 95 | else 96 | flags &= ~ULOOP_EDGE_DEFER; 97 | 98 | return register_kevent(fd, flags); 99 | } 100 | 101 | static int __uloop_fd_delete(struct uloop_fd *fd) 102 | { 103 | return register_poll(fd, 0); 104 | } 105 | 106 | static int64_t get_timestamp_us(void) 107 | { 108 | #ifdef CLOCK_MONOTONIC 109 | struct timespec ts = { 0, 0 }; 110 | 111 | clock_gettime(CLOCK_MONOTONIC, &ts); 112 | 113 | return ts.tv_sec * 1000000 + ts.tv_nsec / 1000; 114 | #else 115 | struct timeval tv = { 0, 0 }; 116 | 117 | gettimeofday(&tv, NULL); 118 | 119 | return tv.tv_sec * 1000000 + tv.tv_usec; 120 | #endif 121 | } 122 | 123 | static int uloop_fetch_events(int timeout) 124 | { 125 | struct timespec ts; 126 | int nfds, n; 127 | 128 | if (timeout >= 0) { 129 | ts.tv_sec = timeout / 1000; 130 | ts.tv_nsec = (timeout % 1000) * 1000000; 131 | } 132 | 133 | nfds = kevent(poll_fd, NULL, 0, events, ARRAY_SIZE(events), timeout >= 0 ? &ts : NULL); 134 | for (n = 0; n < nfds; n++) { 135 | if (events[n].filter == EVFILT_TIMER) { 136 | struct uloop_interval *tm = events[n].udata; 137 | 138 | tm->priv.time.fired = get_timestamp_us(); 139 | tm->expirations += events[n].data; 140 | tm->cb(tm); 141 | 142 | continue; 143 | } 144 | 145 | struct uloop_fd_event *cur = &cur_fds[n]; 146 | struct uloop_fd *u = events[n].udata; 147 | unsigned int ev = 0; 148 | 149 | cur->fd = u; 150 | if (!u) 151 | continue; 152 | 153 | if (events[n].flags & EV_ERROR) { 154 | u->error = true; 155 | if (!(u->flags & ULOOP_ERROR_CB)) 156 | uloop_fd_delete(u); 157 | } 158 | 159 | if(events[n].filter == EVFILT_READ) 160 | ev |= ULOOP_READ; 161 | else if (events[n].filter == EVFILT_WRITE) 162 | ev |= ULOOP_WRITE; 163 | 164 | if (events[n].flags & EV_EOF) 165 | u->eof = true; 166 | else if (!ev) 167 | cur->fd = NULL; 168 | 169 | cur->events = ev; 170 | if (u->flags & ULOOP_EDGE_DEFER) { 171 | u->flags &= ~ULOOP_EDGE_DEFER; 172 | u->flags |= ULOOP_EDGE_TRIGGER; 173 | register_kevent(u, u->flags); 174 | } 175 | } 176 | return nfds; 177 | } 178 | 179 | static int timer_register(struct uloop_interval *tm, unsigned int msecs) 180 | { 181 | struct kevent ev; 182 | 183 | tm->priv.time.msecs = msecs; 184 | tm->priv.time.fired = get_timestamp_us(); 185 | 186 | EV_SET(&ev, (uintptr_t)tm, EVFILT_TIMER, EV_ADD, NOTE_USECONDS, msecs * 1000, tm); 187 | 188 | return kevent(poll_fd, &ev, 1, NULL, 0, NULL); 189 | } 190 | 191 | static int timer_remove(struct uloop_interval *tm) 192 | { 193 | struct kevent ev; 194 | 195 | EV_SET(&ev, (uintptr_t)tm, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); 196 | 197 | return kevent(poll_fd, &ev, 1, NULL, 0, NULL); 198 | } 199 | 200 | static int64_t timer_next(struct uloop_interval *tm) 201 | { 202 | int64_t t1 = tm->priv.time.fired; 203 | int64_t t2 = get_timestamp_us(); 204 | 205 | while (t1 < t2) 206 | t1 += tm->priv.time.msecs * 1000; 207 | 208 | return (t1 - t2) / 1000; 209 | } 210 | -------------------------------------------------------------------------------- /tests/cram/test_blob_parse.t: -------------------------------------------------------------------------------- 1 | check that blob_parse is producing expected results: 2 | 3 | $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" 4 | $ export TEST_INPUTS="$TESTDIR/inputs" 5 | $ export FUZZ_CORPUS="$TESTDIR/../fuzz/corpus" 6 | 7 | $ valgrind --quiet --leak-check=full test-blob-parse $TEST_INPUTS/key-build.ucert 8 | === CHAIN ELEMENT 01 === 9 | signature: 10 | --- 11 | untrusted comment: signed by key 84bfc88a17166577 12 | RWSEv8iKFxZld+bQ+NTqCdDlHOuVYNw5Qw7Q8shjfMgFJcTqrzaqO0bysjIQhTadmcwvWiWvHlyMcwAXSix2BYdfghz/zhDjvgU= 13 | --- 14 | payload: 15 | --- 16 | "ucert": { 17 | \t"certtype": 1, (esc) 18 | \t"validfrom": 1546188410, (esc) 19 | \t"expiresat": 1577724410, (esc) 20 | \t"pubkey": "untrusted comment: Local build key\\nRWSEv8iKFxZld6vicE1icWhYNfEV9PM7C9MKUKl+YNEKB+PdAWGDF5Z9\\n" (esc) 21 | } 22 | --- 23 | 24 | $ valgrind --quiet --leak-check=full test-blob-parse $TEST_INPUTS/signature.ucert 25 | === CHAIN ELEMENT 01 === 26 | signature: 27 | --- 28 | untrusted comment: signed by key ca85add129e64bab 29 | RWTKha3RKeZLq0Sb8kCH9p/3BcFFud8rJnZiRICyRNhjbbpeZSwO2VhkwGaMd7ujW2/YSvT3O67pB0QguV6czgpP5kLX4AKBaQ4= 30 | --- 31 | payload: 32 | --- 33 | "ucert": { 34 | \t"certtype": 1, (esc) 35 | \t"validfrom": 1588532405, (esc) 36 | \t"expiresat": 1620068405, (esc) 37 | \t"pubkey": "untrusted comment: Local build key\\nRWTKha3RKeZLq1EaPsqvnXu+FqLMHZIS7nvDgwjpRo69j+th6eihGvQo\\n" (esc) 38 | } 39 | --- 40 | === CHAIN ELEMENT 02 === 41 | signature: 42 | --- 43 | untrusted comment: signed by key ca85add129e64bab 44 | RWTKha3RKeZLq9VW9CIMyumCQ4J0iFPLQYXr/YvUhw0OTrwpSh2XpKaRZQNZCXfO8ooMOCvG2TPor2veDjskHP1R2RGPIHp57wA= 45 | --- 46 | 47 | $ valgrind --quiet --leak-check=full test-blob-parse $TEST_INPUTS/invalid.ucert 48 | cannot parse cert invalid.ucert 49 | 50 | $ test-blob-parse-san $TEST_INPUTS/key-build.ucert 51 | === CHAIN ELEMENT 01 === 52 | signature: 53 | --- 54 | untrusted comment: signed by key 84bfc88a17166577 55 | RWSEv8iKFxZld+bQ+NTqCdDlHOuVYNw5Qw7Q8shjfMgFJcTqrzaqO0bysjIQhTadmcwvWiWvHlyMcwAXSix2BYdfghz/zhDjvgU= 56 | --- 57 | payload: 58 | --- 59 | "ucert": { 60 | \t"certtype": 1, (esc) 61 | \t"validfrom": 1546188410, (esc) 62 | \t"expiresat": 1577724410, (esc) 63 | \t"pubkey": "untrusted comment: Local build key\\nRWSEv8iKFxZld6vicE1icWhYNfEV9PM7C9MKUKl+YNEKB+PdAWGDF5Z9\\n" (esc) 64 | } 65 | --- 66 | 67 | $ test-blob-parse-san $TEST_INPUTS/signature.ucert 68 | === CHAIN ELEMENT 01 === 69 | signature: 70 | --- 71 | untrusted comment: signed by key ca85add129e64bab 72 | RWTKha3RKeZLq0Sb8kCH9p/3BcFFud8rJnZiRICyRNhjbbpeZSwO2VhkwGaMd7ujW2/YSvT3O67pB0QguV6czgpP5kLX4AKBaQ4= 73 | --- 74 | payload: 75 | --- 76 | "ucert": { 77 | \t"certtype": 1, (esc) 78 | \t"validfrom": 1588532405, (esc) 79 | \t"expiresat": 1620068405, (esc) 80 | \t"pubkey": "untrusted comment: Local build key\\nRWTKha3RKeZLq1EaPsqvnXu+FqLMHZIS7nvDgwjpRo69j+th6eihGvQo\\n" (esc) 81 | } 82 | --- 83 | === CHAIN ELEMENT 02 === 84 | signature: 85 | --- 86 | untrusted comment: signed by key ca85add129e64bab 87 | RWTKha3RKeZLq9VW9CIMyumCQ4J0iFPLQYXr/YvUhw0OTrwpSh2XpKaRZQNZCXfO8ooMOCvG2TPor2veDjskHP1R2RGPIHp57wA= 88 | --- 89 | 90 | $ test-blob-parse-san $TEST_INPUTS/invalid.ucert 91 | cannot parse cert invalid.ucert 92 | 93 | $ for blob in $(LC_ALL=C find $FUZZ_CORPUS -type f | sort ); do 94 | > valgrind --quiet --leak-check=full test-blob-parse $blob; \ 95 | > test-blob-parse-san $blob; \ 96 | > done 97 | cannot parse cert 71520a5c4b5ca73903216857abbad54a8002d44a 98 | cannot parse cert 71520a5c4b5ca73903216857abbad54a8002d44a 99 | cannot parse cert c1dfd96eea8cc2b62785275bca38ac261256e278 100 | cannot parse cert c1dfd96eea8cc2b62785275bca38ac261256e278 101 | cannot parse cert c42ac1c46f1d4e211c735cc7dfad4ff8391110e9 102 | cannot parse cert c42ac1c46f1d4e211c735cc7dfad4ff8391110e9 103 | cannot parse cert crash-1b8fb1be45db3aff7699100f497fb74138f3df4f 104 | cannot parse cert crash-1b8fb1be45db3aff7699100f497fb74138f3df4f 105 | cannot parse cert crash-333757b203a44751d3535f24b05f467183a96d09 106 | cannot parse cert crash-333757b203a44751d3535f24b05f467183a96d09 107 | cannot parse cert crash-4c4d2c3c9ade5da9347534e290305c3b9760f627 108 | cannot parse cert crash-4c4d2c3c9ade5da9347534e290305c3b9760f627 109 | cannot parse cert crash-5e9937b197c88bf4e7b7ee2612456cad4cb83f5b 110 | cannot parse cert crash-5e9937b197c88bf4e7b7ee2612456cad4cb83f5b 111 | cannot parse cert crash-75b146c4e6fac64d3e62236b27c64b50657bab2a 112 | cannot parse cert crash-75b146c4e6fac64d3e62236b27c64b50657bab2a 113 | cannot parse cert crash-813f3e68661da09c26d4a87dbb9d5099e92be50f 114 | cannot parse cert crash-813f3e68661da09c26d4a87dbb9d5099e92be50f 115 | cannot parse cert crash-98595faa58ba01d85ba4fd0b109cd3d490b45795 116 | cannot parse cert crash-98595faa58ba01d85ba4fd0b109cd3d490b45795 117 | cannot parse cert crash-a3585b70f1c7ffbdec10f6dadc964336118485c4 118 | cannot parse cert crash-a3585b70f1c7ffbdec10f6dadc964336118485c4 119 | cannot parse cert crash-b3585b70f1c7ffbdec10f6dadc964336118485c4 120 | cannot parse cert crash-b3585b70f1c7ffbdec10f6dadc964336118485c4 121 | cannot parse cert crash-d0f3aa7d60a094b021f635d4edb7807c055a4ea1 122 | cannot parse cert crash-d0f3aa7d60a094b021f635d4edb7807c055a4ea1 123 | cannot parse cert crash-df9d1243057b27bbad6211e5a23d1cb699028aa2 124 | cannot parse cert crash-df9d1243057b27bbad6211e5a23d1cb699028aa2 125 | cannot parse cert crash-e0f8ecc694d96a09a1fced27b2a0838b670d34a0 126 | cannot parse cert crash-e0f8ecc694d96a09a1fced27b2a0838b670d34a0 127 | cannot parse cert crash-e2fd5ecb3b37926743256f1083f47a07c39e10c2 128 | cannot parse cert crash-e2fd5ecb3b37926743256f1083f47a07c39e10c2 129 | cannot parse cert valid-blobmsg.bin 130 | cannot parse cert valid-blobmsg.bin 131 | -------------------------------------------------------------------------------- /udebug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * udebug - debug ring buffer library 3 | * 4 | * Copyright (C) 2023 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #ifndef __UDEBUG_RINGBUF_H 19 | #define __UDEBUG_RINGBUF_H 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "list.h" 26 | #include "uloop.h" 27 | #include "avl.h" 28 | 29 | #define UDEBUG_SOCK_NAME "/var/run/udebug.sock" 30 | 31 | enum udebug_format { 32 | UDEBUG_FORMAT_PACKET, 33 | UDEBUG_FORMAT_STRING, 34 | UDEBUG_FORMAT_BLOBMSG, 35 | }; 36 | 37 | enum { 38 | UDEBUG_DLT_ETHERNET = 1, 39 | UDEBUG_DLT_PPP = 50, 40 | UDEBUG_DLT_RAW_IP = 101, 41 | UDEBUG_DLT_IEEE_802_11 = 105, 42 | UDEBUG_DLT_IEEE_802_11_RADIOTAP = 127, 43 | UDEBUG_DLT_NETLINK = 253, 44 | }; 45 | 46 | enum udebug_meta_type { 47 | UDEBUG_META_IFACE_NAME, 48 | UDEBUG_META_IFACE_DESC, 49 | __UDEBUG_META_MAX 50 | }; 51 | 52 | #define UDEBUG_TS_MSEC 1000ULL 53 | #define UDEBUG_TS_SEC (1000ULL * UDEBUG_TS_MSEC) 54 | 55 | struct udebug; 56 | struct udebug_hdr; 57 | 58 | struct udebug_buf_flag { 59 | const char *name; 60 | uint64_t mask; 61 | }; 62 | 63 | struct udebug_buf_meta { 64 | const char *name; 65 | enum udebug_format format; 66 | uint32_t sub_format; /* linktype for UDEBUG_FORMAT_PACKET */ 67 | const struct udebug_buf_flag *flags; 68 | unsigned int n_flags; 69 | }; 70 | 71 | struct udebug_buf { 72 | struct udebug *ctx; 73 | const struct udebug_buf_meta *meta; 74 | uint32_t id; 75 | 76 | struct list_head list; 77 | 78 | struct udebug_hdr *hdr; 79 | void *data; 80 | size_t data_size; 81 | size_t head_size; 82 | size_t ring_size; 83 | int fd; 84 | }; 85 | 86 | struct udebug_packet_info { 87 | const char *attr[__UDEBUG_META_MAX]; 88 | }; 89 | 90 | struct udebug_remote_buf { 91 | struct avl_node node; 92 | struct udebug_buf buf; 93 | bool poll; 94 | uint32_t head; 95 | 96 | /* provided by user */ 97 | uint32_t pcap_iface; 98 | void *priv; 99 | const struct udebug_packet_info *meta; 100 | }; 101 | 102 | struct udebug { 103 | struct list_head local_rings; 104 | struct avl_tree remote_rings; 105 | uint32_t next_id; 106 | struct uloop_fd fd; 107 | int poll_handle; 108 | char *socket_path; 109 | struct uloop_timeout reconnect; 110 | 111 | /* filled by user */ 112 | void (*notify_cb)(struct udebug *ctx, struct udebug_remote_buf *rb); 113 | }; 114 | 115 | struct udebug_ptr { 116 | uint32_t start; 117 | uint32_t len; 118 | uint64_t timestamp; 119 | }; 120 | 121 | struct udebug_snapshot { 122 | struct udebug_ptr *entries; 123 | unsigned int n_entries; 124 | unsigned int dropped; 125 | void *data; 126 | size_t data_size; 127 | 128 | uint32_t iter_idx; 129 | 130 | enum udebug_format format; 131 | uint32_t sub_format; 132 | 133 | uint32_t rbuf_idx; 134 | }; 135 | 136 | struct udebug_iter { 137 | struct udebug_snapshot **list; 138 | size_t n; 139 | 140 | struct udebug_snapshot *s; 141 | unsigned int s_idx; 142 | 143 | uint64_t timestamp; 144 | void *data; 145 | size_t len; 146 | }; 147 | 148 | uint64_t udebug_timestamp(void); 149 | 150 | void udebug_entry_init_ts(struct udebug_buf *buf, uint64_t timestamp); 151 | static inline void udebug_entry_init(struct udebug_buf *buf) 152 | { 153 | udebug_entry_init_ts(buf, udebug_timestamp()); 154 | } 155 | void *udebug_entry_append(struct udebug_buf *buf, const void *data, uint32_t len); 156 | int udebug_entry_printf(struct udebug_buf *buf, const char *fmt, ...) 157 | __attribute__ ((format (printf, 2, 3))); 158 | int udebug_entry_vprintf(struct udebug_buf *buf, const char *fmt, va_list ap) 159 | __attribute__ ((format (printf, 2, 0))); 160 | uint16_t udebug_entry_trim(struct udebug_buf *buf, uint16_t len); 161 | void udebug_entry_set_length(struct udebug_buf *buf, uint16_t len); 162 | void udebug_entry_add(struct udebug_buf *buf); 163 | 164 | int udebug_buf_init(struct udebug_buf *buf, size_t entries, size_t size); 165 | int udebug_buf_add(struct udebug *ctx, struct udebug_buf *buf, 166 | const struct udebug_buf_meta *meta); 167 | uint64_t udebug_buf_flags(struct udebug_buf *buf); 168 | void udebug_buf_free(struct udebug_buf *buf); 169 | static inline bool udebug_buf_valid(struct udebug_buf *buf) 170 | { 171 | return buf->hdr; 172 | } 173 | 174 | struct udebug_remote_buf *udebug_remote_buf_get(struct udebug *ctx, uint32_t id); 175 | int udebug_remote_buf_map(struct udebug *ctx, struct udebug_remote_buf *rb, uint32_t id); 176 | void udebug_remote_buf_unmap(struct udebug *ctx, struct udebug_remote_buf *rb); 177 | int udebug_remote_buf_set_poll(struct udebug *ctx, struct udebug_remote_buf *rb, bool val); 178 | void udebug_remote_buf_set_flags(struct udebug_remote_buf *rb, uint64_t mask, uint64_t set); 179 | struct udebug_snapshot *udebug_remote_buf_snapshot(struct udebug_remote_buf *rb); 180 | bool udebug_snapshot_get_entry(struct udebug_snapshot *s, struct udebug_iter *it, unsigned int entry); 181 | 182 | void udebug_remote_buf_set_start_time(struct udebug_remote_buf *rb, uint64_t ts); 183 | void udebug_remote_buf_set_start_offset(struct udebug_remote_buf *rb, uint32_t idx); 184 | 185 | void udebug_iter_start(struct udebug_iter *it, struct udebug_snapshot **s, size_t n); 186 | bool udebug_iter_next(struct udebug_iter *it); 187 | 188 | void udebug_init(struct udebug *ctx); 189 | int udebug_connect(struct udebug *ctx, const char *path); 190 | void udebug_auto_connect(struct udebug *ctx, const char *path); 191 | void udebug_add_uloop(struct udebug *ctx); 192 | void udebug_poll(struct udebug *ctx); 193 | void udebug_free(struct udebug *ctx); 194 | 195 | static inline bool udebug_is_connected(struct udebug *ctx) 196 | { 197 | return ctx->fd.fd >= 0; 198 | } 199 | 200 | int udebug_id_cmp(const void *k1, const void *k2, void *ptr); 201 | 202 | #endif 203 | -------------------------------------------------------------------------------- /examples/json_script-tests.sh: -------------------------------------------------------------------------------- 1 | JSON_SCRIPT=tests.json 2 | JSON_SCRIPT_BIN=./json_script-example 3 | FILE_STDOUT=tests.stdout 4 | FILE_STDERR=tests.stderr 5 | FILE_EXPECTED=tests.expected 6 | 7 | call_json_script() { 8 | #export LD_PRELOAD=../libjson_script.so 9 | $JSON_SCRIPT_BIN "$@" "$JSON_SCRIPT" >"$FILE_STDOUT" 2>"$FILE_STDERR" 10 | } 11 | 12 | assertStdioEquals() { 13 | local expected="$1" 14 | local file_stdio="$2" 15 | 16 | echo "$expected" >"$FILE_EXPECTED" 17 | if [ -z "$expected" ]; then 18 | # we are expecting empty output, but we deliberately added a newline 19 | # with echo above, so adding another echo to compensate for that 20 | echo >>"$file_stdio" 21 | fi 22 | diff -up "$FILE_EXPECTED" "$file_stdio" >/dev/null 2>&1 || { 23 | cat >&2 <"$JSON_SCRIPT" <<-EOF 44 | [ 45 | [ ] 46 | [ ] 47 | ] 48 | EOF 49 | call_json_script 50 | assertStderrEquals "load JSON data from $JSON_SCRIPT failed." 51 | } 52 | 53 | test_expr_eq() { 54 | cat >"$JSON_SCRIPT" <<-EOF 55 | [ 56 | [ "if", 57 | [ "eq", "VAR", "foo" ], 58 | [ "echo", "bar" ], 59 | [ "echo", "baz" ] 60 | ] 61 | ] 62 | EOF 63 | call_json_script "VAR=foo" 64 | assertStdoutEquals "echo bar" 65 | call_json_script "VAR=xxx" 66 | assertStdoutEquals "echo baz" 67 | } 68 | 69 | test_expr_has() { 70 | cat >"$JSON_SCRIPT" <<-EOF 71 | [ 72 | [ "if", 73 | [ "has", "VAR" ], 74 | [ "echo", "bar" ], 75 | [ "echo", "baz" ] 76 | ] 77 | ] 78 | EOF 79 | call_json_script "VAR=foo" 80 | assertStdoutEquals "echo bar" 81 | call_json_script 82 | assertStdoutEquals "echo baz" 83 | } 84 | 85 | test_expr_regex_single() { 86 | cat >"$JSON_SCRIPT" <<-EOF 87 | [ 88 | [ "if", 89 | [ "regex", "VAR", ".ell." ], 90 | [ "echo", "bar" ], 91 | [ "echo", "baz" ] 92 | ] 93 | ] 94 | EOF 95 | call_json_script "VAR=hello" 96 | assertStdoutEquals "echo bar" 97 | call_json_script "VAR=.ell." 98 | assertStdoutEquals "echo bar" 99 | call_json_script 100 | assertStdoutEquals "echo baz" 101 | call_json_script "VAR=" 102 | assertStdoutEquals "echo baz" 103 | call_json_script "VAR=hell" 104 | assertStdoutEquals "echo baz" 105 | } 106 | 107 | test_expr_regex_multi() { 108 | cat >"$JSON_SCRIPT" <<-EOF 109 | [ 110 | [ "if", 111 | [ "regex", "VAR", [ ".ell.", "w.rld" ] ], 112 | [ "echo", "bar" ], 113 | [ "echo", "baz" ] 114 | ] 115 | ] 116 | EOF 117 | call_json_script "VAR=hello" 118 | assertStdoutEquals "echo bar" 119 | call_json_script "VAR=world" 120 | assertStdoutEquals "echo bar" 121 | call_json_script "VAR=.ell." 122 | assertStdoutEquals "echo bar" 123 | call_json_script "VAR=w.rld" 124 | assertStdoutEquals "echo bar" 125 | call_json_script 126 | assertStdoutEquals "echo baz" 127 | call_json_script "VAR=" 128 | assertStdoutEquals "echo baz" 129 | call_json_script "VAR=hell" 130 | assertStdoutEquals "echo baz" 131 | } 132 | 133 | test_expr_not() { 134 | cat >"$JSON_SCRIPT" <<-EOF 135 | [ 136 | [ "if", 137 | [ "not", [ "has", "VAR" ] ], 138 | [ "echo", "bar" ], 139 | [ "echo", "baz" ] 140 | ] 141 | ] 142 | EOF 143 | call_json_script "VAR=foo" 144 | assertStdoutEquals "echo baz" 145 | call_json_script 146 | assertStdoutEquals "echo bar" 147 | } 148 | 149 | test_expr_and() { 150 | cat >"$JSON_SCRIPT" <<-EOF 151 | [ 152 | [ "if", 153 | [ "and", [ "eq", "EQVAR", "eqval" ], 154 | [ "regex", "REGEXVAR", "regex..." ] 155 | ], 156 | [ "echo", "bar" ], 157 | [ "echo", "baz" ] 158 | ] 159 | ] 160 | EOF 161 | call_json_script "EQVAR=eqval" "REGEXVAR=regexval" 162 | assertStdoutEquals "echo bar" 163 | call_json_script "EQVAR=foo" 164 | assertStdoutEquals "echo baz" 165 | call_json_script "REGEXVAR=regex***" 166 | assertStdoutEquals "echo baz" 167 | call_json_script 168 | assertStdoutEquals "echo baz" 169 | } 170 | 171 | test_expr_or() { 172 | cat >"$JSON_SCRIPT" <<-EOF 173 | [ 174 | [ "if", 175 | [ "or", [ "not", [ "eq", "EQVAR", "eqval" ] ], 176 | [ "regex", "REGEXVAR", [ "regexva.[0-9]", "regexva.[a-z]" ] ] 177 | ], 178 | [ "echo", "bar" ], 179 | [ "echo", "baz" ] 180 | ] 181 | ] 182 | EOF 183 | call_json_script "EQVAR=eqval" "REGEXVAR=regexval1" 184 | assertStdoutEquals "echo bar" 185 | call_json_script "EQVAR=neq" "REGEXVAR=sxc" 186 | assertStdoutEquals "echo bar" 187 | call_json_script "REGEXVAR=sxc" 188 | assertStdoutEquals "echo bar" 189 | call_json_script "EQVAR=foo" 190 | assertStdoutEquals "echo bar" 191 | call_json_script 192 | assertStdoutEquals "echo bar" 193 | call_json_script "EQVAR=eqval" "REGEXVAR=regexval" 194 | assertStdoutEquals "echo baz" 195 | } 196 | 197 | test_expr_isdir() { 198 | cat >"$JSON_SCRIPT" <<-EOF 199 | [ 200 | [ "if", 201 | [ "isdir", "%VAR%" ], 202 | [ "echo", "bar" ], 203 | [ "echo", "baz" ] 204 | ] 205 | ] 206 | EOF 207 | call_json_script "VAR=/" 208 | assertStdoutEquals "echo bar" 209 | call_json_script "VAR=$(mktemp -u)" 210 | assertStdoutEquals "echo baz" 211 | call_json_script 212 | assertStdoutEquals "echo baz" 213 | } 214 | 215 | test_cmd_case() { 216 | cat >"$JSON_SCRIPT" <<-EOF 217 | [ 218 | [ "case", "CASEVAR", { 219 | "0": [ "echo", "foo" ], 220 | "1": [ 221 | [ "echo", "bar" ], 222 | [ "echo", "baz" ] 223 | ], 224 | "%VAR%": [ "echo", "quz" ] 225 | } ] 226 | ] 227 | EOF 228 | call_json_script "CASEVAR=0" 229 | assertStdoutEquals "echo foo" 230 | call_json_script "CASEVAR=1" 231 | assertStdoutEquals "echo bar 232 | echo baz" 233 | call_json_script "CASEVAR=%VAR%" 234 | assertStdoutEquals "echo quz" 235 | call_json_script "CASEVAR=" 236 | assertStdoutEquals "" 237 | call_json_script 238 | assertStdoutEquals "" 239 | call_json_script "CASEVAR=xxx" "VAR=xxx" 240 | assertStdoutEquals "" 241 | } 242 | 243 | test_cmd_if() { 244 | cat >"$JSON_SCRIPT" <<-EOF 245 | [ 246 | [ "if", 247 | [ "eq", "VAR", "foo" ], 248 | [ "echo", "bar" ], 249 | [ "echo", "baz" ] 250 | ] 251 | ] 252 | EOF 253 | call_json_script "VAR=foo" 254 | assertStdoutEquals "echo bar" 255 | call_json_script "VAR=xxx" 256 | assertStdoutEquals "echo baz" 257 | } 258 | 259 | test_cmd_cb() { 260 | cat >"$JSON_SCRIPT" <<-EOF 261 | [ 262 | [ "exec", "%VAR%", "/%VAS%%%/" ] 263 | ] 264 | EOF 265 | call_json_script 266 | assertStdoutEquals "exec /%/" 267 | call_json_script "VAR=" 268 | assertStdoutEquals "exec /%/" 269 | call_json_script "VAR=qux" "VAS=3" 270 | assertStdoutEquals "exec qux /3%/" 271 | } 272 | 273 | test_cmd_return() { 274 | cat >"$JSON_SCRIPT" <<-EOF 275 | [ 276 | [ "heh", "%HEHVAR%" ], 277 | [ "%VAR%", "%VAR%" ], 278 | [ "return" ], 279 | [ "exec_non_reachable", "Arghhh" ] 280 | ] 281 | EOF 282 | call_json_script "HEHVAR=dude" "VAR=ow" 283 | assertStdoutEquals "heh dude 284 | %VAR% ow" 285 | } 286 | 287 | . ./shunit2 288 | -------------------------------------------------------------------------------- /ustream.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ustream - library for stream buffer management 3 | * 4 | * Copyright (C) 2012 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef __USTREAM_H 20 | #define __USTREAM_H 21 | 22 | #include 23 | #include "uloop.h" 24 | 25 | struct ustream; 26 | struct ustream_buf; 27 | 28 | enum read_blocked_reason { 29 | READ_BLOCKED_USER = (1 << 0), 30 | READ_BLOCKED_FULL = (1 << 1), 31 | }; 32 | 33 | struct ustream_buf_list { 34 | struct ustream_buf *head; 35 | struct ustream_buf *data_tail; 36 | struct ustream_buf *tail; 37 | 38 | int (*alloc)(struct ustream *s, struct ustream_buf_list *l); 39 | 40 | int data_bytes; 41 | 42 | int min_buffers; 43 | int max_buffers; 44 | int buffer_len; 45 | 46 | int buffers; 47 | }; 48 | 49 | struct ustream { 50 | struct ustream_buf_list r, w; 51 | struct uloop_timeout state_change; 52 | struct ustream *next; 53 | 54 | /* 55 | * notify_read: (optional) 56 | * called by the ustream core to notify that new data is available 57 | * for reading. 58 | * must not free the ustream from this callback 59 | */ 60 | void (*notify_read)(struct ustream *s, int bytes_new); 61 | 62 | /* 63 | * notify_write: (optional) 64 | * called by the ustream core to notify that some buffered data has 65 | * been written to the stream. 66 | * must not free the ustream from this callback 67 | */ 68 | void (*notify_write)(struct ustream *s, int bytes); 69 | 70 | /* 71 | * notify_state: (optional) 72 | * called by the ustream implementation to notify that the read 73 | * side of the stream is closed (eof is set) or there was a write 74 | * error (write_error is set). 75 | * will be called again after the write buffer has been emptied when 76 | * the read side has hit EOF. 77 | */ 78 | void (*notify_state)(struct ustream *s); 79 | 80 | /* 81 | * write: 82 | * must be defined by ustream implementation, accepts new write data. 83 | * 'more' is used to indicate that a subsequent call will provide more 84 | * data (useful for aggregating writes) 85 | * returns the number of bytes accepted, or -1 if no more writes can 86 | * be accepted (link error) 87 | */ 88 | int (*write)(struct ustream *s, const char *buf, int len, bool more); 89 | 90 | /* 91 | * free: (optional) 92 | * defined by ustream implementation, tears down the ustream and frees data 93 | */ 94 | void (*free)(struct ustream *s); 95 | 96 | /* 97 | * set_read_blocked: (optional) 98 | * defined by ustream implementation, called when the read_blocked flag 99 | * changes 100 | */ 101 | void (*set_read_blocked)(struct ustream *s); 102 | 103 | /* 104 | * poll: (optional) 105 | * defined by the upstream implementation, called to request polling for 106 | * available data. 107 | * returns true if data was fetched. 108 | */ 109 | bool (*poll)(struct ustream *s); 110 | 111 | /* 112 | * ustream user should set this if the input stream is expected 113 | * to contain string data. the core will keep all data 0-terminated. 114 | */ 115 | bool string_data; 116 | bool write_error; 117 | bool eof; 118 | uint8_t pending_cb; 119 | 120 | enum read_blocked_reason read_blocked; 121 | }; 122 | 123 | struct ustream_fd { 124 | struct ustream stream; 125 | struct uloop_fd fd; 126 | }; 127 | 128 | struct ustream_buf { 129 | struct ustream_buf *next; 130 | 131 | char *data; 132 | char *tail; 133 | char *end; 134 | 135 | char head[]; 136 | }; 137 | 138 | /* ustream_fd_init: create a file descriptor ustream (uses uloop) */ 139 | void ustream_fd_init(struct ustream_fd *s, int fd); 140 | 141 | /* ustream_free: free all buffers and data associated with a ustream */ 142 | void ustream_free(struct ustream *s); 143 | 144 | /* ustream_consume: remove data from the head of the read buffer */ 145 | void ustream_consume(struct ustream *s, int len); 146 | 147 | /* 148 | * ustream_read: read and consume data in read buffer into caller-specified 149 | * area. Return length of data read. 150 | */ 151 | int ustream_read(struct ustream *s, char *buf, int buflen); 152 | /* ustream_write: add data to the write buffer */ 153 | int ustream_write(struct ustream *s, const char *buf, int len, bool more); 154 | int ustream_printf(struct ustream *s, const char *format, ...) 155 | __attribute__ ((format (printf, 2, 3))); 156 | int ustream_vprintf(struct ustream *s, const char *format, va_list arg) 157 | __attribute__ ((format (printf, 2, 0))); 158 | 159 | /* ustream_get_read_buf: get a pointer to the next read buffer data */ 160 | char *ustream_get_read_buf(struct ustream *s, int *buflen); 161 | 162 | /* 163 | * ustream_set_read_blocked: set read blocked state 164 | * 165 | * if set, the ustream will no longer fetch pending data. 166 | */ 167 | void ustream_set_read_blocked(struct ustream *s, bool set); 168 | 169 | static inline bool ustream_read_blocked(struct ustream *s) 170 | { 171 | return !!(s->read_blocked & READ_BLOCKED_USER); 172 | } 173 | 174 | static inline int ustream_pending_data(struct ustream *s, bool write) 175 | { 176 | struct ustream_buf_list *b = write ? &s->w : &s->r; 177 | return b->data_bytes; 178 | } 179 | 180 | static inline bool ustream_read_buf_full(struct ustream *s) 181 | { 182 | struct ustream_buf *buf = s->r.data_tail; 183 | return buf && buf->data == buf->head && buf->tail == buf->end && 184 | s->r.buffers == s->r.max_buffers; 185 | } 186 | 187 | /*** --- functions only used by ustream implementations --- ***/ 188 | 189 | /* ustream_init_defaults: fill default callbacks and options */ 190 | void ustream_init_defaults(struct ustream *s); 191 | 192 | /* 193 | * ustream_reserve: allocate rx buffer space 194 | * 195 | * len: hint for how much space is needed (not guaranteed to be met) 196 | * maxlen: pointer to where the actual buffer size is going to be stored 197 | */ 198 | char *ustream_reserve(struct ustream *s, int len, int *maxlen); 199 | 200 | /* ustream_fill_read: mark rx buffer space as filled */ 201 | void ustream_fill_read(struct ustream *s, int len); 202 | 203 | /* 204 | * ustream_write_pending: attempt to write more data from write buffers 205 | * returns true if all write buffers have been emptied. 206 | */ 207 | bool ustream_write_pending(struct ustream *s); 208 | 209 | static inline void ustream_state_change(struct ustream *s) 210 | { 211 | uloop_timeout_set(&s->state_change, 0); 212 | } 213 | 214 | static inline bool ustream_poll(struct ustream *s) 215 | { 216 | if (!s->poll) 217 | return false; 218 | 219 | return s->poll(s); 220 | } 221 | 222 | #endif 223 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2011 Felix Fietkau 3 | * Copyright (c) 2010 Isilon Systems, Inc. 4 | * Copyright (c) 2010 iX Systems, Inc. 5 | * Copyright (c) 2010 Panasas, Inc. 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice unmodified, this list of conditions, and the following 13 | * disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | #ifndef _LINUX_LIST_H_ 30 | #define _LINUX_LIST_H_ 31 | 32 | #include 33 | #include 34 | 35 | #define prefetch(x) 36 | 37 | #ifndef container_of 38 | #define container_of(ptr, type, member) \ 39 | ({ \ 40 | const __typeof__(((type *) NULL)->member) *__mptr = (ptr); \ 41 | (type *) ((char *) __mptr - offsetof(type, member)); \ 42 | }) 43 | #endif 44 | 45 | #ifndef container_of_safe 46 | #define container_of_safe(ptr, type, member) \ 47 | ({ \ 48 | const __typeof__(((type *) NULL)->member) *__mptr = (ptr); \ 49 | __mptr ? (type *)((char *) __mptr - offsetof(type, member)) : NULL; \ 50 | }) 51 | #endif 52 | 53 | struct list_head { 54 | struct list_head *next; 55 | struct list_head *prev; 56 | }; 57 | 58 | #define LIST_HEAD_INIT(name) { &(name), &(name) } 59 | #undef LIST_HEAD 60 | #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) 61 | 62 | static inline void 63 | INIT_LIST_HEAD(struct list_head *list) 64 | { 65 | list->next = list->prev = list; 66 | } 67 | 68 | static inline bool 69 | list_empty(const struct list_head *head) 70 | { 71 | return (head->next == head); 72 | } 73 | 74 | static inline bool 75 | list_is_first(const struct list_head *list, 76 | const struct list_head *head) 77 | { 78 | return list->prev == head; 79 | } 80 | 81 | static inline bool 82 | list_is_last(const struct list_head *list, 83 | const struct list_head *head) 84 | { 85 | return list->next == head; 86 | } 87 | 88 | static inline void 89 | _list_del(struct list_head *entry) 90 | { 91 | entry->next->prev = entry->prev; 92 | entry->prev->next = entry->next; 93 | } 94 | 95 | static inline void 96 | list_del(struct list_head *entry) 97 | { 98 | _list_del(entry); 99 | entry->next = entry->prev = NULL; 100 | } 101 | 102 | static inline void 103 | _list_add(struct list_head *_new, struct list_head *prev, 104 | struct list_head *next) 105 | { 106 | 107 | next->prev = _new; 108 | _new->next = next; 109 | _new->prev = prev; 110 | prev->next = _new; 111 | } 112 | 113 | static inline void 114 | list_del_init(struct list_head *entry) 115 | { 116 | _list_del(entry); 117 | INIT_LIST_HEAD(entry); 118 | } 119 | 120 | #define list_entry(ptr, type, field) container_of(ptr, type, field) 121 | #define list_first_entry(ptr, type, field) list_entry((ptr)->next, type, field) 122 | #define list_last_entry(ptr, type, field) list_entry((ptr)->prev, type, field) 123 | #define list_next_entry(pos, member) list_entry((pos)->member.next, typeof(*(pos)), member) 124 | #define list_entry_is_h(p, h, field) (&p->field == (h)) 125 | 126 | #define list_for_each(p, head) \ 127 | for (p = (head)->next; p != (head); p = p->next) 128 | 129 | #define list_for_each_safe(p, n, head) \ 130 | for (p = (head)->next, n = p->next; p != (head); p = n, n = p->next) 131 | 132 | #define list_for_each_entry(p, h, field) \ 133 | for (p = list_first_entry(h, __typeof__(*p), field); &p->field != (h); \ 134 | p = list_entry(p->field.next, __typeof__(*p), field)) 135 | 136 | #define list_for_each_entry_continue(p, h, field) \ 137 | for (p = list_next_entry(p, field); \ 138 | !list_entry_is_h(p, h, field); \ 139 | p = list_next_entry(p, field)) 140 | 141 | #define list_for_each_entry_continue_reverse(p, h, field) \ 142 | for (p = list_prev_entry(p, field); \ 143 | !list_entry_is_h(p, h, field); \ 144 | p = list_prev_entry(p, field)) 145 | 146 | #define list_for_each_entry_safe(p, n, h, field) \ 147 | for (p = list_first_entry(h, __typeof__(*p), field), \ 148 | n = list_entry(p->field.next, __typeof__(*p), field); &p->field != (h);\ 149 | p = n, n = list_entry(n->field.next, __typeof__(*n), field)) 150 | 151 | #define list_for_each_entry_reverse(p, h, field) \ 152 | for (p = list_last_entry(h, __typeof__(*p), field); &p->field != (h); \ 153 | p = list_entry(p->field.prev, __typeof__(*p), field)) 154 | 155 | #define list_for_each_prev(p, h) for (p = (h)->prev; p != (h); p = p->prev) 156 | #define list_for_each_prev_safe(p, n, h) for (p = (h)->prev, n = p->prev; p != (h); p = n, n = p->prev) 157 | 158 | static inline void 159 | list_add(struct list_head *_new, struct list_head *head) 160 | { 161 | _list_add(_new, head, head->next); 162 | } 163 | 164 | static inline void 165 | list_add_tail(struct list_head *_new, struct list_head *head) 166 | { 167 | _list_add(_new, head->prev, head); 168 | } 169 | 170 | static inline void 171 | list_move(struct list_head *list, struct list_head *head) 172 | { 173 | _list_del(list); 174 | list_add(list, head); 175 | } 176 | 177 | static inline void 178 | list_move_tail(struct list_head *entry, struct list_head *head) 179 | { 180 | _list_del(entry); 181 | list_add_tail(entry, head); 182 | } 183 | 184 | static inline void 185 | _list_splice(const struct list_head *list, struct list_head *prev, 186 | struct list_head *next) 187 | { 188 | struct list_head *first; 189 | struct list_head *last; 190 | 191 | if (list_empty(list)) 192 | return; 193 | 194 | first = list->next; 195 | last = list->prev; 196 | first->prev = prev; 197 | prev->next = first; 198 | last->next = next; 199 | next->prev = last; 200 | } 201 | 202 | static inline void 203 | list_splice(const struct list_head *list, struct list_head *head) 204 | { 205 | _list_splice(list, head, head->next); 206 | } 207 | 208 | static inline void 209 | list_splice_tail(struct list_head *list, struct list_head *head) 210 | { 211 | _list_splice(list, head->prev, head); 212 | } 213 | 214 | static inline void 215 | list_splice_init(struct list_head *list, struct list_head *head) 216 | { 217 | _list_splice(list, head, head->next); 218 | INIT_LIST_HEAD(list); 219 | } 220 | 221 | static inline void 222 | list_splice_tail_init(struct list_head *list, struct list_head *head) 223 | { 224 | _list_splice(list, head->prev, head); 225 | INIT_LIST_HEAD(list); 226 | } 227 | 228 | #endif /* _LINUX_LIST_H_ */ 229 | -------------------------------------------------------------------------------- /runqueue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * runqueue.c - a simple task queueing/completion tracking helper 3 | * 4 | * Copyright (C) 2013 Felix Fietkau 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include "runqueue.h" 22 | 23 | static void 24 | __runqueue_empty_cb(struct uloop_timeout *timeout) 25 | { 26 | struct runqueue *q = container_of(timeout, struct runqueue, timeout); 27 | 28 | q->empty_cb(q); 29 | } 30 | 31 | void runqueue_init(struct runqueue *q) 32 | { 33 | INIT_SAFE_LIST(&q->tasks_active); 34 | INIT_SAFE_LIST(&q->tasks_inactive); 35 | } 36 | 37 | static void __runqueue_start_next(struct uloop_timeout *timeout) 38 | { 39 | struct runqueue *q = container_of(timeout, struct runqueue, timeout); 40 | struct runqueue_task *t; 41 | 42 | do { 43 | if (q->stopped) 44 | break; 45 | 46 | if (list_empty(&q->tasks_inactive.list)) 47 | break; 48 | 49 | if (q->max_running_tasks && q->running_tasks >= q->max_running_tasks) 50 | break; 51 | 52 | t = list_first_entry(&q->tasks_inactive.list, struct runqueue_task, list.list); 53 | safe_list_del(&t->list); 54 | safe_list_add(&t->list, &q->tasks_active); 55 | t->running = true; 56 | q->running_tasks++; 57 | if (t->run_timeout) 58 | uloop_timeout_set(&t->timeout, t->run_timeout); 59 | t->type->run(q, t); 60 | } while (1); 61 | 62 | if (!q->empty && 63 | list_empty(&q->tasks_active.list) && 64 | list_empty(&q->tasks_inactive.list)) { 65 | q->empty = true; 66 | if (q->empty_cb) { 67 | q->timeout.cb = __runqueue_empty_cb; 68 | uloop_timeout_set(&q->timeout, 1); 69 | } 70 | } 71 | } 72 | 73 | static void runqueue_start_next(struct runqueue *q) 74 | { 75 | if (q->empty) 76 | return; 77 | 78 | q->timeout.cb = __runqueue_start_next; 79 | uloop_timeout_set(&q->timeout, 1); 80 | } 81 | 82 | static int __runqueue_cancel(void *ctx, struct safe_list *list) 83 | { 84 | struct runqueue_task *t; 85 | 86 | t = container_of(list, struct runqueue_task, list); 87 | runqueue_task_cancel(t, 0); 88 | 89 | return 0; 90 | } 91 | 92 | void runqueue_cancel_active(struct runqueue *q) 93 | { 94 | safe_list_for_each(&q->tasks_active, __runqueue_cancel, NULL); 95 | } 96 | 97 | void runqueue_cancel_pending(struct runqueue *q) 98 | { 99 | safe_list_for_each(&q->tasks_inactive, __runqueue_cancel, NULL); 100 | } 101 | 102 | void runqueue_cancel(struct runqueue *q) 103 | { 104 | runqueue_cancel_pending(q); 105 | runqueue_cancel_active(q); 106 | } 107 | 108 | void runqueue_kill(struct runqueue *q) 109 | { 110 | struct runqueue_task *t; 111 | 112 | while (!list_empty(&q->tasks_active.list)) { 113 | t = list_first_entry(&q->tasks_active.list, struct runqueue_task, list.list); 114 | runqueue_task_kill(t); 115 | } 116 | runqueue_cancel_pending(q); 117 | uloop_timeout_cancel(&q->timeout); 118 | } 119 | 120 | void runqueue_task_cancel(struct runqueue_task *t, int type) 121 | { 122 | if (!t->queued) 123 | return; 124 | 125 | if (!t->running) { 126 | runqueue_task_complete(t); 127 | return; 128 | } 129 | 130 | t->cancelled = true; 131 | if (t->cancel_timeout) 132 | uloop_timeout_set(&t->timeout, t->cancel_timeout); 133 | if (t->type->cancel) 134 | t->type->cancel(t->q, t, type); 135 | } 136 | 137 | static void 138 | __runqueue_task_timeout(struct uloop_timeout *timeout) 139 | { 140 | struct runqueue_task *t = container_of(timeout, struct runqueue_task, timeout); 141 | 142 | if (t->cancelled) 143 | runqueue_task_kill(t); 144 | else 145 | runqueue_task_cancel(t, t->cancel_type); 146 | } 147 | 148 | static void _runqueue_task_add(struct runqueue *q, struct runqueue_task *t, bool running, bool first) 149 | { 150 | struct safe_list *head; 151 | 152 | if (t->queued) 153 | return; 154 | 155 | if (!t->type->run && !running) { 156 | fprintf(stderr, "BUG: inactive task added without run() callback\n"); 157 | return; 158 | } 159 | 160 | if (running) { 161 | q->running_tasks++; 162 | head = &q->tasks_active; 163 | } else { 164 | head = &q->tasks_inactive; 165 | } 166 | 167 | t->timeout.cb = __runqueue_task_timeout; 168 | t->q = q; 169 | if (first) 170 | safe_list_add_first(&t->list, head); 171 | else 172 | safe_list_add(&t->list, head); 173 | t->cancelled = false; 174 | t->queued = true; 175 | t->running = running; 176 | q->empty = false; 177 | 178 | runqueue_start_next(q); 179 | } 180 | 181 | void runqueue_task_add(struct runqueue *q, struct runqueue_task *t, bool running) 182 | { 183 | _runqueue_task_add(q, t, running, 0); 184 | } 185 | 186 | void runqueue_task_add_first(struct runqueue *q, struct runqueue_task *t, bool running) 187 | { 188 | _runqueue_task_add(q, t, running, 1); 189 | } 190 | 191 | void runqueue_task_kill(struct runqueue_task *t) 192 | { 193 | struct runqueue *q = t->q; 194 | bool running = t->running; 195 | 196 | if (!t->queued) 197 | return; 198 | 199 | if (running && t->type->kill) 200 | t->type->kill(q, t); 201 | 202 | runqueue_task_complete(t); 203 | } 204 | 205 | void runqueue_stop(struct runqueue *q) 206 | { 207 | q->stopped = true; 208 | } 209 | 210 | void runqueue_resume(struct runqueue *q) 211 | { 212 | q->stopped = false; 213 | runqueue_start_next(q); 214 | } 215 | 216 | void runqueue_task_complete(struct runqueue_task *t) 217 | { 218 | struct runqueue *q = t->q; 219 | 220 | if (!t->queued) 221 | return; 222 | 223 | if (t->running) 224 | t->q->running_tasks--; 225 | 226 | uloop_timeout_cancel(&t->timeout); 227 | 228 | safe_list_del(&t->list); 229 | t->queued = false; 230 | t->running = false; 231 | t->cancelled = false; 232 | if (t->complete) 233 | t->complete(q, t); 234 | runqueue_start_next(q); 235 | } 236 | 237 | static void 238 | __runqueue_proc_cb(struct uloop_process *p, int ret) 239 | { 240 | struct runqueue_process *t = container_of(p, struct runqueue_process, proc); 241 | 242 | runqueue_task_complete(&t->task); 243 | } 244 | 245 | void runqueue_process_cancel_cb(struct runqueue *q, struct runqueue_task *t, int type) 246 | { 247 | struct runqueue_process *p = container_of(t, struct runqueue_process, task); 248 | 249 | if (!type) 250 | type = SIGTERM; 251 | 252 | kill(p->proc.pid, type); 253 | } 254 | 255 | void runqueue_process_kill_cb(struct runqueue *q, struct runqueue_task *t) 256 | { 257 | struct runqueue_process *p = container_of(t, struct runqueue_process, task); 258 | 259 | uloop_process_delete(&p->proc); 260 | kill(p->proc.pid, SIGKILL); 261 | } 262 | 263 | static const struct runqueue_task_type runqueue_proc_type = { 264 | .name = "process", 265 | .cancel = runqueue_process_cancel_cb, 266 | .kill = runqueue_process_kill_cb, 267 | }; 268 | 269 | void runqueue_process_add(struct runqueue *q, struct runqueue_process *p, pid_t pid) 270 | { 271 | if (p->proc.pending) 272 | return; 273 | 274 | p->proc.pid = pid; 275 | p->proc.cb = __runqueue_proc_cb; 276 | if (!p->task.type) 277 | p->task.type = &runqueue_proc_type; 278 | uloop_process_add(&p->proc); 279 | if (!p->task.running) 280 | runqueue_task_add(q, &p->task, true); 281 | } 282 | -------------------------------------------------------------------------------- /usock.c: -------------------------------------------------------------------------------- 1 | /* 2 | * usock - socket helper functions 3 | * 4 | * Copyright (C) 2010 Steven Barth 5 | * Copyright (C) 2011-2012 Felix Fietkau 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "usock.h" 34 | #include "utils.h" 35 | 36 | static void usock_set_flags(int sock, unsigned int type) 37 | { 38 | if (!(type & USOCK_NOCLOEXEC)) 39 | fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC); 40 | 41 | if (type & USOCK_NONBLOCK) 42 | fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK); 43 | } 44 | 45 | static int usock_connect(int type, struct sockaddr *sa, int sa_len, int family, int socktype, bool server) 46 | { 47 | int sock; 48 | 49 | sock = socket(family, socktype, 0); 50 | if (sock < 0) 51 | return -1; 52 | 53 | usock_set_flags(sock, type); 54 | 55 | if (server) { 56 | const int one = 1; 57 | setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); 58 | 59 | if (!bind(sock, sa, sa_len) && 60 | (socktype != SOCK_STREAM || !listen(sock, SOMAXCONN))) 61 | return sock; 62 | } else { 63 | if (!connect(sock, sa, sa_len) || errno == EINPROGRESS) 64 | return sock; 65 | } 66 | 67 | close(sock); 68 | return -1; 69 | } 70 | 71 | static int usock_unix(int type, const char *host) 72 | { 73 | struct sockaddr_un sun = {.sun_family = AF_UNIX}; 74 | bool server = !!(type & USOCK_SERVER); 75 | int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; 76 | 77 | if (strlen(host) >= sizeof(sun.sun_path)) { 78 | errno = EINVAL; 79 | return -1; 80 | } 81 | strcpy(sun.sun_path, host); 82 | 83 | return usock_connect(type, (struct sockaddr*)&sun, sizeof(sun), AF_UNIX, socktype, server); 84 | } 85 | 86 | static int 87 | usock_inet_notimeout(int type, struct addrinfo *result, void *addr) 88 | { 89 | struct addrinfo *rp; 90 | int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; 91 | bool server = !!(type & USOCK_SERVER); 92 | int sock; 93 | 94 | for (rp = result; rp != NULL; rp = rp->ai_next) { 95 | sock = usock_connect(type, rp->ai_addr, rp->ai_addrlen, rp->ai_family, socktype, server); 96 | if (sock >= 0) { 97 | if (addr) 98 | memcpy(addr, rp->ai_addr, rp->ai_addrlen); 99 | return sock; 100 | } 101 | } 102 | 103 | return -1; 104 | } 105 | 106 | static int poll_restart(struct pollfd *fds, int nfds, int timeout) 107 | { 108 | struct timespec ts, cur; 109 | int msec = timeout % 1000; 110 | int ret; 111 | 112 | clock_gettime(CLOCK_MONOTONIC, &ts); 113 | 114 | ts.tv_nsec += msec * 1000000; 115 | if (ts.tv_nsec > 1000000000) { 116 | ts.tv_sec++; 117 | ts.tv_nsec -= 1000000000; 118 | } 119 | ts.tv_sec += timeout / 1000; 120 | 121 | while (1) { 122 | ret = poll(fds, nfds, timeout); 123 | if (ret >= 0 || (errno != EINTR && errno != EAGAIN)) 124 | return ret; 125 | 126 | clock_gettime(CLOCK_MONOTONIC, &cur); 127 | timeout = (ts.tv_sec - cur.tv_sec) * 1000; 128 | timeout += (ts.tv_nsec - cur.tv_nsec) / 1000000; 129 | if (timeout <= 0) 130 | return 0; 131 | } 132 | } 133 | 134 | int usock_inet_timeout(int type, const char *host, const char *service, 135 | void *addr, int timeout) 136 | { 137 | int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; 138 | bool server = !!(type & USOCK_SERVER); 139 | struct addrinfo *result, *rp; 140 | struct addrinfo hints = { 141 | .ai_family = (type & USOCK_IPV6ONLY) ? AF_INET6 : 142 | (type & USOCK_IPV4ONLY) ? AF_INET : AF_UNSPEC, 143 | .ai_socktype = socktype, 144 | .ai_flags = AI_ADDRCONFIG 145 | | ((type & USOCK_SERVER) ? AI_PASSIVE : 0) 146 | | ((type & USOCK_NUMERIC) ? AI_NUMERICHOST : 0), 147 | }; 148 | struct addrinfo *rp_v6 = NULL; 149 | struct addrinfo *rp_v4 = NULL; 150 | struct pollfd pfds[2] = { 151 | { .fd = -1, .events = POLLOUT }, 152 | { .fd = -1, .events = POLLOUT }, 153 | }; 154 | int sock = -1; 155 | int i; 156 | 157 | if (getaddrinfo(host, service, &hints, &result)) 158 | return -1; 159 | 160 | if (timeout <= 0 || server) { 161 | sock = usock_inet_notimeout(type, result, addr); 162 | goto free_addrinfo; 163 | } 164 | 165 | for (rp = result; rp != NULL; rp = rp->ai_next) { 166 | if (rp->ai_family == AF_INET6 && !rp_v6) 167 | rp_v6 = rp; 168 | if (rp->ai_family == AF_INET && !rp_v4) 169 | rp_v4 = rp; 170 | } 171 | 172 | if (!rp_v6 && !rp_v4) 173 | goto out; 174 | 175 | if (rp_v6) { 176 | rp = rp_v6; 177 | pfds[0].fd = usock_connect(type | USOCK_NONBLOCK, rp->ai_addr, 178 | rp->ai_addrlen, rp->ai_family, 179 | socktype, server); 180 | if (pfds[0].fd < 0) { 181 | rp_v6 = NULL; 182 | goto try_v4; 183 | } 184 | 185 | if (timeout > 300) { 186 | if (poll_restart(pfds, 1, 300) == 1) { 187 | rp = rp_v6; 188 | sock = pfds[0].fd; 189 | goto out; 190 | } 191 | } 192 | timeout -= 300; 193 | } 194 | 195 | try_v4: 196 | if (rp_v4) { 197 | rp = rp_v4; 198 | pfds[1].fd = usock_connect(type | USOCK_NONBLOCK, rp->ai_addr, 199 | rp->ai_addrlen, rp->ai_family, 200 | socktype, server); 201 | if (pfds[1].fd < 0) { 202 | rp_v4 = NULL; 203 | if (!rp_v6) 204 | goto out; 205 | goto wait; 206 | } 207 | } 208 | 209 | wait: 210 | poll_restart(pfds + !rp_v6, !!rp_v6 + !!rp_v4, timeout); 211 | if (pfds[0].revents & POLLOUT) { 212 | rp = rp_v6; 213 | sock = pfds[0].fd; 214 | goto out; 215 | } 216 | 217 | if (pfds[1].revents & POLLOUT) { 218 | rp = rp_v4; 219 | sock = pfds[1].fd; 220 | goto out; 221 | } 222 | 223 | out: 224 | for (i = 0; i < 2; i++) { 225 | int fd = pfds[i].fd; 226 | if (fd >= 0 && fd != sock) 227 | close(fd); 228 | } 229 | 230 | if (!(type & USOCK_NONBLOCK)) 231 | fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) & ~O_NONBLOCK); 232 | 233 | if (addr && sock >= 0) 234 | memcpy(addr, rp->ai_addr, rp->ai_addrlen); 235 | free_addrinfo: 236 | freeaddrinfo(result); 237 | return sock; 238 | } 239 | 240 | const char *usock_port(int port) 241 | { 242 | static char buffer[sizeof("65535\0")]; 243 | 244 | if (port < 0 || port > 65535) 245 | return NULL; 246 | 247 | snprintf(buffer, sizeof(buffer), "%u", port); 248 | 249 | return buffer; 250 | } 251 | 252 | int usock(int type, const char *host, const char *service) { 253 | int sock; 254 | 255 | if (type & USOCK_UNIX) 256 | sock = usock_unix(type, host); 257 | else 258 | sock = usock_inet(type, host, service, NULL); 259 | 260 | if (sock < 0) 261 | return -1; 262 | 263 | return sock; 264 | } 265 | 266 | int usock_wait_ready(int fd, int msecs) { 267 | struct pollfd fds[1]; 268 | int res; 269 | 270 | fds[0].fd = fd; 271 | fds[0].events = POLLOUT; 272 | 273 | res = poll(fds, 1, msecs); 274 | if (res < 0) { 275 | return errno; 276 | } else if (res == 0) { 277 | return -ETIMEDOUT; 278 | } else { 279 | int err = 0; 280 | socklen_t optlen = sizeof(err); 281 | 282 | res = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &optlen); 283 | if (res) 284 | return errno; 285 | if (err) 286 | return err; 287 | } 288 | 289 | return 0; 290 | } 291 | -------------------------------------------------------------------------------- /sh/jshn.sh: -------------------------------------------------------------------------------- 1 | # functions for parsing and generating json 2 | 3 | _json_get_var() { 4 | # dest=$1 5 | # var=$2 6 | eval "$1=\"\$${JSON_PREFIX}$2\"" 7 | } 8 | 9 | _json_set_var() { 10 | # var=$1 11 | local ___val="$2" 12 | eval "${JSON_PREFIX}$1=\"\$___val\"" 13 | } 14 | 15 | __jshn_raw_append() { 16 | # var=$1 17 | local value="$2" 18 | local sep="${3:- }" 19 | 20 | eval "export -- \"$1=\${$1:+\${$1}\${value:+\$sep}}\$value\"" 21 | } 22 | 23 | _jshn_append() { 24 | # var=$1 25 | local _a_value="$2" 26 | eval "${JSON_PREFIX}$1=\"\${${JSON_PREFIX}$1}\${${JSON_PREFIX}$1:+ }\$_a_value\"" 27 | } 28 | 29 | _get_var() { 30 | # var=$1 31 | # value=$2 32 | eval "$1=\"\$$2\"" 33 | } 34 | 35 | _set_var() { 36 | # var=$1 37 | local __val="$2" 38 | eval "$1=\"\$__val\"" 39 | } 40 | 41 | _json_inc() { 42 | # var=$1 43 | # dest=$2 44 | 45 | let "${JSON_PREFIX}$1 += 1" "$2 = ${JSON_PREFIX}$1" 46 | } 47 | 48 | _json_add_generic() { 49 | # type=$1 50 | # name=$2 51 | # value=$3 52 | # cur=$4 53 | 54 | local var 55 | if [ "${4%%[0-9]*}" = "J_A" ]; then 56 | _json_inc "S_$4" var 57 | else 58 | var="${2//[^a-zA-Z0-9_]/_}" 59 | [[ "$var" == "$2" ]] || export -- "${JSON_PREFIX}N_${4}_${var}=$2" 60 | fi 61 | 62 | export -- \ 63 | "${JSON_PREFIX}${4}_$var=$3" \ 64 | "${JSON_PREFIX}T_${4}_$var=$1" 65 | _jshn_append "JSON_UNSET" "${4}_$var" 66 | _jshn_append "K_$4" "$var" 67 | } 68 | 69 | _json_add_table() { 70 | # name=$1 71 | # type=$2 72 | # itype=$3 73 | local cur seq 74 | 75 | _json_get_var cur JSON_CUR 76 | _json_inc JSON_SEQ seq 77 | 78 | local table="J_$3$seq" 79 | _json_set_var "U_$table" "$cur" 80 | export -- "${JSON_PREFIX}K_$table=" 81 | unset "${JSON_PREFIX}S_$table" 82 | _json_set_var JSON_CUR "$table" 83 | _jshn_append "JSON_UNSET" "$table" 84 | 85 | _json_add_generic "$2" "$1" "$table" "$cur" 86 | } 87 | 88 | _json_close_table() { 89 | local _s_cur 90 | 91 | _json_get_var _s_cur JSON_CUR 92 | _json_get_var "${JSON_PREFIX}JSON_CUR" "U_$_s_cur" 93 | } 94 | 95 | json_set_namespace() { 96 | local _new="$1" 97 | local _old="$2" 98 | 99 | [ -n "$_old" ] && _set_var "$_old" "$JSON_PREFIX" 100 | JSON_PREFIX="$_new" 101 | } 102 | 103 | json_cleanup() { 104 | local unset tmp 105 | 106 | _json_get_var unset JSON_UNSET 107 | for tmp in $unset J_V; do 108 | unset \ 109 | ${JSON_PREFIX}U_$tmp \ 110 | ${JSON_PREFIX}K_$tmp \ 111 | ${JSON_PREFIX}S_$tmp \ 112 | ${JSON_PREFIX}T_$tmp \ 113 | ${JSON_PREFIX}N_$tmp \ 114 | ${JSON_PREFIX}$tmp 115 | done 116 | 117 | unset \ 118 | ${JSON_PREFIX}JSON_SEQ \ 119 | ${JSON_PREFIX}JSON_CUR \ 120 | ${JSON_PREFIX}JSON_UNSET 121 | } 122 | 123 | json_init() { 124 | json_cleanup 125 | export -n ${JSON_PREFIX}JSON_SEQ=0 126 | export -- \ 127 | ${JSON_PREFIX}JSON_CUR="J_V" \ 128 | ${JSON_PREFIX}K_J_V= 129 | } 130 | 131 | json_add_object() { 132 | _json_add_table "$1" object T 133 | } 134 | 135 | json_close_object() { 136 | _json_close_table 137 | } 138 | 139 | json_add_array() { 140 | _json_add_table "$1" array A 141 | } 142 | 143 | json_close_array() { 144 | _json_close_table 145 | } 146 | 147 | json_add_string() { 148 | local cur 149 | _json_get_var cur JSON_CUR 150 | _json_add_generic string "$1" "$2" "$cur" 151 | } 152 | 153 | json_add_int() { 154 | local cur 155 | _json_get_var cur JSON_CUR 156 | _json_add_generic int "$1" "$2" "$cur" 157 | } 158 | 159 | json_add_boolean() { 160 | local cur 161 | _json_get_var cur JSON_CUR 162 | _json_add_generic boolean "$1" "$2" "$cur" 163 | } 164 | 165 | json_add_double() { 166 | local cur 167 | _json_get_var cur JSON_CUR 168 | _json_add_generic double "$1" "$2" "$cur" 169 | } 170 | 171 | json_add_null() { 172 | local cur 173 | _json_get_var cur JSON_CUR 174 | _json_add_generic null "$1" "" "$cur" 175 | } 176 | 177 | json_add_fields() { 178 | while [ "$#" -gt 0 ]; do 179 | local field="$1" 180 | shift 181 | 182 | local name="${field%%=*}" 183 | local val="${field#*=}" 184 | [ "$name" != "$val" ] || val="" 185 | 186 | local type="${name#*:}" 187 | [ "$type" != "$name" ] || type=string 188 | name="${name%%:*}" 189 | 190 | case "$type" in 191 | string|int|boolean|double) 192 | local cur 193 | _json_get_var cur JSON_CUR 194 | _json_add_generic "$type" "$name" "$val" "$cur" 195 | ;; 196 | esac 197 | done 198 | } 199 | 200 | json_get_index() { 201 | local __dest="$1" 202 | local __cur __parent __seq 203 | _json_get_var __cur JSON_CUR 204 | _json_get_var __parent "U_$__cur" 205 | if [ "${__parent%%[0-9]*}" != "J_A" ]; then 206 | [ -n "$_json_no_warning" ] || \ 207 | echo "WARNING: Not inside an array" >&2 208 | return 1 209 | fi 210 | __seq="S_$__parent" 211 | eval "export -- \"$__dest=\${$__seq}\"; [ -n \"\${$__seq+x}\" ]" 212 | } 213 | 214 | # functions read access to json variables 215 | 216 | json_compact() { 217 | JSON_NONEWLINE=1 218 | JSON_INDENT= 219 | } 220 | 221 | json_pretty() { 222 | JSON_NONEWLINE= 223 | JSON_INDENT=1 224 | } 225 | 226 | json_load() { 227 | eval "`jshn -r "$1"`" 228 | } 229 | 230 | json_load_file() { 231 | eval "`jshn -R "$1"`" 232 | } 233 | 234 | json_dump() { 235 | jshn "$@" ${JSON_PREFIX:+-p "$JSON_PREFIX"} ${JSON_NONEWLINE:+-n} ${JSON_INDENT:+-i} -w 236 | } 237 | 238 | json_get_type() { 239 | local __dest="$1" 240 | local __cur 241 | 242 | _json_get_var __cur JSON_CUR 243 | local __var="${JSON_PREFIX}T_${__cur}_${2//[^a-zA-Z0-9_]/_}" 244 | eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]" 245 | } 246 | 247 | json_get_keys() { 248 | local __dest="$1" 249 | local _tbl_cur 250 | 251 | if [ -n "$2" ]; then 252 | json_get_var _tbl_cur "$2" 253 | else 254 | _json_get_var _tbl_cur JSON_CUR 255 | fi 256 | local __var="${JSON_PREFIX}K_${_tbl_cur}" 257 | eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]" 258 | } 259 | 260 | json_get_values() { 261 | local _v_dest="$1" 262 | local _v_keys _v_val _select= 263 | local _json_no_warning=1 264 | 265 | unset "$_v_dest" 266 | [ -n "$2" ] && { 267 | json_select "$2" || return 1 268 | _select=1 269 | } 270 | 271 | json_get_keys _v_keys 272 | set -- $_v_keys 273 | while [ "$#" -gt 0 ]; do 274 | json_get_var _v_val "$1" 275 | __jshn_raw_append "$_v_dest" "$_v_val" 276 | shift 277 | done 278 | [ -n "$_select" ] && json_select .. 279 | 280 | return 0 281 | } 282 | 283 | json_get_var() { 284 | local __dest="$1" 285 | local __cur 286 | 287 | _json_get_var __cur JSON_CUR 288 | local __var="${JSON_PREFIX}${__cur}_${2//[^a-zA-Z0-9_]/_}" 289 | eval "export -- \"$__dest=\${$__var:-$3}\"; [ -n \"\${$__var+x}\${3+x}\" ]" 290 | } 291 | 292 | json_get_vars() { 293 | while [ "$#" -gt 0 ]; do 294 | local _var="$1"; shift 295 | if [ "$_var" != "${_var#*:}" ]; then 296 | json_get_var "${_var%%:*}" "${_var%%:*}" "${_var#*:}" 297 | else 298 | json_get_var "$_var" "$_var" 299 | fi 300 | done 301 | } 302 | 303 | json_select() { 304 | local target="$1" 305 | local type 306 | local cur 307 | 308 | [ -z "$1" ] && { 309 | _json_set_var JSON_CUR "J_V" 310 | return 0 311 | } 312 | [[ "$1" == ".." ]] && { 313 | _json_get_var cur JSON_CUR 314 | _json_get_var cur "U_$cur" 315 | _json_set_var JSON_CUR "$cur" 316 | return 0 317 | } 318 | json_get_type type "$target" 319 | case "$type" in 320 | object|array) 321 | json_get_var cur "$target" 322 | _json_set_var JSON_CUR "$cur" 323 | ;; 324 | *) 325 | [ -n "$_json_no_warning" ] || \ 326 | echo "WARNING: Variable '$target' does not exist or is not an array/object" 327 | return 1 328 | ;; 329 | esac 330 | } 331 | 332 | json_is_a() { 333 | local type 334 | 335 | json_get_type type "$1" 336 | [ "$type" = "$2" ] 337 | } 338 | 339 | json_for_each_item() { 340 | [ "$#" -ge 2 ] || return 0 341 | local function="$1"; shift 342 | local target="$1"; shift 343 | local type val 344 | 345 | json_get_type type "$target" 346 | case "$type" in 347 | object|array) 348 | local keys key 349 | json_select "$target" 350 | json_get_keys keys 351 | for key in $keys; do 352 | json_get_var val "$key" 353 | eval "$function \"\$val\" \"\$key\" \"\$@\"" 354 | done 355 | json_select .. 356 | ;; 357 | *) 358 | json_get_var val "$target" 359 | eval "$function \"\$val\" \"\" \"\$@\"" 360 | ;; 361 | esac 362 | } 363 | --------------------------------------------------------------------------------