From c59ca12c5b4f62330e065188ab30054b6a2ca905 Mon Sep 17 00:00:00 2001 From: jbnadal Date: Tue, 6 Dec 2016 17:43:52 +0100 Subject: [PATCH] Bump libubox-2016-07-29-290c64ef5b5c3e75be851594f269d6a9568e64e5 --- src/3P/libubox/.gitignore | 10 + src/3P/libubox/CMakeLists.txt | 71 ++ src/3P/libubox/avl-cmp.c | 37 + src/3P/libubox/avl-cmp.h | 22 + src/3P/libubox/avl.c | 735 ++++++++++++ src/3P/libubox/avl.h | 560 +++++++++ src/3P/libubox/base64.c | 327 +++++ src/3P/libubox/blob.c | 287 +++++ src/3P/libubox/blob.h | 257 ++++ src/3P/libubox/blobmsg.c | 329 +++++ src/3P/libubox/blobmsg.h | 241 ++++ src/3P/libubox/blobmsg_json.c | 370 ++++++ src/3P/libubox/blobmsg_json.h | 59 + src/3P/libubox/builders/cmake/CMakeLists.txt | 67 ++ src/3P/libubox/examples/CMakeLists.txt | 23 + src/3P/libubox/examples/blobmsg-example.c | 140 +++ src/3P/libubox/examples/json_script-example.c | 84 ++ .../libubox/examples/json_script-example.json | 38 + src/3P/libubox/examples/json_script-tests.sh | 287 +++++ src/3P/libubox/examples/runqueue-example.c | 112 ++ src/3P/libubox/examples/shunit2 | 1067 +++++++++++++++++ src/3P/libubox/examples/uloop-example.lua | 78 ++ src/3P/libubox/examples/uloop_pid_test.sh | 11 + src/3P/libubox/examples/ustream-example.c | 148 +++ src/3P/libubox/jshn.c | 378 ++++++ src/3P/libubox/json_script.c | 698 +++++++++++ src/3P/libubox/json_script.h | 136 +++ src/3P/libubox/kvlist.c | 101 ++ src/3P/libubox/kvlist.h | 54 + src/3P/libubox/list.h | 208 ++++ src/3P/libubox/md5.c | 336 ++++++ src/3P/libubox/md5.h | 58 + src/3P/libubox/runqueue.c | 282 +++++ src/3P/libubox/runqueue.h | 124 ++ src/3P/libubox/safe_list.c | 121 ++ src/3P/libubox/safe_list.h | 62 + src/3P/libubox/sh/jshn.sh | 280 +++++ src/3P/libubox/ulog.c | 173 +++ src/3P/libubox/ulog.h | 42 + src/3P/libubox/uloop-epoll.c | 108 ++ src/3P/libubox/uloop-kqueue.c | 148 +++ src/3P/libubox/uloop.c | 567 +++++++++ src/3P/libubox/uloop.h | 109 ++ src/3P/libubox/usock.c | 293 +++++ src/3P/libubox/usock.h | 54 + src/3P/libubox/ustream-fd.c | 173 +++ src/3P/libubox/ustream.c | 547 +++++++++ src/3P/libubox/ustream.h | 219 ++++ src/3P/libubox/utils.c | 105 ++ src/3P/libubox/utils.h | 195 +++ src/3P/libubox/vlist.c | 82 ++ src/3P/libubox/vlist.h | 75 ++ 52 files changed, 11088 insertions(+) create mode 100644 src/3P/libubox/.gitignore create mode 100644 src/3P/libubox/CMakeLists.txt create mode 100644 src/3P/libubox/avl-cmp.c create mode 100644 src/3P/libubox/avl-cmp.h create mode 100644 src/3P/libubox/avl.c create mode 100644 src/3P/libubox/avl.h create mode 100644 src/3P/libubox/base64.c create mode 100644 src/3P/libubox/blob.c create mode 100644 src/3P/libubox/blob.h create mode 100644 src/3P/libubox/blobmsg.c create mode 100644 src/3P/libubox/blobmsg.h create mode 100644 src/3P/libubox/blobmsg_json.c create mode 100644 src/3P/libubox/blobmsg_json.h create mode 100644 src/3P/libubox/builders/cmake/CMakeLists.txt create mode 100644 src/3P/libubox/examples/CMakeLists.txt create mode 100644 src/3P/libubox/examples/blobmsg-example.c create mode 100644 src/3P/libubox/examples/json_script-example.c create mode 100644 src/3P/libubox/examples/json_script-example.json create mode 100644 src/3P/libubox/examples/json_script-tests.sh create mode 100644 src/3P/libubox/examples/runqueue-example.c create mode 100644 src/3P/libubox/examples/shunit2 create mode 100755 src/3P/libubox/examples/uloop-example.lua create mode 100755 src/3P/libubox/examples/uloop_pid_test.sh create mode 100644 src/3P/libubox/examples/ustream-example.c create mode 100644 src/3P/libubox/jshn.c create mode 100644 src/3P/libubox/json_script.c create mode 100644 src/3P/libubox/json_script.h create mode 100644 src/3P/libubox/kvlist.c create mode 100644 src/3P/libubox/kvlist.h create mode 100644 src/3P/libubox/list.h create mode 100644 src/3P/libubox/md5.c create mode 100644 src/3P/libubox/md5.h create mode 100644 src/3P/libubox/runqueue.c create mode 100644 src/3P/libubox/runqueue.h create mode 100644 src/3P/libubox/safe_list.c create mode 100644 src/3P/libubox/safe_list.h create mode 100644 src/3P/libubox/sh/jshn.sh create mode 100644 src/3P/libubox/ulog.c create mode 100644 src/3P/libubox/ulog.h create mode 100644 src/3P/libubox/uloop-epoll.c create mode 100644 src/3P/libubox/uloop-kqueue.c create mode 100644 src/3P/libubox/uloop.c create mode 100644 src/3P/libubox/uloop.h create mode 100644 src/3P/libubox/usock.c create mode 100644 src/3P/libubox/usock.h create mode 100644 src/3P/libubox/ustream-fd.c create mode 100644 src/3P/libubox/ustream.c create mode 100644 src/3P/libubox/ustream.h create mode 100644 src/3P/libubox/utils.c create mode 100644 src/3P/libubox/utils.h create mode 100644 src/3P/libubox/vlist.c create mode 100644 src/3P/libubox/vlist.h diff --git a/src/3P/libubox/.gitignore b/src/3P/libubox/.gitignore new file mode 100644 index 00000000..0c59f966 --- /dev/null +++ b/src/3P/libubox/.gitignore @@ -0,0 +1,10 @@ +Makefile +CMakeCache.txt +CMakeFiles +*.cmake +*.a +*.so +*.dylib +install_manifest.txt +jshn +*-example diff --git a/src/3P/libubox/CMakeLists.txt b/src/3P/libubox/CMakeLists.txt new file mode 100644 index 00000000..57804cf0 --- /dev/null +++ b/src/3P/libubox/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 2.6) +INCLUDE(CheckLibraryExists) +INCLUDE(CheckFunctionExists) + +PROJECT(ubox C) +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations) + +OPTION(BUILD_LUA "build Lua plugin" ON) +OPTION(BUILD_EXAMPLES "build examples" ON) + +INCLUDE(FindPkgConfig) +PKG_SEARCH_MODULE(JSONC json-c) +IF(JSONC_FOUND) + ADD_DEFINITIONS(-DJSONC) + INCLUDE_DIRECTORIES(${JSONC_INCLUDE_DIRS}) +ENDIF() + +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) + +ADD_LIBRARY(ubox SHARED ${SOURCES}) +ADD_LIBRARY(ubox-static STATIC ${SOURCES}) +SET_TARGET_PROPERTIES(ubox-static PROPERTIES OUTPUT_NAME ubox) + +SET(LIBS) +CHECK_FUNCTION_EXISTS(clock_gettime HAVE_GETTIME) +IF(NOT HAVE_GETTIME) + CHECK_LIBRARY_EXISTS(rt clock_gettime "" NEED_GETTIME) + IF(NEED_GETTIME) + TARGET_LINK_LIBRARIES(ubox rt) + ENDIF() +ENDIF() + +FILE(GLOB headers *.h) +INSTALL(FILES ${headers} + DESTINATION include/libubox +) +INSTALL(TARGETS ubox ubox-static + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +ADD_SUBDIRECTORY(lua) +ADD_SUBDIRECTORY(examples) + +find_library(json NAMES json-c) +IF(EXISTS ${json}) + ADD_LIBRARY(blobmsg_json SHARED blobmsg_json.c) + TARGET_LINK_LIBRARIES(blobmsg_json ubox ${json}) + + ADD_LIBRARY(blobmsg_json-static STATIC blobmsg_json.c) + SET_TARGET_PROPERTIES(blobmsg_json-static + PROPERTIES OUTPUT_NAME blobmsg_json) + + ADD_EXECUTABLE(jshn jshn.c) + TARGET_LINK_LIBRARIES(jshn blobmsg_json ${json}) + + ADD_LIBRARY(json_script SHARED json_script.c) + TARGET_LINK_LIBRARIES(json_script ubox) + + INSTALL(TARGETS blobmsg_json blobmsg_json-static jshn json_script + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + ) + + FILE(GLOB scripts sh/*.sh) + INSTALL(FILES ${scripts} + DESTINATION share/libubox + ) + +ENDIF() diff --git a/src/3P/libubox/avl-cmp.c b/src/3P/libubox/avl-cmp.c new file mode 100644 index 00000000..39c1f5b0 --- /dev/null +++ b/src/3P/libubox/avl-cmp.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include "avl-cmp.h" +#include "blob.h" + +static inline int _min(int v1, int v2) +{ + return v1 < v2 ? v1 : v2; +} + +int +avl_strcmp(const void *k1, const void *k2, void *ptr) +{ + return strcmp(k1, k2); +} + +int +avl_blobcmp(const void *k1, const void *k2, void *ptr) +{ + int len = _min(blob_raw_len(k1), blob_raw_len(k2)); + + return memcmp(k1, k2, len); +} diff --git a/src/3P/libubox/avl-cmp.h b/src/3P/libubox/avl-cmp.h new file mode 100644 index 00000000..f97ae8d2 --- /dev/null +++ b/src/3P/libubox/avl-cmp.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __AVL_CMP_H +#define __AVL_CMP_H + +int avl_strcmp(const void *k1, const void *k2, void *ptr); +int avl_blobcmp(const void *k1, const void *k2, void *ptr); + +#endif diff --git a/src/3P/libubox/avl.c b/src/3P/libubox/avl.c new file mode 100644 index 00000000..8d0bf65a --- /dev/null +++ b/src/3P/libubox/avl.c @@ -0,0 +1,735 @@ +/* + * PacketBB handler library (see RFC 5444) + * Copyright (c) 2010 Henning Rogge + * Original OLSRd implementation by Hannes Gredler + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org/git for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + */ + +#include +#include +#include +#include +#include + +#include "avl.h" +#include "list.h" + +/** + * internal type save inline function to calculate the maximum of + * to integers without macro implementation. + * + * @param x first parameter of maximum function + * @param y second parameter of maximum function + * @return largest integer of both parameters + */ +static inline int avl_max(int x, int y) { + return x > y ? x : y; +} + +/** + * internal type save inline function to calculate the minimum of + * to integers without macro implementation. + * + * @param x first parameter of minimum function + * @param y second parameter of minimum function + * @return smallest integer of both parameters + */ +static inline int avl_min(int x, int y) { + return x < y ? x : y; +} + +static struct avl_node * +avl_find_rec(struct avl_node *node, const void *key, avl_tree_comp comp, void *ptr, int *cmp_result); +static void avl_insert_before(struct avl_tree *tree, struct avl_node *pos_node, struct avl_node *node); +static void avl_insert_after(struct avl_tree *tree, struct avl_node *pos_node, struct avl_node *node); +static void post_insert(struct avl_tree *tree, struct avl_node *node); +static void avl_delete_worker(struct avl_tree *tree, struct avl_node *node); +static void avl_remove(struct avl_tree *tree, struct avl_node *node); + +/** + * Initialize a new avl_tree struct + * @param tree pointer to avl-tree + * @param comp pointer to comparator for the tree + * @param allow_dups true if the tree allows multiple + * elements with the same + * @param ptr custom parameter for comparator + */ +void +avl_init(struct avl_tree *tree, avl_tree_comp comp, bool allow_dups, void *ptr) +{ + INIT_LIST_HEAD(&tree->list_head); + tree->root = NULL; + tree->count = 0; + tree->comp = comp; + tree->allow_dups = allow_dups; + tree->cmp_ptr = ptr; +} + +static inline struct avl_node *avl_next(struct avl_node *node) +{ + return list_entry(node->list.next, struct avl_node, list); +} + +/** + * Finds a node in an avl-tree with a certain key + * @param tree pointer to avl-tree + * @param key pointer to key + * @return pointer to avl-node with key, NULL if no node with + * this key exists. + */ +struct avl_node * +avl_find(const struct avl_tree *tree, const void *key) +{ + struct avl_node *node; + int diff; + + if (tree->root == NULL) + return NULL; + + node = avl_find_rec(tree->root, key, tree->comp, tree->cmp_ptr, &diff); + + return diff == 0 ? node : NULL; +} + +/** + * Finds the last node in an avl-tree with a key less or equal + * than the specified key + * @param tree pointer to avl-tree + * @param key pointer to specified key + * @return pointer to avl-node, NULL if no node with + * key less or equal specified key exists. + */ +struct avl_node * +avl_find_lessequal(const struct avl_tree *tree, const void *key) { + struct avl_node *node, *next; + int diff; + + if (tree->root == NULL) + return NULL; + + node = avl_find_rec(tree->root, key, tree->comp, tree->cmp_ptr, &diff); + + /* go left as long as keylist, &tree->list_head)) { + return NULL; + } + + node = (struct avl_node *)node->list.prev; + diff = (*tree->comp) (key, node->key, tree->cmp_ptr); + } + + /* go right as long as key>=next_node.key */ + next = node; + while (diff >= 0) { + node = next; + if (list_is_last(&node->list, &tree->list_head)) { + break; + } + + next = (struct avl_node *)node->list.next; + diff = (*tree->comp) (key, next->key, tree->cmp_ptr); + } + return node; +} + +/** + * Finds the first node in an avl-tree with a key greater or equal + * than the specified key + * @param tree pointer to avl-tree + * @param key pointer to specified key + * @return pointer to avl-node, NULL if no node with + * key greater or equal specified key exists. + */ +struct avl_node * +avl_find_greaterequal(const struct avl_tree *tree, const void *key) { + struct avl_node *node, *next; + int diff; + + if (tree->root == NULL) + return NULL; + + node = avl_find_rec(tree->root, key, tree->comp, tree->cmp_ptr, &diff); + + /* go right as long as key>node.key */ + while (diff > 0) { + if (list_is_last(&node->list, &tree->list_head)) { + return NULL; + } + + node = (struct avl_node *)node->list.next; + diff = (*tree->comp) (key, node->key, tree->cmp_ptr); + } + + /* go left as long as key<=next_node.key */ + next = node; + while (diff <= 0) { + node = next; + if (list_is_first(&node->list, &tree->list_head)) { + break; + } + + next = (struct avl_node *)node->list.prev; + diff = (*tree->comp) (key, next->key, tree->cmp_ptr); + } + return node; +} + +/** + * Inserts an avl_node into a tree + * @param tree pointer to tree + * @param new pointer to node + * @return 0 if node was inserted successfully, -1 if it was not inserted + * because of a key collision + */ +int +avl_insert(struct avl_tree *tree, struct avl_node *new) +{ + struct avl_node *node, *next, *last; + int diff; + + new->parent = NULL; + + new->left = NULL; + new->right = NULL; + + new->balance = 0; + new->leader = true; + + if (tree->root == NULL) { + list_add(&new->list, &tree->list_head); + tree->root = new; + tree->count = 1; + return 0; + } + + node = avl_find_rec(tree->root, new->key, tree->comp, tree->cmp_ptr, &diff); + + last = node; + + while (!list_is_last(&last->list, &tree->list_head)) { + next = avl_next(last); + if (next->leader) { + break; + } + last = next; + } + + diff = (*tree->comp) (new->key, node->key, tree->cmp_ptr); + + if (diff == 0) { + if (!tree->allow_dups) + return -1; + + new->leader = 0; + + avl_insert_after(tree, last, new); + return 0; + } + + if (node->balance == 1) { + avl_insert_before(tree, node, new); + + node->balance = 0; + new->parent = node; + node->left = new; + return 0; + } + + if (node->balance == -1) { + avl_insert_after(tree, last, new); + + node->balance = 0; + new->parent = node; + node->right = new; + return 0; + } + + if (diff < 0) { + avl_insert_before(tree, node, new); + + node->balance = -1; + new->parent = node; + node->left = new; + post_insert(tree, node); + return 0; + } + + avl_insert_after(tree, last, new); + + node->balance = 1; + new->parent = node; + node->right = new; + post_insert(tree, node); + return 0; +} + +/** + * Remove a node from an avl tree + * @param tree pointer to tree + * @param node pointer to node + */ +void +avl_delete(struct avl_tree *tree, struct avl_node *node) +{ + struct avl_node *next; + struct avl_node *parent; + struct avl_node *left; + struct avl_node *right; + if (node->leader) { + if (tree->allow_dups + && !list_is_last(&node->list, &tree->list_head) + && !(next = avl_next(node))->leader) { + next->leader = true; + next->balance = node->balance; + + parent = node->parent; + left = node->left; + right = node->right; + + next->parent = parent; + next->left = left; + next->right = right; + + if (parent == NULL) + tree->root = next; + + else { + if (node == parent->left) + parent->left = next; + + else + parent->right = next; + } + + if (left != NULL) + left->parent = next; + + if (right != NULL) + right->parent = next; + } + + else + avl_delete_worker(tree, node); + } + + avl_remove(tree, node); +} + +static struct avl_node * +avl_find_rec(struct avl_node *node, const void *key, avl_tree_comp comp, void *cmp_ptr, int *cmp_result) +{ + int diff; + + diff = (*comp) (key, node->key, cmp_ptr); + *cmp_result = diff; + + if (diff < 0) { + if (node->left != NULL) + return avl_find_rec(node->left, key, comp, cmp_ptr, cmp_result); + + return node; + } + + if (diff > 0) { + if (node->right != NULL) + return avl_find_rec(node->right, key, comp, cmp_ptr, cmp_result); + + return node; + } + + return node; +} + +static void +avl_rotate_right(struct avl_tree *tree, struct avl_node *node) +{ + struct avl_node *left, *parent; + + left = node->left; + parent = node->parent; + + left->parent = parent; + node->parent = left; + + if (parent == NULL) + tree->root = left; + + else { + if (parent->left == node) + parent->left = left; + + else + parent->right = left; + } + + node->left = left->right; + left->right = node; + + if (node->left != NULL) + node->left->parent = node; + + node->balance += 1 - avl_min(left->balance, 0); + left->balance += 1 + avl_max(node->balance, 0); +} + +static void +avl_rotate_left(struct avl_tree *tree, struct avl_node *node) +{ + struct avl_node *right, *parent; + + right = node->right; + parent = node->parent; + + right->parent = parent; + node->parent = right; + + if (parent == NULL) + tree->root = right; + + else { + if (parent->left == node) + parent->left = right; + + else + parent->right = right; + } + + node->right = right->left; + right->left = node; + + if (node->right != NULL) + node->right->parent = node; + + node->balance -= 1 + avl_max(right->balance, 0); + right->balance -= 1 - avl_min(node->balance, 0); +} + +static void +post_insert(struct avl_tree *tree, struct avl_node *node) +{ + struct avl_node *parent = node->parent; + + if (parent == NULL) + return; + + if (node == parent->left) { + parent->balance--; + + if (parent->balance == 0) + return; + + if (parent->balance == -1) { + post_insert(tree, parent); + return; + } + + if (node->balance == -1) { + avl_rotate_right(tree, parent); + return; + } + + avl_rotate_left(tree, node); + avl_rotate_right(tree, node->parent->parent); + return; + } + + parent->balance++; + + if (parent->balance == 0) + return; + + if (parent->balance == 1) { + post_insert(tree, parent); + return; + } + + if (node->balance == 1) { + avl_rotate_left(tree, parent); + return; + } + + avl_rotate_right(tree, node); + avl_rotate_left(tree, node->parent->parent); +} + +static void +avl_insert_before(struct avl_tree *tree, struct avl_node *pos_node, struct avl_node *node) +{ + list_add_tail(&node->list, &pos_node->list); + tree->count++; +} + +static void +avl_insert_after(struct avl_tree *tree, struct avl_node *pos_node, struct avl_node *node) +{ + list_add(&node->list, &pos_node->list); + tree->count++; +} + +static void +avl_remove(struct avl_tree *tree, struct avl_node *node) +{ + list_del(&node->list); + tree->count--; +} + +static void +avl_post_delete(struct avl_tree *tree, struct avl_node *node) +{ + struct avl_node *parent; + + if ((parent = node->parent) == NULL) + return; + + if (node == parent->left) { + parent->balance++; + + if (parent->balance == 0) { + avl_post_delete(tree, parent); + return; + } + + if (parent->balance == 1) + return; + + if (parent->right->balance == 0) { + avl_rotate_left(tree, parent); + return; + } + + if (parent->right->balance == 1) { + avl_rotate_left(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + + avl_rotate_right(tree, parent->right); + avl_rotate_left(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + + parent->balance--; + + if (parent->balance == 0) { + avl_post_delete(tree, parent); + return; + } + + if (parent->balance == -1) + return; + + if (parent->left->balance == 0) { + avl_rotate_right(tree, parent); + return; + } + + if (parent->left->balance == -1) { + avl_rotate_right(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + + avl_rotate_left(tree, parent->left); + avl_rotate_right(tree, parent); + avl_post_delete(tree, parent->parent); +} + +static struct avl_node * +avl_local_min(struct avl_node *node) +{ + while (node->left != NULL) + node = node->left; + + return node; +} + +#if 0 +static struct avl_node * +avl_local_max(struct avl_node *node) +{ + while (node->right != NULL) + node = node->right; + + return node; +} +#endif + +static void +avl_delete_worker(struct avl_tree *tree, struct avl_node *node) +{ + struct avl_node *parent, *min; + + parent = node->parent; + + if (node->left == NULL && node->right == NULL) { + if (parent == NULL) { + tree->root = NULL; + return; + } + + if (parent->left == node) { + parent->left = NULL; + parent->balance++; + + if (parent->balance == 1) + return; + + if (parent->balance == 0) { + avl_post_delete(tree, parent); + return; + } + + if (parent->right->balance == 0) { + avl_rotate_left(tree, parent); + return; + } + + if (parent->right->balance == 1) { + avl_rotate_left(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + + avl_rotate_right(tree, parent->right); + avl_rotate_left(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + + if (parent->right == node) { + parent->right = NULL; + parent->balance--; + + if (parent->balance == -1) + return; + + if (parent->balance == 0) { + avl_post_delete(tree, parent); + return; + } + + if (parent->left->balance == 0) { + avl_rotate_right(tree, parent); + return; + } + + if (parent->left->balance == -1) { + avl_rotate_right(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + + avl_rotate_left(tree, parent->left); + avl_rotate_right(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + } + + if (node->left == NULL) { + if (parent == NULL) { + tree->root = node->right; + node->right->parent = NULL; + return; + } + + node->right->parent = parent; + + if (parent->left == node) + parent->left = node->right; + + else + parent->right = node->right; + + avl_post_delete(tree, node->right); + return; + } + + if (node->right == NULL) { + if (parent == NULL) { + tree->root = node->left; + node->left->parent = NULL; + return; + } + + node->left->parent = parent; + + if (parent->left == node) + parent->left = node->left; + + else + parent->right = node->left; + + avl_post_delete(tree, node->left); + return; + } + + min = avl_local_min(node->right); + avl_delete_worker(tree, min); + parent = node->parent; + + min->balance = node->balance; + min->parent = parent; + min->left = node->left; + min->right = node->right; + + if (min->left != NULL) + min->left->parent = min; + + if (min->right != NULL) + min->right->parent = min; + + if (parent == NULL) { + tree->root = min; + return; + } + + if (parent->left == node) { + parent->left = min; + return; + } + + parent->right = min; +} + +/* + * Local Variables: + * c-basic-offset: 2 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/3P/libubox/avl.h b/src/3P/libubox/avl.h new file mode 100644 index 00000000..c4685972 --- /dev/null +++ b/src/3P/libubox/avl.h @@ -0,0 +1,560 @@ +/* + * PacketBB handler library (see RFC 5444) + * Copyright (c) 2010 Henning Rogge + * Original OLSRd implementation by Hannes Gredler + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org/git for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + */ + +#ifndef _AVL_H +#define _AVL_H + +#include +#include + +#include "list.h" + +/* Support for OLSR.org linker symbol export */ +#define EXPORT(sym) sym + +/** + * This element is a member of a avl-tree. It must be contained in all + * larger structs that should be put into a tree. + */ +struct avl_node { + /** + * Linked list node for supporting easy iteration and multiple + * elments with the same key. + * + * this must be the first element of an avl_node to + * make casting for lists easier + */ + struct list_head list; + + /** + * Pointer to parent node in tree, NULL if root node + */ + struct avl_node *parent; + + /** + * Pointer to left child + */ + struct avl_node *left; + + /** + * Pointer to right child + */ + struct avl_node *right; + + /** + * pointer to key of node + */ + const void *key; + + /** + * balance state of AVL tree (0,-1,+1) + */ + signed char balance; + + /** + * true if first of a series of nodes with same key + */ + bool leader; +}; + +/** + * Prototype for avl comparators + * @param k1 first key + * @param k2 second key + * @param ptr custom data for tree comparator + * @return +1 if k1>k2, -1 if k1list_head.next == &node->list; +} + +/** + * @param tree pointer to avl-tree + * @param node pointer to node of the tree + * @return true if node is the last one of the tree, false otherwise + */ +static inline bool +avl_is_last(struct avl_tree *tree, struct avl_node *node) { + return tree->list_head.prev == &node->list; +} + +/** + * @param tree pointer to avl-tree + * @return true if the tree is empty, false otherwise + */ +static inline bool +avl_is_empty(struct avl_tree *tree) { + return tree->count == 0; +} + +/** + * Internal function to support returning the element from a avl tree query + * @param tree pointer to avl tree + * @param key pointer to key + * @param offset offset of node inside the embedded struct + * @param mode mode of lookup operation (less equal, equal or greater equal) + * @param pointer to elemen, NULL if no fitting one was found + */ +static inline void * +__avl_find_element(const struct avl_tree *tree, const void *key, size_t offset, enum avl_find_mode mode) { + void *node = NULL; + + switch (mode) { + case AVL_FIND_EQUAL: + node = avl_find(tree, key); + break; + case AVL_FIND_LESSEQUAL: + node = avl_find_lessequal(tree, key); + break; + case AVL_FIND_GREATEREQUAL: + node = avl_find_greaterequal(tree, key); + break; + } + return node == NULL ? NULL : (((char *)node) - offset); +} + +/** + * @param tree pointer to avl-tree + * @param key pointer to key + * @param element pointer to a node element + * (don't need to be initialized) + * @param node_element name of the avl_node element inside the + * larger struct + * @return pointer to tree element with the specified key, + * NULL if no element was found + */ +#define avl_find_element(tree, key, element, node_element) \ + ((typeof(*(element)) *)__avl_find_element(tree, key, offsetof(typeof(*(element)), node_element), AVL_FIND_EQUAL)) + +/** + * @param tree pointer to avl-tree + * @param key pointer to specified key + * @param element pointer to a node element + * (don't need to be initialized) + * @param node_element name of the avl_node element inside the + * larger struct + * return pointer to last tree element with less or equal key than specified key, + * NULL if no element was found + */ +#define avl_find_le_element(tree, key, element, node_element) \ + ((typeof(*(element)) *)__avl_find_element(tree, key, offsetof(typeof(*(element)), node_element), AVL_FIND_LESSEQUAL)) + +/** + * @param tree pointer to avl-tree + * @param key pointer to specified key + * @param element pointer to a node element + * (don't need to be initialized) + * @param node_element name of the avl_node element inside the + * larger struct + * return pointer to first tree element with greater or equal key than specified key, + * NULL if no element was found + */ +#define avl_find_ge_element(tree, key, element, node_element) \ + ((typeof(*(element)) *)__avl_find_element(tree, key, offsetof(typeof(*(element)), node_element), AVL_FIND_GREATEREQUAL)) + +/** + * This function must not be called for an empty tree + * + * @param tree pointer to avl-tree + * @param element pointer to a node element + * (don't need to be initialized) + * @param node_member name of the avl_node element inside the + * larger struct + * @return pointer to the first element of the avl_tree + * (automatically converted to type 'element') + */ +#define avl_first_element(tree, element, node_member) \ + container_of((tree)->list_head.next, typeof(*(element)), node_member.list) + +/** + * @param tree pointer to tree + * @param element pointer to a node struct that contains the avl_node + * (don't need to be initialized) + * @param node_member name of the avl_node element inside the + * larger struct + * @return pointer to the last element of the avl_tree + * (automatically converted to type 'element') + */ +#define avl_last_element(tree, element, node_member) \ + container_of((tree)->list_head.prev, typeof(*(element)), node_member.list) + +/** + * This function must not be called for the last element of + * an avl tree + * + * @param element pointer to a node of the tree + * @param node_member name of the avl_node element inside the + * larger struct + * @return pointer to the node after 'element' + * (automatically converted to type 'element') + */ +#define avl_next_element(element, node_member) \ + container_of((&(element)->node_member.list)->next, typeof(*(element)), node_member.list) + +/** + * This function must not be called for the first element of + * an avl tree + * + * @param element pointer to a node of the tree + * @param node_member name of the avl_node element inside the + * larger struct + * @return pointer to the node before 'element' + * (automatically converted to type 'element') + */ +#define avl_prev_element(element, node_member) \ + container_of((&(element)->node_member.list)->prev, typeof(*(element)), node_member.list) + +/** + * Loop over a block of elements of a tree, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * + * @param first pointer to first element of loop + * @param last pointer to last element of loop + * @param element pointer to a node of the tree, this element will + * contain the current node of the list during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_element_range(first, last, element, node_member) \ + for (element = (first); \ + element->node_member.list.prev != &(last)->node_member.list; \ + element = avl_next_element(element, node_member)) + +/** + * Loop over a block of elements of a tree backwards, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * + * @param first pointer to first element of loop + * @param last pointer to last element of loop + * @param element pointer to a node of the tree, this element will + * contain the current node of the list during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_element_range_reverse(first, last, element, node_member) \ + for (element = (last); \ + element->node_member.list.next != &(first)->node_member.list; \ + element = avl_prev_element(element, node_member)) + +/** + * Loop over all elements of an avl_tree, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * + * @param tree pointer to avl-tree + * @param element pointer to a node of the tree, this element will + * contain the current node of the tree during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_each_element(tree, element, node_member) \ + avl_for_element_range(avl_first_element(tree, element, node_member), \ + avl_last_element(tree, element, node_member), \ + element, node_member) + +/** + * Loop over all elements of an avl_tree backwards, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * + * @param tree pointer to avl-tree + * @param element pointer to a node of the tree, this element will + * contain the current node of the tree during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_each_element_reverse(tree, element, node_member) \ + avl_for_element_range_reverse(avl_first_element(tree, element, node_member), \ + avl_last_element(tree, element, node_member), \ + element, node_member) + +/** + * Loop over a block of elements of a tree, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * The loop runs from the element 'first' to the end of the tree. + * + * @param tree pointer to avl-tree + * @param first pointer to first element of loop + * @param element pointer to a node of the tree, this element will + * contain the current node of the list during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_element_to_last(tree, first, element, node_member) \ + avl_for_element_range(first, avl_last_element(tree, element, node_member), element, node_member) + + +/** + * Loop over a block of elements of a tree backwards, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * The loop runs from the element 'first' to the end of the tree. + * + * @param tree pointer to avl-tree + * @param first pointer to first element of loop + * @param element pointer to a node of the tree, this element will + * contain the current node of the list during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_element_to_last_reverse(tree, first, element, node_member) \ + avl_for_element_range_reverse(first, avl_last_element(tree, element, node_member), element, node_member) + +/** + * Loop over a block of elements of a tree, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * The loop runs from the start of the tree to the element 'last'. + * + * @param tree pointer to avl-tree + * @param last pointer to last element of loop + * @param element pointer to a node of the tree, this element will + * contain the current node of the list during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_first_to_element(tree, last, element, node_member) \ + avl_for_element_range(avl_first_element(tree, element, node_member), last, element, node_member) + + +/** + * Loop over a block of elements of a tree backwards, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * The loop runs from the start of the tree to the element 'last'. + * + * @param tree pointer to avl-tree + * @param last pointer to last element of loop + * @param element pointer to a node of the tree, this element will + * contain the current node of the list during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_first_to_element_reverse(tree, last, element, node_member) \ + avl_for_element_range_reverse(avl_first_element(tree, element, node_member), last, element, node_member) + +/** + * Loop over a block of nodes of a tree, used similar to a for() command. + * This loop can be used if the current element might be removed from + * the tree during the loop. Other elements should not be removed during + * the loop. + * + * @param first_element first element of loop + * @param last_element last element of loop + * @param element iterator pointer to tree element struct + * @param node_member name of avl_node within tree element struct + * @param ptr pointer to tree element struct which is used to store + * the next node during the loop + */ +#define avl_for_element_range_safe(first_element, last_element, element, node_member, ptr) \ + for (element = (first_element), ptr = avl_next_element(first_element, node_member); \ + element->node_member.list.prev != &(last_element)->node_member.list; \ + element = ptr, ptr = avl_next_element(ptr, node_member)) + +/** + * Loop over a block of elements of a tree backwards, used similar to a for() command. + * This loop can be used if the current element might be removed from + * the tree during the loop. Other elements should not be removed during + * the loop. + * + * @param first_element first element of range (will be last returned by the loop) + * @param last_element last element of range (will be first returned by the loop) + * @param element iterator pointer to node element struct + * @param node_member name of avl_node within node element struct + * @param ptr pointer to node element struct which is used to store + * the previous node during the loop + */ +#define avl_for_element_range_reverse_safe(first_element, last_element, element, node_member, ptr) \ + for (element = (last_element), ptr = avl_prev_element(last_element, node_member); \ + element->node_member.list.next != &(first_element)->node_member.list; \ + element = ptr, ptr = avl_prev_element(ptr, node_member)) + +/** + * Loop over all elements of an avl_tree, used similar to a for() command. + * This loop can be used if the current element might be removed from + * the tree during the loop. Other elements should not be removed during + * the loop. + * + * @param tree pointer to avl-tree + * @param element pointer to a node of the tree, this element will + * contain the current node of the tree during the loop + * @param node_member name of the avl_node element inside the + * larger struct + * @param ptr pointer to a tree element which is used to store + * the next node during the loop + */ +#define avl_for_each_element_safe(tree, element, node_member, ptr) \ + avl_for_element_range_safe(avl_first_element(tree, element, node_member), \ + avl_last_element(tree, element, node_member), \ + element, node_member, ptr) + +/** + * Loop over all elements of an avl_tree backwards, used similar to a for() command. + * This loop can be used if the current element might be removed from + * the tree during the loop. Other elements should not be removed during + * the loop. + * + * @param tree pointer to avl-tree + * @param element pointer to a node of the tree, this element will + * contain the current node of the tree during the loop + * @param node_member name of the avl_node element inside the + * larger struct + * @param ptr pointer to a tree element which is used to store + * the next node during the loop + */ +#define avl_for_each_element_reverse_safe(tree, element, node_member, ptr) \ + avl_for_element_range_reverse_safe(avl_first_element(tree, element, node_member), \ + avl_last_element(tree, element, node_member), \ + element, node_member, ptr) + +/** + * A special loop that removes all elements of the tree and cleans up the tree + * root. The loop body is responsible to free the node elements of the tree. + * + * This loop is much faster than a normal one for clearing the tree because it + * does not rebalance the tree after each removal. Do NOT use a break command + * inside. + * You can free the memory of the elements within the loop. + * Do NOT call avl_delete() on the elements within the loop, + * + * @param tree pointer to avl-tree + * @param element pointer to a node of the tree, this element will + * contain the current node of the tree during the loop + * @param node_member name of the avl_node element inside the + * larger struct + * @param ptr pointer to a tree element which is used to store + * the next node during the loop + */ +#define avl_remove_all_elements(tree, element, node_member, ptr) \ + for (element = avl_first_element(tree, element, node_member), \ + ptr = avl_next_element(element, node_member), \ + INIT_LIST_HEAD(&(tree)->list_head), \ + (tree)->root = NULL; \ + (tree)->count > 0; \ + element = ptr, ptr = avl_next_element(ptr, node_member), (tree)->count--) + +#endif /* _AVL_H */ + +/* + * Local Variables: + * c-basic-offset: 2 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/3P/libubox/base64.c b/src/3P/libubox/base64.c new file mode 100644 index 00000000..4186ce84 --- /dev/null +++ b/src/3P/libubox/base64.c @@ -0,0 +1,327 @@ +/* + * base64 - libubox base64 functions + * + * Copyright (C) 2015 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $OpenBSD: base64.c,v 1.7 2013/12/31 02:32:56 tedu Exp $ */ + +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include +#include +#include +#include +#include "utils.h" + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int b64_encode(const void *_src, size_t srclength, + void *dest, size_t targsize) +{ + const unsigned char *src = _src; + char *target = dest; + size_t datalength = 0; + u_char input[3] = {0}; + u_char output[4]; + int i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int b64_decode(const void *_src, void *dest, size_t targsize) +{ + const char *src = _src; + unsigned char *target = dest; + int tarindex, state, ch; + u_char nextbyte; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = (unsigned char)*src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + nextbyte = ((pos - Base64) & 0x0f) << 4; + if (tarindex + 1 < targsize) + target[tarindex+1] = nextbyte; + else if (nextbyte) + return (-1); + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + nextbyte = ((pos - Base64) & 0x03) << 6; + if (tarindex + 1 < targsize) + target[tarindex+1] = nextbyte; + else if (nextbyte) + return (-1); + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = (unsigned char)*src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for (; ch != '\0'; ch = (unsigned char)*src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = (unsigned char)*src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (; ch != '\0'; ch = (unsigned char)*src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && tarindex < targsize && + target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + /* Null-terminate if we have room left */ + if (tarindex < targsize) + target[tarindex] = 0; + + return (tarindex); +} diff --git a/src/3P/libubox/blob.c b/src/3P/libubox/blob.c new file mode 100644 index 00000000..ec8617be --- /dev/null +++ b/src/3P/libubox/blob.c @@ -0,0 +1,287 @@ +/* + * blob - library for generating/parsing tagged binary data + * + * Copyright (C) 2010 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "blob.h" + +static bool +blob_buffer_grow(struct blob_buf *buf, int minlen) +{ + struct blob_buf *new; + int delta = ((minlen / 256) + 1) * 256; + new = realloc(buf->buf, buf->buflen + delta); + if (new) { + buf->buf = new; + memset(buf->buf + buf->buflen, 0, delta); + buf->buflen += delta; + } + return !!new; +} + +static void +blob_init(struct blob_attr *attr, int id, unsigned int len) +{ + len &= BLOB_ATTR_LEN_MASK; + len |= (id << BLOB_ATTR_ID_SHIFT) & BLOB_ATTR_ID_MASK; + attr->id_len = cpu_to_be32(len); +} + +static inline struct blob_attr * +offset_to_attr(struct blob_buf *buf, int offset) +{ + void *ptr = (char *)buf->buf + offset - BLOB_COOKIE; + return ptr; +} + +static inline int +attr_to_offset(struct blob_buf *buf, struct blob_attr *attr) +{ + return (char *)attr - (char *) buf->buf + BLOB_COOKIE; +} + +bool +blob_buf_grow(struct blob_buf *buf, int required) +{ + int offset_head = attr_to_offset(buf, buf->head); + + if (!buf->grow || !buf->grow(buf, required)) + return false; + + buf->head = offset_to_attr(buf, offset_head); + return true; +} + +static struct blob_attr * +blob_add(struct blob_buf *buf, struct blob_attr *pos, int id, int payload) +{ + int offset = attr_to_offset(buf, pos); + int required = (offset - BLOB_COOKIE + sizeof(struct blob_attr) + payload) - buf->buflen; + struct blob_attr *attr; + + if (required > 0) { + if (!blob_buf_grow(buf, required)) + return NULL; + attr = offset_to_attr(buf, offset); + } else { + attr = pos; + } + + blob_init(attr, id, payload + sizeof(struct blob_attr)); + blob_fill_pad(attr); + return attr; +} + +int +blob_buf_init(struct blob_buf *buf, int id) +{ + if (!buf->grow) + buf->grow = blob_buffer_grow; + + buf->head = buf->buf; + if (blob_add(buf, buf->buf, id, 0) == NULL) + return -ENOMEM; + + return 0; +} + +void +blob_buf_free(struct blob_buf *buf) +{ + free(buf->buf); + buf->buf = NULL; + buf->buflen = 0; +} + +void +blob_fill_pad(struct blob_attr *attr) +{ + char *buf = (char *) attr; + int len = blob_pad_len(attr); + int delta = len - blob_raw_len(attr); + + if (delta > 0) + memset(buf + len - delta, 0, delta); +} + +void +blob_set_raw_len(struct blob_attr *attr, unsigned int len) +{ + len &= BLOB_ATTR_LEN_MASK; + attr->id_len &= ~cpu_to_be32(BLOB_ATTR_LEN_MASK); + attr->id_len |= cpu_to_be32(len); +} + +struct blob_attr * +blob_new(struct blob_buf *buf, int id, int payload) +{ + struct blob_attr *attr; + + attr = blob_add(buf, blob_next(buf->head), id, payload); + if (!attr) + return NULL; + + blob_set_raw_len(buf->head, blob_pad_len(buf->head) + blob_pad_len(attr)); + return attr; +} + +struct blob_attr * +blob_put_raw(struct blob_buf *buf, const void *ptr, unsigned int len) +{ + struct blob_attr *attr; + + if (len < sizeof(struct blob_attr) || !ptr) + return NULL; + + attr = blob_add(buf, blob_next(buf->head), 0, len - sizeof(struct blob_attr)); + if (!attr) + return NULL; + blob_set_raw_len(buf->head, blob_pad_len(buf->head) + len); + memcpy(attr, ptr, len); + return attr; +} + +struct blob_attr * +blob_put(struct blob_buf *buf, int id, const void *ptr, unsigned int len) +{ + struct blob_attr *attr; + + attr = blob_new(buf, id, len); + if (!attr) + return NULL; + + if (ptr) + memcpy(blob_data(attr), ptr, len); + return attr; +} + +void * +blob_nest_start(struct blob_buf *buf, int id) +{ + unsigned long offset = attr_to_offset(buf, buf->head); + buf->head = blob_new(buf, id, 0); + if (!buf->head) + return NULL; + return (void *) offset; +} + +void +blob_nest_end(struct blob_buf *buf, void *cookie) +{ + struct blob_attr *attr = offset_to_attr(buf, (unsigned long) cookie); + blob_set_raw_len(attr, blob_pad_len(attr) + blob_len(buf->head)); + buf->head = attr; +} + +static const int blob_type_minlen[BLOB_ATTR_LAST] = { + [BLOB_ATTR_STRING] = 1, + [BLOB_ATTR_INT8] = sizeof(uint8_t), + [BLOB_ATTR_INT16] = sizeof(uint16_t), + [BLOB_ATTR_INT32] = sizeof(uint32_t), + [BLOB_ATTR_INT64] = sizeof(uint64_t), +}; + +bool +blob_check_type(const void *ptr, unsigned int len, int type) +{ + const char *data = ptr; + + if (type >= BLOB_ATTR_LAST) + return false; + + if (type >= BLOB_ATTR_INT8 && type <= BLOB_ATTR_INT64) { + if (len != blob_type_minlen[type]) + return false; + } else { + if (len < blob_type_minlen[type]) + return false; + } + + if (type == BLOB_ATTR_STRING && data[len - 1] != 0) + return false; + + return true; +} + +int +blob_parse(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max) +{ + struct blob_attr *pos; + int found = 0; + int rem; + + memset(data, 0, sizeof(struct blob_attr *) * max); + blob_for_each_attr(pos, attr, rem) { + int id = blob_id(pos); + int len = blob_len(pos); + + if (id >= max) + continue; + + if (info) { + int type = info[id].type; + + if (type < BLOB_ATTR_LAST) { + if (!blob_check_type(blob_data(pos), len, type)) + continue; + } + + if (info[id].minlen && len < info[id].minlen) + continue; + + if (info[id].maxlen && len > info[id].maxlen) + continue; + + if (info[id].validate && !info[id].validate(&info[id], pos)) + continue; + } + + if (!data[id]) + found++; + + data[id] = pos; + } + return found; +} + +bool +blob_attr_equal(const struct blob_attr *a1, const struct blob_attr *a2) +{ + if (!a1 && !a2) + return true; + + if (!a1 || !a2) + return false; + + if (blob_pad_len(a1) != blob_pad_len(a2)) + return false; + + return !memcmp(a1, a2, blob_pad_len(a1)); +} + +struct blob_attr * +blob_memdup(struct blob_attr *attr) +{ + struct blob_attr *ret; + int size = blob_pad_len(attr); + + ret = malloc(size); + if (!ret) + return NULL; + + memcpy(ret, attr, size); + return ret; +} diff --git a/src/3P/libubox/blob.h b/src/3P/libubox/blob.h new file mode 100644 index 00000000..ab077eab --- /dev/null +++ b/src/3P/libubox/blob.h @@ -0,0 +1,257 @@ +/* + * blob - library for generating/parsing tagged binary data + * + * Copyright (C) 2010 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _BLOB_H__ +#define _BLOB_H__ + +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +#define BLOB_COOKIE 0x01234567 + +enum { + BLOB_ATTR_UNSPEC, + BLOB_ATTR_NESTED, + BLOB_ATTR_BINARY, + BLOB_ATTR_STRING, + BLOB_ATTR_INT8, + BLOB_ATTR_INT16, + BLOB_ATTR_INT32, + BLOB_ATTR_INT64, + BLOB_ATTR_LAST +}; + +#define BLOB_ATTR_ID_MASK 0x7f000000 +#define BLOB_ATTR_ID_SHIFT 24 +#define BLOB_ATTR_LEN_MASK 0x00ffffff +#define BLOB_ATTR_ALIGN 4 +#define BLOB_ATTR_EXTENDED 0x80000000 + +struct blob_attr { + uint32_t id_len; + char data[]; +} __packed; + +struct blob_attr_info { + unsigned int type; + unsigned int minlen; + unsigned int maxlen; + bool (*validate)(const struct blob_attr_info *, struct blob_attr *); +}; + +struct blob_buf { + struct blob_attr *head; + bool (*grow)(struct blob_buf *buf, int minlen); + int buflen; + void *buf; +}; + +/* + * blob_data: returns the data pointer for an attribute + */ +static inline void * +blob_data(const struct blob_attr *attr) +{ + return (void *) attr->data; +} + +/* + * blob_id: returns the id of an attribute + */ +static inline unsigned int +blob_id(const struct blob_attr *attr) +{ + int id = (be32_to_cpu(attr->id_len) & BLOB_ATTR_ID_MASK) >> BLOB_ATTR_ID_SHIFT; + return id; +} + +static inline bool +blob_is_extended(const struct blob_attr *attr) +{ + return !!(attr->id_len & cpu_to_be32(BLOB_ATTR_EXTENDED)); +} + +/* + * blob_len: returns the length of the attribute's payload + */ +static inline unsigned int +blob_len(const struct blob_attr *attr) +{ + return (be32_to_cpu(attr->id_len) & BLOB_ATTR_LEN_MASK) - sizeof(struct blob_attr); +} + +/* + * blob_raw_len: returns the complete length of an attribute (including the header) + */ +static inline unsigned int +blob_raw_len(const struct blob_attr *attr) +{ + return blob_len(attr) + sizeof(struct blob_attr); +} + +/* + * blob_pad_len: returns the padded length of an attribute (including the header) + */ +static inline unsigned int +blob_pad_len(const struct blob_attr *attr) +{ + unsigned int len = blob_raw_len(attr); + len = (len + BLOB_ATTR_ALIGN - 1) & ~(BLOB_ATTR_ALIGN - 1); + return len; +} + +static inline uint8_t +blob_get_u8(const struct blob_attr *attr) +{ + return *((uint8_t *) attr->data); +} + +static inline uint16_t +blob_get_u16(const struct blob_attr *attr) +{ + uint16_t *tmp = (uint16_t*)attr->data; + return be16_to_cpu(*tmp); +} + +static inline uint32_t +blob_get_u32(const struct blob_attr *attr) +{ + uint32_t *tmp = (uint32_t*)attr->data; + return be32_to_cpu(*tmp); +} + +static inline uint64_t +blob_get_u64(const struct blob_attr *attr) +{ + uint32_t *ptr = (uint32_t *) blob_data(attr); + uint64_t tmp = ((uint64_t) be32_to_cpu(ptr[0])) << 32; + tmp |= be32_to_cpu(ptr[1]); + return tmp; +} + +static inline int8_t +blob_get_int8(const struct blob_attr *attr) +{ + return blob_get_u8(attr); +} + +static inline int16_t +blob_get_int16(const struct blob_attr *attr) +{ + return blob_get_u16(attr); +} + +static inline int32_t +blob_get_int32(const struct blob_attr *attr) +{ + return blob_get_u32(attr); +} + +static inline int64_t +blob_get_int64(const struct blob_attr *attr) +{ + return blob_get_u64(attr); +} + +static inline const char * +blob_get_string(const struct blob_attr *attr) +{ + return attr->data; +} + +static inline struct blob_attr * +blob_next(const struct blob_attr *attr) +{ + return (struct blob_attr *) ((char *) attr + blob_pad_len(attr)); +} + +extern void blob_fill_pad(struct blob_attr *attr); +extern void blob_set_raw_len(struct blob_attr *attr, unsigned int len); +extern bool blob_attr_equal(const struct blob_attr *a1, const struct blob_attr *a2); +extern int blob_buf_init(struct blob_buf *buf, int id); +extern void blob_buf_free(struct blob_buf *buf); +extern bool blob_buf_grow(struct blob_buf *buf, int required); +extern struct blob_attr *blob_new(struct blob_buf *buf, int id, int payload); +extern void *blob_nest_start(struct blob_buf *buf, int id); +extern void blob_nest_end(struct blob_buf *buf, void *cookie); +extern struct blob_attr *blob_put(struct blob_buf *buf, int id, const void *ptr, unsigned int len); +extern bool blob_check_type(const void *ptr, unsigned int len, int type); +extern int blob_parse(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max); +extern struct blob_attr *blob_memdup(struct blob_attr *attr); +extern struct blob_attr *blob_put_raw(struct blob_buf *buf, const void *ptr, unsigned int len); + +static inline struct blob_attr * +blob_put_string(struct blob_buf *buf, int id, const char *str) +{ + return blob_put(buf, id, str, strlen(str) + 1); +} + +static inline struct blob_attr * +blob_put_u8(struct blob_buf *buf, int id, uint8_t val) +{ + return blob_put(buf, id, &val, sizeof(val)); +} + +static inline struct blob_attr * +blob_put_u16(struct blob_buf *buf, int id, uint16_t val) +{ + val = cpu_to_be16(val); + return blob_put(buf, id, &val, sizeof(val)); +} + +static inline struct blob_attr * +blob_put_u32(struct blob_buf *buf, int id, uint32_t val) +{ + val = cpu_to_be32(val); + return blob_put(buf, id, &val, sizeof(val)); +} + +static inline struct blob_attr * +blob_put_u64(struct blob_buf *buf, int id, uint64_t val) +{ + val = cpu_to_be64(val); + return blob_put(buf, id, &val, sizeof(val)); +} + +#define blob_put_int8 blob_put_u8 +#define blob_put_int16 blob_put_u16 +#define blob_put_int32 blob_put_u32 +#define blob_put_int64 blob_put_u64 + +#define __blob_for_each_attr(pos, attr, rem) \ + for (pos = (void *) attr; \ + rem > 0 && (blob_pad_len(pos) <= rem) && \ + (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ + rem -= blob_pad_len(pos), pos = blob_next(pos)) + + +#define blob_for_each_attr(pos, attr, rem) \ + for (rem = attr ? blob_len(attr) : 0, \ + pos = attr ? blob_data(attr) : 0; \ + rem > 0 && (blob_pad_len(pos) <= rem) && \ + (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ + rem -= blob_pad_len(pos), pos = blob_next(pos)) + + +#endif diff --git a/src/3P/libubox/blobmsg.c b/src/3P/libubox/blobmsg.c new file mode 100644 index 00000000..1e93376a --- /dev/null +++ b/src/3P/libubox/blobmsg.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2010-2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include "blobmsg.h" + +static const int blob_type[__BLOBMSG_TYPE_LAST] = { + [BLOBMSG_TYPE_INT8] = BLOB_ATTR_INT8, + [BLOBMSG_TYPE_INT16] = BLOB_ATTR_INT16, + [BLOBMSG_TYPE_INT32] = BLOB_ATTR_INT32, + [BLOBMSG_TYPE_INT64] = BLOB_ATTR_INT64, + [BLOBMSG_TYPE_STRING] = BLOB_ATTR_STRING, + [BLOBMSG_TYPE_UNSPEC] = BLOB_ATTR_BINARY, +}; + +static uint16_t +blobmsg_namelen(const struct blobmsg_hdr *hdr) +{ + return be16_to_cpu(hdr->namelen); +} + +bool blobmsg_check_attr(const struct blob_attr *attr, bool name) +{ + const struct blobmsg_hdr *hdr; + const char *data; + int id, len; + + if (blob_len(attr) < sizeof(struct blobmsg_hdr)) + return false; + + hdr = (void *) attr->data; + if (!hdr->namelen && name) + return false; + + if (blobmsg_namelen(hdr) > blob_len(attr) - sizeof(struct blobmsg_hdr)) + return false; + + if (hdr->name[blobmsg_namelen(hdr)] != 0) + return false; + + id = blob_id(attr); + len = blobmsg_data_len(attr); + data = blobmsg_data(attr); + + if (id > BLOBMSG_TYPE_LAST) + return false; + + if (!blob_type[id]) + return true; + + return blob_check_type(data, len, blob_type[id]); +} + +int blobmsg_check_array(const struct blob_attr *attr, int type) +{ + struct blob_attr *cur; + bool name; + int rem; + int size = 0; + + switch (blobmsg_type(attr)) { + case BLOBMSG_TYPE_TABLE: + name = true; + break; + case BLOBMSG_TYPE_ARRAY: + name = false; + break; + default: + return -1; + } + + blobmsg_for_each_attr(cur, attr, rem) { + if (type != BLOBMSG_TYPE_UNSPEC && blobmsg_type(cur) != type) + return -1; + + if (!blobmsg_check_attr(cur, name)) + return -1; + + size++; + } + + return size; +} + +bool blobmsg_check_attr_list(const struct blob_attr *attr, int type) +{ + return blobmsg_check_array(attr, type) >= 0; +} + +int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len, + struct blob_attr **tb, void *data, unsigned int len) +{ + struct blob_attr *attr; + int i = 0; + + memset(tb, 0, policy_len * sizeof(*tb)); + __blob_for_each_attr(attr, data, len) { + if (policy[i].type != BLOBMSG_TYPE_UNSPEC && + blob_id(attr) != policy[i].type) + continue; + + if (!blobmsg_check_attr(attr, false)) + return -1; + + if (tb[i]) + continue; + + tb[i++] = attr; + if (i == policy_len) + break; + } + + return 0; +} + + +int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len, + struct blob_attr **tb, void *data, unsigned int len) +{ + struct blobmsg_hdr *hdr; + struct blob_attr *attr; + uint8_t *pslen; + int i; + + memset(tb, 0, policy_len * sizeof(*tb)); + pslen = alloca(policy_len); + for (i = 0; i < policy_len; i++) { + if (!policy[i].name) + continue; + + pslen[i] = strlen(policy[i].name); + } + + __blob_for_each_attr(attr, data, len) { + hdr = blob_data(attr); + for (i = 0; i < policy_len; i++) { + if (!policy[i].name) + continue; + + if (policy[i].type != BLOBMSG_TYPE_UNSPEC && + blob_id(attr) != policy[i].type) + continue; + + if (blobmsg_namelen(hdr) != pslen[i]) + continue; + + if (!blobmsg_check_attr(attr, true)) + return -1; + + if (tb[i]) + continue; + + if (strcmp(policy[i].name, (char *) hdr->name) != 0) + continue; + + tb[i] = attr; + } + } + + return 0; +} + + +static struct blob_attr * +blobmsg_new(struct blob_buf *buf, int type, const char *name, int payload_len, void **data) +{ + struct blob_attr *attr; + struct blobmsg_hdr *hdr; + int attrlen, namelen; + char *pad_start, *pad_end; + + if (!name) + name = ""; + + namelen = strlen(name); + attrlen = blobmsg_hdrlen(namelen) + payload_len; + attr = blob_new(buf, type, attrlen); + if (!attr) + return NULL; + + attr->id_len |= be32_to_cpu(BLOB_ATTR_EXTENDED); + hdr = blob_data(attr); + hdr->namelen = cpu_to_be16(namelen); + strcpy((char *) hdr->name, (const char *)name); + pad_end = *data = blobmsg_data(attr); + pad_start = (char *) &hdr->name[namelen]; + if (pad_start < pad_end) + memset(pad_start, 0, pad_end - pad_start); + + return attr; +} + +static inline int +attr_to_offset(struct blob_buf *buf, struct blob_attr *attr) +{ + return (char *)attr - (char *) buf->buf + BLOB_COOKIE; +} + + +void * +blobmsg_open_nested(struct blob_buf *buf, const char *name, bool array) +{ + struct blob_attr *head; + int type = array ? BLOBMSG_TYPE_ARRAY : BLOBMSG_TYPE_TABLE; + unsigned long offset = attr_to_offset(buf, buf->head); + void *data; + + if (!name) + name = ""; + + head = blobmsg_new(buf, type, name, 0, &data); + if (!head) + return NULL; + blob_set_raw_len(buf->head, blob_pad_len(buf->head) - blobmsg_hdrlen(strlen(name))); + buf->head = head; + return (void *)offset; +} + +int +blobmsg_vprintf(struct blob_buf *buf, const char *name, const char *format, va_list arg) +{ + va_list arg2; + char cbuf; + char *sbuf; + int len, ret; + + va_copy(arg2, arg); + len = vsnprintf(&cbuf, sizeof(cbuf), format, arg2); + va_end(arg2); + + sbuf = blobmsg_alloc_string_buffer(buf, name, len + 1); + if (!sbuf) + return -1; + ret = vsprintf(sbuf, format, arg); + blobmsg_add_string_buffer(buf); + + return ret; +} + +int +blobmsg_printf(struct blob_buf *buf, const char *name, const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = blobmsg_vprintf(buf, name, format, ap); + va_end(ap); + + return ret; +} + +void * +blobmsg_alloc_string_buffer(struct blob_buf *buf, const char *name, unsigned int maxlen) +{ + struct blob_attr *attr; + void *data_dest; + + attr = blobmsg_new(buf, BLOBMSG_TYPE_STRING, name, maxlen, &data_dest); + if (!attr) + return NULL; + + blob_set_raw_len(buf->head, blob_pad_len(buf->head) - blob_pad_len(attr)); + blob_set_raw_len(attr, blob_raw_len(attr) - maxlen); + + return data_dest; +} + +void * +blobmsg_realloc_string_buffer(struct blob_buf *buf, unsigned int maxlen) +{ + struct blob_attr *attr = blob_next(buf->head); + int offset = attr_to_offset(buf, blob_next(buf->head)) + blob_pad_len(attr) - BLOB_COOKIE; + int required = maxlen - (buf->buflen - offset); + + if (required <= 0) + goto out; + + if (!blob_buf_grow(buf, required)) + return NULL; + attr = blob_next(buf->head); + +out: + return blobmsg_data(attr); +} + +void +blobmsg_add_string_buffer(struct blob_buf *buf) +{ + struct blob_attr *attr; + int len, attrlen; + + attr = blob_next(buf->head); + len = strlen(blobmsg_data(attr)) + 1; + + attrlen = blob_raw_len(attr) + len; + blob_set_raw_len(attr, attrlen); + blob_fill_pad(attr); + + blob_set_raw_len(buf->head, blob_raw_len(buf->head) + blob_pad_len(attr)); +} + +int +blobmsg_add_field(struct blob_buf *buf, int type, const char *name, + const void *data, unsigned int len) +{ + struct blob_attr *attr; + void *data_dest; + + attr = blobmsg_new(buf, type, name, len, &data_dest); + if (!attr) + return -1; + + if (len > 0) + memcpy(data_dest, data, len); + + return 0; +} diff --git a/src/3P/libubox/blobmsg.h b/src/3P/libubox/blobmsg.h new file mode 100644 index 00000000..84997a67 --- /dev/null +++ b/src/3P/libubox/blobmsg.h @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2010-2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __BLOBMSG_H +#define __BLOBMSG_H + +#include +#include "blob.h" + +#define BLOBMSG_ALIGN 2 +#define BLOBMSG_PADDING(len) (((len) + (1 << BLOBMSG_ALIGN) - 1) & ~((1 << BLOBMSG_ALIGN) - 1)) + +enum blobmsg_type { + BLOBMSG_TYPE_UNSPEC, + BLOBMSG_TYPE_ARRAY, + BLOBMSG_TYPE_TABLE, + BLOBMSG_TYPE_STRING, + BLOBMSG_TYPE_INT64, + BLOBMSG_TYPE_INT32, + BLOBMSG_TYPE_INT16, + BLOBMSG_TYPE_INT8, + __BLOBMSG_TYPE_LAST, + BLOBMSG_TYPE_LAST = __BLOBMSG_TYPE_LAST - 1, + BLOBMSG_TYPE_BOOL = BLOBMSG_TYPE_INT8, +}; + +struct blobmsg_hdr { + uint16_t namelen; + uint8_t name[]; +} __packed; + +struct blobmsg_policy { + const char *name; + enum blobmsg_type type; +}; + +static inline int blobmsg_hdrlen(unsigned int namelen) +{ + return BLOBMSG_PADDING(sizeof(struct blobmsg_hdr) + namelen + 1); +} + +static inline void blobmsg_clear_name(struct blob_attr *attr) +{ + struct blobmsg_hdr *hdr = (struct blobmsg_hdr *) blob_data(attr); + hdr->name[0] = 0; +} + +static inline const char *blobmsg_name(const struct blob_attr *attr) +{ + struct blobmsg_hdr *hdr = (struct blobmsg_hdr *) blob_data(attr); + return (const char *) hdr->name; +} + +static inline int blobmsg_type(const struct blob_attr *attr) +{ + return blob_id(attr); +} + +static inline void *blobmsg_data(const struct blob_attr *attr) +{ + struct blobmsg_hdr *hdr = (struct blobmsg_hdr *) blob_data(attr); + char *data = (char *) blob_data(attr); + + if (blob_is_extended(attr)) + data += blobmsg_hdrlen(be16_to_cpu(hdr->namelen)); + + return data; +} + +static inline int blobmsg_data_len(const struct blob_attr *attr) +{ + uint8_t *start, *end; + + start = (uint8_t *) blob_data(attr); + end = (uint8_t *) blobmsg_data(attr); + + return blob_len(attr) - (end - start); +} + +static inline int blobmsg_len(const struct blob_attr *attr) +{ + return blobmsg_data_len(attr); +} + +bool blobmsg_check_attr(const struct blob_attr *attr, bool name); +bool blobmsg_check_attr_list(const struct blob_attr *attr, int type); + +/* + * blobmsg_check_array: validate array/table and return size + * + * Checks if all elements of an array or table are valid and have + * the specified type. Returns the number of elements in the array + */ +int blobmsg_check_array(const struct blob_attr *attr, int type); + +int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len, + struct blob_attr **tb, void *data, unsigned int len); +int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len, + struct blob_attr **tb, void *data, unsigned int len); + +int blobmsg_add_field(struct blob_buf *buf, int type, const char *name, + const void *data, unsigned int len); + +static inline int +blobmsg_add_u8(struct blob_buf *buf, const char *name, uint8_t val) +{ + return blobmsg_add_field(buf, BLOBMSG_TYPE_INT8, name, &val, 1); +} + +static inline int +blobmsg_add_u16(struct blob_buf *buf, const char *name, uint16_t val) +{ + val = cpu_to_be16(val); + return blobmsg_add_field(buf, BLOBMSG_TYPE_INT16, name, &val, 2); +} + +static inline int +blobmsg_add_u32(struct blob_buf *buf, const char *name, uint32_t val) +{ + val = cpu_to_be32(val); + return blobmsg_add_field(buf, BLOBMSG_TYPE_INT32, name, &val, 4); +} + +static inline int +blobmsg_add_u64(struct blob_buf *buf, const char *name, uint64_t val) +{ + val = cpu_to_be64(val); + return blobmsg_add_field(buf, BLOBMSG_TYPE_INT64, name, &val, 8); +} + +static inline int +blobmsg_add_string(struct blob_buf *buf, const char *name, const char *string) +{ + return blobmsg_add_field(buf, BLOBMSG_TYPE_STRING, name, string, strlen(string) + 1); +} + +static inline int +blobmsg_add_blob(struct blob_buf *buf, struct blob_attr *attr) +{ + return blobmsg_add_field(buf, blobmsg_type(attr), blobmsg_name(attr), + blobmsg_data(attr), blobmsg_data_len(attr)); +} + +void *blobmsg_open_nested(struct blob_buf *buf, const char *name, bool array); + +static inline void * +blobmsg_open_array(struct blob_buf *buf, const char *name) +{ + return blobmsg_open_nested(buf, name, true); +} + +static inline void * +blobmsg_open_table(struct blob_buf *buf, const char *name) +{ + return blobmsg_open_nested(buf, name, false); +} + +static inline void +blobmsg_close_array(struct blob_buf *buf, void *cookie) +{ + blob_nest_end(buf, cookie); +} + +static inline void +blobmsg_close_table(struct blob_buf *buf, void *cookie) +{ + blob_nest_end(buf, cookie); +} + +static inline int blobmsg_buf_init(struct blob_buf *buf) +{ + return blob_buf_init(buf, BLOBMSG_TYPE_TABLE); +} + +static inline uint8_t blobmsg_get_u8(struct blob_attr *attr) +{ + return *(uint8_t *) blobmsg_data(attr); +} + +static inline bool blobmsg_get_bool(struct blob_attr *attr) +{ + return *(uint8_t *) blobmsg_data(attr); +} + +static inline uint16_t blobmsg_get_u16(struct blob_attr *attr) +{ + return be16_to_cpu(*(uint16_t *) blobmsg_data(attr)); +} + +static inline uint32_t blobmsg_get_u32(struct blob_attr *attr) +{ + return be32_to_cpu(*(uint32_t *) blobmsg_data(attr)); +} + +static inline uint64_t blobmsg_get_u64(struct blob_attr *attr) +{ + uint32_t *ptr = (uint32_t *) blobmsg_data(attr); + uint64_t tmp = ((uint64_t) be32_to_cpu(ptr[0])) << 32; + tmp |= be32_to_cpu(ptr[1]); + return tmp; +} + +static inline char *blobmsg_get_string(struct blob_attr *attr) +{ + if (!attr) + return NULL; + + return (char *) blobmsg_data(attr); +} + +void *blobmsg_alloc_string_buffer(struct blob_buf *buf, const char *name, unsigned int maxlen); +void *blobmsg_realloc_string_buffer(struct blob_buf *buf, unsigned int maxlen); +void blobmsg_add_string_buffer(struct blob_buf *buf); + +int blobmsg_vprintf(struct blob_buf *buf, const char *name, const char *format, va_list arg); +int blobmsg_printf(struct blob_buf *buf, const char *name, const char *format, ...) + __attribute__((format(printf, 3, 4))); + + +/* blobmsg to json formatting */ + +#define blobmsg_for_each_attr(pos, attr, rem) \ + for (rem = attr ? blobmsg_data_len(attr) : 0, \ + pos = attr ? blobmsg_data(attr) : 0; \ + rem > 0 && (blob_pad_len(pos) <= rem) && \ + (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ + rem -= blob_pad_len(pos), pos = blob_next(pos)) + +#endif diff --git a/src/3P/libubox/blobmsg_json.c b/src/3P/libubox/blobmsg_json.c new file mode 100644 index 00000000..72d0de6e --- /dev/null +++ b/src/3P/libubox/blobmsg_json.c @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2010-2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include "blobmsg.h" +#include "blobmsg_json.h" + +#ifdef JSONC + #include +#else + #include +#endif + +bool blobmsg_add_object(struct blob_buf *b, json_object *obj) +{ + json_object_object_foreach(obj, key, val) { + if (!blobmsg_add_json_element(b, key, val)) + return false; + } + return true; +} + +static bool blobmsg_add_array(struct blob_buf *b, struct array_list *a) +{ + int i, len; + + for (i = 0, len = array_list_length(a); i < len; i++) { + if (!blobmsg_add_json_element(b, NULL, array_list_get_idx(a, i))) + return false; + } + + return true; +} + +bool blobmsg_add_json_element(struct blob_buf *b, const char *name, json_object *obj) +{ + bool ret = true; + void *c; + + switch (json_object_get_type(obj)) { + case json_type_object: + c = blobmsg_open_table(b, name); + ret = blobmsg_add_object(b, obj); + blobmsg_close_table(b, c); + break; + case json_type_array: + c = blobmsg_open_array(b, name); + ret = blobmsg_add_array(b, json_object_get_array(obj)); + blobmsg_close_array(b, c); + break; + case json_type_string: + blobmsg_add_string(b, name, json_object_get_string(obj)); + break; + case json_type_boolean: + blobmsg_add_u8(b, name, json_object_get_boolean(obj)); + break; + case json_type_int: + blobmsg_add_u32(b, name, json_object_get_int(obj)); + break; + case json_type_null: + blobmsg_add_field(b, BLOBMSG_TYPE_UNSPEC, name, NULL, 0); + break; + default: + return false; + } + return ret; +} + +static bool __blobmsg_add_json(struct blob_buf *b, json_object *obj) +{ + bool ret = false; + + if (!obj) + return false; + + if (json_object_get_type(obj) != json_type_object) + goto out; + + ret = blobmsg_add_object(b, obj); + +out: + json_object_put(obj); + return ret; +} + +bool blobmsg_add_json_from_file(struct blob_buf *b, const char *file) +{ + return __blobmsg_add_json(b, json_object_from_file(file)); +} + +bool blobmsg_add_json_from_string(struct blob_buf *b, const char *str) +{ + return __blobmsg_add_json(b, json_tokener_parse(str)); +} + + +struct strbuf { + int len; + int pos; + char *buf; + + blobmsg_json_format_t custom_format; + void *priv; + bool indent; + int indent_level; +}; + +static bool blobmsg_puts(struct strbuf *s, const char *c, int len) +{ + size_t new_len; + char *new_buf; + + if (len <= 0) + return true; + + if (s->pos + len >= s->len) { + new_len = s->len + 16 + len; + new_buf = realloc(s->buf, new_len); + if (!new_buf) + return false; + + s->len = new_len; + s->buf = new_buf; + } + + memcpy(s->buf + s->pos, c, len); + s->pos += len; + return true; +} + +static void add_separator(struct strbuf *s) +{ + const char *indent_chars = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; + int len; + + if (!s->indent) + return; + + len = s->indent_level + 1; + if (len > strlen(indent_chars)) + len = strlen(indent_chars); + + blobmsg_puts(s, indent_chars, len); +} + + +static void blobmsg_format_string(struct strbuf *s, const char *str) +{ + const unsigned char *p, *last, *end; + char buf[8] = "\\u00"; + + end = (unsigned char *) str + strlen(str); + blobmsg_puts(s, "\"", 1); + for (p = (unsigned char *) str, last = p; *p; p++) { + char escape = '\0'; + int len; + + switch(*p) { + case '\b': + escape = 'b'; + break; + case '\n': + escape = 'n'; + break; + case '\t': + escape = 't'; + break; + case '\r': + escape = 'r'; + break; + case '"': + case '\\': + case '/': + escape = *p; + break; + default: + if (*p < ' ') + escape = 'u'; + break; + } + + if (!escape) + continue; + + if (p > last) + blobmsg_puts(s, (char *) last, p - last); + last = p + 1; + buf[1] = escape; + + if (escape == 'u') { + sprintf(buf + 4, "%02x", (unsigned char) *p); + len = 6; + } else { + len = 2; + } + blobmsg_puts(s, buf, len); + } + + blobmsg_puts(s, (char *) last, end - last); + blobmsg_puts(s, "\"", 1); +} + +static void blobmsg_format_json_list(struct strbuf *s, struct blob_attr *attr, int len, bool array); + +static void blobmsg_format_element(struct strbuf *s, struct blob_attr *attr, bool without_name, bool head) +{ + const char *data_str; + char buf[32]; + void *data; + int len; + + if (!blobmsg_check_attr(attr, false)) + return; + + if (!without_name && blobmsg_name(attr)[0]) { + blobmsg_format_string(s, blobmsg_name(attr)); + blobmsg_puts(s, ": ", s->indent ? 2 : 1); + } + + data = blobmsg_data(attr); + len = blobmsg_data_len(attr); + + if (!head && s->custom_format) { + data_str = s->custom_format(s->priv, attr); + if (data_str) + goto out; + } + + data_str = buf; + switch(blob_id(attr)) { + case BLOBMSG_TYPE_UNSPEC: + sprintf(buf, "null"); + break; + case BLOBMSG_TYPE_BOOL: + sprintf(buf, "%s", *(uint8_t *)data ? "true" : "false"); + break; + case BLOBMSG_TYPE_INT16: + sprintf(buf, "%d", be16_to_cpu(*(uint16_t *)data)); + break; + case BLOBMSG_TYPE_INT32: + sprintf(buf, "%d", (int32_t) be32_to_cpu(*(uint32_t *)data)); + break; + case BLOBMSG_TYPE_INT64: + sprintf(buf, "%" PRId64, (int64_t) be64_to_cpu(*(uint64_t *)data)); + break; + case BLOBMSG_TYPE_STRING: + blobmsg_format_string(s, data); + return; + case BLOBMSG_TYPE_ARRAY: + blobmsg_format_json_list(s, data, len, true); + return; + case BLOBMSG_TYPE_TABLE: + blobmsg_format_json_list(s, data, len, false); + return; + } + +out: + blobmsg_puts(s, data_str, strlen(data_str)); +} + +static void blobmsg_format_json_list(struct strbuf *s, struct blob_attr *attr, int len, bool array) +{ + struct blob_attr *pos; + bool first = true; + int rem = len; + + blobmsg_puts(s, (array ? "[" : "{" ), 1); + s->indent_level++; + add_separator(s); + __blob_for_each_attr(pos, attr, rem) { + if (!first) { + blobmsg_puts(s, ",", 1); + add_separator(s); + } + + blobmsg_format_element(s, pos, array, false); + first = false; + } + s->indent_level--; + add_separator(s); + blobmsg_puts(s, (array ? "]" : "}"), 1); +} + +static void setup_strbuf(struct strbuf *s, struct blob_attr *attr, blobmsg_json_format_t cb, void *priv, int indent) +{ + s->len = blob_len(attr); + s->buf = malloc(s->len); + s->pos = 0; + s->custom_format = cb; + s->priv = priv; + s->indent = false; + + if (indent >= 0) { + s->indent = true; + s->indent_level = indent; + } +} + +char *blobmsg_format_json_with_cb(struct blob_attr *attr, bool list, blobmsg_json_format_t cb, void *priv, int indent) +{ + struct strbuf s; + bool array; + char *ret; + + setup_strbuf(&s, attr, cb, priv, indent); + if (!s.buf) + return NULL; + + array = blob_is_extended(attr) && + blobmsg_type(attr) == BLOBMSG_TYPE_ARRAY; + + if (list) + blobmsg_format_json_list(&s, blobmsg_data(attr), blobmsg_data_len(attr), array); + else + blobmsg_format_element(&s, attr, false, false); + + if (!s.len) { + free(s.buf); + return NULL; + } + + ret = realloc(s.buf, s.pos + 1); + if (!ret) { + free(s.buf); + return NULL; + } + + ret[s.pos] = 0; + + return ret; +} + +char *blobmsg_format_json_value_with_cb(struct blob_attr *attr, blobmsg_json_format_t cb, void *priv, int indent) +{ + struct strbuf s; + char *ret; + + setup_strbuf(&s, attr, cb, priv, indent); + if (!s.buf) + return NULL; + + blobmsg_format_element(&s, attr, true, false); + + if (!s.len) { + free(s.buf); + return NULL; + } + + ret = realloc(s.buf, s.pos + 1); + if (!ret) { + free(s.buf); + return NULL; + } + + ret[s.pos] = 0; + + return ret; +} diff --git a/src/3P/libubox/blobmsg_json.h b/src/3P/libubox/blobmsg_json.h new file mode 100644 index 00000000..9dfc02dc --- /dev/null +++ b/src/3P/libubox/blobmsg_json.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010-2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __BLOBMSG_JSON_H +#define __BLOBMSG_JSON_H + +struct json_object; + +#include +#include "blobmsg.h" + +bool blobmsg_add_object(struct blob_buf *b, struct json_object *obj); +bool blobmsg_add_json_element(struct blob_buf *b, const char *name, struct json_object *obj); +bool blobmsg_add_json_from_string(struct blob_buf *b, const char *str); +bool blobmsg_add_json_from_file(struct blob_buf *b, const char *file); + +typedef const char *(*blobmsg_json_format_t)(void *priv, struct blob_attr *attr); + +char *blobmsg_format_json_with_cb(struct blob_attr *attr, bool list, + blobmsg_json_format_t cb, void *priv, + int indent); + +static inline char *blobmsg_format_json(struct blob_attr *attr, bool list) +{ + return blobmsg_format_json_with_cb(attr, list, NULL, NULL, -1); +} + +static inline char *blobmsg_format_json_indent(struct blob_attr *attr, bool list, int indent) +{ + return blobmsg_format_json_with_cb(attr, list, NULL, NULL, indent); +} + +char *blobmsg_format_json_value_with_cb(struct blob_attr *attr, + blobmsg_json_format_t cb, void *priv, + int indent); + +static inline char *blobmsg_format_json_value(struct blob_attr *attr) +{ + return blobmsg_format_json_value_with_cb(attr, NULL, NULL, -1); +} + +static inline char *blobmsg_format_json_value_indent(struct blob_attr *attr, int indent) +{ + return blobmsg_format_json_value_with_cb(attr, NULL, NULL, indent); +} + +#endif diff --git a/src/3P/libubox/builders/cmake/CMakeLists.txt b/src/3P/libubox/builders/cmake/CMakeLists.txt new file mode 100644 index 00000000..a2ba59f0 --- /dev/null +++ b/src/3P/libubox/builders/cmake/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required (VERSION 3.0) + +project (libubox) + +set (CMAKE_MODULE_PATH "${MODULE_PATH}") + +set(DISABLE_TARGET_OPTIMIZATION ON) + +include (aw) + +ADD_DEFINITIONS(-DJSONC) + +include_directories ($ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/) + +file ( + GLOB_RECURSE + source_files + + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/avl.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/avl-cmp.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/blob.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/blobmsg.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/uloop.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/usock.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/ustream.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/ustream-fd.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/vlist.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/utils.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/safe_list.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/runqueue.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/md5.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/kvlist.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/ulog.c + $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/base64.c + ) + +add_library (ubox SHARED ${source_files}) +target_link_libraries (ubox + LINK_PUBLIC + json-c + rt + pthread + ) + +target_include_directories (ubox PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + + +file (GLOB ubox_headers $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/*.h) +install (FILES ${ubox_headers} DESTINATION include/libubox) + +add_library (json_script SHARED $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/json_script.c) +target_link_libraries (json_script ubox) + +add_library (blobmsg_json SHARED $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/blobmsg_json.c) +target_link_libraries (blobmsg_json json-c) + +add_executable (jshn $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/jshn.c) +target_link_libraries (jshn ubox blobmsg_json json-c) + +install (TARGETS ubox json_script blobmsg_json + LIBRARY DESTINATION ../lib +) + +install (TARGETS jshn RUNTIME DESTINATION bin) + +install (FILES $ENV{AWOXCVS}/AwoxAudio/Libs/External/libubox/sh/jshn.sh + DESTINATION share/libubox) diff --git a/src/3P/libubox/examples/CMakeLists.txt b/src/3P/libubox/examples/CMakeLists.txt new file mode 100644 index 00000000..a6355356 --- /dev/null +++ b/src/3P/libubox/examples/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 2.6) + +IF (BUILD_EXAMPLES) + PROJECT(ubox-examples C) + ADD_DEFINITIONS(-O1 -Wall -Werror --std=gnu99 -g3) + + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) + LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) + + FIND_LIBRARY(json NAMES json-c json) + + ADD_EXECUTABLE(blobmsg-example blobmsg-example.c) + TARGET_LINK_LIBRARIES(blobmsg-example ubox blobmsg_json ${json}) + + ADD_EXECUTABLE(ustream-example ustream-example.c) + TARGET_LINK_LIBRARIES(ustream-example ubox) + + ADD_EXECUTABLE(runqueue-example runqueue-example.c) + TARGET_LINK_LIBRARIES(runqueue-example ubox) + + ADD_EXECUTABLE(json_script-example json_script-example.c) + TARGET_LINK_LIBRARIES(json_script-example ubox blobmsg_json json_script ${json}) +ENDIF() diff --git a/src/3P/libubox/examples/blobmsg-example.c b/src/3P/libubox/examples/blobmsg-example.c new file mode 100644 index 00000000..01b05181 --- /dev/null +++ b/src/3P/libubox/examples/blobmsg-example.c @@ -0,0 +1,140 @@ +#include +#include + +#include "blobmsg.h" +#include "blobmsg_json.h" + +static const char *indent_str = "\t\t\t\t\t\t\t\t\t\t\t\t\t"; + +#define indent_printf(indent, ...) do { \ + if (indent > 0) \ + fwrite(indent_str, indent, 1, stderr); \ + fprintf(stderr, __VA_ARGS__); \ +} while(0) + +static void dump_attr_data(struct blob_attr *data, int indent, int next_indent); + +static void +dump_table(struct blob_attr *head, int len, int indent, bool array) +{ + struct blob_attr *attr; + struct blobmsg_hdr *hdr; + + indent_printf(indent, "{\n"); + __blob_for_each_attr(attr, head, len) { + hdr = blob_data(attr); + if (!array) + indent_printf(indent + 1, "%s : ", hdr->name); + dump_attr_data(attr, 0, indent + 1); + } + indent_printf(indent, "}\n"); +} + +static void dump_attr_data(struct blob_attr *data, int indent, int next_indent) +{ + int type = blobmsg_type(data); + switch(type) { + case BLOBMSG_TYPE_STRING: + indent_printf(indent, "%s\n", blobmsg_get_string(data)); + break; + case BLOBMSG_TYPE_INT8: + indent_printf(indent, "%d\n", blobmsg_get_u8(data)); + break; + case BLOBMSG_TYPE_INT16: + indent_printf(indent, "%d\n", blobmsg_get_u16(data)); + break; + case BLOBMSG_TYPE_INT32: + indent_printf(indent, "%d\n", blobmsg_get_u32(data)); + break; + case BLOBMSG_TYPE_INT64: + indent_printf(indent, "%"PRIu64"\n", blobmsg_get_u64(data)); + break; + case BLOBMSG_TYPE_TABLE: + case BLOBMSG_TYPE_ARRAY: + if (!indent) + indent_printf(indent, "\n"); + dump_table(blobmsg_data(data), blobmsg_data_len(data), + next_indent, type == BLOBMSG_TYPE_ARRAY); + break; + } +} + +enum { + FOO_MESSAGE, + FOO_LIST, + FOO_TESTDATA +}; + +static const struct blobmsg_policy pol[] = { + [FOO_MESSAGE] = { + .name = "message", + .type = BLOBMSG_TYPE_STRING, + }, + [FOO_LIST] = { + .name = "list", + .type = BLOBMSG_TYPE_ARRAY, + }, + [FOO_TESTDATA] = { + .name = "testdata", + .type = BLOBMSG_TYPE_TABLE, + }, +}; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +static void dump_message(struct blob_buf *buf) +{ + struct blob_attr *tb[ARRAY_SIZE(pol)]; + + if (blobmsg_parse(pol, ARRAY_SIZE(pol), tb, blob_data(buf->head), blob_len(buf->head)) != 0) { + fprintf(stderr, "Parse failed\n"); + return; + } + if (tb[FOO_MESSAGE]) + fprintf(stderr, "Message: %s\n", (char *) blobmsg_data(tb[FOO_MESSAGE])); + + if (tb[FOO_LIST]) { + fprintf(stderr, "List: "); + dump_table(blobmsg_data(tb[FOO_LIST]), blobmsg_data_len(tb[FOO_LIST]), 0, true); + } + if (tb[FOO_TESTDATA]) { + fprintf(stderr, "Testdata: "); + dump_table(blobmsg_data(tb[FOO_TESTDATA]), blobmsg_data_len(tb[FOO_TESTDATA]), 0, false); + } +} + +static void +fill_message(struct blob_buf *buf) +{ + void *tbl; + + blobmsg_add_string(buf, "message", "Hello, world!"); + + tbl = blobmsg_open_table(buf, "testdata"); + blobmsg_add_u32(buf, "hello", 1); + blobmsg_add_string(buf, "world", "2"); + blobmsg_close_table(buf, tbl); + + tbl = blobmsg_open_array(buf, "list"); + blobmsg_add_u32(buf, NULL, 0); + blobmsg_add_u32(buf, NULL, 1); + blobmsg_add_u32(buf, NULL, 2); + blobmsg_close_table(buf, tbl); +} + +int main(int argc, char **argv) +{ + static struct blob_buf buf; + + blobmsg_buf_init(&buf); + fill_message(&buf); + dump_message(&buf); + fprintf(stderr, "json: %s\n", blobmsg_format_json(buf.head, true)); + + if (buf.buf) + free(buf.buf); + + return 0; +} diff --git a/src/3P/libubox/examples/json_script-example.c b/src/3P/libubox/examples/json_script-example.c new file mode 100644 index 00000000..4d252a9a --- /dev/null +++ b/src/3P/libubox/examples/json_script-example.c @@ -0,0 +1,84 @@ +#include +#include + +#include +#include "blobmsg.h" +#include "blobmsg_json.h" +#include "json_script.h" + +struct json_script_ctx jctx; +struct blob_buf b_vars; +struct blob_buf b_script; + +static void handle_command(struct json_script_ctx *ctx, const char *name, + struct blob_attr *data, struct blob_attr *vars) +{ + struct blob_attr *cur; + int rem; + + fprintf(stdout, "%s", name); + blobmsg_for_each_attr(cur, data, rem) + fprintf(stdout, " %s", (char *) blobmsg_data(cur)); + fprintf(stdout, "\n"); +} + +static struct json_script_file * +handle_file(struct json_script_ctx *ctx, const char *filename) +{ + json_object *obj; + + obj = json_object_from_file(filename); + if (!obj) { + fprintf(stderr, "load JSON data from %s failed.\n", filename); + return NULL; + } + + blob_buf_init(&b_script, 0); + blobmsg_add_json_element(&b_script, "", obj); + json_object_put(obj); + + return json_script_file_from_blobmsg(filename, + blob_data(b_script.head), blob_len(b_script.head)); +} + +static void usage(const char *prog, int exit_code) +{ + fprintf(stderr, "Usage: %s [VARNAME=value] \n", prog); + exit(exit_code); +} + +int main(int argc, char *argv[]) +{ + int i; + char *file = NULL; + const char *prog = argv[0]; + + blobmsg_buf_init(&b_vars); + blobmsg_buf_init(&b_script); + + json_script_init(&jctx); + jctx.handle_command = handle_command; + jctx.handle_file = handle_file; + + for (i = 1; i < argc; i++) { + char *sep = strchr(argv[i], '='); + if (sep) { + *sep = '\0'; + blobmsg_add_string(&b_vars, argv[i], sep + 1); + } else if (!file) { + file = argv[i]; + } else { + usage(prog, -1); + } + } + if (i < argc || !file) + usage(prog, -2); + + json_script_run(&jctx, file, b_vars.head); + + json_script_free(&jctx); + blob_buf_free(&b_script); + blob_buf_free(&b_vars); + + return 0; +} diff --git a/src/3P/libubox/examples/json_script-example.json b/src/3P/libubox/examples/json_script-example.json new file mode 100644 index 00000000..5328e599 --- /dev/null +++ b/src/3P/libubox/examples/json_script-example.json @@ -0,0 +1,38 @@ +[ + [ "exec", "%EXECVAR%", "/%%/" ], + [ "if", + [ "eq", "EQVAR", "eqval" ], + [ "exec_if", "%VAR%", "%%", "jk" ] + ], + [ "case", "CASEVAR", { + "caseval0": ["cmd_case_0", "cmd_case_arg0", "case_cmd_arg1"], + "caseval1": ["cmd_case_1", "cmd_case_arg0", "case_cmd_arg1"] + } ], + + [ "if", + [ "and", [ "eq", "EQVAR", "eqval" ], + [ "has", "HASVAR" ], + [ "regex", "REGEXVAR0", "regexval" ], + [ "regex", "REGEXVAR1", [ "regexval10", "regexval11" ] ], + [ "not", [ "eq", "NOTEQVAR", "noteqval" ] ] ], + [ "exec_if_and", "%ANDVAR%" ] + ], + + [ "if", + [ "or", [ "eq", "EQVAR", "eqval" ], + [ "has", "HASVAR" ], + [ "regex", "REGEXVAR0", "regexval" ], + [ "regex", "REGEXVAR1", [ "regexval10", "regexval11" ] ], + [ "not", [ "eq", "NOTEQVAR", "noteqval" ] ] ], + [ "exec_if_or", "%ORVAR%" ] + ], + + [ "if", + [ "isdir", "%ISDIRVAR%" ], + [ "exec_isdir", "%ISDIRVAR%" ] + ], + + [ "return", "foobar" ], + + [ "exec_non_reachable", "Arghhh" ] +] diff --git a/src/3P/libubox/examples/json_script-tests.sh b/src/3P/libubox/examples/json_script-tests.sh new file mode 100644 index 00000000..7120a7e8 --- /dev/null +++ b/src/3P/libubox/examples/json_script-tests.sh @@ -0,0 +1,287 @@ +JSON_SCRIPT=tests.json +JSON_SCRIPT_BIN=./json_script-example +FILE_STDOUT=tests.stdout +FILE_STDERR=tests.stderr +FILE_EXPECTED=tests.expected + +call_json_script() { + #export LD_PRELOAD=../libjson_script.so + $JSON_SCRIPT_BIN "$@" "$JSON_SCRIPT" >"$FILE_STDOUT" 2>"$FILE_STDERR" +} + +assertStdioEquals() { + local expected="$1" + local file_stdio="$2" + + echo "$expected" >"$FILE_EXPECTED" + if [ -z "$expected" ]; then + # we are expecting empty output, but we deliberately added a newline + # with echo above, so adding another echo to compensate for that + echo >>"$file_stdio" + fi + diff -up "$FILE_EXPECTED" "$file_stdio" >/dev/null 2>&1 || { + cat >&2 <"$JSON_SCRIPT" <<-EOF + [ + [ ] + [ ] + ] + EOF + call_json_script + assertStderrEquals "load JSON data from $JSON_SCRIPT failed." +} + +test_expr_eq() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "eq", "VAR", "foo" ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=foo" + assertStdoutEquals "echo bar" + call_json_script "VAR=xxx" + assertStdoutEquals "echo baz" +} + +test_expr_has() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "has", "VAR" ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=foo" + assertStdoutEquals "echo bar" + call_json_script + assertStdoutEquals "echo baz" +} + +test_expr_regex_single() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "regex", "VAR", ".ell." ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=hello" + assertStdoutEquals "echo bar" + call_json_script "VAR=.ell." + assertStdoutEquals "echo bar" + call_json_script + assertStdoutEquals "echo baz" + call_json_script "VAR=" + assertStdoutEquals "echo baz" + call_json_script "VAR=hell" + assertStdoutEquals "echo baz" +} + +test_expr_regex_multi() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "regex", "VAR", [ ".ell.", "w.rld" ] ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=hello" + assertStdoutEquals "echo bar" + call_json_script "VAR=world" + assertStdoutEquals "echo bar" + call_json_script "VAR=.ell." + assertStdoutEquals "echo bar" + call_json_script "VAR=w.rld" + assertStdoutEquals "echo bar" + call_json_script + assertStdoutEquals "echo baz" + call_json_script "VAR=" + assertStdoutEquals "echo baz" + call_json_script "VAR=hell" + assertStdoutEquals "echo baz" +} + +test_expr_not() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "not", [ "has", "VAR" ] ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=foo" + assertStdoutEquals "echo baz" + call_json_script + assertStdoutEquals "echo bar" +} + +test_expr_and() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "and", [ "eq", "EQVAR", "eqval" ], + [ "regex", "REGEXVAR", "regex..." ] + ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "EQVAR=eqval" "REGEXVAR=regexval" + assertStdoutEquals "echo bar" + call_json_script "EQVAR=foo" + assertStdoutEquals "echo baz" + call_json_script "REGEXVAR=regex***" + assertStdoutEquals "echo baz" + call_json_script + assertStdoutEquals "echo baz" +} + +test_expr_or() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "or", [ "not", [ "eq", "EQVAR", "eqval" ] ], + [ "regex", "REGEXVAR", [ "regexva.[0-9]", "regexva.[a-z]" ] ] + ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "EQVAR=eqval" "REGEXVAR=regexval1" + assertStdoutEquals "echo bar" + call_json_script "EQVAR=neq" "REGEXVAR=sxc" + assertStdoutEquals "echo bar" + call_json_script "REGEXVAR=sxc" + assertStdoutEquals "echo bar" + call_json_script "EQVAR=foo" + assertStdoutEquals "echo bar" + call_json_script + assertStdoutEquals "echo bar" + call_json_script "EQVAR=eqval" "REGEXVAR=regexval" + assertStdoutEquals "echo baz" +} + +test_expr_isdir() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "isdir", "%VAR%" ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=/" + assertStdoutEquals "echo bar" + call_json_script "VAR=$(mktemp -u)" + assertStdoutEquals "echo baz" + call_json_script + assertStdoutEquals "echo baz" +} + +test_cmd_case() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "case", "CASEVAR", { + "0": [ "echo", "foo" ], + "1": [ + [ "echo", "bar" ], + [ "echo", "baz" ] + ], + "%VAR%": [ "echo", "quz" ] + } ] + ] + EOF + call_json_script "CASEVAR=0" + assertStdoutEquals "echo foo" + call_json_script "CASEVAR=1" + assertStdoutEquals "echo bar +echo baz" + call_json_script "CASEVAR=%VAR%" + assertStdoutEquals "echo quz" + call_json_script "CASEVAR=" + assertStdoutEquals "" + call_json_script + assertStdoutEquals "" + call_json_script "CASEVAR=xxx" "VAR=xxx" + assertStdoutEquals "" +} + +test_cmd_if() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "eq", "VAR", "foo" ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=foo" + assertStdoutEquals "echo bar" + call_json_script "VAR=xxx" + assertStdoutEquals "echo baz" +} + +test_cmd_cb() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "exec", "%VAR%", "/%VAS%%%/" ] + ] + EOF + call_json_script + assertStdoutEquals "exec /%/" + call_json_script "VAR=" + assertStdoutEquals "exec /%/" + call_json_script "VAR=qux" "VAS=3" + assertStdoutEquals "exec qux /3%/" +} + +test_cmd_return() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "heh", "%HEHVAR%" ], + [ "%VAR%", "%VAR%" ], + [ "return" ], + [ "exec_non_reachable", "Arghhh" ] + ] + EOF + call_json_script "HEHVAR=dude" "VAR=ow" + assertStdoutEquals "heh dude +%VAR% ow" +} + +. ./shunit2 diff --git a/src/3P/libubox/examples/runqueue-example.c b/src/3P/libubox/examples/runqueue-example.c new file mode 100644 index 00000000..13ab864e --- /dev/null +++ b/src/3P/libubox/examples/runqueue-example.c @@ -0,0 +1,112 @@ +/* + * runqueue-example.c + * + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "uloop.h" +#include "runqueue.h" + +static struct runqueue q; + +struct sleeper { + struct runqueue_process proc; + int val; +}; + +static void q_empty(struct runqueue *q) +{ + fprintf(stderr, "All done!\n"); + uloop_end(); +} + +static void q_sleep_run(struct runqueue *q, struct runqueue_task *t) +{ + struct sleeper *s = container_of(t, struct sleeper, proc.task); + char str[32]; + pid_t pid; + + fprintf(stderr, "[%d/%d] start 'sleep %d'\n", q->running_tasks, q->max_running_tasks, s->val); + + pid = fork(); + if (pid < 0) + return; + + if (pid) { + runqueue_process_add(q, &s->proc, pid); + return; + } + + sprintf(str, "%d", s->val); + execlp("sleep", "sleep", str, NULL); + exit(1); +} + +static void q_sleep_cancel(struct runqueue *q, struct runqueue_task *t, int type) +{ + struct sleeper *s = container_of(t, struct sleeper, proc.task); + + fprintf(stderr, "[%d/%d] cancel 'sleep %d'\n", q->running_tasks, q->max_running_tasks, s->val); + runqueue_process_cancel_cb(q, t, type); +} + +static void q_sleep_complete(struct runqueue *q, struct runqueue_task *p) +{ + struct sleeper *s = container_of(p, struct sleeper, proc.task); + + fprintf(stderr, "[%d/%d] finish 'sleep %d'\n", q->running_tasks, q->max_running_tasks, s->val); + free(s); +} + +static void add_sleeper(int val) +{ + static const struct runqueue_task_type sleeper_type = { + .run = q_sleep_run, + .cancel = q_sleep_cancel, + .kill = runqueue_process_kill_cb, + }; + struct sleeper *s; + + s = calloc(1, sizeof(*s)); + s->proc.task.type = &sleeper_type; + s->proc.task.run_timeout = 500; + s->proc.task.complete = q_sleep_complete; + s->val = val; + runqueue_task_add(&q, &s->proc.task, false); +} + +int main(int argc, char **argv) +{ + uloop_init(); + + runqueue_init(&q); + q.empty_cb = q_empty; + q.max_running_tasks = 1; + + if (argc > 1) + q.max_running_tasks = atoi(argv[1]); + + add_sleeper(1); + add_sleeper(1); + add_sleeper(1); + uloop_run(); + uloop_done(); + + return 0; +} diff --git a/src/3P/libubox/examples/shunit2 b/src/3P/libubox/examples/shunit2 new file mode 100644 index 00000000..d6e75033 --- /dev/null +++ b/src/3P/libubox/examples/shunit2 @@ -0,0 +1,1067 @@ +#! /bin/sh +# $Id$ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# +# shUnit2 -- Unit testing framework for Unix shell scripts. +# http://code.google.com/p/shunit2/ +# +# Author: kate.ward@forestent.com (Kate Ward) +# +# shUnit2 is a xUnit based unit test framework for Bourne shell scripts. It is +# based on the popular JUnit unit testing framework for Java. + +# return if shunit already loaded +[ -n "${SHUNIT_VERSION:-}" ] && exit 0 +SHUNIT_VERSION='2.1.7pre' + +# return values that scripts can use +SHUNIT_TRUE=0 +SHUNIT_FALSE=1 +SHUNIT_ERROR=2 + +# logging functions +_shunit_warn() { echo "shunit2:WARN $@" >&2; } +_shunit_error() { echo "shunit2:ERROR $@" >&2; } +_shunit_fatal() { echo "shunit2:FATAL $@" >&2; exit ${SHUNIT_ERROR}; } + +# determine some reasonable command defaults +__SHUNIT_UNAME_S=`uname -s` +case "${__SHUNIT_UNAME_S}" in + BSD) __SHUNIT_EXPR_CMD='gexpr' ;; + *) __SHUNIT_EXPR_CMD='expr' ;; +esac + +# commands a user can override if needed +SHUNIT_EXPR_CMD=${SHUNIT_EXPR_CMD:-${__SHUNIT_EXPR_CMD}} + +# enable strict mode by default +SHUNIT_STRICT=${SHUNIT_STRICT:-${SHUNIT_TRUE}} + +# specific shell checks +if [ -n "${ZSH_VERSION:-}" ]; then + setopt |grep "^shwordsplit$" >/dev/null + if [ $? -ne ${SHUNIT_TRUE} ]; then + _shunit_fatal 'zsh shwordsplit option is required for proper operation' + fi + if [ -z "${SHUNIT_PARENT:-}" ]; then + _shunit_fatal "zsh does not pass \$0 through properly. please declare \ +\"SHUNIT_PARENT=\$0\" before calling shUnit2" + fi +fi + +# +# constants +# + +__SHUNIT_ASSERT_MSG_PREFIX='ASSERT:' +__SHUNIT_MODE_SOURCED='sourced' +__SHUNIT_MODE_STANDALONE='standalone' +__SHUNIT_PARENT=${SHUNIT_PARENT:-$0} + +# set the constants readonly +__shunit_constants=`set |grep '^__SHUNIT_' |cut -d= -f1` +echo "${__shunit_constants}" |grep '^Binary file' >/dev/null && \ + __shunit_constants=`set |grep -a '^__SHUNIT_' |cut -d= -f1` +for __shunit_const in ${__shunit_constants}; do + if [ -z "${ZSH_VERSION:-}" ]; then + readonly ${__shunit_const} + else + case ${ZSH_VERSION} in + [123].*) readonly ${__shunit_const} ;; + *) readonly -g ${__shunit_const} # declare readonly constants globally + esac + fi +done +unset __shunit_const __shunit_constants + +# +# internal variables +# + +# variables +__shunit_lineno='' # line number of executed test +__shunit_mode=${__SHUNIT_MODE_SOURCED} # operating mode +__shunit_reportGenerated=${SHUNIT_FALSE} # is report generated +__shunit_script='' # filename of unittest script (standalone mode) +__shunit_skip=${SHUNIT_FALSE} # is skipping enabled +__shunit_suite='' # suite of tests to execute + +# counts of tests +__shunit_testSuccess=${SHUNIT_TRUE} +__shunit_testsTotal=0 +__shunit_testsPassed=0 +__shunit_testsFailed=0 + +# counts of asserts +__shunit_assertsTotal=0 +__shunit_assertsPassed=0 +__shunit_assertsFailed=0 +__shunit_assertsSkipped=0 + +# macros +_SHUNIT_LINENO_='eval __shunit_lineno=""; if [ "${1:-}" = "--lineno" ]; then [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi' + +#----------------------------------------------------------------------------- +# private functions + +#----------------------------------------------------------------------------- +# assert functions +# + +# Assert that two values are equal to one another. +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertEquals() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertEquals() requires two or three arguments; $# given" + _shunit_error "1: ${1:+$1} 2: ${2:+$2} 3: ${3:+$3}${4:+ 4: $4}" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_expected_=$1 + shunit_actual_=$2 + + shunit_return=${SHUNIT_TRUE} + if [ "${shunit_expected_}" = "${shunit_actual_}" ]; then + _shunit_assertPass + else + failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}" + shunit_return=${SHUNIT_FALSE} + fi + + unset shunit_message_ shunit_expected_ shunit_actual_ + return ${shunit_return} +} +_ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"' + +# Assert that two values are not equal to one another. +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertNotEquals() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertNotEquals() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_expected_=$1 + shunit_actual_=$2 + + shunit_return=${SHUNIT_TRUE} + if [ "${shunit_expected_}" != "${shunit_actual_}" ]; then + _shunit_assertPass + else + failSame "${shunit_message_}" "$@" + shunit_return=${SHUNIT_FALSE} + fi + + unset shunit_message_ shunit_expected_ shunit_actual_ + return ${shunit_return} +} +_ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"' + +# Assert that a value is null (i.e. an empty string) +# +# Args: +# message: string: failure message [optional] +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertNull() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 1 -o $# -gt 2 ]; then + _shunit_error "assertNull() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + assertTrue "${shunit_message_}" "[ -z '$1' ]" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"' + +# Assert that a value is not null (i.e. a non-empty string) +# +# Args: +# message: string: failure message [optional] +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertNotNull() +{ + ${_SHUNIT_LINENO_} + if [ $# -gt 2 ]; then # allowing 0 arguments as $1 might actually be null + _shunit_error "assertNotNull() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"` + test -n "${shunit_actual_}" + assertTrue "${shunit_message_}" $? + shunit_return=$? + + unset shunit_actual_ shunit_message_ + return ${shunit_return} +} +_ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"' + +# Assert that two values are the same (i.e. equal to one another). +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertSame() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + assertEquals "${shunit_message_}" "$1" "$2" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"' + +# Assert that two values are not the same (i.e. not equal to one another). +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertNotSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertNotSame() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_:-}$1" + shift + fi + assertNotEquals "${shunit_message_}" "$1" "$2" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"' + +# Assert that a value or shell test condition is true. +# +# In shell, a value of 0 is true and a non-zero value is false. Any integer +# value passed can thereby be tested. +# +# Shell supports much more complicated tests though, and a means to support +# them was needed. As such, this function tests that conditions are true or +# false through evaluation rather than just looking for a true or false. +# +# The following test will succeed: +# assertTrue 0 +# assertTrue "[ 34 -gt 23 ]" +# The folloing test will fail with a message: +# assertTrue 123 +# assertTrue "test failed" "[ -r '/non/existant/file' ]" +# +# Args: +# message: string: failure message [optional] +# condition: string: integer value or shell conditional statement +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertTrue() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 1 -o $# -gt 2 ]; then + _shunit_error "assertTrue() takes one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_condition_=$1 + + # see if condition is an integer, i.e. a return value + shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` + shunit_return=${SHUNIT_TRUE} + if [ -z "${shunit_condition_}" ]; then + # null condition + shunit_return=${SHUNIT_FALSE} + elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] + then + # possible return value. treating 0 as true, and non-zero as false. + [ ${shunit_condition_} -ne 0 ] && shunit_return=${SHUNIT_FALSE} + else + # (hopefully) a condition + ( eval ${shunit_condition_} ) >/dev/null 2>&1 + [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE} + fi + + # record the test + if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then + _shunit_assertPass + else + _shunit_assertFail "${shunit_message_}" + fi + + unset shunit_message_ shunit_condition_ shunit_match_ + return ${shunit_return} +} +_ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"' + +# Assert that a value or shell test condition is false. +# +# In shell, a value of 0 is true and a non-zero value is false. Any integer +# value passed can thereby be tested. +# +# Shell supports much more complicated tests though, and a means to support +# them was needed. As such, this function tests that conditions are true or +# false through evaluation rather than just looking for a true or false. +# +# The following test will succeed: +# assertFalse 1 +# assertFalse "[ 'apples' = 'oranges' ]" +# The folloing test will fail with a message: +# assertFalse 0 +# assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]" +# +# Args: +# message: string: failure message [optional] +# condition: string: integer value or shell conditional statement +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertFalse() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 1 -o $# -gt 2 ]; then + _shunit_error "assertFalse() quires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_condition_=$1 + + # see if condition is an integer, i.e. a return value + shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` + shunit_return=${SHUNIT_TRUE} + if [ -z "${shunit_condition_}" ]; then + # null condition + shunit_return=${SHUNIT_FALSE} + elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] + then + # possible return value. treating 0 as true, and non-zero as false. + [ ${shunit_condition_} -eq 0 ] && shunit_return=${SHUNIT_FALSE} + else + # (hopefully) a condition + ( eval ${shunit_condition_} ) >/dev/null 2>&1 + [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE} + fi + + # record the test + if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then + _shunit_assertPass + else + _shunit_assertFail "${shunit_message_}" + fi + + unset shunit_message_ shunit_condition_ shunit_match_ + return ${shunit_return} +} +_ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"' + +#----------------------------------------------------------------------------- +# failure functions +# + +# Records a test failure. +# +# Args: +# message: string: failure message [optional] +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +fail() +{ + ${_SHUNIT_LINENO_} + if [ $# -gt 1 ]; then + _shunit_error "fail() requires zero or one arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 1 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + + _shunit_assertFail "${shunit_message_}" + + unset shunit_message_ + return ${SHUNIT_FALSE} +} +_FAIL_='eval fail --lineno "${LINENO:-}"' + +# Records a test failure, stating two values were not equal. +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +failNotEquals() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "failNotEquals() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_expected_=$1 + shunit_actual_=$2 + + _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>" + + unset shunit_message_ shunit_expected_ shunit_actual_ + return ${SHUNIT_FALSE} +} +_FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"' + +# Records a test failure, stating two values should have been the same. +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +failSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "failSame() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + + _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same" + + unset shunit_message_ + return ${SHUNIT_FALSE} +} +_FAIL_SAME_='eval failSame --lineno "${LINENO:-}"' + +# Records a test failure, stating two values were not equal. +# +# This is functionally equivalent to calling failNotEquals(). +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +failNotSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "failNotEquals() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + failNotEquals "${shunit_message_}" "$1" "$2" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"' + +#----------------------------------------------------------------------------- +# skipping functions +# + +# Force remaining assert and fail functions to be "skipped". +# +# This function forces the remaining assert and fail functions to be "skipped", +# i.e. they will have no effect. Each function skipped will be recorded so that +# the total of asserts and fails will not be altered. +# +# Args: +# None +startSkipping() +{ + __shunit_skip=${SHUNIT_TRUE} +} + +# Resume the normal recording behavior of assert and fail calls. +# +# Args: +# None +endSkipping() +{ + __shunit_skip=${SHUNIT_FALSE} +} + +# Returns the state of assert and fail call skipping. +# +# Args: +# None +# Returns: +# boolean: (TRUE/FALSE constant) +isSkipping() +{ + return ${__shunit_skip} +} + +#----------------------------------------------------------------------------- +# suite functions +# + +# Stub. This function should contains all unit test calls to be made. +# +# DEPRECATED (as of 2.1.0) +# +# This function can be optionally overridden by the user in their test suite. +# +# If this function exists, it will be called when shunit2 is sourced. If it +# does not exist, shunit2 will search the parent script for all functions +# beginning with the word 'test', and they will be added dynamically to the +# test suite. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#suite() { :; } # DO NOT UNCOMMENT THIS FUNCTION + +# Adds a function name to the list of tests schedule for execution. +# +# This function should only be called from within the suite() function. +# +# Args: +# function: string: name of a function to add to current unit test suite +suite_addTest() +{ + shunit_func_=${1:-} + + __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}" + __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1` + + unset shunit_func_ +} + +# Stub. This function will be called once before any tests are run. +# +# Common one-time environment preparation tasks shared by all tests can be +# defined here. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#oneTimeSetUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION + +# Stub. This function will be called once after all tests are finished. +# +# Common one-time environment cleanup tasks shared by all tests can be defined +# here. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#oneTimeTearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION + +# Stub. This function will be called before each test is run. +# +# Common environment preparation tasks shared by all tests can be defined here. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#setUp() { :; } + +# Note: see _shunit_mktempFunc() for actual implementation +# Stub. This function will be called after each test is run. +# +# Common environment cleanup tasks shared by all tests can be defined here. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#tearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION + +#------------------------------------------------------------------------------ +# internal shUnit2 functions +# + +# Create a temporary directory to store various run-time files in. +# +# This function is a cross-platform temporary directory creation tool. Not all +# OSes have the mktemp function, so one is included here. +# +# Args: +# None +# Outputs: +# string: the temporary directory that was created +_shunit_mktempDir() +{ + # try the standard mktemp function + ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return + + # the standard mktemp didn't work. doing our own. + if [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then + _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 "${_shunit_file_}" +#! /bin/sh +exit ${SHUNIT_TRUE} +EOF + chmod +x "${_shunit_file_}" + done + + unset _shunit_file_ +} + +# Final cleanup function to leave things as we found them. +# +# Besides removing the temporary directory, this function is in charge of the +# final exit code of the unit test. The exit code is based on how the script +# was ended (e.g. normal exit, or via Ctrl-C). +# +# Args: +# name: string: name of the trap called (specified when trap defined) +_shunit_cleanup() +{ + _shunit_name_=$1 + + case ${_shunit_name_} in + EXIT) _shunit_signal_=0 ;; + INT) _shunit_signal_=2 ;; + TERM) _shunit_signal_=15 ;; + *) + _shunit_warn "unrecognized trap value (${_shunit_name_})" + _shunit_signal_=0 + ;; + esac + + # do our work + rm -fr "${__shunit_tmpDir}" + + # exit for all non-EXIT signals + if [ ${_shunit_name_} != 'EXIT' ]; then + _shunit_warn "trapped and now handling the (${_shunit_name_}) signal" + # disable EXIT trap + trap 0 + # add 128 to signal and exit + exit `expr ${_shunit_signal_} + 128` + elif [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ] ; then + _shunit_assertFail 'Unknown failure encountered running a test' + _shunit_generateReport + exit ${SHUNIT_ERROR} + fi + + unset _shunit_name_ _shunit_signal_ +} + +# The actual running of the tests happens here. +# +# Args: +# None +_shunit_execSuite() +{ + for _shunit_test_ in ${__shunit_suite}; do + __shunit_testSuccess=${SHUNIT_TRUE} + + # disable skipping + endSkipping + + # execute the per-test setup function + setUp + + # execute the test + echo "${_shunit_test_}" + eval ${_shunit_test_} + + # execute the per-test tear-down function + tearDown + + # update stats + if [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then + __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1` + else + __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1` + fi + done + + unset _shunit_test_ +} + +# Generates the user friendly report with appropriate OK/FAILED message. +# +# Args: +# None +# Output: +# string: the report of successful and failed tests, as well as totals. +_shunit_generateReport() +{ + _shunit_ok_=${SHUNIT_TRUE} + + # if no exit code was provided one, determine an appropriate one + [ ${__shunit_testsFailed} -gt 0 \ + -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \ + && _shunit_ok_=${SHUNIT_FALSE} + + echo + if [ ${__shunit_testsTotal} -eq 1 ]; then + echo "Ran ${__shunit_testsTotal} test." + else + echo "Ran ${__shunit_testsTotal} tests." + fi + + _shunit_failures_='' + _shunit_skipped_='' + [ ${__shunit_assertsFailed} -gt 0 ] \ + && _shunit_failures_="failures=${__shunit_assertsFailed}" + [ ${__shunit_assertsSkipped} -gt 0 ] \ + && _shunit_skipped_="skipped=${__shunit_assertsSkipped}" + + if [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then + _shunit_msg_='OK' + [ -n "${_shunit_skipped_}" ] \ + && _shunit_msg_="${_shunit_msg_} (${_shunit_skipped_})" + else + _shunit_msg_="FAILED (${_shunit_failures_}" + [ -n "${_shunit_skipped_}" ] \ + && _shunit_msg_="${_shunit_msg_},${_shunit_skipped_}" + _shunit_msg_="${_shunit_msg_})" + fi + + echo + echo ${_shunit_msg_} + __shunit_reportGenerated=${SHUNIT_TRUE} + + unset _shunit_failures_ _shunit_msg_ _shunit_ok_ _shunit_skipped_ +} + +# Test for whether a function should be skipped. +# +# Args: +# None +# Returns: +# boolean: whether the test should be skipped (TRUE/FALSE constant) +_shunit_shouldSkip() +{ + [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE} + _shunit_assertSkip +} + +# Records a successful test. +# +# Args: +# None +_shunit_assertPass() +{ + __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1` + __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` +} + +# Records a test failure. +# +# Args: +# message: string: failure message to provide user +_shunit_assertFail() +{ + _shunit_msg_=$1 + + __shunit_testSuccess=${SHUNIT_FALSE} + __shunit_assertsFailed=`expr ${__shunit_assertsFailed} + 1` + __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` + echo "${__SHUNIT_ASSERT_MSG_PREFIX}${_shunit_msg_}" + + unset _shunit_msg_ +} + +# Records a skipped test. +# +# Args: +# None +_shunit_assertSkip() +{ + __shunit_assertsSkipped=`expr ${__shunit_assertsSkipped} + 1` + __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` +} + +# Prepare a script filename for sourcing. +# +# Args: +# script: string: path to a script to source +# Returns: +# string: filename prefixed with ./ (if necessary) +_shunit_prepForSourcing() +{ + _shunit_script_=$1 + case "${_shunit_script_}" in + /*|./*) echo "${_shunit_script_}" ;; + *) echo "./${_shunit_script_}" ;; + esac + unset _shunit_script_ +} + +# Escape a character in a string. +# +# Args: +# c: string: unescaped character +# s: string: to escape character in +# Returns: +# string: with escaped character(s) +_shunit_escapeCharInStr() +{ + [ -n "$2" ] || return # no point in doing work on an empty string + + # Note: using shorter variable names to prevent conflicts with + # _shunit_escapeCharactersInString(). + _shunit_c_=$1 + _shunit_s_=$2 + + + # escape the character + echo ''${_shunit_s_}'' |sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g' + + unset _shunit_c_ _shunit_s_ +} + +# Escape a character in a string. +# +# Args: +# str: string: to escape characters in +# Returns: +# string: with escaped character(s) +_shunit_escapeCharactersInString() +{ + [ -n "$1" ] || return # no point in doing work on an empty string + + _shunit_str_=$1 + + # Note: using longer variable names to prevent conflicts with + # _shunit_escapeCharInStr(). + for _shunit_char_ in '"' '$' "'" '`'; do + _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"` + done + + echo "${_shunit_str_}" + unset _shunit_char_ _shunit_str_ +} + +# Extract list of functions to run tests against. +# +# Args: +# script: string: name of script to extract functions from +# Returns: +# string: of function names +_shunit_extractTestFunctions() +{ + _shunit_script_=$1 + + # extract the lines with test function names, strip of anything besides the + # function name, and output everything on a single line. + _shunit_regex_='^[ ]*(function )*test[A-Za-z0-9_]* *\(\)' + egrep "${_shunit_regex_}" "${_shunit_script_}" \ + |sed 's/^[^A-Za-z0-9_]*//;s/^function //;s/\([A-Za-z0-9_]*\).*/\1/g' \ + |xargs + + unset _shunit_regex_ _shunit_script_ +} + +#------------------------------------------------------------------------------ +# main +# + +# determine the operating mode +if [ $# -eq 0 ]; then + __shunit_script=${__SHUNIT_PARENT} + __shunit_mode=${__SHUNIT_MODE_SOURCED} +else + __shunit_script=$1 + [ -r "${__shunit_script}" ] || \ + _shunit_fatal "unable to read from ${__shunit_script}" + __shunit_mode=${__SHUNIT_MODE_STANDALONE} +fi + +# create a temporary storage location +__shunit_tmpDir=`_shunit_mktempDir` + +# provide a public temporary directory for unit test scripts +# TODO(kward): document this +SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp" +mkdir "${SHUNIT_TMPDIR}" + +# setup traps to clean up after ourselves +trap '_shunit_cleanup EXIT' 0 +trap '_shunit_cleanup INT' 2 +trap '_shunit_cleanup TERM' 15 + +# create phantom functions to work around issues with Cygwin +_shunit_mktempFunc +PATH="${__shunit_tmpDir}:${PATH}" + +# make sure phantom functions are executable. this will bite if /tmp (or the +# current $TMPDIR) points to a path on a partition that was mounted with the +# 'noexec' option. the noexec command was created with _shunit_mktempFunc(). +noexec 2>/dev/null || _shunit_fatal \ + 'please declare TMPDIR with path on partition with exec permission' + +# we must manually source the tests in standalone mode +if [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then + . "`_shunit_prepForSourcing \"${__shunit_script}\"`" +fi + +# execute the oneTimeSetUp function (if it exists) +oneTimeSetUp + +# execute the suite function defined in the parent test script +# deprecated as of 2.1.0 +suite + +# if no suite function was defined, dynamically build a list of functions +if [ -z "${__shunit_suite}" ]; then + shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"` + for shunit_func_ in ${shunit_funcs_}; do + suite_addTest ${shunit_func_} + done +fi +unset shunit_func_ shunit_funcs_ + +# execute the tests +_shunit_execSuite + +# execute the oneTimeTearDown function (if it exists) +oneTimeTearDown + +# generate the report +_shunit_generateReport + +# that's it folks +[ ${__shunit_testsFailed} -eq 0 ] +exit $? diff --git a/src/3P/libubox/examples/uloop-example.lua b/src/3P/libubox/examples/uloop-example.lua new file mode 100755 index 00000000..9b0684e6 --- /dev/null +++ b/src/3P/libubox/examples/uloop-example.lua @@ -0,0 +1,78 @@ +#!/usr/bin/env lua + +local socket = require "socket" + +local uloop = require("uloop") +uloop.init() + +local udp = socket.udp() +udp:settimeout(0) +udp:setsockname('*', 8080) + +-- timer example 1 +local timer +function t() + print("1000 ms timer run"); + timer:set(1000) +end +timer = uloop.timer(t) +timer:set(1000) + +-- timer example 2 +uloop.timer(function() print("2000 ms timer run"); end, 2000) + +-- timer example 3 +uloop.timer(function() print("3000 ms timer run"); end, 3000):cancel() + +-- process +function p1(r) + print("Process 1 completed") + print(r) +end + +function p2(r) + print("Process 2 completed") + print(r) +end + +uloop.timer( + function() + uloop.process("uloop_pid_test.sh", {"foo", "bar"}, {"PROCESS=1"}, p1) + end, 1000 +) +uloop.timer( + function() + uloop.process("uloop_pid_test.sh", {"foo", "bar"}, {"PROCESS=2"}, p2) + end, 2000 +) + +udp_ev = uloop.fd_add(udp, function(ufd, events) + local words, msg_or_ip, port_or_nil = ufd:receivefrom() + print('Recv UDP packet from '..msg_or_ip..':'..port_or_nil..' : '..words) + if words == "Stop!" then + udp_ev:delete() + end +end, uloop.ULOOP_READ) + +udp_count = 0 +udp_send_timer = uloop.timer( + function() + local s = socket.udp() + local words + if udp_count > 3 then + words = "Stop!" + udp_send_timer:cancel() + else + words = 'Hello!' + udp_send_timer:set(1000) + end + print('Send UDP packet to 127.0.0.1:8080 :'..words) + s:sendto(words, '127.0.0.1', 8080) + s:close() + + udp_count = udp_count + 1 + end, 3000 +) + +uloop.run() + diff --git a/src/3P/libubox/examples/uloop_pid_test.sh b/src/3P/libubox/examples/uloop_pid_test.sh new file mode 100755 index 00000000..c622064f --- /dev/null +++ b/src/3P/libubox/examples/uloop_pid_test.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +echo $0 $* +echo Environment: +env + +sleep 2 + +echo "stopping child" + +exit 5 diff --git a/src/3P/libubox/examples/ustream-example.c b/src/3P/libubox/examples/ustream-example.c new file mode 100644 index 00000000..3db56c44 --- /dev/null +++ b/src/3P/libubox/examples/ustream-example.c @@ -0,0 +1,148 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "ustream.h" +#include "uloop.h" +#include "usock.h" + +static struct uloop_fd server; +static const char *port = "10000"; +struct client *next_client = NULL; + +struct client { + struct sockaddr_in sin; + + struct ustream_fd s; + int ctr; +}; + +static void client_read_cb(struct ustream *s, int bytes) +{ + struct client *cl = container_of(s, struct client, s.stream); + struct ustream_buf *buf = s->r.head; + char *newline, *str; + + do { + str = ustream_get_read_buf(s, NULL); + if (!str) + break; + + newline = strchr(buf->data, '\n'); + if (!newline) + break; + + *newline = 0; + ustream_printf(s, "%s\n", str); + ustream_consume(s, newline + 1 - str); + cl->ctr += newline + 1 - str; + } while(1); + + if (s->w.data_bytes > 256 && !ustream_read_blocked(s)) { + fprintf(stderr, "Block read, bytes: %d\n", s->w.data_bytes); + ustream_set_read_blocked(s, true); + } +} + +static void client_close(struct ustream *s) +{ + struct client *cl = container_of(s, struct client, s.stream); + + fprintf(stderr, "Connection closed\n"); + ustream_free(s); + close(cl->s.fd.fd); + free(cl); +} + +static void client_notify_write(struct ustream *s, int bytes) +{ + fprintf(stderr, "Wrote %d bytes, pending: %d\n", bytes, s->w.data_bytes); + + if (s->w.data_bytes < 128 && ustream_read_blocked(s)) { + fprintf(stderr, "Unblock read\n"); + ustream_set_read_blocked(s, false); + } +} + +static void client_notify_state(struct ustream *s) +{ + struct client *cl = container_of(s, struct client, s.stream); + + if (!s->eof) + return; + + fprintf(stderr, "eof!, pending: %d, total: %d\n", s->w.data_bytes, cl->ctr); + if (!s->w.data_bytes) + return client_close(s); + +} + +static void server_cb(struct uloop_fd *fd, unsigned int events) +{ + struct client *cl; + unsigned int sl = sizeof(struct sockaddr_in); + int sfd; + + if (!next_client) + next_client = calloc(1, sizeof(*next_client)); + + cl = next_client; + sfd = accept(server.fd, (struct sockaddr *) &cl->sin, &sl); + if (sfd < 0) { + fprintf(stderr, "Accept failed\n"); + return; + } + + cl->s.stream.string_data = true; + cl->s.stream.notify_read = client_read_cb; + cl->s.stream.notify_state = client_notify_state; + cl->s.stream.notify_write = client_notify_write; + ustream_fd_init(&cl->s, sfd); + next_client = NULL; + fprintf(stderr, "New connection\n"); +} + +static int run_server(void) +{ + + server.cb = server_cb; + server.fd = usock(USOCK_TCP | USOCK_SERVER | USOCK_IPV4ONLY | USOCK_NUMERIC, "127.0.0.1", port); + if (server.fd < 0) { + perror("usock"); + return 1; + } + + uloop_init(); + uloop_fd_add(&server, ULOOP_READ); + uloop_run(); + + return 0; +} + +static int usage(const char *name) +{ + fprintf(stderr, "Usage: %s -p \n", name); + return 1; +} + +int main(int argc, char **argv) +{ + int ch; + + while ((ch = getopt(argc, argv, "p:")) != -1) { + switch(ch) { + case 'p': + port = optarg; + break; + default: + return usage(argv[0]); + } + } + + return run_server(); +} diff --git a/src/3P/libubox/jshn.c b/src/3P/libubox/jshn.c new file mode 100644 index 00000000..d396d8f6 --- /dev/null +++ b/src/3P/libubox/jshn.c @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2011-2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifdef JSONC + #include +#else + #include +#endif + +#include +#include +#include +#include +#include +#include +#include "list.h" + +#include "avl.h" +#include "blob.h" +#include "blobmsg_json.h" + +#define MAX_VARLEN 256 + +static struct avl_tree env_vars; +static struct blob_buf b = { 0 }; + +static const char *var_prefix = ""; +static int var_prefix_len = 0; + +static int add_json_element(const char *key, json_object *obj); + +struct env_var { + struct avl_node avl; + char *val; +}; + +static int add_json_object(json_object *obj) +{ + int ret = 0; + + json_object_object_foreach(obj, key, val) { + ret = add_json_element(key, val); + if (ret) + break; + } + return ret; +} + +static int add_json_array(struct array_list *a) +{ + char seq[12]; + int i, len; + int ret; + + for (i = 0, len = array_list_length(a); i < len; i++) { + sprintf(seq, "%d", i); + ret = add_json_element(seq, array_list_get_idx(a, i)); + if (ret) + return ret; + } + + return 0; +} + +static void add_json_string(const char *str) +{ + char *ptr = (char *) str; + int len; + char *c; + + while ((c = strchr(ptr, '\'')) != NULL) { + len = c - ptr; + if (len > 0) + fwrite(ptr, len, 1, stdout); + ptr = c + 1; + c = "'\\''"; + fwrite(c, strlen(c), 1, stdout); + } + len = strlen(ptr); + if (len > 0) + fwrite(ptr, len, 1, stdout); +} + +static void write_key_string(const char *key) +{ + while (*key) { + putc(isalnum(*key) ? *key : '_', stdout); + key++; + } +} + +static int add_json_element(const char *key, json_object *obj) +{ + char *type; + + if (!obj) + return -1; + + switch (json_object_get_type(obj)) { + case json_type_object: + type = "object"; + break; + case json_type_array: + type = "array"; + break; + case json_type_string: + type = "string"; + break; + case json_type_boolean: + type = "boolean"; + break; + case json_type_int: + type = "int"; + break; + case json_type_double: + type = "double"; + break; + default: + return -1; + } + + fprintf(stdout, "json_add_%s '", type); + write_key_string(key); + + switch (json_object_get_type(obj)) { + case json_type_object: + fprintf(stdout, "';\n"); + add_json_object(obj); + fprintf(stdout, "json_close_object;\n"); + break; + case json_type_array: + fprintf(stdout, "';\n"); + add_json_array(json_object_get_array(obj)); + fprintf(stdout, "json_close_array;\n"); + break; + case json_type_string: + fprintf(stdout, "' '"); + add_json_string(json_object_get_string(obj)); + fprintf(stdout, "';\n"); + break; + case json_type_boolean: + fprintf(stdout, "' %d;\n", json_object_get_boolean(obj)); + break; + case json_type_int: + fprintf(stdout, "' %d;\n", json_object_get_int(obj)); + break; + case json_type_double: + fprintf(stdout, "' %lf;\n", json_object_get_double(obj)); + break; + default: + return -1; + } + + return 0; +} + +static int jshn_parse(const char *str) +{ + json_object *obj; + + obj = json_tokener_parse(str); + if (!obj || json_object_get_type(obj) != json_type_object) { + fprintf(stderr, "Failed to parse message data\n"); + return 1; + } + fprintf(stdout, "json_init;\n"); + add_json_object(obj); + fflush(stdout); + + return 0; +} + +static char *getenv_avl(const char *key) +{ + struct env_var *var = avl_find_element(&env_vars, key, var, avl); + return var ? var->val : NULL; +} + +static char *get_keys(const char *prefix) +{ + char *keys; + + keys = alloca(var_prefix_len + strlen(prefix) + sizeof("K_") + 1); + sprintf(keys, "%sK_%s", var_prefix, prefix); + return getenv_avl(keys); +} + +static void get_var(const char *prefix, const char **name, char **var, char **type) +{ + char *tmpname, *varname; + + tmpname = alloca(var_prefix_len + strlen(prefix) + 1 + strlen(*name) + 1 + sizeof("T_")); + + sprintf(tmpname, "%s%s_%s", var_prefix, prefix, *name); + *var = getenv_avl(tmpname); + + sprintf(tmpname, "%sT_%s_%s", var_prefix, prefix, *name); + *type = getenv_avl(tmpname); + + sprintf(tmpname, "%sN_%s_%s", var_prefix, prefix, *name); + varname = getenv_avl(tmpname); + if (varname) + *name = varname; +} + +static json_object *jshn_add_objects(json_object *obj, const char *prefix, bool array); + +static void jshn_add_object_var(json_object *obj, bool array, const char *prefix, const char *name) +{ + json_object *new; + char *var, *type; + + get_var(prefix, &name, &var, &type); + if (!var || !type) + return; + + if (!strcmp(type, "array")) { + new = json_object_new_array(); + jshn_add_objects(new, var, true); + } else if (!strcmp(type, "object")) { + new = json_object_new_object(); + jshn_add_objects(new, var, false); + } else if (!strcmp(type, "string")) { + new = json_object_new_string(var); + } else if (!strcmp(type, "int")) { + new = json_object_new_int(atoi(var)); + } else if (!strcmp(type, "double")) { + new = json_object_new_double(strtod(var, NULL)); + } else if (!strcmp(type, "boolean")) { + new = json_object_new_boolean(!!atoi(var)); + } else { + return; + } + + if (array) + json_object_array_add(obj, new); + else + json_object_object_add(obj, name, new); +} + +static json_object *jshn_add_objects(json_object *obj, const char *prefix, bool array) +{ + char *keys, *key, *brk; + + keys = get_keys(prefix); + if (!keys || !obj) + goto out; + + for (key = strtok_r(keys, " ", &brk); key; + key = strtok_r(NULL, " ", &brk)) { + jshn_add_object_var(obj, array, prefix, key); + } + +out: + return obj; +} + +static int jshn_format(bool no_newline, bool indent) +{ + json_object *obj; + const char *output; + char *blobmsg_output = NULL; + int ret = -1; + + if (!(obj = json_object_new_object())) + return -1; + + jshn_add_objects(obj, "J_V", false); + if (!(output = json_object_to_json_string(obj))) + goto out; + + if (indent) { + blob_buf_init(&b, 0); + if (!blobmsg_add_json_from_string(&b, output)) + goto out; + if (!(blobmsg_output = blobmsg_format_json_indent(b.head, 1, 0))) + goto out; + output = blobmsg_output; + } + fprintf(stdout, "%s%s", output, no_newline ? "" : "\n"); + free(blobmsg_output); + ret = 0; + +out: + json_object_put(obj); + return ret; +} + +static int usage(const char *progname) +{ + fprintf(stderr, "Usage: %s [-n] [-i] -r |-w\n", progname); + return 2; +} + +static int avl_strcmp_var(const void *k1, const void *k2, void *ptr) +{ + const char *s1 = k1; + const char *s2 = k2; + char c1, c2; + + while (*s1 && *s1 == *s2) { + s1++; + s2++; + } + + c1 = *s1; + c2 = *s2; + if (c1 == '=') + c1 = 0; + if (c2 == '=') + c2 = 0; + + return c1 - c2; +} + +int main(int argc, char **argv) +{ + extern char **environ; + bool no_newline = false; + bool indent = false; + struct env_var *vars; + int i; + int ch; + + avl_init(&env_vars, avl_strcmp_var, false, NULL); + for (i = 0; environ[i]; i++); + + vars = calloc(i, sizeof(*vars)); + if (!vars) { + fprintf(stderr, "%m\n"); + return -1; + } + for (i = 0; environ[i]; i++) { + char *c; + + vars[i].avl.key = environ[i]; + c = strchr(environ[i], '='); + if (!c) + continue; + + vars[i].val = c + 1; + avl_insert(&env_vars, &vars[i].avl); + } + + while ((ch = getopt(argc, argv, "p:nir:w")) != -1) { + switch(ch) { + case 'p': + var_prefix = optarg; + var_prefix_len = strlen(var_prefix); + break; + case 'r': + return jshn_parse(optarg); + case 'w': + return jshn_format(no_newline, indent); + case 'n': + no_newline = true; + break; + case 'i': + indent = true; + break; + default: + return usage(argv[0]); + } + } + return usage(argv[0]); +} diff --git a/src/3P/libubox/json_script.c b/src/3P/libubox/json_script.c new file mode 100644 index 00000000..463aac88 --- /dev/null +++ b/src/3P/libubox/json_script.c @@ -0,0 +1,698 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include + +#include "avl-cmp.h" +#include "json_script.h" + +struct json_call { + struct json_script_ctx *ctx; + struct blob_attr *vars; + unsigned int seq; +}; + +struct json_handler { + const char *name; + int (*cb)(struct json_call *call, struct blob_attr *cur); +}; + +static int json_process_expr(struct json_call *call, struct blob_attr *cur); +static int json_process_cmd(struct json_call *call, struct blob_attr *cur); +static int eval_string(struct json_call *call, struct blob_buf *buf, const char *name, const char *pattern); + +struct json_script_file * +json_script_file_from_blobmsg(const char *name, void *data, int len) +{ + struct json_script_file *f; + char *new_name; + int name_len = 0; + + if (name) + name_len = strlen(name) + 1; + + f = calloc_a(sizeof(*f) + len, &new_name, name_len); + if (!f) + return NULL; + + memcpy(f->data, data, len); + if (name) + f->avl.key = strcpy(new_name, name); + + return f; +} + +static struct json_script_file * +json_script_get_file(struct json_script_ctx *ctx, const char *filename) +{ + struct json_script_file *f; + + f = avl_find_element(&ctx->files, filename, f, avl); + if (f) + return f; + + f = ctx->handle_file(ctx, filename); + if (!f) + return NULL; + + avl_insert(&ctx->files, &f->avl); + return f; +} + +static void __json_script_run(struct json_call *call, struct json_script_file *file, + struct blob_attr *context) +{ + struct json_script_ctx *ctx = call->ctx; + + if (file->seq == call->seq) { + if (context) + ctx->handle_error(ctx, "Recursive include", context); + + return; + } + + file->seq = call->seq; + while (file) { + json_process_cmd(call, file->data); + file = file->next; + } +} + +const char *json_script_find_var(struct json_script_ctx *ctx, struct blob_attr *vars, + const char *name) +{ + struct blob_attr *cur; + int rem; + + blobmsg_for_each_attr(cur, vars, rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) + continue; + + if (strcmp(blobmsg_name(cur), name) != 0) + continue; + + return blobmsg_data(cur); + } + + return ctx->handle_var(ctx, name, vars); +} + +static const char * +msg_find_var(struct json_call *call, const char *name) +{ + return json_script_find_var(call->ctx, call->vars, name); +} + +static void +json_get_tuple(struct blob_attr *cur, struct blob_attr **tb, int t1, int t2) +{ + static struct blobmsg_policy expr_tuple[3] = { + { .type = BLOBMSG_TYPE_STRING }, + {}, + {}, + }; + + expr_tuple[1].type = t1; + expr_tuple[2].type = t2; + blobmsg_parse_array(expr_tuple, 3, tb, blobmsg_data(cur), blobmsg_data_len(cur)); +} + +static int handle_if(struct json_call *call, struct blob_attr *expr) +{ + struct blob_attr *tb[4]; + int ret; + + static const struct blobmsg_policy if_tuple[4] = { + { .type = BLOBMSG_TYPE_STRING }, + { .type = BLOBMSG_TYPE_ARRAY }, + { .type = BLOBMSG_TYPE_ARRAY }, + { .type = BLOBMSG_TYPE_ARRAY }, + }; + + blobmsg_parse_array(if_tuple, 4, tb, blobmsg_data(expr), blobmsg_data_len(expr)); + + if (!tb[1] || !tb[2]) + return 0; + + ret = json_process_expr(call, tb[1]); + if (ret < 0) + return 0; + + if (ret) + return json_process_cmd(call, tb[2]); + + if (!tb[3]) + return 0; + + return json_process_cmd(call, tb[3]); +} + +static int handle_case(struct json_call *call, struct blob_attr *expr) +{ + struct blob_attr *tb[3], *cur; + const char *var; + int rem; + + json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, BLOBMSG_TYPE_TABLE); + if (!tb[1] || !tb[2]) + return 0; + + var = msg_find_var(call, blobmsg_data(tb[1])); + if (!var) + return 0; + + blobmsg_for_each_attr(cur, tb[2], rem) { + if (!strcmp(var, blobmsg_name(cur))) + return json_process_cmd(call, cur); + } + + return 0; +} + +static int handle_return(struct json_call *call, struct blob_attr *expr) +{ + return -2; +} + +static int handle_include(struct json_call *call, struct blob_attr *expr) +{ + struct blob_attr *tb[3]; + struct json_script_file *f; + + json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0); + if (!tb[1]) + return 0; + + f = json_script_get_file(call->ctx, blobmsg_data(tb[1])); + if (!f) + return 0; + + __json_script_run(call, f, expr); + return 0; +} + +static const struct json_handler cmd[] = { + { "if", handle_if }, + { "case", handle_case }, + { "return", handle_return }, + { "include", handle_include }, +}; + +static int eq_regex_cmp(const char *str, const char *pattern, bool regex) +{ + regex_t reg; + int ret; + + if (!regex) + return !strcmp(str, pattern); + + if (regcomp(®, pattern, REG_EXTENDED | REG_NOSUB)) + return 0; + + ret = !regexec(®, str, 0, NULL, 0); + regfree(®); + + return ret; +} + +static int expr_eq_regex(struct json_call *call, struct blob_attr *expr, bool regex) +{ + struct json_script_ctx *ctx = call->ctx; + struct blob_attr *tb[3], *cur; + const char *var; + int rem; + + json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0); + if (!tb[1] || !tb[2]) + return -1; + + var = msg_find_var(call, blobmsg_data(tb[1])); + if (!var) + return 0; + + switch(blobmsg_type(tb[2])) { + case BLOBMSG_TYPE_STRING: + return eq_regex_cmp(var, blobmsg_data(tb[2]), regex); + case BLOBMSG_TYPE_ARRAY: + blobmsg_for_each_attr(cur, tb[2], rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) { + ctx->handle_error(ctx, "Unexpected element type", cur); + return -1; + } + + if (eq_regex_cmp(var, blobmsg_data(cur), regex)) + return 1; + } + return 0; + default: + ctx->handle_error(ctx, "Unexpected element type", tb[2]); + return -1; + } +} + +static int handle_expr_eq(struct json_call *call, struct blob_attr *expr) +{ + return expr_eq_regex(call, expr, false); +} + +static int handle_expr_regex(struct json_call *call, struct blob_attr *expr) +{ + return expr_eq_regex(call, expr, true); +} + +static int handle_expr_has(struct json_call *call, struct blob_attr *expr) +{ + struct json_script_ctx *ctx = call->ctx; + struct blob_attr *tb[3], *cur; + int rem; + + json_get_tuple(expr, tb, 0, 0); + if (!tb[1]) + return -1; + + switch(blobmsg_type(tb[1])) { + case BLOBMSG_TYPE_STRING: + return !!msg_find_var(call, blobmsg_data(tb[1])); + case BLOBMSG_TYPE_ARRAY: + blobmsg_for_each_attr(cur, tb[1], rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) { + ctx->handle_error(ctx, "Unexpected element type", cur); + return -1; + } + + if (msg_find_var(call, blobmsg_data(cur))) + return 1; + } + return 0; + default: + ctx->handle_error(ctx, "Unexpected element type", tb[1]); + return -1; + } +} + +static int expr_and_or(struct json_call *call, struct blob_attr *expr, bool and) +{ + struct blob_attr *cur; + int ret, rem; + int i = 0; + + blobmsg_for_each_attr(cur, expr, rem) { + if (i++ < 1) + continue; + + ret = json_process_expr(call, cur); + if (ret < 0) + return ret; + + if (ret != and) + return ret; + } + + return and; +} + +static int handle_expr_and(struct json_call *call, struct blob_attr *expr) +{ + return expr_and_or(call, expr, 1); +} + +static int handle_expr_or(struct json_call *call, struct blob_attr *expr) +{ + return expr_and_or(call, expr, 0); +} + +static int handle_expr_not(struct json_call *call, struct blob_attr *expr) +{ + struct blob_attr *tb[3]; + int ret; + + json_get_tuple(expr, tb, BLOBMSG_TYPE_ARRAY, 0); + if (!tb[1]) + return -1; + + ret = json_process_expr(call, tb[1]); + if (ret < 0) + return ret; + return !ret; +} + +static int handle_expr_isdir(struct json_call *call, struct blob_attr *expr) +{ + static struct blob_buf b; + struct blob_attr *tb[3]; + const char *pattern, *path; + struct stat s; + int ret; + + json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0); + if (!tb[1] || blobmsg_type(tb[1]) != BLOBMSG_TYPE_STRING) + return -1; + pattern = blobmsg_data(tb[1]); + + blob_buf_init(&b, 0); + ret = eval_string(call, &b, NULL, pattern); + if (ret < 0) + return ret; + path = blobmsg_data(blob_data(b.head)); + ret = stat(path, &s); + if (ret < 0) + return 0; + return S_ISDIR(s.st_mode); +} + +static const struct json_handler expr[] = { + { "eq", handle_expr_eq }, + { "regex", handle_expr_regex }, + { "has", handle_expr_has }, + { "and", handle_expr_and }, + { "or", handle_expr_or }, + { "not", handle_expr_not }, + { "isdir", handle_expr_isdir }, +}; + +static int +__json_process_type(struct json_call *call, struct blob_attr *cur, + const struct json_handler *h, int n, bool *found) +{ + const char *name = blobmsg_data(blobmsg_data(cur)); + int i; + + for (i = 0; i < n; i++) { + if (strcmp(name, h[i].name) != 0) + continue; + + *found = true; + return h[i].cb(call, cur); + } + + *found = false; + return -1; +} + +static int json_process_expr(struct json_call *call, struct blob_attr *cur) +{ + struct json_script_ctx *ctx = call->ctx; + bool found; + int ret; + + if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || + blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) { + ctx->handle_error(ctx, "Unexpected element type", cur); + return -1; + } + + ret = __json_process_type(call, cur, expr, ARRAY_SIZE(expr), &found); + if (!found) + ctx->handle_error(ctx, "Unknown expression type", cur); + + return ret; +} + +static int eval_string(struct json_call *call, struct blob_buf *buf, const char *name, const char *pattern) +{ + char *dest, *next, *str; + int len = 0; + bool var = false; + char c = '%'; + + dest = blobmsg_alloc_string_buffer(buf, name, 1); + if (!dest) + return -1; + + next = alloca(strlen(pattern) + 1); + strcpy(next, pattern); + + for (str = next; str; str = next) { + const char *cur; + char *end, *new_buf; + int cur_len = 0; + bool cur_var = var; + + end = strchr(str, '%'); + if (end) { + *end = 0; + next = end + 1; + var = !var; + } else { + end = str + strlen(str); + next = NULL; + } + + if (cur_var) { + if (end > str) { + cur = msg_find_var(call, str); + if (!cur) + continue; + + cur_len = strlen(cur); + } else { + cur = &c; + cur_len = 1; + } + } else { + if (str == end) + continue; + + cur = str; + cur_len = end - str; + } + + new_buf = blobmsg_realloc_string_buffer(buf, len + cur_len + 1); + if (!new_buf) { + /* Make eval_string return -1 */ + var = true; + break; + } + + dest = new_buf; + memcpy(dest + len, cur, cur_len); + len += cur_len; + } + + dest[len] = 0; + blobmsg_add_string_buffer(buf); + + if (var) + return -1; + + return 0; +} + +static int cmd_add_string(struct json_call *call, const char *pattern) +{ + return eval_string(call, &call->ctx->buf, NULL, pattern); +} + +int json_script_eval_string(struct json_script_ctx *ctx, struct blob_attr *vars, + struct blob_buf *buf, const char *name, + const char *pattern) +{ + struct json_call call = { + .ctx = ctx, + .vars = vars, + }; + + return eval_string(&call, buf, name, pattern); +} + +static int cmd_process_strings(struct json_call *call, struct blob_attr *attr) +{ + struct json_script_ctx *ctx = call->ctx; + struct blob_attr *cur; + int args = -1; + int rem, ret; + void *c; + + blob_buf_init(&ctx->buf, 0); + c = blobmsg_open_array(&ctx->buf, NULL); + blobmsg_for_each_attr(cur, attr, rem) { + if (args++ < 0) + continue; + + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) { + blobmsg_add_blob(&ctx->buf, cur); + continue; + } + + ret = cmd_add_string(call, blobmsg_data(cur)); + if (ret) { + ctx->handle_error(ctx, "Unterminated variable reference in string", attr); + return ret; + } + } + + blobmsg_close_array(&ctx->buf, c); + + return 0; +} + +static int __json_process_cmd(struct json_call *call, struct blob_attr *cur) +{ + struct json_script_ctx *ctx = call->ctx; + const char *name; + bool found; + int ret; + + if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || + blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) { + ctx->handle_error(ctx, "Unexpected element type", cur); + return -1; + } + + ret = __json_process_type(call, cur, cmd, ARRAY_SIZE(cmd), &found); + if (found) + return ret; + + name = blobmsg_data(blobmsg_data(cur)); + ret = cmd_process_strings(call, cur); + if (ret) + return ret; + + ctx->handle_command(ctx, name, blob_data(ctx->buf.head), call->vars); + + return 0; +} + +static int json_process_cmd(struct json_call *call, struct blob_attr *block) +{ + struct json_script_ctx *ctx = call->ctx; + struct blob_attr *cur; + int rem; + int ret; + int i = 0; + + if (blobmsg_type(block) != BLOBMSG_TYPE_ARRAY) { + ctx->handle_error(ctx, "Unexpected element type", block); + return -1; + } + + blobmsg_for_each_attr(cur, block, rem) { + if (ctx->abort) + break; + + switch(blobmsg_type(cur)) { + case BLOBMSG_TYPE_STRING: + if (!i) + return __json_process_cmd(call, block); + default: + ret = json_process_cmd(call, cur); + if (ret < -1) + return ret; + break; + } + i++; + } + + return 0; +} + +void json_script_run_file(struct json_script_ctx *ctx, struct json_script_file *file, + struct blob_attr *vars) +{ + static unsigned int _seq = 0; + struct json_call call = { + .ctx = ctx, + .vars = vars, + .seq = ++_seq, + }; + + /* overflow */ + if (!call.seq) + call.seq = ++_seq; + + ctx->abort = false; + + __json_script_run(&call, file, NULL); +} + +void json_script_run(struct json_script_ctx *ctx, const char *name, + struct blob_attr *vars) +{ + struct json_script_file *file; + + file = json_script_get_file(ctx, name); + if (!file) + return; + + json_script_run_file(ctx, file, vars); +} + +static void __json_script_file_free(struct json_script_file *f) +{ + struct json_script_file *next; + + if (!f) + return; + + next = f->next; + free(f); + + __json_script_file_free(next); +} + +void +json_script_free(struct json_script_ctx *ctx) +{ + struct json_script_file *f, *next; + + avl_remove_all_elements(&ctx->files, f, avl, next) + __json_script_file_free(f); + + blob_buf_free(&ctx->buf); +} + +static void +__default_handle_error(struct json_script_ctx *ctx, const char *msg, + struct blob_attr *context) +{ +} + +static const char * +__default_handle_var(struct json_script_ctx *ctx, const char *name, + struct blob_attr *vars) +{ + return NULL; +} + +static int +__default_handle_expr(struct json_script_ctx *ctx, const char *name, + struct blob_attr *expr, struct blob_attr *vars) +{ + return -1; +} + +static struct json_script_file * +__default_handle_file(struct json_script_ctx *ctx, const char *name) +{ + return NULL; +} + +void json_script_init(struct json_script_ctx *ctx) +{ + avl_init(&ctx->files, avl_strcmp, false, NULL); + + if (!ctx->handle_error) + ctx->handle_error = __default_handle_error; + + if (!ctx->handle_var) + ctx->handle_var = __default_handle_var; + + if (!ctx->handle_expr) + ctx->handle_expr = __default_handle_expr; + + if (!ctx->handle_file) + ctx->handle_file = __default_handle_file; +} diff --git a/src/3P/libubox/json_script.h b/src/3P/libubox/json_script.h new file mode 100644 index 00000000..66563e9d --- /dev/null +++ b/src/3P/libubox/json_script.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __JSON_SCRIPT_H +#define __JSON_SCRIPT_H + +#include "avl.h" +#include "blob.h" +#include "blobmsg.h" +#include "utils.h" + +struct json_script_file; + +struct json_script_ctx { + struct avl_tree files; + struct blob_buf buf; + + uint32_t run_seq; + bool abort; + + /* + * handle_command: handle a command that was not recognized by the + * json_script core (required) + * + * @cmd: blobmsg container of the processed command + * @vars: blobmsg container of current run variables + */ + void (*handle_command)(struct json_script_ctx *ctx, const char *name, + struct blob_attr *cmd, struct blob_attr *vars); + + /* + * handle_expr: handle an expression that was not recognized by the + * json_script core (optional) + * + * @expr: blobmsg container of the processed expression + * @vars: blobmsg container of current runtime variables + */ + int (*handle_expr)(struct json_script_ctx *ctx, const char *name, + struct blob_attr *expr, struct blob_attr *vars); + + /* + * handle_var - look up a variable that's not part of the runtime + * variable set (optional) + */ + const char *(*handle_var)(struct json_script_ctx *ctx, const char *name, + struct blob_attr *vars); + + /* + * handle_file - load a file by filename (optional) + * + * in case of wildcards, it can return a chain of json_script files + * linked via the ::next pointer. Only the first json_script file is + * added to the tree. + */ + struct json_script_file *(*handle_file)(struct json_script_ctx *ctx, + const char *name); + + /* + * handle_error - handle a processing error in a command or expression + * (optional) + * + * @msg: error message + * @context: source file context of the error (blobmsg container) + */ + void (*handle_error)(struct json_script_ctx *ctx, const char *msg, + struct blob_attr *context); +}; + +struct json_script_file { + struct avl_node avl; + struct json_script_file *next; + + unsigned int seq; + struct blob_attr data[]; +}; + +void json_script_init(struct json_script_ctx *ctx); +void json_script_free(struct json_script_ctx *ctx); + +/* + * json_script_run - run a json script with a set of runtime variables + * + * @filename: initial filename to run + * @vars: blobmsg container of the current runtime variables + */ +void json_script_run(struct json_script_ctx *ctx, const char *filename, + struct blob_attr *vars); + +void json_script_run_file(struct json_script_ctx *ctx, struct json_script_file *file, + struct blob_attr *vars); + +/* + * json_script_abort - abort current json script run + * + * to be called from a script context callback + */ +static inline void +json_script_abort(struct json_script_ctx *ctx) +{ + ctx->abort = true; +} + +/* + * json_script_eval_string - evaluate a string and store the result + * + * Can be used to process variable references outside of a script + * in a same way that they would be interpreted in the script context. + */ +int json_script_eval_string(struct json_script_ctx *ctx, struct blob_attr *vars, + struct blob_buf *buf, const char *name, + const char *pattern); + +struct json_script_file * +json_script_file_from_blobmsg(const char *name, void *data, int len); + +/* + * json_script_find_var - helper function to find a runtime variable from + * the list passed by json_script user. + * It is intended to be used by the .handle_var callback + */ +const char *json_script_find_var(struct json_script_ctx *ctx, struct blob_attr *vars, + const char *name); + +#endif diff --git a/src/3P/libubox/kvlist.c b/src/3P/libubox/kvlist.c new file mode 100644 index 00000000..a7b6ea07 --- /dev/null +++ b/src/3P/libubox/kvlist.c @@ -0,0 +1,101 @@ +/* + * kvlist - simple key/value store + * + * Copyright (C) 2014 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include + +#include "utils.h" +#include "avl-cmp.h" +#include "blob.h" + +#include "kvlist.h" + +int kvlist_strlen(struct kvlist *kv, const void *data) +{ + return strlen(data) + 1; +} + +int kvlist_blob_len(struct kvlist *kv, const void *data) +{ + return blob_pad_len(data); +} + +void kvlist_init(struct kvlist *kv, int (*get_len)(struct kvlist *kv, const void *data)) +{ + avl_init(&kv->avl, avl_strcmp, false, NULL); + kv->get_len = get_len; +} + +static struct kvlist_node *__kvlist_get(struct kvlist *kv, const char *name) +{ + struct kvlist_node *node; + + return avl_find_element(&kv->avl, name, node, avl); +} + +void *kvlist_get(struct kvlist *kv, const char *name) +{ + struct kvlist_node *node; + + node = __kvlist_get(kv, name); + if (!node) + return NULL; + + return node->data; +} + +bool kvlist_delete(struct kvlist *kv, const char *name) +{ + struct kvlist_node *node; + + node = __kvlist_get(kv, name); + if (node) { + avl_delete(&kv->avl, &node->avl); + free(node); + } + + return !!node; +} + +bool kvlist_set(struct kvlist *kv, const char *name, const void *data) +{ + struct kvlist_node *node; + char *name_buf; + int len = kv->get_len(kv, data); + + node = calloc_a(sizeof(struct kvlist_node) + len, + &name_buf, strlen(name) + 1); + if (!node) + return false; + + kvlist_delete(kv, name); + + memcpy(node->data, data, len); + + node->avl.key = strcpy(name_buf, name); + avl_insert(&kv->avl, &node->avl); + + return true; +} + +void kvlist_free(struct kvlist *kv) +{ + struct kvlist_node *node, *tmp; + + avl_remove_all_elements(&kv->avl, node, avl, tmp) + free(node); +} diff --git a/src/3P/libubox/kvlist.h b/src/3P/libubox/kvlist.h new file mode 100644 index 00000000..d59ff9e0 --- /dev/null +++ b/src/3P/libubox/kvlist.h @@ -0,0 +1,54 @@ +/* + * kvlist - simple key/value store + * + * Copyright (C) 2014 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __LIBUBOX_KVLIST_H +#define __LIBUBOX_KVLIST_H + +#include "avl.h" + +struct kvlist { + struct avl_tree avl; + + int (*get_len)(struct kvlist *kv, const void *data); +}; + +struct kvlist_node { + struct avl_node avl; + + char data[0] __attribute__((aligned(4))); +}; + +#define __ptr_to_kv(_ptr) container_of(((char *) (_ptr)), struct kvlist_node, data[0]) +#define __avl_list_to_kv(_l) container_of(_l, struct kvlist_node, avl.list) + +#define kvlist_for_each(kv, name, value) \ + for (value = (void *) __avl_list_to_kv((kv)->avl.list_head.next)->data, \ + name = (const char *) __ptr_to_kv(value)->avl.key, (void) name; \ + &__ptr_to_kv(value)->avl.list != &(kv)->avl.list_head; \ + value = (void *) (__avl_list_to_kv(__ptr_to_kv(value)->avl.list.next))->data, \ + name = (const char *) __ptr_to_kv(value)->avl.key) + +void kvlist_init(struct kvlist *kv, int (*get_len)(struct kvlist *kv, const void *data)); +void kvlist_free(struct kvlist *kv); +void *kvlist_get(struct kvlist *kv, const char *name); +bool kvlist_set(struct kvlist *kv, const char *name, const void *data); +bool kvlist_delete(struct kvlist *kv, const char *name); + +int kvlist_strlen(struct kvlist *kv, const void *data); +int kvlist_blob_len(struct kvlist *kv, const void *data); + +#endif diff --git a/src/3P/libubox/list.h b/src/3P/libubox/list.h new file mode 100644 index 00000000..ab52acff --- /dev/null +++ b/src/3P/libubox/list.h @@ -0,0 +1,208 @@ +/*- + * Copyright (c) 2011 Felix Fietkau + * Copyright (c) 2010 Isilon Systems, Inc. + * Copyright (c) 2010 iX Systems, Inc. + * Copyright (c) 2010 Panasas, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _LINUX_LIST_H_ +#define _LINUX_LIST_H_ + +#include +#include + +#define prefetch(x) + +#ifndef container_of +#define container_of(ptr, type, member) \ + ({ \ + const typeof(((type *) NULL)->member) *__mptr = (ptr); \ + (type *) ((char *) __mptr - offsetof(type, member)); \ + }) +#endif + +struct list_head { + struct list_head *next; + struct list_head *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } +#undef LIST_HEAD +#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) + +static inline void +INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list->prev = list; +} + +static inline bool +list_empty(const struct list_head *head) +{ + return (head->next == head); +} + +static inline bool +list_is_first(const struct list_head *list, + const struct list_head *head) +{ + return list->prev == head; +} + +static inline bool +list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +static inline void +_list_del(struct list_head *entry) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; +} + +static inline void +list_del(struct list_head *entry) +{ + _list_del(entry); + entry->next = entry->prev = NULL; +} + +static inline void +_list_add(struct list_head *_new, struct list_head *prev, + struct list_head *next) +{ + + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +static inline void +list_del_init(struct list_head *entry) +{ + _list_del(entry); + INIT_LIST_HEAD(entry); +} + +#define list_entry(ptr, type, field) container_of(ptr, type, field) +#define list_first_entry(ptr, type, field) list_entry((ptr)->next, type, field) +#define list_last_entry(ptr, type, field) list_entry((ptr)->prev, type, field) + +#define list_for_each(p, head) \ + for (p = (head)->next; p != (head); p = p->next) + +#define list_for_each_safe(p, n, head) \ + for (p = (head)->next, n = p->next; p != (head); p = n, n = p->next) + +#define list_for_each_entry(p, h, field) \ + for (p = list_first_entry(h, typeof(*p), field); &p->field != (h); \ + p = list_entry(p->field.next, typeof(*p), field)) + +#define list_for_each_entry_safe(p, n, h, field) \ + for (p = list_first_entry(h, typeof(*p), field), \ + n = list_entry(p->field.next, typeof(*p), field); &p->field != (h);\ + p = n, n = list_entry(n->field.next, typeof(*n), field)) + +#define list_for_each_entry_reverse(p, h, field) \ + for (p = list_last_entry(h, typeof(*p), field); &p->field != (h); \ + p = list_entry(p->field.prev, typeof(*p), field)) + +#define list_for_each_prev(p, h) for (p = (h)->prev; p != (h); p = p->prev) +#define list_for_each_prev_safe(p, n, h) for (p = (h)->prev, n = p->prev; p != (h); p = n, n = p->prev) + +static inline void +list_add(struct list_head *_new, struct list_head *head) +{ + _list_add(_new, head, head->next); +} + +static inline void +list_add_tail(struct list_head *_new, struct list_head *head) +{ + _list_add(_new, head->prev, head); +} + +static inline void +list_move(struct list_head *list, struct list_head *head) +{ + _list_del(list); + list_add(list, head); +} + +static inline void +list_move_tail(struct list_head *entry, struct list_head *head) +{ + _list_del(entry); + list_add_tail(entry, head); +} + +static inline void +_list_splice(const struct list_head *list, struct list_head *prev, + struct list_head *next) +{ + struct list_head *first; + struct list_head *last; + + if (list_empty(list)) + return; + + first = list->next; + last = list->prev; + first->prev = prev; + prev->next = first; + last->next = next; + next->prev = last; +} + +static inline void +list_splice(const struct list_head *list, struct list_head *head) +{ + _list_splice(list, head, head->next); +} + +static inline void +list_splice_tail(struct list_head *list, struct list_head *head) +{ + _list_splice(list, head->prev, head); +} + +static inline void +list_splice_init(struct list_head *list, struct list_head *head) +{ + _list_splice(list, head, head->next); + INIT_LIST_HEAD(list); +} + +static inline void +list_splice_tail_init(struct list_head *list, struct list_head *head) +{ + _list_splice(list, head->prev, head); + INIT_LIST_HEAD(list); +} + +#endif /* _LINUX_LIST_H_ */ diff --git a/src/3P/libubox/md5.c b/src/3P/libubox/md5.c new file mode 100644 index 00000000..781dbd16 --- /dev/null +++ b/src/3P/libubox/md5.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2014 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#include +#include + +#include "utils.h" +#include "md5.h" + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + */ +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define SET(n) \ + (*(uint32_t *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (block[(n)] = \ + (uint32_t)ptr[(n) * 4] | \ + ((uint32_t)ptr[(n) * 4 + 1] << 8) | \ + ((uint32_t)ptr[(n) * 4 + 2] << 16) | \ + ((uint32_t)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There are no alignment requirements. + */ +static const void *body(md5_ctx_t *ctx, const void *data, unsigned long size) +{ + const unsigned char *ptr; + uint32_t a, b, c, d; + uint32_t saved_a, saved_b, saved_c, saved_d; +#if __BYTE_ORDER != __LITTLE_ENDIAN + uint32_t block[16]; +#endif + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void md5_begin(md5_ctx_t *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void md5_hash(const void *data, size_t size, md5_ctx_t *ctx) +{ + uint32_t saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) { + available = 64 - used; + + if (size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~((size_t) 0x3f)); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +void md5_end(void *resbuf, md5_ctx_t *ctx) +{ + unsigned char *result = resbuf; + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; + + body(ctx, ctx->buffer, 64); + + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; + + memset(ctx, 0, sizeof(*ctx)); +} + +int md5sum(char *file, void *md5_buf) +{ + char buf[256]; + md5_ctx_t ctx; + int ret = 0; + FILE *f; + + f = fopen(file, "r"); + if (!f) + return -1; + + md5_begin(&ctx); + do { + int len = fread(buf, 1, sizeof(buf), f); + if (!len) + break; + + md5_hash(buf, len, &ctx); + ret += len; + } while(1); + + md5_end(md5_buf, &ctx); + fclose(f); + + return ret; +} diff --git a/src/3P/libubox/md5.h b/src/3P/libubox/md5.h new file mode 100644 index 00000000..b2f1b80c --- /dev/null +++ b/src/3P/libubox/md5.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. + */ + +#ifndef _LIBUBOX_MD5_H +#define _LIBUBOX_MD5_H + +#include +#include + +typedef struct md5_ctx { + uint32_t lo, hi; + uint32_t a, b, c, d; + unsigned char buffer[64]; +} md5_ctx_t; + +extern void md5_begin(md5_ctx_t *ctx); +extern void md5_hash(const void *data, size_t length, md5_ctx_t *ctx); +extern void md5_end(void *resbuf, md5_ctx_t *ctx); +int md5sum(char *file, void *md5_buf); + +#endif diff --git a/src/3P/libubox/runqueue.c b/src/3P/libubox/runqueue.c new file mode 100644 index 00000000..1d9fa4b3 --- /dev/null +++ b/src/3P/libubox/runqueue.c @@ -0,0 +1,282 @@ +/* + * runqueue.c - a simple task queueing/completion tracking helper + * + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include "runqueue.h" + +static void +__runqueue_empty_cb(struct uloop_timeout *timeout) +{ + struct runqueue *q = container_of(timeout, struct runqueue, timeout); + + q->empty_cb(q); +} + +void runqueue_init(struct runqueue *q) +{ + INIT_SAFE_LIST(&q->tasks_active); + INIT_SAFE_LIST(&q->tasks_inactive); +} + +static void __runqueue_start_next(struct uloop_timeout *timeout) +{ + struct runqueue *q = container_of(timeout, struct runqueue, timeout); + struct runqueue_task *t; + + do { + if (q->stopped) + break; + + if (list_empty(&q->tasks_inactive.list)) + break; + + if (q->max_running_tasks && q->running_tasks >= q->max_running_tasks) + break; + + t = list_first_entry(&q->tasks_inactive.list, struct runqueue_task, list.list); + safe_list_del(&t->list); + safe_list_add(&t->list, &q->tasks_active); + t->running = true; + q->running_tasks++; + if (t->run_timeout) + uloop_timeout_set(&t->timeout, t->run_timeout); + t->type->run(q, t); + } while (1); + + if (!q->empty && + list_empty(&q->tasks_active.list) && + list_empty(&q->tasks_inactive.list)) { + q->empty = true; + if (q->empty_cb) { + q->timeout.cb = __runqueue_empty_cb; + uloop_timeout_set(&q->timeout, 1); + } + } +} + +static void runqueue_start_next(struct runqueue *q) +{ + if (q->empty) + return; + + q->timeout.cb = __runqueue_start_next; + uloop_timeout_set(&q->timeout, 1); +} + +static int __runqueue_cancel(void *ctx, struct safe_list *list) +{ + struct runqueue_task *t; + + t = container_of(list, struct runqueue_task, list); + runqueue_task_cancel(t, 0); + + return 0; +} + +void runqueue_cancel_active(struct runqueue *q) +{ + safe_list_for_each(&q->tasks_active, __runqueue_cancel, NULL); +} + +void runqueue_cancel_pending(struct runqueue *q) +{ + safe_list_for_each(&q->tasks_inactive, __runqueue_cancel, NULL); +} + +void runqueue_cancel(struct runqueue *q) +{ + runqueue_cancel_pending(q); + runqueue_cancel_active(q); +} + +void runqueue_kill(struct runqueue *q) +{ + struct runqueue_task *t; + + while (!list_empty(&q->tasks_active.list)) { + t = list_first_entry(&q->tasks_active.list, struct runqueue_task, list.list); + runqueue_task_kill(t); + } + runqueue_cancel_pending(q); + uloop_timeout_cancel(&q->timeout); +} + +void runqueue_task_cancel(struct runqueue_task *t, int type) +{ + if (!t->queued) + return; + + if (!t->running) { + runqueue_task_complete(t); + return; + } + + t->cancelled = true; + if (t->cancel_timeout) + uloop_timeout_set(&t->timeout, t->cancel_timeout); + if (t->type->cancel) + t->type->cancel(t->q, t, type); +} + +static void +__runqueue_task_timeout(struct uloop_timeout *timeout) +{ + struct runqueue_task *t = container_of(timeout, struct runqueue_task, timeout); + + if (t->cancelled) + runqueue_task_kill(t); + else + runqueue_task_cancel(t, t->cancel_type); +} + +static void _runqueue_task_add(struct runqueue *q, struct runqueue_task *t, bool running, bool first) +{ + struct safe_list *head; + + if (t->queued) + return; + + if (!t->type->run && !running) { + fprintf(stderr, "BUG: inactive task added without run() callback\n"); + return; + } + + if (running) { + q->running_tasks++; + head = &q->tasks_active; + } else { + head = &q->tasks_inactive; + } + + t->timeout.cb = __runqueue_task_timeout; + t->q = q; + if (first) + safe_list_add_first(&t->list, head); + else + safe_list_add(&t->list, head); + t->cancelled = false; + t->queued = true; + t->running = running; + q->empty = false; + + runqueue_start_next(q); +} + +void runqueue_task_add(struct runqueue *q, struct runqueue_task *t, bool running) +{ + _runqueue_task_add(q, t, running, 0); +} + +void runqueue_task_add_first(struct runqueue *q, struct runqueue_task *t, bool running) +{ + _runqueue_task_add(q, t, running, 1); +} + +void runqueue_task_kill(struct runqueue_task *t) +{ + struct runqueue *q = t->q; + bool running = t->running; + + if (!t->queued) + return; + + runqueue_task_complete(t); + if (running && t->type->kill) + t->type->kill(q, t); + + runqueue_start_next(q); +} + +void runqueue_stop(struct runqueue *q) +{ + q->stopped = true; +} + +void runqueue_resume(struct runqueue *q) +{ + q->stopped = false; + runqueue_start_next(q); +} + +void runqueue_task_complete(struct runqueue_task *t) +{ + struct runqueue *q = t->q; + + if (!t->queued) + return; + + if (t->running) + t->q->running_tasks--; + + uloop_timeout_cancel(&t->timeout); + + safe_list_del(&t->list); + t->queued = false; + t->running = false; + t->cancelled = false; + if (t->complete) + t->complete(q, t); + runqueue_start_next(t->q); +} + +static void +__runqueue_proc_cb(struct uloop_process *p, int ret) +{ + struct runqueue_process *t = container_of(p, struct runqueue_process, proc); + + runqueue_task_complete(&t->task); +} + +void runqueue_process_cancel_cb(struct runqueue *q, struct runqueue_task *t, int type) +{ + struct runqueue_process *p = container_of(t, struct runqueue_process, task); + + if (!type) + type = SIGTERM; + + kill(p->proc.pid, type); +} + +void runqueue_process_kill_cb(struct runqueue *q, struct runqueue_task *t) +{ + struct runqueue_process *p = container_of(t, struct runqueue_process, task); + + uloop_process_delete(&p->proc); + kill(p->proc.pid, SIGKILL); +} + +static const struct runqueue_task_type runqueue_proc_type = { + .name = "process", + .cancel = runqueue_process_cancel_cb, + .kill = runqueue_process_kill_cb, +}; + +void runqueue_process_add(struct runqueue *q, struct runqueue_process *p, pid_t pid) +{ + if (p->proc.pending) + return; + + p->proc.pid = pid; + p->proc.cb = __runqueue_proc_cb; + if (!p->task.type) + p->task.type = &runqueue_proc_type; + uloop_process_add(&p->proc); + if (!p->task.running) + runqueue_task_add(q, &p->task, true); +} diff --git a/src/3P/libubox/runqueue.h b/src/3P/libubox/runqueue.h new file mode 100644 index 00000000..f182e4e3 --- /dev/null +++ b/src/3P/libubox/runqueue.h @@ -0,0 +1,124 @@ +/* + * runqueue.c - a simple task queueing/completion tracking helper + * + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __LIBUBOX_RUNQUEUE_H +#define __LIBUBOX_RUNQUEUE_H + +#include "list.h" +#include "safe_list.h" +#include "uloop.h" + +struct runqueue; +struct runqueue_task; +struct runqueue_task_type; + +struct runqueue { + struct safe_list tasks_active; + struct safe_list tasks_inactive; + struct uloop_timeout timeout; + + int running_tasks; + int max_running_tasks; + bool stopped; + bool empty; + + /* called when the runqueue is emptied */ + void (*empty_cb)(struct runqueue *q); +}; + +struct runqueue_task_type { + const char *name; + + /* + * called when a task is requested to run + * + * The task is removed from the list before this callback is run. It + * can re-arm itself using runqueue_task_add. + */ + void (*run)(struct runqueue *q, struct runqueue_task *t); + + /* + * called to request cancelling a task + * + * int type is used as an optional hint for the method to be used when + * cancelling the task, e.g. a signal number for processes. Calls + * runqueue_task_complete when done. + */ + void (*cancel)(struct runqueue *q, struct runqueue_task *t, int type); + + /* + * called to kill a task. must not make any calls to runqueue_task_complete, + * it has already been removed from the list. + */ + void (*kill)(struct runqueue *q, struct runqueue_task *t); +}; + +struct runqueue_task { + struct safe_list list; + const struct runqueue_task_type *type; + struct runqueue *q; + + void (*complete)(struct runqueue *q, struct runqueue_task *t); + + struct uloop_timeout timeout; + int run_timeout; + int cancel_timeout; + int cancel_type; + + bool queued; + bool running; + bool cancelled; +}; + +struct runqueue_process { + struct runqueue_task task; + struct uloop_process proc; +}; + +#define RUNQUEUE_INIT(_name, _max_running) { \ + .tasks_active = SAFE_LIST_INIT(_name.tasks_active), \ + .tasks_inactive = SAFE_LIST_INIT(_name.tasks_inactive), \ + .max_running_tasks = _max_running \ + } + +#define RUNQUEUE(_name, _max_running) \ + struct runqueue _name = RUNQUEUE_INIT(_name, _max_running) + +void runqueue_init(struct runqueue *q); +void runqueue_cancel(struct runqueue *q); +void runqueue_cancel_active(struct runqueue *q); +void runqueue_cancel_pending(struct runqueue *q); +void runqueue_kill(struct runqueue *q); + +void runqueue_stop(struct runqueue *q); +void runqueue_resume(struct runqueue *q); + +void runqueue_task_add(struct runqueue *q, struct runqueue_task *t, bool running); +void runqueue_task_add_first(struct runqueue *q, struct runqueue_task *t, bool running); +void runqueue_task_complete(struct runqueue_task *t); + +void runqueue_task_cancel(struct runqueue_task *t, int type); +void runqueue_task_kill(struct runqueue_task *t); + +void runqueue_process_add(struct runqueue *q, struct runqueue_process *p, pid_t pid); + +/* to be used only from runqueue_process callbacks */ +void runqueue_process_cancel_cb(struct runqueue *q, struct runqueue_task *t, int type); +void runqueue_process_kill_cb(struct runqueue *q, struct runqueue_task *t); + +#endif diff --git a/src/3P/libubox/safe_list.c b/src/3P/libubox/safe_list.c new file mode 100644 index 00000000..16f57e08 --- /dev/null +++ b/src/3P/libubox/safe_list.c @@ -0,0 +1,121 @@ +/* + * safe_list - linked list protected against recursive iteration with deletes + * + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "safe_list.h" + +struct safe_list_iterator { + struct safe_list_iterator **head; + struct safe_list_iterator *next_i; + struct safe_list *next; +}; + +static void +__safe_list_set_iterator(struct safe_list *list, + struct safe_list_iterator *i) +{ + struct safe_list_iterator *next_i; + struct safe_list *next; + + next = list_entry(list->list.next, struct safe_list, list); + next_i = next->i; + + next->i = i; + i->next = next; + i->head = &next->i; + + i->next_i = next_i; + if (next_i) + next_i->head = &i->next_i; +} + +static void +__safe_list_del_iterator(struct safe_list_iterator *i) +{ + *i->head = i->next_i; + if (i->next_i) + i->next_i->head = i->head; +} + +static void +__safe_list_move_iterator(struct safe_list *list, + struct safe_list_iterator *i) +{ + __safe_list_del_iterator(i); + __safe_list_set_iterator(list, i); +} + +int safe_list_for_each(struct safe_list *head, + int (*cb)(void *ctx, struct safe_list *list), + void *ctx) +{ + struct safe_list_iterator i; + struct safe_list *cur; + int ret = 0; + + for (cur = list_entry(head->list.next, struct safe_list, list), + __safe_list_set_iterator(cur, &i); + cur != head; + cur = i.next, __safe_list_move_iterator(cur, &i)) { + ret = cb(ctx, cur); + if (ret) + break; + } + + __safe_list_del_iterator(&i); + return ret; +} + +void safe_list_add(struct safe_list *list, struct safe_list *head) +{ + list->i = NULL; + list_add_tail(&list->list, &head->list); +} + +void safe_list_add_first(struct safe_list *list, struct safe_list *head) +{ + list->i = NULL; + list_add(&list->list, &head->list); +} + +void safe_list_del(struct safe_list *list) +{ + struct safe_list_iterator *i, *next_i, **tail; + struct safe_list *next; + + next = list_entry(list->list.next, struct safe_list, list); + list_del(&list->list); + + if (!list->i) + return; + + next_i = next->i; + tail = &next->i; + + for (i = list->i; i; i = i->next_i) { + tail = &i->next_i; + i->next = next; + } + + next->i = list->i; + list->i->head = &next->i; + *tail = next_i; + if (next_i) + next_i->head = tail; + + list->i = NULL; +} diff --git a/src/3P/libubox/safe_list.h b/src/3P/libubox/safe_list.h new file mode 100644 index 00000000..67b673d9 --- /dev/null +++ b/src/3P/libubox/safe_list.h @@ -0,0 +1,62 @@ +/* + * safe_list - linked list protected against recursive iteration with deletes + * + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Use this linked list implementation as a replacement for list.h if you + * want to allow deleting arbitrary list entries from within one or more + * recursive iterator calling context + */ + +#ifndef __LIBUBOX_SAFE_LIST_H +#define __LIBUBOX_SAFE_LIST_H + +#include +#include "list.h" +#include "utils.h" + +struct safe_list; +struct safe_list_iterator; + +struct safe_list { + struct list_head list; + struct safe_list_iterator *i; +}; + +int safe_list_for_each(struct safe_list *list, + int (*cb)(void *ctx, struct safe_list *list), + void *ctx); + +void safe_list_add(struct safe_list *list, struct safe_list *head); +void safe_list_add_first(struct safe_list *list, struct safe_list *head); +void safe_list_del(struct safe_list *list); + +#define INIT_SAFE_LIST(_head) \ + do { \ + INIT_LIST_HEAD(_head.list); \ + (_head)->i = NULL; \ + } while (0) + +#define SAFE_LIST_INIT(_name) { LIST_HEAD_INIT(_name.list), NULL } +#define SAFE_LIST(_name) struct safe_list _name = SAFE_LIST_INIT(_name) + +static inline bool safe_list_empty(struct safe_list *head) +{ + return list_empty(&head->list); +} + +#endif diff --git a/src/3P/libubox/sh/jshn.sh b/src/3P/libubox/sh/jshn.sh new file mode 100644 index 00000000..bf76edbe --- /dev/null +++ b/src/3P/libubox/sh/jshn.sh @@ -0,0 +1,280 @@ +# functions for parsing and generating json + +_json_get_var() { + # dest=$1 + # var=$2 + eval "$1=\"\$${JSON_PREFIX}$2\"" +} + +_json_set_var() { + # var=$1 + local ___val="$2" + eval "${JSON_PREFIX}$1=\"\$___val\"" +} + +__jshn_raw_append() { + # var=$1 + local value="$2" + local sep="${3:- }" + + eval "export -- \"$1=\${$1:+\${$1}\${value:+\$sep}}\$value\"" +} + +_jshn_append() { + # var=$1 + local _a_value="$2" + eval "${JSON_PREFIX}$1=\"\${${JSON_PREFIX}$1} \$_a_value\"" +} + +_get_var() { + # var=$1 + # value=$2 + eval "$1=\"\$$2\"" +} + +_set_var() { + # var=$1 + local __val="$2" + eval "$1=\"\$__val\"" +} + +_json_inc() { + # var=$1 + # dest=$2 + + let "${JSON_PREFIX}$1 += 1" "$2 = ${JSON_PREFIX}$1" +} + +_json_add_generic() { + # type=$1 + # name=$2 + # value=$3 + # cur=$4 + + local var + if [ "${4%%[0-9]*}" = "J_A" ]; then + _json_inc "S_$4" var + else + var="${2//[^a-zA-Z0-9_]/_}" + [[ "$var" == "$2" ]] || export -- "${JSON_PREFIX}N_${4}_${var}=$2" + fi + + export -- \ + "${JSON_PREFIX}${4}_$var=$3" \ + "${JSON_PREFIX}T_${4}_$var=$1" + _jshn_append "JSON_UNSET" "${4}_$var" + _jshn_append "K_$4" "$var" +} + +_json_add_table() { + # name=$1 + # type=$2 + # itype=$3 + local cur seq + + _json_get_var cur JSON_CUR + _json_inc JSON_SEQ seq + + local table="J_$3$seq" + _json_set_var "U_$table" "$cur" + export -- "${JSON_PREFIX}K_$table=" + unset "${JSON_PREFIX}S_$table" + _json_set_var JSON_CUR "$table" + _jshn_append "JSON_UNSET" "$table" + + _json_add_generic "$2" "$1" "$table" "$cur" +} + +_json_close_table() { + local _s_cur + + _json_get_var _s_cur JSON_CUR + _json_get_var "${JSON_PREFIX}JSON_CUR" "U_$_s_cur" +} + +json_set_namespace() { + local _new="$1" + local _old="$2" + + [ -n "$_old" ] && _set_var "$_old" "$JSON_PREFIX" + JSON_PREFIX="$_new" +} + +json_cleanup() { + local unset tmp + + _json_get_var unset JSON_UNSET + for tmp in $unset J_V; do + unset \ + ${JSON_PREFIX}U_$tmp \ + ${JSON_PREFIX}K_$tmp \ + ${JSON_PREFIX}S_$tmp \ + ${JSON_PREFIX}T_$tmp \ + ${JSON_PREFIX}N_$tmp \ + ${JSON_PREFIX}$tmp + done + + unset \ + ${JSON_PREFIX}JSON_SEQ \ + ${JSON_PREFIX}JSON_CUR \ + ${JSON_PREFIX}JSON_UNSET +} + +json_init() { + json_cleanup + export -n ${JSON_PREFIX}JSON_SEQ=0 + export -- \ + ${JSON_PREFIX}JSON_CUR="J_V" \ + ${JSON_PREFIX}K_J_V= +} + +json_add_object() { + _json_add_table "$1" object T +} + +json_close_object() { + _json_close_table +} + +json_add_array() { + _json_add_table "$1" array A +} + +json_close_array() { + _json_close_table +} + +json_add_string() { + local cur + _json_get_var cur JSON_CUR + _json_add_generic string "$1" "$2" "$cur" +} + +json_add_int() { + local cur + _json_get_var cur JSON_CUR + _json_add_generic int "$1" "$2" "$cur" +} + +json_add_boolean() { + local cur + _json_get_var cur JSON_CUR + _json_add_generic boolean "$1" "$2" "$cur" +} + +json_add_double() { + local cur + _json_get_var cur JSON_CUR + _json_add_generic double "$1" "$2" "$cur" +} + +# functions read access to json variables + +json_load() { + eval "`jshn -r "$1"`" +} + +json_dump() { + jshn "$@" ${JSON_PREFIX:+-p "$JSON_PREFIX"} -w +} + +json_get_type() { + local __dest="$1" + local __cur + + _json_get_var __cur JSON_CUR + local __var="${JSON_PREFIX}T_${__cur}_${2//[^a-zA-Z0-9_]/_}" + eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]" +} + +json_get_keys() { + local __dest="$1" + local _tbl_cur + + if [ -n "$2" ]; then + json_get_var _tbl_cur "$2" + else + _json_get_var _tbl_cur JSON_CUR + fi + local __var="${JSON_PREFIX}K_${_tbl_cur}" + eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]" +} + +json_get_values() { + local _v_dest="$1" + local _v_keys _v_val _select= + local _json_no_warning=1 + + unset "$_v_dest" + [ -n "$2" ] && { + json_select "$2" || return 1 + _select=1 + } + + json_get_keys _v_keys + set -- $_v_keys + while [ "$#" -gt 0 ]; do + json_get_var _v_val "$1" + __jshn_raw_append "$_v_dest" "$_v_val" + shift + done + [ -n "$_select" ] && json_select .. + + return 0 +} + +json_get_var() { + local __dest="$1" + local __cur + + _json_get_var __cur JSON_CUR + local __var="${JSON_PREFIX}${__cur}_${2//[^a-zA-Z0-9_]/_}" + eval "export -- \"$__dest=\${$__var:-$3}\"; [ -n \"\${$__var+x}\${3+x}\" ]" +} + +json_get_vars() { + while [ "$#" -gt 0 ]; do + local _var="$1"; shift + if [ "$_var" != "${_var#*:}" ]; then + json_get_var "${_var%%:*}" "${_var%%:*}" "${_var#*:}" + else + json_get_var "$_var" "$_var" + fi + done +} + +json_select() { + local target="$1" + local type + local cur + + [ -z "$1" ] && { + _json_set_var JSON_CUR "J_V" + return 0 + } + [[ "$1" == ".." ]] && { + _json_get_var cur JSON_CUR + _json_get_var cur "U_$cur" + _json_set_var JSON_CUR "$cur" + return 0 + } + json_get_type type "$target" + case "$type" in + object|array) + json_get_var cur "$target" + _json_set_var JSON_CUR "$cur" + ;; + *) + [ -n "$_json_no_warning" ] || \ + echo "WARNING: Variable '$target' does not exist or is not an array/object" + return 1 + ;; + esac +} + +json_is_a() { + local type + + json_get_type type "$1" + [ "$type" = "$2" ] +} diff --git a/src/3P/libubox/ulog.c b/src/3P/libubox/ulog.c new file mode 100644 index 00000000..296605dd --- /dev/null +++ b/src/3P/libubox/ulog.c @@ -0,0 +1,173 @@ +/* + * ulog - simple logging functions + * + * Copyright (C) 2015 Jo-Philipp Wich + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ulog.h" + +#include +#include +#include +#include +#include + +static int _ulog_channels = -1; +static int _ulog_facility = -1; +static int _ulog_threshold = LOG_DEBUG; +static int _ulog_initialized = 0; +static const char *_ulog_ident = NULL; + +static const char *ulog_default_ident(void) +{ + FILE *self; + static char line[64]; + char *p = NULL; + + if ((self = fopen("/proc/self/status", "r")) != NULL) { + while (fgets(line, sizeof(line), self)) { + if (!strncmp(line, "Name:", 5)) { + strtok(line, "\t\n"); + p = strtok(NULL, "\t\n"); + break; + } + } + fclose(self); + } + + return p; +} + +static void ulog_defaults(void) +{ + char *env; + + if (_ulog_initialized) + return; + + env = getenv("PREINIT"); + + if (_ulog_channels < 0) { + if (env && !strcmp(env, "1")) + _ulog_channels = ULOG_KMSG; + else if (isatty(1)) + _ulog_channels = ULOG_STDIO; + else + _ulog_channels = ULOG_SYSLOG; + } + + if (_ulog_facility < 0) { + if (env && !strcmp(env, "1")) + _ulog_facility = LOG_DAEMON; + else if (isatty(1)) + _ulog_facility = LOG_USER; + else + _ulog_facility = LOG_DAEMON; + } + + if (_ulog_ident == NULL && _ulog_channels != ULOG_STDIO) + _ulog_ident = ulog_default_ident(); + + if (_ulog_channels & ULOG_SYSLOG) + openlog(_ulog_ident, 0, _ulog_facility); + + _ulog_initialized = 1; +} + +static void ulog_kmsg(int priority, const char *fmt, va_list ap) +{ + FILE *kmsg; + + if ((kmsg = fopen("/dev/kmsg", "r+")) != NULL) { + fprintf(kmsg, "<%u>", priority); + + if (_ulog_ident) + fprintf(kmsg, "%s: ", _ulog_ident); + + vfprintf(kmsg, fmt, ap); + fclose(kmsg); + } +} + +static void ulog_stdio(int priority, const char *fmt, va_list ap) +{ + FILE *out = stderr; + + if (_ulog_ident) + fprintf(out, "%s: ", _ulog_ident); + + vfprintf(out, fmt, ap); +} + +static void ulog_syslog(int priority, const char *fmt, va_list ap) +{ + vsyslog(priority, fmt, ap); +} + +void ulog_open(int channels, int facility, const char *ident) +{ + ulog_close(); + + _ulog_channels = channels; + _ulog_facility = facility; + _ulog_ident = ident; +} + +void ulog_close(void) +{ + if (!_ulog_initialized) + return; + + if (_ulog_channels & ULOG_SYSLOG) + closelog(); + + _ulog_initialized = 0; +} + +void ulog_threshold(int threshold) +{ + _ulog_threshold = threshold; +} + +void ulog(int priority, const char *fmt, ...) +{ + va_list ap; + + if (priority > _ulog_threshold) + return; + + ulog_defaults(); + + if (_ulog_channels & ULOG_KMSG) + { + va_start(ap, fmt); + ulog_kmsg(priority, fmt, ap); + va_end(ap); + } + + if (_ulog_channels & ULOG_STDIO) + { + va_start(ap, fmt); + ulog_stdio(priority, fmt, ap); + va_end(ap); + } + + if (_ulog_channels & ULOG_SYSLOG) + { + va_start(ap, fmt); + ulog_syslog(priority, fmt, ap); + va_end(ap); + } +} diff --git a/src/3P/libubox/ulog.h b/src/3P/libubox/ulog.h new file mode 100644 index 00000000..4818b1a8 --- /dev/null +++ b/src/3P/libubox/ulog.h @@ -0,0 +1,42 @@ +/* + * ulog - simple logging functions + * + * Copyright (C) 2015 Jo-Philipp Wich + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __LIBUBOX_ULOG_H +#define __LIBUBOX_ULOG_H + +#include + +enum { + ULOG_KMSG = (1 << 0), + ULOG_SYSLOG = (1 << 1), + ULOG_STDIO = (1 << 2) +}; + +void ulog_open(int channels, int facility, const char *ident); +void ulog_close(void); + +void ulog_threshold(int threshold); + +void ulog(int priority, const char *fmt, ...); + +#define ULOG_INFO(fmt, ...) ulog(LOG_INFO, fmt, ## __VA_ARGS__) +#define ULOG_NOTE(fmt, ...) ulog(LOG_NOTICE, fmt, ## __VA_ARGS__) +#define ULOG_WARN(fmt, ...) ulog(LOG_WARNING, fmt, ## __VA_ARGS__) +#define ULOG_ERR(fmt, ...) ulog(LOG_ERR, fmt, ## __VA_ARGS__) + +#endif diff --git a/src/3P/libubox/uloop-epoll.c b/src/3P/libubox/uloop-epoll.c new file mode 100644 index 00000000..6014bea8 --- /dev/null +++ b/src/3P/libubox/uloop-epoll.c @@ -0,0 +1,108 @@ +/* + * uloop - event loop implementation + * + * Copyright (C) 2010-2016 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/** + * FIXME: uClibc < 0.9.30.3 does not define EPOLLRDHUP for Linux >= 2.6.17 + */ +#ifndef EPOLLRDHUP +#define EPOLLRDHUP 0x2000 +#endif + +static int uloop_init_pollfd(void) +{ + if (poll_fd >= 0) + return 0; + + poll_fd = epoll_create(32); + if (poll_fd < 0) + return -1; + + fcntl(poll_fd, F_SETFD, fcntl(poll_fd, F_GETFD) | FD_CLOEXEC); + return 0; +} + +static int register_poll(struct uloop_fd *fd, unsigned int flags) +{ + struct epoll_event ev; + int op = fd->registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; + + memset(&ev, 0, sizeof(struct epoll_event)); + + if (flags & ULOOP_READ) + ev.events |= EPOLLIN | EPOLLRDHUP; + + if (flags & ULOOP_WRITE) + ev.events |= EPOLLOUT; + + if (flags & ULOOP_EDGE_TRIGGER) + ev.events |= EPOLLET; + + ev.data.fd = fd->fd; + ev.data.ptr = fd; + fd->flags = flags; + + return epoll_ctl(poll_fd, op, fd->fd, &ev); +} + +static struct epoll_event events[ULOOP_MAX_EVENTS]; + +static int __uloop_fd_delete(struct uloop_fd *sock) +{ + sock->flags = 0; + return epoll_ctl(poll_fd, EPOLL_CTL_DEL, sock->fd, 0); +} + +static int uloop_fetch_events(int timeout) +{ + int n, nfds; + + nfds = epoll_wait(poll_fd, events, ARRAY_SIZE(events), timeout); + for (n = 0; n < nfds; ++n) { + struct uloop_fd_event *cur = &cur_fds[n]; + struct uloop_fd *u = events[n].data.ptr; + unsigned int ev = 0; + + cur->fd = u; + if (!u) + continue; + + if (events[n].events & (EPOLLERR|EPOLLHUP)) { + u->error = true; + if (!(u->flags & ULOOP_ERROR_CB)) + uloop_fd_delete(u); + } + + if(!(events[n].events & (EPOLLRDHUP|EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP))) { + cur->fd = NULL; + continue; + } + + if(events[n].events & EPOLLRDHUP) + u->eof = true; + + if(events[n].events & EPOLLIN) + ev |= ULOOP_READ; + + if(events[n].events & EPOLLOUT) + ev |= ULOOP_WRITE; + + cur->events = ev; + } + + return nfds; +} diff --git a/src/3P/libubox/uloop-kqueue.c b/src/3P/libubox/uloop-kqueue.c new file mode 100644 index 00000000..ba5595b8 --- /dev/null +++ b/src/3P/libubox/uloop-kqueue.c @@ -0,0 +1,148 @@ +/* + * uloop - event loop implementation + * + * Copyright (C) 2010-2016 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +static int uloop_init_pollfd(void) +{ + struct timespec timeout = { 0, 0 }; + struct kevent ev = {}; + + if (poll_fd >= 0) + return 0; + + poll_fd = kqueue(); + if (poll_fd < 0) + return -1; + + EV_SET(&ev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, 0); + kevent(poll_fd, &ev, 1, NULL, 0, &timeout); + + return 0; +} + + +static uint16_t get_flags(unsigned int flags, unsigned int mask) +{ + uint16_t kflags = 0; + + if (!(flags & mask)) + return EV_DELETE; + + kflags = EV_ADD; + if (flags & ULOOP_EDGE_TRIGGER) + kflags |= EV_CLEAR; + + return kflags; +} + +static struct kevent events[ULOOP_MAX_EVENTS]; + +static int register_kevent(struct uloop_fd *fd, unsigned int flags) +{ + struct timespec timeout = { 0, 0 }; + struct kevent ev[2]; + int nev = 0; + unsigned int fl = 0; + unsigned int changed; + uint16_t kflags; + + if (flags & ULOOP_EDGE_DEFER) + flags &= ~ULOOP_EDGE_TRIGGER; + + changed = flags ^ fd->flags; + if (changed & ULOOP_EDGE_TRIGGER) + changed |= flags; + + if (changed & ULOOP_READ) { + kflags = get_flags(flags, ULOOP_READ); + EV_SET(&ev[nev++], fd->fd, EVFILT_READ, kflags, 0, 0, fd); + } + + if (changed & ULOOP_WRITE) { + kflags = get_flags(flags, ULOOP_WRITE); + EV_SET(&ev[nev++], fd->fd, EVFILT_WRITE, kflags, 0, 0, fd); + } + + if (!flags) + fl |= EV_DELETE; + + fd->flags = flags; + if (kevent(poll_fd, ev, nev, NULL, fl, &timeout) == -1) + return -1; + + return 0; +} + +static int register_poll(struct uloop_fd *fd, unsigned int flags) +{ + if (flags & ULOOP_EDGE_TRIGGER) + flags |= ULOOP_EDGE_DEFER; + else + flags &= ~ULOOP_EDGE_DEFER; + + return register_kevent(fd, flags); +} + +static int __uloop_fd_delete(struct uloop_fd *fd) +{ + return register_poll(fd, 0); +} + +static int uloop_fetch_events(int timeout) +{ + struct timespec ts; + int nfds, n; + + if (timeout >= 0) { + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + } + + nfds = kevent(poll_fd, NULL, 0, events, ARRAY_SIZE(events), timeout >= 0 ? &ts : NULL); + for (n = 0; n < nfds; n++) { + struct uloop_fd_event *cur = &cur_fds[n]; + struct uloop_fd *u = events[n].udata; + unsigned int ev = 0; + + cur->fd = u; + if (!u) + continue; + + if (events[n].flags & EV_ERROR) { + u->error = true; + if (!(u->flags & ULOOP_ERROR_CB)) + uloop_fd_delete(u); + } + + if(events[n].filter == EVFILT_READ) + ev |= ULOOP_READ; + else if (events[n].filter == EVFILT_WRITE) + ev |= ULOOP_WRITE; + + if (events[n].flags & EV_EOF) + u->eof = true; + else if (!ev) + cur->fd = NULL; + + cur->events = ev; + if (u->flags & ULOOP_EDGE_DEFER) { + u->flags &= ~ULOOP_EDGE_DEFER; + u->flags |= ULOOP_EDGE_TRIGGER; + register_kevent(u, u->flags); + } + } + return nfds; +} diff --git a/src/3P/libubox/uloop.c b/src/3P/libubox/uloop.c new file mode 100644 index 00000000..296366e8 --- /dev/null +++ b/src/3P/libubox/uloop.c @@ -0,0 +1,567 @@ +/* + * uloop - event loop implementation + * + * Copyright (C) 2010-2016 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uloop.h" +#include "utils.h" + +#ifdef USE_KQUEUE +#include +#endif +#ifdef USE_EPOLL +#include +#endif +#include + +struct uloop_fd_event { + struct uloop_fd *fd; + unsigned int events; +}; + +struct uloop_fd_stack { + struct uloop_fd_stack *next; + struct uloop_fd *fd; + unsigned int events; +}; + +static struct uloop_fd_stack *fd_stack = NULL; + +#define ULOOP_MAX_EVENTS 10 + +static struct list_head timeouts = LIST_HEAD_INIT(timeouts); +static struct list_head processes = LIST_HEAD_INIT(processes); + +static int poll_fd = -1; +bool uloop_cancelled = false; +static int uloop_status = 0; +static bool do_sigchld = false; + +static struct uloop_fd_event cur_fds[ULOOP_MAX_EVENTS]; +static int cur_fd, cur_nfds; + +int uloop_fd_add(struct uloop_fd *sock, unsigned int flags); + +#ifdef USE_KQUEUE +#include "uloop-kqueue.c" +#endif + +#ifdef USE_EPOLL +#include "uloop-epoll.c" +#endif + +static void waker_consume(struct uloop_fd *fd, unsigned int events) +{ + char buf[4]; + + while (read(fd->fd, buf, 4) > 0) + ; +} + +static int waker_pipe = -1; +static struct uloop_fd waker_fd = { + .fd = -1, + .cb = waker_consume, +}; + +static void waker_init_fd(int fd) +{ + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); +} + +static int waker_init(void) +{ + int fds[2]; + + if (waker_pipe >= 0) + return 0; + + if (pipe(fds) < 0) + return -1; + + waker_init_fd(fds[0]); + waker_init_fd(fds[1]); + waker_pipe = fds[1]; + + waker_fd.fd = fds[0]; + waker_fd.cb = waker_consume; + uloop_fd_add(&waker_fd, ULOOP_READ); + + return 0; +} + +int uloop_init(void) +{ + if (uloop_init_pollfd() < 0) + return -1; + + if (waker_init() < 0) { + uloop_done(); + return -1; + } + + return 0; +} + +static bool uloop_fd_stack_event(struct uloop_fd *fd, int events) +{ + struct uloop_fd_stack *cur; + + /* + * Do not buffer events for level-triggered fds, they will keep firing. + * Caller needs to take care of recursion issues. + */ + if (!(fd->flags & ULOOP_EDGE_TRIGGER)) + return false; + + for (cur = fd_stack; cur; cur = cur->next) { + if (cur->fd != fd) + continue; + + if (events < 0) + cur->fd = NULL; + else + cur->events |= events | ULOOP_EVENT_BUFFERED; + + return true; + } + + return false; +} + +static void uloop_run_events(int timeout) +{ + struct uloop_fd_event *cur; + struct uloop_fd *fd; + + if (!cur_nfds) { + cur_fd = 0; + cur_nfds = uloop_fetch_events(timeout); + if (cur_nfds < 0) + cur_nfds = 0; + } + + while (cur_nfds > 0) { + struct uloop_fd_stack stack_cur; + unsigned int events; + + cur = &cur_fds[cur_fd++]; + cur_nfds--; + + fd = cur->fd; + events = cur->events; + if (!fd) + continue; + + if (!fd->cb) + continue; + + if (uloop_fd_stack_event(fd, cur->events)) + continue; + + stack_cur.next = fd_stack; + stack_cur.fd = fd; + fd_stack = &stack_cur; + do { + stack_cur.events = 0; + fd->cb(fd, events); + events = stack_cur.events & ULOOP_EVENT_MASK; + } while (stack_cur.fd && events); + fd_stack = stack_cur.next; + + return; + } +} + +int uloop_fd_add(struct uloop_fd *sock, unsigned int flags) +{ + unsigned int fl; + int ret; + + if (!(flags & (ULOOP_READ | ULOOP_WRITE))) + return uloop_fd_delete(sock); + + if (!sock->registered && !(flags & ULOOP_BLOCKING)) { + fl = fcntl(sock->fd, F_GETFL, 0); + fl |= O_NONBLOCK; + fcntl(sock->fd, F_SETFL, fl); + } + + ret = register_poll(sock, flags); + if (ret < 0) + goto out; + + sock->registered = true; + sock->eof = false; + sock->error = false; + +out: + return ret; +} + +int uloop_fd_delete(struct uloop_fd *fd) +{ + int i; + + for (i = 0; i < cur_nfds; i++) { + if (cur_fds[cur_fd + i].fd != fd) + continue; + + cur_fds[cur_fd + i].fd = NULL; + } + + if (!fd->registered) + return 0; + + fd->registered = false; + uloop_fd_stack_event(fd, -1); + return __uloop_fd_delete(fd); +} + +static int tv_diff(struct timeval *t1, struct timeval *t2) +{ + return + (t1->tv_sec - t2->tv_sec) * 1000 + + (t1->tv_usec - t2->tv_usec) / 1000; +} + +int uloop_timeout_add(struct uloop_timeout *timeout) +{ + struct uloop_timeout *tmp; + struct list_head *h = &timeouts; + + if (timeout->pending) + return -1; + + list_for_each_entry(tmp, &timeouts, list) { + if (tv_diff(&tmp->time, &timeout->time) > 0) { + h = &tmp->list; + break; + } + } + + list_add_tail(&timeout->list, h); + timeout->pending = true; + + return 0; +} + +static void uloop_gettime(struct timeval *tv) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; +} + +int uloop_timeout_set(struct uloop_timeout *timeout, int msecs) +{ + struct timeval *time = &timeout->time; + + if (timeout->pending) + uloop_timeout_cancel(timeout); + + uloop_gettime(time); + + time->tv_sec += msecs / 1000; + time->tv_usec += (msecs % 1000) * 1000; + + if (time->tv_usec > 1000000) { + time->tv_sec++; + time->tv_usec -= 1000000; + } + + return uloop_timeout_add(timeout); +} + +int uloop_timeout_cancel(struct uloop_timeout *timeout) +{ + if (!timeout->pending) + return -1; + + list_del(&timeout->list); + timeout->pending = false; + + return 0; +} + +int uloop_timeout_remaining(struct uloop_timeout *timeout) +{ + struct timeval now; + + if (!timeout->pending) + return -1; + + uloop_gettime(&now); + + return tv_diff(&timeout->time, &now); +} + +int uloop_process_add(struct uloop_process *p) +{ + struct uloop_process *tmp; + struct list_head *h = &processes; + + if (p->pending) + return -1; + + list_for_each_entry(tmp, &processes, list) { + if (tmp->pid > p->pid) { + h = &tmp->list; + break; + } + } + + list_add_tail(&p->list, h); + p->pending = true; + + return 0; +} + +int uloop_process_delete(struct uloop_process *p) +{ + if (!p->pending) + return -1; + + list_del(&p->list); + p->pending = false; + + return 0; +} + +static void uloop_handle_processes(void) +{ + struct uloop_process *p, *tmp; + pid_t pid; + int ret; + + do_sigchld = false; + + while (1) { + pid = waitpid(-1, &ret, WNOHANG); + if (pid < 0 && errno == EINTR) + continue; + + if (pid <= 0) + return; + + list_for_each_entry_safe(p, tmp, &processes, list) { + if (p->pid < pid) + continue; + + if (p->pid > pid) + break; + + uloop_process_delete(p); + p->cb(p, ret); + } + } +} + +static void uloop_signal_wake(void) +{ + do { + if (write(waker_pipe, "w", 1) < 0) { + if (errno == EINTR) + continue; + } + break; + } while (1); +} + +static void uloop_handle_sigint(int signo) +{ + uloop_status = signo; + uloop_cancelled = true; + uloop_signal_wake(); +} + +static void uloop_sigchld(int signo) +{ + do_sigchld = true; + uloop_signal_wake(); +} + +static void uloop_install_handler(int signum, void (*handler)(int), struct sigaction* old, bool add) +{ + struct sigaction s; + struct sigaction *act; + + act = NULL; + sigaction(signum, NULL, &s); + + if (add) { + if (s.sa_handler == SIG_DFL) { /* Do not override existing custom signal handlers */ + memcpy(old, &s, sizeof(struct sigaction)); + s.sa_handler = handler; + s.sa_flags = 0; + act = &s; + } + } + else if (s.sa_handler == handler) { /* Do not restore if someone modified our handler */ + act = old; + } + + if (act != NULL) + sigaction(signum, act, NULL); +} + +static void uloop_ignore_signal(int signum, bool ignore) +{ + struct sigaction s; + void *new_handler = NULL; + + sigaction(signum, NULL, &s); + + if (ignore) { + if (s.sa_handler == SIG_DFL) /* Ignore only if there isn't any custom handler */ + new_handler = SIG_IGN; + } else { + if (s.sa_handler == SIG_IGN) /* Restore only if noone modified our SIG_IGN */ + new_handler = SIG_DFL; + } + + if (new_handler) { + s.sa_handler = new_handler; + s.sa_flags = 0; + sigaction(signum, &s, NULL); + } +} + +static void uloop_setup_signals(bool add) +{ + static struct sigaction old_sigint, old_sigchld, old_sigterm; + + uloop_install_handler(SIGINT, uloop_handle_sigint, &old_sigint, add); + uloop_install_handler(SIGTERM, uloop_handle_sigint, &old_sigterm, add); + uloop_install_handler(SIGCHLD, uloop_sigchld, &old_sigchld, add); + + uloop_ignore_signal(SIGPIPE, add); +} + +static int uloop_get_next_timeout(struct timeval *tv) +{ + struct uloop_timeout *timeout; + int diff; + + if (list_empty(&timeouts)) + return -1; + + timeout = list_first_entry(&timeouts, struct uloop_timeout, list); + diff = tv_diff(&timeout->time, tv); + if (diff < 0) + return 0; + + return diff; +} + +static void uloop_process_timeouts(struct timeval *tv) +{ + struct uloop_timeout *t; + + while (!list_empty(&timeouts)) { + t = list_first_entry(&timeouts, struct uloop_timeout, list); + + if (tv_diff(&t->time, tv) > 0) + break; + + uloop_timeout_cancel(t); + if (t->cb) + t->cb(t); + } +} + +static void uloop_clear_timeouts(void) +{ + struct uloop_timeout *t, *tmp; + + list_for_each_entry_safe(t, tmp, &timeouts, list) + uloop_timeout_cancel(t); +} + +static void uloop_clear_processes(void) +{ + struct uloop_process *p, *tmp; + + list_for_each_entry_safe(p, tmp, &processes, list) + uloop_process_delete(p); +} + +int uloop_run(void) +{ + static int recursive_calls = 0; + struct timeval tv; + + /* + * Handlers are only updated for the first call to uloop_run() (and restored + * when this call is done). + */ + if (!recursive_calls++) + uloop_setup_signals(true); + + uloop_status = 0; + uloop_cancelled = false; + while (!uloop_cancelled) + { + uloop_gettime(&tv); + uloop_process_timeouts(&tv); + + if (do_sigchld) + uloop_handle_processes(); + + if (uloop_cancelled) + break; + + uloop_gettime(&tv); + uloop_run_events(uloop_get_next_timeout(&tv)); + } + + if (!--recursive_calls) + uloop_setup_signals(false); + + return uloop_status; +} + +void uloop_done(void) +{ + if (poll_fd >= 0) { + close(poll_fd); + poll_fd = -1; + } + + if (waker_pipe >= 0) { + uloop_fd_delete(&waker_fd); + close(waker_pipe); + close(waker_fd.fd); + waker_pipe = -1; + } + + uloop_clear_timeouts(); + uloop_clear_processes(); +} diff --git a/src/3P/libubox/uloop.h b/src/3P/libubox/uloop.h new file mode 100644 index 00000000..2f1eb4c4 --- /dev/null +++ b/src/3P/libubox/uloop.h @@ -0,0 +1,109 @@ +/* + * uloop - event loop implementation + * + * Copyright (C) 2010-2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _ULOOP_H__ +#define _ULOOP_H__ + +#include +#include +#include +#include +#include + +#if defined(__APPLE__) || defined(__FreeBSD__) +#define USE_KQUEUE +#else +#define USE_EPOLL +#endif + +#include "list.h" + +struct uloop_fd; +struct uloop_timeout; +struct uloop_process; + +typedef void (*uloop_fd_handler)(struct uloop_fd *u, unsigned int events); +typedef void (*uloop_timeout_handler)(struct uloop_timeout *t); +typedef void (*uloop_process_handler)(struct uloop_process *c, int ret); + +#define ULOOP_READ (1 << 0) +#define ULOOP_WRITE (1 << 1) +#define ULOOP_EDGE_TRIGGER (1 << 2) +#define ULOOP_BLOCKING (1 << 3) + +#define ULOOP_EVENT_MASK (ULOOP_READ | ULOOP_WRITE) + +/* internal flags */ +#define ULOOP_EVENT_BUFFERED (1 << 4) +#ifdef USE_KQUEUE +#define ULOOP_EDGE_DEFER (1 << 5) +#endif + +#define ULOOP_ERROR_CB (1 << 6) + +struct uloop_fd +{ + uloop_fd_handler cb; + int fd; + bool eof; + bool error; + bool registered; + uint8_t flags; +}; + +struct uloop_timeout +{ + struct list_head list; + bool pending; + + uloop_timeout_handler cb; + struct timeval time; +}; + +struct uloop_process +{ + struct list_head list; + bool pending; + + uloop_process_handler cb; + pid_t pid; +}; + +extern bool uloop_cancelled; +extern bool uloop_handle_sigchld; + +int uloop_fd_add(struct uloop_fd *sock, unsigned int flags); +int uloop_fd_delete(struct uloop_fd *sock); + +int uloop_timeout_add(struct uloop_timeout *timeout); +int uloop_timeout_set(struct uloop_timeout *timeout, int msecs); +int uloop_timeout_cancel(struct uloop_timeout *timeout); +int uloop_timeout_remaining(struct uloop_timeout *timeout); + +int uloop_process_add(struct uloop_process *p); +int uloop_process_delete(struct uloop_process *p); + +static inline void uloop_end(void) +{ + uloop_cancelled = true; +} + +int uloop_init(void); +int uloop_run(void); +void uloop_done(void); + +#endif diff --git a/src/3P/libubox/usock.c b/src/3P/libubox/usock.c new file mode 100644 index 00000000..0ce53904 --- /dev/null +++ b/src/3P/libubox/usock.c @@ -0,0 +1,293 @@ +/* + * usock - socket helper functions + * + * Copyright (C) 2010 Steven Barth + * Copyright (C) 2011-2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usock.h" +#include "utils.h" + +static void usock_set_flags(int sock, unsigned int type) +{ + if (!(type & USOCK_NOCLOEXEC)) + fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC); + + if (type & USOCK_NONBLOCK) + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK); +} + +static int usock_connect(int type, struct sockaddr *sa, int sa_len, int family, int socktype, bool server) +{ + int sock; + + sock = socket(family, socktype, 0); + if (sock < 0) + return -1; + + usock_set_flags(sock, type); + + if (server) { + const int one = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + + if (!bind(sock, sa, sa_len) && + (socktype != SOCK_STREAM || !listen(sock, SOMAXCONN))) + return sock; + } else { + if (!connect(sock, sa, sa_len) || errno == EINPROGRESS) + return sock; + } + + close(sock); + return -1; +} + +static int usock_unix(int type, const char *host) +{ + struct sockaddr_un sun = {.sun_family = AF_UNIX}; + bool server = !!(type & USOCK_SERVER); + int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; + + if (strlen(host) >= sizeof(sun.sun_path)) { + errno = EINVAL; + return -1; + } + strcpy(sun.sun_path, host); + + return usock_connect(type, (struct sockaddr*)&sun, sizeof(sun), AF_UNIX, socktype, server); +} + +static int +usock_inet_notimeout(int type, struct addrinfo *result, void *addr) +{ + struct addrinfo *rp; + int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; + bool server = !!(type & USOCK_SERVER); + int sock; + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sock = usock_connect(type, rp->ai_addr, rp->ai_addrlen, rp->ai_family, socktype, server); + if (sock >= 0) { + if (addr) + memcpy(addr, rp->ai_addr, rp->ai_addrlen); + return sock; + } + } + + return -1; +} + +static int poll_restart(struct pollfd *fds, int nfds, int timeout) +{ + struct timespec ts, cur; + int msec = timeout % 1000; + int ret; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + ts.tv_nsec += msec * 1000000; + if (ts.tv_nsec > 1000000000) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + ts.tv_sec += timeout / 1000; + + while (1) { + ret = poll(fds, nfds, timeout); + if (ret == EAGAIN) + continue; + + if (ret != EINTR) + return ret; + + clock_gettime(CLOCK_MONOTONIC, &cur); + timeout = (ts.tv_sec - cur.tv_sec) * 1000; + timeout += (ts.tv_nsec - cur.tv_nsec) / 1000000; + if (timeout <= 0) + return 0; + } +} + +int usock_inet_timeout(int type, const char *host, const char *service, + void *addr, int timeout) +{ + int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; + bool server = !!(type & USOCK_SERVER); + struct addrinfo *result, *rp; + struct addrinfo hints = { + .ai_family = (type & USOCK_IPV6ONLY) ? AF_INET6 : + (type & USOCK_IPV4ONLY) ? AF_INET : AF_UNSPEC, + .ai_socktype = socktype, + .ai_flags = AI_ADDRCONFIG + | ((type & USOCK_SERVER) ? AI_PASSIVE : 0) + | ((type & USOCK_NUMERIC) ? AI_NUMERICHOST : 0), + }; + struct addrinfo *rp_v6 = NULL; + struct addrinfo *rp_v4 = NULL; + struct pollfd pfds[2] = { + { .fd = -1, .events = POLLOUT }, + { .fd = -1, .events = POLLOUT }, + }; + int sock = -1; + int i; + + if (getaddrinfo(host, service, &hints, &result)) + return -1; + + if (timeout <= 0 || server) { + sock = usock_inet_notimeout(type, result, addr); + goto free_addrinfo; + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + if (rp->ai_family == AF_INET6 && !rp_v6) + rp_v6 = rp; + if (rp->ai_family == AF_INET && !rp_v4) + rp_v4 = rp; + } + + if (!rp_v6 && !rp_v4) + goto out; + + if (rp_v6) { + rp = rp_v6; + pfds[0].fd = usock_connect(type | USOCK_NONBLOCK, rp->ai_addr, + rp->ai_addrlen, rp->ai_family, + socktype, server); + if (pfds[0].fd < 0) { + rp_v6 = NULL; + goto try_v4; + } + + if (timeout > 300) { + if (poll_restart(pfds, 1, 300) == 1) { + rp = rp_v6; + sock = pfds[0].fd; + goto out; + } + } + timeout -= 300; + } + +try_v4: + if (rp_v4) { + rp = rp_v4; + pfds[1].fd = usock_connect(type | USOCK_NONBLOCK, rp->ai_addr, + rp->ai_addrlen, rp->ai_family, + socktype, server); + if (pfds[1].fd < 0) { + rp_v4 = NULL; + if (!rp_v6) + goto out; + goto wait; + } + } + +wait: + poll_restart(pfds + !rp_v6, !!rp_v6 + !!rp_v4, timeout); + if (pfds[0].revents & POLLOUT) { + rp = rp_v6; + sock = pfds[0].fd; + goto out; + } + + if (pfds[1].revents & POLLOUT) { + rp = rp_v4; + sock = pfds[1].fd; + goto out; + } + +out: + for (i = 0; i < 2; i++) { + int fd = pfds[i].fd; + if (fd >= 0 && fd != sock) + close(fd); + } + + if (!(type & USOCK_NONBLOCK)) + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) & ~O_NONBLOCK); + + if (addr && sock >= 0) + memcpy(addr, rp->ai_addr, rp->ai_addrlen); +free_addrinfo: + freeaddrinfo(result); + return sock; +} + +const char *usock_port(int port) +{ + static char buffer[sizeof("65535\0")]; + + if (port < 0 || port > 65535) + return NULL; + + snprintf(buffer, sizeof(buffer), "%u", port); + + return buffer; +} + +int usock(int type, const char *host, const char *service) { + int sock; + + if (type & USOCK_UNIX) + sock = usock_unix(type, host); + else + sock = usock_inet(type, host, service, NULL); + + if (sock < 0) + return -1; + + return sock; +} + +int usock_wait_ready(int fd, int msecs) { + struct pollfd fds[1]; + int res; + + fds[0].fd = fd; + fds[0].events = POLLOUT; + + res = poll(fds, 1, msecs); + if (res < 0) { + return errno; + } else if (res == 0) { + return -ETIMEDOUT; + } else { + int err = 0; + socklen_t optlen = sizeof(err); + + res = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &optlen); + if (res) + return errno; + if (err) + return err; + } + + return 0; +} diff --git a/src/3P/libubox/usock.h b/src/3P/libubox/usock.h new file mode 100644 index 00000000..5f2b84b0 --- /dev/null +++ b/src/3P/libubox/usock.h @@ -0,0 +1,54 @@ +/* + * usock - socket helper functions + * + * Copyright (C) 2010 Steven Barth + * Copyright (C) 2011-2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef USOCK_H_ +#define USOCK_H_ + +#define USOCK_TCP 0 +#define USOCK_UDP 1 + +#define USOCK_SERVER 0x0100 +#define USOCK_NOCLOEXEC 0x0200 +#define USOCK_NONBLOCK 0x0400 +#define USOCK_NUMERIC 0x0800 +#define USOCK_IPV6ONLY 0x2000 +#define USOCK_IPV4ONLY 0x4000 +#define USOCK_UNIX 0x8000 + +const char *usock_port(int port); +int usock(int type, const char *host, const char *service); +int usock_inet_timeout(int type, const char *host, const char *service, + void *addr, int timeout); +static inline int +usock_inet(int type, const char *host, const char *service, void *addr) +{ + return usock_inet_timeout(type, host, service, addr, -1); +} + +/** + * Wait for a socket to become ready. + * + * This may be useful for users of USOCK_NONBLOCK to wait (with a timeout) + * for a socket. + * + * @param fd file descriptor of socket + * @param msecs timeout in microseconds + */ +int usock_wait_ready(int fd, int msecs); + +#endif /* USOCK_H_ */ diff --git a/src/3P/libubox/ustream-fd.c b/src/3P/libubox/ustream-fd.c new file mode 100644 index 00000000..b546fa1c --- /dev/null +++ b/src/3P/libubox/ustream-fd.c @@ -0,0 +1,173 @@ +/* + * ustream - library for stream buffer management + * + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include "ustream.h" + +static void ustream_fd_set_uloop(struct ustream *s, bool write) +{ + struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); + struct ustream_buf *buf; + unsigned int flags = ULOOP_EDGE_TRIGGER | ULOOP_ERROR_CB; + + if (!s->read_blocked && !s->eof) + flags |= ULOOP_READ; + + buf = s->w.head; + if (write || (buf && s->w.data_bytes && !s->write_error)) + flags |= ULOOP_WRITE; + + uloop_fd_add(&sf->fd, flags); +} + +static void ustream_fd_set_read_blocked(struct ustream *s) +{ + ustream_fd_set_uloop(s, false); +} + +static void ustream_fd_read_pending(struct ustream_fd *sf, bool *more) +{ + struct ustream *s = &sf->stream; + int buflen = 0; + ssize_t len; + char *buf; + + do { + if (s->read_blocked) + break; + + buf = ustream_reserve(s, 1, &buflen); + if (!buf) + break; + + len = read(sf->fd.fd, buf, buflen); + if (len < 0) { + if (errno == EINTR) + continue; + + if (errno == EAGAIN || errno == ENOTCONN) + return; + + len = 0; + } + + if (!len) { + if (!s->eof) + ustream_state_change(s); + s->eof = true; + ustream_fd_set_uloop(s, false); + return; + } + + ustream_fill_read(s, len); + *more = true; + } while (1); +} + +static int ustream_fd_write(struct ustream *s, const char *buf, int buflen, bool more) +{ + struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); + ssize_t ret = 0, len; + + if (!buflen) + return 0; + + while (buflen) { + len = write(sf->fd.fd, buf, buflen); + + if (len < 0) { + if (errno == EINTR) + continue; + + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOTCONN) + break; + + return -1; + } + + ret += len; + buf += len; + buflen -= len; + } + + if (buflen) + ustream_fd_set_uloop(s, true); + + return ret; +} + +static bool __ustream_fd_poll(struct ustream_fd *sf, unsigned int events) +{ + struct ustream *s = &sf->stream; + bool more = false; + + if (events & ULOOP_READ) + ustream_fd_read_pending(sf, &more); + + if (events & ULOOP_WRITE) { + bool no_more = ustream_write_pending(s); + if (no_more) + ustream_fd_set_uloop(s, false); + } + + if (sf->fd.error && !s->write_error) { + ustream_state_change(s); + s->write_error = true; + ustream_fd_set_uloop(s, false); + } + + return more; +} + +static bool ustream_fd_poll(struct ustream *s) +{ + struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); + + return __ustream_fd_poll(sf, ULOOP_READ | ULOOP_WRITE); +} + +static void ustream_uloop_cb(struct uloop_fd *fd, unsigned int events) +{ + struct ustream_fd *sf = container_of(fd, struct ustream_fd, fd); + + __ustream_fd_poll(sf, events); +} + +static void ustream_fd_free(struct ustream *s) +{ + struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); + + uloop_fd_delete(&sf->fd); +} + +void ustream_fd_init(struct ustream_fd *sf, int fd) +{ + struct ustream *s = &sf->stream; + + ustream_init_defaults(s); + + sf->fd.fd = fd; + sf->fd.cb = ustream_uloop_cb; + s->set_read_blocked = ustream_fd_set_read_blocked; + s->write = ustream_fd_write; + s->free = ustream_fd_free; + s->poll = ustream_fd_poll; + ustream_fd_set_uloop(s, false); +} diff --git a/src/3P/libubox/ustream.c b/src/3P/libubox/ustream.c new file mode 100644 index 00000000..d36ce080 --- /dev/null +++ b/src/3P/libubox/ustream.c @@ -0,0 +1,547 @@ +/* + * ustream - library for stream buffer management + * + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "ustream.h" + +static void ustream_init_buf(struct ustream_buf *buf, int len) +{ + if (!len) + abort(); + + memset(buf, 0, sizeof(*buf)); + buf->data = buf->tail = buf->head; + buf->end = buf->head + len; + *buf->head = 0; +} + +static void ustream_add_buf(struct ustream_buf_list *l, struct ustream_buf *buf) +{ + l->buffers++; + if (!l->tail) + l->head = buf; + else + l->tail->next = buf; + + buf->next = NULL; + l->tail = buf; + if (!l->data_tail) + l->data_tail = l->head; +} + +static bool ustream_can_alloc(struct ustream_buf_list *l) +{ + if (l->max_buffers <= 0) + return true; + + return (l->buffers < l->max_buffers); +} + +static int ustream_alloc_default(struct ustream *s, struct ustream_buf_list *l) +{ + struct ustream_buf *buf; + + if (!ustream_can_alloc(l)) + return -1; + + buf = malloc(sizeof(*buf) + l->buffer_len + s->string_data); + if (!buf) + return -1; + + ustream_init_buf(buf, l->buffer_len); + ustream_add_buf(l, buf); + + return 0; +} + +static void ustream_free_buffers(struct ustream_buf_list *l) +{ + struct ustream_buf *buf = l->head; + + while (buf) { + struct ustream_buf *next = buf->next; + + free(buf); + buf = next; + } + l->head = NULL; + l->tail = NULL; + l->data_tail = NULL; +} + +void ustream_free(struct ustream *s) +{ + if (s->free) + s->free(s); + + uloop_timeout_cancel(&s->state_change); + ustream_free_buffers(&s->r); + ustream_free_buffers(&s->w); +} + +static void ustream_state_change_cb(struct uloop_timeout *t) +{ + struct ustream *s = container_of(t, struct ustream, state_change); + + if (s->write_error) + ustream_free_buffers(&s->w); + if (s->notify_state) + s->notify_state(s); +} + +void ustream_init_defaults(struct ustream *s) +{ +#define DEFAULT_SET(_f, _default) \ + do { \ + if (!_f) \ + _f = _default; \ + } while(0) + + DEFAULT_SET(s->r.alloc, ustream_alloc_default); + DEFAULT_SET(s->w.alloc, ustream_alloc_default); + + DEFAULT_SET(s->r.min_buffers, 1); + DEFAULT_SET(s->r.max_buffers, 1); + DEFAULT_SET(s->r.buffer_len, 4096); + + DEFAULT_SET(s->w.min_buffers, 2); + DEFAULT_SET(s->w.max_buffers, -1); + DEFAULT_SET(s->w.buffer_len, 256); + +#undef DEFAULT_SET + + s->state_change.cb = ustream_state_change_cb; + s->write_error = false; + s->eof = false; + s->eof_write_done = false; + s->read_blocked = 0; + + s->r.buffers = 0; + s->r.data_bytes = 0; + + s->w.buffers = 0; + s->w.data_bytes = 0; +} + +static bool ustream_should_move(struct ustream_buf_list *l, struct ustream_buf *buf, int len) +{ + int maxlen; + int offset; + + /* nothing to squeeze */ + if (buf->data == buf->head) + return false; + + maxlen = buf->end - buf->head; + offset = buf->data - buf->head; + + /* less than half is available */ + if (offset > maxlen / 2) + return true; + + /* less than 32 bytes data but takes more than 1/4 space */ + if (buf->tail - buf->data < 32 && offset > maxlen / 4) + return true; + + /* more buf is already in list or can be allocated */ + if (buf != l->tail || ustream_can_alloc(l)) + return false; + + /* no need to move if len is available at the tail */ + return (buf->end - buf->tail < len); +} + +static void ustream_free_buf(struct ustream_buf_list *l, struct ustream_buf *buf) +{ + if (buf == l->head) + l->head = buf->next; + + if (buf == l->data_tail) + l->data_tail = buf->next; + + if (buf == l->tail) + l->tail = NULL; + + if (--l->buffers >= l->min_buffers) { + free(buf); + return; + } + + /* recycle */ + ustream_init_buf(buf, buf->end - buf->head); + ustream_add_buf(l, buf); +} + +static void __ustream_set_read_blocked(struct ustream *s, unsigned char val) +{ + bool changed = !!s->read_blocked != !!val; + + s->read_blocked = val; + if (changed) + s->set_read_blocked(s); +} + +void ustream_set_read_blocked(struct ustream *s, bool set) +{ + unsigned char val = s->read_blocked & ~READ_BLOCKED_USER; + + if (set) + val |= READ_BLOCKED_USER; + + __ustream_set_read_blocked(s, val); +} + +void ustream_consume(struct ustream *s, int len) +{ + struct ustream_buf *buf = s->r.head; + + if (!len) + return; + + s->r.data_bytes -= len; + if (s->r.data_bytes < 0) + abort(); + + do { + struct ustream_buf *next = buf->next; + int buf_len = buf->tail - buf->data; + + if (len < buf_len) { + buf->data += len; + break; + } + + len -= buf_len; + ustream_free_buf(&s->r, buf); + buf = next; + } while(len); + + __ustream_set_read_blocked(s, s->read_blocked & ~READ_BLOCKED_FULL); +} + +static void ustream_fixup_string(struct ustream *s, struct ustream_buf *buf) +{ + if (!s->string_data) + return; + + *buf->tail = 0; +} + +static bool ustream_prepare_buf(struct ustream *s, struct ustream_buf_list *l, int len) +{ + struct ustream_buf *buf; + + buf = l->data_tail; + if (buf) { + if (ustream_should_move(l, buf, len)) { + int len = buf->tail - buf->data; + + memmove(buf->head, buf->data, len); + buf->data = buf->head; + buf->tail = buf->data + len; + + if (l == &s->r) + ustream_fixup_string(s, buf); + } + /* some chunks available at the tail */ + if (buf->tail != buf->end) + return true; + /* next buf available */ + if (buf->next) { + l->data_tail = buf->next; + return true; + } + } + + if (!ustream_can_alloc(l)) + return false; + + if (l->alloc(s, l) < 0) + return false; + + l->data_tail = l->tail; + return true; +} + +char *ustream_reserve(struct ustream *s, int len, int *maxlen) +{ + struct ustream_buf *buf; + + if (!ustream_prepare_buf(s, &s->r, len)) { + __ustream_set_read_blocked(s, s->read_blocked | READ_BLOCKED_FULL); + *maxlen = 0; + return NULL; + } + + buf = s->r.data_tail; + *maxlen = buf->end - buf->tail; + return buf->tail; +} + +void ustream_fill_read(struct ustream *s, int len) +{ + struct ustream_buf *buf = s->r.data_tail; + int n = len; + int maxlen; + + s->r.data_bytes += len; + do { + if (!buf) + abort(); + + maxlen = buf->end - buf->tail; + if (len < maxlen) + maxlen = len; + + len -= maxlen; + buf->tail += maxlen; + ustream_fixup_string(s, buf); + + s->r.data_tail = buf; + buf = buf->next; + } while (len); + + if (s->notify_read) + s->notify_read(s, n); +} + +char *ustream_get_read_buf(struct ustream *s, int *buflen) +{ + char *data = NULL; + int len = 0; + + if (s->r.head) { + len = s->r.head->tail - s->r.head->data; + if (len > 0) + data = s->r.head->data; + } + + if (buflen) + *buflen = len; + + return data; +} + +int ustream_read(struct ustream *s, char *buf, int buflen) +{ + char *chunk; + int chunk_len; + int len = 0; + + do { + chunk = ustream_get_read_buf(s, &chunk_len); + if (!chunk) + break; + if (chunk_len > buflen - len) + chunk_len = buflen - len; + memcpy(buf + len, chunk, chunk_len); + ustream_consume(s, chunk_len); + len += chunk_len; + } while (len < buflen); + + return len; +} + +static void ustream_write_error(struct ustream *s) +{ + if (!s->write_error) + ustream_state_change(s); + s->write_error = true; +} + +bool ustream_write_pending(struct ustream *s) +{ + struct ustream_buf *buf = s->w.head; + int wr = 0, len; + + if (s->write_error) + return false; + + while (buf && s->w.data_bytes) { + struct ustream_buf *next = buf->next; + int maxlen = buf->tail - buf->data; + + len = s->write(s, buf->data, maxlen, !!buf->next); + if (len < 0) { + ustream_write_error(s); + break; + } + + if (len == 0) + break; + + wr += len; + s->w.data_bytes -= len; + if (len < maxlen) { + buf->data += len; + break; + } + + ustream_free_buf(&s->w, buf); + buf = next; + } + + if (s->notify_write) + s->notify_write(s, wr); + + if (s->eof && wr && !s->w.data_bytes) + ustream_state_change(s); + + return !s->w.data_bytes; +} + +static int ustream_write_buffered(struct ustream *s, const char *data, int len, int wr) +{ + struct ustream_buf_list *l = &s->w; + struct ustream_buf *buf; + int maxlen; + + while (len) { + if (!ustream_prepare_buf(s, &s->w, len)) + break; + + buf = l->data_tail; + + maxlen = buf->end - buf->tail; + if (maxlen > len) + maxlen = len; + + memcpy(buf->tail, data, maxlen); + buf->tail += maxlen; + data += maxlen; + len -= maxlen; + wr += maxlen; + l->data_bytes += maxlen; + } + + return wr; +} + +int ustream_write(struct ustream *s, const char *data, int len, bool more) +{ + struct ustream_buf_list *l = &s->w; + int wr = 0; + + if (s->write_error) + return 0; + + if (!l->data_bytes) { + wr = s->write(s, data, len, more); + if (wr == len) + return wr; + + if (wr < 0) { + ustream_write_error(s); + return wr; + } + + data += wr; + len -= wr; + } + + return ustream_write_buffered(s, data, len, wr); +} + +#define MAX_STACK_BUFLEN 256 + +int ustream_vprintf(struct ustream *s, const char *format, va_list arg) +{ + struct ustream_buf_list *l = &s->w; + char *buf; + va_list arg2; + int wr, maxlen, buflen; + + if (s->write_error) + return 0; + + if (!l->data_bytes) { + buf = alloca(MAX_STACK_BUFLEN); + va_copy(arg2, arg); + maxlen = vsnprintf(buf, MAX_STACK_BUFLEN, format, arg2); + va_end(arg2); + if (maxlen < MAX_STACK_BUFLEN) { + wr = s->write(s, buf, maxlen, false); + if (wr < 0) { + ustream_write_error(s); + return wr; + } + if (wr == maxlen) + return wr; + + buf += wr; + maxlen -= wr; + return ustream_write_buffered(s, buf, maxlen, wr); + } else { + buf = malloc(maxlen + 1); + if (!buf) + return 0; + wr = vsnprintf(buf, maxlen + 1, format, arg); + wr = ustream_write(s, buf, wr, false); + free(buf); + return wr; + } + } + + if (!ustream_prepare_buf(s, l, 1)) + return 0; + + buf = l->data_tail->tail; + buflen = l->data_tail->end - buf; + + va_copy(arg2, arg); + maxlen = vsnprintf(buf, buflen, format, arg2); + va_end(arg2); + + wr = maxlen; + if (wr >= buflen) + wr = buflen - 1; + + l->data_tail->tail += wr; + l->data_bytes += wr; + if (maxlen < buflen) + return wr; + + buf = malloc(maxlen + 1); + if (!buf) + return wr; + maxlen = vsnprintf(buf, maxlen + 1, format, arg); + wr = ustream_write_buffered(s, buf + wr, maxlen - wr, wr); + free(buf); + + return wr; +} + +int ustream_printf(struct ustream *s, const char *format, ...) +{ + va_list arg; + int ret; + + if (s->write_error) + return 0; + + va_start(arg, format); + ret = ustream_vprintf(s, format, arg); + va_end(arg); + + return ret; +} diff --git a/src/3P/libubox/ustream.h b/src/3P/libubox/ustream.h new file mode 100644 index 00000000..53e08280 --- /dev/null +++ b/src/3P/libubox/ustream.h @@ -0,0 +1,219 @@ +/* + * ustream - library for stream buffer management + * + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __USTREAM_H +#define __USTREAM_H + +#include +#include "uloop.h" + +struct ustream; +struct ustream_buf; + +enum read_blocked_reason { + READ_BLOCKED_USER = (1 << 0), + READ_BLOCKED_FULL = (1 << 1), +}; + +struct ustream_buf_list { + struct ustream_buf *head; + struct ustream_buf *data_tail; + struct ustream_buf *tail; + + int (*alloc)(struct ustream *s, struct ustream_buf_list *l); + + int data_bytes; + + int min_buffers; + int max_buffers; + int buffer_len; + + int buffers; +}; + +struct ustream { + struct ustream_buf_list r, w; + struct uloop_timeout state_change; + struct ustream *next; + + /* + * notify_read: (optional) + * called by the ustream core to notify that new data is available + * for reading. + * must not free the ustream from this callback + */ + void (*notify_read)(struct ustream *s, int bytes_new); + + /* + * notify_write: (optional) + * called by the ustream core to notify that some buffered data has + * been written to the stream. + * must not free the ustream from this callback + */ + void (*notify_write)(struct ustream *s, int bytes); + + /* + * notify_state: (optional) + * called by the ustream implementation to notify that the read + * side of the stream is closed (eof is set) or there was a write + * error (write_error is set). + * will be called again after the write buffer has been emptied when + * the read side has hit EOF. + */ + void (*notify_state)(struct ustream *s); + + /* + * write: + * must be defined by ustream implementation, accepts new write data. + * 'more' is used to indicate that a subsequent call will provide more + * data (useful for aggregating writes) + * returns the number of bytes accepted, or -1 if no more writes can + * be accepted (link error) + */ + int (*write)(struct ustream *s, const char *buf, int len, bool more); + + /* + * free: (optional) + * defined by ustream implementation, tears down the ustream and frees data + */ + void (*free)(struct ustream *s); + + /* + * set_read_blocked: (optional) + * defined by ustream implementation, called when the read_blocked flag + * changes + */ + void (*set_read_blocked)(struct ustream *s); + + /* + * poll: (optional) + * defined by the upstream implementation, called to request polling for + * available data. + * returns true if data was fetched. + */ + bool (*poll)(struct ustream *s); + + /* + * ustream user should set this if the input stream is expected + * to contain string data. the core will keep all data 0-terminated. + */ + bool string_data; + bool write_error; + bool eof, eof_write_done; + + enum read_blocked_reason read_blocked; +}; + +struct ustream_fd { + struct ustream stream; + struct uloop_fd fd; +}; + +struct ustream_buf { + struct ustream_buf *next; + + char *data; + char *tail; + char *end; + + char head[]; +}; + +/* ustream_fd_init: create a file descriptor ustream (uses uloop) */ +void ustream_fd_init(struct ustream_fd *s, int fd); + +/* ustream_free: free all buffers and data associated with a ustream */ +void ustream_free(struct ustream *s); + +/* ustream_consume: remove data from the head of the read buffer */ +void ustream_consume(struct ustream *s, int len); + +/* + * ustream_read: read and consume data in read buffer into caller-specified + * area. Return length of data read. + */ +int ustream_read(struct ustream *s, char *buf, int buflen); +/* ustream_write: add data to the write buffer */ +int ustream_write(struct ustream *s, const char *buf, int len, bool more); +int ustream_printf(struct ustream *s, const char *format, ...); +int ustream_vprintf(struct ustream *s, const char *format, va_list arg); + +/* ustream_get_read_buf: get a pointer to the next read buffer data */ +char *ustream_get_read_buf(struct ustream *s, int *buflen); + +/* + * ustream_set_read_blocked: set read blocked state + * + * if set, the ustream will no longer fetch pending data. + */ +void ustream_set_read_blocked(struct ustream *s, bool set); + +static inline bool ustream_read_blocked(struct ustream *s) +{ + return !!(s->read_blocked & READ_BLOCKED_USER); +} + +static inline int ustream_pending_data(struct ustream *s, bool write) +{ + struct ustream_buf_list *b = write ? &s->w : &s->r; + return b->data_bytes; +} + +static inline bool ustream_read_buf_full(struct ustream *s) +{ + struct ustream_buf *buf = s->r.data_tail; + return buf && buf->data == buf->head && buf->tail == buf->end && + s->r.buffers == s->r.max_buffers; +} + +/*** --- functions only used by ustream implementations --- ***/ + +/* ustream_init_defaults: fill default callbacks and options */ +void ustream_init_defaults(struct ustream *s); + +/* + * ustream_reserve: allocate rx buffer space + * + * len: hint for how much space is needed (not guaranteed to be met) + * maxlen: pointer to where the actual buffer size is going to be stored + */ +char *ustream_reserve(struct ustream *s, int len, int *maxlen); + +/* ustream_fill_read: mark rx buffer space as filled */ +void ustream_fill_read(struct ustream *s, int len); + +/* + * ustream_write_pending: attempt to write more data from write buffers + * returns true if all write buffers have been emptied. + */ +bool ustream_write_pending(struct ustream *s); + +static inline void ustream_state_change(struct ustream *s) +{ + uloop_timeout_set(&s->state_change, 0); +} + +static inline bool ustream_poll(struct ustream *s) +{ + if (!s->poll) + return false; + + return s->poll(s); +} + +#endif diff --git a/src/3P/libubox/utils.c b/src/3P/libubox/utils.c new file mode 100644 index 00000000..91dd71e6 --- /dev/null +++ b/src/3P/libubox/utils.c @@ -0,0 +1,105 @@ +/* + * utils - misc libubox utility functions + * + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "utils.h" +#include +#include +#include + +#define foreach_arg(_arg, _addr, _len, _first_addr, _first_len) \ + for (_addr = (_first_addr), _len = (_first_len); \ + _addr; \ + _addr = va_arg(_arg, void **), _len = _addr ? va_arg(_arg, size_t) : 0) + +void *__calloc_a(size_t len, ...) +{ + va_list ap, ap1; + void *ret; + void **cur_addr; + size_t cur_len; + int alloc_len = 0; + char *ptr; + + va_start(ap, len); + + va_copy(ap1, ap); + foreach_arg(ap1, cur_addr, cur_len, &ret, len) + alloc_len += cur_len; + va_end(ap1); + + ptr = calloc(1, alloc_len); + if (!ptr) + return NULL; + alloc_len = 0; + foreach_arg(ap, cur_addr, cur_len, &ret, len) { + *cur_addr = &ptr[alloc_len]; + alloc_len += cur_len; + } + va_end(ap); + + return ret; +} + +#ifdef __APPLE__ +#include /* host_get_clock_service() */ +#include /* mach_port_deallocate() */ +#include /* mach_host_self(), mach_task_self() */ +#include /* clock_get_time() */ + +static clock_serv_t clock_realtime; +static clock_serv_t clock_monotonic; + +static void __constructor clock_name_init(void) +{ + mach_port_t host_self = mach_host_self(); + + host_get_clock_service(host_self, CLOCK_REALTIME, &clock_realtime); + host_get_clock_service(host_self, CLOCK_MONOTONIC, &clock_monotonic); +} + +static void __destructor clock_name_dealloc(void) +{ + mach_port_t self = mach_task_self(); + + mach_port_deallocate(self, clock_realtime); + mach_port_deallocate(self, clock_monotonic); +} + +int clock_gettime(int type, struct timespec *tv) +{ + int retval = -1; + mach_timespec_t mts; + + switch (type) { + case CLOCK_REALTIME: + retval = clock_get_time(clock_realtime, &mts); + break; + case CLOCK_MONOTONIC: + retval = clock_get_time(clock_monotonic, &mts); + break; + default: + goto out; + } + + tv->tv_sec = mts.tv_sec; + tv->tv_nsec = mts.tv_nsec; +out: + return retval; +} + +#endif diff --git a/src/3P/libubox/utils.h b/src/3P/libubox/utils.h new file mode 100644 index 00000000..d00e76b2 --- /dev/null +++ b/src/3P/libubox/utils.h @@ -0,0 +1,195 @@ +/* + * utils - misc libubox utility functions + * + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __LIBUBOX_UTILS_H +#define __LIBUBOX_UTILS_H + +#include +#include +#include +#include +#include + +/* + * calloc_a(size_t len, [void **addr, size_t len,...], NULL) + * + * allocate a block of memory big enough to hold multiple aligned objects. + * the pointer to the full object (starting with the first chunk) is returned, + * all other pointers are stored in the locations behind extra addr arguments. + * the last argument needs to be a NULL pointer + */ + +#define calloc_a(len, ...) __calloc_a(len, ##__VA_ARGS__, NULL) + +void *__calloc_a(size_t len, ...); + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +#define __BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) + +#ifdef __OPTIMIZE__ +extern int __BUILD_BUG_ON_CONDITION_FAILED; +#define BUILD_BUG_ON(condition) \ + do { \ + __BUILD_BUG_ON(condition); \ + if (condition) \ + __BUILD_BUG_ON_CONDITION_FAILED = 1; \ + } while(0) +#else +#define BUILD_BUG_ON __BUILD_BUG_ON +#endif + +#ifdef __APPLE__ + +#include +#define CLOCK_REALTIME CALENDAR_CLOCK +#define CLOCK_MONOTONIC SYSTEM_CLOCK + +int clock_gettime(int type, struct timespec *tv); + +#endif + +#ifdef __GNUC__ +#define _GNUC_MIN_VER(maj, min) (((__GNUC__ << 8) + __GNUC_MINOR__) >= (((maj) << 8) + (min))) +#else +#define _GNUC_MIN_VER(maj, min) 0 +#endif + +#if defined(__linux__) || defined(__CYGWIN__) +#include +#include + +#elif defined(__APPLE__) +#include +#include +#define bswap_32(x) OSSwapInt32(x) +#define bswap_64(x) OSSwapInt64(x) +#elif defined(__FreeBSD__) +#include +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) +#else +#include +#define bswap_32(x) swap32(x) +#define bswap_64(x) swap64(x) +#endif + +#ifndef __BYTE_ORDER +#define __BYTE_ORDER BYTE_ORDER +#endif +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN +#endif +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#endif + +static inline uint16_t __u_bswap16(uint16_t val) +{ + return ((val >> 8) & 0xffu) | ((val & 0xffu) << 8); +} + +#if _GNUC_MIN_VER(4, 2) +#define __u_bswap32(x) __builtin_bswap32(x) +#define __u_bswap64(x) __builtin_bswap64(x) +#else +#define __u_bswap32(x) bswap_32(x) +#define __u_bswap64(x) bswap_64(x) +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +#define cpu_to_be64(x) __u_bswap64(x) +#define cpu_to_be32(x) __u_bswap32(x) +#define cpu_to_be16(x) __u_bswap16((uint16_t) (x)) + +#define be64_to_cpu(x) __u_bswap64(x) +#define be32_to_cpu(x) __u_bswap32(x) +#define be16_to_cpu(x) __u_bswap16((uint16_t) (x)) + +#define cpu_to_le64(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le16(x) (x) + +#define le64_to_cpu(x) (x) +#define le32_to_cpu(x) (x) +#define le16_to_cpu(x) (x) + +#else /* __BYTE_ORDER == __LITTLE_ENDIAN */ + +#define cpu_to_le64(x) __u_bswap64(x) +#define cpu_to_le32(x) __u_bswap32(x) +#define cpu_to_le16(x) __u_bswap16((uint16_t) (x)) + +#define le64_to_cpu(x) __u_bswap64(x) +#define le32_to_cpu(x) __u_bswap32(x) +#define le16_to_cpu(x) __u_bswap16((uint16_t) (x)) + +#define cpu_to_be64(x) (x) +#define cpu_to_be32(x) (x) +#define cpu_to_be16(x) (x) + +#define be64_to_cpu(x) (x) +#define be32_to_cpu(x) (x) +#define be16_to_cpu(x) (x) + +#endif + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +#ifndef __constructor +#define __constructor __attribute__((constructor)) +#endif + +#ifndef __destructor +#define __destructor __attribute__((destructor)) +#endif + +#ifndef __hidden +#define __hidden __attribute__((visibility("hidden"))) +#endif + +#ifndef BITS_PER_LONG +#define BITS_PER_LONG (8 * sizeof(unsigned long)) +#endif + +#define BITFIELD_SIZE(_n) (((_n) + (BITS_PER_LONG - 1)) / BITS_PER_LONG) + +static inline void bitfield_set(unsigned long *bits, int bit) +{ + bits[bit / BITS_PER_LONG] |= (1UL << (bit % BITS_PER_LONG)); +} + +static inline bool bitfield_test(unsigned long *bits, int bit) +{ + return !!(bits[bit / BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))); +} + +int b64_encode(const void *src, size_t src_len, + void *dest, size_t dest_len); + +int b64_decode(const void *src, void *dest, size_t dest_len); + +#define B64_ENCODE_LEN(_len) ((((_len) + 2) / 3) * 4 + 1) +#define B64_DECODE_LEN(_len) (((_len) / 4) * 3 + 1) + +#endif diff --git a/src/3P/libubox/vlist.c b/src/3P/libubox/vlist.c new file mode 100644 index 00000000..d497c632 --- /dev/null +++ b/src/3P/libubox/vlist.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include "vlist.h" + +void +vlist_init(struct vlist_tree *tree, avl_tree_comp cmp, vlist_update_cb update) +{ + tree->update = update; + tree->version = 1; + + avl_init(&tree->avl, cmp, 0, tree); +} + +void +vlist_delete(struct vlist_tree *tree, struct vlist_node *node) +{ + if (!tree->no_delete) + avl_delete(&tree->avl, &node->avl); + tree->update(tree, NULL, node); +} + +void +vlist_add(struct vlist_tree *tree, struct vlist_node *node, const void *key) +{ + struct vlist_node *old_node = NULL; + struct avl_node *anode; + + node->avl.key = key; + node->version = tree->version; + + anode = avl_find(&tree->avl, key); + if (anode) { + old_node = container_of(anode, struct vlist_node, avl); + if (tree->keep_old || tree->no_delete) { + old_node->version = tree->version; + goto update_only; + } + + avl_delete(&tree->avl, anode); + } + + avl_insert(&tree->avl, &node->avl); + +update_only: + tree->update(tree, node, old_node); +} + +void +vlist_flush(struct vlist_tree *tree) +{ + struct vlist_node *node, *tmp; + + avl_for_each_element_safe(&tree->avl, node, avl, tmp) { + if ((node->version == tree->version || node->version == -1) && + tree->version != -1) + continue; + + vlist_delete(tree, node); + } +} + +void +vlist_flush_all(struct vlist_tree *tree) +{ + tree->version = -1; + vlist_flush(tree); +} + + diff --git a/src/3P/libubox/vlist.h b/src/3P/libubox/vlist.h new file mode 100644 index 00000000..8170abf7 --- /dev/null +++ b/src/3P/libubox/vlist.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __LIBUBOX_VLIST_H +#define __LIBUBOX_VLIST_H + +#include "avl.h" + +struct vlist_tree; +struct vlist_node; + +typedef void (*vlist_update_cb)(struct vlist_tree *tree, + struct vlist_node *node_new, + struct vlist_node *node_old); + +struct vlist_tree { + struct avl_tree avl; + + vlist_update_cb update; + bool keep_old; + bool no_delete; + + int version; +}; + +struct vlist_node { + struct avl_node avl; + int version; +}; + +#define VLIST_TREE_INIT(_name, _comp, _update, _keep_old, _no_delete) \ + { \ + .avl = AVL_TREE_INIT(_name.avl, _comp, false, NULL), \ + .update = _update, \ + .version = 1, \ + .keep_old = _keep_old, \ + .no_delete = _no_delete, \ + } + +#define VLIST_TREE(_name, ...) \ + struct vlist_tree _name = \ + VLIST_TREE_INIT(_name, __VA_ARGS__) + +void vlist_init(struct vlist_tree *tree, avl_tree_comp cmp, vlist_update_cb update); + +#define vlist_find(tree, name, element, node_member) \ + avl_find_element(&(tree)->avl, name, element, node_member.avl) + +static inline void vlist_update(struct vlist_tree *tree) +{ + tree->version++; +} + +void vlist_add(struct vlist_tree *tree, struct vlist_node *node, const void *key); +void vlist_delete(struct vlist_tree *tree, struct vlist_node *node); +void vlist_flush(struct vlist_tree *tree); +void vlist_flush_all(struct vlist_tree *tree); + +#define vlist_for_each_element(tree, element, node_member) \ + avl_for_each_element(&(tree)->avl, element, node_member.avl) + +#endif