Bump libubox-2016-07-29-290c64ef5b5c3e75be851594f269d6a9568e64e5
This commit is contained in:
10
src/3P/libubox/.gitignore
vendored
Normal file
10
src/3P/libubox/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
Makefile
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
*.cmake
|
||||
*.a
|
||||
*.so
|
||||
*.dylib
|
||||
install_manifest.txt
|
||||
jshn
|
||||
*-example
|
||||
71
src/3P/libubox/CMakeLists.txt
Normal file
71
src/3P/libubox/CMakeLists.txt
Normal file
@@ -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()
|
||||
37
src/3P/libubox/avl-cmp.c
Normal file
37
src/3P/libubox/avl-cmp.c
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <string.h>
|
||||
#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);
|
||||
}
|
||||
22
src/3P/libubox/avl-cmp.h
Normal file
22
src/3P/libubox/avl-cmp.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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
|
||||
735
src/3P/libubox/avl.c
Normal file
735
src/3P/libubox/avl.c
Normal file
@@ -0,0 +1,735 @@
|
||||
/*
|
||||
* PacketBB handler library (see RFC 5444)
|
||||
* Copyright (c) 2010 Henning Rogge <hrogge@googlemail.com>
|
||||
* Original OLSRd implementation by Hannes Gredler <hannes@gredler.at>
|
||||
* 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 <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 key<node.key */
|
||||
while (diff < 0) {
|
||||
if (list_is_first(&node->list, &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:
|
||||
*/
|
||||
560
src/3P/libubox/avl.h
Normal file
560
src/3P/libubox/avl.h
Normal file
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* PacketBB handler library (see RFC 5444)
|
||||
* Copyright (c) 2010 Henning Rogge <hrogge@googlemail.com>
|
||||
* Original OLSRd implementation by Hannes Gredler <hannes@gredler.at>
|
||||
* 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 <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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 k1<k2, 0 if k1==k2
|
||||
*/
|
||||
typedef int (*avl_tree_comp) (const void *k1, const void *k2, void *ptr);
|
||||
|
||||
/**
|
||||
* This struct is the central management part of an avl tree.
|
||||
* One of them is necessary for each avl_tree.
|
||||
*/
|
||||
struct avl_tree {
|
||||
/**
|
||||
* Head of linked list node for supporting easy iteration
|
||||
* and multiple elments with the same key.
|
||||
*/
|
||||
struct list_head list_head;
|
||||
|
||||
/**
|
||||
* pointer to the root node of the avl tree, NULL if tree is empty
|
||||
*/
|
||||
struct avl_node *root;
|
||||
|
||||
/**
|
||||
* number of nodes in the avl tree
|
||||
*/
|
||||
unsigned int count;
|
||||
|
||||
/**
|
||||
* true if multiple nodes with the same key are
|
||||
* allowed in the tree, false otherwise
|
||||
*/
|
||||
bool allow_dups;
|
||||
|
||||
/**
|
||||
* pointer to the tree comparator
|
||||
*
|
||||
* First two parameters are keys to compare,
|
||||
* third parameter is a copy of cmp_ptr
|
||||
*/
|
||||
avl_tree_comp comp;
|
||||
|
||||
/**
|
||||
* custom pointer delivered to the tree comparator
|
||||
*/
|
||||
void *cmp_ptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* internal enum for avl_find_... macros
|
||||
*/
|
||||
enum avl_find_mode {
|
||||
AVL_FIND_EQUAL,
|
||||
AVL_FIND_LESSEQUAL,
|
||||
AVL_FIND_GREATEREQUAL
|
||||
};
|
||||
|
||||
#define AVL_TREE_INIT(_name, _comp, _allow_dups, _cmp_ptr) \
|
||||
{ \
|
||||
.list_head = LIST_HEAD_INIT(_name.list_head), \
|
||||
.comp = _comp, \
|
||||
.allow_dups = _allow_dups, \
|
||||
.cmp_ptr = _cmp_ptr \
|
||||
}
|
||||
|
||||
#define AVL_TREE(_name, _comp, _allow_dups, _cmp_ptr) \
|
||||
struct avl_tree _name = \
|
||||
AVL_TREE_INIT(_name, _comp, _allow_dups, _cmp_ptr)
|
||||
|
||||
void EXPORT(avl_init)(struct avl_tree *, avl_tree_comp, bool, void *);
|
||||
struct avl_node *EXPORT(avl_find)(const struct avl_tree *, const void *);
|
||||
struct avl_node *EXPORT(avl_find_greaterequal)(const struct avl_tree *tree, const void *key);
|
||||
struct avl_node *EXPORT(avl_find_lessequal)(const struct avl_tree *tree, const void *key);
|
||||
int EXPORT(avl_insert)(struct avl_tree *, struct avl_node *);
|
||||
void EXPORT(avl_delete)(struct avl_tree *, struct avl_node *);
|
||||
|
||||
/**
|
||||
* @param tree pointer to avl-tree
|
||||
* @param node pointer to node of the tree
|
||||
* @return true if node is the first one of the tree, false otherwise
|
||||
*/
|
||||
static inline bool
|
||||
avl_is_first(struct avl_tree *tree, struct avl_node *node) {
|
||||
return tree->list_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:
|
||||
*/
|
||||
327
src/3P/libubox/base64.c
Normal file
327
src/3P/libubox/base64.c
Normal file
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
* base64 - libubox base64 functions
|
||||
*
|
||||
* Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <sys/types.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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);
|
||||
}
|
||||
287
src/3P/libubox/blob.c
Normal file
287
src/3P/libubox/blob.c
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* blob - library for generating/parsing tagged binary data
|
||||
*
|
||||
* Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
257
src/3P/libubox/blob.h
Normal file
257
src/3P/libubox/blob.h
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* blob - library for generating/parsing tagged binary data
|
||||
*
|
||||
* Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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
|
||||
329
src/3P/libubox/blobmsg.c
Normal file
329
src/3P/libubox/blobmsg.c
Normal file
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
241
src/3P/libubox/blobmsg.h
Normal file
241
src/3P/libubox/blobmsg.h
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <stdarg.h>
|
||||
#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
|
||||
370
src/3P/libubox/blobmsg_json.c
Normal file
370
src/3P/libubox/blobmsg_json.c
Normal file
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <inttypes.h>
|
||||
#include "blobmsg.h"
|
||||
#include "blobmsg_json.h"
|
||||
|
||||
#ifdef JSONC
|
||||
#include <json-c/json.h>
|
||||
#else
|
||||
#include <json/json.h>
|
||||
#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;
|
||||
}
|
||||
59
src/3P/libubox/blobmsg_json.h
Normal file
59
src/3P/libubox/blobmsg_json.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#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
|
||||
67
src/3P/libubox/builders/cmake/CMakeLists.txt
Normal file
67
src/3P/libubox/builders/cmake/CMakeLists.txt
Normal file
@@ -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)
|
||||
23
src/3P/libubox/examples/CMakeLists.txt
Normal file
23
src/3P/libubox/examples/CMakeLists.txt
Normal file
@@ -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()
|
||||
140
src/3P/libubox/examples/blobmsg-example.c
Normal file
140
src/3P/libubox/examples/blobmsg-example.c
Normal file
@@ -0,0 +1,140 @@
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
84
src/3P/libubox/examples/json_script-example.c
Normal file
84
src/3P/libubox/examples/json_script-example.c
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <json.h>
|
||||
#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] <filename_json_script>\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;
|
||||
}
|
||||
38
src/3P/libubox/examples/json_script-example.json
Normal file
38
src/3P/libubox/examples/json_script-example.json
Normal file
@@ -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" ]
|
||||
]
|
||||
287
src/3P/libubox/examples/json_script-tests.sh
Normal file
287
src/3P/libubox/examples/json_script-tests.sh
Normal file
@@ -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 <<EOF
|
||||
|--- expecting
|
||||
$expected<
|
||||
|--- actual
|
||||
$(cat $file_stdio)<
|
||||
|--- END
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
assertStdoutEquals() {
|
||||
assertStdioEquals "$1" "$FILE_STDOUT"
|
||||
}
|
||||
|
||||
assertStderrEquals() {
|
||||
assertStdioEquals "$1" "$FILE_STDERR"
|
||||
}
|
||||
|
||||
test_bad_json() {
|
||||
cat >"$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
|
||||
112
src/3P/libubox/examples/runqueue-example.c
Normal file
112
src/3P/libubox/examples/runqueue-example.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* runqueue-example.c
|
||||
*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
1067
src/3P/libubox/examples/shunit2
Normal file
1067
src/3P/libubox/examples/shunit2
Normal file
File diff suppressed because it is too large
Load Diff
78
src/3P/libubox/examples/uloop-example.lua
Executable file
78
src/3P/libubox/examples/uloop-example.lua
Executable file
@@ -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()
|
||||
|
||||
11
src/3P/libubox/examples/uloop_pid_test.sh
Executable file
11
src/3P/libubox/examples/uloop_pid_test.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo $0 $*
|
||||
echo Environment:
|
||||
env
|
||||
|
||||
sleep 2
|
||||
|
||||
echo "stopping child"
|
||||
|
||||
exit 5
|
||||
148
src/3P/libubox/examples/ustream-example.c
Normal file
148
src/3P/libubox/examples/ustream-example.c
Normal file
@@ -0,0 +1,148 @@
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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 <port>\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();
|
||||
}
|
||||
378
src/3P/libubox/jshn.c
Normal file
378
src/3P/libubox/jshn.c
Normal file
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2013 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <json-c/json.h>
|
||||
#else
|
||||
#include <json/json.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#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 <message>|-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]);
|
||||
}
|
||||
698
src/3P/libubox/json_script.c
Normal file
698
src/3P/libubox/json_script.c
Normal file
@@ -0,0 +1,698 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <sys/stat.h>
|
||||
#include <regex.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
136
src/3P/libubox/json_script.h
Normal file
136
src/3P/libubox/json_script.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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
|
||||
101
src/3P/libubox/kvlist.c
Normal file
101
src/3P/libubox/kvlist.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* kvlist - simple key/value store
|
||||
*
|
||||
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
54
src/3P/libubox/kvlist.h
Normal file
54
src/3P/libubox/kvlist.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* kvlist - simple key/value store
|
||||
*
|
||||
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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
|
||||
208
src/3P/libubox/list.h
Normal file
208
src/3P/libubox/list.h
Normal file
@@ -0,0 +1,208 @@
|
||||
/*-
|
||||
* Copyright (c) 2011 Felix Fietkau <nbd@openwrt.org>
|
||||
* 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 <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#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_ */
|
||||
336
src/3P/libubox/md5.c
Normal file
336
src/3P/libubox/md5.c
Normal file
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <solar at openwall.com>
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
58
src/3P/libubox/md5.h
Normal file
58
src/3P/libubox/md5.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <solar at openwall.com>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
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
|
||||
282
src/3P/libubox/runqueue.c
Normal file
282
src/3P/libubox/runqueue.c
Normal file
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* runqueue.c - a simple task queueing/completion tracking helper
|
||||
*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <stdio.h>
|
||||
#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);
|
||||
}
|
||||
124
src/3P/libubox/runqueue.h
Normal file
124
src/3P/libubox/runqueue.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* runqueue.c - a simple task queueing/completion tracking helper
|
||||
*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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
|
||||
121
src/3P/libubox/safe_list.c
Normal file
121
src/3P/libubox/safe_list.c
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* safe_list - linked list protected against recursive iteration with deletes
|
||||
*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
62
src/3P/libubox/safe_list.h
Normal file
62
src/3P/libubox/safe_list.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* safe_list - linked list protected against recursive iteration with deletes
|
||||
*
|
||||
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <stdbool.h>
|
||||
#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
|
||||
280
src/3P/libubox/sh/jshn.sh
Normal file
280
src/3P/libubox/sh/jshn.sh
Normal file
@@ -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" ]
|
||||
}
|
||||
173
src/3P/libubox/ulog.c
Normal file
173
src/3P/libubox/ulog.c
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* ulog - simple logging functions
|
||||
*
|
||||
* Copyright (C) 2015 Jo-Philipp Wich <jow@openwrt.org>
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
42
src/3P/libubox/ulog.h
Normal file
42
src/3P/libubox/ulog.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* ulog - simple logging functions
|
||||
*
|
||||
* Copyright (C) 2015 Jo-Philipp Wich <jow@openwrt.org>
|
||||
*
|
||||
* 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 <syslog.h>
|
||||
|
||||
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
|
||||
108
src/3P/libubox/uloop-epoll.c
Normal file
108
src/3P/libubox/uloop-epoll.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* uloop - event loop implementation
|
||||
*
|
||||
* Copyright (C) 2010-2016 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
148
src/3P/libubox/uloop-kqueue.c
Normal file
148
src/3P/libubox/uloop-kqueue.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* uloop - event loop implementation
|
||||
*
|
||||
* Copyright (C) 2010-2016 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
567
src/3P/libubox/uloop.c
Normal file
567
src/3P/libubox/uloop.c
Normal file
@@ -0,0 +1,567 @@
|
||||
/*
|
||||
* uloop - event loop implementation
|
||||
*
|
||||
* Copyright (C) 2010-2016 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "uloop.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifdef USE_KQUEUE
|
||||
#include <sys/event.h>
|
||||
#endif
|
||||
#ifdef USE_EPOLL
|
||||
#include <sys/epoll.h>
|
||||
#endif
|
||||
#include <sys/wait.h>
|
||||
|
||||
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();
|
||||
}
|
||||
109
src/3P/libubox/uloop.h
Normal file
109
src/3P/libubox/uloop.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* uloop - event loop implementation
|
||||
*
|
||||
* Copyright (C) 2010-2013 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <signal.h>
|
||||
|
||||
#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
|
||||
293
src/3P/libubox/usock.c
Normal file
293
src/3P/libubox/usock.c
Normal file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* usock - socket helper functions
|
||||
*
|
||||
* Copyright (C) 2010 Steven Barth <steven@midlink.org>
|
||||
* Copyright (C) 2011-2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netdb.h>
|
||||
#include <poll.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <poll.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
54
src/3P/libubox/usock.h
Normal file
54
src/3P/libubox/usock.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* usock - socket helper functions
|
||||
*
|
||||
* Copyright (C) 2010 Steven Barth <steven@midlink.org>
|
||||
* Copyright (C) 2011-2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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_ */
|
||||
173
src/3P/libubox/ustream-fd.c
Normal file
173
src/3P/libubox/ustream-fd.c
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* ustream - library for stream buffer management
|
||||
*
|
||||
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#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);
|
||||
}
|
||||
547
src/3P/libubox/ustream.c
Normal file
547
src/3P/libubox/ustream.c
Normal file
@@ -0,0 +1,547 @@
|
||||
/*
|
||||
* ustream - library for stream buffer management
|
||||
*
|
||||
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
219
src/3P/libubox/ustream.h
Normal file
219
src/3P/libubox/ustream.h
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* ustream - library for stream buffer management
|
||||
*
|
||||
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <stdarg.h>
|
||||
#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
|
||||
105
src/3P/libubox/utils.c
Normal file
105
src/3P/libubox/utils.c
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* utils - misc libubox utility functions
|
||||
*
|
||||
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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 <mach/mach_host.h> /* host_get_clock_service() */
|
||||
#include <mach/mach_port.h> /* mach_port_deallocate() */
|
||||
#include <mach/mach_init.h> /* mach_host_self(), mach_task_self() */
|
||||
#include <mach/clock.h> /* 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
|
||||
195
src/3P/libubox/utils.h
Normal file
195
src/3P/libubox/utils.h
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* utils - misc libubox utility functions
|
||||
*
|
||||
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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 <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
* 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 <mach/clock_types.h>
|
||||
#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 <byteswap.h>
|
||||
#include <endian.h>
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#include <machine/endian.h>
|
||||
#include <machine/byte_order.h>
|
||||
#define bswap_32(x) OSSwapInt32(x)
|
||||
#define bswap_64(x) OSSwapInt64(x)
|
||||
#elif defined(__FreeBSD__)
|
||||
#include <sys/endian.h>
|
||||
#define bswap_32(x) bswap32(x)
|
||||
#define bswap_64(x) bswap64(x)
|
||||
#else
|
||||
#include <machine/endian.h>
|
||||
#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
|
||||
82
src/3P/libubox/vlist.c
Normal file
82
src/3P/libubox/vlist.c
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
75
src/3P/libubox/vlist.h
Normal file
75
src/3P/libubox/vlist.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
|
||||
*
|
||||
* 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
|
||||
Reference in New Issue
Block a user