Bump libubox-2016-07-29-290c64ef5b5c3e75be851594f269d6a9568e64e5

This commit is contained in:
jbnadal
2016-12-06 17:43:52 +01:00
parent 2b5e82bd8c
commit c59ca12c5b
52 changed files with 11088 additions and 0 deletions

10
src/3P/libubox/.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
Makefile
CMakeCache.txt
CMakeFiles
*.cmake
*.a
*.so
*.dylib
install_manifest.txt
jshn
*-example

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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;
}

View 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

View 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)

View 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()

View 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;
}

View 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;
}

View 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" ]
]

View 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

View 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;
}

File diff suppressed because it is too large Load Diff

View 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()

View File

@@ -0,0 +1,11 @@
#!/bin/sh
echo $0 $*
echo Environment:
env
sleep 2
echo "stopping child"
exit 5

View 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
View 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]);
}

View 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(&reg, pattern, REG_EXTENDED | REG_NOSUB))
return 0;
ret = !regexec(&reg, str, 0, NULL, 0);
regfree(&reg);
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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

View 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
View 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
View 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
View 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

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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