diff --git a/3P/CMakeLists.txt b/3P/CMakeLists.txt new file mode 100644 index 00000000..d8e740f8 --- /dev/null +++ b/3P/CMakeLists.txt @@ -0,0 +1,14 @@ +# CMakeLists files in this project can +# refer to the root source directory of the project as ${HELLO_SOURCE_DIR} and +# to the root binary directory of the project as ${HELLO_BINARY_DIR}. +cmake_minimum_required (VERSION 2.8.11) + +#add_subdirectory (wolfssl/builders/cmake) +add_subdirectory (json/builders/cmake) +#add_subdirectory (jsoncpp/builders/cmake) +add_subdirectory (libubox/builders/cmake) +add_subdirectory (ubus/libubus/builders/cmake) +add_subdirectory (ubus/ubus/builders/cmake) +add_subdirectory (ubus/ubusd/builders/cmake) +#add_subdirectory (civetweb/builders/cmake) +#add_subdirectory (wiringPi/) diff --git a/3P/json/.gitignore b/3P/json/.gitignore new file mode 100644 index 00000000..f3b18f12 --- /dev/null +++ b/3P/json/.gitignore @@ -0,0 +1,49 @@ +*~ +*.swp +/INSTALL +.deps/ +.libs/ +/aclocal.m4 +/autom4te.cache +/config.guess +/json_config.h +/config.h +/config.log +/config.status +/config.sub +/configure +/depcomp +/doc +/install-sh +/json.pc +/json-c.pc +/json-c-uninstalled.pc +/libtool +/ltmain.sh +/Makefile +/Makefile.in +/missing +/stamp-h1 +/stamp-h2 +/tests/Makefile +/tests/Makefile.in +/tests/test1 +/tests/test1Formatted +/tests/test2 +/tests/test2Formatted +/tests/test4 +/tests/testReplaceExisting +/tests/testSubDir +/tests/test_parse_int64 +/tests/test_parse +/tests/test_cast +/tests/test_null +/tests/test_printbuf +/tests/test_set_serializer +/tests/*.vg.out +/Debug +/Release +*.lo +*.o +/libjson-c.la +/libjson.la diff --git a/3P/json/0.11 b/3P/json/0.11 new file mode 100644 index 00000000..e69de29b diff --git a/3P/json/AUTHORS b/3P/json/AUTHORS new file mode 100644 index 00000000..b389989c --- /dev/null +++ b/3P/json/AUTHORS @@ -0,0 +1,5 @@ +Michael Clark +Jehiah Czebotar +Eric Haszlakiewicz +C. Watford (christopher.watford@gmail.com) + diff --git a/3P/json/Android.configure.mk b/3P/json/Android.configure.mk new file mode 100644 index 00000000..a6265ada --- /dev/null +++ b/3P/json/Android.configure.mk @@ -0,0 +1,39 @@ +# This file is the top android makefile for all sub-modules. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +json_c_TOP := $(LOCAL_PATH) + +JSON_C_BUILT_SOURCES := Android.mk + +JSON_C_BUILT_SOURCES := $(patsubst %, $(abspath $(json_c_TOP))/%, $(JSON_C_BUILT_SOURCES)) + +.PHONY: json-c-configure json-c-configure-real +json-c-configure-real: + echo $(JSON_C_BUILT_SOURCES) + cd $(json_c_TOP) ; \ + $(abspath $(json_c_TOP))/autogen.sh && \ + CC="$(CONFIGURE_CC)" \ + CFLAGS="$(CONFIGURE_CFLAGS)" \ + LD=$(TARGET_LD) \ + LDFLAGS="$(CONFIGURE_LDFLAGS)" \ + CPP=$(CONFIGURE_CPP) \ + CPPFLAGS="$(CONFIGURE_CPPFLAGS)" \ + PKG_CONFIG_LIBDIR=$(CONFIGURE_PKG_CONFIG_LIBDIR) \ + PKG_CONFIG_TOP_BUILD_DIR=/ \ + ac_cv_func_malloc_0_nonnull=yes \ + ac_cv_func_realloc_0_nonnull=yes \ + $(abspath $(json_c_TOP))/$(CONFIGURE) --host=$(CONFIGURE_HOST) \ + --prefix=/system \ + && \ + for file in $(JSON_C_BUILT_SOURCES); do \ + rm -f $$file && \ + make -C $$(dirname $$file) $$(basename $$file) ; \ + done + +json-c-configure: json-c-configure-real + +PA_CONFIGURE_TARGETS += json-c-configure + +-include $(json_c_TOP)/Android.mk diff --git a/3P/json/COPYING b/3P/json/COPYING new file mode 100644 index 00000000..740d1258 --- /dev/null +++ b/3P/json/COPYING @@ -0,0 +1,42 @@ + +Copyright (c) 2009-2012 Eric Haszlakiewicz + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +---------------------------------------------------------------- + +Copyright (c) 2004, 2005 Metaparadigm Pte Ltd + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/3P/json/ChangeLog b/3P/json/ChangeLog new file mode 100644 index 00000000..4e74c907 --- /dev/null +++ b/3P/json/ChangeLog @@ -0,0 +1,175 @@ + +0.11 + + * IMPORTANT: the name of the library has changed to libjson-c.so and + the header files are now in include/json-c. + The pkgconfig name has also changed from json to json-c. + You should change your build to use appropriate -I and -l options. + A compatibility shim is in place so builds using the old name will + continue to work, but that will be removed in the next release. + * Maximum recursion depth is now a runtime option. + json_tokener_new() is provided for compatibility. + json_tokener_new_ex(depth) + * Include json_object_iterator.h in the installed headers. + * Add support for building on Android. + * Rewrite json_object_object_add to replace just the value if the key already exists so keys remain valid. + * Make it safe to delete keys while iterating with the json_object_object_foreach macro. + * Add a json_set_serializer() function to allow the string output of a json_object to be customized. + * Make float parsing locale independent. + * Add a json_tokener_set_flags() function and a JSON_TOKENER_STRICT flag. + * Enable -Werror when building. + * speed improvements to parsing 64-bit integers on systems with working sscanf + * Add a json_object_object_length function. + * Fix a bug (buffer overrun) when expanding arrays to more than 64 entries. + +0.10 + + * Add a json_object_to_json_string_ext() function to allow output to be + formatted in a more human readable form. + * Add json_object_object_get_ex(), a NULL-safe get object method, to be able + to distinguish between a key not present and the value being NULL. + * Add an alternative iterator implementation, see json_object_iterator.h + * Make json_object_iter public to enable external use of the + json_object_object_foreachC macro. + * Add a printbuf_memset() function to provide an effecient way to set and + append things like whitespace indentation. + * Adjust json_object_is_type and json_object_get_type so they return + json_type_null for NULL objects and handle NULL passed to + json_objct_object_get(). + * Rename boolean type to json_bool. + * Fix various compile issues for Visual Studio and MinGW. + * Allow json_tokener_parse_ex() to be re-used to parse multiple object. + Also, fix some parsing issues with capitalized hexadecimal numbers and + number in E notation. + * Add json_tokener_get_error() and json_tokener_error_desc() to better + encapsulate the process of retrieving errors while parsing. + * Various improvements to the documentation of many functions. + * Add new json_object_array_sort() function. + * Fix a bug in json_object_get_int(), which would incorrectly return 0 + when called on a string type object. + Eric Haszlakiewicz + * Add a json_type_to_name() function. + Eric Haszlakiewicz + * Add a json_tokener_parse_verbose() function. + Jehiah Czebotar + * Improve support for null bytes within JSON strings. + Jehiah Czebotar + * Fix file descriptor leak if memory allocation fails in json_util + Zachary Blair, zack_blair at hotmail dot com + * Add int64 support. Two new functions json_object_net_int64 and + json_object_get_int64. Binary compatibility preserved. + Eric Haszlakiewicz, EHASZLA at transunion com + Rui Miguel Silva Seabra, rms at 1407 dot org + * Fix subtle bug in linkhash where lookup could hang after all slots + were filled then successively freed. + Spotted by Jean-Marc Naud, j dash m at newtraxtech dot com + * Make json_object_from_file take const char *filename + Spotted by Vikram Raj V, vsagar at attinteractive dot com + * Add handling of surrogate pairs (json_tokener.c, test4.c, Makefile.am) + Brent Miller, bdmiller at yahoo dash inc dot com + * Correction to comment describing printbuf_memappend in printbuf.h + Brent Miller, bdmiller at yahoo dash inc dot com + +0.9 + * Add README.html README-WIN32.html config.h.win32 to Makefile.am + Michael Clark, + * Add const qualifier to the json_tokener_parse functions + Eric Haszlakiewicz, EHASZLA at transunion dot com + * Rename min and max so we can never clash with C or C++ std library + Ian Atha, thatha at yahoo dash inc dot com + * Fix any noticeable spelling or grammar errors. + * Make sure every va_start has a va_end. + * Check all pointers for validity. + Erik Hovland, erik at hovland dot org + * Fix json_object_get_boolean to return false for empty string + Spotted by Vitaly Kruglikov, Vitaly dot Kruglikov at palm dot com + * optimizations to json_tokener_parse_ex(), printbuf_memappend() + Brent Miller, bdmiller at yahoo dash inc dot com + * Disable REFCOUNT_DEBUG by default in json_object.c + * Don't use this as a variable, so we can compile with a C++ compiler + * Add casts from void* to type of assignment when using malloc + * Add #ifdef __cplusplus guards to all of the headers + * Add typedefs for json_object, json_tokener, array_list, printbuf, lh_table + Michael Clark, + * Null pointer dereference fix. Fix json_object_get_boolean strlen test + to not return TRUE for zero length string. Remove redundant includes. + Erik Hovland, erik at hovland dot org + * Fixed warning reported by adding -Wstrict-prototypes + -Wold-style-definition to the compilatin flags. + Dotan Barak, dotanba at gmail dot com + * Add const correctness to public interfaces + Gerard Krol, g dot c dot krol at student dot tudelft dot nl + +0.8 + * Add va_end for every va_start + Dotan Barak, dotanba at gmail dot com + * Add macros to enable compiling out debug code + Geoffrey Young, geoff at modperlcookbook dot org + * Fix bug with use of capital E in numbers with exponents + Mateusz Loskot, mateusz at loskot dot net + * Add stddef.h include + * Patch allows for json-c compile with -Werror and not fail due to + -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations + Geoffrey Young, geoff at modperlcookbook dot org + +0.7 + * Add escaping of backslash to json output + * Add escaping of foward slash on tokenizing and output + * Changes to internal tokenizer from using recursion to + using a depth state structure to allow incremental parsing + +0.6 + * Fix bug in escaping of control characters + Johan Björklund, johbjo09 at kth dot se + * Remove include "config.h" from headers (should only + be included from .c files) + Michael Clark + +0.5 + * Make headers C++ compatible by change *this to *obj + * Add ifdef C++ extern "C" to headers + * Use simpler definition of min and max in bits.h + Larry Lansing, llansing at fuzzynerd dot com + + * Remove automake 1.6 requirement + * Move autogen commands into autogen.sh. Update README + * Remove error pointer special case for Windows + * Change license from LGPL to MIT + Michael Clark + +0.4 + * Fix additional error case in object parsing + * Add back sign reversal in nested object parse as error pointer + value is negative, while error value is positive. + Michael Clark + +0.3 + * fix pointer arithmetic bug for error pointer check in is_error() macro + * fix type passed to printbuf_memappend in json_tokener + * update autotools bootstrap instructions in README + Michael Clark + +0.2 + * printbuf.c - C. Watford (christopher.watford@gmail.com) + Added a Win32/Win64 compliant implementation of vasprintf + * debug.c - C. Watford (christopher.watford@gmail.com) + Removed usage of vsyslog on Win32/Win64 systems, needs to be handled + by a configure script + * json_object.c - C. Watford (christopher.watford@gmail.com) + Added scope operator to wrap usage of json_object_object_foreach, this + needs to be rethought to be more ANSI C friendly + * json_object.h - C. Watford (christopher.watford@gmail.com) + Added Microsoft C friendly version of json_object_object_foreach + * json_tokener.c - C. Watford (christopher.watford@gmail.com) + Added a Win32/Win64 compliant implementation of strndup + * json_util.c - C. Watford (christopher.watford@gmail.com) + Added cast and mask to suffice size_t v. unsigned int conversion + correctness + * json_tokener.c - sign reversal issue on error info for nested object parse + spotted by Johan Björklund (johbjo09 at kth.se) + * json_object.c - escape " in json_escape_str + * Change to automake and libtool to build shared and static library + Michael Clark + +0.1 + * initial release diff --git a/3P/json/Doxyfile b/3P/json/Doxyfile new file mode 100644 index 00000000..605e729a --- /dev/null +++ b/3P/json/Doxyfile @@ -0,0 +1,1153 @@ +# Doxyfile 1.3.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = json-c + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0.11 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of source +# files, where putting all generated files in the same directory would otherwise +# cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is used +# as the annotated text. Otherwise, the brief description is used as-is. If left +# blank, the following values are used ("$name" is automatically replaced with the +# name of the entity): "The $name class" "The $name widget" "The $name file" +# "is" "provides" "specifies" "contains" "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm + +FILE_PATTERNS = *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse the +# parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superseded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes that +# lay further from the root node will be omitted. Note that setting this option to +# 1 or 2 may greatly reduce the computation time needed for large code bases. Also +# note that a graph may be further truncated if the graph's image dimensions are +# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). +# If 0 is used for the depth value (the default), the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/3P/json/Makefile.am b/3P/json/Makefile.am new file mode 100644 index 00000000..df56a692 --- /dev/null +++ b/3P/json/Makefile.am @@ -0,0 +1,92 @@ +include Makefile.am.inc + +EXTRA_DIST = README.html README-WIN32.html config.h.win32 doc json-c.vcproj +SUBDIRS = . tests + +lib_LTLIBRARIES = libjson-c.la +if ENABLE_OLDNAME_COMPAT +lib_LTLIBRARIES+=libjson.la +endif + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = json-c.pc +if ENABLE_OLDNAME_COMPAT +pkgconfig_DATA += json.pc +endif + +libjson_cincludedir = $(includedir)/json-c +libjson_cinclude_HEADERS = \ + arraylist.h \ + bits.h \ + debug.h \ + json.h \ + json_config.h \ + json_c_version.h \ + json_inttypes.h \ + json_object.h \ + json_object_iterator.h \ + json_object_private.h \ + json_tokener.h \ + json_util.h \ + linkhash.h \ + printbuf.h + +#libjsonx_includedir = $(libdir)/json-c-@VERSION@ +# +#libjsonx_include_HEADERS = \ +# json_config.h + +libjson_c_la_LDFLAGS = -version-info 2:1:0 -no-undefined + +if ENABLE_OLDNAME_COMPAT +libjson_la_LDFLAGS = -version-info 1:0:1 -no-undefined -ljson-c + +# Temporary libjson library. This will be removed after one release. +libjson_la_LIBADD = -ljson-c +endif + + +libjson_c_la_SOURCES = \ + arraylist.c \ + debug.c \ + json_c_version.c \ + json_object.c \ + json_object_iterator.c \ + json_tokener.c \ + json_util.c \ + linkhash.c \ + printbuf.c + + +distclean-local: + -rm -rf $(testsubdir) + -rm -rf config.h.in~ Makefile.in aclocal.m4 autom4te.cache/ config.guess config.sub depcomp install-sh ltmain.sh missing + +maintainer-clean-local: + -rm -rf configure + +if ENABLE_OLDNAME_COMPAT +install-data-hook: + test \! -e "$(DESTDIR)@includedir@/json" || rm "$(DESTDIR)@includedir@/json" + $(LN_S) json-c "$(DESTDIR)@includedir@/json" + +uninstall-local: + rm -f "$(DESTDIR)@includedir@/json" + rm -rf "$(DESTDIR)@includedir@/json-c" + +endif + +ANDROID_CFLAGS = -I$(top_srcdir) -DHAVE_CONFIG_H + +Android.mk: Makefile.am + androgenizer -:PROJECT json-c \ + -:SHARED libjson-c \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libjson_c_la_SOURCES) $(nodist_libjson_c_la_SOURCES) \ + -:CFLAGS $(DEFS) $(ANDROID_CFLAGS) $(libjson_c_la_CFLAGS) \ + -:LDFLAGS $(libjson_c_la_LDFLAGS) $(libjson_c_la_LIBADD) \ + -:HEADER_TARGET json-c \ + -:HEADERS $(libjson_cinclude_HEADERS) \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + > $@ diff --git a/3P/json/Makefile.am.inc b/3P/json/Makefile.am.inc new file mode 100644 index 00000000..fd68a25f --- /dev/null +++ b/3P/json/Makefile.am.inc @@ -0,0 +1,2 @@ +AM_CFLAGS = -Wall -Werror -Wextra -Wwrite-strings -Wno-unused-parameter -std=gnu99 -D_GNU_SOURCE -D_REENTRANT + diff --git a/3P/json/NEWS b/3P/json/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/3P/json/README b/3P/json/README new file mode 100644 index 00000000..ac1e812b --- /dev/null +++ b/3P/json/README @@ -0,0 +1,44 @@ +Building on Unix with git, gcc and autotools + +Home page for json-c: + https://github.com/json-c/json-c/wiki + + Caution: do NOT use sources from svn.metaparadigm.com, they are old. + +Prerequisites: + gcc (or another C compiler) + libtool + + If you're not using a release tarball, you'll also need: + autoconf (autoreconf) + automake + +Github repo for json-c: + https://github.com/json-c/json-c + + $ git clone https://github.com/json-c/json-c.git + $ cd json-c + $ sh autogen.sh + +Then + + $ ./configure + $ make + $ make install + +To build and run the test programs run + + $ make check + +Linking to libjson-c + +If your system has pkgconfig then you can just add this to your makefile + +CFLAGS += $(shell pkg-config --cflags json-c) +LDFLAGS += $(shell pkg-config --libs json-c) + +Without pkgconfig, you would do something like this: + +JSON_C_DIR=/path/to/json_c/install +CFLAGS += -I$(JSON_C_DIR)/include/json-c +LDFLAGS+= -L$(JSON_C_DIR)/lib -ljson-c diff --git a/3P/json/README-WIN32.html b/3P/json/README-WIN32.html new file mode 100644 index 00000000..28fc7d88 --- /dev/null +++ b/3P/json/README-WIN32.html @@ -0,0 +1,50 @@ + + + + JSON-C - A JSON implementation in C - Win32 specific notes + + + +

Windows specific notes for JSON-C

+

Please send Win32 bug reports to christopher.watford@gmail.com

+

Win32 Specific Changes:

+
    +
  • + Various functions have been redefined to their Win32 version (i.e. open + on win32 is _open)
  • +
  • + Implemented missing functions from MS's libc (i.e. vasprintf and strndup)
  • +
  • + Added code to allow Win64 support without integer resizing issues, this + probably makes it much nicer on 64bit machines everywhere (i.e. using ptrdiff_t + for pointer math)
  • +
+

Porting Changelog:

+
+
printbuf.c - C. Watford (christopher.watford@gmail.com)
+
+ Added a Win32/Win64 compliant implementation of vasprintf
+
debug.c - C. Watford (christopher.watford@gmail.com)
+
+ Removed usage of vsyslog on Win32/Win64 systems, needs to be handled + by a configure script
+
json_object.c - C. Watford (christopher.watford@gmail.com)
+
+ Added scope operator to wrap usage of json_object_object_foreach, this needs to be + rethought to be more ANSI C friendly
+
json_object.h - C. Watford (christopher.watford@gmail.com)
+
+ Added Microsoft C friendly version of json_object_object_foreach
+
json_tokener.c - C. Watford (christopher.watford@gmail.com)
+
+ Added a Win32/Win64 compliant implementation of strndup
+
json_util.c - C. Watford (christopher.watford@gmail.com)
+
+ Added cast and mask to suffice size_t v. unsigned int + conversion correctness
+
+

This program is free software; you can redistribute it and/or modify it under + the terms of the MIT License. See COPYING for details.

+
+ + diff --git a/3P/json/README.html b/3P/json/README.html new file mode 100644 index 00000000..dc696af3 --- /dev/null +++ b/3P/json/README.html @@ -0,0 +1,34 @@ + + + + JSON-C - A JSON implementation in C + + + +

JSON-C - A JSON implementation in C

+ +

Overview

+

JSON-C implements a reference counting object model that allows you to easily + construct JSON objects in C, output them as JSON formatted strings and parse + JSON formatted strings back into the C representation of JSON objects.

+ +

Building

+

To setup JSON-C to build on your system please run configure and make.

+

If you are on Win32 and are not using the VS project file, be sure + to rename config.h.win32 to config.h before building.

+ +

Documentation

+

Doxygen generated documentation exists here + and Win32 specific notes can be found here.

+ +

GIT Reposository

+

git clone https://github.com/json-c/json-c.git

+ +

Mailing List

+ Send email to json-c <at> googlegroups <dot> com

+ +

License

+

This program is free software; you can redistribute it and/or modify it under the terms of the MIT License..

+
+ + diff --git a/3P/json/RELEASE_CHECKLIST.txt b/3P/json/RELEASE_CHECKLIST.txt new file mode 100644 index 00000000..2e901c35 --- /dev/null +++ b/3P/json/RELEASE_CHECKLIST.txt @@ -0,0 +1,61 @@ + +Release checklist: + +release=0.11 +git clone https://github.com/json-c/json-c json-c-${release} +cd json-c-${release} + +Check that the compile works on Linux +Check that the compile works on NetBSD +Check that the compile works on Windows +Check ChangeLog to see if anything should be added. + + git branch json-c-${release} + git checkout json-c-${release} + +Generate the configure script and other files: + sh autogen.sh + git add -f Makefile.in aclocal.m4 config.guess \ + config.sub configure depcomp install-sh \ + ltmain.sh missing tests/Makefile.in \ + INSTALL + + # check for anything else to be added: + git status --ignored + git commit + +Generate the doxygen documentation: + doxygen + git add -f doc + git commit doc + +cd .. +echo .git > excludes +echo autom4te.cache >> excludes +tar -czf json-c-${release}.tar.gz -X excludes json-c-${release} + +echo doc >> excludes +tar -czf json-c-${release}-nodoc.tar.gz -X excludes json-c-${release} + +Tag the branch: +cd json-c-${release} +git tag -a json-c-${release}-$(date +%Y%m%d) +git push +git push --tags + +Go to https://github.com/json-c/json-c/downloads +Upload the two tarballs. + + +=================================== + +Post-release checklist: + +git branch master +Add new section to CHANGES +Update the version in json_c_version.h +Update the version in Doxyfile +Update the version in configure.in +Update the libjson_la_LDFLAGS line in Makefile.am to the new version. + http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html + diff --git a/3P/json/arraylist.c b/3P/json/arraylist.c new file mode 100644 index 00000000..81b6fa2b --- /dev/null +++ b/3P/json/arraylist.c @@ -0,0 +1,101 @@ +/* + * $Id: arraylist.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#ifdef STDC_HEADERS +# include +# include +#endif /* STDC_HEADERS */ + +#if defined(HAVE_STRINGS_H) && !defined(_STRING_H) && !defined(__USE_BSD) +# include +#endif /* HAVE_STRINGS_H */ + +#include "bits.h" +#include "arraylist.h" + +struct array_list* +array_list_new(array_list_free_fn *free_fn) +{ + struct array_list *arr; + + arr = (struct array_list*)calloc(1, sizeof(struct array_list)); + if(!arr) return NULL; + arr->size = ARRAY_LIST_DEFAULT_SIZE; + arr->length = 0; + arr->free_fn = free_fn; + if(!(arr->array = (void**)calloc(sizeof(void*), arr->size))) { + free(arr); + return NULL; + } + return arr; +} + +extern void +array_list_free(struct array_list *arr) +{ + int i; + for(i = 0; i < arr->length; i++) + if(arr->array[i]) arr->free_fn(arr->array[i]); + free(arr->array); + free(arr); +} + +void* +array_list_get_idx(struct array_list *arr, int i) +{ + if(i >= arr->length) return NULL; + return arr->array[i]; +} + +static int array_list_expand_internal(struct array_list *arr, int max) +{ + void *t; + int new_size; + + if(max < arr->size) return 0; + new_size = json_max(arr->size << 1, max); + if(!(t = realloc(arr->array, new_size*sizeof(void*)))) return -1; + arr->array = (void**)t; + (void)memset(arr->array + arr->size, 0, (new_size-arr->size)*sizeof(void*)); + arr->size = new_size; + return 0; +} + +int +array_list_put_idx(struct array_list *arr, int idx, void *data) +{ + if(array_list_expand_internal(arr, idx+1)) return -1; + if(arr->array[idx]) arr->free_fn(arr->array[idx]); + arr->array[idx] = data; + if(arr->length <= idx) arr->length = idx + 1; + return 0; +} + +int +array_list_add(struct array_list *arr, void *data) +{ + return array_list_put_idx(arr, arr->length, data); +} + +void +array_list_sort(struct array_list *arr, int(*sort_fn)(const void *, const void *)) +{ + qsort(arr->array, arr->length, sizeof(arr->array[0]), + (int (*)(const void *, const void *))sort_fn); +} + +int +array_list_length(struct array_list *arr) +{ + return arr->length; +} diff --git a/3P/json/arraylist.h b/3P/json/arraylist.h new file mode 100644 index 00000000..4f3113c0 --- /dev/null +++ b/3P/json/arraylist.h @@ -0,0 +1,56 @@ +/* + * $Id: arraylist.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _arraylist_h_ +#define _arraylist_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ARRAY_LIST_DEFAULT_SIZE 32 + +typedef void (array_list_free_fn) (void *data); + +struct array_list +{ + void **array; + int length; + int size; + array_list_free_fn *free_fn; +}; + +extern struct array_list* +array_list_new(array_list_free_fn *free_fn); + +extern void +array_list_free(struct array_list *al); + +extern void* +array_list_get_idx(struct array_list *al, int i); + +extern int +array_list_put_idx(struct array_list *al, int i, void *data); + +extern int +array_list_add(struct array_list *al, void *data); + +extern int +array_list_length(struct array_list *al); + +extern void +array_list_sort(struct array_list *arr, int(*compar)(const void *, const void *)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3P/json/autogen.sh b/3P/json/autogen.sh new file mode 100755 index 00000000..69e765a6 --- /dev/null +++ b/3P/json/autogen.sh @@ -0,0 +1,13 @@ +#!/bin/sh +autoreconf -v --install || exit 1 + +# If there are any options, assume the user wants to run configure. +# To run configure w/o any options, use ./autogen.sh --configure +if [ $# -gt 0 ] ; then + case "$1" in + --conf*) + shift 1 + ;; + esac + exec ./configure "$@" +fi diff --git a/3P/json/bits.h b/3P/json/bits.h new file mode 100644 index 00000000..c8cbbc82 --- /dev/null +++ b/3P/json/bits.h @@ -0,0 +1,28 @@ +/* + * $Id: bits.h,v 1.10 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _bits_h_ +#define _bits_h_ + +#ifndef json_min +#define json_min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef json_max +#define json_max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#define hexdigit(x) (((x) <= '9') ? (x) - '0' : ((x) & 7) + 9) +#define error_ptr(error) ((void*)error) +#define error_description(error) (json_tokener_errors[error]) +#define is_error(ptr) (ptr == NULL) + +#endif diff --git a/3P/json/builders/cmake/CMakeLists.txt b/3P/json/builders/cmake/CMakeLists.txt new file mode 100644 index 00000000..cae1bf93 --- /dev/null +++ b/3P/json/builders/cmake/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 2.8.11) + +include (libjson-c) + +project(json-c) + +file( + GLOB + source_files + ../../arraylist.c + ../../debug.c + ../../json_c_version.c + ../../json_object.c + ../../json_object_iterator.c + ../../json_tokener.c + ../../json_util.c + ../../linkhash.c + ../../printbuf.c +) + +set(CMAKE_C_FLAGS "-Wall -Werror -Wextra -Wwrite-strings -Wno-unused-parameter -std=gnu99 -D_GNU_SOURCE -D_REENTRANT") + +add_library( + json-c + SHARED + ${source_files} +) + +target_include_directories (json-c PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/3P/json/builders/linux-gcc/Makefile b/3P/json/builders/linux-gcc/Makefile new file mode 100644 index 00000000..9281599b --- /dev/null +++ b/3P/json/builders/linux-gcc/Makefile @@ -0,0 +1,31 @@ +ifeq (,$(filter _%,$(notdir $(CURDIR)))) +include $(AWOXCVS)/AwoxSoftware/builders/linux-gcc/target.mk +else + + +include $(BUILDERS)/main.mk +include $(AUDIOBUILDERS)/use-libjson-c.mk + +VPATH = ../../.. + +OBJS += arraylist.o +OBJS += debug.o +OBJS += json_c_version.o +OBJS += json_object.o +OBJS += json_object_iterator.o +OBJS += json_tokener.o +OBJS += json_util.o +OBJS += linkhash.o +OBJS += printbuf.o + +AWOX_CFLAGS += -I../../../ + +AWOX_CFLAGS += -Wall -Werror -Wextra -Wwrite-strings -Wno-unused-parameter -std=gnu99 -D_GNU_SOURCE -D_REENTRANT + +MAKELIB_TARGET=json-c + +LIB_DEPS=$(AWOX_LIB_DEPS) + +include $(BUILDERS)/make-lib.mk + +endif diff --git a/3P/json/config.h.in b/3P/json/config.h.in new file mode 100644 index 00000000..b8b2d17e --- /dev/null +++ b/3P/json/config.h.in @@ -0,0 +1,148 @@ +/* config.h.in. Generated from configure.in by autoheader. */ + +/* Define if .gnu.warning accepts long strings. */ +#undef HAS_GNU_WARNING_LONG + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_LOCALE_H + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `open' function. */ +#undef HAVE_OPEN + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#undef HAVE_REALLOC + +/* Define to 1 if you have the `setlocale' function. */ +#undef HAVE_SETLOCALE + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDARG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strcasecmp' function. */ +#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strncasecmp' function. */ +#undef HAVE_STRNCASECMP + +/* Define to 1 if you have the `strndup' function. */ +#undef HAVE_STRNDUP + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_CDEFS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vasprintf' function. */ +#undef HAVE_VASPRINTF + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Define to 1 if you have the `vsyslog' function. */ +#undef HAVE_VSYSLOG + +/* Public define for json_inttypes.h */ +#undef JSON_C_HAVE_INTTYPES_H + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to rpl_realloc if the replacement function should be used. */ +#undef realloc + +/* Define to `unsigned int' if does not define. */ +#undef size_t diff --git a/3P/json/config.h.win32 b/3P/json/config.h.win32 new file mode 100644 index 00000000..ec3a84aa --- /dev/null +++ b/3P/json/config.h.win32 @@ -0,0 +1,94 @@ +/* + * $Id: config.h.win32,v 1.2 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +/* config.h.win32 Generated by configure. */ + +#define PACKAGE_STRING "JSON C Library 0.2" +#define PACKAGE_BUGREPORT "json-c@googlegroups.com" +#define PACKAGE_NAME "JSON C Library" +#define PACKAGE_TARNAME "json-c" +#define PACKAGE_VERSION "0.2" + +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `open' function. */ +#undef HAVE_OPEN + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#define HAVE_REALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRNDUP + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYSLOG_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if you have the `vsyslog' function. */ +#undef HAVE_VSYSLOG + +/* Define to 1 if you have the `strncasecmp' function. */ +#undef HAVE_STRNCASECMP + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 diff --git a/3P/json/configure.in b/3P/json/configure.in new file mode 100644 index 00000000..d87161c4 --- /dev/null +++ b/3P/json/configure.in @@ -0,0 +1,69 @@ +AC_PREREQ(2.52) + +# Process this file with autoconf to produce a configure script. +AC_INIT([json-c], 0.11, [json-c@googlegroups.com]) + +AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) + +AC_PROG_MAKE_SET + +AC_ARG_ENABLE(oldname-compat, + AS_HELP_STRING([--disable-oldname-compat], + [Don't include the old libjson.so library and include/json directory.]), +[], +[enable_oldname_compat=yes] +) +AM_CONDITIONAL(ENABLE_OLDNAME_COMPAT, [test "x${enable_oldname_compat}" != "xno"]) + +# Checks for programs. + +# Checks for libraries. + +# Checks for header files. +AC_CONFIG_HEADER(config.h) +AC_CONFIG_HEADER(json_config.h) +AC_HEADER_STDC +AC_CHECK_HEADERS(fcntl.h limits.h strings.h syslog.h unistd.h [sys/cdefs.h] [sys/param.h] stdarg.h locale.h) +AC_CHECK_HEADER(inttypes.h,[AC_DEFINE([JSON_C_HAVE_INTTYPES_H],[1],[Public define for json_inttypes.h])]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_VPRINTF +AC_FUNC_MEMCMP +AC_FUNC_MALLOC +AC_FUNC_REALLOC +AC_CHECK_FUNCS(strcasecmp strdup strndup strerror snprintf vsnprintf vasprintf open vsyslog strncasecmp setlocale) + +#check if .section.gnu.warning accepts long strings (for __warn_references) +AC_LANG_PUSH([C]) + +AC_MSG_CHECKING([if .gnu.warning accepts long strings]) +AC_LINK_IFELSE([[ +extern void json_object_get(); +__asm__(".section .gnu.json_object_get,\n\t.ascii \"Please link against libjson-c instead of libjson\"\n\t.text"); + +int main(int c,char* v) {return 0;} +]], [ + AC_DEFINE(HAS_GNU_WARNING_LONG, 1, [Define if .gnu.warning accepts long strings.]) + AC_MSG_RESULT(yes) +], [ + AC_MSG_RESULT(no) +]) + +AC_LANG_POP([C]) + +AM_PROG_LIBTOOL + +AC_CONFIG_FILES([ +Makefile +json.pc +json-c.pc +tests/Makefile +json-c-uninstalled.pc +]) + +AC_OUTPUT + diff --git a/3P/json/debug.c b/3P/json/debug.c new file mode 100644 index 00000000..e0294caa --- /dev/null +++ b/3P/json/debug.c @@ -0,0 +1,98 @@ +/* + * $Id: debug.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#if HAVE_SYSLOG_H +# include +#endif /* HAVE_SYSLOG_H */ + +#if HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#if HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#include "debug.h" + +static int _syslog = 0; +static int _debug = 0; + +void mc_set_debug(int debug) { _debug = debug; } +int mc_get_debug(void) { return _debug; } + +extern void mc_set_syslog(int syslog) +{ + _syslog = syslog; +} + +void mc_abort(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_ERR, msg, ap); + } else +#endif + vprintf(msg, ap); + va_end(ap); + exit(1); +} + + +void mc_debug(const char *msg, ...) +{ + va_list ap; + if(_debug) { + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_DEBUG, msg, ap); + } else +#endif + vprintf(msg, ap); + va_end(ap); + } +} + +void mc_error(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_ERR, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} + +void mc_info(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); +#if HAVE_VSYSLOG + if(_syslog) { + vsyslog(LOG_INFO, msg, ap); + } else +#endif + vfprintf(stderr, msg, ap); + va_end(ap); +} diff --git a/3P/json/debug.h b/3P/json/debug.h new file mode 100644 index 00000000..1e097017 --- /dev/null +++ b/3P/json/debug.h @@ -0,0 +1,72 @@ +/* + * $Id: debug.h,v 1.5 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _DEBUG_H_ +#define _DEBUG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void mc_set_debug(int debug); +extern int mc_get_debug(void); + +extern void mc_set_syslog(int syslog); +extern void mc_abort(const char *msg, ...); +extern void mc_debug(const char *msg, ...); +extern void mc_error(const char *msg, ...); +extern void mc_info(const char *msg, ...); + +#ifndef __STRING +#define __STRING(x) #x +#endif + +#ifndef PARSER_BROKEN_FIXED + +#define JASSERT(cond) do {} while(0) + +#else + +#define JASSERT(cond) do { \ + if (!(cond)) { \ + mc_error("cjson assert failure %s:%d : cond \"" __STRING(cond) "failed\n", __FILE__, __LINE__); \ + *(int *)0 = 1;\ + abort(); \ + }\ + } while(0) + +#endif + +#define MC_ABORT(x, ...) mc_abort(x, ##__VA_ARGS__) +#define MC_ERROR(x, ...) mc_error(x, ##__VA_ARGS__) + +#ifdef MC_MAINTAINER_MODE +#define MC_SET_DEBUG(x) mc_set_debug(x) +#define MC_GET_DEBUG() mc_get_debug() +#define MC_SET_SYSLOG(x) mc_set_syslog(x) +#define MC_DEBUG(x, ...) mc_debug(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) mc_info(x, ##__VA_ARGS__) +#else +#define MC_SET_DEBUG(x) if (0) mc_set_debug(x) +#define MC_GET_DEBUG() (0) +#define MC_SET_SYSLOG(x) if (0) mc_set_syslog(x) +#define MC_DEBUG(x, ...) if (0) mc_debug(x, ##__VA_ARGS__) +#define MC_INFO(x, ...) if (0) mc_info(x, ##__VA_ARGS__) +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3P/json/json-c-uninstalled.pc.in b/3P/json/json-c-uninstalled.pc.in new file mode 100644 index 00000000..dab2bab5 --- /dev/null +++ b/3P/json/json-c-uninstalled.pc.in @@ -0,0 +1,11 @@ +prefix= +exec_prefix= +libdir=@abs_top_builddir@ +includedir=@abs_top_srcdir@ + +Name: json +Description: JSON implementation in C +Version: @VERSION@ +Requires: +Libs: -L@abs_top_builddir@ -ljson-c +Cflags: -I@abs_top_srcdir@ diff --git a/3P/json/json-c.pc.in b/3P/json/json-c.pc.in new file mode 100644 index 00000000..037739d2 --- /dev/null +++ b/3P/json/json-c.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: json-c +Description: JSON implementation in C +Version: @VERSION@ +Requires: +Libs: -L${libdir} -ljson-c +Cflags: -I${includedir}/json-c diff --git a/3P/json/json-c.vcproj b/3P/json/json-c.vcproj new file mode 100644 index 00000000..0b887544 --- /dev/null +++ b/3P/json/json-c.vcproj @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/3P/json/json.h b/3P/json/json.h new file mode 100644 index 00000000..4339b20e --- /dev/null +++ b/3P/json/json.h @@ -0,0 +1,34 @@ +/* + * $Id: json.h,v 1.6 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_h_ +#define _json_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bits.h" +#include "debug.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_util.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_object_iterator.h" +#include "json_c_version.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3P/json/json.pc.in b/3P/json/json.pc.in new file mode 100644 index 00000000..80e75d14 --- /dev/null +++ b/3P/json/json.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: json-c +Description: JSON implementation in C, compat shim. Use json-c instead. +Version: @VERSION@ +Requires: json-c +Libs: +Cflags: diff --git a/3P/json/json_c_version.c b/3P/json/json_c_version.c new file mode 100644 index 00000000..13eb1885 --- /dev/null +++ b/3P/json/json_c_version.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2012 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ +#include "config.h" + +#include "json_c_version.h" + +const char *json_c_version(void) +{ + return JSON_C_VERSION; +} + +int json_c_version_num(void) +{ + return JSON_C_VERSION_NUM; +} + diff --git a/3P/json/json_c_version.h b/3P/json/json_c_version.h new file mode 100644 index 00000000..ff20f950 --- /dev/null +++ b/3P/json/json_c_version.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2012 Eric Haszlakiewicz + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + */ + +#ifndef _json_c_version_h_ +#define _json_c_version_h_ + +#define JSON_C_MAJOR_VERSION 0 +#define JSON_C_MINOR_VERSION 11 +#define JSON_C_MICRO_VERSION 0 +#define JSON_C_VERSION_NUM ((JSON_C_MAJOR_VERSION << 16) | \ + (JSON_C_MINOR_VERSION << 8) | \ + JSON_C_MICRO_VERSION) +#define JSON_C_VERSION "0.11" + +const char *json_c_version(void); /* Returns JSON_C_VERSION */ +int json_c_version_num(void); /* Returns JSON_C_VERSION_NUM */ + +#endif diff --git a/3P/json/json_config.h.in b/3P/json/json_config.h.in new file mode 100644 index 00000000..7888e021 --- /dev/null +++ b/3P/json/json_config.h.in @@ -0,0 +1,3 @@ + +/* Define to 1 if you have the header file. */ +#undef JSON_C_HAVE_INTTYPES_H diff --git a/3P/json/json_inttypes.h b/3P/json/json_inttypes.h new file mode 100644 index 00000000..2f84adec --- /dev/null +++ b/3P/json/json_inttypes.h @@ -0,0 +1,28 @@ + +#ifndef _json_inttypes_h_ +#define _json_inttypes_h_ + +#include "json_config.h" + +#if defined(_MSC_VER) && _MSC_VER < 1700 + +/* Anything less than Visual Studio C++ 10 is missing stdint.h and inttypes.h */ +typedef __int32 int32_t; +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX ((int32_t)_I32_MAX) +typedef __int64 int64_t; +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX ((int64_t)_I64_MAX) +#define PRId64 "I64d" +#define SCNd64 "I64d" + +#else + +#ifdef JSON_C_HAVE_INTTYPES_H +#include +#endif +/* inttypes.h includes stdint.h */ + +#endif + +#endif diff --git a/3P/json/json_object.c b/3P/json/json_object.c new file mode 100644 index 00000000..f2b5ce04 --- /dev/null +++ b/3P/json/json_object.c @@ -0,0 +1,782 @@ +/* + * $Id: json_object.c,v 1.17 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "debug.h" +#include "printbuf.h" +#include "linkhash.h" +#include "arraylist.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_object_private.h" +#include "json_util.h" + +#if !defined(HAVE_STRDUP) && defined(_MSC_VER) + /* MSC has the version as _strdup */ +# define strdup _strdup +#elif !defined(HAVE_STRDUP) +# error You do not have strdup on your system. +#endif /* HAVE_STRDUP */ + +#if !defined(HAVE_STRNDUP) + char* strndup(const char* str, size_t n); +#endif /* !HAVE_STRNDUP */ + +// Don't define this. It's not thread-safe. +/* #define REFCOUNT_DEBUG 1 */ + +const char *json_number_chars = "0123456789.+-eE"; +const char *json_hex_chars = "0123456789abcdefABCDEF"; + +static void json_object_generic_delete(struct json_object* jso); +static struct json_object* json_object_new(enum json_type o_type); + +static json_object_to_json_string_fn json_object_object_to_json_string; +static json_object_to_json_string_fn json_object_boolean_to_json_string; +static json_object_to_json_string_fn json_object_int_to_json_string; +static json_object_to_json_string_fn json_object_double_to_json_string; +static json_object_to_json_string_fn json_object_string_to_json_string; +static json_object_to_json_string_fn json_object_array_to_json_string; + + +/* ref count debugging */ + +#ifdef REFCOUNT_DEBUG + +static struct lh_table *json_object_table; + +static void json_object_init(void) __attribute__ ((constructor)); +static void json_object_init(void) { + MC_DEBUG("json_object_init: creating object table\n"); + json_object_table = lh_kptr_table_new(128, "json_object_table", NULL); +} + +static void json_object_fini(void) __attribute__ ((destructor)); +static void json_object_fini(void) { + struct lh_entry *ent; + if(MC_GET_DEBUG()) { + if (json_object_table->count) { + MC_DEBUG("json_object_fini: %d referenced objects at exit\n", + json_object_table->count); + lh_foreach(json_object_table, ent) { + struct json_object* obj = (struct json_object*)ent->v; + MC_DEBUG("\t%s:%p\n", json_type_to_name(obj->o_type), obj); + } + } + } + MC_DEBUG("json_object_fini: freeing object table\n"); + lh_table_free(json_object_table); +} +#endif /* REFCOUNT_DEBUG */ + + +/* string escaping */ + +static int json_escape_str(struct printbuf *pb, char *str, int len) +{ + int pos = 0, start_offset = 0; + unsigned char c; + while (len--) { + c = str[pos]; + switch(c) { + case '\b': + case '\n': + case '\r': + case '\t': + case '\f': + case '"': + case '\\': + case '/': + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + if(c == '\b') printbuf_memappend(pb, "\\b", 2); + else if(c == '\n') printbuf_memappend(pb, "\\n", 2); + else if(c == '\r') printbuf_memappend(pb, "\\r", 2); + else if(c == '\t') printbuf_memappend(pb, "\\t", 2); + else if(c == '\f') printbuf_memappend(pb, "\\f", 2); + else if(c == '"') printbuf_memappend(pb, "\\\"", 2); + else if(c == '\\') printbuf_memappend(pb, "\\\\", 2); + else if(c == '/') printbuf_memappend(pb, "\\/", 2); + start_offset = ++pos; + break; + default: + if(c < ' ') { + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + sprintbuf(pb, "\\u00%c%c", + json_hex_chars[c >> 4], + json_hex_chars[c & 0xf]); + start_offset = ++pos; + } else pos++; + } + } + if(pos - start_offset > 0) + printbuf_memappend(pb, str + start_offset, pos - start_offset); + return 0; +} + + +/* reference counting */ + +extern struct json_object* json_object_get(struct json_object *jso) +{ + if(jso) { + jso->_ref_count++; + } + return jso; +} + +int json_object_put(struct json_object *jso) +{ + if(jso) + { + jso->_ref_count--; + if(!jso->_ref_count) + { + if (jso->_user_delete) + jso->_user_delete(jso, jso->_userdata); + jso->_delete(jso); + return 1; + } + } + return 0; +} + + +/* generic object construction and destruction parts */ + +static void json_object_generic_delete(struct json_object* jso) +{ +#ifdef REFCOUNT_DEBUG + MC_DEBUG("json_object_delete_%s: %p\n", + json_type_to_name(jso->o_type), jso); + lh_table_delete(json_object_table, jso); +#endif /* REFCOUNT_DEBUG */ + printbuf_free(jso->_pb); + free(jso); +} + +static struct json_object* json_object_new(enum json_type o_type) +{ + struct json_object *jso; + + jso = (struct json_object*)calloc(sizeof(struct json_object), 1); + if(!jso) return NULL; + jso->o_type = o_type; + jso->_ref_count = 1; + jso->_delete = &json_object_generic_delete; +#ifdef REFCOUNT_DEBUG + lh_table_insert(json_object_table, jso, jso); + MC_DEBUG("json_object_new_%s: %p\n", json_type_to_name(jso->o_type), jso); +#endif /* REFCOUNT_DEBUG */ + return jso; +} + + +/* type checking functions */ + +int json_object_is_type(struct json_object *jso, enum json_type type) +{ + if (!jso) + return (type == json_type_null); + return (jso->o_type == type); +} + +enum json_type json_object_get_type(struct json_object *jso) +{ + if (!jso) + return json_type_null; + return jso->o_type; +} + +/* set a custom conversion to string */ + +void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn to_string_func, + void *userdata, + json_object_delete_fn *user_delete) +{ + // First, clean up any previously existing user info + if (jso->_user_delete) + { + jso->_user_delete(jso, jso->_userdata); + } + jso->_userdata = NULL; + jso->_user_delete = NULL; + + if (to_string_func == NULL) + { + // Reset to the standard serialization function + switch(jso->o_type) + { + case json_type_null: + jso->_to_json_string = NULL; + break; + case json_type_boolean: + jso->_to_json_string = &json_object_boolean_to_json_string; + break; + case json_type_double: + jso->_to_json_string = &json_object_double_to_json_string; + break; + case json_type_int: + jso->_to_json_string = &json_object_int_to_json_string; + break; + case json_type_object: + jso->_to_json_string = &json_object_object_to_json_string; + break; + case json_type_array: + jso->_to_json_string = &json_object_array_to_json_string; + break; + case json_type_string: + jso->_to_json_string = &json_object_string_to_json_string; + break; + } + return; + } + + jso->_to_json_string = to_string_func; + jso->_userdata = userdata; + jso->_user_delete = user_delete; +} + + +/* extended conversion to string */ + +const char* json_object_to_json_string_ext(struct json_object *jso, int flags) +{ + if (!jso) + return "null"; + + if ((!jso->_pb) && !(jso->_pb = printbuf_new())) + return NULL; + + printbuf_reset(jso->_pb); + + if(jso->_to_json_string(jso, jso->_pb, 0, flags) < 0) + return NULL; + + return jso->_pb->buf; +} + +/* backwards-compatible conversion to string */ + +const char* json_object_to_json_string(struct json_object *jso) +{ + return json_object_to_json_string_ext(jso, JSON_C_TO_STRING_SPACED); +} + +static void indent(struct printbuf *pb, int level, int flags) +{ + if (flags & JSON_C_TO_STRING_PRETTY) + { + printbuf_memset(pb, -1, ' ', level * 2); + } +} + +/* json_object_object */ + +static int json_object_object_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + int had_children = 0; + struct json_object_iter iter; + + sprintbuf(pb, "{" /*}*/); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + json_object_object_foreachC(jso, iter) + { + if (had_children) + { + sprintbuf(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED) + sprintbuf(pb, " "); + indent(pb, level+1, flags); + sprintbuf(pb, "\""); + json_escape_str(pb, iter.key, strlen(iter.key)); + if (flags & JSON_C_TO_STRING_SPACED) + sprintbuf(pb, "\": "); + else + sprintbuf(pb, "\":"); + if(iter.val == NULL) + sprintbuf(pb, "null"); + else + iter.val->_to_json_string(iter.val, pb, level+1,flags); + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + sprintbuf(pb, "\n"); + indent(pb,level,flags); + } + if (flags & JSON_C_TO_STRING_SPACED) + return sprintbuf(pb, /*{*/ " }"); + else + return sprintbuf(pb, /*{*/ "}"); +} + + +static void json_object_lh_entry_free(struct lh_entry *ent) +{ + free(ent->k); + json_object_put((struct json_object*)ent->v); +} + +static void json_object_object_delete(struct json_object* jso) +{ + lh_table_free(jso->o.c_object); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_object(void) +{ + struct json_object *jso = json_object_new(json_type_object); + if(!jso) return NULL; + jso->_delete = &json_object_object_delete; + jso->_to_json_string = &json_object_object_to_json_string; + jso->o.c_object = lh_kchar_table_new(JSON_OBJECT_DEF_HASH_ENTRIES, + NULL, &json_object_lh_entry_free); + return jso; +} + +struct lh_table* json_object_get_object(struct json_object *jso) +{ + if(!jso) return NULL; + switch(jso->o_type) { + case json_type_object: + return jso->o.c_object; + default: + return NULL; + } +} + +void json_object_object_add(struct json_object* jso, const char *key, + struct json_object *val) +{ + // We lookup the entry and replace the value, rather than just deleting + // and re-adding it, so the existing key remains valid. + json_object *existing_value = NULL; + struct lh_entry *existing_entry; + existing_entry = lh_table_lookup_entry(jso->o.c_object, (void*)key); + if (!existing_entry) + { + lh_table_insert(jso->o.c_object, strdup(key), val); + return; + } + existing_value = (void *)existing_entry->v; + if (existing_value) + json_object_put(existing_value); + existing_entry->v = val; +} + +int json_object_object_length(struct json_object *jso) +{ + return lh_table_length(jso->o.c_object); +} + +struct json_object* json_object_object_get(struct json_object* jso, const char *key) +{ + struct json_object *result = NULL; + json_object_object_get_ex(jso, key, &result); + return result; +} + +json_bool json_object_object_get_ex(struct json_object* jso, const char *key, struct json_object **value) +{ + if (value != NULL) + *value = NULL; + + if (NULL == jso) + return FALSE; + + switch(jso->o_type) + { + case json_type_object: + return lh_table_lookup_ex(jso->o.c_object, (void*)key, (void**)value); + default: + if (value != NULL) + *value = NULL; + return FALSE; + } +} + +void json_object_object_del(struct json_object* jso, const char *key) +{ + lh_table_delete(jso->o.c_object, key); +} + + +/* json_object_boolean */ + +static int json_object_boolean_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + if(jso->o.c_boolean) return sprintbuf(pb, "true"); + else return sprintbuf(pb, "false"); +} + +struct json_object* json_object_new_boolean(json_bool b) +{ + struct json_object *jso = json_object_new(json_type_boolean); + if(!jso) return NULL; + jso->_to_json_string = &json_object_boolean_to_json_string; + jso->o.c_boolean = b; + return jso; +} + +json_bool json_object_get_boolean(struct json_object *jso) +{ + if(!jso) return FALSE; + switch(jso->o_type) { + case json_type_boolean: + return jso->o.c_boolean; + case json_type_int: + return (jso->o.c_int64 != 0); + case json_type_double: + return (jso->o.c_double != 0); + case json_type_string: + return (jso->o.c_string.len != 0); + default: + return FALSE; + } +} + + +/* json_object_int */ + +static int json_object_int_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + return sprintbuf(pb, "%"PRId64, jso->o.c_int64); +} + +struct json_object* json_object_new_int(int32_t i) +{ + struct json_object *jso = json_object_new(json_type_int); + if(!jso) return NULL; + jso->_to_json_string = &json_object_int_to_json_string; + jso->o.c_int64 = i; + return jso; +} + +int32_t json_object_get_int(struct json_object *jso) +{ + int64_t cint64; + enum json_type o_type; + + if(!jso) return 0; + + o_type = jso->o_type; + cint64 = jso->o.c_int64; + + if (o_type == json_type_string) + { + /* + * Parse strings into 64-bit numbers, then use the + * 64-to-32-bit number handling below. + */ + if (json_parse_int64(jso->o.c_string.str, &cint64) != 0) + return 0; /* whoops, it didn't work. */ + o_type = json_type_int; + } + + switch(o_type) { + case json_type_int: + /* Make sure we return the correct values for out of range numbers. */ + if (cint64 <= INT32_MIN) + return INT32_MIN; + else if (cint64 >= INT32_MAX) + return INT32_MAX; + else + return (int32_t)cint64; + case json_type_double: + return (int32_t)jso->o.c_double; + case json_type_boolean: + return jso->o.c_boolean; + default: + return 0; + } +} + +struct json_object* json_object_new_int64(int64_t i) +{ + struct json_object *jso = json_object_new(json_type_int); + if(!jso) return NULL; + jso->_to_json_string = &json_object_int_to_json_string; + jso->o.c_int64 = i; + return jso; +} + +int64_t json_object_get_int64(struct json_object *jso) +{ + int64_t cint; + + if(!jso) return 0; + switch(jso->o_type) { + case json_type_int: + return jso->o.c_int64; + case json_type_double: + return (int64_t)jso->o.c_double; + case json_type_boolean: + return jso->o.c_boolean; + case json_type_string: + if (json_parse_int64(jso->o.c_string.str, &cint) == 0) return cint; + default: + return 0; + } +} + + +/* json_object_double */ + +static int json_object_double_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + char buf[128], *p, *q; + int size; + + size = snprintf(buf, 128, "%f", jso->o.c_double); + p = strchr(buf, ','); + if (p) { + *p = '.'; + } else { + p = strchr(buf, '.'); + } + if (p && (flags & JSON_C_TO_STRING_NOZERO)) { + /* last useful digit, always keep 1 zero */ + p++; + for (q=p ; *q ; q++) { + if (*q!='0') p=q; + } + /* drop trailing zeroes */ + *(++p) = 0; + size = p-buf; + } + printbuf_memappend(pb, buf, size); + return size; +} + +struct json_object* json_object_new_double(double d) +{ + struct json_object *jso = json_object_new(json_type_double); + if(!jso) return NULL; + jso->_to_json_string = &json_object_double_to_json_string; + jso->o.c_double = d; + return jso; +} + +double json_object_get_double(struct json_object *jso) +{ + double cdouble; + + if(!jso) return 0.0; + switch(jso->o_type) { + case json_type_double: + return jso->o.c_double; + case json_type_int: + return jso->o.c_int64; + case json_type_boolean: + return jso->o.c_boolean; + case json_type_string: + if(sscanf(jso->o.c_string.str, "%lf", &cdouble) == 1) return cdouble; + default: + return 0.0; + } +} + + +/* json_object_string */ + +static int json_object_string_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + sprintbuf(pb, "\""); + json_escape_str(pb, jso->o.c_string.str, jso->o.c_string.len); + sprintbuf(pb, "\""); + return 0; +} + +static void json_object_string_delete(struct json_object* jso) +{ + free(jso->o.c_string.str); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_string(const char *s) +{ + struct json_object *jso = json_object_new(json_type_string); + if(!jso) return NULL; + jso->_delete = &json_object_string_delete; + jso->_to_json_string = &json_object_string_to_json_string; + jso->o.c_string.str = strdup(s); + jso->o.c_string.len = strlen(s); + return jso; +} + +struct json_object* json_object_new_string_len(const char *s, int len) +{ + struct json_object *jso = json_object_new(json_type_string); + if(!jso) return NULL; + jso->_delete = &json_object_string_delete; + jso->_to_json_string = &json_object_string_to_json_string; + jso->o.c_string.str = (char*)malloc(len + 1); + memcpy(jso->o.c_string.str, (void *)s, len); + jso->o.c_string.str[len] = '\0'; + jso->o.c_string.len = len; + return jso; +} + +const char* json_object_get_string(struct json_object *jso) +{ + if(!jso) return NULL; + switch(jso->o_type) { + case json_type_string: + return jso->o.c_string.str; + default: + return json_object_to_json_string(jso); + } +} + +int json_object_get_string_len(struct json_object *jso) { + if(!jso) return 0; + switch(jso->o_type) { + case json_type_string: + return jso->o.c_string.len; + default: + return 0; + } +} + + +/* json_object_array */ + +static int json_object_array_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + int had_children = 0; + int ii; + sprintbuf(pb, "["); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + for(ii=0; ii < json_object_array_length(jso); ii++) + { + struct json_object *val; + if (had_children) + { + sprintbuf(pb, ","); + if (flags & JSON_C_TO_STRING_PRETTY) + sprintbuf(pb, "\n"); + } + had_children = 1; + if (flags & JSON_C_TO_STRING_SPACED) + sprintbuf(pb, " "); + indent(pb, level + 1, flags); + val = json_object_array_get_idx(jso, ii); + if(val == NULL) + sprintbuf(pb, "null"); + else + val->_to_json_string(val, pb, level+1, flags); + } + if (flags & JSON_C_TO_STRING_PRETTY) + { + if (had_children) + sprintbuf(pb, "\n"); + indent(pb,level,flags); + } + + if (flags & JSON_C_TO_STRING_SPACED) + return sprintbuf(pb, " ]"); + else + return sprintbuf(pb, "]"); +} + +static void json_object_array_entry_free(void *data) +{ + json_object_put((struct json_object*)data); +} + +static void json_object_array_delete(struct json_object* jso) +{ + array_list_free(jso->o.c_array); + json_object_generic_delete(jso); +} + +struct json_object* json_object_new_array(void) +{ + struct json_object *jso = json_object_new(json_type_array); + if(!jso) return NULL; + jso->_delete = &json_object_array_delete; + jso->_to_json_string = &json_object_array_to_json_string; + jso->o.c_array = array_list_new(&json_object_array_entry_free); + return jso; +} + +struct array_list* json_object_get_array(struct json_object *jso) +{ + if(!jso) return NULL; + switch(jso->o_type) { + case json_type_array: + return jso->o.c_array; + default: + return NULL; + } +} + +void json_object_array_sort(struct json_object *jso, int(*sort_fn)(const void *, const void *)) +{ + array_list_sort(jso->o.c_array, sort_fn); +} + +int json_object_array_length(struct json_object *jso) +{ + return array_list_length(jso->o.c_array); +} + +int json_object_array_add(struct json_object *jso,struct json_object *val) +{ + return array_list_add(jso->o.c_array, val); +} + +int json_object_array_put_idx(struct json_object *jso, int idx, + struct json_object *val) +{ + return array_list_put_idx(jso->o.c_array, idx, val); +} + +struct json_object* json_object_array_get_idx(struct json_object *jso, + int idx) +{ + return (struct json_object*)array_list_get_idx(jso->o.c_array, idx); +} + diff --git a/3P/json/json_object.h b/3P/json/json_object.h new file mode 100644 index 00000000..f16f4fea --- /dev/null +++ b/3P/json/json_object.h @@ -0,0 +1,562 @@ +/* + * $Id: json_object.h,v 1.12 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_object_h_ +#define _json_object_h_ + +#include "json_inttypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_OBJECT_DEF_HASH_ENTRIES 16 + +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output + * to have no extra whitespace or formatting applied. + */ +#define JSON_C_TO_STRING_PLAIN 0 +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes the output to have + * minimal whitespace inserted to make things slightly more readable. + */ +#define JSON_C_TO_STRING_SPACED (1<<0) +/** + * A flag for the json_object_to_json_string_ext() and + * json_object_to_file_ext() functions which causes + * the output to be formatted. + * + * See the "Two Space Tab" option at http://jsonformatter.curiousconcept.com/ + * for an example of the format. + */ +#define JSON_C_TO_STRING_PRETTY (1<<1) +/** + * A flag to drop trailing zero for float values + */ +#define JSON_C_TO_STRING_NOZERO (1<<2) + +#undef FALSE +#define FALSE ((json_bool)0) + +#undef TRUE +#define TRUE ((json_bool)1) + +extern const char *json_number_chars; +extern const char *json_hex_chars; + +/* CAW: added for ANSI C iteration correctness */ +struct json_object_iter +{ + char *key; + struct json_object *val; + struct lh_entry *entry; +}; + +/* forward structure definitions */ + +typedef int json_bool; +typedef struct printbuf printbuf; +typedef struct lh_table lh_table; +typedef struct array_list array_list; +typedef struct json_object json_object; +typedef struct json_object_iter json_object_iter; +typedef struct json_tokener json_tokener; + +/** + * Type of custom user delete functions. See json_object_set_serializer. + */ +typedef void (json_object_delete_fn)(struct json_object *jso, void *userdata); + +/** + * Type of a custom serialization function. See json_object_set_serializer. + */ +typedef int (json_object_to_json_string_fn)(struct json_object *jso, + struct printbuf *pb, + int level, + int flags); + +/* supported object types */ + +typedef enum json_type { + /* If you change this, be sure to update json_type_to_name() too */ + json_type_null, + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, +} json_type; + +/* reference counting functions */ + +/** + * Increment the reference count of json_object, thereby grabbing shared + * ownership of obj. + * + * @param obj the json_object instance + */ +extern struct json_object* json_object_get(struct json_object *obj); + +/** + * Decrement the reference count of json_object and free if it reaches zero. + * You must have ownership of obj prior to doing this or you will cause an + * imbalance in the reference count. + * + * @param obj the json_object instance + * @returns 1 if the object was freed. + */ +int json_object_put(struct json_object *obj); + +/** + * Check if the json_object is of a given type + * @param obj the json_object instance + * @param type one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, + */ +extern int json_object_is_type(struct json_object *obj, enum json_type type); + +/** + * Get the type of the json_object. See also json_type_to_name() to turn this + * into a string suitable, for instance, for logging. + * + * @param obj the json_object instance + * @returns type being one of: + json_type_null (i.e. obj == NULL), + json_type_boolean, + json_type_double, + json_type_int, + json_type_object, + json_type_array, + json_type_string, + */ +extern enum json_type json_object_get_type(struct json_object *obj); + + +/** Stringify object to json format. + * Equivalent to json_object_to_json_string_ext(obj, JSON_C_TO_STRING_SPACED) + * @param obj the json_object instance + * @returns a string in JSON format + */ +extern const char* json_object_to_json_string(struct json_object *obj); + +/** Stringify object to json format + * @param obj the json_object instance + * @param flags formatting options, see JSON_C_TO_STRING_PRETTY and other constants + * @returns a string in JSON format + */ +extern const char* json_object_to_json_string_ext(struct json_object *obj, int +flags); + +/** + * Set a custom serialization function to be used when this particular object + * is converted to a string by json_object_to_json_string. + * + * If a custom serializer is already set on this object, any existing + * user_delete function is called before the new one is set. + * + * If to_string_func is NULL, the other parameters are ignored + * and the default behaviour is reset. + * + * The userdata parameter is optional and may be passed as NULL. If provided, + * it is passed to to_string_func as-is. This parameter may be NULL even + * if user_delete is non-NULL. + * + * The user_delete parameter is optional and may be passed as NULL, even if + * the userdata parameter is non-NULL. It will be called just before the + * json_object is deleted, after it's reference count goes to zero + * (see json_object_put()). + * If this is not provided, it is up to the caller to free the userdata at + * an appropriate time. (i.e. after the json_object is deleted) + * + * @param jso the object to customize + * @param to_string_func the custom serialization function + * @param userdata an optional opaque cookie + * @param user_delete an optional function from freeing userdata + */ +void json_object_set_serializer(json_object *jso, + json_object_to_json_string_fn to_string_func, + void *userdata, + json_object_delete_fn *user_delete); + + + +/* object type methods */ + +/** Create a new empty object with a reference count of 1. The caller of + * this object initially has sole ownership. Remember, when using + * json_object_object_add or json_object_array_put_idx, ownership will + * transfer to the object/array. Call json_object_get if you want to maintain + * shared ownership or also add this object as a child of multiple objects or + * arrays. Any ownerships you acquired but did not transfer must be released + * through json_object_put. + * + * @returns a json_object of type json_type_object + */ +extern struct json_object* json_object_new_object(void); + +/** Get the hashtable of a json_object of type json_type_object + * @param obj the json_object instance + * @returns a linkhash + */ +extern struct lh_table* json_object_get_object(struct json_object *obj); + +/** Get the size of an object in terms of the number of fields it has. + * @param obj the json_object whose length to return + */ +extern int json_object_object_length(struct json_object* obj); + +/** Add an object field to a json_object of type json_type_object + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object, independent of the lifetime of obj, you must wrap the + * passed object with json_object_get. + * + * Upon calling this, the ownership of val transfers to obj. Thus you must + * make sure that you do in fact have ownership over this object. For instance, + * json_object_new_object will give you ownership until you transfer it, + * whereas json_object_object_get does not. + * + * @param obj the json_object instance + * @param key the object field name (a private copy will be duplicated) + * @param val a json_object or NULL member to associate with the given field + */ +extern void json_object_object_add(struct json_object* obj, const char *key, + struct json_object *val); + +/** Get the json_object associate with a given object field + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of the returned value is retained + * by obj (do not do json_object_put unless you have done a json_object_get). + * If you delete the value from obj (json_object_object_del) and wish to access + * the returned reference afterwards, make sure you have first gotten shared + * ownership through json_object_get (& don't forget to do a json_object_put + * or transfer ownership to prevent a memory leak). + * + * @param obj the json_object instance + * @param key the object field name + * @returns the json_object associated with the given field name + * @deprecated Please use json_object_object_get_ex + */ +extern struct json_object* json_object_object_get(struct json_object* obj, + const char *key); + +/** Get the json_object associated with a given object field. + * + * This returns true if the key is found, false in all other cases (including + * if obj isn't a json_type_object). + * + * *No* reference counts will be changed. There is no need to manually adjust + * reference counts through the json_object_put/json_object_get methods unless + * you need to have the child (value) reference maintain a different lifetime + * than the owning parent (obj). Ownership of value is retained by obj. + * + * @param obj the json_object instance + * @param key the object field name + * @param value a pointer where to store a reference to the json_object + * associated with the given field name. + * + * It is safe to pass a NULL value. + * @returns whether or not the key exists + */ +extern json_bool json_object_object_get_ex(struct json_object* obj, + const char *key, + struct json_object **value); + +/** Delete the given json_object field + * + * The reference count will be decremented for the deleted object. If there + * are no more owners of the value represented by this key, then the value is + * freed. Otherwise, the reference to the value will remain in memory. + * + * @param obj the json_object instance + * @param key the object field name + */ +extern void json_object_object_del(struct json_object* obj, const char *key); + +/** + * Iterate through all keys and values of an object. + * + * Adding keys to the object while iterating is NOT allowed. + * + * Deleting an existing key, or replacing an existing key with a + * new value IS allowed. + * + * @param obj the json_object instance + * @param key the local name for the char* key variable defined in the body + * @param val the local name for the json_object* object variable defined in + * the body + */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +# define json_object_object_foreach(obj,key,val) \ + char *key; \ + struct json_object *val __attribute__((__unused__)); \ + for(struct lh_entry *entry ## key = json_object_get_object(obj)->head, *entry_next ## key = NULL; \ + ({ if(entry ## key) { \ + key = (char*)entry ## key->k; \ + val = (struct json_object*)entry ## key->v; \ + entry_next ## key = entry ## key->next; \ + } ; entry ## key; }); \ + entry ## key = entry_next ## key ) + +#else /* ANSI C or MSC */ + +# define json_object_object_foreach(obj,key,val) \ + char *key;\ + struct json_object *val; \ + struct lh_entry *entry ## key; \ + struct lh_entry *entry_next ## key = NULL; \ + for(entry ## key = json_object_get_object(obj)->head; \ + (entry ## key ? ( \ + key = (char*)entry ## key->k, \ + val = (struct json_object*)entry ## key->v, \ + entry_next ## key = entry ## key->next, \ + entry ## key) : 0); \ + entry ## key = entry_next ## key) + +#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && __STDC_VERSION__ >= 199901L */ + +/** Iterate through all keys and values of an object (ANSI C Safe) + * @param obj the json_object instance + * @param iter the object iterator + */ +#define json_object_object_foreachC(obj,iter) \ + for(iter.entry = json_object_get_object(obj)->head; (iter.entry ? (iter.key = (char*)iter.entry->k, iter.val = (struct json_object*)iter.entry->v, iter.entry) : 0); iter.entry = iter.entry->next) + +/* Array type methods */ + +/** Create a new empty json_object of type json_type_array + * @returns a json_object of type json_type_array + */ +extern struct json_object* json_object_new_array(void); + +/** Get the arraylist of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an arraylist + */ +extern struct array_list* json_object_get_array(struct json_object *obj); + +/** Get the length of a json_object of type json_type_array + * @param obj the json_object instance + * @returns an int + */ +extern int json_object_array_length(struct json_object *obj); + +/** Sorts the elements of jso of type json_type_array +* +* Pointers to the json_object pointers will be passed as the two arguments +* to @sort_fn +* +* @param obj the json_object instance +* @param sort_fn a sorting function +*/ +extern void json_object_array_sort(struct json_object *jso, int(*sort_fn)(const void *, const void *)); + +/** Add an element to the end of a json_object of type json_type_array + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * @param obj the json_object instance + * @param val the json_object to be added + */ +extern int json_object_array_add(struct json_object *obj, + struct json_object *val); + +/** Insert or replace an element at a specified index in an array (a json_object of type json_type_array) + * + * The reference count will *not* be incremented. This is to make adding + * fields to objects in code more compact. If you want to retain a reference + * to an added object you must wrap the passed object with json_object_get + * + * The reference count of a replaced object will be decremented. + * + * The array size will be automatically be expanded to the size of the + * index if the index is larger than the current size. + * + * @param obj the json_object instance + * @param idx the index to insert the element at + * @param val the json_object to be added + */ +extern int json_object_array_put_idx(struct json_object *obj, int idx, + struct json_object *val); + +/** Get the element at specificed index of the array (a json_object of type json_type_array) + * @param obj the json_object instance + * @param idx the index to get the element at + * @returns the json_object at the specified index (or NULL) + */ +extern struct json_object* json_object_array_get_idx(struct json_object *obj, + int idx); + +/* json_bool type methods */ + +/** Create a new empty json_object of type json_type_boolean + * @param b a json_bool TRUE or FALSE (0 or 1) + * @returns a json_object of type json_type_boolean + */ +extern struct json_object* json_object_new_boolean(json_bool b); + +/** Get the json_bool value of a json_object + * + * The type is coerced to a json_bool if the passed object is not a json_bool. + * integer and double objects will return FALSE if there value is zero + * or TRUE otherwise. If the passed object is a string it will return + * TRUE if it has a non zero length. If any other object type is passed + * TRUE will be returned if the object is not NULL. + * + * @param obj the json_object instance + * @returns a json_bool + */ +extern json_bool json_object_get_boolean(struct json_object *obj); + + +/* int type methods */ + +/** Create a new empty json_object of type json_type_int + * Note that values are stored as 64-bit values internally. + * To ensure the full range is maintained, use json_object_new_int64 instead. + * @param i the integer + * @returns a json_object of type json_type_int + */ +extern struct json_object* json_object_new_int(int32_t i); + + +/** Create a new empty json_object of type json_type_int + * @param i the integer + * @returns a json_object of type json_type_int + */ +extern struct json_object* json_object_new_int64(int64_t i); + + +/** Get the int value of a json_object + * + * The type is coerced to a int if the passed object is not a int. + * double objects will return their integer conversion. Strings will be + * parsed as an integer. If no conversion exists then 0 is returned + * and errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * Note that integers are stored internally as 64-bit values. + * If the value of too big or too small to fit into 32-bit, INT32_MAX or + * INT32_MIN are returned, respectively. + * + * @param obj the json_object instance + * @returns an int + */ +extern int32_t json_object_get_int(struct json_object *obj); + +/** Get the int value of a json_object + * + * The type is coerced to a int64 if the passed object is not a int64. + * double objects will return their int64 conversion. Strings will be + * parsed as an int64. If no conversion exists then 0 is returned. + * + * NOTE: Set errno to 0 directly before a call to this function to determine + * whether or not conversion was successful (it does not clear the value for + * you). + * + * @param obj the json_object instance + * @returns an int64 + */ +extern int64_t json_object_get_int64(struct json_object *obj); + + +/* double type methods */ + +/** Create a new empty json_object of type json_type_double + * @param d the double + * @returns a json_object of type json_type_double + */ +extern struct json_object* json_object_new_double(double d); + +/** Get the double floating point value of a json_object + * + * The type is coerced to a double if the passed object is not a double. + * integer objects will return their double conversion. Strings will be + * parsed as a double. If no conversion exists then 0.0 is returned and + * errno is set to EINVAL. null is equivalent to 0 (no error values set) + * + * If the value is too big to fit in a double, then the value is set to + * the closest infinity with errno set to ERANGE. If strings cannot be + * converted to their double value, then EINVAL is set & NaN is returned. + * + * Arrays of length 0 are interpreted as 0 (with no error flags set). + * Arrays of length 1 are effectively cast to the equivalent object and + * converted using the above rules. All other arrays set the error to + * EINVAL & return NaN. + * + * NOTE: Set errno to 0 directly before a call to this function to + * determine whether or not conversion was successful (it does not clear + * the value for you). + * + * @param obj the json_object instance + * @returns a double floating point number + */ +extern double json_object_get_double(struct json_object *obj); + + +/* string type methods */ + +/** Create a new empty json_object of type json_type_string + * + * A copy of the string is made and the memory is managed by the json_object + * + * @param s the string + * @returns a json_object of type json_type_string + */ +extern struct json_object* json_object_new_string(const char *s); + +extern struct json_object* json_object_new_string_len(const char *s, int len); + +/** Get the string value of a json_object + * + * If the passed object is not of type json_type_string then the JSON + * representation of the object is returned. + * + * The returned string memory is managed by the json_object and will + * be freed when the reference count of the json_object drops to zero. + * + * @param obj the json_object instance + * @returns a string + */ +extern const char* json_object_get_string(struct json_object *obj); + +/** Get the string length of a json_object + * + * If the passed object is not of type json_type_string then zero + * will be returned. + * + * @param obj the json_object instance + * @returns int + */ +extern int json_object_get_string_len(struct json_object *obj); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3P/json/json_object_iterator.c b/3P/json/json_object_iterator.c new file mode 100644 index 00000000..7066649c --- /dev/null +++ b/3P/json/json_object_iterator.c @@ -0,0 +1,168 @@ +/** +******************************************************************************* +* @file json_object_iterator.c +* +* Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. +* +* This library is free software; you can redistribute it and/or modify +* it under the terms of the MIT license. See COPYING for details. +* +* @brief json-c forces clients to use its private data +* structures for JSON Object iteration. This API +* implementation corrects that by abstracting the +* private json-c details. +* +******************************************************************************* +*/ + +#include + +#include "json.h" +#include "json_object_private.h" + +#include "json_object_iterator.h" + +/** + * How It Works + * + * For each JSON Object, json-c maintains a linked list of zero + * or more lh_entry (link-hash entry) structures inside the + * Object's link-hash table (lh_table). + * + * Each lh_entry structure on the JSON Object's linked list + * represents a single name/value pair. The "next" field of the + * last lh_entry in the list is set to NULL, which terminates + * the list. + * + * We represent a valid iterator that refers to an actual + * name/value pair via a pointer to the pair's lh_entry + * structure set as the iterator's opaque_ field. + * + * We follow json-c's current pair list representation by + * representing a valid "end" iterator (one that refers past the + * last pair) with a NULL value in the iterator's opaque_ field. + * + * A JSON Object without any pairs in it will have the "head" + * field of its lh_table structure set to NULL. For such an + * object, json_object_iter_begin will return an iterator with + * the opaque_ field set to NULL, which is equivalent to the + * "end" iterator. + * + * When iterating, we simply update the iterator's opaque_ field + * to point to the next lh_entry structure in the linked list. + * opaque_ will become NULL once we iterate past the last pair + * in the list, which makes the iterator equivalent to the "end" + * iterator. + */ + +/// Our current representation of the "end" iterator; +/// +/// @note May not always be NULL +static const void* kObjectEndIterValue = NULL; + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj) +{ + struct json_object_iterator iter; + struct lh_table* pTable; + + /// @note json_object_get_object will return NULL if passed NULL + /// or a non-json_type_object instance + pTable = json_object_get_object(obj); + JASSERT(NULL != pTable); + + /// @note For a pair-less Object, head is NULL, which matches our + /// definition of the "end" iterator + iter.opaque_ = pTable->head; + return iter; +} + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj) +{ + struct json_object_iterator iter; + + JASSERT(NULL != obj); + JASSERT(json_object_is_type(obj, json_type_object)); + + iter.opaque_ = kObjectEndIterValue; + + return iter; +} + +/** + * **************************************************************************** + */ +void +json_object_iter_next(struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + iter->opaque_ = ((struct lh_entry *)iter->opaque_)->next; +} + + +/** + * **************************************************************************** + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (const char*)(((struct lh_entry *)iter->opaque_)->k); +} + + +/** + * **************************************************************************** + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter) +{ + JASSERT(NULL != iter); + JASSERT(kObjectEndIterValue != iter->opaque_); + + return (struct json_object*)(((struct lh_entry *)iter->opaque_)->v); +} + + +/** + * **************************************************************************** + */ +json_bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2) +{ + JASSERT(NULL != iter1); + JASSERT(NULL != iter2); + + return (iter1->opaque_ == iter2->opaque_); +} + + +/** + * **************************************************************************** + */ +struct json_object_iterator +json_object_iter_init_default(void) +{ + struct json_object_iterator iter; + + /** + * @note Make this a negative, invalid value, such that + * accidental access to it would likely be trapped by the + * hardware as an invalid address. + */ + iter.opaque_ = NULL; + + return iter; +} diff --git a/3P/json/json_object_iterator.h b/3P/json/json_object_iterator.h new file mode 100644 index 00000000..f6e7ca62 --- /dev/null +++ b/3P/json/json_object_iterator.h @@ -0,0 +1,239 @@ +/** +******************************************************************************* +* @file json_object_iterator.h +* +* Copyright (c) 2009-2012 Hewlett-Packard Development Company, L.P. +* +* This library is free software; you can redistribute it and/or modify +* it under the terms of the MIT license. See COPYING for details. +* +* @brief json-c forces clients to use its private data +* structures for JSON Object iteration. This API +* corrects that by abstracting the private json-c +* details. +* +* API attributes:
+* * Thread-safe: NO
+* * Reentrant: NO +* +******************************************************************************* +*/ + + +#ifndef JSON_OBJECT_ITERATOR_H +#define JSON_OBJECT_ITERATOR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Forward declaration for the opaque iterator information. + */ +struct json_object_iter_info_; + +/** + * The opaque iterator that references a name/value pair within + * a JSON Object instance or the "end" iterator value. + */ +struct json_object_iterator { + const void* opaque_; +}; + + +/** + * forward declaration of json-c's JSON value instance structure + */ +struct json_object; + + +/** + * Initializes an iterator structure to a "default" value that + * is convenient for initializing an iterator variable to a + * default state (e.g., initialization list in a class' + * constructor). + * + * @code + * struct json_object_iterator iter = json_object_iter_init_default(); + * MyClass() : iter_(json_object_iter_init_default()) + * @endcode + * + * @note The initialized value doesn't reference any specific + * pair, is considered an invalid iterator, and MUST NOT + * be passed to any json-c API that expects a valid + * iterator. + * + * @note User and internal code MUST NOT make any assumptions + * about and dependencies on the value of the "default" + * iterator value. + * + * @return json_object_iterator + */ +struct json_object_iterator +json_object_iter_init_default(void); + +/** Retrieves an iterator to the first pair of the JSON Object. + * + * @warning Any modification of the underlying pair invalidates all + * iterators to that pair. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator If the JSON Object has at + * least one pair, on return, the iterator refers + * to the first pair. If the JSON Object doesn't + * have any pairs, the returned iterator is + * equivalent to the "end" iterator for the same + * JSON Object instance. + * + * @code + * struct json_object_iterator it; + * struct json_object_iterator itEnd; + * struct json_object* obj; + * + * obj = json_tokener_parse("{'first':'george', 'age':100}"); + * it = json_object_iter_begin(obj); + * itEnd = json_object_iter_end(obj); + * + * while (!json_object_iter_equal(&it, &itEnd)) { + * printf("%s\n", + * json_object_iter_peek_name(&it)); + * json_object_iter_next(&it); + * } + * + * @endcode + */ +struct json_object_iterator +json_object_iter_begin(struct json_object* obj); + +/** Retrieves the iterator that represents the position beyond the + * last pair of the given JSON Object instance. + * + * @warning Do NOT write code that assumes that the "end" + * iterator value is NULL, even if it is so in a + * particular instance of the implementation. + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The "end" iterator and the + * equality test method, on the other hand, permit us to + * cleanly abstract pretty much any reasonable underlying + * representation without burdening the iterator + * structure with unnecessary data. + * + * @note For performance reasons, memorize the "end" iterator prior + * to any loop. + * + * @param obj JSON Object instance (MUST be of type json_object) + * + * @return json_object_iterator On return, the iterator refers + * to the "end" of the Object instance's pairs + * (i.e., NOT the last pair, but "beyond the last + * pair" value) + */ +struct json_object_iterator +json_object_iter_end(const struct json_object* obj); + +/** Returns an iterator to the next pair, if any + * + * @warning Any modification of the underlying pair + * invalidates all iterators to that pair. + * + * @param iter [IN/OUT] Pointer to iterator that references a + * name/value pair; MUST be a valid, non-end iterator. + * WARNING: bad things will happen if invalid or "end" + * iterator is passed. Upon return will contain the + * reference to the next pair if there is one; if there + * are no more pairs, will contain the "end" iterator + * value, which may be compared against the return value + * of json_object_iter_end() for the same JSON Object + * instance. + */ +void +json_object_iter_next(struct json_object_iterator* iter); + + +/** Returns a const pointer to the name of the pair referenced + * by the given iterator. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if an invalid or + * "end" iterator is passed. + * + * @return const char* Pointer to the name of the referenced + * name/value pair. The name memory belongs to the + * name/value pair, will be freed when the pair is + * deleted or modified, and MUST NOT be modified or + * freed by the user. + */ +const char* +json_object_iter_peek_name(const struct json_object_iterator* iter); + + +/** Returns a pointer to the json-c instance representing the + * value of the referenced name/value pair, without altering + * the instance's reference count. + * + * @param iter pointer to iterator that references a name/value + * pair; MUST be a valid, non-end iterator. + * + * @warning bad things will happen if invalid or + * "end" iterator is passed. + * + * @return struct json_object* Pointer to the json-c value + * instance of the referenced name/value pair; the + * value's reference count is not changed by this + * function: if you plan to hold on to this json-c node, + * take a look at json_object_get() and + * json_object_put(). IMPORTANT: json-c API represents + * the JSON Null value as a NULL json_object instance + * pointer. + */ +struct json_object* +json_object_iter_peek_value(const struct json_object_iterator* iter); + + +/** Tests two iterators for equality. Typically used to test + * for end of iteration by comparing an iterator to the + * corresponding "end" iterator (that was derived from the same + * JSON Object instance). + * + * @note The reason we do not (and MUST NOT) provide + * "json_object_iter_is_end(json_object_iterator* iter)" + * type of API is because it would limit the underlying + * representation of name/value containment (or force us + * to add additional, otherwise unnecessary, fields to + * the iterator structure). The equality test method, on + * the other hand, permits us to cleanly abstract pretty + * much any reasonable underlying representation. + * + * @param iter1 Pointer to first valid, non-NULL iterator + * @param iter2 POinter to second valid, non-NULL iterator + * + * @warning if a NULL iterator pointer or an uninitialized + * or invalid iterator, or iterators derived from + * different JSON Object instances are passed, bad things + * will happen! + * + * @return json_bool non-zero if iterators are equal (i.e., both + * reference the same name/value pair or are both at + * "end"); zero if they are not equal. + */ +json_bool +json_object_iter_equal(const struct json_object_iterator* iter1, + const struct json_object_iterator* iter2); + + +#ifdef __cplusplus +} +#endif + + +#endif // JSON_OBJECT_ITERATOR_H diff --git a/3P/json/json_object_private.h b/3P/json/json_object_private.h new file mode 100644 index 00000000..5ed791b5 --- /dev/null +++ b/3P/json/json_object_private.h @@ -0,0 +1,47 @@ +/* + * $Id: json_object_private.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_object_private_h_ +#define _json_object_private_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (json_object_private_delete_fn)(struct json_object *o); + +struct json_object +{ + enum json_type o_type; + json_object_private_delete_fn *_delete; + json_object_to_json_string_fn *_to_json_string; + int _ref_count; + struct printbuf *_pb; + union data { + json_bool c_boolean; + double c_double; + int64_t c_int64; + struct lh_table *c_object; + struct array_list *c_array; + struct { + char *str; + int len; + } c_string; + } o; + json_object_delete_fn *_user_delete; + void *_userdata; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3P/json/json_tokener.c b/3P/json/json_tokener.c new file mode 100644 index 00000000..b2b47f9d --- /dev/null +++ b/3P/json/json_tokener.c @@ -0,0 +1,792 @@ +/* + * $Id: json_tokener.c,v 1.20 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" +#include "arraylist.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +#ifdef HAVE_LOCALE_H +#include +#endif /* HAVE_LOCALE_H */ + +#if !HAVE_STRDUP && defined(_MSC_VER) + /* MSC has the version as _strdup */ +# define strdup _strdup +#elif !HAVE_STRDUP +# error You do not have strdup on your system. +#endif /* HAVE_STRDUP */ + +#if !HAVE_STRNCASECMP && defined(_MSC_VER) + /* MSC has the version as _strnicmp */ +# define strncasecmp _strnicmp +#elif !HAVE_STRNCASECMP +# error You do not have strncasecmp on your system. +#endif /* HAVE_STRNCASECMP */ + +static const char* json_null_str = "null"; +static const char* json_true_str = "true"; +static const char* json_false_str = "false"; + +// XXX after v0.10 this array will become static: +const char* json_tokener_errors[] = { + "success", + "continue", + "nesting too deep", + "unexpected end of data", + "unexpected character", + "null expected", + "boolean expected", + "number expected", + "array value separator ',' expected", + "quoted object property name expected", + "object property name separator ':' expected", + "object value separator ',' expected", + "invalid string sequence", + "expected comment", +}; + +const char *json_tokener_error_desc(enum json_tokener_error jerr) +{ + int jerr_int = (int)jerr; + if (jerr_int < 0 || jerr_int > (int)sizeof(json_tokener_errors)) + return "Unknown error, invalid json_tokener_error value passed to json_tokener_error_desc()"; + return json_tokener_errors[jerr]; +} + +enum json_tokener_error json_tokener_get_error(json_tokener *tok) +{ + return tok->err; +} + +/* Stuff for decoding unicode sequences */ +#define IS_HIGH_SURROGATE(uc) (((uc) & 0xFC00) == 0xD800) +#define IS_LOW_SURROGATE(uc) (((uc) & 0xFC00) == 0xDC00) +#define DECODE_SURROGATE_PAIR(hi,lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000) +static unsigned char utf8_replacement_char[3] = { 0xEF, 0xBF, 0xBD }; + +struct json_tokener* json_tokener_new_ex(int depth) +{ + struct json_tokener *tok; + + tok = (struct json_tokener*)calloc(1, sizeof(struct json_tokener)); + if (!tok) return NULL; + tok->stack = (struct json_tokener_srec *)calloc(depth, sizeof(struct json_tokener_srec)); + if (!tok->stack) { + free(tok); + return NULL; + } + tok->pb = printbuf_new(); + tok->max_depth = depth; + json_tokener_reset(tok); + return tok; +} + +struct json_tokener* json_tokener_new(void) +{ + return json_tokener_new_ex(JSON_TOKENER_DEFAULT_DEPTH); +} + +void json_tokener_free(struct json_tokener *tok) +{ + json_tokener_reset(tok); + if (tok->pb) printbuf_free(tok->pb); + if (tok->stack) free(tok->stack); + free(tok); +} + +static void json_tokener_reset_level(struct json_tokener *tok, int depth) +{ + tok->stack[depth].state = json_tokener_state_eatws; + tok->stack[depth].saved_state = json_tokener_state_start; + json_object_put(tok->stack[depth].current); + tok->stack[depth].current = NULL; + free(tok->stack[depth].obj_field_name); + tok->stack[depth].obj_field_name = NULL; +} + +void json_tokener_reset(struct json_tokener *tok) +{ + int i; + if (!tok) + return; + + for(i = tok->depth; i >= 0; i--) + json_tokener_reset_level(tok, i); + tok->depth = 0; + tok->err = json_tokener_success; +} + +struct json_object* json_tokener_parse(const char *str) +{ + enum json_tokener_error jerr_ignored; + struct json_object* obj; + obj = json_tokener_parse_verbose(str, &jerr_ignored); + return obj; +} + +struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error) +{ + struct json_tokener* tok; + struct json_object* obj; + + tok = json_tokener_new(); + if (!tok) + return NULL; + obj = json_tokener_parse_ex(tok, str, -1); + *error = tok->err; + if(tok->err != json_tokener_success) { + if (obj != NULL) + json_object_put(obj); + obj = NULL; + } + + json_tokener_free(tok); + return obj; +} + + +#if !HAVE_STRNDUP +/* CAW: compliant version of strndup() */ +char* strndup(const char* str, size_t n) +{ + if(str) { + size_t len = strlen(str); + size_t nn = json_min(len,n); + char* s = (char*)malloc(sizeof(char) * (nn + 1)); + + if(s) { + memcpy(s, str, nn); + s[nn] = '\0'; + } + + return s; + } + + return NULL; +} +#endif + + +#define state tok->stack[tok->depth].state +#define saved_state tok->stack[tok->depth].saved_state +#define current tok->stack[tok->depth].current +#define obj_field_name tok->stack[tok->depth].obj_field_name + +/* Optimization: + * json_tokener_parse_ex() consumed a lot of CPU in its main loop, + * iterating character-by character. A large performance boost is + * achieved by using tighter loops to locally handle units such as + * comments and strings. Loops that handle an entire token within + * their scope also gather entire strings and pass them to + * printbuf_memappend() in a single call, rather than calling + * printbuf_memappend() one char at a time. + * + * PEEK_CHAR() and ADVANCE_CHAR() macros are used for code that is + * common to both the main loop and the tighter loops. + */ + +/* PEEK_CHAR(dest, tok) macro: + * Peeks at the current char and stores it in dest. + * Returns 1 on success, sets tok->err and returns 0 if no more chars. + * Implicit inputs: str, len vars + */ +#define PEEK_CHAR(dest, tok) \ + (((tok)->char_offset == len) ? \ + (((tok)->depth == 0 && state == json_tokener_state_eatws && saved_state == json_tokener_state_finish) ? \ + (((tok)->err = json_tokener_success), 0) \ + : \ + (((tok)->err = json_tokener_continue), 0) \ + ) : \ + (((dest) = *str), 1) \ + ) + +/* ADVANCE_CHAR() macro: + * Incrementes str & tok->char_offset. + * For convenience of existing conditionals, returns the old value of c (0 on eof) + * Implicit inputs: c var + */ +#define ADVANCE_CHAR(str, tok) \ + ( ++(str), ((tok)->char_offset)++, c) + + +/* End optimization macro defs */ + + +struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + const char *str, int len) +{ + struct json_object *obj = NULL; + char c = '\1'; +#ifdef HAVE_SETLOCALE + char *oldlocale=NULL, *tmplocale; + + tmplocale = setlocale(LC_NUMERIC, NULL); + if (tmplocale) oldlocale = strdup(tmplocale); + setlocale(LC_NUMERIC, "C"); +#endif + + tok->char_offset = 0; + tok->err = json_tokener_success; + + while (PEEK_CHAR(c, tok)) { + + redo_char: + switch(state) { + + case json_tokener_state_eatws: + /* Advance until we change state */ + while (isspace((int)c)) { + if ((!ADVANCE_CHAR(str, tok)) || (!PEEK_CHAR(c, tok))) + goto out; + } + if(c == '/') { + printbuf_reset(tok->pb); + printbuf_memappend_fast(tok->pb, &c, 1); + state = json_tokener_state_comment_start; + } else { + state = saved_state; + goto redo_char; + } + break; + + case json_tokener_state_start: + switch(c) { + case '{': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_object_field_start; + current = json_object_new_object(); + break; + case '[': + state = json_tokener_state_eatws; + saved_state = json_tokener_state_array; + current = json_object_new_array(); + break; + case 'N': + case 'n': + state = json_tokener_state_null; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; + case '"': + case '\'': + state = json_tokener_state_string; + printbuf_reset(tok->pb); + tok->quote_char = c; + break; + case 'T': + case 't': + case 'F': + case 'f': + state = json_tokener_state_boolean; + printbuf_reset(tok->pb); + tok->st_pos = 0; + goto redo_char; +#if defined(__GNUC__) + case '0' ... '9': +#else + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': +#endif + case '-': + state = json_tokener_state_number; + printbuf_reset(tok->pb); + tok->is_double = 0; + goto redo_char; + default: + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + break; + + case json_tokener_state_finish: + if(tok->depth == 0) goto out; + obj = json_object_get(current); + json_tokener_reset_level(tok, tok->depth); + tok->depth--; + goto redo_char; + + case json_tokener_state_null: + printbuf_memappend_fast(tok->pb, &c, 1); + if(strncasecmp(json_null_str, tok->pb->buf, + json_min(tok->st_pos+1, (int)strlen(json_null_str))) == 0) { + if(tok->st_pos == (int)strlen(json_null_str)) { + current = NULL; + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_null; + goto out; + } + tok->st_pos++; + break; + + case json_tokener_state_comment_start: + if(c == '*') { + state = json_tokener_state_comment; + } else if(c == '/') { + state = json_tokener_state_comment_eol; + } else { + tok->err = json_tokener_error_parse_comment; + goto out; + } + printbuf_memappend_fast(tok->pb, &c, 1); + break; + + case json_tokener_state_comment: + { + /* Advance until we change state */ + const char *case_start = str; + while(c != '*') { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, 1+str-case_start); + state = json_tokener_state_comment_end; + } + break; + + case json_tokener_state_comment_eol: + { + /* Advance until we change state */ + const char *case_start = str; + while(c != '\n') { + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } + break; + + case json_tokener_state_comment_end: + printbuf_memappend_fast(tok->pb, &c, 1); + if(c == '/') { + MC_DEBUG("json_tokener_comment: %s\n", tok->pb->buf); + state = json_tokener_state_eatws; + } else { + state = json_tokener_state_comment; + } + break; + + case json_tokener_state_string: + { + /* Advance until we change state */ + const char *case_start = str; + while(1) { + if(c == tok->quote_char) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + current = json_object_new_string_len(tok->pb->buf, tok->pb->bpos); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + break; + } else if(c == '\\') { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + saved_state = json_tokener_state_string; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + } + break; + + case json_tokener_state_string_escape: + switch(c) { + case '"': + case '\\': + case '/': + printbuf_memappend_fast(tok->pb, &c, 1); + state = saved_state; + break; + case 'b': + case 'n': + case 'r': + case 't': + case 'f': + if(c == 'b') printbuf_memappend_fast(tok->pb, "\b", 1); + else if(c == 'n') printbuf_memappend_fast(tok->pb, "\n", 1); + else if(c == 'r') printbuf_memappend_fast(tok->pb, "\r", 1); + else if(c == 't') printbuf_memappend_fast(tok->pb, "\t", 1); + else if(c == 'f') printbuf_memappend_fast(tok->pb, "\f", 1); + state = saved_state; + break; + case 'u': + tok->ucs_char = 0; + tok->st_pos = 0; + state = json_tokener_state_escape_unicode; + break; + default: + tok->err = json_tokener_error_parse_string; + goto out; + } + break; + + case json_tokener_state_escape_unicode: + { + unsigned int got_hi_surrogate = 0; + + /* Handle a 4-byte sequence, or two sequences if a surrogate pair */ + while(1) { + if(strchr(json_hex_chars, c)) { + tok->ucs_char += ((unsigned int)hexdigit(c) << ((3-tok->st_pos++)*4)); + if(tok->st_pos == 4) { + unsigned char unescaped_utf[4]; + + if (got_hi_surrogate) { + if (IS_LOW_SURROGATE(tok->ucs_char)) { + /* Recalculate the ucs_char, then fall thru to process normally */ + tok->ucs_char = DECODE_SURROGATE_PAIR(got_hi_surrogate, tok->ucs_char); + } else { + /* Hi surrogate was not followed by a low surrogate */ + /* Replace the hi and process the rest normally */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + got_hi_surrogate = 0; + } + + if (tok->ucs_char < 0x80) { + unescaped_utf[0] = tok->ucs_char; + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 1); + } else if (tok->ucs_char < 0x800) { + unescaped_utf[0] = 0xc0 | (tok->ucs_char >> 6); + unescaped_utf[1] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 2); + } else if (IS_HIGH_SURROGATE(tok->ucs_char)) { + /* Got a high surrogate. Remember it and look for the + * the beginning of another sequence, which should be the + * low surrogate. + */ + got_hi_surrogate = tok->ucs_char; + /* Not at end, and the next two chars should be "\u" */ + if ((tok->char_offset+1 != len) && + (tok->char_offset+2 != len) && + (str[1] == '\\') && + (str[2] == 'u')) + { + /* Advance through the 16 bit surrogate, and move on to the + * next sequence. The next step is to process the following + * characters. + */ + if( !ADVANCE_CHAR(str, tok) || !ADVANCE_CHAR(str, tok) ) { + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + /* Advance to the first char of the next sequence and + * continue processing with the next sequence. + */ + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + goto out; + } + tok->ucs_char = 0; + tok->st_pos = 0; + continue; /* other json_tokener_state_escape_unicode */ + } else { + /* Got a high surrogate without another sequence following + * it. Put a replacement char in for the hi surrogate + * and pretend we finished. + */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + } else if (IS_LOW_SURROGATE(tok->ucs_char)) { + /* Got a low surrogate not preceded by a high */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } else if (tok->ucs_char < 0x10000) { + unescaped_utf[0] = 0xe0 | (tok->ucs_char >> 12); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[2] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 3); + } else if (tok->ucs_char < 0x110000) { + unescaped_utf[0] = 0xf0 | ((tok->ucs_char >> 18) & 0x07); + unescaped_utf[1] = 0x80 | ((tok->ucs_char >> 12) & 0x3f); + unescaped_utf[2] = 0x80 | ((tok->ucs_char >> 6) & 0x3f); + unescaped_utf[3] = 0x80 | (tok->ucs_char & 0x3f); + printbuf_memappend_fast(tok->pb, (char*)unescaped_utf, 4); + } else { + /* Don't know what we got--insert the replacement char */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + } + state = saved_state; + break; + } + } else { + tok->err = json_tokener_error_parse_string; + goto out; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + if (got_hi_surrogate) /* Clean up any pending chars */ + printbuf_memappend_fast(tok->pb, (char*)utf8_replacement_char, 3); + goto out; + } + } + } + break; + + case json_tokener_state_boolean: + printbuf_memappend_fast(tok->pb, &c, 1); + if(strncasecmp(json_true_str, tok->pb->buf, + json_min(tok->st_pos+1, (int)strlen(json_true_str))) == 0) { + if(tok->st_pos == (int)strlen(json_true_str)) { + current = json_object_new_boolean(1); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else if(strncasecmp(json_false_str, tok->pb->buf, + json_min(tok->st_pos+1, (int)strlen(json_false_str))) == 0) { + if(tok->st_pos == (int)strlen(json_false_str)) { + current = json_object_new_boolean(0); + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + } else { + tok->err = json_tokener_error_parse_boolean; + goto out; + } + tok->st_pos++; + break; + + case json_tokener_state_number: + { + /* Advance until we change state */ + const char *case_start = str; + int case_len=0; + while(c && strchr(json_number_chars, c)) { + ++case_len; + if(c == '.' || c == 'e' || c == 'E') + tok->is_double = 1; + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, case_len); + goto out; + } + } + if (case_len>0) + printbuf_memappend_fast(tok->pb, case_start, case_len); + } + { + int64_t num64; + double numd; + if (!tok->is_double && json_parse_int64(tok->pb->buf, &num64) == 0) { + current = json_object_new_int64(num64); + } else if(tok->is_double && json_parse_double(tok->pb->buf, &numd) == 0) { + current = json_object_new_double(numd); + } else { + tok->err = json_tokener_error_parse_number; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + goto redo_char; + } + break; + + case json_tokener_state_array_after_sep: + case json_tokener_state_array: + if(c == ']') { + if (state == json_tokener_state_array_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else { + if(tok->depth >= tok->max_depth-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_array_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + } + break; + + case json_tokener_state_array_add: + json_object_array_add(current, obj); + saved_state = json_tokener_state_array_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_array_sep: + if(c == ']') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_array_after_sep; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_array; + goto out; + } + break; + + case json_tokener_state_object_field_start: + case json_tokener_state_object_field_start_after_sep: + if(c == '}') { + if (state == json_tokener_state_object_field_start_after_sep && + (tok->flags & JSON_TOKENER_STRICT)) + { + tok->err = json_tokener_error_parse_unexpected; + goto out; + } + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if (c == '"' || c == '\'') { + tok->quote_char = c; + printbuf_reset(tok->pb); + state = json_tokener_state_object_field; + } else { + tok->err = json_tokener_error_parse_object_key_name; + goto out; + } + break; + + case json_tokener_state_object_field: + { + /* Advance until we change state */ + const char *case_start = str; + while(1) { + if(c == tok->quote_char) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + obj_field_name = strdup(tok->pb->buf); + saved_state = json_tokener_state_object_field_end; + state = json_tokener_state_eatws; + break; + } else if(c == '\\') { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + saved_state = json_tokener_state_object_field; + state = json_tokener_state_string_escape; + break; + } + if (!ADVANCE_CHAR(str, tok) || !PEEK_CHAR(c, tok)) { + printbuf_memappend_fast(tok->pb, case_start, str-case_start); + goto out; + } + } + } + break; + + case json_tokener_state_object_field_end: + if(c == ':') { + saved_state = json_tokener_state_object_value; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_key_sep; + goto out; + } + break; + + case json_tokener_state_object_value: + if(tok->depth >= tok->max_depth-1) { + tok->err = json_tokener_error_depth; + goto out; + } + state = json_tokener_state_object_value_add; + tok->depth++; + json_tokener_reset_level(tok, tok->depth); + goto redo_char; + + case json_tokener_state_object_value_add: + json_object_object_add(current, obj_field_name, obj); + free(obj_field_name); + obj_field_name = NULL; + saved_state = json_tokener_state_object_sep; + state = json_tokener_state_eatws; + goto redo_char; + + case json_tokener_state_object_sep: + if(c == '}') { + saved_state = json_tokener_state_finish; + state = json_tokener_state_eatws; + } else if(c == ',') { + saved_state = json_tokener_state_object_field_start_after_sep; + state = json_tokener_state_eatws; + } else { + tok->err = json_tokener_error_parse_object_value_sep; + goto out; + } + break; + + } + if (!ADVANCE_CHAR(str, tok)) + goto out; + } /* while(POP_CHAR) */ + + out: + if (!c) { /* We hit an eof char (0) */ + if(state != json_tokener_state_finish && + saved_state != json_tokener_state_finish) + tok->err = json_tokener_error_parse_eof; + } + +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, oldlocale); + if (oldlocale) free(oldlocale); +#endif + + if (tok->err == json_tokener_success) + { + json_object *ret = json_object_get(current); + int ii; + + /* Partially reset, so we parse additional objects on subsequent calls. */ + for(ii = tok->depth; ii >= 0; ii--) + json_tokener_reset_level(tok, ii); + return ret; + } + + MC_DEBUG("json_tokener_parse_ex: error %s at offset %d\n", + json_tokener_errors[tok->err], tok->char_offset); + return NULL; +} + +void json_tokener_set_flags(struct json_tokener *tok, int flags) +{ + tok->flags = flags; +} diff --git a/3P/json/json_tokener.h b/3P/json/json_tokener.h new file mode 100644 index 00000000..08e5ff7f --- /dev/null +++ b/3P/json/json_tokener.h @@ -0,0 +1,209 @@ +/* + * $Id: json_tokener.h,v 1.10 2006/07/25 03:24:50 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_tokener_h_ +#define _json_tokener_h_ + +#include +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum json_tokener_error { + json_tokener_success, + json_tokener_continue, + json_tokener_error_depth, + json_tokener_error_parse_eof, + json_tokener_error_parse_unexpected, + json_tokener_error_parse_null, + json_tokener_error_parse_boolean, + json_tokener_error_parse_number, + json_tokener_error_parse_array, + json_tokener_error_parse_object_key_name, + json_tokener_error_parse_object_key_sep, + json_tokener_error_parse_object_value_sep, + json_tokener_error_parse_string, + json_tokener_error_parse_comment +}; + +enum json_tokener_state { + json_tokener_state_eatws, + json_tokener_state_start, + json_tokener_state_finish, + json_tokener_state_null, + json_tokener_state_comment_start, + json_tokener_state_comment, + json_tokener_state_comment_eol, + json_tokener_state_comment_end, + json_tokener_state_string, + json_tokener_state_string_escape, + json_tokener_state_escape_unicode, + json_tokener_state_boolean, + json_tokener_state_number, + json_tokener_state_array, + json_tokener_state_array_add, + json_tokener_state_array_sep, + json_tokener_state_object_field_start, + json_tokener_state_object_field, + json_tokener_state_object_field_end, + json_tokener_state_object_value, + json_tokener_state_object_value_add, + json_tokener_state_object_sep, + json_tokener_state_array_after_sep, + json_tokener_state_object_field_start_after_sep +}; + +struct json_tokener_srec +{ + enum json_tokener_state state, saved_state; + struct json_object *obj; + struct json_object *current; + char *obj_field_name; +}; + +#define JSON_TOKENER_DEFAULT_DEPTH 32 + +struct json_tokener +{ + char *str; + struct printbuf *pb; + int max_depth, depth, is_double, st_pos, char_offset; + enum json_tokener_error err; + unsigned int ucs_char; + char quote_char; + struct json_tokener_srec *stack; + int flags; +}; + +/** + * Be strict when parsing JSON input. Use caution with + * this flag as what is considered valid may become more + * restrictive from one release to the next, causing your + * code to fail on previously working input. + * + * This flag is not set by default. + * + * @see json_tokener_set_flags() + */ +#define JSON_TOKENER_STRICT 0x01 + +/** + * Given an error previously returned by json_tokener_get_error(), + * return a human readable description of the error. + * + * @return a generic error message is returned if an invalid error value is provided. + */ +const char *json_tokener_error_desc(enum json_tokener_error jerr); + +/** + * @b XXX do not use json_tokener_errors directly. + * After v0.10 this will be removed. + * + * See json_tokener_error_desc() instead. + */ +extern const char* json_tokener_errors[]; + +/** + * Retrieve the error caused by the last call to json_tokener_parse_ex(), + * or json_tokener_success if there is no error. + * + * When parsing a JSON string in pieces, if the tokener is in the middle + * of parsing this will return json_tokener_continue. + * + * See also json_tokener_error_desc(). + */ +enum json_tokener_error json_tokener_get_error(struct json_tokener *tok); + +extern struct json_tokener* json_tokener_new(void); +extern struct json_tokener* json_tokener_new_ex(int depth); +extern void json_tokener_free(struct json_tokener *tok); +extern void json_tokener_reset(struct json_tokener *tok); +extern struct json_object* json_tokener_parse(const char *str); +extern struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error); + +/** + * Set flags that control how parsing will be done. + */ +extern void json_tokener_set_flags(struct json_tokener *tok, int flags); + +/** + * Parse a string and return a non-NULL json_object if a valid JSON value + * is found. The string does not need to be a JSON object or array; + * it can also be a string, number or boolean value. + * + * A partial JSON string can be parsed. If the parsing is incomplete, + * NULL will be returned and json_tokener_get_error() will be return + * json_tokener_continue. + * json_tokener_parse_ex() can then be called with additional bytes in str + * to continue the parsing. + * + * If json_tokener_parse_ex() returns NULL and the error anything other than + * json_tokener_continue, a fatal error has occurred and parsing must be + * halted. Then tok object must not be re-used until json_tokener_reset() is + * called. + * + * When a valid JSON value is parsed, a non-NULL json_object will be + * returned. Also, json_tokener_get_error() will return json_tokener_success. + * Be sure to check the type with json_object_is_type() or + * json_object_get_type() before using the object. + * + * @b XXX this shouldn't use internal fields: + * Trailing characters after the parsed value do not automatically cause an + * error. It is up to the caller to decide whether to treat this as an + * error or to handle the additional characters, perhaps by parsing another + * json value starting from that point. + * + * Extra characters can be detected by comparing the tok->char_offset against + * the length of the last len parameter passed in. + * + * The tokener does \b not maintain an internal buffer so the caller is + * responsible for calling json_tokener_parse_ex with an appropriate str + * parameter starting with the extra characters. + * + * Example: + * @code +json_object *jobj = NULL; +const char *mystring = NULL; +int stringlen = 0; +enum json_tokener_error jerr; +do { + mystring = ... // get JSON string, e.g. read from file, etc... + stringlen = strlen(mystring); + jobj = json_tokener_parse_ex(tok, mystring, stringlen); +} while ((jerr = json_tokener_get_error(tok)) == json_tokener_continue); +if (jerr != json_tokener_success) +{ + fprintf(stderr, "Error: %s\n", json_tokener_error_desc(jerr)); + // Handle errors, as appropriate for your application. +} +if (tok->char_offset < stringlen) // XXX shouldn't access internal fields +{ + // Handle extra characters after parsed object as desired. + // e.g. issue an error, parse another object from that point, etc... +} +// Success, use jobj here. + +@endcode + * + * @param tok a json_tokener previously allocated with json_tokener_new() + * @param str an string with any valid JSON expression, or portion of. This does not need to be null terminated. + * @param len the length of str + */ +extern struct json_object* json_tokener_parse_ex(struct json_tokener *tok, + const char *str, int len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3P/json/json_util.c b/3P/json/json_util.c new file mode 100644 index 00000000..111fa01a --- /dev/null +++ b/3P/json/json_util.c @@ -0,0 +1,299 @@ +/* + * $Id: json_util.c,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include "config.h" +#undef realloc + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef HAVE_SYS_STAT_H +#include +#endif /* HAVE_SYS_STAT_H */ + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ + +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ + +#ifdef WIN32 +# define WIN32_LEAN_AND_MEAN +# include +# include +#endif /* defined(WIN32) */ + +#if !defined(HAVE_OPEN) && defined(WIN32) +# define open _open +#endif + +#if !defined(HAVE_SNPRINTF) && defined(_MSC_VER) + /* MSC has the version as _snprintf */ +# define snprintf _snprintf +#elif !defined(HAVE_SNPRINTF) +# error You do not have snprintf on your system. +#endif /* HAVE_SNPRINTF */ + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +static int sscanf_is_broken = 0; +static int sscanf_is_broken_testdone = 0; +static void sscanf_is_broken_test(void); + +struct json_object* json_object_from_file(const char *filename) +{ + struct printbuf *pb; + struct json_object *obj; + char buf[JSON_FILE_BUF_SIZE]; + int fd, ret; + + if((fd = open(filename, O_RDONLY)) < 0) { + MC_ERROR("json_object_from_file: error reading file %s: %s\n", + filename, strerror(errno)); + return NULL; + } + if(!(pb = printbuf_new())) { + close(fd); + MC_ERROR("json_object_from_file: printbuf_new failed\n"); + return NULL; + } + while((ret = read(fd, buf, JSON_FILE_BUF_SIZE)) > 0) { + printbuf_memappend(pb, buf, ret); + } + close(fd); + if(ret < 0) { + MC_ABORT("json_object_from_file: error reading file %s: %s\n", + filename, strerror(errno)); + printbuf_free(pb); + return NULL; + } + obj = json_tokener_parse(pb->buf); + printbuf_free(pb); + return obj; +} + +/* extended "format and write to file" function */ + +int json_object_to_file_ext(char *filename, struct json_object *obj, int flags) +{ + const char *json_str; + int fd, ret; + unsigned int wpos, wsize; + + if(!obj) { + MC_ERROR("json_object_to_file: object is null\n"); + return -1; + } + + if((fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0) { + MC_ERROR("json_object_to_file: error opening file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + if(!(json_str = json_object_to_json_string_ext(obj,flags))) { + close(fd); + return -1; + } + + wsize = (unsigned int)(strlen(json_str) & UINT_MAX); /* CAW: probably unnecessary, but the most 64bit safe */ + wpos = 0; + while(wpos < wsize) { + if((ret = write(fd, json_str + wpos, wsize-wpos)) < 0) { + close(fd); + MC_ERROR("json_object_to_file: error writing file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + /* because of the above check for ret < 0, we can safely cast and add */ + wpos += (unsigned int)ret; + } + + close(fd); + return 0; +} + +// backwards compatible "format and write to file" function + +int json_object_to_file(char *filename, struct json_object *obj) +{ + return json_object_to_file_ext(filename, obj, JSON_C_TO_STRING_PLAIN); +} + +int json_parse_double(const char *buf, double *retval) +{ + return (sscanf(buf, "%lf", retval)==1 ? 0 : 1); +} + +/* + * Not all implementations of sscanf actually work properly. + * Check whether the one we're currently using does, and if + * it's broken, enable the workaround code. + */ +static void sscanf_is_broken_test() +{ + int64_t num64; + + (void)sscanf(" -01234567890123456789012345", "%" SCNd64, &num64); + int ret_errno = errno; + int is_int64_min = (num64 == INT64_MIN); + + (void)sscanf(" 01234567890123456789012345", "%" SCNd64, &num64); + int ret_errno2 = errno; + int is_int64_max = (num64 == INT64_MAX); + + if (ret_errno != ERANGE || !is_int64_min || + ret_errno2 != ERANGE || !is_int64_max) + { + MC_DEBUG("sscanf_is_broken_test failed, enabling workaround code\n"); + sscanf_is_broken = 1; + } +} + +int json_parse_int64(const char *buf, int64_t *retval) +{ + int64_t num64; + const char *buf_sig_digits; + int orig_has_neg; + int saved_errno; + + if (!sscanf_is_broken_testdone) + { + sscanf_is_broken_test(); + sscanf_is_broken_testdone = 1; + } + + // Skip leading spaces + while (isspace((int)*buf) && *buf) + buf++; + + errno = 0; // sscanf won't always set errno, so initialize + if (sscanf(buf, "%" SCNd64, &num64) != 1) + { + MC_DEBUG("Failed to parse, sscanf != 1\n"); + return 1; + } + + saved_errno = errno; + buf_sig_digits = buf; + orig_has_neg = 0; + if (*buf_sig_digits == '-') + { + buf_sig_digits++; + orig_has_neg = 1; + } + + // Not all sscanf implementations actually work + if (sscanf_is_broken && saved_errno != ERANGE) + { + char buf_cmp[100]; + char *buf_cmp_start = buf_cmp; + int recheck_has_neg = 0; + int buf_cmp_len; + + // Skip leading zeros, but keep at least one digit + while (buf_sig_digits[0] == '0' && buf_sig_digits[1] != '\0') + buf_sig_digits++; + if (num64 == 0) // assume all sscanf impl's will parse -0 to 0 + orig_has_neg = 0; // "-0" is the same as just plain "0" + + snprintf(buf_cmp_start, sizeof(buf_cmp), "%" PRId64, num64); + if (*buf_cmp_start == '-') + { + recheck_has_neg = 1; + buf_cmp_start++; + } + // No need to skip leading spaces or zeros here. + + buf_cmp_len = strlen(buf_cmp_start); + /** + * If the sign is different, or + * some of the digits are different, or + * there is another digit present in the original string + * then we have NOT successfully parsed the value. + */ + if (orig_has_neg != recheck_has_neg || + strncmp(buf_sig_digits, buf_cmp_start, strlen(buf_cmp_start)) != 0 || + ((int)strlen(buf_sig_digits) != buf_cmp_len && + isdigit((int)buf_sig_digits[buf_cmp_len]) + ) + ) + { + saved_errno = ERANGE; + } + } + + // Not all sscanf impl's set the value properly when out of range. + // Always do this, even for properly functioning implementations, + // since it shouldn't slow things down much. + if (saved_errno == ERANGE) + { + if (orig_has_neg) + num64 = INT64_MIN; + else + num64 = INT64_MAX; + } + *retval = num64; + return 0; +} + +#ifndef HAVE_REALLOC +void* rpl_realloc(void* p, size_t n) +{ + if (n == 0) + n = 1; + if (p == 0) + return malloc(n); + return realloc(p, n); +} +#endif + +#define NELEM(a) (sizeof(a) / sizeof(a[0])) +static const char* json_type_name[] = { + /* If you change this, be sure to update the enum json_type definition too */ + "null", + "boolean", + "double", + "int", + "object", + "array", + "string", +}; + +const char *json_type_to_name(enum json_type o_type) +{ + int o_type_int = (int)o_type; + if (o_type_int < 0 || o_type_int >= (int)NELEM(json_type_name)) + { + MC_ERROR("json_type_to_name: type %d is out of range [0,%d]\n", o_type, NELEM(json_type_name)); + return NULL; + } + return json_type_name[o_type]; +} + diff --git a/3P/json/json_util.h b/3P/json/json_util.h new file mode 100644 index 00000000..b9a69c8b --- /dev/null +++ b/3P/json/json_util.h @@ -0,0 +1,41 @@ +/* + * $Id: json_util.h,v 1.4 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _json_util_h_ +#define _json_util_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define JSON_FILE_BUF_SIZE 4096 + +/* utility functions */ +extern struct json_object* json_object_from_file(const char *filename); +extern int json_object_to_file(char *filename, struct json_object *obj); +extern int json_object_to_file_ext(char *filename, struct json_object *obj, int flags); +extern int json_parse_int64(const char *buf, int64_t *retval); +extern int json_parse_double(const char *buf, double *retval); + + +/** + * Return a string describing the type of the object. + * e.g. "int", or "object", etc... + */ +extern const char *json_type_to_name(enum json_type o_type); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3P/json/libjson.c b/3P/json/libjson.c new file mode 100644 index 00000000..5284fd0e --- /dev/null +++ b/3P/json/libjson.c @@ -0,0 +1,26 @@ + +/* dummy source file for compatibility purposes */ + +#if defined(HAVE_CDEFS_H) +#include +#endif + +#ifndef __warn_references + +#if defined(__GNUC__) && defined (HAS_GNU_WARNING_LONG) + +#define __warn_references(sym,msg) \ + __asm__(".section .gnu" #sym ",\n\t.ascii \"" msg "\"\n\t.text"); + +#else +#define __warn_references(sym,msg) /* nothing */ +#endif + +#endif + +#include "json_object.h" + +__warn_references(json_object_get, "Warning: please link against libjson-c instead of libjson"); + +/* __asm__(".section .gnu.warning." __STRING(sym) \ + " ; .ascii \"" msg "\" ; .text") */ diff --git a/3P/json/linkhash.c b/3P/json/linkhash.c new file mode 100644 index 00000000..50431485 --- /dev/null +++ b/3P/json/linkhash.c @@ -0,0 +1,233 @@ +/* + * $Id: linkhash.c,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "linkhash.h" + +void lh_abort(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vprintf(msg, ap); + va_end(ap); + exit(1); +} + +unsigned long lh_ptr_hash(const void *k) +{ + /* CAW: refactored to be 64bit nice */ + return (unsigned long)((((ptrdiff_t)k * LH_PRIME) >> 4) & ULONG_MAX); +} + +int lh_ptr_equal(const void *k1, const void *k2) +{ + return (k1 == k2); +} + +unsigned long lh_char_hash(const void *k) +{ + unsigned int h = 0; + const char* data = (const char*)k; + + while( *data!=0 ) h = h*129 + (unsigned int)(*data++) + LH_PRIME; + + return h; +} + +int lh_char_equal(const void *k1, const void *k2) +{ + return (strcmp((const char*)k1, (const char*)k2) == 0); +} + +struct lh_table* lh_table_new(int size, const char *name, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn) +{ + int i; + struct lh_table *t; + + t = (struct lh_table*)calloc(1, sizeof(struct lh_table)); + if(!t) lh_abort("lh_table_new: calloc failed\n"); + t->count = 0; + t->size = size; + t->name = name; + t->table = (struct lh_entry*)calloc(size, sizeof(struct lh_entry)); + if(!t->table) lh_abort("lh_table_new: calloc failed\n"); + t->free_fn = free_fn; + t->hash_fn = hash_fn; + t->equal_fn = equal_fn; + for(i = 0; i < size; i++) t->table[i].k = LH_EMPTY; + return t; +} + +struct lh_table* lh_kchar_table_new(int size, const char *name, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, name, free_fn, lh_char_hash, lh_char_equal); +} + +struct lh_table* lh_kptr_table_new(int size, const char *name, + lh_entry_free_fn *free_fn) +{ + return lh_table_new(size, name, free_fn, lh_ptr_hash, lh_ptr_equal); +} + +void lh_table_resize(struct lh_table *t, int new_size) +{ + struct lh_table *new_t; + struct lh_entry *ent; + + new_t = lh_table_new(new_size, t->name, NULL, t->hash_fn, t->equal_fn); + ent = t->head; + while(ent) { + lh_table_insert(new_t, ent->k, ent->v); + ent = ent->next; + } + free(t->table); + t->table = new_t->table; + t->size = new_size; + t->head = new_t->head; + t->tail = new_t->tail; + t->resizes++; + free(new_t); +} + +void lh_table_free(struct lh_table *t) +{ + struct lh_entry *c; + for(c = t->head; c != NULL; c = c->next) { + if(t->free_fn) { + t->free_fn(c); + } + } + free(t->table); + free(t); +} + + +int lh_table_insert(struct lh_table *t, void *k, const void *v) +{ + unsigned long h, n; + + t->inserts++; + if(t->count >= t->size * LH_LOAD_FACTOR) lh_table_resize(t, t->size * 2); + + h = t->hash_fn(k); + n = h % t->size; + + while( 1 ) { + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) break; + t->collisions++; + if ((int)++n == t->size) n = 0; + } + + t->table[n].k = k; + t->table[n].v = v; + t->count++; + + if(t->head == NULL) { + t->head = t->tail = &t->table[n]; + t->table[n].next = t->table[n].prev = NULL; + } else { + t->tail->next = &t->table[n]; + t->table[n].prev = t->tail; + t->table[n].next = NULL; + t->tail = &t->table[n]; + } + + return 0; +} + + +struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k) +{ + unsigned long h = t->hash_fn(k); + unsigned long n = h % t->size; + int count = 0; + + t->lookups++; + while( count < t->size ) { + if(t->table[n].k == LH_EMPTY) return NULL; + if(t->table[n].k != LH_FREED && + t->equal_fn(t->table[n].k, k)) return &t->table[n]; + if ((int)++n == t->size) n = 0; + count++; + } + return NULL; +} + + +const void* lh_table_lookup(struct lh_table *t, const void *k) +{ + void *result; + lh_table_lookup_ex(t, k, &result); + return result; +} + +json_bool lh_table_lookup_ex(struct lh_table* t, const void* k, void **v) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if (e != NULL) { + if (v != NULL) *v = (void *)e->v; + return TRUE; /* key found */ + } + if (v != NULL) *v = NULL; + return FALSE; /* key not found */ +} + +int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e) +{ + ptrdiff_t n = (ptrdiff_t)(e - t->table); /* CAW: fixed to be 64bit nice, still need the crazy negative case... */ + + /* CAW: this is bad, really bad, maybe stack goes other direction on this machine... */ + if(n < 0) { return -2; } + + if(t->table[n].k == LH_EMPTY || t->table[n].k == LH_FREED) return -1; + t->count--; + if(t->free_fn) t->free_fn(e); + t->table[n].v = NULL; + t->table[n].k = LH_FREED; + if(t->tail == &t->table[n] && t->head == &t->table[n]) { + t->head = t->tail = NULL; + } else if (t->head == &t->table[n]) { + t->head->next->prev = NULL; + t->head = t->head->next; + } else if (t->tail == &t->table[n]) { + t->tail->prev->next = NULL; + t->tail = t->tail->prev; + } else { + t->table[n].prev->next = t->table[n].next; + t->table[n].next->prev = t->table[n].prev; + } + t->table[n].next = t->table[n].prev = NULL; + return 0; +} + + +int lh_table_delete(struct lh_table *t, const void *k) +{ + struct lh_entry *e = lh_table_lookup_entry(t, k); + if(!e) return -1; + return lh_table_delete_entry(t, e); +} + +int lh_table_length(struct lh_table *t) +{ + return t->count; +} diff --git a/3P/json/linkhash.h b/3P/json/linkhash.h new file mode 100644 index 00000000..378de0b7 --- /dev/null +++ b/3P/json/linkhash.h @@ -0,0 +1,292 @@ +/* + * $Id: linkhash.h,v 1.6 2006/01/30 23:07:57 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * Copyright (c) 2009 Hewlett-Packard Development Company, L.P. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + */ + +#ifndef _linkhash_h_ +#define _linkhash_h_ + +#include "json_object.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * golden prime used in hash functions + */ +#define LH_PRIME 0x9e370001UL + +/** + * The fraction of filled hash buckets until an insert will cause the table + * to be resized. + * This can range from just above 0 up to 1.0. + */ +#define LH_LOAD_FACTOR 0.66 + +/** + * sentinel pointer value for empty slots + */ +#define LH_EMPTY (void*)-1 + +/** + * sentinel pointer value for freed slots + */ +#define LH_FREED (void*)-2 + +struct lh_entry; + +/** + * callback function prototypes + */ +typedef void (lh_entry_free_fn) (struct lh_entry *e); +/** + * callback function prototypes + */ +typedef unsigned long (lh_hash_fn) (const void *k); +/** + * callback function prototypes + */ +typedef int (lh_equal_fn) (const void *k1, const void *k2); + +/** + * An entry in the hash table + */ +struct lh_entry { + /** + * The key. + */ + void *k; + /** + * The value. + */ + const void *v; + /** + * The next entry + */ + struct lh_entry *next; + /** + * The previous entry. + */ + struct lh_entry *prev; +}; + + +/** + * The hash table structure. + */ +struct lh_table { + /** + * Size of our hash. + */ + int size; + /** + * Numbers of entries. + */ + int count; + + /** + * Number of collisions. + */ + int collisions; + + /** + * Number of resizes. + */ + int resizes; + + /** + * Number of lookups. + */ + int lookups; + + /** + * Number of inserts. + */ + int inserts; + + /** + * Number of deletes. + */ + int deletes; + + /** + * Name of the hash table. + */ + const char *name; + + /** + * The first entry. + */ + struct lh_entry *head; + + /** + * The last entry. + */ + struct lh_entry *tail; + + struct lh_entry *table; + + /** + * A pointer onto the function responsible for freeing an entry. + */ + lh_entry_free_fn *free_fn; + lh_hash_fn *hash_fn; + lh_equal_fn *equal_fn; +}; + + +/** + * Pre-defined hash and equality functions + */ +extern unsigned long lh_ptr_hash(const void *k); +extern int lh_ptr_equal(const void *k1, const void *k2); + +extern unsigned long lh_char_hash(const void *k); +extern int lh_char_equal(const void *k1, const void *k2); + + +/** + * Convenience list iterator. + */ +#define lh_foreach(table, entry) \ +for(entry = table->head; entry; entry = entry->next) + +/** + * lh_foreach_safe allows calling of deletion routine while iterating. + */ +#define lh_foreach_safe(table, entry, tmp) \ +for(entry = table->head; entry && ((tmp = entry->next) || 1); entry = tmp) + + + +/** + * Create a new linkhash table. + * @param size initial table size. The table is automatically resized + * although this incurs a performance penalty. + * @param name the table name. + * @param free_fn callback function used to free memory for entries + * when lh_table_free or lh_table_delete is called. + * If NULL is provided, then memory for keys and values + * must be freed by the caller. + * @param hash_fn function used to hash keys. 2 standard ones are defined: + * lh_ptr_hash and lh_char_hash for hashing pointer values + * and C strings respectively. + * @param equal_fn comparison function to compare keys. 2 standard ones defined: + * lh_ptr_hash and lh_char_hash for comparing pointer values + * and C strings respectively. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_table_new(int size, const char *name, + lh_entry_free_fn *free_fn, + lh_hash_fn *hash_fn, + lh_equal_fn *equal_fn); + +/** + * Convenience function to create a new linkhash + * table with char keys. + * @param size initial table size. + * @param name table name. + * @param free_fn callback function used to free memory for entries. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_kchar_table_new(int size, const char *name, + lh_entry_free_fn *free_fn); + + +/** + * Convenience function to create a new linkhash + * table with ptr keys. + * @param size initial table size. + * @param name table name. + * @param free_fn callback function used to free memory for entries. + * @return a pointer onto the linkhash table. + */ +extern struct lh_table* lh_kptr_table_new(int size, const char *name, + lh_entry_free_fn *free_fn); + + +/** + * Free a linkhash table. + * If a callback free function is provided then it is called for all + * entries in the table. + * @param t table to free. + */ +extern void lh_table_free(struct lh_table *t); + + +/** + * Insert a record into the table. + * @param t the table to insert into. + * @param k a pointer to the key to insert. + * @param v a pointer to the value to insert. + */ +extern int lh_table_insert(struct lh_table *t, void *k, const void *v); + + +/** + * Lookup a record into the table. + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the record structure of the value or NULL if it does not exist. + */ +extern struct lh_entry* lh_table_lookup_entry(struct lh_table *t, const void *k); + +/** + * Lookup a record into the table + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @return a pointer to the found value or NULL if it does not exist. + * @deprecated Use lh_table_lookup_ex instead. + */ +extern const void* lh_table_lookup(struct lh_table *t, const void *k); + +/** + * Lookup a record in the table + * @param t the table to lookup + * @param k a pointer to the key to lookup + * @param v a pointer to a where to store the found value (set to NULL if it doesn't exist). + * @return whether or not the key was found + */ +extern json_bool lh_table_lookup_ex(struct lh_table *t, const void *k, void **v); + +/** + * Delete a record from the table. + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param e a pointer to the entry to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete_entry(struct lh_table *t, struct lh_entry *e); + + +/** + * Delete a record from the table. + * If a callback free function is provided then it is called for the + * for the item being deleted. + * @param t the table to delete from. + * @param k a pointer to the key to delete. + * @return 0 if the item was deleted. + * @return -1 if it was not found. + */ +extern int lh_table_delete(struct lh_table *t, const void *k); + +extern int lh_table_length(struct lh_table *t); + +void lh_abort(const char *msg, ...); +void lh_table_resize(struct lh_table *t, int new_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3P/json/printbuf.c b/3P/json/printbuf.c new file mode 100644 index 00000000..9d565220 --- /dev/null +++ b/3P/json/printbuf.c @@ -0,0 +1,192 @@ +/* + * $Id: printbuf.c,v 1.5 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_STDARG_H +# include +#else /* !HAVE_STDARG_H */ +# error Not enough var arg support! +#endif /* HAVE_STDARG_H */ + +#include "bits.h" +#include "debug.h" +#include "printbuf.h" + +static int printbuf_extend(struct printbuf *p, int min_size); + +struct printbuf* printbuf_new(void) +{ + struct printbuf *p; + + p = (struct printbuf*)calloc(1, sizeof(struct printbuf)); + if(!p) return NULL; + p->size = 32; + p->bpos = 0; + if(!(p->buf = (char*)malloc(p->size))) { + free(p); + return NULL; + } + return p; +} + + +/** + * Extend the buffer p so it has a size of at least min_size. + * + * If the current size is large enough, nothing is changed. + * + * Note: this does not check the available space! The caller + * is responsible for performing those calculations. + */ +static int printbuf_extend(struct printbuf *p, int min_size) +{ + char *t; + int new_size; + + if (p->size >= min_size) + return 0; + + new_size = json_max(p->size * 2, min_size + 8); +#ifdef PRINTBUF_DEBUG + MC_DEBUG("printbuf_memappend: realloc " + "bpos=%d min_size=%d old_size=%d new_size=%d\n", + p->bpos, min_size, p->size, new_size); +#endif /* PRINTBUF_DEBUG */ + if(!(t = (char*)realloc(p->buf, new_size))) + return -1; + p->size = new_size; + p->buf = t; + return 0; +} + +int printbuf_memappend(struct printbuf *p, const char *buf, int size) +{ + if (p->size <= p->bpos + size + 1) { + if (printbuf_extend(p, p->bpos + size + 1) < 0) + return -1; + } + memcpy(p->buf + p->bpos, buf, size); + p->bpos += size; + p->buf[p->bpos]= '\0'; + return size; +} + +int printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len) +{ + int size_needed; + + if (offset == -1) + offset = pb->bpos; + size_needed = offset + len; + if (pb->size < size_needed) + { + if (printbuf_extend(pb, size_needed) < 0) + return -1; + } + + memset(pb->buf + offset, charvalue, len); + if (pb->bpos < size_needed) + pb->bpos = size_needed; + + return 0; +} + +#if !defined(HAVE_VSNPRINTF) && defined(_MSC_VER) +# define vsnprintf _vsnprintf +#elif !defined(HAVE_VSNPRINTF) /* !HAVE_VSNPRINTF */ +# error Need vsnprintf! +#endif /* !HAVE_VSNPRINTF && defined(WIN32) */ + +#if !defined(HAVE_VASPRINTF) +/* CAW: compliant version of vasprintf */ +static int vasprintf(char **buf, const char *fmt, va_list ap) +{ +#ifndef WIN32 + static char _T_emptybuffer = '\0'; +#endif /* !defined(WIN32) */ + int chars; + char *b; + + if(!buf) { return -1; } + +#ifdef WIN32 + chars = _vscprintf(fmt, ap)+1; +#else /* !defined(WIN32) */ + /* CAW: RAWR! We have to hope to god here that vsnprintf doesn't overwrite + our buffer like on some 64bit sun systems.... but hey, its time to move on */ + chars = vsnprintf(&_T_emptybuffer, 0, fmt, ap)+1; + if(chars < 0) { chars *= -1; } /* CAW: old glibc versions have this problem */ +#endif /* defined(WIN32) */ + + b = (char*)malloc(sizeof(char)*chars); + if(!b) { return -1; } + + if((chars = vsprintf(b, fmt, ap)) < 0) + { + free(b); + } else { + *buf = b; + } + + return chars; +} +#endif /* !HAVE_VASPRINTF */ + +int sprintbuf(struct printbuf *p, const char *msg, ...) +{ + va_list ap; + char *t; + int size; + char buf[128]; + + /* user stack buffer first */ + va_start(ap, msg); + size = vsnprintf(buf, 128, msg, ap); + va_end(ap); + /* if string is greater than stack buffer, then use dynamic string + with vasprintf. Note: some implementation of vsnprintf return -1 + if output is truncated whereas some return the number of bytes that + would have been written - this code handles both cases. */ + if(size == -1 || size > 127) { + va_start(ap, msg); + if((size = vasprintf(&t, msg, ap)) < 0) { va_end(ap); return -1; } + va_end(ap); + printbuf_memappend(p, t, size); + free(t); + return size; + } else { + printbuf_memappend(p, buf, size); + return size; + } +} + +void printbuf_reset(struct printbuf *p) +{ + p->buf[0] = '\0'; + p->bpos = 0; +} + +void printbuf_free(struct printbuf *p) +{ + if(p) { + free(p->buf); + free(p); + } +} diff --git a/3P/json/printbuf.h b/3P/json/printbuf.h new file mode 100644 index 00000000..b1bde7f9 --- /dev/null +++ b/3P/json/printbuf.h @@ -0,0 +1,77 @@ +/* + * $Id: printbuf.h,v 1.4 2006/01/26 02:16:28 mclark Exp $ + * + * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd. + * Michael Clark + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the MIT license. See COPYING for details. + * + * + * Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved. + * The copyrights to the contents of this file are licensed under the MIT License + * (http://www.opensource.org/licenses/mit-license.php) + */ + +#ifndef _printbuf_h_ +#define _printbuf_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct printbuf { + char *buf; + int bpos; + int size; +}; + +extern struct printbuf* +printbuf_new(void); + +/* As an optimization, printbuf_memappend_fast is defined as a macro + * that handles copying data if the buffer is large enough; otherwise + * it invokes printbuf_memappend_real() which performs the heavy + * lifting of realloc()ing the buffer and copying data. + * Your code should not use printbuf_memappend directly--use + * printbuf_memappend_fast instead. + */ +extern int +printbuf_memappend(struct printbuf *p, const char *buf, int size); + +#define printbuf_memappend_fast(p, bufptr, bufsize) \ +do { \ + if ((p->size - p->bpos) > bufsize) { \ + memcpy(p->buf + p->bpos, (bufptr), bufsize); \ + p->bpos += bufsize; \ + p->buf[p->bpos]= '\0'; \ + } else { printbuf_memappend(p, (bufptr), bufsize); } \ +} while (0) + +#define printbuf_length(p) ((p)->bpos) + +/** + * Set len bytes of the buffer to charvalue, starting at offset offset. + * Similar to calling memset(x, charvalue, len); + * + * The memory allocated for the buffer is extended as necessary. + * + * If offset is -1, this starts at the end of the current data in the buffer. + */ +extern int +printbuf_memset(struct printbuf *pb, int offset, int charvalue, int len); + +extern int +sprintbuf(struct printbuf *p, const char *msg, ...); + +extern void +printbuf_reset(struct printbuf *p); + +extern void +printbuf_free(struct printbuf *p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/3P/json/tests/Makefile.am b/3P/json/tests/Makefile.am new file mode 100644 index 00000000..c6123ed1 --- /dev/null +++ b/3P/json/tests/Makefile.am @@ -0,0 +1,59 @@ + +include ../Makefile.am.inc + +LIBJSON_LA=$(top_builddir)/libjson-c.la + +check_PROGRAMS = test1 test1Formatted +check_PROGRAMS += test2 test2Formatted +check_PROGRAMS += test4 +check_PROGRAMS += testReplaceExisting +check_PROGRAMS += test_parse_int64 +check_PROGRAMS += test_null +check_PROGRAMS += test_cast +check_PROGRAMS += test_parse +check_PROGRAMS += test_locale + +test1_LDADD = $(LIBJSON_LA) + +test1Formatted_LDADD= $(LIBJSON_LA) +test1Formatted_SOURCES = test1.c parse_flags.c +test1Formatted_CPPFLAGS = -DTEST_FORMATTED + +test2_LDADD = $(LIBJSON_LA) + +test2Formatted_LDADD= $(LIBJSON_LA) +test2Formatted_SOURCES = test2.c parse_flags.c +test2Formatted_CPPFLAGS = -DTEST_FORMATTED + +test4_LDADD = $(LIBJSON_LA) + +testReplaceExisting_LDADD = $(LIBJSON_LA) + +test_parse_int64_LDADD = $(LIBJSON_LA) + +test_null_LDADD = $(LIBJSON_LA) + +test_cast_LDADD = $(LIBJSON_LA) + +test_parse_LDADD = $(LIBJSON_LA) + +test_locale_LDADD = $(LIBJSON_LA) + +TESTS = test1.test test2.test test4.test testReplaceExisting.test parse_int64.test test_null.test test_cast.test test_parse.test test_locale.test + +TESTS+= test_printbuf.test +check_PROGRAMS+=test_printbuf +test_printbuf_LDADD = $(LIBJSON_LA) + +TESTS+= test_set_serializer.test +check_PROGRAMS += test_set_serializer +test_set_serializer_LDADD = $(LIBJSON_LA) + +EXTRA_DIST= +EXTRA_DIST += $(TESTS) + +testsubdir=testSubDir +TESTS_ENVIRONMENT = top_builddir=$(top_builddir) + +distclean-local: + -rm -rf $(testsubdir) diff --git a/3P/json/tests/parse_flags.c b/3P/json/tests/parse_flags.c new file mode 100644 index 00000000..1af61ea4 --- /dev/null +++ b/3P/json/tests/parse_flags.c @@ -0,0 +1,50 @@ +#include "config.h" + +#include +#include + +#include "json.h" +#include "parse_flags.h" + +#if !defined(HAVE_STRCASECMP) && defined(_MSC_VER) +# define strcasecmp _stricmp +#elif !defined(HAVE_STRCASECMP) +# error You do not have strcasecmp on your system. +#endif /* HAVE_STRNCASECMP */ + +static struct { + const char *arg; + int flag; +} format_args[] = { + { "plain", JSON_C_TO_STRING_PLAIN }, + { "spaced", JSON_C_TO_STRING_SPACED }, + { "pretty", JSON_C_TO_STRING_PRETTY }, +}; + +#ifndef NELEM +#define NELEM(x) (sizeof(x) / sizeof(&x[0])) +#endif + +int parse_flags(int argc, char **argv) +{ + int arg_idx; + int sflags = 0; + for (arg_idx = 1; arg_idx < argc ; arg_idx++) + { + int jj; + for (jj = 0; jj < (int)NELEM(format_args); jj++) + { + if (strcasecmp(argv[arg_idx], format_args[jj].arg) == 0) + { + sflags |= format_args[jj].flag; + break; + } + } + if (jj == NELEM(format_args)) + { + printf("Unknown arg: %s\n", argv[arg_idx]); + exit(1); + } + } + return sflags; +} diff --git a/3P/json/tests/parse_flags.h b/3P/json/tests/parse_flags.h new file mode 100644 index 00000000..c5e2f410 --- /dev/null +++ b/3P/json/tests/parse_flags.h @@ -0,0 +1,4 @@ +#ifndef __parse_flags_h +#define __parse_flags_h +int parse_flags(int argc, char **argv); +#endif diff --git a/3P/json/tests/parse_int64.test b/3P/json/tests/parse_int64.test new file mode 100755 index 00000000..2b7fbfb6 --- /dev/null +++ b/3P/json/tests/parse_int64.test @@ -0,0 +1,12 @@ +#!/bin/sh + +# Common definitions +if test -z "$srcdir"; then + srcdir="${0%/*}" + test "$srcdir" = "$0" && srcdir=. + test -z "$srcdir" && srcdir=. +fi +. "$srcdir/test-defs.sh" + +run_output_test test_parse_int64 +exit $? diff --git a/3P/json/tests/test-defs.sh b/3P/json/tests/test-defs.sh new file mode 100755 index 00000000..658a75d9 --- /dev/null +++ b/3P/json/tests/test-defs.sh @@ -0,0 +1,128 @@ +#!/bin/sh + +# Make sure srcdir is an absolute path. Supply the variable +# if it does not exist. We want to be able to run the tests +# stand-alone!! +# +srcdir=${srcdir-.} +if test ! -d $srcdir ; then + echo "test-defs.sh: installation error" 1>&2 + exit 1 +fi + +# Use absolute paths +case "$srcdir" in + /* | [A-Za-z]:\\*) ;; + *) srcdir=`\cd $srcdir && pwd` ;; +esac + +case "$top_builddir" in + /* | [A-Za-z]:\\*) ;; + *) top_builddir=`\cd ${top_builddir-..} && pwd` ;; +esac + +top_builddir=${top_builddir}/tests + +progname=`echo "$0" | sed 's,^.*/,,'` +testname=`echo "$progname" | sed 's,-.*$,,'` +testsubdir=${testsubdir-testSubDir} +testsubdir=${testsubdir}/${progname} + +# User can set VERBOSE to cause output redirection +case "$VERBOSE" in +[Nn]|[Nn][Oo]|0|"") + VERBOSE=0 + exec > /dev/null + ;; +[Yy]|[Yy][Ee][Ss]) + VERBOSE=1 + ;; +esac + +rm -rf "$testsubdir" > /dev/null 2>&1 +mkdir -p "$testsubdir" +CURDIR=$(pwd) +cd "$testsubdir" \ + || { echo "Cannot make or change into $testsubdir"; exit 1; } + +echo "=== Running test $progname" + +CMP="${CMP-cmp}" + +use_valgrind=${USE_VALGRIND-1} +valgrind_path=$(which valgrind 2> /dev/null) +if [ -z "${valgrind_path}" -o ! -x "${valgrind_path}" ] ; then + use_valgrind=0 +fi + +# +# This is a common function to check the results of a test program +# that is intended to generate consistent output across runs. +# +# ${top_builddir} must be set to the top level build directory. +# +# Output will be written to the current directory. +# +# It must be passed the name of the test command to run, which must be present +# in the ${top_builddir} directory. +# +# It will compare the output of running that against .expected +# +run_output_test() +{ + if [ "$1" = "-o" ] ; then + TEST_OUTPUT="$2" + shift + shift + fi + TEST_COMMAND="$1" + shift + if [ -z "${TEST_OUTPUT}" ] ; then + TEST_OUTPUT=${TEST_COMMAND} + fi + + REDIR_OUTPUT="> \"${TEST_OUTPUT}.out\"" + if [ $VERBOSE -gt 1 ] ; then + REDIR_OUTPUT="| tee \"${TEST_OUTPUT}.out\"" + fi + + if [ $use_valgrind -eq 1 ] ; then + eval valgrind --tool=memcheck \ + --trace-children=yes \ + --demangle=yes \ + --log-file="${TEST_OUTPUT}.vg.out" \ + --leak-check=full \ + --show-reachable=yes \ + --run-libc-freeres=yes \ + "\"${top_builddir}/${TEST_COMMAND}\"" \"\$@\" ${REDIR_OUTPUT} + err=$? + + else + eval "\"${top_builddir}/${TEST_COMMAND}"\" \"\$@\" ${REDIR_OUTPUT} + err=$? + fi + + if [ $err -ne 0 ] ; then + echo "ERROR: \"${TEST_COMMAND} $@\" exited with non-zero exit status: $err" 1>&2 + fi + + if [ $use_valgrind -eq 1 ] ; then + if ! tail -1 "${TEST_OUTPUT}.vg.out" | grep -q "ERROR SUMMARY: 0 errors" ; then + echo "ERROR: valgrind found errors during execution:" 1>&2 + cat "${TEST_OUTPUT}.vg.out" + err=1 + fi + fi + + if ! "$CMP" -s "${top_builddir}/${TEST_OUTPUT}.expected" "${TEST_OUTPUT}.out" ; then + echo "ERROR: \"${TEST_COMMAND} $@\" (${TEST_OUTPUT}) failed (set VERBOSE=1 to see full output):" 1>&2 + (cd "${CURDIR}" ; set -x ; diff "${top_builddir}/${TEST_OUTPUT}.expected" "$testsubdir/${TEST_OUTPUT}.out") + echo "cp \"$testsubdir/${TEST_OUTPUT}.out\" \"${top_builddir}/${TEST_OUTPUT}.expected\"" 1>&2 + + err=1 + fi + + return $err +} + + diff --git a/3P/json/tests/test1.c b/3P/json/tests/test1.c new file mode 100644 index 00000000..7acea622 --- /dev/null +++ b/3P/json/tests/test1.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include + +#include "json.h" +#include "parse_flags.h" + +static int sort_fn (const void *j1, const void *j2) +{ + json_object * const *jso1, * const *jso2; + int i1, i2; + + jso1 = (json_object* const*)j1; + jso2 = (json_object* const*)j2; + if (!*jso1 && !*jso2) + return 0; + if (!*jso1) + return -1; + if (!*jso2) + return 1; + + i1 = json_object_get_int(*jso1); + i2 = json_object_get_int(*jso2); + + return i1 - i2; +} + +#ifdef TEST_FORMATTED +#define json_object_to_json_string(obj) json_object_to_json_string_ext(obj,sflags) +#else +/* no special define */ +#endif + +int main(int argc, char **argv) +{ + json_object *my_string, *my_int, *my_object, *my_array; + int i; +#ifdef TEST_FORMATTED + int sflags = 0; +#endif + + MC_SET_DEBUG(1); + +#ifdef TEST_FORMATTED + sflags = parse_flags(argc, argv); +#endif + + my_string = json_object_new_string("\t"); + printf("my_string=%s\n", json_object_get_string(my_string)); + printf("my_string.to_string()=%s\n", json_object_to_json_string(my_string)); + json_object_put(my_string); + + my_string = json_object_new_string("\\"); + printf("my_string=%s\n", json_object_get_string(my_string)); + printf("my_string.to_string()=%s\n", json_object_to_json_string(my_string)); + json_object_put(my_string); + + my_string = json_object_new_string("foo"); + printf("my_string=%s\n", json_object_get_string(my_string)); + printf("my_string.to_string()=%s\n", json_object_to_json_string(my_string)); + + my_int = json_object_new_int(9); + printf("my_int=%d\n", json_object_get_int(my_int)); + printf("my_int.to_string()=%s\n", json_object_to_json_string(my_int)); + + my_array = json_object_new_array(); + json_object_array_add(my_array, json_object_new_int(1)); + json_object_array_add(my_array, json_object_new_int(2)); + json_object_array_add(my_array, json_object_new_int(3)); + json_object_array_put_idx(my_array, 4, json_object_new_int(5)); + printf("my_array=\n"); + for(i=0; i < json_object_array_length(my_array); i++) + { + json_object *obj = json_object_array_get_idx(my_array, i); + printf("\t[%d]=%s\n", i, json_object_to_json_string(obj)); + } + printf("my_array.to_string()=%s\n", json_object_to_json_string(my_array)); + + json_object_put(my_array); + + my_array = json_object_new_array(); + json_object_array_add(my_array, json_object_new_int(3)); + json_object_array_add(my_array, json_object_new_int(1)); + json_object_array_add(my_array, json_object_new_int(2)); + json_object_array_put_idx(my_array, 4, json_object_new_int(0)); + printf("my_array=\n"); + for(i=0; i < json_object_array_length(my_array); i++) + { + json_object *obj = json_object_array_get_idx(my_array, i); + printf("\t[%d]=%s\n", i, json_object_to_json_string(obj)); + } + printf("my_array.to_string()=%s\n", json_object_to_json_string(my_array)); + json_object_array_sort(my_array, sort_fn); + printf("my_array=\n"); + for(i=0; i < json_object_array_length(my_array); i++) + { + json_object *obj = json_object_array_get_idx(my_array, i); + printf("\t[%d]=%s\n", i, json_object_to_json_string(obj)); + } + printf("my_array.to_string()=%s\n", json_object_to_json_string(my_array)); + + my_object = json_object_new_object(); + json_object_object_add(my_object, "abc", json_object_new_int(12)); + json_object_object_add(my_object, "foo", json_object_new_string("bar")); + json_object_object_add(my_object, "bool0", json_object_new_boolean(0)); + json_object_object_add(my_object, "bool1", json_object_new_boolean(1)); + json_object_object_add(my_object, "baz", json_object_new_string("bang")); + + json_object *baz_obj = json_object_new_string("fark"); + json_object_get(baz_obj); + json_object_object_add(my_object, "baz", baz_obj); + json_object_object_del(my_object, "baz"); + + /* baz_obj should still be valid */ + printf("baz_obj.to_string()=%s\n", json_object_to_json_string(baz_obj)); + json_object_put(baz_obj); + + /*json_object_object_add(my_object, "arr", my_array);*/ + printf("my_object=\n"); + json_object_object_foreach(my_object, key, val) + { + printf("\t%s: %s\n", key, json_object_to_json_string(val)); + } + printf("my_object.to_string()=%s\n", json_object_to_json_string(my_object)); + + json_object_put(my_string); + json_object_put(my_int); + json_object_put(my_object); + json_object_put(my_array); + + return 0; +} diff --git a/3P/json/tests/test1.expected b/3P/json/tests/test1.expected new file mode 100644 index 00000000..e36aaa4c --- /dev/null +++ b/3P/json/tests/test1.expected @@ -0,0 +1,36 @@ +my_string= +my_string.to_string()="\t" +my_string=\ +my_string.to_string()="\\" +my_string=foo +my_string.to_string()="foo" +my_int=9 +my_int.to_string()=9 +my_array= + [0]=1 + [1]=2 + [2]=3 + [3]=null + [4]=5 +my_array.to_string()=[ 1, 2, 3, null, 5 ] +my_array= + [0]=3 + [1]=1 + [2]=2 + [3]=null + [4]=0 +my_array.to_string()=[ 3, 1, 2, null, 0 ] +my_array= + [0]=null + [1]=0 + [2]=1 + [3]=2 + [4]=3 +my_array.to_string()=[ null, 0, 1, 2, 3 ] +baz_obj.to_string()="fark" +my_object= + abc: 12 + foo: "bar" + bool0: false + bool1: true +my_object.to_string()={ "abc": 12, "foo": "bar", "bool0": false, "bool1": true } diff --git a/3P/json/tests/test1.test b/3P/json/tests/test1.test new file mode 100755 index 00000000..79d2e09a --- /dev/null +++ b/3P/json/tests/test1.test @@ -0,0 +1,22 @@ +#!/bin/sh + +# Common definitions +if test -z "$srcdir"; then + srcdir="${0%/*}" + test "$srcdir" = "$0" && srcdir=. + test -z "$srcdir" && srcdir=. +fi +. "$srcdir/test-defs.sh" + +run_output_test test1 +_err=$? + +for flag in plain spaced pretty ; do + run_output_test -o test1Formatted_${flag} test1Formatted ${flag} + _err2=$? + if [ $_err -eq 0 ] ; then + _err=$_err2 + fi +done + +exit $_err diff --git a/3P/json/tests/test1Formatted_plain.expected b/3P/json/tests/test1Formatted_plain.expected new file mode 100644 index 00000000..edcc1b93 --- /dev/null +++ b/3P/json/tests/test1Formatted_plain.expected @@ -0,0 +1,36 @@ +my_string= +my_string.to_string()="\t" +my_string=\ +my_string.to_string()="\\" +my_string=foo +my_string.to_string()="foo" +my_int=9 +my_int.to_string()=9 +my_array= + [0]=1 + [1]=2 + [2]=3 + [3]=null + [4]=5 +my_array.to_string()=[1,2,3,null,5] +my_array= + [0]=3 + [1]=1 + [2]=2 + [3]=null + [4]=0 +my_array.to_string()=[3,1,2,null,0] +my_array= + [0]=null + [1]=0 + [2]=1 + [3]=2 + [4]=3 +my_array.to_string()=[null,0,1,2,3] +baz_obj.to_string()="fark" +my_object= + abc: 12 + foo: "bar" + bool0: false + bool1: true +my_object.to_string()={"abc":12,"foo":"bar","bool0":false,"bool1":true} diff --git a/3P/json/tests/test1Formatted_pretty.expected b/3P/json/tests/test1Formatted_pretty.expected new file mode 100644 index 00000000..95e48ed0 --- /dev/null +++ b/3P/json/tests/test1Formatted_pretty.expected @@ -0,0 +1,59 @@ +my_string= +my_string.to_string()="\t" +my_string=\ +my_string.to_string()="\\" +my_string=foo +my_string.to_string()="foo" +my_int=9 +my_int.to_string()=9 +my_array= + [0]=1 + [1]=2 + [2]=3 + [3]=null + [4]=5 +my_array.to_string()=[ + 1, + 2, + 3, + null, + 5 +] +my_array= + [0]=3 + [1]=1 + [2]=2 + [3]=null + [4]=0 +my_array.to_string()=[ + 3, + 1, + 2, + null, + 0 +] +my_array= + [0]=null + [1]=0 + [2]=1 + [3]=2 + [4]=3 +my_array.to_string()=[ + null, + 0, + 1, + 2, + 3 +] +baz_obj.to_string()="fark" +my_object= + abc: 12 + foo: "bar" + bool0: false + bool1: true +my_object.to_string()={ + "abc":12, + "foo":"bar", + "bool0":false, + "bool1":true +} diff --git a/3P/json/tests/test1Formatted_spaced.expected b/3P/json/tests/test1Formatted_spaced.expected new file mode 100644 index 00000000..e36aaa4c --- /dev/null +++ b/3P/json/tests/test1Formatted_spaced.expected @@ -0,0 +1,36 @@ +my_string= +my_string.to_string()="\t" +my_string=\ +my_string.to_string()="\\" +my_string=foo +my_string.to_string()="foo" +my_int=9 +my_int.to_string()=9 +my_array= + [0]=1 + [1]=2 + [2]=3 + [3]=null + [4]=5 +my_array.to_string()=[ 1, 2, 3, null, 5 ] +my_array= + [0]=3 + [1]=1 + [2]=2 + [3]=null + [4]=0 +my_array.to_string()=[ 3, 1, 2, null, 0 ] +my_array= + [0]=null + [1]=0 + [2]=1 + [3]=2 + [4]=3 +my_array.to_string()=[ null, 0, 1, 2, 3 ] +baz_obj.to_string()="fark" +my_object= + abc: 12 + foo: "bar" + bool0: false + bool1: true +my_object.to_string()={ "abc": 12, "foo": "bar", "bool0": false, "bool1": true } diff --git a/3P/json/tests/test2.c b/3P/json/tests/test2.c new file mode 100644 index 00000000..ce44e46f --- /dev/null +++ b/3P/json/tests/test2.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include + +#include "json.h" +#include "parse_flags.h" + +#ifdef TEST_FORMATTED +#define json_object_to_json_string(obj) json_object_to_json_string_ext(obj,sflags) +#else +/* no special define */ +#endif + + +int main(int argc, char **argv) +{ + json_object *new_obj; +#ifdef TEST_FORMATTED + int sflags = 0; +#endif + + MC_SET_DEBUG(1); + +#ifdef TEST_FORMATTED + sflags = parse_flags(argc, argv); +#endif + + new_obj = json_tokener_parse("/* more difficult test case */ { \"glossary\": { \"title\": \"example glossary\", \"GlossDiv\": { \"title\": \"S\", \"GlossList\": [ { \"ID\": \"SGML\", \"SortAs\": \"SGML\", \"GlossTerm\": \"Standard Generalized Markup Language\", \"Acronym\": \"SGML\", \"Abbrev\": \"ISO 8879:1986\", \"GlossDef\": \"A meta-markup language, used to create markup languages such as DocBook.\", \"GlossSeeAlso\": [\"GML\", \"XML\", \"markup\"] } ] } } }"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + return 0; +} diff --git a/3P/json/tests/test2.expected b/3P/json/tests/test2.expected new file mode 100644 index 00000000..0b740a9f --- /dev/null +++ b/3P/json/tests/test2.expected @@ -0,0 +1 @@ +new_obj.to_string()={ "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": [ { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": [ "GML", "XML", "markup" ] } ] } } } diff --git a/3P/json/tests/test2.test b/3P/json/tests/test2.test new file mode 100755 index 00000000..d4a4e792 --- /dev/null +++ b/3P/json/tests/test2.test @@ -0,0 +1,22 @@ +#!/bin/sh + +# Common definitions +if test -z "$srcdir"; then + srcdir="${0%/*}" + test "$srcdir" = "$0" && srcdir=. + test -z "$srcdir" && srcdir=. +fi +. "$srcdir/test-defs.sh" + +run_output_test test2 +_err=$? + +for flag in plain spaced pretty ; do + run_output_test -o test2Formatted_${flag} test2Formatted ${flag} + _err2=$? + if [ $_err -eq 0 ] ; then + _err=$_err2 + fi +done + +exit $_err diff --git a/3P/json/tests/test2Formatted_plain.expected b/3P/json/tests/test2Formatted_plain.expected new file mode 100644 index 00000000..cc587e90 --- /dev/null +++ b/3P/json/tests/test2Formatted_plain.expected @@ -0,0 +1 @@ +new_obj.to_string()={"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossList":[{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","GlossDef":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML","markup"]}]}}} diff --git a/3P/json/tests/test2Formatted_pretty.expected b/3P/json/tests/test2Formatted_pretty.expected new file mode 100644 index 00000000..8d6d7402 --- /dev/null +++ b/3P/json/tests/test2Formatted_pretty.expected @@ -0,0 +1,23 @@ +new_obj.to_string()={ + "glossary":{ + "title":"example glossary", + "GlossDiv":{ + "title":"S", + "GlossList":[ + { + "ID":"SGML", + "SortAs":"SGML", + "GlossTerm":"Standard Generalized Markup Language", + "Acronym":"SGML", + "Abbrev":"ISO 8879:1986", + "GlossDef":"A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso":[ + "GML", + "XML", + "markup" + ] + } + ] + } + } +} diff --git a/3P/json/tests/test2Formatted_spaced.expected b/3P/json/tests/test2Formatted_spaced.expected new file mode 100644 index 00000000..0b740a9f --- /dev/null +++ b/3P/json/tests/test2Formatted_spaced.expected @@ -0,0 +1 @@ +new_obj.to_string()={ "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": [ { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": [ "GML", "XML", "markup" ] } ] } } } diff --git a/3P/json/tests/test4.c b/3P/json/tests/test4.c new file mode 100644 index 00000000..23e97dac --- /dev/null +++ b/3P/json/tests/test4.c @@ -0,0 +1,53 @@ +/* + * gcc -o utf8 utf8.c -I/home/y/include -L./.libs -ljson + */ + +#include +#include +#include "config.h" + +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" + +void print_hex( const char* s) +{ + const char *iter = s; + unsigned char ch; + while ((ch = *iter++) != 0) + { + if( ',' != ch) + printf("%x ", ch); + else + printf( ","); + } + printf("\n"); +} + +int main() +{ + const char *input = "\"\\ud840\\udd26,\\ud840\\udd27,\\ud800\\udd26,\\ud800\\udd27\""; + const char *expected = "\xF0\xA0\x84\xA6,\xF0\xA0\x84\xA7,\xF0\x90\x84\xA6,\xF0\x90\x84\xA7"; + struct json_object *parse_result = json_tokener_parse((char*)input); + const char *unjson = json_object_get_string(parse_result); + + printf("input: %s\n", input); + + int strings_match = !strcmp( expected, unjson); + int retval = 0; + if (strings_match) + { + printf("JSON parse result is correct: %s\n", unjson); + printf("PASS\n"); + } else { + printf("JSON parse result doesn't match expected string\n"); + printf("expected string bytes: "); + print_hex( expected); + printf("parsed string bytes: "); + print_hex( unjson); + printf("FAIL\n"); + retval = 1; + } + json_object_put(parse_result); + return retval; +} diff --git a/3P/json/tests/test4.expected b/3P/json/tests/test4.expected new file mode 100644 index 00000000..68d4336d --- /dev/null +++ b/3P/json/tests/test4.expected @@ -0,0 +1,3 @@ +input: "\ud840\udd26,\ud840\udd27,\ud800\udd26,\ud800\udd27" +JSON parse result is correct: 𠄦,ð „§,ð„¦,ð„§ +PASS diff --git a/3P/json/tests/test4.test b/3P/json/tests/test4.test new file mode 100755 index 00000000..8bcc4609 --- /dev/null +++ b/3P/json/tests/test4.test @@ -0,0 +1,12 @@ +#!/bin/sh + +# Common definitions +if test -z "$srcdir"; then + srcdir="${0%/*}" + test "$srcdir" = "$0" && srcdir=. + test -z "$srcdir" && srcdir=. +fi +. "$srcdir/test-defs.sh" + +run_output_test test4 +exit $? diff --git a/3P/json/tests/testReplaceExisting.c b/3P/json/tests/testReplaceExisting.c new file mode 100644 index 00000000..6db7b98e --- /dev/null +++ b/3P/json/tests/testReplaceExisting.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include + +#include "json.h" + +int main(int argc, char **argv) +{ + MC_SET_DEBUG(1); + + /* + * Check that replacing an existing object keeps the key valid, + * and that it keeps the order the same. + */ + json_object *my_object = json_object_new_object(); + json_object_object_add(my_object, "foo1", json_object_new_string("bar1")); + json_object_object_add(my_object, "foo2", json_object_new_string("bar2")); + json_object_object_add(my_object, "deleteme", json_object_new_string("bar2")); + json_object_object_add(my_object, "foo3", json_object_new_string("bar3")); + + printf("==== delete-in-loop test starting ====\n"); + + int orig_count = 0; + json_object_object_foreach(my_object, key0, val0) + { + printf("Key at index %d is [%s]", orig_count, key0); + if (strcmp(key0, "deleteme") == 0) + { + json_object_object_del(my_object, key0); + printf(" (deleted)\n"); + } + else + printf(" (kept)\n"); + orig_count++; + } + + printf("==== replace-value first loop starting ====\n"); + + const char *original_key = NULL; + orig_count = 0; + json_object_object_foreach(my_object, key, val) + { + printf("Key at index %d is [%s]\n", orig_count, key); + orig_count++; + if (strcmp(key, "foo2") != 0) + continue; + printf("replacing value for key [%s]\n", key); + original_key = key; + json_object_object_add(my_object, key, json_object_new_string("zzz")); + } + + printf("==== second loop starting ====\n"); + + int new_count = 0; + int retval = 0; + json_object_object_foreach(my_object, key2, val2) + { + printf("Key at index %d is [%s]\n", new_count, key2); + new_count++; + if (strcmp(key2, "foo2") != 0) + continue; + printf("pointer for key [%s] does %smatch\n", key2, + (key2 == original_key) ? "" : "NOT "); + if (key2 != original_key) + retval = 1; + } + if (new_count != orig_count) + { + printf("mismatch between original count (%d) and new count (%d)\n", + orig_count, new_count); + retval = 1; + } + + json_object_put( my_object ); + + return retval; +} diff --git a/3P/json/tests/testReplaceExisting.expected b/3P/json/tests/testReplaceExisting.expected new file mode 100644 index 00000000..b1d4461b --- /dev/null +++ b/3P/json/tests/testReplaceExisting.expected @@ -0,0 +1,15 @@ +==== delete-in-loop test starting ==== +Key at index 0 is [foo1] (kept) +Key at index 1 is [foo2] (kept) +Key at index 2 is [deleteme] (deleted) +Key at index 3 is [foo3] (kept) +==== replace-value first loop starting ==== +Key at index 0 is [foo1] +Key at index 1 is [foo2] +replacing value for key [foo2] +Key at index 2 is [foo3] +==== second loop starting ==== +Key at index 0 is [foo1] +Key at index 1 is [foo2] +pointer for key [foo2] does match +Key at index 2 is [foo3] diff --git a/3P/json/tests/testReplaceExisting.test b/3P/json/tests/testReplaceExisting.test new file mode 100755 index 00000000..ec5cbf19 --- /dev/null +++ b/3P/json/tests/testReplaceExisting.test @@ -0,0 +1,12 @@ +#!/bin/sh + +# Common definitions +if test -z "$srcdir"; then + srcdir="${0%/*}" + test "$srcdir" = "$0" && srcdir=. + test -z "$srcdir" && srcdir=. +fi +. "$srcdir/test-defs.sh" + +run_output_test testReplaceExisting +exit $? diff --git a/3P/json/tests/test_cast.c b/3P/json/tests/test_cast.c new file mode 100644 index 00000000..72c8cc4a --- /dev/null +++ b/3P/json/tests/test_cast.c @@ -0,0 +1,106 @@ +/* + * Tests if casting within the json_object_get_* functions work correctly. + * Also checks the json_object_get_type and json_object_is_type functions. + */ + +#include +#include +#include +#include "config.h" + +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +static void getit(struct json_object *new_obj, const char *field); +static void checktype_header(void); +static void checktype(struct json_object *new_obj, const char *field); + +int main(int argc, char **argv) +{ + const char *input = "{\n\ + \"string_of_digits\": \"123\",\n\ + \"regular_number\": 222,\n\ + \"decimal_number\": 99.55,\n\ + \"boolean_true\": true,\n\ + \"boolean_false\": false,\n\ + \"big_number\": 2147483649,\n\ + \"a_null\": null,\n\ + }"; + /* Note: 2147483649 = INT_MAX + 2 */ + + struct json_object *new_obj; + + new_obj = json_tokener_parse(input); + printf("Parsed input: %s\n", input); + printf("Result is %s\n", (new_obj == NULL) ? "NULL (error!)" : "not NULL"); + if (!new_obj) + return 1; // oops, we failed. + + getit(new_obj, "string_of_digits"); + getit(new_obj, "regular_number"); + getit(new_obj, "decimal_number"); + getit(new_obj, "boolean_true"); + getit(new_obj, "boolean_false"); + getit(new_obj, "big_number"); + getit(new_obj, "a_null"); + + // Now check the behaviour of the json_object_is_type() function. + printf("\n================================\n"); + checktype_header(); + checktype(new_obj, NULL); + checktype(new_obj, "string_of_digits"); + checktype(new_obj, "regular_number"); + checktype(new_obj, "decimal_number"); + checktype(new_obj, "boolean_true"); + checktype(new_obj, "boolean_false"); + checktype(new_obj, "big_number"); + checktype(new_obj, "a_null"); + + json_object_put(new_obj); + + return 0; +} + +static void getit(struct json_object *new_obj, const char *field) +{ + struct json_object *o = json_object_object_get(new_obj, field); + + enum json_type o_type = json_object_get_type(o); + printf("new_obj.%s json_object_get_type()=%s\n", field, + json_type_to_name(o_type)); + printf("new_obj.%s json_object_get_int()=%d\n", field, + json_object_get_int(o)); + printf("new_obj.%s json_object_get_int64()=%" PRId64 "\n", field, + json_object_get_int64(o)); + printf("new_obj.%s json_object_get_boolean()=%d\n", field, + json_object_get_boolean(o)); + printf("new_obj.%s json_object_get_double()=%f\n", field, + json_object_get_double(o)); +} + +static void checktype_header() +{ + printf("json_object_is_type: %s,%s,%s,%s,%s,%s,%s\n", + json_type_to_name(json_type_null), + json_type_to_name(json_type_boolean), + json_type_to_name(json_type_double), + json_type_to_name(json_type_int), + json_type_to_name(json_type_object), + json_type_to_name(json_type_array), + json_type_to_name(json_type_string)); +} +static void checktype(struct json_object *new_obj, const char *field) +{ + struct json_object *o = field ? json_object_object_get(new_obj, field) : new_obj; + printf("new_obj%s%-18s: %d,%d,%d,%d,%d,%d,%d\n", + field ? "." : " ", field ? field : "", + json_object_is_type(o, json_type_null), + json_object_is_type(o, json_type_boolean), + json_object_is_type(o, json_type_double), + json_object_is_type(o, json_type_int), + json_object_is_type(o, json_type_object), + json_object_is_type(o, json_type_array), + json_object_is_type(o, json_type_string)); +} diff --git a/3P/json/tests/test_cast.expected b/3P/json/tests/test_cast.expected new file mode 100644 index 00000000..76ff8231 --- /dev/null +++ b/3P/json/tests/test_cast.expected @@ -0,0 +1,56 @@ +Parsed input: { + "string_of_digits": "123", + "regular_number": 222, + "decimal_number": 99.55, + "boolean_true": true, + "boolean_false": false, + "big_number": 2147483649, + "a_null": null, + } +Result is not NULL +new_obj.string_of_digits json_object_get_type()=string +new_obj.string_of_digits json_object_get_int()=123 +new_obj.string_of_digits json_object_get_int64()=123 +new_obj.string_of_digits json_object_get_boolean()=1 +new_obj.string_of_digits json_object_get_double()=123.000000 +new_obj.regular_number json_object_get_type()=int +new_obj.regular_number json_object_get_int()=222 +new_obj.regular_number json_object_get_int64()=222 +new_obj.regular_number json_object_get_boolean()=1 +new_obj.regular_number json_object_get_double()=222.000000 +new_obj.decimal_number json_object_get_type()=double +new_obj.decimal_number json_object_get_int()=99 +new_obj.decimal_number json_object_get_int64()=99 +new_obj.decimal_number json_object_get_boolean()=1 +new_obj.decimal_number json_object_get_double()=99.550000 +new_obj.boolean_true json_object_get_type()=boolean +new_obj.boolean_true json_object_get_int()=1 +new_obj.boolean_true json_object_get_int64()=1 +new_obj.boolean_true json_object_get_boolean()=1 +new_obj.boolean_true json_object_get_double()=1.000000 +new_obj.boolean_false json_object_get_type()=boolean +new_obj.boolean_false json_object_get_int()=0 +new_obj.boolean_false json_object_get_int64()=0 +new_obj.boolean_false json_object_get_boolean()=0 +new_obj.boolean_false json_object_get_double()=0.000000 +new_obj.big_number json_object_get_type()=int +new_obj.big_number json_object_get_int()=2147483647 +new_obj.big_number json_object_get_int64()=2147483649 +new_obj.big_number json_object_get_boolean()=1 +new_obj.big_number json_object_get_double()=2147483649.000000 +new_obj.a_null json_object_get_type()=null +new_obj.a_null json_object_get_int()=0 +new_obj.a_null json_object_get_int64()=0 +new_obj.a_null json_object_get_boolean()=0 +new_obj.a_null json_object_get_double()=0.000000 + +================================ +json_object_is_type: null,boolean,double,int,object,array,string +new_obj : 0,0,0,0,1,0,0 +new_obj.string_of_digits : 0,0,0,0,0,0,1 +new_obj.regular_number : 0,0,0,1,0,0,0 +new_obj.decimal_number : 0,0,1,0,0,0,0 +new_obj.boolean_true : 0,1,0,0,0,0,0 +new_obj.boolean_false : 0,1,0,0,0,0,0 +new_obj.big_number : 0,0,0,1,0,0,0 +new_obj.a_null : 1,0,0,0,0,0,0 diff --git a/3P/json/tests/test_cast.test b/3P/json/tests/test_cast.test new file mode 100755 index 00000000..21024677 --- /dev/null +++ b/3P/json/tests/test_cast.test @@ -0,0 +1,12 @@ +#!/bin/sh + +# Common definitions +if test -z "$srcdir"; then + srcdir="${0%/*}" + test "$srcdir" = "$0" && srcdir=. + test -z "$srcdir" && srcdir=. +fi +. "$srcdir/test-defs.sh" + +run_output_test test_cast +exit $? diff --git a/3P/json/tests/test_locale.c b/3P/json/tests/test_locale.c new file mode 100644 index 00000000..da070cf5 --- /dev/null +++ b/3P/json/tests/test_locale.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include + +#include "config.h" +#include "json.h" +#include "json_tokener.h" + +#ifdef HAVE_LOCALE_H +#include +#endif /* HAVE_LOCALE_H */ + +int main(int argc, char **argv) +{ + json_object *new_obj; +#ifdef HAVE_SETLOCALE + setlocale(LC_NUMERIC, "de_DE"); +#else + printf("No locale\n"); +#endif + + MC_SET_DEBUG(1); + + new_obj = json_tokener_parse("[1.2,3.4,123456.78,5.0,2.3e10]"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + printf("new_obj.to_string()=%s\n", json_object_to_json_string_ext(new_obj,JSON_C_TO_STRING_NOZERO)); + json_object_put(new_obj); +} + diff --git a/3P/json/tests/test_locale.expected b/3P/json/tests/test_locale.expected new file mode 100644 index 00000000..80317851 --- /dev/null +++ b/3P/json/tests/test_locale.expected @@ -0,0 +1,2 @@ +new_obj.to_string()=[ 1.200000, 3.400000, 123456.780000, 5.000000, 23000000000.000000 ] +new_obj.to_string()=[1.2,3.4,123456.78,5.0,23000000000.0] diff --git a/3P/json/tests/test_locale.test b/3P/json/tests/test_locale.test new file mode 100755 index 00000000..e4b6c6dc --- /dev/null +++ b/3P/json/tests/test_locale.test @@ -0,0 +1,12 @@ +#!/bin/sh + +# Common definitions +if test -z "$srcdir"; then + srcdir="${0%/*}" + test "$srcdir" = "$0" && srcdir=. + test -z "$srcdir" && srcdir=. +fi +. "$srcdir/test-defs.sh" + +run_output_test test_locale +exit $? diff --git a/3P/json/tests/test_null.c b/3P/json/tests/test_null.c new file mode 100644 index 00000000..1f07910e --- /dev/null +++ b/3P/json/tests/test_null.c @@ -0,0 +1,57 @@ +/* +* Tests if binary strings are supported. +*/ + +#include +#include +#include "config.h" + +#include "json_inttypes.h" +#include "json_object.h" +#include "json_tokener.h" + +int main() +{ + // this test has a space after the null character. check that it's still included + const char *input = " \0 "; + const char *expected = "\" \\u0000 \""; + struct json_object *string = json_object_new_string_len(input, 3); + const char *json = json_object_to_json_string(string); + + int strings_match = !strcmp( expected, json); + int retval = 0; + if (strings_match) + { + printf("JSON write result is correct: %s\n", json); + printf("PASS\n"); + } else { + printf("JSON write result doesn't match expected string\n"); + printf("expected string: "); + printf("%s\n", expected); + printf("parsed string: "); + printf("%s\n", json); + printf("FAIL\n"); + retval=1; + } + json_object_put(string); + + struct json_object *parsed_str = json_tokener_parse(expected); + if (parsed_str) + { + int parsed_len = json_object_get_string_len(parsed_str); + const char *parsed_cstr = json_object_get_string(parsed_str); + int ii; + printf("Re-parsed object string len=%d, chars=[", parsed_len); + for (ii = 0; ii < parsed_len ; ii++) + { + printf("%s%d", (ii ? ", " : ""), (int)parsed_cstr[ii]); + } + printf("]\n"); + json_object_put(parsed_str); + } + else + { + printf("ERROR: failed to parse\n"); + } + return retval; +} diff --git a/3P/json/tests/test_null.expected b/3P/json/tests/test_null.expected new file mode 100644 index 00000000..52d28900 --- /dev/null +++ b/3P/json/tests/test_null.expected @@ -0,0 +1,3 @@ +JSON write result is correct: " \u0000 " +PASS +Re-parsed object string len=3, chars=[32, 0, 32] diff --git a/3P/json/tests/test_null.test b/3P/json/tests/test_null.test new file mode 100755 index 00000000..469ec64a --- /dev/null +++ b/3P/json/tests/test_null.test @@ -0,0 +1,12 @@ +#!/bin/sh + +# Common definitions +if test -z "$srcdir"; then + srcdir="${0%/*}" + test "$srcdir" = "$0" && srcdir=. + test -z "$srcdir" && srcdir=. +fi +. "$srcdir/test-defs.sh" + +run_output_test test_null +exit $? diff --git a/3P/json/tests/test_parse.c b/3P/json/tests/test_parse.c new file mode 100644 index 00000000..1a59a6bd --- /dev/null +++ b/3P/json/tests/test_parse.c @@ -0,0 +1,290 @@ +#include +#include +#include +#include +#include + +#include "json.h" +#include "json_tokener.h" + +static void test_basic_parse(void); +static void test_verbose_parse(void); +static void test_incremental_parse(void); + +int main(int argc, char **argv) +{ + MC_SET_DEBUG(1); + + test_basic_parse(); + printf("==================================\n"); + test_verbose_parse(); + printf("==================================\n"); + test_incremental_parse(); + printf("==================================\n"); +} + +static void test_basic_parse() +{ + json_object *new_obj; + + new_obj = json_tokener_parse("\"\003\""); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("/* hello */\"foo\""); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("// hello\n\"foo\""); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("\"\\u0041\\u0042\\u0043\""); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("null"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("True"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("12"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("12.3"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("[\"\\n\"]"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("[\"\\nabc\\n\"]"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("[null]"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("[]"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("[false]"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("[\"abc\",null,\"def\",12]"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("{}"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("{ \"foo\": \"bar\" }"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("{ \"foo\": \"bar\", \"baz\": null, \"bool0\": true }"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("{ \"foo\": [null, \"foo\"] }"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); + + new_obj = json_tokener_parse("{ \"abc\": 12, \"foo\": \"bar\", \"bool0\": false, \"bool1\": true, \"arr\": [ 1, 2, 3, null, 5 ] }"); + printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); + json_object_put(new_obj); +} + +static void test_verbose_parse() +{ + json_object *new_obj; + enum json_tokener_error error = json_tokener_success; + + new_obj = json_tokener_parse_verbose("{ foo }", &error); + assert (error == json_tokener_error_parse_object_key_name); + assert (new_obj == NULL); + + new_obj = json_tokener_parse("{ foo }"); + assert (new_obj == NULL); + + new_obj = json_tokener_parse("foo"); + assert (new_obj == NULL); + new_obj = json_tokener_parse_verbose("foo", &error); + assert (new_obj == NULL); + + /* b/c the string starts with 'f' parsing return a boolean error */ + assert (error == json_tokener_error_parse_boolean); + + printf("json_tokener_parse_versbose() OK\n"); +} + +struct incremental_step { + const char *string_to_parse; + int length; + int char_offset; + enum json_tokener_error expected_error; + int reset_tokener; +} incremental_steps[] = { + + /* Check that full json messages can be parsed, both w/ and w/o a reset */ + { "{ \"foo\": 123 }", -1, -1, json_tokener_success, 0 }, + { "{ \"foo\": 456 }", -1, -1, json_tokener_success, 1 }, + { "{ \"foo\": 789 }", -1, -1, json_tokener_success, 1 }, + + /* Check a basic incremental parse */ + { "{ \"foo", -1, -1, json_tokener_continue, 0 }, + { "\": {\"bar", -1, -1, json_tokener_continue, 0 }, + { "\":13}}", -1, -1, json_tokener_success, 1 }, + + /* Check that json_tokener_reset actually resets */ + { "{ \"foo", -1, -1, json_tokener_continue, 1 }, + { ": \"bar\"}", -1, 0, json_tokener_error_parse_unexpected, 1 }, + + /* Check incremental parsing with trailing characters */ + { "{ \"foo", -1, -1, json_tokener_continue, 0 }, + { "\": {\"bar", -1, -1, json_tokener_continue, 0 }, + { "\":13}}XXXX", 10, 6, json_tokener_success, 0 }, + { "XXXX", 4, 0, json_tokener_error_parse_unexpected, 1 }, + + /* Check that trailing characters can change w/o a reset */ + { "{\"x\": 123 }\"X\"", -1, 11, json_tokener_success, 0 }, + { "\"Y\"", -1, -1, json_tokener_success, 1 }, + + /* To stop parsing a number we need to reach a non-digit, e.g. a \0 */ + { "1", 1, 1, json_tokener_continue, 0 }, + { "2", 2, 1, json_tokener_success, 0 }, + + /* Strings have a well defined end point, so we can stop at the quote */ + { "\"blue\"", -1, -1, json_tokener_success, 0 }, + + /* Check each of the escape sequences defined by the spec */ + { "\"\\\"\"", -1, -1, json_tokener_success, 0 }, + { "\"\\\\\"", -1, -1, json_tokener_success, 0 }, + { "\"\\b\"", -1, -1, json_tokener_success, 0 }, + { "\"\\f\"", -1, -1, json_tokener_success, 0 }, + { "\"\\n\"", -1, -1, json_tokener_success, 0 }, + { "\"\\r\"", -1, -1, json_tokener_success, 0 }, + { "\"\\t\"", -1, -1, json_tokener_success, 0 }, + + { "[1,2,3]", -1, -1, json_tokener_success, 0 }, + + /* This behaviour doesn't entirely follow the json spec, but until we have + a way to specify how strict to be we follow Postel's Law and be liberal + in what we accept (up to a point). */ + { "[1,2,3,]", -1, -1, json_tokener_success, 0 }, + { "[1,2,,3,]", -1, 5, json_tokener_error_parse_unexpected, 0 }, + + { "[1,2,3,]", -1, 7, json_tokener_error_parse_unexpected, 3 }, + { "{\"a\":1,}", -1, 7, json_tokener_error_parse_unexpected, 3 }, + + { NULL, -1, -1, json_tokener_success, 0 }, +}; + +static void test_incremental_parse() +{ + json_object *new_obj; + enum json_tokener_error jerr; + json_tokener *tok; + const char *string_to_parse; + int ii; + int num_ok, num_error; + + num_ok = 0; + num_error = 0; + + printf("Starting incremental tests.\n"); + printf("Note: quotes and backslashes seen in the output here are literal values passed\n"); + printf(" to the parse functions. e.g. this is 4 characters: \"\\f\"\n"); + + string_to_parse = "{ \"foo"; /* } */ + printf("json_tokener_parse(%s) ... ", string_to_parse); + new_obj = json_tokener_parse(string_to_parse); + if (new_obj == NULL) printf("got error as expected\n"); + + /* test incremental parsing in various forms */ + tok = json_tokener_new(); + for (ii = 0; incremental_steps[ii].string_to_parse != NULL; ii++) + { + int this_step_ok = 0; + struct incremental_step *step = &incremental_steps[ii]; + int length = step->length; + int expected_char_offset = step->char_offset; + + if (step->reset_tokener & 2) + json_tokener_set_flags(tok, JSON_TOKENER_STRICT); + else + json_tokener_set_flags(tok, 0); + + if (length == -1) + length = strlen(step->string_to_parse); + if (expected_char_offset == -1) + expected_char_offset = length; + + printf("json_tokener_parse_ex(tok, %-12s, %3d) ... ", + step->string_to_parse, length); + new_obj = json_tokener_parse_ex(tok, step->string_to_parse, length); + + jerr = json_tokener_get_error(tok); + if (step->expected_error != json_tokener_success) + { + if (new_obj != NULL) + printf("ERROR: invalid object returned: %s\n", + json_object_to_json_string(new_obj)); + else if (jerr != step->expected_error) + printf("ERROR: got wrong error: %s\n", + json_tokener_error_desc(jerr)); + else if (tok->char_offset != expected_char_offset) + printf("ERROR: wrong char_offset %d != expected %d\n", + tok->char_offset, + expected_char_offset); + else + { + printf("OK: got correct error: %s\n", json_tokener_error_desc(jerr)); + this_step_ok = 1; + } + } + else + { + if (new_obj == NULL) + printf("ERROR: expected valid object, instead: %s\n", + json_tokener_error_desc(jerr)); + else if (tok->char_offset != expected_char_offset) + printf("ERROR: wrong char_offset %d != expected %d\n", + tok->char_offset, + expected_char_offset); + else + { + printf("OK: got object of type [%s]: %s\n", + json_type_to_name(json_object_get_type(new_obj)), + json_object_to_json_string(new_obj)); + this_step_ok = 1; + } + } + + if (new_obj) + json_object_put(new_obj); + + if (step->reset_tokener & 1) + json_tokener_reset(tok); + + if (this_step_ok) + num_ok++; + else + num_error++; + } + + json_tokener_free(tok); + + printf("End Incremental Tests OK=%d ERROR=%d\n", num_ok, num_error); + + return; +} diff --git a/3P/json/tests/test_parse.expected b/3P/json/tests/test_parse.expected new file mode 100644 index 00000000..f0af0fa1 --- /dev/null +++ b/3P/json/tests/test_parse.expected @@ -0,0 +1,57 @@ +new_obj.to_string()="\u0003" +new_obj.to_string()="foo" +new_obj.to_string()="foo" +new_obj.to_string()="ABC" +new_obj.to_string()=null +new_obj.to_string()=true +new_obj.to_string()=12 +new_obj.to_string()=12.300000 +new_obj.to_string()=[ "\n" ] +new_obj.to_string()=[ "\nabc\n" ] +new_obj.to_string()=[ null ] +new_obj.to_string()=[ ] +new_obj.to_string()=[ false ] +new_obj.to_string()=[ "abc", null, "def", 12 ] +new_obj.to_string()={ } +new_obj.to_string()={ "foo": "bar" } +new_obj.to_string()={ "foo": "bar", "baz": null, "bool0": true } +new_obj.to_string()={ "foo": [ null, "foo" ] } +new_obj.to_string()={ "abc": 12, "foo": "bar", "bool0": false, "bool1": true, "arr": [ 1, 2, 3, null, 5 ] } +================================== +json_tokener_parse_versbose() OK +================================== +Starting incremental tests. +Note: quotes and backslashes seen in the output here are literal values passed + to the parse functions. e.g. this is 4 characters: "\f" +json_tokener_parse({ "foo) ... got error as expected +json_tokener_parse_ex(tok, { "foo": 123 }, 14) ... OK: got object of type [object]: { "foo": 123 } +json_tokener_parse_ex(tok, { "foo": 456 }, 14) ... OK: got object of type [object]: { "foo": 456 } +json_tokener_parse_ex(tok, { "foo": 789 }, 14) ... OK: got object of type [object]: { "foo": 789 } +json_tokener_parse_ex(tok, { "foo , 6) ... OK: got correct error: continue +json_tokener_parse_ex(tok, ": {"bar , 8) ... OK: got correct error: continue +json_tokener_parse_ex(tok, ":13}} , 6) ... OK: got object of type [object]: { "foo": { "bar": 13 } } +json_tokener_parse_ex(tok, { "foo , 6) ... OK: got correct error: continue +json_tokener_parse_ex(tok, : "bar"} , 8) ... OK: got correct error: unexpected character +json_tokener_parse_ex(tok, { "foo , 6) ... OK: got correct error: continue +json_tokener_parse_ex(tok, ": {"bar , 8) ... OK: got correct error: continue +json_tokener_parse_ex(tok, ":13}}XXXX , 10) ... OK: got object of type [object]: { "foo": { "bar": 13 } } +json_tokener_parse_ex(tok, XXXX , 4) ... OK: got correct error: unexpected character +json_tokener_parse_ex(tok, {"x": 123 }"X", 14) ... OK: got object of type [object]: { "x": 123 } +json_tokener_parse_ex(tok, "Y" , 3) ... OK: got object of type [string]: "Y" +json_tokener_parse_ex(tok, 1 , 1) ... OK: got correct error: continue +json_tokener_parse_ex(tok, 2 , 2) ... OK: got object of type [int]: 12 +json_tokener_parse_ex(tok, "blue" , 6) ... OK: got object of type [string]: "blue" +json_tokener_parse_ex(tok, "\"" , 4) ... OK: got object of type [string]: "\"" +json_tokener_parse_ex(tok, "\\" , 4) ... OK: got object of type [string]: "\\" +json_tokener_parse_ex(tok, "\b" , 4) ... OK: got object of type [string]: "\b" +json_tokener_parse_ex(tok, "\f" , 4) ... OK: got object of type [string]: "\f" +json_tokener_parse_ex(tok, "\n" , 4) ... OK: got object of type [string]: "\n" +json_tokener_parse_ex(tok, "\r" , 4) ... OK: got object of type [string]: "\r" +json_tokener_parse_ex(tok, "\t" , 4) ... OK: got object of type [string]: "\t" +json_tokener_parse_ex(tok, [1,2,3] , 7) ... OK: got object of type [array]: [ 1, 2, 3 ] +json_tokener_parse_ex(tok, [1,2,3,] , 8) ... OK: got object of type [array]: [ 1, 2, 3 ] +json_tokener_parse_ex(tok, [1,2,,3,] , 9) ... OK: got correct error: unexpected character +json_tokener_parse_ex(tok, [1,2,3,] , 8) ... OK: got correct error: unexpected character +json_tokener_parse_ex(tok, {"a":1,} , 8) ... OK: got correct error: unexpected character +End Incremental Tests OK=29 ERROR=0 +================================== diff --git a/3P/json/tests/test_parse.test b/3P/json/tests/test_parse.test new file mode 100755 index 00000000..70d1c82e --- /dev/null +++ b/3P/json/tests/test_parse.test @@ -0,0 +1,12 @@ +#!/bin/sh + +# Common definitions +if test -z "$srcdir"; then + srcdir="${0%/*}" + test "$srcdir" = "$0" && srcdir=. + test -z "$srcdir" && srcdir=. +fi +. "$srcdir/test-defs.sh" + +run_output_test test_parse +exit $? diff --git a/3P/json/tests/test_parse_int64.c b/3P/json/tests/test_parse_int64.c new file mode 100644 index 00000000..c251e013 --- /dev/null +++ b/3P/json/tests/test_parse_int64.c @@ -0,0 +1,115 @@ + +#include +#include + +#include "config.h" + +#include "json_inttypes.h" +#include "json_util.h" + +void checkit(const char *buf) +{ + int64_t cint64 = -666; + + int retval = json_parse_int64(buf, &cint64); + printf("buf=%s parseit=%d, value=%" PRId64 " \n", buf, retval, cint64); +} + +/** + * This test calls json_parse_int64 with a variety of different strings. + * It's purpose is to ensure that the results are consistent across all + * different environments that it might be executed in. + * + * This always exits with a 0 exit value. The output should be compared + * against previously saved expected output. + */ +int main() +{ + char buf[100]; + + checkit("x"); + + checkit("0"); + checkit("-0"); + + checkit("00000000"); + checkit("-00000000"); + + checkit("1"); + + strcpy(buf, "2147483647"); // aka INT32_MAX + checkit(buf); + + strcpy(buf, "-1"); + checkit(buf); + + strcpy(buf, " -1"); + checkit(buf); + + strcpy(buf, "00001234"); + checkit(buf); + + strcpy(buf, "0001234x"); + checkit(buf); + + strcpy(buf, "-00001234"); + checkit(buf); + + strcpy(buf, "-00001234x"); + checkit(buf); + + strcpy(buf, "4294967295"); // aka UINT32_MAX + + sprintf(buf, "4294967296"); // aka UINT32_MAX + 1 + + strcpy(buf, "21474836470"); // INT32_MAX * 10 + checkit(buf); + + strcpy(buf, "31474836470"); // INT32_MAX * 10 + a bunch + checkit(buf); + + strcpy(buf, "-2147483647"); // INT32_MIN + 1 + checkit(buf); + + strcpy(buf, "-2147483648"); // INT32_MIN + checkit(buf); + + strcpy(buf, "-2147483649"); // INT32_MIN - 1 + checkit(buf); + + strcpy(buf, "-21474836480"); // INT32_MIN * 10 + checkit(buf); + + strcpy(buf, "9223372036854775806"); // INT64_MAX - 1 + checkit(buf); + + strcpy(buf, "9223372036854775807"); // INT64_MAX + checkit(buf); + + strcpy(buf, "9223372036854775808"); // INT64_MAX + 1 + checkit(buf); + + strcpy(buf, "-9223372036854775808"); // INT64_MIN + checkit(buf); + + strcpy(buf, "-9223372036854775809"); // INT64_MIN - 1 + checkit(buf); + + strcpy(buf, "18446744073709551614"); // UINT64_MAX - 1 + checkit(buf); + + strcpy(buf, "18446744073709551615"); // UINT64_MAX + checkit(buf); + + strcpy(buf, "18446744073709551616"); // UINT64_MAX + 1 + checkit(buf); + + strcpy(buf, "-18446744073709551616"); // -UINT64_MAX + checkit(buf); + + // Ensure we can still parse valid numbers after parsing out of range ones. + strcpy(buf, "123"); + checkit(buf); + + return 0; +} diff --git a/3P/json/tests/test_parse_int64.expected b/3P/json/tests/test_parse_int64.expected new file mode 100644 index 00000000..d9cdf5ac --- /dev/null +++ b/3P/json/tests/test_parse_int64.expected @@ -0,0 +1,29 @@ +buf=x parseit=1, value=-666 +buf=0 parseit=0, value=0 +buf=-0 parseit=0, value=0 +buf=00000000 parseit=0, value=0 +buf=-00000000 parseit=0, value=0 +buf=1 parseit=0, value=1 +buf=2147483647 parseit=0, value=2147483647 +buf=-1 parseit=0, value=-1 +buf= -1 parseit=0, value=-1 +buf=00001234 parseit=0, value=1234 +buf=0001234x parseit=0, value=1234 +buf=-00001234 parseit=0, value=-1234 +buf=-00001234x parseit=0, value=-1234 +buf=21474836470 parseit=0, value=21474836470 +buf=31474836470 parseit=0, value=31474836470 +buf=-2147483647 parseit=0, value=-2147483647 +buf=-2147483648 parseit=0, value=-2147483648 +buf=-2147483649 parseit=0, value=-2147483649 +buf=-21474836480 parseit=0, value=-21474836480 +buf=9223372036854775806 parseit=0, value=9223372036854775806 +buf=9223372036854775807 parseit=0, value=9223372036854775807 +buf=9223372036854775808 parseit=0, value=9223372036854775807 +buf=-9223372036854775808 parseit=0, value=-9223372036854775808 +buf=-9223372036854775809 parseit=0, value=-9223372036854775808 +buf=18446744073709551614 parseit=0, value=9223372036854775807 +buf=18446744073709551615 parseit=0, value=9223372036854775807 +buf=18446744073709551616 parseit=0, value=9223372036854775807 +buf=-18446744073709551616 parseit=0, value=-9223372036854775808 +buf=123 parseit=0, value=123 diff --git a/3P/json/tests/test_printbuf.c b/3P/json/tests/test_printbuf.c new file mode 100644 index 00000000..c8b8ad03 --- /dev/null +++ b/3P/json/tests/test_printbuf.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "printbuf.h" + +static void test_basic_printbuf_memset(void); +static void test_printbuf_memset_length(void); + +static void test_basic_printbuf_memset() +{ + struct printbuf *pb; + + printf("%s: starting test\n", __func__); + pb = printbuf_new(); + sprintbuf(pb, "blue:%d", 1); + printbuf_memset(pb, -1, 'x', 52); + printf("Buffer contents:%.*s\n", printbuf_length(pb), pb->buf); + printbuf_free(pb); + printf("%s: end test\n", __func__); +} + +static void test_printbuf_memset_length() +{ + struct printbuf *pb; + + printf("%s: starting test\n", __func__); + pb = printbuf_new(); + printbuf_memset(pb, -1, ' ', 0); + printbuf_memset(pb, -1, ' ', 0); + printbuf_memset(pb, -1, ' ', 0); + printbuf_memset(pb, -1, ' ', 0); + printbuf_memset(pb, -1, ' ', 0); + printf("Buffer length: %d\n", printbuf_length(pb)); + printbuf_memset(pb, -1, ' ', 2); + printbuf_memset(pb, -1, ' ', 4); + printbuf_memset(pb, -1, ' ', 6); + printf("Buffer length: %d\n", printbuf_length(pb)); + printbuf_memset(pb, -1, ' ', 6); + printf("Buffer length: %d\n", printbuf_length(pb)); + printbuf_memset(pb, -1, ' ', 8); + printbuf_memset(pb, -1, ' ', 10); + printbuf_memset(pb, -1, ' ', 10); + printbuf_memset(pb, -1, ' ', 10); + printbuf_memset(pb, -1, ' ', 20); + printf("Buffer length: %d\n", printbuf_length(pb)); + + // No length change should occur + printbuf_memset(pb, 0, 'x', 30); + printf("Buffer length: %d\n", printbuf_length(pb)); + + // This should extend it by one. + printbuf_memset(pb, 0, 'x', printbuf_length(pb) + 1); + printf("Buffer length: %d\n", printbuf_length(pb)); + + printbuf_free(pb); + printf("%s: end test\n", __func__); +} + +static void test_printbuf_memappend(int *before_resize); +static void test_printbuf_memappend(int *before_resize) +{ + struct printbuf *pb; + int initial_size; + + printf("%s: starting test\n", __func__); + pb = printbuf_new(); + printf("Buffer length: %d\n", printbuf_length(pb)); + + initial_size = pb->size; + + while(pb->size == initial_size) + { + printbuf_memappend_fast(pb, "x", 1); + } + *before_resize = printbuf_length(pb) - 1; + printf("Appended %d bytes for resize: [%s]\n", *before_resize + 1, pb->buf); + + printbuf_reset(pb); + printbuf_memappend_fast(pb, "bluexyz123", 3); + printf("Partial append: %d, [%s]\n", printbuf_length(pb), pb->buf); + + char with_nulls[] = { 'a', 'b', '\0', 'c' }; + printbuf_reset(pb); + printbuf_memappend_fast(pb, with_nulls, (int)sizeof(with_nulls)); + printf("With embedded \\0 character: %d, [%s]\n", printbuf_length(pb), pb->buf); + + printbuf_free(pb); + pb = printbuf_new(); + char *data = malloc(*before_resize); + memset(data, 'X', *before_resize); + printbuf_memappend_fast(pb, data, *before_resize); + printf("Append to just before resize: %d, [%s]\n", printbuf_length(pb), pb->buf); + + free(data); + printbuf_free(pb); + + pb = printbuf_new(); + data = malloc(*before_resize + 1); + memset(data, 'X', *before_resize + 1); + printbuf_memappend_fast(pb, data, *before_resize + 1); + printf("Append to just after resize: %d, [%s]\n", printbuf_length(pb), pb->buf); + + free(data); + + printbuf_free(pb); + printf("%s: end test\n", __func__); +} + +static void test_sprintbuf(int before_resize); +static void test_sprintbuf(int before_resize) +{ + struct printbuf *pb; + + printf("%s: starting test\n", __func__); + pb = printbuf_new(); + printf("Buffer length: %d\n", printbuf_length(pb)); + + char *data = malloc(before_resize + 1 + 1); + memset(data, 'X', before_resize + 1 + 1); + data[before_resize + 1] = '\0'; + sprintbuf(pb, "%s", data); + free(data); + printf("sprintbuf to just after resize(%d+1): %d, [%s], strlen(buf)=%d\n", before_resize, printbuf_length(pb), pb->buf, (int)strlen(pb->buf)); + + printbuf_reset(pb); + sprintbuf(pb, "plain"); + printf("%d, [%s]\n", printbuf_length(pb), pb->buf); + + sprintbuf(pb, "%d", 1); + printf("%d, [%s]\n", printbuf_length(pb), pb->buf); + + sprintbuf(pb, "%d", INT_MAX); + printf("%d, [%s]\n", printbuf_length(pb), pb->buf); + + sprintbuf(pb, "%d", INT_MIN); + printf("%d, [%s]\n", printbuf_length(pb), pb->buf); + + sprintbuf(pb, "%s", "%s"); + printf("%d, [%s]\n", printbuf_length(pb), pb->buf); + + printbuf_free(pb); + printf("%s: end test\n", __func__); +} + +int main(int argc, char **argv) +{ + int before_resize = 0; + + mc_set_debug(1); + + test_basic_printbuf_memset(); + printf("========================================\n"); + test_printbuf_memset_length(); + printf("========================================\n"); + test_printbuf_memappend(&before_resize); + printf("========================================\n"); + test_sprintbuf(before_resize); + printf("========================================\n"); + + return 0; +} diff --git a/3P/json/tests/test_printbuf.expected b/3P/json/tests/test_printbuf.expected new file mode 100644 index 00000000..142db0ba --- /dev/null +++ b/3P/json/tests/test_printbuf.expected @@ -0,0 +1,32 @@ +test_basic_printbuf_memset: starting test +Buffer contents:blue:1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +test_basic_printbuf_memset: end test +======================================== +test_printbuf_memset_length: starting test +Buffer length: 0 +Buffer length: 12 +Buffer length: 18 +Buffer length: 76 +Buffer length: 76 +Buffer length: 77 +test_printbuf_memset_length: end test +======================================== +test_printbuf_memappend: starting test +Buffer length: 0 +Appended 32 bytes for resize: [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] +Partial append: 3, [blu] +With embedded \0 character: 4, [ab] +Append to just before resize: 31, [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX] +Append to just after resize: 32, [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX] +test_printbuf_memappend: end test +======================================== +test_sprintbuf: starting test +Buffer length: 0 +sprintbuf to just after resize(31+1): 32, [XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX], strlen(buf)=32 +5, [plain] +6, [plain1] +16, [plain12147483647] +27, [plain12147483647-2147483648] +29, [plain12147483647-2147483648%s] +test_sprintbuf: end test +======================================== diff --git a/3P/json/tests/test_printbuf.test b/3P/json/tests/test_printbuf.test new file mode 100755 index 00000000..09d27c23 --- /dev/null +++ b/3P/json/tests/test_printbuf.test @@ -0,0 +1,12 @@ +#!/bin/sh + +# Common definitions +if test -z "$srcdir"; then + srcdir="${0%/*}" + test "$srcdir" = "$0" && srcdir=. + test -z "$srcdir" && srcdir=. +fi +. "$srcdir/test-defs.sh" + +run_output_test test_printbuf +exit $? diff --git a/3P/json/tests/test_set_serializer.c b/3P/json/tests/test_set_serializer.c new file mode 100644 index 00000000..0f122af2 --- /dev/null +++ b/3P/json/tests/test_set_serializer.c @@ -0,0 +1,71 @@ +#include +#include +#include + +#include "json.h" +#include "printbuf.h" + +struct myinfo { + int value; +}; + +static int freeit_was_called = 0; +static void freeit(json_object *jso, void *userdata) +{ + struct myinfo *info = userdata; + printf("freeit, value=%d\n", info->value); + // Don't actually free anything here, the userdata is stack allocated. + freeit_was_called = 1; +} +static int custom_serializer(struct json_object *o, + struct printbuf *pb, + int level, + int flags) +{ + sprintbuf(pb, "Custom Output"); + return 0; +} + +int main(int argc, char **argv) +{ + json_object *my_object; + + MC_SET_DEBUG(1); + + printf("Test setting, then resetting a custom serializer:\n"); + my_object = json_object_new_object(); + json_object_object_add(my_object, "abc", json_object_new_int(12)); + json_object_object_add(my_object, "foo", json_object_new_string("bar")); + + printf("my_object.to_string(standard)=%s\n", json_object_to_json_string(my_object)); + + struct myinfo userdata = { .value = 123 }; + json_object_set_serializer(my_object, custom_serializer, &userdata, freeit); + + printf("my_object.to_string(custom serializer)=%s\n", json_object_to_json_string(my_object)); + + printf("Next line of output should be from the custom freeit function:\n"); + freeit_was_called = 0; + json_object_set_serializer(my_object, NULL, NULL, NULL); + assert(freeit_was_called); + + printf("my_object.to_string(standard)=%s\n", json_object_to_json_string(my_object)); + + json_object_put(my_object); + + // ============================================ + + my_object = json_object_new_object(); + printf("Check that the custom serializer isn't free'd until the last json_object_put:\n"); + json_object_set_serializer(my_object, custom_serializer, &userdata, freeit); + json_object_get(my_object); + json_object_put(my_object); + printf("my_object.to_string(custom serializer)=%s\n", json_object_to_json_string(my_object)); + printf("Next line of output should be from the custom freeit function:\n"); + + freeit_was_called = 0; + json_object_put(my_object); + assert(freeit_was_called); + + return 0; +} diff --git a/3P/json/tests/test_set_serializer.expected b/3P/json/tests/test_set_serializer.expected new file mode 100644 index 00000000..ad44a905 --- /dev/null +++ b/3P/json/tests/test_set_serializer.expected @@ -0,0 +1,10 @@ +Test setting, then resetting a custom serializer: +my_object.to_string(standard)={ "abc": 12, "foo": "bar" } +my_object.to_string(custom serializer)=Custom Output +Next line of output should be from the custom freeit function: +freeit, value=123 +my_object.to_string(standard)={ "abc": 12, "foo": "bar" } +Check that the custom serializer isn't free'd until the last json_object_put: +my_object.to_string(custom serializer)=Custom Output +Next line of output should be from the custom freeit function: +freeit, value=123 diff --git a/3P/json/tests/test_set_serializer.test b/3P/json/tests/test_set_serializer.test new file mode 100755 index 00000000..728dfedf --- /dev/null +++ b/3P/json/tests/test_set_serializer.test @@ -0,0 +1,12 @@ +#!/bin/sh + +# Common definitions +if test -z "$srcdir"; then + srcdir="${0%/*}" + test "$srcdir" = "$0" && srcdir=. + test -z "$srcdir" && srcdir=. +fi +. "$srcdir/test-defs.sh" + +run_output_test test_set_serializer +exit $? diff --git a/3P/libubox/.gitignore b/3P/libubox/.gitignore new file mode 100644 index 00000000..0c59f966 --- /dev/null +++ b/3P/libubox/.gitignore @@ -0,0 +1,10 @@ +Makefile +CMakeCache.txt +CMakeFiles +*.cmake +*.a +*.so +*.dylib +install_manifest.txt +jshn +*-example diff --git a/3P/libubox/avl-cmp.c b/3P/libubox/avl-cmp.c new file mode 100644 index 00000000..2d0acecd --- /dev/null +++ b/3P/libubox/avl-cmp.c @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include "avl-cmp.h" + +int +avl_strcmp(const void *k1, const void *k2, void *ptr) +{ + return strcmp(k1, k2); +} + diff --git a/3P/libubox/avl-cmp.h b/3P/libubox/avl-cmp.h new file mode 100644 index 00000000..5adb495c --- /dev/null +++ b/3P/libubox/avl-cmp.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __AVL_CMP_H +#define __AVL_CMP_H + +int avl_strcmp(const void *k1, const void *k2, void *ptr); + +#endif diff --git a/3P/libubox/avl.c b/3P/libubox/avl.c new file mode 100644 index 00000000..8d0bf65a --- /dev/null +++ b/3P/libubox/avl.c @@ -0,0 +1,735 @@ +/* + * PacketBB handler library (see RFC 5444) + * Copyright (c) 2010 Henning Rogge + * Original OLSRd implementation by Hannes Gredler + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org/git for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + */ + +#include +#include +#include +#include +#include + +#include "avl.h" +#include "list.h" + +/** + * internal type save inline function to calculate the maximum of + * to integers without macro implementation. + * + * @param x first parameter of maximum function + * @param y second parameter of maximum function + * @return largest integer of both parameters + */ +static inline int avl_max(int x, int y) { + return x > y ? x : y; +} + +/** + * internal type save inline function to calculate the minimum of + * to integers without macro implementation. + * + * @param x first parameter of minimum function + * @param y second parameter of minimum function + * @return smallest integer of both parameters + */ +static inline int avl_min(int x, int y) { + return x < y ? x : y; +} + +static struct avl_node * +avl_find_rec(struct avl_node *node, const void *key, avl_tree_comp comp, void *ptr, int *cmp_result); +static void avl_insert_before(struct avl_tree *tree, struct avl_node *pos_node, struct avl_node *node); +static void avl_insert_after(struct avl_tree *tree, struct avl_node *pos_node, struct avl_node *node); +static void post_insert(struct avl_tree *tree, struct avl_node *node); +static void avl_delete_worker(struct avl_tree *tree, struct avl_node *node); +static void avl_remove(struct avl_tree *tree, struct avl_node *node); + +/** + * Initialize a new avl_tree struct + * @param tree pointer to avl-tree + * @param comp pointer to comparator for the tree + * @param allow_dups true if the tree allows multiple + * elements with the same + * @param ptr custom parameter for comparator + */ +void +avl_init(struct avl_tree *tree, avl_tree_comp comp, bool allow_dups, void *ptr) +{ + INIT_LIST_HEAD(&tree->list_head); + tree->root = NULL; + tree->count = 0; + tree->comp = comp; + tree->allow_dups = allow_dups; + tree->cmp_ptr = ptr; +} + +static inline struct avl_node *avl_next(struct avl_node *node) +{ + return list_entry(node->list.next, struct avl_node, list); +} + +/** + * Finds a node in an avl-tree with a certain key + * @param tree pointer to avl-tree + * @param key pointer to key + * @return pointer to avl-node with key, NULL if no node with + * this key exists. + */ +struct avl_node * +avl_find(const struct avl_tree *tree, const void *key) +{ + struct avl_node *node; + int diff; + + if (tree->root == NULL) + return NULL; + + node = avl_find_rec(tree->root, key, tree->comp, tree->cmp_ptr, &diff); + + return diff == 0 ? node : NULL; +} + +/** + * Finds the last node in an avl-tree with a key less or equal + * than the specified key + * @param tree pointer to avl-tree + * @param key pointer to specified key + * @return pointer to avl-node, NULL if no node with + * key less or equal specified key exists. + */ +struct avl_node * +avl_find_lessequal(const struct avl_tree *tree, const void *key) { + struct avl_node *node, *next; + int diff; + + if (tree->root == NULL) + return NULL; + + node = avl_find_rec(tree->root, key, tree->comp, tree->cmp_ptr, &diff); + + /* go left as long as keylist, &tree->list_head)) { + return NULL; + } + + node = (struct avl_node *)node->list.prev; + diff = (*tree->comp) (key, node->key, tree->cmp_ptr); + } + + /* go right as long as key>=next_node.key */ + next = node; + while (diff >= 0) { + node = next; + if (list_is_last(&node->list, &tree->list_head)) { + break; + } + + next = (struct avl_node *)node->list.next; + diff = (*tree->comp) (key, next->key, tree->cmp_ptr); + } + return node; +} + +/** + * Finds the first node in an avl-tree with a key greater or equal + * than the specified key + * @param tree pointer to avl-tree + * @param key pointer to specified key + * @return pointer to avl-node, NULL if no node with + * key greater or equal specified key exists. + */ +struct avl_node * +avl_find_greaterequal(const struct avl_tree *tree, const void *key) { + struct avl_node *node, *next; + int diff; + + if (tree->root == NULL) + return NULL; + + node = avl_find_rec(tree->root, key, tree->comp, tree->cmp_ptr, &diff); + + /* go right as long as key>node.key */ + while (diff > 0) { + if (list_is_last(&node->list, &tree->list_head)) { + return NULL; + } + + node = (struct avl_node *)node->list.next; + diff = (*tree->comp) (key, node->key, tree->cmp_ptr); + } + + /* go left as long as key<=next_node.key */ + next = node; + while (diff <= 0) { + node = next; + if (list_is_first(&node->list, &tree->list_head)) { + break; + } + + next = (struct avl_node *)node->list.prev; + diff = (*tree->comp) (key, next->key, tree->cmp_ptr); + } + return node; +} + +/** + * Inserts an avl_node into a tree + * @param tree pointer to tree + * @param new pointer to node + * @return 0 if node was inserted successfully, -1 if it was not inserted + * because of a key collision + */ +int +avl_insert(struct avl_tree *tree, struct avl_node *new) +{ + struct avl_node *node, *next, *last; + int diff; + + new->parent = NULL; + + new->left = NULL; + new->right = NULL; + + new->balance = 0; + new->leader = true; + + if (tree->root == NULL) { + list_add(&new->list, &tree->list_head); + tree->root = new; + tree->count = 1; + return 0; + } + + node = avl_find_rec(tree->root, new->key, tree->comp, tree->cmp_ptr, &diff); + + last = node; + + while (!list_is_last(&last->list, &tree->list_head)) { + next = avl_next(last); + if (next->leader) { + break; + } + last = next; + } + + diff = (*tree->comp) (new->key, node->key, tree->cmp_ptr); + + if (diff == 0) { + if (!tree->allow_dups) + return -1; + + new->leader = 0; + + avl_insert_after(tree, last, new); + return 0; + } + + if (node->balance == 1) { + avl_insert_before(tree, node, new); + + node->balance = 0; + new->parent = node; + node->left = new; + return 0; + } + + if (node->balance == -1) { + avl_insert_after(tree, last, new); + + node->balance = 0; + new->parent = node; + node->right = new; + return 0; + } + + if (diff < 0) { + avl_insert_before(tree, node, new); + + node->balance = -1; + new->parent = node; + node->left = new; + post_insert(tree, node); + return 0; + } + + avl_insert_after(tree, last, new); + + node->balance = 1; + new->parent = node; + node->right = new; + post_insert(tree, node); + return 0; +} + +/** + * Remove a node from an avl tree + * @param tree pointer to tree + * @param node pointer to node + */ +void +avl_delete(struct avl_tree *tree, struct avl_node *node) +{ + struct avl_node *next; + struct avl_node *parent; + struct avl_node *left; + struct avl_node *right; + if (node->leader) { + if (tree->allow_dups + && !list_is_last(&node->list, &tree->list_head) + && !(next = avl_next(node))->leader) { + next->leader = true; + next->balance = node->balance; + + parent = node->parent; + left = node->left; + right = node->right; + + next->parent = parent; + next->left = left; + next->right = right; + + if (parent == NULL) + tree->root = next; + + else { + if (node == parent->left) + parent->left = next; + + else + parent->right = next; + } + + if (left != NULL) + left->parent = next; + + if (right != NULL) + right->parent = next; + } + + else + avl_delete_worker(tree, node); + } + + avl_remove(tree, node); +} + +static struct avl_node * +avl_find_rec(struct avl_node *node, const void *key, avl_tree_comp comp, void *cmp_ptr, int *cmp_result) +{ + int diff; + + diff = (*comp) (key, node->key, cmp_ptr); + *cmp_result = diff; + + if (diff < 0) { + if (node->left != NULL) + return avl_find_rec(node->left, key, comp, cmp_ptr, cmp_result); + + return node; + } + + if (diff > 0) { + if (node->right != NULL) + return avl_find_rec(node->right, key, comp, cmp_ptr, cmp_result); + + return node; + } + + return node; +} + +static void +avl_rotate_right(struct avl_tree *tree, struct avl_node *node) +{ + struct avl_node *left, *parent; + + left = node->left; + parent = node->parent; + + left->parent = parent; + node->parent = left; + + if (parent == NULL) + tree->root = left; + + else { + if (parent->left == node) + parent->left = left; + + else + parent->right = left; + } + + node->left = left->right; + left->right = node; + + if (node->left != NULL) + node->left->parent = node; + + node->balance += 1 - avl_min(left->balance, 0); + left->balance += 1 + avl_max(node->balance, 0); +} + +static void +avl_rotate_left(struct avl_tree *tree, struct avl_node *node) +{ + struct avl_node *right, *parent; + + right = node->right; + parent = node->parent; + + right->parent = parent; + node->parent = right; + + if (parent == NULL) + tree->root = right; + + else { + if (parent->left == node) + parent->left = right; + + else + parent->right = right; + } + + node->right = right->left; + right->left = node; + + if (node->right != NULL) + node->right->parent = node; + + node->balance -= 1 + avl_max(right->balance, 0); + right->balance -= 1 - avl_min(node->balance, 0); +} + +static void +post_insert(struct avl_tree *tree, struct avl_node *node) +{ + struct avl_node *parent = node->parent; + + if (parent == NULL) + return; + + if (node == parent->left) { + parent->balance--; + + if (parent->balance == 0) + return; + + if (parent->balance == -1) { + post_insert(tree, parent); + return; + } + + if (node->balance == -1) { + avl_rotate_right(tree, parent); + return; + } + + avl_rotate_left(tree, node); + avl_rotate_right(tree, node->parent->parent); + return; + } + + parent->balance++; + + if (parent->balance == 0) + return; + + if (parent->balance == 1) { + post_insert(tree, parent); + return; + } + + if (node->balance == 1) { + avl_rotate_left(tree, parent); + return; + } + + avl_rotate_right(tree, node); + avl_rotate_left(tree, node->parent->parent); +} + +static void +avl_insert_before(struct avl_tree *tree, struct avl_node *pos_node, struct avl_node *node) +{ + list_add_tail(&node->list, &pos_node->list); + tree->count++; +} + +static void +avl_insert_after(struct avl_tree *tree, struct avl_node *pos_node, struct avl_node *node) +{ + list_add(&node->list, &pos_node->list); + tree->count++; +} + +static void +avl_remove(struct avl_tree *tree, struct avl_node *node) +{ + list_del(&node->list); + tree->count--; +} + +static void +avl_post_delete(struct avl_tree *tree, struct avl_node *node) +{ + struct avl_node *parent; + + if ((parent = node->parent) == NULL) + return; + + if (node == parent->left) { + parent->balance++; + + if (parent->balance == 0) { + avl_post_delete(tree, parent); + return; + } + + if (parent->balance == 1) + return; + + if (parent->right->balance == 0) { + avl_rotate_left(tree, parent); + return; + } + + if (parent->right->balance == 1) { + avl_rotate_left(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + + avl_rotate_right(tree, parent->right); + avl_rotate_left(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + + parent->balance--; + + if (parent->balance == 0) { + avl_post_delete(tree, parent); + return; + } + + if (parent->balance == -1) + return; + + if (parent->left->balance == 0) { + avl_rotate_right(tree, parent); + return; + } + + if (parent->left->balance == -1) { + avl_rotate_right(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + + avl_rotate_left(tree, parent->left); + avl_rotate_right(tree, parent); + avl_post_delete(tree, parent->parent); +} + +static struct avl_node * +avl_local_min(struct avl_node *node) +{ + while (node->left != NULL) + node = node->left; + + return node; +} + +#if 0 +static struct avl_node * +avl_local_max(struct avl_node *node) +{ + while (node->right != NULL) + node = node->right; + + return node; +} +#endif + +static void +avl_delete_worker(struct avl_tree *tree, struct avl_node *node) +{ + struct avl_node *parent, *min; + + parent = node->parent; + + if (node->left == NULL && node->right == NULL) { + if (parent == NULL) { + tree->root = NULL; + return; + } + + if (parent->left == node) { + parent->left = NULL; + parent->balance++; + + if (parent->balance == 1) + return; + + if (parent->balance == 0) { + avl_post_delete(tree, parent); + return; + } + + if (parent->right->balance == 0) { + avl_rotate_left(tree, parent); + return; + } + + if (parent->right->balance == 1) { + avl_rotate_left(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + + avl_rotate_right(tree, parent->right); + avl_rotate_left(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + + if (parent->right == node) { + parent->right = NULL; + parent->balance--; + + if (parent->balance == -1) + return; + + if (parent->balance == 0) { + avl_post_delete(tree, parent); + return; + } + + if (parent->left->balance == 0) { + avl_rotate_right(tree, parent); + return; + } + + if (parent->left->balance == -1) { + avl_rotate_right(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + + avl_rotate_left(tree, parent->left); + avl_rotate_right(tree, parent); + avl_post_delete(tree, parent->parent); + return; + } + } + + if (node->left == NULL) { + if (parent == NULL) { + tree->root = node->right; + node->right->parent = NULL; + return; + } + + node->right->parent = parent; + + if (parent->left == node) + parent->left = node->right; + + else + parent->right = node->right; + + avl_post_delete(tree, node->right); + return; + } + + if (node->right == NULL) { + if (parent == NULL) { + tree->root = node->left; + node->left->parent = NULL; + return; + } + + node->left->parent = parent; + + if (parent->left == node) + parent->left = node->left; + + else + parent->right = node->left; + + avl_post_delete(tree, node->left); + return; + } + + min = avl_local_min(node->right); + avl_delete_worker(tree, min); + parent = node->parent; + + min->balance = node->balance; + min->parent = parent; + min->left = node->left; + min->right = node->right; + + if (min->left != NULL) + min->left->parent = min; + + if (min->right != NULL) + min->right->parent = min; + + if (parent == NULL) { + tree->root = min; + return; + } + + if (parent->left == node) { + parent->left = min; + return; + } + + parent->right = min; +} + +/* + * Local Variables: + * c-basic-offset: 2 + * indent-tabs-mode: nil + * End: + */ diff --git a/3P/libubox/avl.h b/3P/libubox/avl.h new file mode 100644 index 00000000..c4685972 --- /dev/null +++ b/3P/libubox/avl.h @@ -0,0 +1,560 @@ +/* + * PacketBB handler library (see RFC 5444) + * Copyright (c) 2010 Henning Rogge + * Original OLSRd implementation by Hannes Gredler + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of olsr.org, olsrd nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Visit http://www.olsr.org/git for more information. + * + * If you find this software useful feel free to make a donation + * to the project. For more information see the website or contact + * the copyright holders. + */ + +#ifndef _AVL_H +#define _AVL_H + +#include +#include + +#include "list.h" + +/* Support for OLSR.org linker symbol export */ +#define EXPORT(sym) sym + +/** + * This element is a member of a avl-tree. It must be contained in all + * larger structs that should be put into a tree. + */ +struct avl_node { + /** + * Linked list node for supporting easy iteration and multiple + * elments with the same key. + * + * this must be the first element of an avl_node to + * make casting for lists easier + */ + struct list_head list; + + /** + * Pointer to parent node in tree, NULL if root node + */ + struct avl_node *parent; + + /** + * Pointer to left child + */ + struct avl_node *left; + + /** + * Pointer to right child + */ + struct avl_node *right; + + /** + * pointer to key of node + */ + const void *key; + + /** + * balance state of AVL tree (0,-1,+1) + */ + signed char balance; + + /** + * true if first of a series of nodes with same key + */ + bool leader; +}; + +/** + * Prototype for avl comparators + * @param k1 first key + * @param k2 second key + * @param ptr custom data for tree comparator + * @return +1 if k1>k2, -1 if k1list_head.next == &node->list; +} + +/** + * @param tree pointer to avl-tree + * @param node pointer to node of the tree + * @return true if node is the last one of the tree, false otherwise + */ +static inline bool +avl_is_last(struct avl_tree *tree, struct avl_node *node) { + return tree->list_head.prev == &node->list; +} + +/** + * @param tree pointer to avl-tree + * @return true if the tree is empty, false otherwise + */ +static inline bool +avl_is_empty(struct avl_tree *tree) { + return tree->count == 0; +} + +/** + * Internal function to support returning the element from a avl tree query + * @param tree pointer to avl tree + * @param key pointer to key + * @param offset offset of node inside the embedded struct + * @param mode mode of lookup operation (less equal, equal or greater equal) + * @param pointer to elemen, NULL if no fitting one was found + */ +static inline void * +__avl_find_element(const struct avl_tree *tree, const void *key, size_t offset, enum avl_find_mode mode) { + void *node = NULL; + + switch (mode) { + case AVL_FIND_EQUAL: + node = avl_find(tree, key); + break; + case AVL_FIND_LESSEQUAL: + node = avl_find_lessequal(tree, key); + break; + case AVL_FIND_GREATEREQUAL: + node = avl_find_greaterequal(tree, key); + break; + } + return node == NULL ? NULL : (((char *)node) - offset); +} + +/** + * @param tree pointer to avl-tree + * @param key pointer to key + * @param element pointer to a node element + * (don't need to be initialized) + * @param node_element name of the avl_node element inside the + * larger struct + * @return pointer to tree element with the specified key, + * NULL if no element was found + */ +#define avl_find_element(tree, key, element, node_element) \ + ((typeof(*(element)) *)__avl_find_element(tree, key, offsetof(typeof(*(element)), node_element), AVL_FIND_EQUAL)) + +/** + * @param tree pointer to avl-tree + * @param key pointer to specified key + * @param element pointer to a node element + * (don't need to be initialized) + * @param node_element name of the avl_node element inside the + * larger struct + * return pointer to last tree element with less or equal key than specified key, + * NULL if no element was found + */ +#define avl_find_le_element(tree, key, element, node_element) \ + ((typeof(*(element)) *)__avl_find_element(tree, key, offsetof(typeof(*(element)), node_element), AVL_FIND_LESSEQUAL)) + +/** + * @param tree pointer to avl-tree + * @param key pointer to specified key + * @param element pointer to a node element + * (don't need to be initialized) + * @param node_element name of the avl_node element inside the + * larger struct + * return pointer to first tree element with greater or equal key than specified key, + * NULL if no element was found + */ +#define avl_find_ge_element(tree, key, element, node_element) \ + ((typeof(*(element)) *)__avl_find_element(tree, key, offsetof(typeof(*(element)), node_element), AVL_FIND_GREATEREQUAL)) + +/** + * This function must not be called for an empty tree + * + * @param tree pointer to avl-tree + * @param element pointer to a node element + * (don't need to be initialized) + * @param node_member name of the avl_node element inside the + * larger struct + * @return pointer to the first element of the avl_tree + * (automatically converted to type 'element') + */ +#define avl_first_element(tree, element, node_member) \ + container_of((tree)->list_head.next, typeof(*(element)), node_member.list) + +/** + * @param tree pointer to tree + * @param element pointer to a node struct that contains the avl_node + * (don't need to be initialized) + * @param node_member name of the avl_node element inside the + * larger struct + * @return pointer to the last element of the avl_tree + * (automatically converted to type 'element') + */ +#define avl_last_element(tree, element, node_member) \ + container_of((tree)->list_head.prev, typeof(*(element)), node_member.list) + +/** + * This function must not be called for the last element of + * an avl tree + * + * @param element pointer to a node of the tree + * @param node_member name of the avl_node element inside the + * larger struct + * @return pointer to the node after 'element' + * (automatically converted to type 'element') + */ +#define avl_next_element(element, node_member) \ + container_of((&(element)->node_member.list)->next, typeof(*(element)), node_member.list) + +/** + * This function must not be called for the first element of + * an avl tree + * + * @param element pointer to a node of the tree + * @param node_member name of the avl_node element inside the + * larger struct + * @return pointer to the node before 'element' + * (automatically converted to type 'element') + */ +#define avl_prev_element(element, node_member) \ + container_of((&(element)->node_member.list)->prev, typeof(*(element)), node_member.list) + +/** + * Loop over a block of elements of a tree, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * + * @param first pointer to first element of loop + * @param last pointer to last element of loop + * @param element pointer to a node of the tree, this element will + * contain the current node of the list during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_element_range(first, last, element, node_member) \ + for (element = (first); \ + element->node_member.list.prev != &(last)->node_member.list; \ + element = avl_next_element(element, node_member)) + +/** + * Loop over a block of elements of a tree backwards, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * + * @param first pointer to first element of loop + * @param last pointer to last element of loop + * @param element pointer to a node of the tree, this element will + * contain the current node of the list during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_element_range_reverse(first, last, element, node_member) \ + for (element = (last); \ + element->node_member.list.next != &(first)->node_member.list; \ + element = avl_prev_element(element, node_member)) + +/** + * Loop over all elements of an avl_tree, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * + * @param tree pointer to avl-tree + * @param element pointer to a node of the tree, this element will + * contain the current node of the tree during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_each_element(tree, element, node_member) \ + avl_for_element_range(avl_first_element(tree, element, node_member), \ + avl_last_element(tree, element, node_member), \ + element, node_member) + +/** + * Loop over all elements of an avl_tree backwards, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * + * @param tree pointer to avl-tree + * @param element pointer to a node of the tree, this element will + * contain the current node of the tree during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_each_element_reverse(tree, element, node_member) \ + avl_for_element_range_reverse(avl_first_element(tree, element, node_member), \ + avl_last_element(tree, element, node_member), \ + element, node_member) + +/** + * Loop over a block of elements of a tree, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * The loop runs from the element 'first' to the end of the tree. + * + * @param tree pointer to avl-tree + * @param first pointer to first element of loop + * @param element pointer to a node of the tree, this element will + * contain the current node of the list during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_element_to_last(tree, first, element, node_member) \ + avl_for_element_range(first, avl_last_element(tree, element, node_member), element, node_member) + + +/** + * Loop over a block of elements of a tree backwards, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * The loop runs from the element 'first' to the end of the tree. + * + * @param tree pointer to avl-tree + * @param first pointer to first element of loop + * @param element pointer to a node of the tree, this element will + * contain the current node of the list during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_element_to_last_reverse(tree, first, element, node_member) \ + avl_for_element_range_reverse(first, avl_last_element(tree, element, node_member), element, node_member) + +/** + * Loop over a block of elements of a tree, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * The loop runs from the start of the tree to the element 'last'. + * + * @param tree pointer to avl-tree + * @param last pointer to last element of loop + * @param element pointer to a node of the tree, this element will + * contain the current node of the list during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_first_to_element(tree, last, element, node_member) \ + avl_for_element_range(avl_first_element(tree, element, node_member), last, element, node_member) + + +/** + * Loop over a block of elements of a tree backwards, used similar to a for() command. + * This loop should not be used if elements are removed from the tree during + * the loop. + * The loop runs from the start of the tree to the element 'last'. + * + * @param tree pointer to avl-tree + * @param last pointer to last element of loop + * @param element pointer to a node of the tree, this element will + * contain the current node of the list during the loop + * @param node_member name of the avl_node element inside the + * larger struct + */ +#define avl_for_first_to_element_reverse(tree, last, element, node_member) \ + avl_for_element_range_reverse(avl_first_element(tree, element, node_member), last, element, node_member) + +/** + * Loop over a block of nodes of a tree, used similar to a for() command. + * This loop can be used if the current element might be removed from + * the tree during the loop. Other elements should not be removed during + * the loop. + * + * @param first_element first element of loop + * @param last_element last element of loop + * @param element iterator pointer to tree element struct + * @param node_member name of avl_node within tree element struct + * @param ptr pointer to tree element struct which is used to store + * the next node during the loop + */ +#define avl_for_element_range_safe(first_element, last_element, element, node_member, ptr) \ + for (element = (first_element), ptr = avl_next_element(first_element, node_member); \ + element->node_member.list.prev != &(last_element)->node_member.list; \ + element = ptr, ptr = avl_next_element(ptr, node_member)) + +/** + * Loop over a block of elements of a tree backwards, used similar to a for() command. + * This loop can be used if the current element might be removed from + * the tree during the loop. Other elements should not be removed during + * the loop. + * + * @param first_element first element of range (will be last returned by the loop) + * @param last_element last element of range (will be first returned by the loop) + * @param element iterator pointer to node element struct + * @param node_member name of avl_node within node element struct + * @param ptr pointer to node element struct which is used to store + * the previous node during the loop + */ +#define avl_for_element_range_reverse_safe(first_element, last_element, element, node_member, ptr) \ + for (element = (last_element), ptr = avl_prev_element(last_element, node_member); \ + element->node_member.list.next != &(first_element)->node_member.list; \ + element = ptr, ptr = avl_prev_element(ptr, node_member)) + +/** + * Loop over all elements of an avl_tree, used similar to a for() command. + * This loop can be used if the current element might be removed from + * the tree during the loop. Other elements should not be removed during + * the loop. + * + * @param tree pointer to avl-tree + * @param element pointer to a node of the tree, this element will + * contain the current node of the tree during the loop + * @param node_member name of the avl_node element inside the + * larger struct + * @param ptr pointer to a tree element which is used to store + * the next node during the loop + */ +#define avl_for_each_element_safe(tree, element, node_member, ptr) \ + avl_for_element_range_safe(avl_first_element(tree, element, node_member), \ + avl_last_element(tree, element, node_member), \ + element, node_member, ptr) + +/** + * Loop over all elements of an avl_tree backwards, used similar to a for() command. + * This loop can be used if the current element might be removed from + * the tree during the loop. Other elements should not be removed during + * the loop. + * + * @param tree pointer to avl-tree + * @param element pointer to a node of the tree, this element will + * contain the current node of the tree during the loop + * @param node_member name of the avl_node element inside the + * larger struct + * @param ptr pointer to a tree element which is used to store + * the next node during the loop + */ +#define avl_for_each_element_reverse_safe(tree, element, node_member, ptr) \ + avl_for_element_range_reverse_safe(avl_first_element(tree, element, node_member), \ + avl_last_element(tree, element, node_member), \ + element, node_member, ptr) + +/** + * A special loop that removes all elements of the tree and cleans up the tree + * root. The loop body is responsible to free the node elements of the tree. + * + * This loop is much faster than a normal one for clearing the tree because it + * does not rebalance the tree after each removal. Do NOT use a break command + * inside. + * You can free the memory of the elements within the loop. + * Do NOT call avl_delete() on the elements within the loop, + * + * @param tree pointer to avl-tree + * @param element pointer to a node of the tree, this element will + * contain the current node of the tree during the loop + * @param node_member name of the avl_node element inside the + * larger struct + * @param ptr pointer to a tree element which is used to store + * the next node during the loop + */ +#define avl_remove_all_elements(tree, element, node_member, ptr) \ + for (element = avl_first_element(tree, element, node_member), \ + ptr = avl_next_element(element, node_member), \ + INIT_LIST_HEAD(&(tree)->list_head), \ + (tree)->root = NULL; \ + (tree)->count > 0; \ + element = ptr, ptr = avl_next_element(ptr, node_member), (tree)->count--) + +#endif /* _AVL_H */ + +/* + * Local Variables: + * c-basic-offset: 2 + * indent-tabs-mode: nil + * End: + */ diff --git a/3P/libubox/blob.c b/3P/libubox/blob.c new file mode 100644 index 00000000..ec8617be --- /dev/null +++ b/3P/libubox/blob.c @@ -0,0 +1,287 @@ +/* + * blob - library for generating/parsing tagged binary data + * + * Copyright (C) 2010 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "blob.h" + +static bool +blob_buffer_grow(struct blob_buf *buf, int minlen) +{ + struct blob_buf *new; + int delta = ((minlen / 256) + 1) * 256; + new = realloc(buf->buf, buf->buflen + delta); + if (new) { + buf->buf = new; + memset(buf->buf + buf->buflen, 0, delta); + buf->buflen += delta; + } + return !!new; +} + +static void +blob_init(struct blob_attr *attr, int id, unsigned int len) +{ + len &= BLOB_ATTR_LEN_MASK; + len |= (id << BLOB_ATTR_ID_SHIFT) & BLOB_ATTR_ID_MASK; + attr->id_len = cpu_to_be32(len); +} + +static inline struct blob_attr * +offset_to_attr(struct blob_buf *buf, int offset) +{ + void *ptr = (char *)buf->buf + offset - BLOB_COOKIE; + return ptr; +} + +static inline int +attr_to_offset(struct blob_buf *buf, struct blob_attr *attr) +{ + return (char *)attr - (char *) buf->buf + BLOB_COOKIE; +} + +bool +blob_buf_grow(struct blob_buf *buf, int required) +{ + int offset_head = attr_to_offset(buf, buf->head); + + if (!buf->grow || !buf->grow(buf, required)) + return false; + + buf->head = offset_to_attr(buf, offset_head); + return true; +} + +static struct blob_attr * +blob_add(struct blob_buf *buf, struct blob_attr *pos, int id, int payload) +{ + int offset = attr_to_offset(buf, pos); + int required = (offset - BLOB_COOKIE + sizeof(struct blob_attr) + payload) - buf->buflen; + struct blob_attr *attr; + + if (required > 0) { + if (!blob_buf_grow(buf, required)) + return NULL; + attr = offset_to_attr(buf, offset); + } else { + attr = pos; + } + + blob_init(attr, id, payload + sizeof(struct blob_attr)); + blob_fill_pad(attr); + return attr; +} + +int +blob_buf_init(struct blob_buf *buf, int id) +{ + if (!buf->grow) + buf->grow = blob_buffer_grow; + + buf->head = buf->buf; + if (blob_add(buf, buf->buf, id, 0) == NULL) + return -ENOMEM; + + return 0; +} + +void +blob_buf_free(struct blob_buf *buf) +{ + free(buf->buf); + buf->buf = NULL; + buf->buflen = 0; +} + +void +blob_fill_pad(struct blob_attr *attr) +{ + char *buf = (char *) attr; + int len = blob_pad_len(attr); + int delta = len - blob_raw_len(attr); + + if (delta > 0) + memset(buf + len - delta, 0, delta); +} + +void +blob_set_raw_len(struct blob_attr *attr, unsigned int len) +{ + len &= BLOB_ATTR_LEN_MASK; + attr->id_len &= ~cpu_to_be32(BLOB_ATTR_LEN_MASK); + attr->id_len |= cpu_to_be32(len); +} + +struct blob_attr * +blob_new(struct blob_buf *buf, int id, int payload) +{ + struct blob_attr *attr; + + attr = blob_add(buf, blob_next(buf->head), id, payload); + if (!attr) + return NULL; + + blob_set_raw_len(buf->head, blob_pad_len(buf->head) + blob_pad_len(attr)); + return attr; +} + +struct blob_attr * +blob_put_raw(struct blob_buf *buf, const void *ptr, unsigned int len) +{ + struct blob_attr *attr; + + if (len < sizeof(struct blob_attr) || !ptr) + return NULL; + + attr = blob_add(buf, blob_next(buf->head), 0, len - sizeof(struct blob_attr)); + if (!attr) + return NULL; + blob_set_raw_len(buf->head, blob_pad_len(buf->head) + len); + memcpy(attr, ptr, len); + return attr; +} + +struct blob_attr * +blob_put(struct blob_buf *buf, int id, const void *ptr, unsigned int len) +{ + struct blob_attr *attr; + + attr = blob_new(buf, id, len); + if (!attr) + return NULL; + + if (ptr) + memcpy(blob_data(attr), ptr, len); + return attr; +} + +void * +blob_nest_start(struct blob_buf *buf, int id) +{ + unsigned long offset = attr_to_offset(buf, buf->head); + buf->head = blob_new(buf, id, 0); + if (!buf->head) + return NULL; + return (void *) offset; +} + +void +blob_nest_end(struct blob_buf *buf, void *cookie) +{ + struct blob_attr *attr = offset_to_attr(buf, (unsigned long) cookie); + blob_set_raw_len(attr, blob_pad_len(attr) + blob_len(buf->head)); + buf->head = attr; +} + +static const int blob_type_minlen[BLOB_ATTR_LAST] = { + [BLOB_ATTR_STRING] = 1, + [BLOB_ATTR_INT8] = sizeof(uint8_t), + [BLOB_ATTR_INT16] = sizeof(uint16_t), + [BLOB_ATTR_INT32] = sizeof(uint32_t), + [BLOB_ATTR_INT64] = sizeof(uint64_t), +}; + +bool +blob_check_type(const void *ptr, unsigned int len, int type) +{ + const char *data = ptr; + + if (type >= BLOB_ATTR_LAST) + return false; + + if (type >= BLOB_ATTR_INT8 && type <= BLOB_ATTR_INT64) { + if (len != blob_type_minlen[type]) + return false; + } else { + if (len < blob_type_minlen[type]) + return false; + } + + if (type == BLOB_ATTR_STRING && data[len - 1] != 0) + return false; + + return true; +} + +int +blob_parse(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max) +{ + struct blob_attr *pos; + int found = 0; + int rem; + + memset(data, 0, sizeof(struct blob_attr *) * max); + blob_for_each_attr(pos, attr, rem) { + int id = blob_id(pos); + int len = blob_len(pos); + + if (id >= max) + continue; + + if (info) { + int type = info[id].type; + + if (type < BLOB_ATTR_LAST) { + if (!blob_check_type(blob_data(pos), len, type)) + continue; + } + + if (info[id].minlen && len < info[id].minlen) + continue; + + if (info[id].maxlen && len > info[id].maxlen) + continue; + + if (info[id].validate && !info[id].validate(&info[id], pos)) + continue; + } + + if (!data[id]) + found++; + + data[id] = pos; + } + return found; +} + +bool +blob_attr_equal(const struct blob_attr *a1, const struct blob_attr *a2) +{ + if (!a1 && !a2) + return true; + + if (!a1 || !a2) + return false; + + if (blob_pad_len(a1) != blob_pad_len(a2)) + return false; + + return !memcmp(a1, a2, blob_pad_len(a1)); +} + +struct blob_attr * +blob_memdup(struct blob_attr *attr) +{ + struct blob_attr *ret; + int size = blob_pad_len(attr); + + ret = malloc(size); + if (!ret) + return NULL; + + memcpy(ret, attr, size); + return ret; +} diff --git a/3P/libubox/blob.h b/3P/libubox/blob.h new file mode 100644 index 00000000..ab077eab --- /dev/null +++ b/3P/libubox/blob.h @@ -0,0 +1,257 @@ +/* + * blob - library for generating/parsing tagged binary data + * + * Copyright (C) 2010 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _BLOB_H__ +#define _BLOB_H__ + +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +#define BLOB_COOKIE 0x01234567 + +enum { + BLOB_ATTR_UNSPEC, + BLOB_ATTR_NESTED, + BLOB_ATTR_BINARY, + BLOB_ATTR_STRING, + BLOB_ATTR_INT8, + BLOB_ATTR_INT16, + BLOB_ATTR_INT32, + BLOB_ATTR_INT64, + BLOB_ATTR_LAST +}; + +#define BLOB_ATTR_ID_MASK 0x7f000000 +#define BLOB_ATTR_ID_SHIFT 24 +#define BLOB_ATTR_LEN_MASK 0x00ffffff +#define BLOB_ATTR_ALIGN 4 +#define BLOB_ATTR_EXTENDED 0x80000000 + +struct blob_attr { + uint32_t id_len; + char data[]; +} __packed; + +struct blob_attr_info { + unsigned int type; + unsigned int minlen; + unsigned int maxlen; + bool (*validate)(const struct blob_attr_info *, struct blob_attr *); +}; + +struct blob_buf { + struct blob_attr *head; + bool (*grow)(struct blob_buf *buf, int minlen); + int buflen; + void *buf; +}; + +/* + * blob_data: returns the data pointer for an attribute + */ +static inline void * +blob_data(const struct blob_attr *attr) +{ + return (void *) attr->data; +} + +/* + * blob_id: returns the id of an attribute + */ +static inline unsigned int +blob_id(const struct blob_attr *attr) +{ + int id = (be32_to_cpu(attr->id_len) & BLOB_ATTR_ID_MASK) >> BLOB_ATTR_ID_SHIFT; + return id; +} + +static inline bool +blob_is_extended(const struct blob_attr *attr) +{ + return !!(attr->id_len & cpu_to_be32(BLOB_ATTR_EXTENDED)); +} + +/* + * blob_len: returns the length of the attribute's payload + */ +static inline unsigned int +blob_len(const struct blob_attr *attr) +{ + return (be32_to_cpu(attr->id_len) & BLOB_ATTR_LEN_MASK) - sizeof(struct blob_attr); +} + +/* + * blob_raw_len: returns the complete length of an attribute (including the header) + */ +static inline unsigned int +blob_raw_len(const struct blob_attr *attr) +{ + return blob_len(attr) + sizeof(struct blob_attr); +} + +/* + * blob_pad_len: returns the padded length of an attribute (including the header) + */ +static inline unsigned int +blob_pad_len(const struct blob_attr *attr) +{ + unsigned int len = blob_raw_len(attr); + len = (len + BLOB_ATTR_ALIGN - 1) & ~(BLOB_ATTR_ALIGN - 1); + return len; +} + +static inline uint8_t +blob_get_u8(const struct blob_attr *attr) +{ + return *((uint8_t *) attr->data); +} + +static inline uint16_t +blob_get_u16(const struct blob_attr *attr) +{ + uint16_t *tmp = (uint16_t*)attr->data; + return be16_to_cpu(*tmp); +} + +static inline uint32_t +blob_get_u32(const struct blob_attr *attr) +{ + uint32_t *tmp = (uint32_t*)attr->data; + return be32_to_cpu(*tmp); +} + +static inline uint64_t +blob_get_u64(const struct blob_attr *attr) +{ + uint32_t *ptr = (uint32_t *) blob_data(attr); + uint64_t tmp = ((uint64_t) be32_to_cpu(ptr[0])) << 32; + tmp |= be32_to_cpu(ptr[1]); + return tmp; +} + +static inline int8_t +blob_get_int8(const struct blob_attr *attr) +{ + return blob_get_u8(attr); +} + +static inline int16_t +blob_get_int16(const struct blob_attr *attr) +{ + return blob_get_u16(attr); +} + +static inline int32_t +blob_get_int32(const struct blob_attr *attr) +{ + return blob_get_u32(attr); +} + +static inline int64_t +blob_get_int64(const struct blob_attr *attr) +{ + return blob_get_u64(attr); +} + +static inline const char * +blob_get_string(const struct blob_attr *attr) +{ + return attr->data; +} + +static inline struct blob_attr * +blob_next(const struct blob_attr *attr) +{ + return (struct blob_attr *) ((char *) attr + blob_pad_len(attr)); +} + +extern void blob_fill_pad(struct blob_attr *attr); +extern void blob_set_raw_len(struct blob_attr *attr, unsigned int len); +extern bool blob_attr_equal(const struct blob_attr *a1, const struct blob_attr *a2); +extern int blob_buf_init(struct blob_buf *buf, int id); +extern void blob_buf_free(struct blob_buf *buf); +extern bool blob_buf_grow(struct blob_buf *buf, int required); +extern struct blob_attr *blob_new(struct blob_buf *buf, int id, int payload); +extern void *blob_nest_start(struct blob_buf *buf, int id); +extern void blob_nest_end(struct blob_buf *buf, void *cookie); +extern struct blob_attr *blob_put(struct blob_buf *buf, int id, const void *ptr, unsigned int len); +extern bool blob_check_type(const void *ptr, unsigned int len, int type); +extern int blob_parse(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max); +extern struct blob_attr *blob_memdup(struct blob_attr *attr); +extern struct blob_attr *blob_put_raw(struct blob_buf *buf, const void *ptr, unsigned int len); + +static inline struct blob_attr * +blob_put_string(struct blob_buf *buf, int id, const char *str) +{ + return blob_put(buf, id, str, strlen(str) + 1); +} + +static inline struct blob_attr * +blob_put_u8(struct blob_buf *buf, int id, uint8_t val) +{ + return blob_put(buf, id, &val, sizeof(val)); +} + +static inline struct blob_attr * +blob_put_u16(struct blob_buf *buf, int id, uint16_t val) +{ + val = cpu_to_be16(val); + return blob_put(buf, id, &val, sizeof(val)); +} + +static inline struct blob_attr * +blob_put_u32(struct blob_buf *buf, int id, uint32_t val) +{ + val = cpu_to_be32(val); + return blob_put(buf, id, &val, sizeof(val)); +} + +static inline struct blob_attr * +blob_put_u64(struct blob_buf *buf, int id, uint64_t val) +{ + val = cpu_to_be64(val); + return blob_put(buf, id, &val, sizeof(val)); +} + +#define blob_put_int8 blob_put_u8 +#define blob_put_int16 blob_put_u16 +#define blob_put_int32 blob_put_u32 +#define blob_put_int64 blob_put_u64 + +#define __blob_for_each_attr(pos, attr, rem) \ + for (pos = (void *) attr; \ + rem > 0 && (blob_pad_len(pos) <= rem) && \ + (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ + rem -= blob_pad_len(pos), pos = blob_next(pos)) + + +#define blob_for_each_attr(pos, attr, rem) \ + for (rem = attr ? blob_len(attr) : 0, \ + pos = attr ? blob_data(attr) : 0; \ + rem > 0 && (blob_pad_len(pos) <= rem) && \ + (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ + rem -= blob_pad_len(pos), pos = blob_next(pos)) + + +#endif diff --git a/3P/libubox/blobmsg.c b/3P/libubox/blobmsg.c new file mode 100644 index 00000000..9fe96e44 --- /dev/null +++ b/3P/libubox/blobmsg.c @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2010-2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include "blobmsg.h" + +static const int blob_type[__BLOBMSG_TYPE_LAST] = { + [BLOBMSG_TYPE_INT8] = BLOB_ATTR_INT8, + [BLOBMSG_TYPE_INT16] = BLOB_ATTR_INT16, + [BLOBMSG_TYPE_INT32] = BLOB_ATTR_INT32, + [BLOBMSG_TYPE_INT64] = BLOB_ATTR_INT64, + [BLOBMSG_TYPE_STRING] = BLOB_ATTR_STRING, + [BLOBMSG_TYPE_UNSPEC] = BLOB_ATTR_BINARY, +}; + +static uint16_t +blobmsg_namelen(const struct blobmsg_hdr *hdr) +{ + return be16_to_cpu(hdr->namelen); +} + +bool blobmsg_check_attr(const struct blob_attr *attr, bool name) +{ + const struct blobmsg_hdr *hdr; + const char *data; + int id, len; + + if (blob_len(attr) < sizeof(struct blobmsg_hdr)) + return false; + + hdr = (void *) attr->data; + if (!hdr->namelen && name) + return false; + + if (blobmsg_namelen(hdr) > blob_len(attr) - sizeof(struct blobmsg_hdr)) + return false; + + if (hdr->name[blobmsg_namelen(hdr)] != 0) + return false; + + id = blob_id(attr); + len = blobmsg_data_len(attr); + data = blobmsg_data(attr); + + if (id > BLOBMSG_TYPE_LAST) + return false; + + if (!blob_type[id]) + return true; + + return blob_check_type(data, len, blob_type[id]); +} + +int blobmsg_check_array(const struct blob_attr *attr, int type) +{ + struct blob_attr *cur; + bool name; + int rem; + int size = 0; + + switch (blobmsg_type(attr)) { + case BLOBMSG_TYPE_TABLE: + name = true; + break; + case BLOBMSG_TYPE_ARRAY: + name = false; + break; + default: + return -1; + } + + blobmsg_for_each_attr(cur, attr, rem) { + if (type != BLOBMSG_TYPE_UNSPEC && blobmsg_type(cur) != type) + return -1; + + if (!blobmsg_check_attr(cur, name)) + return -1; + + size++; + } + + return size; +} + +bool blobmsg_check_attr_list(const struct blob_attr *attr, int type) +{ + return blobmsg_check_array(attr, type) >= 0; +} + +int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len, + struct blob_attr **tb, void *data, unsigned int len) +{ + struct blob_attr *attr; + int i = 0; + + memset(tb, 0, policy_len * sizeof(*tb)); + __blob_for_each_attr(attr, data, len) { + if (policy[i].type != BLOBMSG_TYPE_UNSPEC && + blob_id(attr) != policy[i].type) + continue; + + if (!blobmsg_check_attr(attr, false)) + return -1; + + if (tb[i]) + continue; + + tb[i++] = attr; + if (i == policy_len) + break; + } + + return 0; +} + + +int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len, + struct blob_attr **tb, void *data, unsigned int len) +{ + struct blobmsg_hdr *hdr; + struct blob_attr *attr; + uint8_t *pslen; + int i; + + memset(tb, 0, policy_len * sizeof(*tb)); + pslen = alloca(policy_len); + for (i = 0; i < policy_len; i++) { + if (!policy[i].name) + continue; + + pslen[i] = strlen(policy[i].name); + } + + __blob_for_each_attr(attr, data, len) { + hdr = blob_data(attr); + for (i = 0; i < policy_len; i++) { + if (!policy[i].name) + continue; + + if (policy[i].type != BLOBMSG_TYPE_UNSPEC && + blob_id(attr) != policy[i].type) + continue; + + if (blobmsg_namelen(hdr) != pslen[i]) + continue; + + if (!blobmsg_check_attr(attr, true)) + return -1; + + if (tb[i]) + continue; + + if (strcmp(policy[i].name, (char *) hdr->name) != 0) + continue; + + tb[i] = attr; + } + } + + return 0; +} + + +static struct blob_attr * +blobmsg_new(struct blob_buf *buf, int type, const char *name, int payload_len, void **data) +{ + struct blob_attr *attr; + struct blobmsg_hdr *hdr; + int attrlen, namelen; + char *pad_start, *pad_end; + + if (!name) + name = ""; + + namelen = strlen(name); + attrlen = blobmsg_hdrlen(namelen) + payload_len; + attr = blob_new(buf, type, attrlen); + if (!attr) + return NULL; + + attr->id_len |= be32_to_cpu(BLOB_ATTR_EXTENDED); + hdr = blob_data(attr); + hdr->namelen = cpu_to_be16(namelen); + strcpy((char *) hdr->name, (const char *)name); + pad_end = *data = blobmsg_data(attr); + pad_start = (char *) &hdr->name[namelen]; + if (pad_start < pad_end) + memset(pad_start, 0, pad_end - pad_start); + + return attr; +} + +static inline int +attr_to_offset(struct blob_buf *buf, struct blob_attr *attr) +{ + return (char *)attr - (char *) buf->buf + BLOB_COOKIE; +} + + +void * +blobmsg_open_nested(struct blob_buf *buf, const char *name, bool array) +{ + struct blob_attr *head; + int type = array ? BLOBMSG_TYPE_ARRAY : BLOBMSG_TYPE_TABLE; + unsigned long offset = attr_to_offset(buf, buf->head); + void *data; + + if (!name) + name = ""; + + head = blobmsg_new(buf, type, name, 0, &data); + if (!head) + return NULL; + blob_set_raw_len(buf->head, blob_pad_len(buf->head) - blobmsg_hdrlen(strlen(name))); + buf->head = head; + return (void *)offset; +} + +void +blobmsg_vprintf(struct blob_buf *buf, const char *name, const char *format, va_list arg) +{ + va_list arg2; + char cbuf; + int len; + + va_copy(arg2, arg); + len = vsnprintf(&cbuf, sizeof(cbuf), format, arg2); + va_end(arg2); + + vsprintf(blobmsg_alloc_string_buffer(buf, name, len + 1), format, arg); + blobmsg_add_string_buffer(buf); +} + +void +blobmsg_printf(struct blob_buf *buf, const char *name, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + blobmsg_vprintf(buf, name, format, ap); + va_end(ap); +} + +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; + + data_dest = blobmsg_data(attr); + 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; + + blob_buf_grow(buf, required); + attr = blob_next(buf->head); + +out: + return blobmsg_data(attr); +} + +void +blobmsg_add_string_buffer(struct blob_buf *buf) +{ + struct blob_attr *attr; + int len, attrlen; + + attr = blob_next(buf->head); + len = strlen(blobmsg_data(attr)) + 1; + + attrlen = blob_raw_len(attr) + len; + blob_set_raw_len(attr, attrlen); + blob_fill_pad(attr); + + blob_set_raw_len(buf->head, blob_raw_len(buf->head) + blob_pad_len(attr)); +} + +int +blobmsg_add_field(struct blob_buf *buf, int type, const char *name, + const void *data, unsigned int len) +{ + struct blob_attr *attr; + void *data_dest; + + attr = blobmsg_new(buf, type, name, len, &data_dest); + if (!attr) + return -1; + + if (len > 0) + memcpy(data_dest, data, len); + + return 0; +} diff --git a/3P/libubox/blobmsg.h b/3P/libubox/blobmsg.h new file mode 100644 index 00000000..e58f95de --- /dev/null +++ b/3P/libubox/blobmsg.h @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2010-2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __BLOBMSG_H +#define __BLOBMSG_H + +#include +#include "blob.h" + +#define BLOBMSG_ALIGN 2 +#define BLOBMSG_PADDING(len) (((len) + (1 << BLOBMSG_ALIGN) - 1) & ~((1 << BLOBMSG_ALIGN) - 1)) + +enum blobmsg_type { + BLOBMSG_TYPE_UNSPEC, + BLOBMSG_TYPE_ARRAY, + BLOBMSG_TYPE_TABLE, + BLOBMSG_TYPE_STRING, + BLOBMSG_TYPE_INT64, + BLOBMSG_TYPE_INT32, + BLOBMSG_TYPE_INT16, + BLOBMSG_TYPE_INT8, + __BLOBMSG_TYPE_LAST, + BLOBMSG_TYPE_LAST = __BLOBMSG_TYPE_LAST - 1, + BLOBMSG_TYPE_BOOL = BLOBMSG_TYPE_INT8, +}; + +struct blobmsg_hdr { + uint16_t namelen; + uint8_t name[]; +} __packed; + +struct blobmsg_policy { + const char *name; + enum blobmsg_type type; +}; + +static inline int blobmsg_hdrlen(unsigned int namelen) +{ + return BLOBMSG_PADDING(sizeof(struct blobmsg_hdr) + namelen + 1); +} + +static inline void blobmsg_clear_name(struct blob_attr *attr) +{ + struct blobmsg_hdr *hdr = (struct blobmsg_hdr *) blob_data(attr); + hdr->name[0] = 0; +} + +static inline const char *blobmsg_name(const struct blob_attr *attr) +{ + struct blobmsg_hdr *hdr = (struct blobmsg_hdr *) blob_data(attr); + return (const char *) hdr->name; +} + +static inline int blobmsg_type(const struct blob_attr *attr) +{ + return blob_id(attr); +} + +static inline void *blobmsg_data(const struct blob_attr *attr) +{ + struct blobmsg_hdr *hdr = (struct blobmsg_hdr *) blob_data(attr); + char *data = (char *) blob_data(attr); + + if (blob_is_extended(attr)) + data += blobmsg_hdrlen(be16_to_cpu(hdr->namelen)); + + return data; +} + +static inline int blobmsg_data_len(const struct blob_attr *attr) +{ + uint8_t *start, *end; + + start = (uint8_t *) blob_data(attr); + end = (uint8_t *) blobmsg_data(attr); + + return blob_len(attr) - (end - start); +} + +static inline int blobmsg_len(const struct blob_attr *attr) +{ + return blobmsg_data_len(attr); +} + +bool blobmsg_check_attr(const struct blob_attr *attr, bool name); +bool blobmsg_check_attr_list(const struct blob_attr *attr, int type); + +/* + * blobmsg_check_array: validate array/table and return size + * + * Checks if all elements of an array or table are valid and have + * the specified type. Returns the number of elements in the array + */ +int blobmsg_check_array(const struct blob_attr *attr, int type); + +int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len, + struct blob_attr **tb, void *data, unsigned int len); +int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len, + struct blob_attr **tb, void *data, unsigned int len); + +int blobmsg_add_field(struct blob_buf *buf, int type, const char *name, + const void *data, unsigned int len); + +static inline int +blobmsg_add_u8(struct blob_buf *buf, const char *name, uint8_t val) +{ + return blobmsg_add_field(buf, BLOBMSG_TYPE_INT8, name, &val, 1); +} + +static inline int +blobmsg_add_u16(struct blob_buf *buf, const char *name, uint16_t val) +{ + val = cpu_to_be16(val); + return blobmsg_add_field(buf, BLOBMSG_TYPE_INT16, name, &val, 2); +} + +static inline int +blobmsg_add_u32(struct blob_buf *buf, const char *name, uint32_t val) +{ + val = cpu_to_be32(val); + return blobmsg_add_field(buf, BLOBMSG_TYPE_INT32, name, &val, 4); +} + +static inline int +blobmsg_add_u64(struct blob_buf *buf, const char *name, uint64_t val) +{ + val = cpu_to_be64(val); + return blobmsg_add_field(buf, BLOBMSG_TYPE_INT64, name, &val, 8); +} + +static inline int +blobmsg_add_string(struct blob_buf *buf, const char *name, const char *string) +{ + return blobmsg_add_field(buf, BLOBMSG_TYPE_STRING, name, string, strlen(string) + 1); +} + +static inline int +blobmsg_add_blob(struct blob_buf *buf, struct blob_attr *attr) +{ + return blobmsg_add_field(buf, blobmsg_type(attr), blobmsg_name(attr), + blobmsg_data(attr), blobmsg_data_len(attr)); +} + +void *blobmsg_open_nested(struct blob_buf *buf, const char *name, bool array); + +static inline void * +blobmsg_open_array(struct blob_buf *buf, const char *name) +{ + return blobmsg_open_nested(buf, name, true); +} + +static inline void * +blobmsg_open_table(struct blob_buf *buf, const char *name) +{ + return blobmsg_open_nested(buf, name, false); +} + +static inline void +blobmsg_close_array(struct blob_buf *buf, void *cookie) +{ + blob_nest_end(buf, cookie); +} + +static inline void +blobmsg_close_table(struct blob_buf *buf, void *cookie) +{ + blob_nest_end(buf, cookie); +} + +static inline int blobmsg_buf_init(struct blob_buf *buf) +{ + return blob_buf_init(buf, BLOBMSG_TYPE_TABLE); +} + +static inline uint8_t blobmsg_get_u8(struct blob_attr *attr) +{ + return *(uint8_t *) blobmsg_data(attr); +} + +static inline bool blobmsg_get_bool(struct blob_attr *attr) +{ + return *(uint8_t *) blobmsg_data(attr); +} + +static inline uint16_t blobmsg_get_u16(struct blob_attr *attr) +{ + return be16_to_cpu(*(uint16_t *) blobmsg_data(attr)); +} + +static inline uint32_t blobmsg_get_u32(struct blob_attr *attr) +{ + return be32_to_cpu(*(uint32_t *) blobmsg_data(attr)); +} + +static inline uint64_t blobmsg_get_u64(struct blob_attr *attr) +{ + uint32_t *ptr = (uint32_t *) blobmsg_data(attr); + uint64_t tmp = ((uint64_t) be32_to_cpu(ptr[0])) << 32; + tmp |= be32_to_cpu(ptr[1]); + return tmp; +} + +static inline char *blobmsg_get_string(struct blob_attr *attr) +{ + if (!attr) + return NULL; + + return (char *) blobmsg_data(attr); +} + +void *blobmsg_alloc_string_buffer(struct blob_buf *buf, const char *name, unsigned int maxlen); +void *blobmsg_realloc_string_buffer(struct blob_buf *buf, unsigned int maxlen); +void blobmsg_add_string_buffer(struct blob_buf *buf); + +void blobmsg_vprintf(struct blob_buf *buf, const char *name, const char *format, va_list arg); +void blobmsg_printf(struct blob_buf *buf, const char *name, const char *format, ...) + __attribute__((format(printf, 3, 4))); + + +/* blobmsg to json formatting */ + +#define blobmsg_for_each_attr(pos, attr, rem) \ + for (rem = attr ? blobmsg_data_len(attr) : 0, \ + pos = attr ? blobmsg_data(attr) : 0; \ + rem > 0 && (blob_pad_len(pos) <= rem) && \ + (blob_pad_len(pos) >= sizeof(struct blob_attr)); \ + rem -= blob_pad_len(pos), pos = blob_next(pos)) + +#endif diff --git a/3P/libubox/blobmsg_json.c b/3P/libubox/blobmsg_json.c new file mode 100644 index 00000000..8f208e0c --- /dev/null +++ b/3P/libubox/blobmsg_json.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2010-2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include "blobmsg.h" +#include "blobmsg_json.h" + +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; + + if (!obj) + return false; + + 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; + default: + return false; + } + return ret; +} + +static bool __blobmsg_add_json(struct blob_buf *b, json_object *obj) +{ + bool ret = false; + + if (is_error(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) +{ + if (len <= 0) + return true; + + if (s->pos + len >= s->len) { + s->len += 16 + len; + s->buf = realloc(s->buf, s->len); + if (!s->buf) + return false; + } + memcpy(s->buf + s->pos, c, len); + s->pos += len; + return true; +} + +static void add_separator(struct strbuf *s) +{ + static char indent_chars[17] = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; + int indent; + char *start; + + if (!s->indent) + return; + + indent = s->indent_level; + if (indent > 16) + indent = 16; + + start = &indent_chars[sizeof(indent_chars) - indent - 1]; + *start = '\n'; + blobmsg_puts(s, start, indent + 1); + *start = '\t'; +} + + +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 array, bool head) +{ + const char *data_str; + char buf[32]; + void *data; + int len; + + if (!blobmsg_check_attr(attr, false)) + return; + + if (!array && 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); +} + +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; + + 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; + } + + 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; + } + + s.buf = realloc(s.buf, s.pos + 1); + s.buf[s.pos] = 0; + + return s.buf; +} diff --git a/3P/libubox/blobmsg_json.h b/3P/libubox/blobmsg_json.h new file mode 100644 index 00000000..e8036eb0 --- /dev/null +++ b/3P/libubox/blobmsg_json.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010-2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __BLOBMSG_JSON_H +#define __BLOBMSG_JSON_H + +#ifdef JSONC + #include +#else + #include +#endif + +#include +#include "blobmsg.h" + +bool blobmsg_add_object(struct blob_buf *b, json_object *obj); +bool blobmsg_add_json_element(struct blob_buf *b, const char *name, 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); +} + +#endif diff --git a/3P/libubox/builders/cmake/CMakeLists.txt b/3P/libubox/builders/cmake/CMakeLists.txt new file mode 100644 index 00000000..0736f0c8 --- /dev/null +++ b/3P/libubox/builders/cmake/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 2.8.11) + +project(libubox) + +include_directories( + ../../../ +) + +file( + GLOB + source_files + ../../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 + ../../blobmsg_json.c +) + +set(CMAKE_C_FLAGS "-Wall -Werror --std=gnu99 -g3 -Wmissing-declarations") + +add_library( + ubox + SHARED + ${source_files} +) + +target_link_libraries (ubox + LINK_PUBLIC + json-c + rt + ) + +target_include_directories (ubox PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/3P/libubox/examples/CMakeLists.txt b/3P/libubox/examples/CMakeLists.txt new file mode 100644 index 00000000..2126d298 --- /dev/null +++ b/3P/libubox/examples/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(ubox-examples C) +ADD_DEFINITIONS(-O1 -Wall -Werror --std=gnu99 -g3) + +IF(APPLE) + INCLUDE_DIRECTORIES(/opt/local/include) + LINK_DIRECTORIES(/opt/local/lib) +ENDIF() + +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) + diff --git a/3P/libubox/examples/blobmsg-example.c b/3P/libubox/examples/blobmsg-example.c new file mode 100644 index 00000000..69ddce89 --- /dev/null +++ b/3P/libubox/examples/blobmsg-example.c @@ -0,0 +1,139 @@ +#include + +#include "blobmsg.h" +#include "blobmsg_json.h" + +static const char *indent_str = "\t\t\t\t\t\t\t\t\t\t\t\t\t"; + +#define indent_printf(indent, ...) do { \ + if (indent > 0) \ + fwrite(indent_str, indent, 1, stderr); \ + fprintf(stderr, __VA_ARGS__); \ +} while(0) + +static void dump_attr_data(struct blob_attr *data, int indent, int next_indent); + +static void +dump_table(struct blob_attr *head, int len, int indent, bool array) +{ + struct blob_attr *attr; + struct blobmsg_hdr *hdr; + + indent_printf(indent, "{\n"); + __blob_for_each_attr(attr, head, len) { + hdr = blob_data(attr); + if (!array) + indent_printf(indent + 1, "%s : ", hdr->name); + dump_attr_data(attr, 0, indent + 1); + } + indent_printf(indent, "}\n"); +} + +static void dump_attr_data(struct blob_attr *data, int indent, int next_indent) +{ + int type = blobmsg_type(data); + switch(type) { + case BLOBMSG_TYPE_STRING: + indent_printf(indent, "%s\n", blobmsg_get_string(data)); + break; + case BLOBMSG_TYPE_INT8: + indent_printf(indent, "%d\n", blobmsg_get_u8(data)); + break; + case BLOBMSG_TYPE_INT16: + indent_printf(indent, "%d\n", blobmsg_get_u16(data)); + break; + case BLOBMSG_TYPE_INT32: + indent_printf(indent, "%d\n", blobmsg_get_u32(data)); + break; + case BLOBMSG_TYPE_INT64: + indent_printf(indent, "%"PRIu64"\n", blobmsg_get_u64(data)); + break; + case BLOBMSG_TYPE_TABLE: + case BLOBMSG_TYPE_ARRAY: + if (!indent) + indent_printf(indent, "\n"); + dump_table(blobmsg_data(data), blobmsg_data_len(data), + next_indent, type == BLOBMSG_TYPE_ARRAY); + break; + } +} + +enum { + FOO_MESSAGE, + FOO_LIST, + FOO_TESTDATA +}; + +static const struct blobmsg_policy pol[] = { + [FOO_MESSAGE] = { + .name = "message", + .type = BLOBMSG_TYPE_STRING, + }, + [FOO_LIST] = { + .name = "list", + .type = BLOBMSG_TYPE_ARRAY, + }, + [FOO_TESTDATA] = { + .name = "testdata", + .type = BLOBMSG_TYPE_TABLE, + }, +}; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +static void dump_message(struct blob_buf *buf) +{ + struct blob_attr *tb[ARRAY_SIZE(pol)]; + + if (blobmsg_parse(pol, ARRAY_SIZE(pol), tb, blob_data(buf->head), blob_len(buf->head)) != 0) { + fprintf(stderr, "Parse failed\n"); + return; + } + if (tb[FOO_MESSAGE]) + fprintf(stderr, "Message: %s\n", (char *) blobmsg_data(tb[FOO_MESSAGE])); + + if (tb[FOO_LIST]) { + fprintf(stderr, "List: "); + dump_table(blobmsg_data(tb[FOO_LIST]), blobmsg_data_len(tb[FOO_LIST]), 0, true); + } + if (tb[FOO_TESTDATA]) { + fprintf(stderr, "Testdata: "); + dump_table(blobmsg_data(tb[FOO_TESTDATA]), blobmsg_data_len(tb[FOO_TESTDATA]), 0, false); + } +} + +static void +fill_message(struct blob_buf *buf) +{ + void *tbl; + + blobmsg_add_string(buf, "message", "Hello, world!"); + + tbl = blobmsg_open_table(buf, "testdata"); + blobmsg_add_u32(buf, "hello", 1); + blobmsg_add_string(buf, "world", "2"); + blobmsg_close_table(buf, tbl); + + tbl = blobmsg_open_array(buf, "list"); + blobmsg_add_u32(buf, NULL, 0); + blobmsg_add_u32(buf, NULL, 1); + blobmsg_add_u32(buf, NULL, 2); + blobmsg_close_table(buf, tbl); +} + +int main(int argc, char **argv) +{ + static struct blob_buf buf; + + blobmsg_buf_init(&buf); + fill_message(&buf); + dump_message(&buf); + fprintf(stderr, "json: %s\n", blobmsg_format_json(buf.head, true)); + + if (buf.buf) + free(buf.buf); + + return 0; +} diff --git a/3P/libubox/examples/runqueue-example.c b/3P/libubox/examples/runqueue-example.c new file mode 100644 index 00000000..1ae184a9 --- /dev/null +++ b/3P/libubox/examples/runqueue-example.c @@ -0,0 +1,112 @@ +/* + * runqueue-example.c + * + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "runqueue.h" + +static struct runqueue q; + +struct sleeper { + struct runqueue_process proc; + int val; +}; + +static void q_empty(struct runqueue *q) +{ + fprintf(stderr, "All done!\n"); + uloop_end(); +} + +static void q_sleep_run(struct runqueue *q, struct runqueue_task *t) +{ + struct sleeper *s = container_of(t, struct sleeper, proc.task); + char str[32]; + pid_t pid; + + fprintf(stderr, "[%d/%d] start 'sleep %d'\n", q->running_tasks, q->max_running_tasks, s->val); + + pid = fork(); + if (pid < 0) + return; + + if (pid) { + runqueue_process_add(q, &s->proc, pid); + return; + } + + sprintf(str, "%d", s->val); + execlp("sleep", "sleep", str, NULL); + exit(1); +} + +static void q_sleep_cancel(struct runqueue *q, struct runqueue_task *t, int type) +{ + struct sleeper *s = container_of(t, struct sleeper, proc.task); + + fprintf(stderr, "[%d/%d] cancel 'sleep %d'\n", q->running_tasks, q->max_running_tasks, s->val); + runqueue_process_cancel_cb(q, t, type); +} + +static void q_sleep_complete(struct runqueue *q, struct runqueue_task *p) +{ + struct sleeper *s = container_of(p, struct sleeper, proc.task); + + fprintf(stderr, "[%d/%d] finish 'sleep %d'\n", q->running_tasks, q->max_running_tasks, s->val); + free(s); +} + +static void add_sleeper(int val) +{ + static const struct runqueue_task_type sleeper_type = { + .run = q_sleep_run, + .cancel = q_sleep_cancel, + .kill = runqueue_process_kill_cb, + }; + struct sleeper *s; + + s = calloc(1, sizeof(*s)); + s->proc.task.type = &sleeper_type; + s->proc.task.run_timeout = 500; + s->proc.task.complete = q_sleep_complete; + s->val = val; + runqueue_task_add(&q, &s->proc.task, false); +} + +int main(int argc, char **argv) +{ + uloop_init(); + + runqueue_init(&q); + q.empty_cb = q_empty; + q.max_running_tasks = 1; + + if (argc > 1) + q.max_running_tasks = atoi(argv[1]); + + add_sleeper(1); + add_sleeper(1); + add_sleeper(1); + uloop_run(); + uloop_done(); + + return 0; +} diff --git a/3P/libubox/examples/uloop-example.lua b/3P/libubox/examples/uloop-example.lua new file mode 100755 index 00000000..9b0684e6 --- /dev/null +++ b/3P/libubox/examples/uloop-example.lua @@ -0,0 +1,78 @@ +#!/usr/bin/env lua + +local socket = require "socket" + +local uloop = require("uloop") +uloop.init() + +local udp = socket.udp() +udp:settimeout(0) +udp:setsockname('*', 8080) + +-- timer example 1 +local timer +function t() + print("1000 ms timer run"); + timer:set(1000) +end +timer = uloop.timer(t) +timer:set(1000) + +-- timer example 2 +uloop.timer(function() print("2000 ms timer run"); end, 2000) + +-- timer example 3 +uloop.timer(function() print("3000 ms timer run"); end, 3000):cancel() + +-- process +function p1(r) + print("Process 1 completed") + print(r) +end + +function p2(r) + print("Process 2 completed") + print(r) +end + +uloop.timer( + function() + uloop.process("uloop_pid_test.sh", {"foo", "bar"}, {"PROCESS=1"}, p1) + end, 1000 +) +uloop.timer( + function() + uloop.process("uloop_pid_test.sh", {"foo", "bar"}, {"PROCESS=2"}, p2) + end, 2000 +) + +udp_ev = uloop.fd_add(udp, function(ufd, events) + local words, msg_or_ip, port_or_nil = ufd:receivefrom() + print('Recv UDP packet from '..msg_or_ip..':'..port_or_nil..' : '..words) + if words == "Stop!" then + udp_ev:delete() + end +end, uloop.ULOOP_READ) + +udp_count = 0 +udp_send_timer = uloop.timer( + function() + local s = socket.udp() + local words + if udp_count > 3 then + words = "Stop!" + udp_send_timer:cancel() + else + words = 'Hello!' + udp_send_timer:set(1000) + end + print('Send UDP packet to 127.0.0.1:8080 :'..words) + s:sendto(words, '127.0.0.1', 8080) + s:close() + + udp_count = udp_count + 1 + end, 3000 +) + +uloop.run() + diff --git a/3P/libubox/examples/uloop_pid_test.sh b/3P/libubox/examples/uloop_pid_test.sh new file mode 100755 index 00000000..c622064f --- /dev/null +++ b/3P/libubox/examples/uloop_pid_test.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +echo $0 $* +echo Environment: +env + +sleep 2 + +echo "stopping child" + +exit 5 diff --git a/3P/libubox/examples/ustream-example.c b/3P/libubox/examples/ustream-example.c new file mode 100644 index 00000000..3db56c44 --- /dev/null +++ b/3P/libubox/examples/ustream-example.c @@ -0,0 +1,148 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "ustream.h" +#include "uloop.h" +#include "usock.h" + +static struct uloop_fd server; +static const char *port = "10000"; +struct client *next_client = NULL; + +struct client { + struct sockaddr_in sin; + + struct ustream_fd s; + int ctr; +}; + +static void client_read_cb(struct ustream *s, int bytes) +{ + struct client *cl = container_of(s, struct client, s.stream); + struct ustream_buf *buf = s->r.head; + char *newline, *str; + + do { + str = ustream_get_read_buf(s, NULL); + if (!str) + break; + + newline = strchr(buf->data, '\n'); + if (!newline) + break; + + *newline = 0; + ustream_printf(s, "%s\n", str); + ustream_consume(s, newline + 1 - str); + cl->ctr += newline + 1 - str; + } while(1); + + if (s->w.data_bytes > 256 && !ustream_read_blocked(s)) { + fprintf(stderr, "Block read, bytes: %d\n", s->w.data_bytes); + ustream_set_read_blocked(s, true); + } +} + +static void client_close(struct ustream *s) +{ + struct client *cl = container_of(s, struct client, s.stream); + + fprintf(stderr, "Connection closed\n"); + ustream_free(s); + close(cl->s.fd.fd); + free(cl); +} + +static void client_notify_write(struct ustream *s, int bytes) +{ + fprintf(stderr, "Wrote %d bytes, pending: %d\n", bytes, s->w.data_bytes); + + if (s->w.data_bytes < 128 && ustream_read_blocked(s)) { + fprintf(stderr, "Unblock read\n"); + ustream_set_read_blocked(s, false); + } +} + +static void client_notify_state(struct ustream *s) +{ + struct client *cl = container_of(s, struct client, s.stream); + + if (!s->eof) + return; + + fprintf(stderr, "eof!, pending: %d, total: %d\n", s->w.data_bytes, cl->ctr); + if (!s->w.data_bytes) + return client_close(s); + +} + +static void server_cb(struct uloop_fd *fd, unsigned int events) +{ + struct client *cl; + unsigned int sl = sizeof(struct sockaddr_in); + int sfd; + + if (!next_client) + next_client = calloc(1, sizeof(*next_client)); + + cl = next_client; + sfd = accept(server.fd, (struct sockaddr *) &cl->sin, &sl); + if (sfd < 0) { + fprintf(stderr, "Accept failed\n"); + return; + } + + cl->s.stream.string_data = true; + cl->s.stream.notify_read = client_read_cb; + cl->s.stream.notify_state = client_notify_state; + cl->s.stream.notify_write = client_notify_write; + ustream_fd_init(&cl->s, sfd); + next_client = NULL; + fprintf(stderr, "New connection\n"); +} + +static int run_server(void) +{ + + server.cb = server_cb; + server.fd = usock(USOCK_TCP | USOCK_SERVER | USOCK_IPV4ONLY | USOCK_NUMERIC, "127.0.0.1", port); + if (server.fd < 0) { + perror("usock"); + return 1; + } + + uloop_init(); + uloop_fd_add(&server, ULOOP_READ); + uloop_run(); + + return 0; +} + +static int usage(const char *name) +{ + fprintf(stderr, "Usage: %s -p \n", name); + return 1; +} + +int main(int argc, char **argv) +{ + int ch; + + while ((ch = getopt(argc, argv, "p:")) != -1) { + switch(ch) { + case 'p': + port = optarg; + break; + default: + return usage(argv[0]); + } + } + + return run_server(); +} diff --git a/3P/libubox/jshn.c b/3P/libubox/jshn.c new file mode 100644 index 00000000..cec48a07 --- /dev/null +++ b/3P/libubox/jshn.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2011-2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifdef JSONC + #include +#else + #include +#endif + +#include +#include +#include +#include +#include +#include +#include "list.h" + +#include "blob.h" +#include "blobmsg_json.h" + +#define MAX_VARLEN 256 + +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); + +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 (is_error(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 *get_keys(const char *prefix) +{ + char *keys; + + keys = alloca(var_prefix_len + strlen(prefix) + sizeof("KEYS_") + 1); + sprintf(keys, "%sKEYS_%s", var_prefix, prefix); + return getenv(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("TYPE_")); + + sprintf(tmpname, "%s%s_%s", var_prefix, prefix, *name); + *var = getenv(tmpname); + + sprintf(tmpname, "%sTYPE_%s_%s", var_prefix, prefix, *name); + *type = getenv(tmpname); + + sprintf(tmpname, "%sNAME_%s_%s", var_prefix, prefix, *name); + varname = getenv(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; + + obj = json_object_new_object(); + jshn_add_objects(obj, "JSON_VAR", false); + output = json_object_to_json_string(obj); + if (indent) { + blob_buf_init(&b, 0); + blobmsg_add_json_from_string(&b, output); + output = blobmsg_format_json_indent(b.head, 1, 0); + } + fprintf(stdout, "%s%s", output, no_newline ? "" : "\n"); + json_object_put(obj); + return 0; +} + +static int usage(const char *progname) +{ + fprintf(stderr, "Usage: %s [-n] [-i] -r |-w\n", progname); + return 2; +} + +int main(int argc, char **argv) +{ + bool no_newline = false; + bool indent = false; + int ch; + + while ((ch = getopt(argc, argv, "p:nir:w")) != -1) { + switch(ch) { + case 'p': + var_prefix = optarg; + var_prefix_len = strlen(var_prefix); + break; + case 'r': + return jshn_parse(optarg); + case 'w': + return jshn_format(no_newline, indent); + case 'n': + no_newline = true; + break; + case 'i': + indent = true; + break; + default: + return usage(argv[0]); + } + } + return usage(argv[0]); +} diff --git a/3P/libubox/json_script.c b/3P/libubox/json_script.c new file mode 100644 index 00000000..5c35d871 --- /dev/null +++ b/3P/libubox/json_script.c @@ -0,0 +1,651 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include + +#include "avl-cmp.h" +#include "json_script.h" + +struct json_call { + struct json_script_ctx *ctx; + struct blob_attr *vars; + unsigned int seq; +}; + +struct json_handler { + const char *name; + int (*cb)(struct json_call *call, struct blob_attr *cur); +}; + +static int json_process_expr(struct json_call *call, struct blob_attr *cur); +static int json_process_cmd(struct json_call *call, struct blob_attr *cur); + +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); + memcpy(f->data, data, len); + if (name) + f->avl.key = strcpy(new_name, name); + + return f; +} + +static struct json_script_file * +json_script_get_file(struct json_script_ctx *ctx, const char *filename) +{ + struct json_script_file *f; + + f = avl_find_element(&ctx->files, filename, f, avl); + if (f) + return f; + + f = ctx->handle_file(ctx, filename); + if (!f) + return NULL; + + avl_insert(&ctx->files, &f->avl); + return f; +} + +static void __json_script_run(struct json_call *call, struct json_script_file *file, + struct blob_attr *context) +{ + struct json_script_ctx *ctx = call->ctx; + + if (file->seq == call->seq) { + if (context) + ctx->handle_error(ctx, "Recursive include", context); + + return; + } + + file->seq = call->seq; + while (file) { + json_process_cmd(call, file->data); + file = file->next; + } +} + +const char *json_script_find_var(struct json_script_ctx *ctx, struct blob_attr *vars, + const char *name) +{ + struct blob_attr *cur; + int rem; + + blobmsg_for_each_attr(cur, vars, rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) + continue; + + if (strcmp(blobmsg_name(cur), name) != 0) + continue; + + return blobmsg_data(cur); + } + + return ctx->handle_var(ctx, name, vars); +} + +static const char * +msg_find_var(struct json_call *call, const char *name) +{ + return json_script_find_var(call->ctx, call->vars, name); +} + +static void +json_get_tuple(struct blob_attr *cur, struct blob_attr **tb, int t1, int t2) +{ + static struct blobmsg_policy expr_tuple[3] = { + { .type = BLOBMSG_TYPE_STRING }, + {}, + {}, + }; + + expr_tuple[1].type = t1; + expr_tuple[2].type = t2; + blobmsg_parse_array(expr_tuple, 3, tb, blobmsg_data(cur), blobmsg_data_len(cur)); +} + +static int handle_if(struct json_call *call, struct blob_attr *expr) +{ + struct blob_attr *tb[4]; + int ret; + + static const struct blobmsg_policy if_tuple[4] = { + { .type = BLOBMSG_TYPE_STRING }, + { .type = BLOBMSG_TYPE_ARRAY }, + { .type = BLOBMSG_TYPE_ARRAY }, + { .type = BLOBMSG_TYPE_ARRAY }, + }; + + blobmsg_parse_array(if_tuple, 4, tb, blobmsg_data(expr), blobmsg_data_len(expr)); + + if (!tb[1] || !tb[2]) + return 0; + + ret = json_process_expr(call, tb[1]); + if (ret < 0) + return 0; + + if (ret) + return json_process_cmd(call, tb[2]); + + if (!tb[3]) + return 0; + + return json_process_cmd(call, tb[3]); +} + +static int handle_case(struct json_call *call, struct blob_attr *expr) +{ + struct blob_attr *tb[3], *cur; + const char *var; + int rem; + + json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, BLOBMSG_TYPE_TABLE); + if (!tb[1] || !tb[2]) + return 0; + + var = msg_find_var(call, blobmsg_data(tb[1])); + if (!var) + return 0; + + blobmsg_for_each_attr(cur, tb[2], rem) { + if (!strcmp(var, blobmsg_name(cur))) + return json_process_cmd(call, cur); + } + + return 0; +} + +static int handle_return(struct json_call *call, struct blob_attr *expr) +{ + return -2; +} + +static int handle_include(struct json_call *call, struct blob_attr *expr) +{ + struct blob_attr *tb[3]; + struct json_script_file *f; + + json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0); + if (!tb[1]) + return 0; + + f = json_script_get_file(call->ctx, blobmsg_data(tb[1])); + if (!f) + return 0; + + __json_script_run(call, f, expr); + return 0; +} + +static const struct json_handler cmd[] = { + { "if", handle_if }, + { "case", handle_case }, + { "return", handle_return }, + { "include", handle_include }, +}; + +static int eq_regex_cmp(const char *str, const char *pattern, bool regex) +{ + regex_t reg; + int ret; + + if (!regex) + return !strcmp(str, pattern); + + if (regcomp(®, pattern, REG_EXTENDED | REG_NOSUB)) + return 0; + + ret = !regexec(®, str, 0, NULL, 0); + regfree(®); + + return ret; +} + +static int expr_eq_regex(struct json_call *call, struct blob_attr *expr, bool regex) +{ + struct json_script_ctx *ctx = call->ctx; + struct blob_attr *tb[3], *cur; + const char *var; + int rem; + + json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0); + if (!tb[1] || !tb[2]) + return -1; + + var = msg_find_var(call, blobmsg_data(tb[1])); + if (!var) + return 0; + + switch(blobmsg_type(tb[2])) { + case BLOBMSG_TYPE_STRING: + return eq_regex_cmp(var, blobmsg_data(tb[2]), regex); + case BLOBMSG_TYPE_ARRAY: + blobmsg_for_each_attr(cur, tb[2], rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) { + ctx->handle_error(ctx, "Unexpected element type", cur); + return -1; + } + + if (eq_regex_cmp(var, blobmsg_data(cur), regex)) + return 1; + } + return 0; + default: + ctx->handle_error(ctx, "Unexpected element type", tb[2]); + return -1; + } +} + +static int handle_expr_eq(struct json_call *call, struct blob_attr *expr) +{ + return expr_eq_regex(call, expr, false); +} + +static int handle_expr_regex(struct json_call *call, struct blob_attr *expr) +{ + return expr_eq_regex(call, expr, true); +} + +static int handle_expr_has(struct json_call *call, struct blob_attr *expr) +{ + struct json_script_ctx *ctx = call->ctx; + struct blob_attr *tb[3], *cur; + int rem; + + json_get_tuple(expr, tb, 0, 0); + if (!tb[1]) + return -1; + + switch(blobmsg_type(tb[1])) { + case BLOBMSG_TYPE_STRING: + return !!msg_find_var(call, blobmsg_data(tb[1])); + case BLOBMSG_TYPE_ARRAY: + blobmsg_for_each_attr(cur, tb[1], rem) { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) { + ctx->handle_error(ctx, "Unexpected element type", cur); + return -1; + } + + if (msg_find_var(call, blobmsg_data(cur))) + return 1; + } + return 0; + default: + ctx->handle_error(ctx, "Unexpected element type", tb[1]); + return -1; + } +} + +static int expr_and_or(struct json_call *call, struct blob_attr *expr, bool and) +{ + struct blob_attr *cur; + int ret, rem; + int i = 0; + + blobmsg_for_each_attr(cur, expr, rem) { + if (i++ < 1) + continue; + + ret = json_process_expr(call, cur); + if (ret < 0) + return ret; + + if (ret != and) + return ret; + } + + return and; +} + +static int handle_expr_and(struct json_call *call, struct blob_attr *expr) +{ + return expr_and_or(call, expr, 1); +} + +static int handle_expr_or(struct json_call *call, struct blob_attr *expr) +{ + return expr_and_or(call, expr, 0); +} + +static int handle_expr_not(struct json_call *call, struct blob_attr *expr) +{ + struct blob_attr *tb[3]; + + json_get_tuple(expr, tb, BLOBMSG_TYPE_ARRAY, 0); + if (!tb[1]) + return -1; + + return json_process_expr(call, tb[1]); +} + +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 }, +}; + +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); + next = alloca(strlen(pattern) + 1); + strcpy(next, pattern); + + for (str = next; str; str = next) { + const char *cur; + char *end; + 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 (next > 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; + } + + dest = blobmsg_realloc_string_buffer(buf, cur_len + 1); + 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) { + ctx->handle_error(ctx, "Invalid argument in command", attr); + return -1; + } + + 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) { + 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 __thread unsigned int _seq = 0; + struct json_call call = { + .ctx = ctx, + .vars = vars, + .seq = ++_seq, + }; + + /* overflow */ + if (!call.seq) + call.seq = ++_seq; + + __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); + + if (next) + return __json_script_file_free(next); +} + +void +json_script_free(struct json_script_ctx *ctx) +{ + struct json_script_file *f, *next; + + avl_remove_all_elements(&ctx->files, f, avl, next) + __json_script_file_free(f); + + blob_buf_free(&ctx->buf); +} + +static void +__default_handle_error(struct json_script_ctx *ctx, const char *msg, + struct blob_attr *context) +{ +} + +static const char * +__default_handle_var(struct json_script_ctx *ctx, const char *name, + struct blob_attr *vars) +{ + return NULL; +} + +static int +__default_handle_expr(struct json_script_ctx *ctx, const char *name, + struct blob_attr *expr, struct blob_attr *vars) +{ + return -1; +} + +static struct json_script_file * +__default_handle_file(struct json_script_ctx *ctx, const char *name) +{ + return NULL; +} + +void json_script_init(struct json_script_ctx *ctx) +{ + avl_init(&ctx->files, avl_strcmp, false, NULL); + + if (!ctx->handle_error) + ctx->handle_error = __default_handle_error; + + if (!ctx->handle_var) + ctx->handle_var = __default_handle_var; + + if (!ctx->handle_expr) + ctx->handle_expr = __default_handle_expr; + + if (!ctx->handle_file) + ctx->handle_file = __default_handle_file; +} diff --git a/3P/libubox/json_script.h b/3P/libubox/json_script.h new file mode 100644 index 00000000..9475baa5 --- /dev/null +++ b/3P/libubox/json_script.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __JSON_SCRIPT_H +#define __JSON_SCRIPT_H + +#include "avl.h" +#include "blob.h" +#include "blobmsg.h" +#include "utils.h" + +struct json_script_file; + +struct json_script_ctx { + struct avl_tree files; + struct blob_buf buf; + + uint32_t run_seq; + + /* + * 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_eval_string - evaluate a string and store the result + * + * Can be used to process variable references outside of a script + * in a same way that they would be interpreted in the script context. + */ +int json_script_eval_string(struct json_script_ctx *ctx, struct blob_attr *vars, + struct blob_buf *buf, const char *name, + const char *pattern); + +struct json_script_file * +json_script_file_from_blobmsg(const char *name, void *data, int len); + +/* + * json_script_find_var - helper function to find a runtime variable from + * the list passed by json_script user. + * It is intended to be used by the .handle_var callback + */ +const char *json_script_find_var(struct json_script_ctx *ctx, struct blob_attr *vars, + const char *name); + +#endif diff --git a/3P/libubox/kvlist.c b/3P/libubox/kvlist.c new file mode 100644 index 00000000..e0a8acb0 --- /dev/null +++ b/3P/libubox/kvlist.c @@ -0,0 +1,96 @@ +/* + * kvlist - simple key/value store + * + * Copyright (C) 2014 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include + +#include "utils.h" +#include "avl-cmp.h" +#include "blob.h" + +#include "kvlist.h" + +int kvlist_strlen(struct kvlist *kv, const void *data) +{ + return strlen(data) + 1; +} + +int kvlist_blob_len(struct kvlist *kv, const void *data) +{ + return blob_pad_len(data); +} + +void kvlist_init(struct kvlist *kv, int (*get_len)(struct kvlist *kv, const void *data)) +{ + avl_init(&kv->avl, avl_strcmp, false, NULL); + kv->get_len = get_len; +} + +static struct kvlist_node *__kvlist_get(struct kvlist *kv, const char *name) +{ + struct kvlist_node *node; + + return avl_find_element(&kv->avl, name, node, avl); +} + +void *kvlist_get(struct kvlist *kv, const char *name) +{ + struct kvlist_node *node; + + node = __kvlist_get(kv, name); + if (!node) + return NULL; + + return node->data; +} + +bool kvlist_delete(struct kvlist *kv, const char *name) +{ + struct kvlist_node *node; + + node = __kvlist_get(kv, name); + if (node) { + avl_delete(&kv->avl, &node->avl); + free(node); + } + + return !!node; +} + +void 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); + + kvlist_delete(kv, name); + + node = calloc_a(sizeof(struct kvlist_node) + len, + &name_buf, strlen(name) + 1); + memcpy(node->data, data, len); + + node->avl.key = strcpy(name_buf, name); + avl_insert(&kv->avl, &node->avl); +} + +void kvlist_free(struct kvlist *kv) +{ + struct kvlist_node *node, *tmp; + + avl_remove_all_elements(&kv->avl, node, avl, tmp) + free(node); +} diff --git a/3P/libubox/kvlist.h b/3P/libubox/kvlist.h new file mode 100644 index 00000000..0d35b46a --- /dev/null +++ b/3P/libubox/kvlist.h @@ -0,0 +1,54 @@ +/* + * kvlist - simple key/value store + * + * Copyright (C) 2014 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __LIBUBOX_KVLIST_H +#define __LIBUBOX_KVLIST_H + +#include "avl.h" + +struct kvlist { + struct avl_tree avl; + + int (*get_len)(struct kvlist *kv, const void *data); +}; + +struct kvlist_node { + struct avl_node avl; + + char data[0] __attribute__((aligned(4))); +}; + +#define __ptr_to_kv(_ptr) container_of(((char *) (_ptr)), struct kvlist_node, data[0]) +#define __avl_list_to_kv(_l) container_of(_l, struct kvlist_node, avl.list) + +#define kvlist_for_each(kv, name, value) \ + for (value = (void *) __avl_list_to_kv((kv)->avl.list_head.next)->data, \ + name = (const char *) __ptr_to_kv(value)->avl.key, (void) name; \ + &__ptr_to_kv(value)->avl.list != &(kv)->avl.list_head; \ + value = (void *) (__avl_list_to_kv(__ptr_to_kv(value)->avl.list.next))->data, \ + name = (const char *) __ptr_to_kv(value)->avl.key) + +void kvlist_init(struct kvlist *kv, int (*get_len)(struct kvlist *kv, const void *data)); +void kvlist_free(struct kvlist *kv); +void *kvlist_get(struct kvlist *kv, const char *name); +void kvlist_set(struct kvlist *kv, const char *name, const void *data); +bool kvlist_delete(struct kvlist *kv, const char *name); + +int kvlist_strlen(struct kvlist *kv, const void *data); +int kvlist_blob_len(struct kvlist *kv, const void *data); + +#endif diff --git a/3P/libubox/list.h b/3P/libubox/list.h new file mode 100644 index 00000000..ab52acff --- /dev/null +++ b/3P/libubox/list.h @@ -0,0 +1,208 @@ +/*- + * Copyright (c) 2011 Felix Fietkau + * Copyright (c) 2010 Isilon Systems, Inc. + * Copyright (c) 2010 iX Systems, Inc. + * Copyright (c) 2010 Panasas, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _LINUX_LIST_H_ +#define _LINUX_LIST_H_ + +#include +#include + +#define prefetch(x) + +#ifndef container_of +#define container_of(ptr, type, member) \ + ({ \ + const typeof(((type *) NULL)->member) *__mptr = (ptr); \ + (type *) ((char *) __mptr - offsetof(type, member)); \ + }) +#endif + +struct list_head { + struct list_head *next; + struct list_head *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } +#undef LIST_HEAD +#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) + +static inline void +INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list->prev = list; +} + +static inline bool +list_empty(const struct list_head *head) +{ + return (head->next == head); +} + +static inline bool +list_is_first(const struct list_head *list, + const struct list_head *head) +{ + return list->prev == head; +} + +static inline bool +list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +static inline void +_list_del(struct list_head *entry) +{ + entry->next->prev = entry->prev; + entry->prev->next = entry->next; +} + +static inline void +list_del(struct list_head *entry) +{ + _list_del(entry); + entry->next = entry->prev = NULL; +} + +static inline void +_list_add(struct list_head *_new, struct list_head *prev, + struct list_head *next) +{ + + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +static inline void +list_del_init(struct list_head *entry) +{ + _list_del(entry); + INIT_LIST_HEAD(entry); +} + +#define list_entry(ptr, type, field) container_of(ptr, type, field) +#define list_first_entry(ptr, type, field) list_entry((ptr)->next, type, field) +#define list_last_entry(ptr, type, field) list_entry((ptr)->prev, type, field) + +#define list_for_each(p, head) \ + for (p = (head)->next; p != (head); p = p->next) + +#define list_for_each_safe(p, n, head) \ + for (p = (head)->next, n = p->next; p != (head); p = n, n = p->next) + +#define list_for_each_entry(p, h, field) \ + for (p = list_first_entry(h, typeof(*p), field); &p->field != (h); \ + p = list_entry(p->field.next, typeof(*p), field)) + +#define list_for_each_entry_safe(p, n, h, field) \ + for (p = list_first_entry(h, typeof(*p), field), \ + n = list_entry(p->field.next, typeof(*p), field); &p->field != (h);\ + p = n, n = list_entry(n->field.next, typeof(*n), field)) + +#define list_for_each_entry_reverse(p, h, field) \ + for (p = list_last_entry(h, typeof(*p), field); &p->field != (h); \ + p = list_entry(p->field.prev, typeof(*p), field)) + +#define list_for_each_prev(p, h) for (p = (h)->prev; p != (h); p = p->prev) +#define list_for_each_prev_safe(p, n, h) for (p = (h)->prev, n = p->prev; p != (h); p = n, n = p->prev) + +static inline void +list_add(struct list_head *_new, struct list_head *head) +{ + _list_add(_new, head, head->next); +} + +static inline void +list_add_tail(struct list_head *_new, struct list_head *head) +{ + _list_add(_new, head->prev, head); +} + +static inline void +list_move(struct list_head *list, struct list_head *head) +{ + _list_del(list); + list_add(list, head); +} + +static inline void +list_move_tail(struct list_head *entry, struct list_head *head) +{ + _list_del(entry); + list_add_tail(entry, head); +} + +static inline void +_list_splice(const struct list_head *list, struct list_head *prev, + struct list_head *next) +{ + struct list_head *first; + struct list_head *last; + + if (list_empty(list)) + return; + + first = list->next; + last = list->prev; + first->prev = prev; + prev->next = first; + last->next = next; + next->prev = last; +} + +static inline void +list_splice(const struct list_head *list, struct list_head *head) +{ + _list_splice(list, head, head->next); +} + +static inline void +list_splice_tail(struct list_head *list, struct list_head *head) +{ + _list_splice(list, head->prev, head); +} + +static inline void +list_splice_init(struct list_head *list, struct list_head *head) +{ + _list_splice(list, head, head->next); + INIT_LIST_HEAD(list); +} + +static inline void +list_splice_tail_init(struct list_head *list, struct list_head *head) +{ + _list_splice(list, head->prev, head); + INIT_LIST_HEAD(list); +} + +#endif /* _LINUX_LIST_H_ */ diff --git a/3P/libubox/lua/CMakeLists.txt b/3P/libubox/lua/CMakeLists.txt new file mode 100644 index 00000000..04abe0ca --- /dev/null +++ b/3P/libubox/lua/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(uloop C) + +SET(CMAKE_INSTALL_PREFIX /) + +IF(NOT LUA_CFLAGS) + FIND_PROGRAM(PKG_CONFIG pkg-config) + IF(PKG_CONFIG) + EXECUTE_PROCESS( + COMMAND pkg-config --silence-errors --cflags lua5.1 + OUTPUT_VARIABLE LUA_CFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + ENDIF() +ENDIF() + +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -I.. ${LUA_CFLAGS}) +LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) + +IF(APPLE) + ADD_DEFINITIONS(-I/opt/local/include) + SET(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") +ENDIF(APPLE) + +IF(NOT LUAPATH) + EXECUTE_PROCESS( + COMMAND lua -e "for k in string.gmatch(package.cpath .. \";\", \"([^;]+)/..so;\") do if k:sub(1,1) == \"/\" then print(k) break end end" + OUTPUT_VARIABLE LUAPATH + RESULT_VARIABLE LUA_CHECK_RES + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + IF(BUILD_LUA) + IF(NOT ${LUA_CHECK_RES} EQUAL 0 OR "${LUAPATH}" EQUAL "") + MESSAGE(SEND_ERROR "Lua was not found on your system") + ENDIF() + ENDIF() +ENDIF() + +IF(BUILD_LUA) + ADD_LIBRARY(uloop_lua MODULE uloop.c) + SET_TARGET_PROPERTIES(uloop_lua PROPERTIES + OUTPUT_NAME uloop + PREFIX "" + ) + TARGET_LINK_LIBRARIES(uloop_lua ubox) + + INSTALL(TARGETS uloop_lua + LIBRARY DESTINATION ${LUAPATH} + ) +ENDIF() diff --git a/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/uloop.dep b/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/uloop.dep new file mode 100644 index 00000000..1809ca2e --- /dev/null +++ b/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/uloop.dep @@ -0,0 +1,8 @@ +uloop.o: ../../../uloop.c \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/luaconf.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lnum_config.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lualib.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lauxlib.h \ + ../../../../uloop.h ../../../../list.h ../../../../list.h diff --git a/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/uloop.o b/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/uloop.o new file mode 100644 index 00000000..a5dba2ef Binary files /dev/null and b/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/uloop.o differ diff --git a/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/uloop.dep b/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/uloop.dep new file mode 100644 index 00000000..1809ca2e --- /dev/null +++ b/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/uloop.dep @@ -0,0 +1,8 @@ +uloop.o: ../../../uloop.c \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/luaconf.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lnum_config.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lualib.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lauxlib.h \ + ../../../../uloop.h ../../../../list.h ../../../../list.h diff --git a/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/uloop.o b/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/uloop.o new file mode 100644 index 00000000..f79ef714 Binary files /dev/null and b/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/uloop.o differ diff --git a/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/uloop.dep b/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/uloop.dep new file mode 100644 index 00000000..1809ca2e --- /dev/null +++ b/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/uloop.dep @@ -0,0 +1,8 @@ +uloop.o: ../../../uloop.c \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/luaconf.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lnum_config.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lualib.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lauxlib.h \ + ../../../../uloop.h ../../../../list.h ../../../../list.h diff --git a/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/uloop.o b/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/uloop.o new file mode 100644 index 00000000..ce9d92ec Binary files /dev/null and b/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/uloop.o differ diff --git a/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/uloop.dep b/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/uloop.dep new file mode 100644 index 00000000..1809ca2e --- /dev/null +++ b/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/uloop.dep @@ -0,0 +1,8 @@ +uloop.o: ../../../uloop.c \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/luaconf.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lnum_config.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lualib.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lauxlib.h \ + ../../../../uloop.h ../../../../list.h ../../../../list.h diff --git a/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/uloop.o b/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/uloop.o new file mode 100644 index 00000000..5924e5d8 Binary files /dev/null and b/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/uloop.o differ diff --git a/3P/libubox/lua/uloop.c b/3P/libubox/lua/uloop.c new file mode 100644 index 00000000..2a0a516b --- /dev/null +++ b/3P/libubox/lua/uloop.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2012 John Crispin + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include + +#include +#include +#include + +#include "../uloop.h" +#include "../list.h" + +struct lua_uloop_fd { + struct uloop_fd fd; + int r; + int fd_r; +}; + +struct lua_uloop_timeout { + struct uloop_timeout t; + int r; +}; + +struct lua_uloop_process { + struct uloop_process p; + int r; +}; + +static lua_State *state; + +static void ul_timer_cb(struct uloop_timeout *t) +{ + struct lua_uloop_timeout *tout = container_of(t, struct lua_uloop_timeout, t); + + lua_getglobal(state, "__uloop_cb"); + lua_rawgeti(state, -1, tout->r); + lua_remove(state, -2); + + lua_call(state, 0, 0); + +} + +static int ul_timer_set(lua_State *L) +{ + struct lua_uloop_timeout *tout; + double set; + + if (!lua_isnumber(L, -1)) { + lua_pushstring(L, "invalid arg list"); + lua_error(L); + + return 0; + } + + set = lua_tointeger(L, -1); + tout = lua_touserdata(L, 1); + uloop_timeout_set(&tout->t, set); + + return 1; +} + +static int ul_timer_free(lua_State *L) +{ + struct lua_uloop_timeout *tout = lua_touserdata(L, 1); + + uloop_timeout_cancel(&tout->t); + + /* obj.__index.__gc = nil , make sure executing only once*/ + lua_getfield(L, -1, "__index"); + lua_pushstring(L, "__gc"); + lua_pushnil(L); + lua_settable(L, -3); + + lua_getglobal(state, "__uloop_cb"); + luaL_unref(state, -1, tout->r); + + return 1; +} + +static const luaL_Reg timer_m[] = { + { "set", ul_timer_set }, + { "cancel", ul_timer_free }, + { NULL, NULL } +}; + +static int ul_timer(lua_State *L) +{ + struct lua_uloop_timeout *tout; + int set = 0; + int ref; + + if (lua_isnumber(L, -1)) { + set = lua_tointeger(L, -1); + lua_pop(L, 1); + } + + if (!lua_isfunction(L, -1)) { + lua_pushstring(L, "invalid arg list"); + lua_error(L); + + return 0; + } + + lua_getglobal(L, "__uloop_cb"); + lua_pushvalue(L, -2); + ref = luaL_ref(L, -2); + + tout = lua_newuserdata(L, sizeof(*tout)); + lua_createtable(L, 0, 2); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, ul_timer_free); + lua_setfield(L, -2, "__gc"); + lua_pushvalue(L, -1); + lua_setmetatable(L, -3); + lua_pushvalue(L, -2); + luaI_openlib(L, NULL, timer_m, 1); + lua_pushvalue(L, -2); + + memset(tout, 0, sizeof(*tout)); + + tout->r = ref; + tout->t.cb = ul_timer_cb; + if (set) + uloop_timeout_set(&tout->t, set); + + return 1; +} + +static void ul_ufd_cb(struct uloop_fd *fd, unsigned int events) +{ + struct lua_uloop_fd *ufd = container_of(fd, struct lua_uloop_fd, fd); + + lua_getglobal(state, "__uloop_cb"); + lua_rawgeti(state, -1, ufd->r); + lua_remove(state, -2); + + /* push fd object */ + lua_getglobal(state, "__uloop_fds"); + lua_rawgeti(state, -1, ufd->fd_r); + lua_remove(state, -2); + + /* push events */ + lua_pushinteger(state, events); + lua_call(state, 2, 0); +} + + +static int get_sock_fd(lua_State* L, int idx) { + int fd; + if(lua_isnumber(L, idx)) { + fd = lua_tonumber(L, idx); + } else { + luaL_checktype(L, idx, LUA_TUSERDATA); + lua_getfield(L, idx, "getfd"); + if(lua_isnil(L, -1)) + return luaL_error(L, "socket type missing 'getfd' method"); + lua_pushvalue(L, idx - 1); + lua_call(L, 1, 1); + fd = lua_tointeger(L, -1); + lua_pop(L, 1); + } + return fd; +} + +static int ul_ufd_delete(lua_State *L) +{ + struct lua_uloop_fd *ufd = lua_touserdata(L, 1); + + uloop_fd_delete(&ufd->fd); + + /* obj.__index.__gc = nil , make sure executing only once*/ + lua_getfield(L, -1, "__index"); + lua_pushstring(L, "__gc"); + lua_pushnil(L); + lua_settable(L, -3); + + lua_getglobal(state, "__uloop_cb"); + luaL_unref(state, -1, ufd->r); + lua_remove(state, -1); + + lua_getglobal(state, "__uloop_fds"); + luaL_unref(state, -1, ufd->fd_r); + lua_remove(state, -1); + + return 1; +} + +static const luaL_Reg ufd_m[] = { + { "delete", ul_ufd_delete }, + { NULL, NULL } +}; + +static int ul_ufd_add(lua_State *L) +{ + struct lua_uloop_fd *ufd; + int fd = 0; + unsigned int flags = 0; + int ref; + int fd_ref; + + if (lua_isnumber(L, -1)) { + flags = lua_tointeger(L, -1); + lua_pop(L, 1); + } + + if (!lua_isfunction(L, -1)) { + lua_pushstring(L, "invalid arg list"); + lua_error(L); + + return 0; + } + + fd = get_sock_fd(L, -2); + + lua_getglobal(L, "__uloop_cb"); + lua_pushvalue(L, -2); + ref = luaL_ref(L, -2); + lua_pop(L, 1); + + lua_getglobal(L, "__uloop_fds"); + lua_pushvalue(L, -3); + fd_ref = luaL_ref(L, -2); + lua_pop(L, 1); + + ufd = lua_newuserdata(L, sizeof(*ufd)); + + lua_createtable(L, 0, 2); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, ul_ufd_delete); + lua_setfield(L, -2, "__gc"); + lua_pushvalue(L, -1); + lua_setmetatable(L, -3); + lua_pushvalue(L, -2); + luaI_openlib(L, NULL, ufd_m, 1); + lua_pushvalue(L, -2); + + memset(ufd, 0, sizeof(*ufd)); + + ufd->r = ref; + ufd->fd.fd = fd; + ufd->fd_r = fd_ref; + ufd->fd.cb = ul_ufd_cb; + if (flags) + uloop_fd_add(&ufd->fd, flags); + + return 1; +} + +static void ul_process_cb(struct uloop_process *p, int ret) +{ + struct lua_uloop_process *proc = container_of(p, struct lua_uloop_process, p); + + lua_getglobal(state, "__uloop_cb"); + lua_rawgeti(state, -1, proc->r); + + luaL_unref(state, -2, proc->r); + lua_remove(state, -2); + lua_pushinteger(state, ret >> 8); + lua_call(state, 1, 0); +} + +static int ul_process(lua_State *L) +{ + struct lua_uloop_process *proc; + pid_t pid; + int ref; + + if (!lua_isfunction(L, -1) || !lua_istable(L, -2) || + !lua_istable(L, -3) || !lua_isstring(L, -4)) { + lua_pushstring(L, "invalid arg list"); + lua_error(L); + + return 0; + } + + pid = fork(); + + if (pid == -1) { + lua_pushstring(L, "failed to fork"); + lua_error(L); + + return 0; + } + + if (pid == 0) { + /* child */ + int argn = lua_objlen(L, -3); + int envn = lua_objlen(L, -2); + char** argp = malloc(sizeof(char*) * (argn + 2)); + char** envp = malloc(sizeof(char*) * envn + 1); + int i = 1; + + argp[0] = (char*) lua_tostring(L, -4); + for (i = 1; i <= argn; i++) { + lua_rawgeti(L, -3, i); + argp[i] = (char*) lua_tostring(L, -1); + lua_pop(L, 1); + } + argp[i] = NULL; + + for (i = 1; i <= envn; i++) { + lua_rawgeti(L, -2, i); + envp[i - 1] = (char*) lua_tostring(L, -1); + lua_pop(L, 1); + } + envp[i - 1] = NULL; + + execve(*argp, argp, envp); + exit(-1); + } + + lua_getglobal(L, "__uloop_cb"); + lua_pushvalue(L, -2); + ref = luaL_ref(L, -2); + + proc = lua_newuserdata(L, sizeof(*proc)); + memset(proc, 0, sizeof(*proc)); + + proc->r = ref; + proc->p.pid = pid; + proc->p.cb = ul_process_cb; + uloop_process_add(&proc->p); + + return 1; +} + +static int ul_init(lua_State *L) +{ + uloop_init(); + lua_pushboolean(L, 1); + + return 1; +} + +static int ul_run(lua_State *L) +{ + uloop_run(); + lua_pushboolean(L, 1); + + return 1; +} + +static int ul_end(lua_State *L) +{ + uloop_end(); + return 1; +} + +static luaL_reg uloop_func[] = { + {"init", ul_init}, + {"run", ul_run}, + {"timer", ul_timer}, + {"process", ul_process}, + {"fd_add", ul_ufd_add}, + {"cancel", ul_end}, + {NULL, NULL}, +}; + +/* avoid warnings about missing declarations */ +int luaopen_uloop(lua_State *L); +int luaclose_uloop(lua_State *L); + +int luaopen_uloop(lua_State *L) +{ + state = L; + + lua_createtable(L, 1, 0); + lua_setglobal(L, "__uloop_cb"); + + lua_createtable(L, 1, 0); + lua_setglobal(L, "__uloop_fds"); + + luaL_openlib(L, "uloop", uloop_func, 0); + lua_pushstring(L, "_VERSION"); + lua_pushstring(L, "1.0"); + lua_rawset(L, -3); + + lua_pushstring(L, "ULOOP_READ"); + lua_pushinteger(L, ULOOP_READ); + lua_rawset(L, -3); + + lua_pushstring(L, "ULOOP_WRITE"); + lua_pushinteger(L, ULOOP_WRITE); + lua_rawset(L, -3); + + lua_pushstring(L, "ULOOP_EDGE_TRIGGER"); + lua_pushinteger(L, ULOOP_EDGE_TRIGGER); + lua_rawset(L, -3); + + lua_pushstring(L, "ULOOP_BLOCKING"); + lua_pushinteger(L, ULOOP_BLOCKING); + lua_rawset(L, -3); + + return 1; +} + +int luaclose_uloop(lua_State *L) +{ + lua_pushstring(L, "Called"); + + return 1; +} diff --git a/3P/libubox/md5.c b/3P/libubox/md5.c new file mode 100644 index 00000000..c9064dce --- /dev/null +++ b/3P/libubox/md5.c @@ -0,0 +1,284 @@ +/* + * md5.c - Compute MD5 checksum of strings according to the + * definition of MD5 in RFC 1321 from April 1992. + * + * Written by Ulrich Drepper , 1995. + * + * Copyright (C) 1995-1999 Free Software Foundation, Inc. + * Copyright (C) 2001 Manuel Novoa III + * Copyright (C) 2003 Glenn L. McGrath + * Copyright (C) 2003 Erik Andersen + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include "blob.h" /* TODO: better include for bswap_32 compat */ + +#include +#include + +#include +#include + +#include "md5.h" + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define SWAP_LE32(x) (x) +#else +#define SWAP_LE32(x) bswap_32(x) +#endif + +/* Initialize structure containing state of computation. + * (RFC 1321, 3.3: Step 3) + */ +void md5_begin(md5_ctx_t *ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total = 0; + ctx->buflen = 0; +} + +/* These are the four functions used in the four steps of the MD5 algorithm + * and defined in the RFC 1321. The first function is a little bit optimized + * (as found in Colin Plumbs public domain implementation). + * #define FF(b, c, d) ((b & c) | (~b & d)) + */ +# define FF(b, c, d) (d ^ (b & (c ^ d))) +# define FG(b, c, d) FF (d, b, c) +# define FH(b, c, d) (b ^ c ^ d) +# define FI(b, c, d) (c ^ (b | ~d)) + +/* Hash a single block, 64 bytes long and 4-byte aligned. */ +static void md5_hash_block(const void *buffer, md5_ctx_t *ctx) +{ + uint32_t correct_words[16]; + const uint32_t *words = buffer; + + static const uint32_t C_array[] = { + /* round 1 */ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + /* round 2 */ + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + /* round 3 */ + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + /* round 4 */ + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 + }; + + static const char P_array[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */ + 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */ + 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */ + 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */ + }; + + static const char S_array[] = { + 7, 12, 17, 22, + 5, 9, 14, 20, + 4, 11, 16, 23, + 6, 10, 15, 21 + }; + + uint32_t A = ctx->A; + uint32_t B = ctx->B; + uint32_t C = ctx->C; + uint32_t D = ctx->D; + + uint32_t *cwp = correct_words; + +# define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + const uint32_t *pc; + const char *pp; + const char *ps; + int i; + uint32_t temp; + + for (i = 0; i < 16; i++) { + cwp[i] = SWAP_LE32(words[i]); + } + + pc = C_array; + pp = P_array; + ps = S_array; + + for (i = 0; i < 16; i++) { + temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + ps += 4; + for (i = 0; i < 16; i++) { + temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++; + CYCLIC(temp, ps[i & 3]); + temp += B; + A = D; + D = C; + C = B; + B = temp; + } + + + ctx->A += A; + ctx->B += B; + ctx->C += C; + ctx->D += D; +} + +/* Feed data through a temporary buffer to call md5_hash_aligned_block() + * with chunks of data that are 4-byte aligned and a multiple of 64 bytes. + * This function's internal buffer remembers previous data until it has 64 + * bytes worth to pass on. Call md5_end() to flush this buffer. */ + +void md5_hash(const void *buffer, size_t len, md5_ctx_t *ctx) +{ + char *buf = (char *)buffer; + + /* RFC 1321 specifies the possible length of the file up to 2^64 bits, + * Here we only track the number of bytes. */ + + ctx->total += len; + + // Process all input. + + while (len) { + unsigned i = 64 - ctx->buflen; + + // Copy data into aligned buffer. + + if (i > len) + i = len; + memcpy(ctx->buffer + ctx->buflen, buf, i); + len -= i; + ctx->buflen += i; + buf += i; + + // When buffer fills up, process it. + + if (ctx->buflen == 64) { + md5_hash_block(ctx->buffer, ctx); + ctx->buflen = 0; + } + } +} + +/* Process the remaining bytes in the buffer and put result from CTX + * in first 16 bytes following RESBUF. The result is always in little + * endian byte order, so that a byte-wise output yields to the wanted + * ASCII representation of the message digest. + * + * IMPORTANT: On some systems it is required that RESBUF is correctly + * aligned for a 32 bits value. + */ +void md5_end(void *resbuf, md5_ctx_t *ctx) +{ + char *buf = ctx->buffer; + int i; + + /* Pad data to block size. */ + + buf[ctx->buflen++] = 0x80; + memset(buf + ctx->buflen, 0, 128 - ctx->buflen); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + ctx->total <<= 3; + if (ctx->buflen > 56) + buf += 64; + + for (i = 0; i < 8; i++) + buf[56 + i] = ctx->total >> (i*8); + + /* Process last bytes. */ + if (buf != ctx->buffer) + md5_hash_block(ctx->buffer, ctx); + md5_hash_block(buf, ctx); + + /* Put result from CTX in first 16 bytes following RESBUF. The result is + * always in little endian byte order, so that a byte-wise output yields + * to the wanted ASCII representation of the message digest. + * + * IMPORTANT: On some systems it is required that RESBUF is correctly + * aligned for a 32 bits value. + */ + ((uint32_t *) resbuf)[0] = SWAP_LE32(ctx->A); + ((uint32_t *) resbuf)[1] = SWAP_LE32(ctx->B); + ((uint32_t *) resbuf)[2] = SWAP_LE32(ctx->C); + ((uint32_t *) resbuf)[3] = SWAP_LE32(ctx->D); +} + +int md5sum(char *file, uint32_t *md5) +{ + char buf[256]; + md5_ctx_t ctx; + int len, fd; + int ret = 0; + + memset(md5, 0, sizeof(*md5) * 4); + + fd = open(file, O_RDONLY); + if (fd < 0) + return -1; + + md5_begin(&ctx); + do { + len = read(fd, buf, sizeof(buf)); + if (len < 0) { + if (errno == EINTR) + continue; + + ret = -1; + goto out; + } + if (!len) + break; + + md5_hash(buf, len, &ctx); + } while(1); + + md5_end(md5, &ctx); +out: + close(fd); + + return ret; +} diff --git a/3P/libubox/md5.h b/3P/libubox/md5.h new file mode 100644 index 00000000..63059891 --- /dev/null +++ b/3P/libubox/md5.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * Copyright (C) 2013 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __PROCD_MD5_H +#define __PROCD_MD5_H + +#include +#include + +typedef struct md5_ctx { + uint32_t A; + uint32_t B; + uint32_t C; + uint32_t D; + uint64_t total; + uint32_t buflen; + char buffer[128]; +} md5_ctx_t; + +void md5_begin(md5_ctx_t *ctx); +void md5_hash(const void *data, size_t length, md5_ctx_t *ctx); +void md5_end(void *resbuf, md5_ctx_t *ctx); +int md5sum(char *file, uint32_t *md5); + +#endif diff --git a/3P/libubox/runqueue.c b/3P/libubox/runqueue.c new file mode 100644 index 00000000..1d9fa4b3 --- /dev/null +++ b/3P/libubox/runqueue.c @@ -0,0 +1,282 @@ +/* + * runqueue.c - a simple task queueing/completion tracking helper + * + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include "runqueue.h" + +static void +__runqueue_empty_cb(struct uloop_timeout *timeout) +{ + struct runqueue *q = container_of(timeout, struct runqueue, timeout); + + q->empty_cb(q); +} + +void runqueue_init(struct runqueue *q) +{ + INIT_SAFE_LIST(&q->tasks_active); + INIT_SAFE_LIST(&q->tasks_inactive); +} + +static void __runqueue_start_next(struct uloop_timeout *timeout) +{ + struct runqueue *q = container_of(timeout, struct runqueue, timeout); + struct runqueue_task *t; + + do { + if (q->stopped) + break; + + if (list_empty(&q->tasks_inactive.list)) + break; + + if (q->max_running_tasks && q->running_tasks >= q->max_running_tasks) + break; + + t = list_first_entry(&q->tasks_inactive.list, struct runqueue_task, list.list); + safe_list_del(&t->list); + safe_list_add(&t->list, &q->tasks_active); + t->running = true; + q->running_tasks++; + if (t->run_timeout) + uloop_timeout_set(&t->timeout, t->run_timeout); + t->type->run(q, t); + } while (1); + + if (!q->empty && + list_empty(&q->tasks_active.list) && + list_empty(&q->tasks_inactive.list)) { + q->empty = true; + if (q->empty_cb) { + q->timeout.cb = __runqueue_empty_cb; + uloop_timeout_set(&q->timeout, 1); + } + } +} + +static void runqueue_start_next(struct runqueue *q) +{ + if (q->empty) + return; + + q->timeout.cb = __runqueue_start_next; + uloop_timeout_set(&q->timeout, 1); +} + +static int __runqueue_cancel(void *ctx, struct safe_list *list) +{ + struct runqueue_task *t; + + t = container_of(list, struct runqueue_task, list); + runqueue_task_cancel(t, 0); + + return 0; +} + +void runqueue_cancel_active(struct runqueue *q) +{ + safe_list_for_each(&q->tasks_active, __runqueue_cancel, NULL); +} + +void runqueue_cancel_pending(struct runqueue *q) +{ + safe_list_for_each(&q->tasks_inactive, __runqueue_cancel, NULL); +} + +void runqueue_cancel(struct runqueue *q) +{ + runqueue_cancel_pending(q); + runqueue_cancel_active(q); +} + +void runqueue_kill(struct runqueue *q) +{ + struct runqueue_task *t; + + while (!list_empty(&q->tasks_active.list)) { + t = list_first_entry(&q->tasks_active.list, struct runqueue_task, list.list); + runqueue_task_kill(t); + } + runqueue_cancel_pending(q); + uloop_timeout_cancel(&q->timeout); +} + +void runqueue_task_cancel(struct runqueue_task *t, int type) +{ + if (!t->queued) + return; + + if (!t->running) { + runqueue_task_complete(t); + return; + } + + t->cancelled = true; + if (t->cancel_timeout) + uloop_timeout_set(&t->timeout, t->cancel_timeout); + if (t->type->cancel) + t->type->cancel(t->q, t, type); +} + +static void +__runqueue_task_timeout(struct uloop_timeout *timeout) +{ + struct runqueue_task *t = container_of(timeout, struct runqueue_task, timeout); + + if (t->cancelled) + runqueue_task_kill(t); + else + runqueue_task_cancel(t, t->cancel_type); +} + +static void _runqueue_task_add(struct runqueue *q, struct runqueue_task *t, bool running, bool first) +{ + struct safe_list *head; + + if (t->queued) + return; + + if (!t->type->run && !running) { + fprintf(stderr, "BUG: inactive task added without run() callback\n"); + return; + } + + if (running) { + q->running_tasks++; + head = &q->tasks_active; + } else { + head = &q->tasks_inactive; + } + + t->timeout.cb = __runqueue_task_timeout; + t->q = q; + if (first) + safe_list_add_first(&t->list, head); + else + safe_list_add(&t->list, head); + t->cancelled = false; + t->queued = true; + t->running = running; + q->empty = false; + + runqueue_start_next(q); +} + +void runqueue_task_add(struct runqueue *q, struct runqueue_task *t, bool running) +{ + _runqueue_task_add(q, t, running, 0); +} + +void runqueue_task_add_first(struct runqueue *q, struct runqueue_task *t, bool running) +{ + _runqueue_task_add(q, t, running, 1); +} + +void runqueue_task_kill(struct runqueue_task *t) +{ + struct runqueue *q = t->q; + bool running = t->running; + + if (!t->queued) + return; + + runqueue_task_complete(t); + if (running && t->type->kill) + t->type->kill(q, t); + + runqueue_start_next(q); +} + +void runqueue_stop(struct runqueue *q) +{ + q->stopped = true; +} + +void runqueue_resume(struct runqueue *q) +{ + q->stopped = false; + runqueue_start_next(q); +} + +void runqueue_task_complete(struct runqueue_task *t) +{ + struct runqueue *q = t->q; + + if (!t->queued) + return; + + if (t->running) + t->q->running_tasks--; + + uloop_timeout_cancel(&t->timeout); + + safe_list_del(&t->list); + t->queued = false; + t->running = false; + t->cancelled = false; + if (t->complete) + t->complete(q, t); + runqueue_start_next(t->q); +} + +static void +__runqueue_proc_cb(struct uloop_process *p, int ret) +{ + struct runqueue_process *t = container_of(p, struct runqueue_process, proc); + + runqueue_task_complete(&t->task); +} + +void runqueue_process_cancel_cb(struct runqueue *q, struct runqueue_task *t, int type) +{ + struct runqueue_process *p = container_of(t, struct runqueue_process, task); + + if (!type) + type = SIGTERM; + + kill(p->proc.pid, type); +} + +void runqueue_process_kill_cb(struct runqueue *q, struct runqueue_task *t) +{ + struct runqueue_process *p = container_of(t, struct runqueue_process, task); + + uloop_process_delete(&p->proc); + kill(p->proc.pid, SIGKILL); +} + +static const struct runqueue_task_type runqueue_proc_type = { + .name = "process", + .cancel = runqueue_process_cancel_cb, + .kill = runqueue_process_kill_cb, +}; + +void runqueue_process_add(struct runqueue *q, struct runqueue_process *p, pid_t pid) +{ + if (p->proc.pending) + return; + + p->proc.pid = pid; + p->proc.cb = __runqueue_proc_cb; + if (!p->task.type) + p->task.type = &runqueue_proc_type; + uloop_process_add(&p->proc); + if (!p->task.running) + runqueue_task_add(q, &p->task, true); +} diff --git a/3P/libubox/runqueue.h b/3P/libubox/runqueue.h new file mode 100644 index 00000000..0d4173db --- /dev/null +++ b/3P/libubox/runqueue.h @@ -0,0 +1,115 @@ +/* + * runqueue.c - a simple task queueing/completion tracking helper + * + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __LIBUBOX_RUNQUEUE_H +#define __LIBUBOX_RUNQUEUE_H + +#include "list.h" +#include "safe_list.h" +#include "uloop.h" + +struct runqueue; +struct runqueue_task; +struct runqueue_task_type; + +struct runqueue { + struct safe_list tasks_active; + struct safe_list tasks_inactive; + struct uloop_timeout timeout; + + int running_tasks; + int max_running_tasks; + bool stopped; + bool empty; + + /* called when the runqueue is emptied */ + void (*empty_cb)(struct runqueue *q); +}; + +struct runqueue_task_type { + const char *name; + + /* + * called when a task is requested to run + * + * The task is removed from the list before this callback is run. It + * can re-arm itself using runqueue_task_add. + */ + void (*run)(struct runqueue *q, struct runqueue_task *t); + + /* + * called to request cancelling a task + * + * int type is used as an optional hint for the method to be used when + * cancelling the task, e.g. a signal number for processes. Calls + * runqueue_task_complete when done. + */ + void (*cancel)(struct runqueue *q, struct runqueue_task *t, int type); + + /* + * called to kill a task. must not make any calls to runqueue_task_complete, + * it has already been removed from the list. + */ + void (*kill)(struct runqueue *q, struct runqueue_task *t); +}; + +struct runqueue_task { + struct safe_list list; + const struct runqueue_task_type *type; + struct runqueue *q; + + void (*complete)(struct runqueue *q, struct runqueue_task *t); + + struct uloop_timeout timeout; + int run_timeout; + int cancel_timeout; + int cancel_type; + + bool queued; + bool running; + bool cancelled; +}; + +struct runqueue_process { + struct runqueue_task task; + struct uloop_process proc; +}; + +void runqueue_init(struct runqueue *q); +void runqueue_cancel(struct runqueue *q); +void runqueue_cancel_active(struct runqueue *q); +void runqueue_cancel_pending(struct runqueue *q); +void runqueue_kill(struct runqueue *q); + +void runqueue_stop(struct runqueue *q); +void runqueue_resume(struct runqueue *q); + +void runqueue_task_add(struct runqueue *q, struct runqueue_task *t, bool running); +void runqueue_task_add_first(struct runqueue *q, struct runqueue_task *t, bool running); +void runqueue_task_complete(struct runqueue_task *t); + +void runqueue_task_cancel(struct runqueue_task *t, int type); +void runqueue_task_kill(struct runqueue_task *t); + +void runqueue_process_add(struct runqueue *q, struct runqueue_process *p, pid_t pid); + +/* to be used only from runqueue_process callbacks */ +void runqueue_process_cancel_cb(struct runqueue *q, struct runqueue_task *t, int type); +void runqueue_process_kill_cb(struct runqueue *q, struct runqueue_task *t); + +#endif diff --git a/3P/libubox/safe_list.c b/3P/libubox/safe_list.c new file mode 100644 index 00000000..16f57e08 --- /dev/null +++ b/3P/libubox/safe_list.c @@ -0,0 +1,121 @@ +/* + * safe_list - linked list protected against recursive iteration with deletes + * + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "safe_list.h" + +struct safe_list_iterator { + struct safe_list_iterator **head; + struct safe_list_iterator *next_i; + struct safe_list *next; +}; + +static void +__safe_list_set_iterator(struct safe_list *list, + struct safe_list_iterator *i) +{ + struct safe_list_iterator *next_i; + struct safe_list *next; + + next = list_entry(list->list.next, struct safe_list, list); + next_i = next->i; + + next->i = i; + i->next = next; + i->head = &next->i; + + i->next_i = next_i; + if (next_i) + next_i->head = &i->next_i; +} + +static void +__safe_list_del_iterator(struct safe_list_iterator *i) +{ + *i->head = i->next_i; + if (i->next_i) + i->next_i->head = i->head; +} + +static void +__safe_list_move_iterator(struct safe_list *list, + struct safe_list_iterator *i) +{ + __safe_list_del_iterator(i); + __safe_list_set_iterator(list, i); +} + +int safe_list_for_each(struct safe_list *head, + int (*cb)(void *ctx, struct safe_list *list), + void *ctx) +{ + struct safe_list_iterator i; + struct safe_list *cur; + int ret = 0; + + for (cur = list_entry(head->list.next, struct safe_list, list), + __safe_list_set_iterator(cur, &i); + cur != head; + cur = i.next, __safe_list_move_iterator(cur, &i)) { + ret = cb(ctx, cur); + if (ret) + break; + } + + __safe_list_del_iterator(&i); + return ret; +} + +void safe_list_add(struct safe_list *list, struct safe_list *head) +{ + list->i = NULL; + list_add_tail(&list->list, &head->list); +} + +void safe_list_add_first(struct safe_list *list, struct safe_list *head) +{ + list->i = NULL; + list_add(&list->list, &head->list); +} + +void safe_list_del(struct safe_list *list) +{ + struct safe_list_iterator *i, *next_i, **tail; + struct safe_list *next; + + next = list_entry(list->list.next, struct safe_list, list); + list_del(&list->list); + + if (!list->i) + return; + + next_i = next->i; + tail = &next->i; + + for (i = list->i; i; i = i->next_i) { + tail = &i->next_i; + i->next = next; + } + + next->i = list->i; + list->i->head = &next->i; + *tail = next_i; + if (next_i) + next_i->head = tail; + + list->i = NULL; +} diff --git a/3P/libubox/safe_list.h b/3P/libubox/safe_list.h new file mode 100644 index 00000000..67b673d9 --- /dev/null +++ b/3P/libubox/safe_list.h @@ -0,0 +1,62 @@ +/* + * safe_list - linked list protected against recursive iteration with deletes + * + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Use this linked list implementation as a replacement for list.h if you + * want to allow deleting arbitrary list entries from within one or more + * recursive iterator calling context + */ + +#ifndef __LIBUBOX_SAFE_LIST_H +#define __LIBUBOX_SAFE_LIST_H + +#include +#include "list.h" +#include "utils.h" + +struct safe_list; +struct safe_list_iterator; + +struct safe_list { + struct list_head list; + struct safe_list_iterator *i; +}; + +int safe_list_for_each(struct safe_list *list, + int (*cb)(void *ctx, struct safe_list *list), + void *ctx); + +void safe_list_add(struct safe_list *list, struct safe_list *head); +void safe_list_add_first(struct safe_list *list, struct safe_list *head); +void safe_list_del(struct safe_list *list); + +#define INIT_SAFE_LIST(_head) \ + do { \ + INIT_LIST_HEAD(_head.list); \ + (_head)->i = NULL; \ + } while (0) + +#define SAFE_LIST_INIT(_name) { LIST_HEAD_INIT(_name.list), NULL } +#define SAFE_LIST(_name) struct safe_list _name = SAFE_LIST_INIT(_name) + +static inline bool safe_list_empty(struct safe_list *head) +{ + return list_empty(&head->list); +} + +#endif diff --git a/3P/libubox/sh/jshn.sh b/3P/libubox/sh/jshn.sh new file mode 100644 index 00000000..5db1667d --- /dev/null +++ b/3P/libubox/sh/jshn.sh @@ -0,0 +1,283 @@ +# 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 + + eval "${JSON_PREFIX}$1=\$(( \${${JSON_PREFIX}$1:-0} + 1))${2:+; $2=\"\$${JSON_PREFIX}$1\"}" +} + +_json_add_generic() { + # type=$1 + # name=$2 + # value=$3 + # cur=$4 + + local var + if [ "${4%%[0-9]*}" = "JSON_ARRAY" ]; then + _json_inc "SEQ_$4" var + else + local name="${2//[^a-zA-Z0-9_]/_}" + [[ "$name" == "$2" ]] || export -- "${JSON_PREFIX}NAME_${4}_${name}=$2" + var="$name" + fi + + local cur_var= + export -- \ + "${JSON_PREFIX}${4}_$var=$3" \ + "${JSON_PREFIX}TYPE_${4}_$var=$1" + _jshn_append "JSON_UNSET" "${4}_$var" + _jshn_append "KEYS_$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="JSON_$3$seq" + _json_set_var "UP_$table" "$cur" + export -- "${JSON_PREFIX}KEYS_$table=" + unset "${JSON_PREFIX}SEQ_$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" "UP_$_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 JSON_VAR; do + unset \ + ${JSON_PREFIX}UP_$tmp \ + ${JSON_PREFIX}KEYS_$tmp \ + ${JSON_PREFIX}SEQ_$tmp \ + ${JSON_PREFIX}TYPE_$tmp \ + ${JSON_PREFIX}NAME_$tmp \ + ${JSON_PREFIX}$tmp + done + + unset \ + ${JSON_PREFIX}JSON_SEQ \ + ${JSON_PREFIX}JSON_CUR \ + ${JSON_PREFIX}JSON_UNSET +} + +json_init() { + json_cleanup + export -- \ + ${JSON_PREFIX}JSON_SEQ=0 \ + ${JSON_PREFIX}JSON_CUR="JSON_VAR" \ + ${JSON_PREFIX}KEYS_JSON_VAR= \ + ${JSON_PREFIX}TYPE_JSON_VAR= +} + +json_add_object() { + _json_add_table "$1" object TABLE +} + +json_close_object() { + _json_close_table +} + +json_add_array() { + _json_add_table "$1" array ARRAY +} + +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}TYPE_${__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}KEYS_${_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 "JSON_VAR" + return 0 + } + [[ "$1" == ".." ]] && { + _json_get_var cur JSON_CUR + _json_get_var cur "UP_$cur" + _json_set_var JSON_CUR "$cur" + return 0 + } + json_get_type type "$target" + case "$type" in + object|array) + json_get_var cur "$target" + _json_set_var JSON_CUR "$cur" + ;; + *) + [ -n "$_json_no_warning" ] || \ + echo "WARNING: Variable '$target' does not exist or is not an array/object" + return 1 + ;; + esac +} + +json_is_a() { + local type + + json_get_type type "$1" + [ "$type" = "$2" ] +} diff --git a/3P/libubox/uloop.c b/3P/libubox/uloop.c new file mode 100644 index 00000000..6b484125 --- /dev/null +++ b/3P/libubox/uloop.c @@ -0,0 +1,680 @@ +/* + * uloop - event loop implementation + * + * Copyright (C) 2010-2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uloop.h" +#include "utils.h" + +#ifdef USE_KQUEUE +#include +#endif +#ifdef USE_EPOLL +#include +#endif +#include + +struct uloop_fd_event { + struct uloop_fd *fd; + unsigned int events; +}; + +struct uloop_fd_stack { + struct uloop_fd_stack *next; + struct uloop_fd *fd; + unsigned int events; +}; + +static __thread struct uloop_fd_stack *fd_stack = NULL; + +#define ULOOP_MAX_EVENTS 10 + +static __thread struct list_head timeouts = {0}; +static __thread struct list_head processes = {0}; + +static __thread int poll_fd = -1; +__thread bool uloop_cancelled = false; +__thread bool uloop_handle_sigchld = true; +static __thread bool do_sigchld = false; + +static __thread struct uloop_fd_event cur_fds[ULOOP_MAX_EVENTS]; +static __thread int cur_fd, cur_nfds; + +#ifdef USE_KQUEUE + +int uloop_init(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 __thread 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; +} + +#endif + +#ifdef USE_EPOLL + +/** + * FIXME: uClibc < 0.9.30.3 does not define EPOLLRDHUP for Linux >= 2.6.17 + */ +#ifndef EPOLLRDHUP +#define EPOLLRDHUP 0x2000 +#endif + +int uloop_init(void) +{ + if (poll_fd >= 0) + return 0; + + if ((timeouts.next == 0) && (timeouts.prev == 0)) INIT_LIST_HEAD (&timeouts); + if ((processes.next == 0) && (processes.prev == 0)) INIT_LIST_HEAD (&processes); + + 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 __thread 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; +} + +#endif + +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(&timeout->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) + 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_handle_sigint(int signo) +{ + uloop_cancelled = true; +} + +static void uloop_sigchld(int signo) +{ + do_sigchld = true; +} + +static void uloop_setup_signals(bool add) +{ + static struct sigaction old_sigint, old_sigchld; + struct sigaction s; + + memset(&s, 0, sizeof(struct sigaction)); + + if (add) { + s.sa_handler = uloop_handle_sigint; + s.sa_flags = 0; + } else { + s = old_sigint; + } + + sigaction(SIGINT, &s, &old_sigint); + + if (!uloop_handle_sigchld) + return; + + if (add) + s.sa_handler = uloop_sigchld; + else + s = old_sigchld; + + sigaction(SIGCHLD, &s, &old_sigchld); +} + +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); +} + +void uloop_run(void) +{ + static __thread 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_cancelled = false; + while(!uloop_cancelled) + { + uloop_gettime(&tv); + uloop_process_timeouts(&tv); + if (uloop_cancelled) + break; + + if (do_sigchld) + uloop_handle_processes(); + uloop_gettime(&tv); + uloop_run_events(uloop_get_next_timeout(&tv)); + } + printf ("uloop_cancelled !\n"); + if (!--recursive_calls) + uloop_setup_signals(false); +} + +void uloop_done(void) +{ + if (poll_fd < 0) + return; + + close(poll_fd); + poll_fd = -1; + + uloop_clear_timeouts(); + uloop_clear_processes(); +} diff --git a/3P/libubox/uloop.h b/3P/libubox/uloop.h new file mode 100644 index 00000000..cea048e8 --- /dev/null +++ b/3P/libubox/uloop.h @@ -0,0 +1,109 @@ +/* + * uloop - event loop implementation + * + * Copyright (C) 2010-2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _ULOOP_H__ +#define _ULOOP_H__ + +#include +#include +#include +#include +#include + +#if defined(__APPLE__) || defined(__FreeBSD__) +#define USE_KQUEUE +#else +#define USE_EPOLL +#endif + +#include "list.h" + +struct uloop_fd; +struct uloop_timeout; +struct uloop_process; + +typedef void (*uloop_fd_handler)(struct uloop_fd *u, unsigned int events); +typedef void (*uloop_timeout_handler)(struct uloop_timeout *t); +typedef void (*uloop_process_handler)(struct uloop_process *c, int ret); + +#define ULOOP_READ (1 << 0) +#define ULOOP_WRITE (1 << 1) +#define ULOOP_EDGE_TRIGGER (1 << 2) +#define ULOOP_BLOCKING (1 << 3) + +#define ULOOP_EVENT_MASK (ULOOP_READ | ULOOP_WRITE) + +/* internal flags */ +#define ULOOP_EVENT_BUFFERED (1 << 4) +#ifdef USE_KQUEUE +#define ULOOP_EDGE_DEFER (1 << 5) +#endif + +#define ULOOP_ERROR_CB (1 << 6) + +struct uloop_fd +{ + uloop_fd_handler cb; + int fd; + bool eof; + bool error; + bool registered; + uint8_t flags; +}; + +struct uloop_timeout +{ + struct list_head list; + bool pending; + + uloop_timeout_handler cb; + struct timeval time; +}; + +struct uloop_process +{ + struct list_head list; + bool pending; + + uloop_process_handler cb; + pid_t pid; +}; + +extern __thread bool uloop_cancelled; +extern __thread 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); +void uloop_run(void); +void uloop_done(void); + +#endif diff --git a/3P/libubox/usock.c b/3P/libubox/usock.c new file mode 100644 index 00000000..04ed4ee5 --- /dev/null +++ b/3P/libubox/usock.c @@ -0,0 +1,119 @@ +/* + * usock - socket helper functions + * + * Copyright (C) 2010 Steven Barth + * Copyright (C) 2011-2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usock.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(struct sockaddr *sa, int sa_len, int family, int socktype, bool server) +{ + int sock; + + sock = socket(family, socktype, 0); + if (sock < 0) + return -1; + + 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(const char *host, int socktype, bool server) +{ + struct sockaddr_un sun = {.sun_family = AF_UNIX}; + + if (strlen(host) >= sizeof(sun.sun_path)) { + errno = EINVAL; + return -1; + } + strcpy(sun.sun_path, host); + + return usock_connect((struct sockaddr*)&sun, sizeof(sun), AF_UNIX, socktype, server); +} + +static int usock_inet(int type, const char *host, const char *service, int socktype, bool 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), + }; + int sock = -1; + + if (getaddrinfo(host, service, &hints, &result)) + return -1; + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sock = usock_connect(rp->ai_addr, rp->ai_addrlen, rp->ai_family, socktype, server); + if (sock >= 0) + break; + } + + freeaddrinfo(result); + return sock; +} + +int usock(int type, const char *host, const char *service) { + int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; + bool server = !!(type & USOCK_SERVER); + int sock; + + if (type & USOCK_UNIX) + sock = usock_unix(host, socktype, server); + else + sock = usock_inet(type, host, service, socktype, server); + + if (sock < 0) + return -1; + + usock_set_flags(sock, type); + return sock; +} diff --git a/3P/libubox/usock.h b/3P/libubox/usock.h new file mode 100644 index 00000000..5df4362c --- /dev/null +++ b/3P/libubox/usock.h @@ -0,0 +1,35 @@ +/* + * usock - socket helper functions + * + * Copyright (C) 2010 Steven Barth + * Copyright (C) 2011-2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef USOCK_H_ +#define USOCK_H_ + +#define USOCK_TCP 0 +#define USOCK_UDP 1 + +#define USOCK_SERVER 0x0100 +#define USOCK_NOCLOEXEC 0x0200 +#define USOCK_NONBLOCK 0x0400 +#define USOCK_NUMERIC 0x0800 +#define USOCK_IPV6ONLY 0x2000 +#define USOCK_IPV4ONLY 0x4000 +#define USOCK_UNIX 0x8000 + +int usock(int type, const char *host, const char *service); + +#endif /* USOCK_H_ */ diff --git a/3P/libubox/ustream-fd.c b/3P/libubox/ustream-fd.c new file mode 100644 index 00000000..4abb5305 --- /dev/null +++ b/3P/libubox/ustream-fd.c @@ -0,0 +1,163 @@ +/* + * ustream - library for stream buffer management + * + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include "ustream.h" + +static void ustream_fd_set_uloop(struct ustream *s, bool write) +{ + struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); + struct ustream_buf *buf; + unsigned int flags = ULOOP_EDGE_TRIGGER; + + 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 { + 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) + 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) + 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) { + if (!ustream_write_pending(s)) + ustream_fd_set_uloop(s, false); + } + + return more; +} + +static bool ustream_fd_poll(struct ustream *s) +{ + struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); + + return __ustream_fd_poll(sf, ULOOP_READ | ULOOP_WRITE); +} + +static void ustream_uloop_cb(struct uloop_fd *fd, unsigned int events) +{ + struct ustream_fd *sf = container_of(fd, struct ustream_fd, fd); + + __ustream_fd_poll(sf, events); +} + +static void ustream_fd_free(struct ustream *s) +{ + struct ustream_fd *sf = container_of(s, struct ustream_fd, stream); + + uloop_fd_delete(&sf->fd); +} + +void ustream_fd_init(struct ustream_fd *sf, int fd) +{ + struct ustream *s = &sf->stream; + + ustream_init_defaults(s); + + sf->fd.fd = fd; + sf->fd.cb = ustream_uloop_cb; + s->set_read_blocked = ustream_fd_set_read_blocked; + s->write = ustream_fd_write; + s->free = ustream_fd_free; + s->poll = ustream_fd_poll; + ustream_fd_set_uloop(s, false); +} diff --git a/3P/libubox/ustream.c b/3P/libubox/ustream.c new file mode 100644 index 00000000..828a025b --- /dev/null +++ b/3P/libubox/ustream.c @@ -0,0 +1,514 @@ +/* + * ustream - library for stream buffer management + * + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "ustream.h" + +static void ustream_init_buf(struct ustream_buf *buf, int len) +{ + if (!len) + abort(); + + memset(buf, 0, sizeof(*buf)); + buf->data = buf->tail = buf->head; + buf->end = buf->head + len; + *buf->head = 0; +} + +static void ustream_add_buf(struct ustream_buf_list *l, struct ustream_buf *buf) +{ + l->buffers++; + if (!l->tail) + l->head = buf; + else + l->tail->next = buf; + + buf->next = NULL; + l->tail = buf; + if (!l->data_tail) + l->data_tail = l->head; +} + +static bool ustream_can_alloc(struct ustream_buf_list *l) +{ + if (l->max_buffers <= 0) + return true; + + return (l->buffers < l->max_buffers); +} + +static int ustream_alloc_default(struct ustream *s, struct ustream_buf_list *l) +{ + struct ustream_buf *buf; + + if (!ustream_can_alloc(l)) + return -1; + + buf = malloc(sizeof(*buf) + l->buffer_len + s->string_data); + 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; + + if (buf->data == buf->head) + return false; + + maxlen = buf->end - buf->head; + offset = buf->data - buf->head; + + if (offset > maxlen / 2) + return true; + + if (buf->tail - buf->data < 32 && offset > maxlen / 4) + return true; + + if (buf != l->tail || ustream_can_alloc(l)) + return false; + + 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); + } + if (buf->tail != buf->end) + return true; + } + + if (buf && 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; +} + +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); + 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); + maxlen = vsnprintf(buf, maxlen + 1, format, arg); + wr = ustream_write_buffered(s, buf + wr, maxlen - wr, wr); + free(buf); + + return wr; +} + +int ustream_printf(struct ustream *s, const char *format, ...) +{ + va_list arg; + int ret; + + if (s->write_error) + return 0; + + va_start(arg, format); + ret = ustream_vprintf(s, format, arg); + va_end(arg); + + return ret; +} diff --git a/3P/libubox/ustream.h b/3P/libubox/ustream.h new file mode 100644 index 00000000..64317441 --- /dev/null +++ b/3P/libubox/ustream.h @@ -0,0 +1,214 @@ +/* + * ustream - library for stream buffer management + * + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __USTREAM_H +#define __USTREAM_H + +#include +#include "uloop.h" + +struct ustream; +struct ustream_buf; + +enum read_blocked_reason { + READ_BLOCKED_USER = (1 << 0), + READ_BLOCKED_FULL = (1 << 1), +}; + +struct ustream_buf_list { + struct ustream_buf *head; + struct ustream_buf *data_tail; + struct ustream_buf *tail; + + int (*alloc)(struct ustream *s, struct ustream_buf_list *l); + + int data_bytes; + + int min_buffers; + int max_buffers; + int buffer_len; + + int buffers; +}; + +struct ustream { + struct ustream_buf_list r, w; + struct uloop_timeout state_change; + struct ustream *next; + + /* + * notify_read: (optional) + * called by the ustream core to notify that new data is available + * for reading. + * must not free the ustream from this callback + */ + void (*notify_read)(struct ustream *s, int bytes_new); + + /* + * notify_write: (optional) + * called by the ustream core to notify that some buffered data has + * been written to the stream. + * must not free the ustream from this callback + */ + void (*notify_write)(struct ustream *s, int bytes); + + /* + * notify_state: (optional) + * called by the ustream implementation to notify that the read + * side of the stream is closed (eof is set) or there was a write + * error (write_error is set). + * will be called again after the write buffer has been emptied when + * the read side has hit EOF. + */ + void (*notify_state)(struct ustream *s); + + /* + * write: + * must be defined by ustream implementation, accepts new write data. + * 'more' is used to indicate that a subsequent call will provide more + * data (useful for aggregating writes) + * returns the number of bytes accepted, or -1 if no more writes can + * be accepted (link error) + */ + int (*write)(struct ustream *s, const char *buf, int len, bool more); + + /* + * free: (optional) + * defined by ustream implementation, tears down the ustream and frees data + */ + void (*free)(struct ustream *s); + + /* + * set_read_blocked: (optional) + * defined by ustream implementation, called when the read_blocked flag + * changes + */ + void (*set_read_blocked)(struct ustream *s); + + /* + * poll: (optional) + * defined by the upstream implementation, called to request polling for + * available data. + * returns true if data was fetched. + */ + bool (*poll)(struct ustream *s); + + /* + * ustream user should set this if the input stream is expected + * to contain string data. the core will keep all data 0-terminated. + */ + bool string_data; + bool write_error; + bool eof, eof_write_done; + + enum read_blocked_reason read_blocked; +}; + +struct ustream_fd { + struct ustream stream; + struct uloop_fd fd; +}; + +struct ustream_buf { + struct ustream_buf *next; + + char *data; + char *tail; + char *end; + + char head[]; +}; + +/* ustream_fd_init: create a file descriptor ustream (uses uloop) */ +void ustream_fd_init(struct ustream_fd *s, int fd); + +/* ustream_free: free all buffers and data associated with a ustream */ +void ustream_free(struct ustream *s); + +/* ustream_consume: remove data from the head of the read buffer */ +void ustream_consume(struct ustream *s, int len); + +/* ustream_write: add data to the write buffer */ +int ustream_write(struct ustream *s, const char *buf, int len, bool more); +int ustream_printf(struct ustream *s, const char *format, ...); +int ustream_vprintf(struct ustream *s, const char *format, va_list arg); + +/* ustream_get_read_buf: get a pointer to the next read buffer data */ +char *ustream_get_read_buf(struct ustream *s, int *buflen); + +/* + * ustream_set_read_blocked: set read blocked state + * + * if set, the ustream will no longer fetch pending data. + */ +void ustream_set_read_blocked(struct ustream *s, bool set); + +static inline bool ustream_read_blocked(struct ustream *s) +{ + return !!(s->read_blocked & READ_BLOCKED_USER); +} + +static inline int ustream_pending_data(struct ustream *s, bool write) +{ + struct ustream_buf_list *b = write ? &s->w : &s->r; + return b->data_bytes; +} + +static inline bool ustream_read_buf_full(struct ustream *s) +{ + struct ustream_buf *buf = s->r.data_tail; + return buf && buf->data == buf->head && buf->tail == buf->end && + s->r.buffers == s->r.max_buffers; +} + +/*** --- functions only used by ustream implementations --- ***/ + +/* ustream_init_defaults: fill default callbacks and options */ +void ustream_init_defaults(struct ustream *s); + +/* + * ustream_reserve: allocate rx buffer space + * + * len: hint for how much space is needed (not guaranteed to be met) + * maxlen: pointer to where the actual buffer size is going to be stored + */ +char *ustream_reserve(struct ustream *s, int len, int *maxlen); + +/* ustream_fill_read: mark rx buffer space as filled */ +void ustream_fill_read(struct ustream *s, int len); + +/* + * ustream_write_pending: attempt to write more data from write buffers + * returns true if all write buffers have been emptied. + */ +bool ustream_write_pending(struct ustream *s); + +static inline void ustream_state_change(struct ustream *s) +{ + uloop_timeout_set(&s->state_change, 0); +} + +static inline bool ustream_poll(struct ustream *s) +{ + if (!s->poll) + return false; + + return s->poll(s); +} + +#endif diff --git a/3P/libubox/utils.c b/3P/libubox/utils.c new file mode 100644 index 00000000..078a8a19 --- /dev/null +++ b/3P/libubox/utils.c @@ -0,0 +1,94 @@ +/* + * utils - misc libubox utility functions + * + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "utils.h" +#include +#include +#include + +#define foreach_arg(_arg, _addr, _len, _first_addr, _first_len) \ + for (_addr = (_first_addr), _len = (_first_len); \ + _addr; \ + _addr = va_arg(_arg, void **), _len = _addr ? va_arg(_arg, size_t) : 0) + +void *__calloc_a(size_t len, ...) +{ + va_list ap, ap1; + void *ret; + void **cur_addr; + size_t cur_len; + int alloc_len = 0; + char *ptr; + + va_start(ap, len); + + va_copy(ap1, ap); + foreach_arg(ap1, cur_addr, cur_len, &ret, len) + alloc_len += cur_len; + va_end(ap1); + + ptr = calloc(1, alloc_len); + 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 + +static void clock_gettime_realtime(struct timespec *tv) +{ + struct timeval _tv; + + gettimeofday(&_tv, NULL); + tv->tv_sec = _tv.tv_sec; + tv->tv_nsec = _tv.tv_usec * 1000; +} + +static void clock_gettime_monotonic(struct timespec *tv) +{ + mach_timebase_info_data_t info; + float sec; + uint64_t val; + + mach_timebase_info(&info); + + val = mach_absolute_time(); + tv->tv_nsec = (val * info.numer / info.denom) % 1000000000; + + sec = val; + sec *= info.numer; + sec /= info.denom; + sec /= 1000000000; + tv->tv_sec = sec; +} + +void clock_gettime(int type, struct timespec *tv) +{ + if (type == CLOCK_REALTIME) + return clock_gettime_realtime(tv); + else + return clock_gettime_monotonic(tv); +} + +#endif diff --git a/3P/libubox/utils.h b/3P/libubox/utils.h new file mode 100644 index 00000000..9688a6a4 --- /dev/null +++ b/3P/libubox/utils.h @@ -0,0 +1,182 @@ +/* + * utils - misc libubox utility functions + * + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __LIBUBOX_UTILS_H +#define __LIBUBOX_UTILS_H + +#include +#include +#include +#include +#include + +/* + * calloc_a(size_t len, [void **addr, size_t len,...], NULL) + * + * allocate a block of memory big enough to hold multiple aligned objects. + * the pointer to the full object (starting with the first chunk) is returned, + * all other pointers are stored in the locations behind extra addr arguments. + * the last argument needs to be a NULL pointer + */ + +#define calloc_a(len, ...) __calloc_a(len, ##__VA_ARGS__, NULL) + +void *__calloc_a(size_t len, ...); + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +#define __BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) + +#ifdef __OPTIMIZE__ +extern int __BUILD_BUG_ON_CONDITION_FAILED; +#define BUILD_BUG_ON(condition) \ + do { \ + __BUILD_BUG_ON(condition); \ + if (condition) \ + __BUILD_BUG_ON_CONDITION_FAILED = 1; \ + } while(0) +#else +#define BUILD_BUG_ON __BUILD_BUG_ON +#endif + +#ifdef __APPLE__ + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 + +void clock_gettime(int type, struct timespec *tv); + +#endif + +#ifdef __GNUC__ +#define _GNUC_MIN_VER(maj, min) (((__GNUC__ << 8) + __GNUC_MINOR__) >= (((maj) << 8) + (min))) +#else +#define _GNUC_MIN_VER(maj, min) 0 +#endif + +#if defined(__linux__) || defined(__CYGWIN__) +#include +#include + +#elif defined(__APPLE__) +#include +#include +#define bswap_32(x) OSSwapInt32(x) +#define bswap_64(x) OSSwapInt64(x) +#elif defined(__FreeBSD__) +#include +#define bswap_32(x) bswap32(x) +#define bswap_64(x) bswap64(x) +#else +#include +#define bswap_32(x) swap32(x) +#define bswap_64(x) swap64(x) +#endif + +#ifndef __BYTE_ORDER +#define __BYTE_ORDER BYTE_ORDER +#endif +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN +#endif +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#endif + +static inline uint16_t __u_bswap16(uint16_t val) +{ + return ((val >> 8) & 0xffu) | ((val & 0xffu) << 8); +} + +#if _GNUC_MIN_VER(4, 2) +#define __u_bswap32(x) __builtin_bswap32(x) +#define __u_bswap64(x) __builtin_bswap64(x) +#else +#define __u_bswap32(x) bswap_32(x) +#define __u_bswap64(x) bswap_64(x) +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +#define cpu_to_be64(x) __u_bswap64(x) +#define cpu_to_be32(x) __u_bswap32(x) +#define cpu_to_be16(x) __u_bswap16((uint16_t) (x)) + +#define be64_to_cpu(x) __u_bswap64(x) +#define be32_to_cpu(x) __u_bswap32(x) +#define be16_to_cpu(x) __u_bswap16((uint16_t) (x)) + +#define cpu_to_le64(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le16(x) (x) + +#define le64_to_cpu(x) (x) +#define le32_to_cpu(x) (x) +#define le16_to_cpu(x) (x) + +#else /* __BYTE_ORDER == __LITTLE_ENDIAN */ + +#define cpu_to_le64(x) __u_bswap64(x) +#define cpu_to_le32(x) __u_bswap32(x) +#define cpu_to_le16(x) __u_bswap16((uint16_t) (x)) + +#define le64_to_cpu(x) __u_bswap64(x) +#define le32_to_cpu(x) __u_bswap32(x) +#define le16_to_cpu(x) __u_bswap16((uint16_t) (x)) + +#define cpu_to_be64(x) (x) +#define cpu_to_be32(x) (x) +#define cpu_to_be16(x) (x) + +#define be64_to_cpu(x) (x) +#define be32_to_cpu(x) (x) +#define be16_to_cpu(x) (x) + +#endif + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +#ifndef __constructor +#define __constructor __attribute__((constructor)) +#endif + +#ifndef __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))); +} + +#endif diff --git a/3P/libubox/vlist.c b/3P/libubox/vlist.c new file mode 100644 index 00000000..d497c632 --- /dev/null +++ b/3P/libubox/vlist.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include "vlist.h" + +void +vlist_init(struct vlist_tree *tree, avl_tree_comp cmp, vlist_update_cb update) +{ + tree->update = update; + tree->version = 1; + + avl_init(&tree->avl, cmp, 0, tree); +} + +void +vlist_delete(struct vlist_tree *tree, struct vlist_node *node) +{ + if (!tree->no_delete) + avl_delete(&tree->avl, &node->avl); + tree->update(tree, NULL, node); +} + +void +vlist_add(struct vlist_tree *tree, struct vlist_node *node, const void *key) +{ + struct vlist_node *old_node = NULL; + struct avl_node *anode; + + node->avl.key = key; + node->version = tree->version; + + anode = avl_find(&tree->avl, key); + if (anode) { + old_node = container_of(anode, struct vlist_node, avl); + if (tree->keep_old || tree->no_delete) { + old_node->version = tree->version; + goto update_only; + } + + avl_delete(&tree->avl, anode); + } + + avl_insert(&tree->avl, &node->avl); + +update_only: + tree->update(tree, node, old_node); +} + +void +vlist_flush(struct vlist_tree *tree) +{ + struct vlist_node *node, *tmp; + + avl_for_each_element_safe(&tree->avl, node, avl, tmp) { + if ((node->version == tree->version || node->version == -1) && + tree->version != -1) + continue; + + vlist_delete(tree, node); + } +} + +void +vlist_flush_all(struct vlist_tree *tree) +{ + tree->version = -1; + vlist_flush(tree); +} + + diff --git a/3P/libubox/vlist.h b/3P/libubox/vlist.h new file mode 100644 index 00000000..8170abf7 --- /dev/null +++ b/3P/libubox/vlist.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __LIBUBOX_VLIST_H +#define __LIBUBOX_VLIST_H + +#include "avl.h" + +struct vlist_tree; +struct vlist_node; + +typedef void (*vlist_update_cb)(struct vlist_tree *tree, + struct vlist_node *node_new, + struct vlist_node *node_old); + +struct vlist_tree { + struct avl_tree avl; + + vlist_update_cb update; + bool keep_old; + bool no_delete; + + int version; +}; + +struct vlist_node { + struct avl_node avl; + int version; +}; + +#define VLIST_TREE_INIT(_name, _comp, _update, _keep_old, _no_delete) \ + { \ + .avl = AVL_TREE_INIT(_name.avl, _comp, false, NULL), \ + .update = _update, \ + .version = 1, \ + .keep_old = _keep_old, \ + .no_delete = _no_delete, \ + } + +#define VLIST_TREE(_name, ...) \ + struct vlist_tree _name = \ + VLIST_TREE_INIT(_name, __VA_ARGS__) + +void vlist_init(struct vlist_tree *tree, avl_tree_comp cmp, vlist_update_cb update); + +#define vlist_find(tree, name, element, node_member) \ + avl_find_element(&(tree)->avl, name, element, node_member.avl) + +static inline void vlist_update(struct vlist_tree *tree) +{ + tree->version++; +} + +void vlist_add(struct vlist_tree *tree, struct vlist_node *node, const void *key); +void vlist_delete(struct vlist_tree *tree, struct vlist_node *node); +void vlist_flush(struct vlist_tree *tree); +void vlist_flush_all(struct vlist_tree *tree); + +#define vlist_for_each_element(tree, element, node_member) \ + avl_for_each_element(&(tree)->avl, element, node_member.avl) + +#endif diff --git a/3P/ubus/.gitignore b/3P/ubus/.gitignore new file mode 100644 index 00000000..551b024f --- /dev/null +++ b/3P/ubus/.gitignore @@ -0,0 +1,12 @@ +Makefile +CMakeCache.txt +CMakeFiles +*.cmake +*.a +*.so +*.dylib +examples/server +examples/client +ubusd +ubus +install_manifest.txt diff --git a/3P/ubus/CMakeLists.txt b/3P/ubus/CMakeLists.txt new file mode 100644 index 00000000..6ebde7d4 --- /dev/null +++ b/3P/ubus/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(ubus C) +# -Os +ADD_DEFINITIONS(-Wall -Werror --std=gnu99 -g3 -Wmissing-declarations) + +OPTION(BUILD_LUA "build Lua plugin" OFF) +OPTION(BUILD_EXAMPLES "build examples" OFF) +OPTION(ENABLE_SYSTEMD "systemd support" OFF) + +SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +SET(UBUS_UNIX_SOCKET "/var/run/ubus.sock") +SET(UBUS_MAX_MSGLEN 1048576) + +ADD_DEFINITIONS( -DUBUS_UNIX_SOCKET="${UBUS_UNIX_SOCKET}") +ADD_DEFINITIONS( -DUBUS_MAX_MSGLEN=${UBUS_MAX_MSGLEN}) + +IF(APPLE) + INCLUDE_DIRECTORIES(/opt/local/include) + LINK_DIRECTORIES(/opt/local/lib) +ENDIF() + +ADD_LIBRARY(ubus SHARED libubus.c libubus-io.c libubus-obj.c libubus-sub.c libubus-req.c) +TARGET_LINK_LIBRARIES(ubus ubox) + +ADD_EXECUTABLE(ubusd ubusd.c ubusd_id.c ubusd_obj.c ubusd_proto.c ubusd_event.c) +TARGET_LINK_LIBRARIES(ubusd ubox) + +find_library(json NAMES json-c json) +ADD_EXECUTABLE(cli cli.c) +SET_TARGET_PROPERTIES(cli PROPERTIES OUTPUT_NAME ubus) +TARGET_LINK_LIBRARIES(cli ubus ubox blobmsg_json ${json}) + +ADD_SUBDIRECTORY(lua) +ADD_SUBDIRECTORY(examples) + +INSTALL(TARGETS ubus cli + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) +INSTALL(TARGETS ubusd + RUNTIME DESTINATION sbin +) + +INSTALL(FILES ubusmsg.h ubus_common.h libubus.h DESTINATION include) + +# FIXME: this works but certainly can be done better: +SET(UBUSD_BINARY "${CMAKE_INSTALL_PREFIX}/sbin/ubusd") + +# do this after the installs so we have the proper paths +IF(ENABLE_SYSTEMD) + INCLUDE(FindPkgConfig) + PKG_CHECK_MODULES(SYSTEMD systemd) + IF(SYSTEMD_FOUND) + ADD_SUBDIRECTORY(systemd) + ENDIF() +ENDIF() diff --git a/3P/ubus/cli.c b/3P/ubus/cli.c new file mode 100644 index 00000000..c99a2d8e --- /dev/null +++ b/3P/ubus/cli.c @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#include +#include "libubus.h" + +static struct blob_buf b; +static int timeout = 30; +static bool simple_output = false; +static int verbose = 0; + +static const char *format_type(void *priv, struct blob_attr *attr) +{ + static const char * const attr_types[] = { + [BLOBMSG_TYPE_INT8] = "\"Boolean\"", + [BLOBMSG_TYPE_INT32] = "\"Integer\"", + [BLOBMSG_TYPE_STRING] = "\"String\"", + [BLOBMSG_TYPE_ARRAY] = "\"Array\"", + [BLOBMSG_TYPE_TABLE] = "\"Table\"", + }; + const char *type = NULL; + int typeid; + + if (blob_id(attr) != BLOBMSG_TYPE_INT32) + return NULL; + + typeid = blobmsg_get_u32(attr); + if (typeid < ARRAY_SIZE(attr_types)) + type = attr_types[typeid]; + if (!type) + type = "\"(unknown)\""; + + return type; +} + +static void receive_list_result(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv) +{ + struct blob_attr *cur; + char *s; + int rem; + + if (simple_output || !verbose) { + printf("%s\n", obj->path); + return; + } + + printf("'%s' @%08x\n", obj->path, obj->id); + + if (!obj->signature) + return; + + blob_for_each_attr(cur, obj->signature, rem) { + s = blobmsg_format_json_with_cb(cur, false, format_type, NULL, -1); + printf("\t%s\n", s); + free(s); + } +} + +static void receive_call_result_data(struct ubus_request *req, int type, struct blob_attr *msg) +{ + char *str; + if (!msg) + return; + + str = blobmsg_format_json_indent(msg, true, simple_output ? -1 : 0); + printf("%s\n", str); + free(str); +} + +static void receive_event(struct ubus_context *ctx, struct ubus_event_handler *ev, + const char *type, struct blob_attr *msg) +{ + char *str; + + str = blobmsg_format_json(msg, true); + printf("{ \"%s\": %s }\n", type, str); + free(str); +} + +static int ubus_cli_list(struct ubus_context *ctx, int argc, char **argv) +{ + const char *path = NULL; + + if (argc > 1) + return -2; + + if (argc == 1) + path = argv[0]; + + return ubus_lookup(ctx, path, receive_list_result, NULL); +} + +static int ubus_cli_call(struct ubus_context *ctx, int argc, char **argv) +{ + uint32_t id; + int ret; + + if (argc < 2 || argc > 3) + return -2; + + blob_buf_init(&b, 0); + if (argc == 3 && !blobmsg_add_json_from_string(&b, argv[2])) { + if (!simple_output) + fprintf(stderr, "Failed to parse message data\n"); + return -1; + } + + ret = ubus_lookup_id(ctx, argv[0], &id); + if (ret) + return ret; + + return ubus_invoke(ctx, id, argv[1], b.head, receive_call_result_data, NULL, timeout * 1000); +} + +static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv) +{ + static struct ubus_event_handler listener; + const char *event; + int ret = 0; + + memset(&listener, 0, sizeof(listener)); + listener.cb = receive_event; + + if (argc > 0) { + event = argv[0]; + } else { + event = "*"; + argc = 1; + } + + do { + ret = ubus_register_event_handler(ctx, &listener, event); + if (ret) + break; + + argv++; + argc--; + if (argc <= 0) + break; + + event = argv[0]; + } while (1); + + if (ret) { + if (!simple_output) + fprintf(stderr, "Error while registering for event '%s': %s\n", + event, ubus_strerror(ret)); + return -1; + } + + uloop_init(); + ubus_add_uloop(ctx); + uloop_run(); + uloop_done(); + + return 0; +} + +static int ubus_cli_send(struct ubus_context *ctx, int argc, char **argv) +{ + if (argc < 1 || argc > 2) + return -2; + + blob_buf_init(&b, 0); + + if (argc == 2 && !blobmsg_add_json_from_string(&b, argv[1])) { + if (!simple_output) + fprintf(stderr, "Failed to parse message data\n"); + return -1; + } + + return ubus_send_event(ctx, argv[0], b.head); +} + +struct cli_wait_data { + struct uloop_timeout timeout; + struct ubus_event_handler ev; + char **pending; + int n_pending; +}; + +static void wait_check_object(struct cli_wait_data *data, const char *path) +{ + int i; + + for (i = 0; i < data->n_pending; i++) { + if (strcmp(path, data->pending[i]) != 0) + continue; + + data->n_pending--; + if (i == data->n_pending) + break; + + memmove(&data->pending[i], &data->pending[i + 1], + (data->n_pending - i) * sizeof(*data->pending)); + i--; + } + + if (!data->n_pending) + uloop_end(); +} + +static void wait_event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev, + const char *type, struct blob_attr *msg) +{ + static const struct blobmsg_policy policy = { + "path", BLOBMSG_TYPE_STRING + }; + struct cli_wait_data *data = container_of(ev, struct cli_wait_data, ev); + struct blob_attr *attr; + const char *path; + + if (strcmp(type, "ubus.object.add") != 0) + return; + + blobmsg_parse(&policy, 1, &attr, blob_data(msg), blob_len(msg)); + if (!attr) + return; + + path = blobmsg_data(attr); + wait_check_object(data, path); +} + +static void wait_list_cb(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv) +{ + struct cli_wait_data *data = priv; + + wait_check_object(data, obj->path); +} + + +static void wait_timeout(struct uloop_timeout *timeout) +{ + uloop_end(); +} + +static int ubus_cli_wait_for(struct ubus_context *ctx, int argc, char **argv) +{ + struct cli_wait_data data = { + .timeout.cb = wait_timeout, + .ev.cb = wait_event_cb, + .pending = argv, + .n_pending = argc, + }; + int ret; + + if (argc < 1) + return -2; + + uloop_init(); + ubus_add_uloop(ctx); + + ret = ubus_lookup(ctx, NULL, wait_list_cb, &data); + if (ret) + return ret; + + if (!data.n_pending) + return ret; + + ret = ubus_register_event_handler(ctx, &data.ev, "ubus.object.add"); + if (ret) + return ret; + + uloop_timeout_set(&data.timeout, timeout * 1000); + uloop_run(); + uloop_done(); + + if (data.n_pending) + return UBUS_STATUS_TIMEOUT; + + return ret; +} + + +static int usage(const char *prog) +{ + fprintf(stderr, + "Usage: %s [] [arguments...]\n" + "Options:\n" + " -s : Set the unix domain socket to connect to\n" + " -t : Set the timeout (in seconds) for a command to complete\n" + " -S: Use simplified output (for scripts)\n" + " -v: More verbose output\n" + "\n" + "Commands:\n" + " - list [] List objects\n" + " - call [] Call an object method\n" + " - listen [...] Listen for events\n" + " - send [] Send an event\n" + " - wait_for [...] Wait for multiple objects to appear on ubus\n" + "\n", prog); + return 1; +} + + +struct { + const char *name; + int (*cb)(struct ubus_context *ctx, int argc, char **argv); +} commands[] = { + { "list", ubus_cli_list }, + { "call", ubus_cli_call }, + { "listen", ubus_cli_listen }, + { "send", ubus_cli_send }, + { "wait_for", ubus_cli_wait_for }, +}; + +int main(int argc, char **argv) +{ + const char *progname, *ubus_socket = NULL; + static struct ubus_context *ctx; + char *cmd; + int ret = 0; + int i, ch; + + progname = argv[0]; + + while ((ch = getopt(argc, argv, "vs:t:S")) != -1) { + switch (ch) { + case 's': + ubus_socket = optarg; + break; + case 't': + timeout = atoi(optarg); + break; + case 'S': + simple_output = true; + break; + case 'v': + verbose++; + break; + default: + return usage(progname); + } + } + + argc -= optind; + argv += optind; + + cmd = argv[0]; + if (argc < 1) + return usage(progname); + + ctx = ubus_connect(ubus_socket); + if (!ctx) { + if (!simple_output) + fprintf(stderr, "Failed to connect to ubus\n"); + return -1; + } + + argv++; + argc--; + + ret = -2; + for (i = 0; i < ARRAY_SIZE(commands); i++) { + if (strcmp(commands[i].name, cmd) != 0) + continue; + + ret = commands[i].cb(ctx, argc, argv); + break; + } + + if (ret > 0 && !simple_output) + fprintf(stderr, "Command failed: %s\n", ubus_strerror(ret)); + else if (ret == -2) + usage(progname); + + ubus_free(ctx); + return ret; +} diff --git a/3P/ubus/examples/CMakeLists.txt b/3P/ubus/examples/CMakeLists.txt new file mode 100644 index 00000000..0279f6e6 --- /dev/null +++ b/3P/ubus/examples/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.6) + +ADD_DEFINITIONS(-I..) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) + +IF (BUILD_EXAMPLES) + ADD_EXECUTABLE(server server.c count.c) + TARGET_LINK_LIBRARIES(server ubus ubox blobmsg_json) + + ADD_EXECUTABLE(client client.c count.c) + TARGET_LINK_LIBRARIES(client ubus ubox) +ENDIF() diff --git a/3P/ubus/examples/client.c b/3P/ubus/examples/client.c new file mode 100644 index 00000000..516d9d0b --- /dev/null +++ b/3P/ubus/examples/client.c @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include + +#include "libubus.h" +#include "count.h" + +static struct ubus_context *ctx; +static struct blob_buf b; + +static void test_client_subscribe_cb(struct ubus_context *ctx, struct ubus_object *obj) +{ + fprintf(stderr, "Subscribers active: %d\n", obj->has_subscribers); +} + +static struct ubus_object test_client_object = { + .subscribe_cb = test_client_subscribe_cb, +}; + +static void test_client_notify_cb(struct uloop_timeout *timeout) +{ + static int counter = 0; + int err; + struct timeval tv1, tv2; + int max = 1000; + long delta; + int i = 0; + + blob_buf_init(&b, 0); + blobmsg_add_u32(&b, "counter", counter++); + + gettimeofday(&tv1, NULL); + for (i = 0; i < max; i++) + err = ubus_notify(ctx, &test_client_object, "ping", b.head, 1000); + gettimeofday(&tv2, NULL); + if (err) + fprintf(stderr, "Notify failed: %s\n", ubus_strerror(err)); + + delta = (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec); + fprintf(stderr, "Avg time per iteration: %ld usec\n", delta / max); + + uloop_timeout_set(timeout, 1000); +} + +enum { + RETURN_CODE, + __RETURN_MAX, +}; + +static const struct blobmsg_policy return_policy[__RETURN_MAX] = { + [RETURN_CODE] = { .name = "rc", .type = BLOBMSG_TYPE_INT32 }, +}; + +static void test_count_data_cb(struct ubus_request *req, + int type, struct blob_attr *msg) +{ + struct blob_attr *tb[__RETURN_MAX]; + int rc; + uint32_t count_to = *(uint32_t *)req->priv; + + blobmsg_parse(return_policy, __RETURN_MAX, tb, blob_data(msg), blob_len(msg)); + + if (!tb[RETURN_CODE]) { + fprintf(stderr, "No return code received from server\n"); + return; + } + rc = blobmsg_get_u32(tb[RETURN_CODE]); + if (rc) + fprintf(stderr, "Corruption of data with count up to '%u'\n", count_to); + else + fprintf(stderr, "Server validated our count up to '%u'\n", count_to); +} + +static void test_count(struct uloop_timeout *timeout) +{ + enum { + COUNT_TO_MIN = 10000, + COUNT_TO_MAX = 1000000, + PROGRESSION = 100, + }; + + uint32_t id; + static uint32_t count_to = 100000; + static int count_progression = PROGRESSION; + char *s; + + if (count_to <= COUNT_TO_MIN) + count_progression = PROGRESSION; + else if (count_to >= COUNT_TO_MAX) + count_progression = -PROGRESSION; + + count_to += count_progression; + + s = count_to_number(count_to); + if (!s) + fprintf(stderr, "Could not allocate memory to count up to '%u'\n", count_to); + + fprintf(stderr, "Sending count up to '%u'; string has length '%u'\n", + count_to, (uint32_t)strlen(s)); + blob_buf_init(&b, 0); + blobmsg_add_u32(&b, "to", count_to); + blobmsg_add_string(&b, "string", s); + + if (ubus_lookup_id(ctx, "test", &id)) { + fprintf(stderr, "Failed to look up test object\n"); + return; + } + + ubus_invoke(ctx, id, "count", b.head, test_count_data_cb, &count_to, 5000); + + free(s); + + uloop_timeout_set(timeout, 2000); +} + +static struct uloop_timeout notify_timer = { + .cb = test_client_notify_cb, +}; + +static struct uloop_timeout count_timer = { + .cb = test_count, +}; + +static void test_client_fd_data_cb(struct ustream *s, int bytes) +{ + char *data, *sep; + int len; + + data = ustream_get_read_buf(s, &len); + if (len < 1) + return; + + sep = strchr(data, '\n'); + if (!sep) + return; + + *sep = 0; + fprintf(stderr, "Got line: %s\n", data); + ustream_consume(s, sep + 1 - data); +} + +static void test_client_fd_cb(struct ubus_request *req, int fd) +{ + static struct ustream_fd test_fd; + + fprintf(stderr, "Got fd from the server, watching...\n"); + + test_fd.stream.notify_read = test_client_fd_data_cb; + ustream_fd_init(&test_fd, fd); +} + +static void test_client_complete_cb(struct ubus_request *req, int ret) +{ + fprintf(stderr, "completed request, ret: %d\n", ret); +} + +static void client_main(void) +{ + static struct ubus_request req; + uint32_t id; + int ret; + + ret = ubus_add_object(ctx, &test_client_object); + if (ret) { + fprintf(stderr, "Failed to add_object object: %s\n", ubus_strerror(ret)); + return; + } + + if (ubus_lookup_id(ctx, "test", &id)) { + fprintf(stderr, "Failed to look up test object\n"); + return; + } + + blob_buf_init(&b, 0); + blobmsg_add_u32(&b, "id", test_client_object.id); + ubus_invoke(ctx, id, "watch", b.head, NULL, 0, 3000); + test_client_notify_cb(¬ify_timer); + + blob_buf_init(&b, 0); + blobmsg_add_string(&b, "msg", "blah"); + ubus_invoke_async(ctx, id, "hello", b.head, &req); + req.fd_cb = test_client_fd_cb; + req.complete_cb = test_client_complete_cb; + ubus_complete_request_async(ctx, &req); + + uloop_timeout_set(&count_timer, 2000); + + uloop_run(); +} + +int main(int argc, char **argv) +{ + const char *ubus_socket = NULL; + int ch; + + while ((ch = getopt(argc, argv, "cs:")) != -1) { + switch (ch) { + case 's': + ubus_socket = optarg; + break; + default: + break; + } + } + + argc -= optind; + argv += optind; + + uloop_init(); + + ctx = ubus_connect(ubus_socket); + if (!ctx) { + fprintf(stderr, "Failed to connect to ubus\n"); + return -1; + } + + ubus_add_uloop(ctx); + + client_main(); + + ubus_free(ctx); + uloop_done(); + + return 0; +} diff --git a/3P/ubus/examples/count.c b/3P/ubus/examples/count.c new file mode 100644 index 00000000..e3e9c8ac --- /dev/null +++ b/3P/ubus/examples/count.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include "count.h" + +char *count_to_number(uint32_t num) +{ + uint32_t ptr = 0, size = 0; + uint32_t written = 0, i; + int new_line_every_n_numbers = 30; + char *s; + + for (i=0; i < num; ++i) { + size += snprintf(NULL, 0, "%u ", i); + if (i > 0 && i % new_line_every_n_numbers == 0) + size++; + } + size++; /* one for null char */ + + s = calloc(size, sizeof(char)); + if (!s) + goto out; + + for (i=0; i < num; ++i) { + written = sprintf(&s[ptr], "%u ", i); + ptr += written; + if (i > 0 && i % new_line_every_n_numbers == 0) { + sprintf(&s[ptr], "\n"); + ptr++; + } + } + +out: + return s; +} diff --git a/3P/ubus/examples/count.h b/3P/ubus/examples/count.h new file mode 100644 index 00000000..1f90f21d --- /dev/null +++ b/3P/ubus/examples/count.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __COUNT_H +#define __COUNT_H + +char *count_to_number(uint32_t num); + +#endif diff --git a/3P/ubus/examples/server.c b/3P/ubus/examples/server.c new file mode 100644 index 00000000..87da7705 --- /dev/null +++ b/3P/ubus/examples/server.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include +#include "libubus.h" +#include "count.h" + +static struct ubus_context *ctx; +static struct ubus_subscriber test_event; +static struct blob_buf b; + +enum { + HELLO_ID, + HELLO_MSG, + __HELLO_MAX +}; + +static const struct blobmsg_policy hello_policy[] = { + [HELLO_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, + [HELLO_MSG] = { .name = "msg", .type = BLOBMSG_TYPE_STRING }, +}; + +struct hello_request { + struct ubus_request_data req; + struct uloop_timeout timeout; + int fd; + int idx; + char data[]; +}; + +static void test_hello_fd_reply(struct uloop_timeout *t) +{ + struct hello_request *req = container_of(t, struct hello_request, timeout); + char *data; + + data = alloca(strlen(req->data) + 32); + sprintf(data, "msg%d: %s\n", ++req->idx, req->data); + if (write(req->fd, data, strlen(data)) < 0) { + close(req->fd); + free(req); + return; + } + + uloop_timeout_set(&req->timeout, 1000); +} + +static void test_hello_reply(struct uloop_timeout *t) +{ + struct hello_request *req = container_of(t, struct hello_request, timeout); + int fds[2]; + + blob_buf_init(&b, 0); + blobmsg_add_string(&b, "message", req->data); + ubus_send_reply(ctx, &req->req, b.head); + + if (pipe(fds) == -1) { + fprintf(stderr, "Failed to create pipe\n"); + return; + } + ubus_request_set_fd(ctx, &req->req, fds[0]); + ubus_complete_deferred_request(ctx, &req->req, 0); + req->fd = fds[1]; + + req->timeout.cb = test_hello_fd_reply; + test_hello_fd_reply(t); +} + +static int test_hello(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct hello_request *hreq; + struct blob_attr *tb[__HELLO_MAX]; + const char *format = "%s received a message: %s"; + const char *msgstr = "(unknown)"; + + blobmsg_parse(hello_policy, ARRAY_SIZE(hello_policy), tb, blob_data(msg), blob_len(msg)); + + if (tb[HELLO_MSG]) + msgstr = blobmsg_data(tb[HELLO_MSG]); + + hreq = calloc(1, sizeof(*hreq) + strlen(format) + strlen(obj->name) + strlen(msgstr) + 1); + sprintf(hreq->data, format, obj->name, msgstr); + ubus_defer_request(ctx, req, &hreq->req); + hreq->timeout.cb = test_hello_reply; + uloop_timeout_set(&hreq->timeout, 1000); + + return 0; +} + +enum { + WATCH_ID, + WATCH_COUNTER, + __WATCH_MAX +}; + +static const struct blobmsg_policy watch_policy[__WATCH_MAX] = { + [WATCH_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, + [WATCH_COUNTER] = { .name = "counter", .type = BLOBMSG_TYPE_INT32 }, +}; + +static void +test_handle_remove(struct ubus_context *ctx, struct ubus_subscriber *s, + uint32_t id) +{ + fprintf(stderr, "Object %08x went away\n", id); +} + +static int +test_notify(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ +#if 0 + char *str; + + str = blobmsg_format_json(msg, true); + fprintf(stderr, "Received notification '%s': %s\n", method, str); + free(str); +#endif + + return 0; +} + +static int test_watch(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__WATCH_MAX]; + int ret; + + blobmsg_parse(watch_policy, __WATCH_MAX, tb, blob_data(msg), blob_len(msg)); + if (!tb[WATCH_ID]) + return UBUS_STATUS_INVALID_ARGUMENT; + + test_event.remove_cb = test_handle_remove; + test_event.cb = test_notify; + ret = ubus_subscribe(ctx, &test_event, blobmsg_get_u32(tb[WATCH_ID])); + fprintf(stderr, "Watching object %08x: %s\n", blobmsg_get_u32(tb[WATCH_ID]), ubus_strerror(ret)); + return ret; +} + +enum { + COUNT_TO, + COUNT_STRING, + __COUNT_MAX +}; + +static const struct blobmsg_policy count_policy[__COUNT_MAX] = { + [COUNT_TO] = { .name = "to", .type = BLOBMSG_TYPE_INT32 }, + [COUNT_STRING] = { .name = "string", .type = BLOBMSG_TYPE_STRING }, +}; + +static int test_count(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct blob_attr *tb[__COUNT_MAX]; + char *s1, *s2; + uint32_t num; + + blobmsg_parse(count_policy, __COUNT_MAX, tb, blob_data(msg), blob_len(msg)); + if (!tb[COUNT_TO] || !tb[COUNT_STRING]) + return UBUS_STATUS_INVALID_ARGUMENT; + + num = blobmsg_get_u32(tb[COUNT_TO]); + s1 = blobmsg_get_string(tb[COUNT_STRING]); + s2 = count_to_number(num); + if (!s1 || !s2) { + free(s2); + return UBUS_STATUS_UNKNOWN_ERROR; + } + blob_buf_init(&b, 0); + blobmsg_add_u32(&b, "rc", strcmp(s1, s2)); + ubus_send_reply(ctx, req, b.head); + free(s2); + + return 0; +} + +static const struct ubus_method test_methods[] = { + UBUS_METHOD("hello", test_hello, hello_policy), + UBUS_METHOD("watch", test_watch, watch_policy), + UBUS_METHOD("count", test_count, count_policy), +}; + +static struct ubus_object_type test_object_type = + UBUS_OBJECT_TYPE("test", test_methods); + +static struct ubus_object test_object = { + .name = "test", + .type = &test_object_type, + .methods = test_methods, + .n_methods = ARRAY_SIZE(test_methods), +}; + +static void server_main(void) +{ + int ret; + + ret = ubus_add_object(ctx, &test_object); + if (ret) + fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret)); + + ret = ubus_register_subscriber(ctx, &test_event); + if (ret) + fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret)); + + uloop_run(); +} + +int main(int argc, char **argv) +{ + const char *ubus_socket = NULL; + int ch; + + while ((ch = getopt(argc, argv, "cs:")) != -1) { + switch (ch) { + case 's': + ubus_socket = optarg; + break; + default: + break; + } + } + + argc -= optind; + argv += optind; + + uloop_init(); + signal(SIGPIPE, SIG_IGN); + + ctx = ubus_connect(ubus_socket); + if (!ctx) { + fprintf(stderr, "Failed to connect to ubus\n"); + return -1; + } + + ubus_add_uloop(ctx); + + server_main(); + + ubus_free(ctx); + uloop_done(); + + return 0; +} diff --git a/3P/ubus/libubus-internal.h b/3P/ubus/libubus-internal.h new file mode 100644 index 00000000..6af6ad8a --- /dev/null +++ b/3P/ubus/libubus-internal.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LIBUBUS_IO_H +#define __LIBUBUS_IO_H + +extern __thread struct blob_buf b; +extern const struct ubus_method watch_method; + +struct blob_attr **ubus_parse_msg(struct blob_attr *msg); +void ubus_handle_data(struct uloop_fd *u, unsigned int events); +int ubus_send_msg(struct ubus_context *ctx, uint32_t seq, + struct blob_attr *msg, int cmd, uint32_t peer, int fd); +void ubus_process_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf, int fd); +int __hidden ubus_start_request(struct ubus_context *ctx, struct ubus_request *req, + struct blob_attr *msg, int cmd, uint32_t peer); +void ubus_process_obj_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf); +void ubus_process_req_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf, int fd); +void __hidden ubus_poll_data(struct ubus_context *ctx, int timeout); + + +#endif diff --git a/3P/ubus/libubus-io.c b/3P/ubus/libubus-io.c new file mode 100644 index 00000000..6500da2f --- /dev/null +++ b/3P/ubus/libubus-io.c @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +// AWOX #define _GNU_SOURCE +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "libubus.h" +#include "libubus-internal.h" + +#define STATIC_IOV(_var) { .iov_base = (char *) &(_var), .iov_len = sizeof(_var) } + +#define UBUS_MSGBUF_REDUCTION_INTERVAL 16 + +static const struct blob_attr_info ubus_policy[UBUS_ATTR_MAX] = { + [UBUS_ATTR_STATUS] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_OBJID] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_OBJPATH] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_METHOD] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_ACTIVE] = { .type = BLOB_ATTR_INT8 }, + [UBUS_ATTR_NO_REPLY] = { .type = BLOB_ATTR_INT8 }, + [UBUS_ATTR_SUBSCRIBERS] = { .type = BLOB_ATTR_NESTED }, +}; + +static __thread struct blob_attr *attrbuf[UBUS_ATTR_MAX]; + +__hidden struct blob_attr **ubus_parse_msg(struct blob_attr *msg) +{ + blob_parse(msg, attrbuf, ubus_policy, UBUS_ATTR_MAX); + return attrbuf; +} + +static void wait_data(int fd, bool write) +{ + struct pollfd pfd = { .fd = fd }; + + pfd.events = write ? POLLOUT : POLLIN; + poll(&pfd, 1, 0); +} + +static int writev_retry(int fd, struct iovec *iov, int iov_len, int sock_fd) +{ + static __thread struct { + struct cmsghdr h; + int fd; + } fd_buf = { + .h = { + .cmsg_len = sizeof(fd_buf), + .cmsg_level = SOL_SOCKET, + .cmsg_type = SCM_RIGHTS, + } + }; + struct msghdr msghdr = { + .msg_iov = iov, + .msg_iovlen = iov_len, + .msg_control = &fd_buf, + .msg_controllen = sizeof(fd_buf), + }; + int len = 0; + + do { + int cur_len; + + if (sock_fd < 0) { + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + } else { + fd_buf.fd = sock_fd; + } + + cur_len = sendmsg(fd, &msghdr, 0); + if (cur_len < 0) { + switch(errno) { + case EAGAIN: + wait_data(fd, true); + break; + case EINTR: + break; + default: + return -1; + } + continue; + } + + if (len > 0) + sock_fd = -1; + + len += cur_len; + while (cur_len >= iov->iov_len) { + cur_len -= iov->iov_len; + iov_len--; + iov++; + if (!iov_len) + return len; + } + iov->iov_base += cur_len; + iov->iov_len -= cur_len; + msghdr.msg_iov = iov; + msghdr.msg_iovlen = iov_len; + } while (1); + + /* Should never reach here */ + return -1; +} + +int __hidden ubus_send_msg(struct ubus_context *ctx, uint32_t seq, + struct blob_attr *msg, int cmd, uint32_t peer, int fd) +{ + struct ubus_msghdr hdr; + struct iovec iov[2] = { + STATIC_IOV(hdr) + }; + int ret; + + hdr.version = 0; + hdr.type = cmd; + hdr.seq = seq; + hdr.peer = peer; + + if (!msg) { + blob_buf_init(&b, 0); + msg = b.head; + } + + iov[1].iov_base = (char *) msg; + iov[1].iov_len = blob_raw_len(msg); + + ret = writev_retry(ctx->sock.fd, iov, ARRAY_SIZE(iov), fd); + if (ret < 0) + ctx->sock.eof = true; + + if (fd >= 0) + close(fd); + + return ret; +} + +static int recv_retry(int fd, struct iovec *iov, bool wait, int *recv_fd) +{ + int bytes, total = 0; + static __thread struct { + struct cmsghdr h; + int fd; + } fd_buf = { + .h = { + .cmsg_type = SCM_RIGHTS, + .cmsg_level = SOL_SOCKET, + .cmsg_len = sizeof(fd_buf), + }, + }; + struct msghdr msghdr = { + .msg_iov = iov, + .msg_iovlen = 1, + }; + + while (iov->iov_len > 0) { + if (wait) + wait_data(fd, false); + + if (recv_fd) { + msghdr.msg_control = &fd_buf; + msghdr.msg_controllen = sizeof(fd_buf); + } else { + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + } + + fd_buf.fd = -1; + bytes = recvmsg(fd, &msghdr, 0); + if (!bytes) + return -1; + + if (bytes < 0) { + bytes = 0; + if (uloop_cancelled) + return 0; + if (errno == EINTR) + continue; + + if (errno != EAGAIN) + return -1; + } + if (!wait && !bytes) + return 0; + + if (recv_fd) + *recv_fd = fd_buf.fd; + + recv_fd = NULL; + + wait = true; + iov->iov_len -= bytes; + iov->iov_base += bytes; + total += bytes; + } + + return total; +} + +static bool ubus_validate_hdr(struct ubus_msghdr *hdr) +{ + struct blob_attr *data = (struct blob_attr *) (hdr + 1); + + if (hdr->version != 0) + return false; + + if (blob_raw_len(data) < sizeof(*data)) + return false; + + if (blob_pad_len(data) > UBUS_MAX_MSGLEN) + return false; + + return true; +} + +static bool alloc_msg_buf(struct ubus_context *ctx, int len) +{ + void *ptr; + int buf_len = ctx->msgbuf_data_len; + int rem; + + if (!ctx->msgbuf.data) + buf_len = 0; + + rem = (len % UBUS_MSG_CHUNK_SIZE); + if (rem > 0) + len += UBUS_MSG_CHUNK_SIZE - rem; + + if (len < buf_len && + ++ctx->msgbuf_reduction_counter > UBUS_MSGBUF_REDUCTION_INTERVAL) { + ctx->msgbuf_reduction_counter = 0; + buf_len = 0; + } + + if (len <= buf_len) + return true; + + ptr = realloc(ctx->msgbuf.data, len); + if (!ptr) + return false; + + ctx->msgbuf.data = ptr; + return true; +} + +static bool get_next_msg(struct ubus_context *ctx, int *recv_fd) +{ + struct { + struct ubus_msghdr hdr; + struct blob_attr data; + } hdrbuf; + struct iovec iov = STATIC_IOV(hdrbuf); + int len; + int r; + + /* receive header + start attribute */ + r = recv_retry(ctx->sock.fd, &iov, false, recv_fd); + if (r <= 0) { + if (r < 0) + ctx->sock.eof = true; + + return false; + } + + if (!ubus_validate_hdr(&hdrbuf.hdr)) + return false; + + len = blob_raw_len(&hdrbuf.data); + if (!alloc_msg_buf(ctx, len)) + return false; + + memcpy(&ctx->msgbuf.hdr, &hdrbuf.hdr, sizeof(hdrbuf.hdr)); + memcpy(ctx->msgbuf.data, &hdrbuf.data, sizeof(hdrbuf.data)); + + iov.iov_base = (char *)ctx->msgbuf.data + sizeof(hdrbuf.data); + iov.iov_len = blob_len(ctx->msgbuf.data); + if (iov.iov_len > 0 && !recv_retry(ctx->sock.fd, &iov, true, NULL)) + return false; + + return true; +} + +void __hidden ubus_handle_data(struct uloop_fd *u, unsigned int events) +{ + struct ubus_context *ctx = container_of(u, struct ubus_context, sock); + int recv_fd = -1; + + while (get_next_msg(ctx, &recv_fd)) { + ubus_process_msg(ctx, &ctx->msgbuf, recv_fd); + if (uloop_cancelled) + break; + } + + if (u->eof) + ctx->connection_lost(ctx); +} + +void __hidden ubus_poll_data(struct ubus_context *ctx, int timeout) +{ + struct pollfd pfd = { + .fd = ctx->sock.fd, + .events = POLLIN | POLLERR, + }; + + poll(&pfd, 1, timeout); + ubus_handle_data(&ctx->sock, ULOOP_READ); +} + +static void +ubus_refresh_state(struct ubus_context *ctx) +{ + struct ubus_object *obj, *tmp; + struct ubus_object **objs; + int n, i = 0; + + /* clear all type IDs, they need to be registered again */ + avl_for_each_element(&ctx->objects, obj, avl) + if (obj->type) + obj->type->id = 0; + + /* push out all objects again */ + objs = alloca(ctx->objects.count * sizeof(*objs)); + avl_remove_all_elements(&ctx->objects, obj, avl, tmp) { + objs[i++] = obj; + obj->id = 0; + } + + for (n = i, i = 0; i < n; i++) + ubus_add_object(ctx, objs[i]); +} + +int ubus_reconnect(struct ubus_context *ctx, const char *path) +{ + struct { + struct ubus_msghdr hdr; + struct blob_attr data; + } hdr; + struct blob_attr *buf; + int ret = UBUS_STATUS_UNKNOWN_ERROR; + + if (!path) + path = UBUS_UNIX_SOCKET; + + if (ctx->sock.fd >= 0) { + if (ctx->sock.registered) + uloop_fd_delete(&ctx->sock); + + close(ctx->sock.fd); + } + + ctx->sock.fd = usock(USOCK_UNIX, path, NULL); + if (ctx->sock.fd < 0) + return UBUS_STATUS_CONNECTION_FAILED; + + if (read(ctx->sock.fd, &hdr, sizeof(hdr)) != sizeof(hdr)) + goto out_close; + + if (!ubus_validate_hdr(&hdr.hdr)) + goto out_close; + + if (hdr.hdr.type != UBUS_MSG_HELLO) + goto out_close; + + buf = calloc(1, blob_raw_len(&hdr.data)); + if (!buf) + goto out_close; + + memcpy(buf, &hdr.data, sizeof(hdr.data)); + if (read(ctx->sock.fd, blob_data(buf), blob_len(buf)) != blob_len(buf)) + goto out_free; + + ctx->local_id = hdr.hdr.peer; + if (!ctx->local_id) + goto out_free; + + ret = UBUS_STATUS_OK; + //fcntl(ctx->sock.fd, F_SETFL, fcntl(ctx->sock.fd, F_GETFL) | O_NONBLOCK | O_CLOEXEC); + // AWOX M2: Should add F_SETFD: + fcntl(ctx->sock.fd, F_SETFL, fcntl(ctx->sock.fd, F_GETFL) | O_NONBLOCK); + + ubus_refresh_state(ctx); + +out_free: + free(buf); +out_close: + if (ret) + close(ctx->sock.fd); + + return ret; +} diff --git a/3P/ubus/libubus-obj.c b/3P/ubus/libubus-obj.c new file mode 100644 index 00000000..8bc83dc6 --- /dev/null +++ b/3P/ubus/libubus-obj.c @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2011-2012 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "libubus.h" +#include "libubus-internal.h" + +static void +ubus_process_unsubscribe(struct ubus_context *ctx, struct ubus_msghdr *hdr, + struct ubus_object *obj, struct blob_attr **attrbuf) +{ + struct ubus_subscriber *s; + + if (!obj || !attrbuf[UBUS_ATTR_TARGET]) + return; + + if (obj->methods != &watch_method) + return; + + s = container_of(obj, struct ubus_subscriber, obj); + if (s->remove_cb) + s->remove_cb(ctx, s, blob_get_u32(attrbuf[UBUS_ATTR_TARGET])); +} + +static void +ubus_process_notify(struct ubus_context *ctx, struct ubus_msghdr *hdr, + struct ubus_object *obj, struct blob_attr **attrbuf) +{ + if (!obj || !attrbuf[UBUS_ATTR_ACTIVE]) + return; + + obj->has_subscribers = blob_get_u8(attrbuf[UBUS_ATTR_ACTIVE]); + if (obj->subscribe_cb) + obj->subscribe_cb(ctx, obj); +} +static void +ubus_process_invoke(struct ubus_context *ctx, struct ubus_msghdr *hdr, + struct ubus_object *obj, struct blob_attr **attrbuf) +{ + struct ubus_request_data req = { + .fd = -1, + }; + int method; + int ret; + bool no_reply = false; + + if (!obj) { + ret = UBUS_STATUS_NOT_FOUND; + goto send; + } + + if (!attrbuf[UBUS_ATTR_METHOD]) { + ret = UBUS_STATUS_INVALID_ARGUMENT; + goto send; + } + + if (attrbuf[UBUS_ATTR_NO_REPLY]) + no_reply = blob_get_int8(attrbuf[UBUS_ATTR_NO_REPLY]); + + req.peer = hdr->peer; + req.seq = hdr->seq; + req.object = obj->id; + + for (method = 0; method < obj->n_methods; method++) + if (!obj->methods[method].name || + !strcmp(obj->methods[method].name, + blob_data(attrbuf[UBUS_ATTR_METHOD]))) + goto found; + + /* not found */ + ret = UBUS_STATUS_METHOD_NOT_FOUND; + goto send; + +found: + ret = obj->methods[method].handler(ctx, obj, &req, + blob_data(attrbuf[UBUS_ATTR_METHOD]), + attrbuf[UBUS_ATTR_DATA]); + if (req.deferred || no_reply) + return; + +send: + ubus_complete_deferred_request(ctx, &req, ret); +} + +void __hidden ubus_process_obj_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf) +{ + void (*cb)(struct ubus_context *, struct ubus_msghdr *, + struct ubus_object *, struct blob_attr **); + struct ubus_msghdr *hdr = &buf->hdr; + struct blob_attr **attrbuf; + struct ubus_object *obj; + uint32_t objid; + void *prev_data = NULL; + + attrbuf = ubus_parse_msg(buf->data); + if (!attrbuf[UBUS_ATTR_OBJID]) + return; + + objid = blob_get_u32(attrbuf[UBUS_ATTR_OBJID]); + obj = avl_find_element(&ctx->objects, &objid, obj, avl); + + switch (hdr->type) { + case UBUS_MSG_INVOKE: + cb = ubus_process_invoke; + break; + case UBUS_MSG_UNSUBSCRIBE: + cb = ubus_process_unsubscribe; + break; + case UBUS_MSG_NOTIFY: + cb = ubus_process_notify; + break; + default: + return; + } + + if (buf == &ctx->msgbuf) { + prev_data = buf->data; + buf->data = NULL; + } + + cb(ctx, hdr, obj, attrbuf); + + if (prev_data) { + if (buf->data) + free(prev_data); + else + buf->data = prev_data; + } +} + +static void ubus_add_object_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + struct ubus_object *obj = req->priv; + struct blob_attr **attrbuf = ubus_parse_msg(msg); + + if (!attrbuf[UBUS_ATTR_OBJID]) + return; + + obj->id = blob_get_u32(attrbuf[UBUS_ATTR_OBJID]); + + if (attrbuf[UBUS_ATTR_OBJTYPE]) + obj->type->id = blob_get_u32(attrbuf[UBUS_ATTR_OBJTYPE]); + + obj->avl.key = &obj->id; + avl_insert(&req->ctx->objects, &obj->avl); +} + +static void ubus_push_method_data(const struct ubus_method *m) +{ + void *mtbl; + int i; + + mtbl = blobmsg_open_table(&b, m->name); + + for (i = 0; i < m->n_policy; i++) { + if (m->mask && !(m->mask & (1 << i))) + continue; + + blobmsg_add_u32(&b, m->policy[i].name, m->policy[i].type); + } + + blobmsg_close_table(&b, mtbl); +} + +static bool ubus_push_object_type(const struct ubus_object_type *type) +{ + void *s; + int i; + + s = blob_nest_start(&b, UBUS_ATTR_SIGNATURE); + + for (i = 0; i < type->n_methods; i++) + ubus_push_method_data(&type->methods[i]); + + blob_nest_end(&b, s); + + return true; +} + +int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj) +{ + struct ubus_request req; + int ret; + + blob_buf_init(&b, 0); + + if (obj->name && obj->type) { + blob_put_string(&b, UBUS_ATTR_OBJPATH, obj->name); + + if (obj->type->id) + blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id); + else if (!ubus_push_object_type(obj->type)) + return UBUS_STATUS_INVALID_ARGUMENT; + } + + if (ubus_start_request(ctx, &req, b.head, UBUS_MSG_ADD_OBJECT, 0) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + req.raw_data_cb = ubus_add_object_cb; + req.priv = obj; + ret = ubus_complete_request(ctx, &req, 0); + if (ret) + return ret; + + if (!obj->id) + return UBUS_STATUS_NO_DATA; + + return 0; +} + +static void ubus_remove_object_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + struct ubus_object *obj = req->priv; + struct blob_attr **attrbuf = ubus_parse_msg(msg); + + if (!attrbuf[UBUS_ATTR_OBJID]) + return; + + obj->id = 0; + + if (attrbuf[UBUS_ATTR_OBJTYPE] && obj->type) + obj->type->id = 0; + + avl_delete(&req->ctx->objects, &obj->avl); +} + +int ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj) +{ + struct ubus_request req; + int ret; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id); + + if (ubus_start_request(ctx, &req, b.head, UBUS_MSG_REMOVE_OBJECT, 0) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + req.raw_data_cb = ubus_remove_object_cb; + req.priv = obj; + ret = ubus_complete_request(ctx, &req, 0); + if (ret) + return ret; + + if (obj->id) + return UBUS_STATUS_NO_DATA; + + return 0; +} diff --git a/3P/ubus/libubus-req.c b/3P/ubus/libubus-req.c new file mode 100644 index 00000000..d44db51f --- /dev/null +++ b/3P/ubus/libubus-req.c @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "libubus.h" +#include "libubus-internal.h" + +struct ubus_pending_data { + struct list_head list; + int type; + struct blob_attr data[]; +}; + +static void req_data_cb(struct ubus_request *req, int type, struct blob_attr *data) +{ + struct blob_attr **attr; + + if (req->raw_data_cb) + req->raw_data_cb(req, type, data); + + if (!req->data_cb) + return; + + attr = ubus_parse_msg(data); + req->data_cb(req, type, attr[UBUS_ATTR_DATA]); +} + +static void __ubus_process_req_data(struct ubus_request *req) +{ + struct ubus_pending_data *data; + + while (!list_empty(&req->pending)) { + data = list_first_entry(&req->pending, + struct ubus_pending_data, list); + list_del(&data->list); + if (!req->cancelled) + req_data_cb(req, data->type, data->data); + free(data); + } +} + +int __hidden ubus_start_request(struct ubus_context *ctx, struct ubus_request *req, + struct blob_attr *msg, int cmd, uint32_t peer) +{ + memset(req, 0, sizeof(*req)); + + if (msg && blob_pad_len(msg) > UBUS_MAX_MSGLEN) + return -1; + + INIT_LIST_HEAD(&req->list); + INIT_LIST_HEAD(&req->pending); + req->ctx = ctx; + req->peer = peer; + req->seq = ++ctx->request_seq; + return ubus_send_msg(ctx, req->seq, msg, cmd, peer, -1); +} + +void ubus_abort_request(struct ubus_context *ctx, struct ubus_request *req) +{ + if (list_empty(&req->list)) + return; + + req->cancelled = true; + __ubus_process_req_data(req); + list_del_init(&req->list); +} + +void ubus_complete_request_async(struct ubus_context *ctx, struct ubus_request *req) +{ + if (!list_empty(&req->list)) + return; + + list_add(&req->list, &ctx->requests); +} + +static void +ubus_req_complete_cb(struct ubus_request *req) +{ + ubus_complete_handler_t cb = req->complete_cb; + + if (!cb) + return; + + req->complete_cb = NULL; + cb(req, req->status_code); +} + +static void +ubus_set_req_status(struct ubus_request *req, int ret) +{ + if (!list_empty(&req->list)) + list_del_init(&req->list); + + req->status_msg = true; + req->status_code = ret; + if (!req->blocked) + ubus_req_complete_cb(req); +} + +static void ubus_sync_req_cb(struct ubus_request *req, int ret) +{ + req->status_msg = true; + req->status_code = ret; + uloop_end(); +} + +static int64_t get_time_msec(void) +{ + struct timespec ts; + int64_t val; + + clock_gettime(CLOCK_MONOTONIC, &ts); + val = (int64_t) ts.tv_sec * 1000LL; + val += ts.tv_nsec / 1000000LL; + return val; +} + +int ubus_complete_request(struct ubus_context *ctx, struct ubus_request *req, + int req_timeout) +{ + ubus_complete_handler_t complete_cb = req->complete_cb; + bool registered = ctx->sock.registered; + int status = UBUS_STATUS_NO_DATA; + int64_t timeout = 0, time_end = 0; + + if (!registered) { + uloop_init(); + ubus_add_uloop(ctx); + } + + if (req_timeout) + time_end = get_time_msec() + req_timeout; + + ubus_complete_request_async(ctx, req); + req->complete_cb = ubus_sync_req_cb; + + ctx->stack_depth++; + while (!req->status_msg) { + bool cancelled = uloop_cancelled; + + uloop_cancelled = false; + if (req_timeout) { + timeout = time_end - get_time_msec(); + if (timeout <= 0) { + ubus_set_req_status(req, UBUS_STATUS_TIMEOUT); + uloop_cancelled = cancelled; + break; + } + } + ubus_poll_data(ctx, (unsigned int) timeout); + + uloop_cancelled = cancelled; + } + ctx->stack_depth--; + if (ctx->stack_depth) + uloop_cancelled = true; + + if (req->status_msg) + status = req->status_code; + + req->complete_cb = complete_cb; + if (req->complete_cb) + req->complete_cb(req, status); + + if (!registered) { + uloop_fd_delete(&ctx->sock); + + if (ctx->stack_depth) + ctx->pending_timer.cb(&ctx->pending_timer); + } + + return status; +} + +void ubus_complete_deferred_request(struct ubus_context *ctx, struct ubus_request_data *req, int ret) +{ + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_STATUS, ret); + blob_put_int32(&b, UBUS_ATTR_OBJID, req->object); + ubus_send_msg(ctx, req->seq, b.head, UBUS_MSG_STATUS, req->peer, req->fd); +} + +int ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req, + struct blob_attr *msg) +{ + int ret; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, req->object); + blob_put(&b, UBUS_ATTR_DATA, blob_data(msg), blob_len(msg)); + ret = ubus_send_msg(ctx, req->seq, b.head, UBUS_MSG_DATA, req->peer, -1); + if (ret < 0) + return UBUS_STATUS_NO_DATA; + + return 0; +} + +int ubus_invoke_async(struct ubus_context *ctx, uint32_t obj, const char *method, + struct blob_attr *msg, struct ubus_request *req) +{ + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj); + blob_put_string(&b, UBUS_ATTR_METHOD, method); + if (msg) + blob_put(&b, UBUS_ATTR_DATA, blob_data(msg), blob_len(msg)); + + if (ubus_start_request(ctx, req, b.head, UBUS_MSG_INVOKE, obj) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + return 0; +} + +int ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method, + struct blob_attr *msg, ubus_data_handler_t cb, void *priv, + int timeout) +{ + struct ubus_request req; + int rc; + + rc = ubus_invoke_async(ctx, obj, method, msg, &req); + if (rc) + return rc; + + req.data_cb = cb; + req.priv = priv; + return ubus_complete_request(ctx, &req, timeout); +} + +static void +ubus_notify_complete_cb(struct ubus_request *req, int ret) +{ + struct ubus_notify_request *nreq; + + nreq = container_of(req, struct ubus_notify_request, req); + if (!nreq->complete_cb) + return; + + nreq->complete_cb(nreq, 0, 0); +} + +static int +__ubus_notify_async(struct ubus_context *ctx, struct ubus_object *obj, + const char *type, struct blob_attr *msg, + struct ubus_notify_request *req, bool reply) +{ + memset(req, 0, sizeof(*req)); + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id); + blob_put_string(&b, UBUS_ATTR_METHOD, type); + + if (!reply) + blob_put_int8(&b, UBUS_ATTR_NO_REPLY, true); + + if (msg) + blob_put(&b, UBUS_ATTR_DATA, blob_data(msg), blob_len(msg)); + + if (ubus_start_request(ctx, &req->req, b.head, UBUS_MSG_NOTIFY, obj->id) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + /* wait for status message from ubusd first */ + req->req.notify = true; + req->pending = 1; + req->id[0] = obj->id; + req->req.complete_cb = ubus_notify_complete_cb; + + return 0; +} + +int ubus_notify_async(struct ubus_context *ctx, struct ubus_object *obj, + const char *type, struct blob_attr *msg, + struct ubus_notify_request *req) +{ + return __ubus_notify_async(ctx, obj, type, msg, req, true); +} + +int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj, + const char *type, struct blob_attr *msg, int timeout) +{ + struct ubus_notify_request req; + int ret; + + ret = __ubus_notify_async(ctx, obj, type, msg, &req, timeout >= 0); + if (ret < 0) + return ret; + + if (timeout < 0) { + ubus_abort_request(ctx, &req.req); + return 0; + } + + return ubus_complete_request(ctx, &req.req, timeout); +} + +static bool ubus_get_status(struct ubus_msghdr_buf *buf, int *ret) +{ + struct blob_attr **attrbuf = ubus_parse_msg(buf->data); + + if (!attrbuf[UBUS_ATTR_STATUS]) + return false; + + *ret = blob_get_u32(attrbuf[UBUS_ATTR_STATUS]); + return true; +} + +static int +ubus_process_req_status(struct ubus_request *req, struct ubus_msghdr_buf *buf) +{ + int ret = UBUS_STATUS_INVALID_ARGUMENT; + + ubus_get_status(buf, &ret); + req->peer = buf->hdr.peer; + ubus_set_req_status(req, ret); + + return ret; +} + +static void +ubus_process_req_data(struct ubus_request *req, struct ubus_msghdr_buf *buf) +{ + struct ubus_pending_data *data; + int len; + + if (!req->blocked) { + req->blocked = true; + req_data_cb(req, buf->hdr.type, buf->data); + __ubus_process_req_data(req); + req->blocked = false; + + if (req->status_msg) + ubus_req_complete_cb(req); + + return; + } + + len = blob_raw_len(buf->data); + data = calloc(1, sizeof(*data) + len); + if (!data) + return; + + data->type = buf->hdr.type; + memcpy(data->data, buf->data, len); + list_add(&data->list, &req->pending); +} + +static int +ubus_find_notify_id(struct ubus_notify_request *n, uint32_t objid) +{ + uint32_t pending = n->pending; + int i; + + for (i = 0; pending; i++, pending >>= 1) { + if (!(pending & 1)) + continue; + + if (n->id[i] == objid) + return i; + } + + return -1; +} + +static struct ubus_request * +ubus_find_request(struct ubus_context *ctx, uint32_t seq, uint32_t peer, int *id) +{ + struct ubus_request *req; + + list_for_each_entry(req, &ctx->requests, list) { + struct ubus_notify_request *nreq; + nreq = container_of(req, struct ubus_notify_request, req); + + if (seq != req->seq) + continue; + + if (req->notify) { + if (!nreq->pending) + continue; + + *id = ubus_find_notify_id(nreq, peer); + if (*id < 0) + continue; + } else if (peer != req->peer) + continue; + + return req; + } + return NULL; +} + +static void ubus_process_notify_status(struct ubus_request *req, int id, struct ubus_msghdr_buf *buf) +{ + struct ubus_notify_request *nreq; + struct blob_attr **tb; + struct blob_attr *cur; + int rem, idx = 1; + int ret = 0; + + nreq = container_of(req, struct ubus_notify_request, req); + nreq->pending &= ~(1 << id); + + if (!id) { + /* first id: ubusd's status message with a list of ids */ + tb = ubus_parse_msg(buf->data); + if (tb[UBUS_ATTR_SUBSCRIBERS]) { + blob_for_each_attr(cur, tb[UBUS_ATTR_SUBSCRIBERS], rem) { + if (!blob_check_type(blob_data(cur), blob_len(cur), BLOB_ATTR_INT32)) + continue; + + nreq->pending |= (1 << idx); + nreq->id[idx] = blob_get_int32(cur); + idx++; + + if (idx == UBUS_MAX_NOTIFY_PEERS + 1) + break; + } + } + } else { + ubus_get_status(buf, &ret); + if (nreq->status_cb) + nreq->status_cb(nreq, id, ret); + } + + if (!nreq->pending) + ubus_set_req_status(req, 0); +} + +void __hidden ubus_process_req_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf, int fd) +{ + struct ubus_msghdr *hdr = &buf->hdr; + struct ubus_request *req; + int id = -1; + + switch(hdr->type) { + case UBUS_MSG_STATUS: + req = ubus_find_request(ctx, hdr->seq, hdr->peer, &id); + if (!req) + break; + + if (fd >= 0) { + if (req->fd_cb) + req->fd_cb(req, fd); + else + close(fd); + } + + if (id >= 0) + ubus_process_notify_status(req, id, buf); + else + ubus_process_req_status(req, buf); + break; + + case UBUS_MSG_DATA: + req = ubus_find_request(ctx, hdr->seq, hdr->peer, &id); + if (req && (req->data_cb || req->raw_data_cb)) + ubus_process_req_data(req, buf); + break; + } +} diff --git a/3P/ubus/libubus-sub.c b/3P/ubus/libubus-sub.c new file mode 100644 index 00000000..8793133a --- /dev/null +++ b/3P/ubus/libubus-sub.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2011-2012 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "libubus.h" +#include "libubus-internal.h" + +static int ubus_subscriber_cb(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, + const char *method, struct blob_attr *msg) +{ + struct ubus_subscriber *s; + + s = container_of(obj, struct ubus_subscriber, obj); + if (s->cb) + return s->cb(ctx, obj, req, method, msg); + return 0; +} + +const struct ubus_method watch_method __hidden = { + .name = NULL, + .handler = ubus_subscriber_cb, +}; + +int ubus_register_subscriber(struct ubus_context *ctx, struct ubus_subscriber *s) +{ + struct ubus_object *obj = &s->obj; + + obj->methods = &watch_method; + obj->n_methods = 1; + + return ubus_add_object(ctx, obj); +} + +static int +__ubus_subscribe_request(struct ubus_context *ctx, struct ubus_object *obj, uint32_t id, int type) +{ + struct ubus_request req; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id); + blob_put_int32(&b, UBUS_ATTR_TARGET, id); + + if (ubus_start_request(ctx, &req, b.head, type, 0) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + return ubus_complete_request(ctx, &req, 0); + +} + +int ubus_subscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id) +{ + return __ubus_subscribe_request(ctx, &obj->obj, id, UBUS_MSG_SUBSCRIBE); +} + +int ubus_unsubscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id) +{ + return __ubus_subscribe_request(ctx, &obj->obj, id, UBUS_MSG_UNSUBSCRIBE); +} + diff --git a/3P/ubus/libubus.c b/3P/ubus/libubus.c new file mode 100644 index 00000000..de1a6dd9 --- /dev/null +++ b/3P/ubus/libubus.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include + +#include +#include + +#include "libubus.h" +#include "libubus-internal.h" +#include "ubusmsg.h" + +const char *__ubus_strerror[__UBUS_STATUS_LAST] = { + [UBUS_STATUS_OK] = "Success", + [UBUS_STATUS_INVALID_COMMAND] = "Invalid command", + [UBUS_STATUS_INVALID_ARGUMENT] = "Invalid argument", + [UBUS_STATUS_METHOD_NOT_FOUND] = "Method not found", + [UBUS_STATUS_NOT_FOUND] = "Not found", + [UBUS_STATUS_NO_DATA] = "No response", + [UBUS_STATUS_PERMISSION_DENIED] = "Permission denied", + [UBUS_STATUS_TIMEOUT] = "Request timed out", + [UBUS_STATUS_NOT_SUPPORTED] = "Operation not supported", + [UBUS_STATUS_UNKNOWN_ERROR] = "Unknown error", + [UBUS_STATUS_CONNECTION_FAILED] = "Connection failed", +}; + +__thread struct blob_buf b __hidden = {}; + +struct ubus_pending_msg { + struct list_head list; + struct ubus_msghdr_buf hdr; +}; + +static int ubus_cmp_id(const void *k1, const void *k2, void *ptr) +{ + const uint32_t *id1 = k1, *id2 = k2; + + if (*id1 < *id2) + return -1; + else + return *id1 > *id2; +} + +const char *ubus_strerror(int error) +{ + static char err[32]; + + if (error < 0 || error >= __UBUS_STATUS_LAST) + goto out; + + if (!__ubus_strerror[error]) + goto out; + + return __ubus_strerror[error]; + +out: + sprintf(err, "Unknown error: %d", error); + return err; +} + +static void +ubus_queue_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf) +{ + struct ubus_pending_msg *pending; + void *data; + + pending = calloc_a(sizeof(*pending), &data, blob_raw_len(buf->data)); + + pending->hdr.data = data; + memcpy(&pending->hdr.hdr, &buf->hdr, sizeof(buf->hdr)); + memcpy(data, buf->data, blob_raw_len(buf->data)); + list_add(&pending->list, &ctx->pending); + if (ctx->sock.registered) + uloop_timeout_set(&ctx->pending_timer, 1); +} + +void __hidden +ubus_process_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf, int fd) +{ + switch(buf->hdr.type) { + case UBUS_MSG_STATUS: + case UBUS_MSG_DATA: + ubus_process_req_msg(ctx, buf, fd); + break; + + case UBUS_MSG_INVOKE: + case UBUS_MSG_UNSUBSCRIBE: + case UBUS_MSG_NOTIFY: + if (ctx->stack_depth) { + ubus_queue_msg(ctx, buf); + break; + } + + ubus_process_obj_msg(ctx, buf); + break; + } +} + +static void ubus_process_pending_msg(struct uloop_timeout *timeout) +{ + struct ubus_context *ctx = container_of(timeout, struct ubus_context, pending_timer); + struct ubus_pending_msg *pending; + + while (!ctx->stack_depth && !list_empty(&ctx->pending)) { + pending = list_first_entry(&ctx->pending, struct ubus_pending_msg, list); + list_del(&pending->list); + ubus_process_msg(ctx, &pending->hdr, -1); + free(pending); + } +} + +struct ubus_lookup_request { + struct ubus_request req; + ubus_lookup_handler_t cb; +}; + +static void ubus_lookup_cb(struct ubus_request *ureq, int type, struct blob_attr *msg) +{ + struct ubus_lookup_request *req; + struct ubus_object_data obj; + struct blob_attr **attr; + + req = container_of(ureq, struct ubus_lookup_request, req); + attr = ubus_parse_msg(msg); + + if (!attr[UBUS_ATTR_OBJID] || !attr[UBUS_ATTR_OBJPATH] || + !attr[UBUS_ATTR_OBJTYPE]) + return; + + memset(&obj, 0, sizeof(obj)); + obj.id = blob_get_u32(attr[UBUS_ATTR_OBJID]); + obj.path = blob_data(attr[UBUS_ATTR_OBJPATH]); + obj.type_id = blob_get_u32(attr[UBUS_ATTR_OBJTYPE]); + obj.signature = attr[UBUS_ATTR_SIGNATURE]; + req->cb(ureq->ctx, &obj, ureq->priv); +} + +int ubus_lookup(struct ubus_context *ctx, const char *path, + ubus_lookup_handler_t cb, void *priv) +{ + struct ubus_lookup_request lookup; + + blob_buf_init(&b, 0); + if (path) + blob_put_string(&b, UBUS_ATTR_OBJPATH, path); + + if (ubus_start_request(ctx, &lookup.req, b.head, UBUS_MSG_LOOKUP, 0) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + lookup.req.raw_data_cb = ubus_lookup_cb; + lookup.req.priv = priv; + lookup.cb = cb; + return ubus_complete_request(ctx, &lookup.req, 0); +} + +static void ubus_lookup_id_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + struct blob_attr **attr; + uint32_t *id = req->priv; + + attr = ubus_parse_msg(msg); + + if (!attr[UBUS_ATTR_OBJID]) + return; + + *id = blob_get_u32(attr[UBUS_ATTR_OBJID]); +} + +int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id) +{ + struct ubus_request req; + + blob_buf_init(&b, 0); + if (path) + blob_put_string(&b, UBUS_ATTR_OBJPATH, path); + + if (ubus_start_request(ctx, &req, b.head, UBUS_MSG_LOOKUP, 0) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + req.raw_data_cb = ubus_lookup_id_cb; + req.priv = id; + + // Awox Remomve infinite timeout: return ubus_complete_request(ctx, &req, 0); + return ubus_complete_request(ctx, &req, 5000); +} + +static int ubus_event_cb(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, + const char *method, struct blob_attr *msg) +{ + struct ubus_event_handler *ev; + + ev = container_of(obj, struct ubus_event_handler, obj); + ev->cb(ctx, ev, method, msg); + return 0; +} + +static const struct ubus_method event_method = { + .name = NULL, + .handler = ubus_event_cb, +}; + +int ubus_register_event_handler(struct ubus_context *ctx, + struct ubus_event_handler *ev, + const char *pattern) +{ + struct ubus_object *obj = &ev->obj; + struct blob_buf b2; + int ret; + + if (!obj->id) { + obj->methods = &event_method; + obj->n_methods = 1; + + if (!!obj->name ^ !!obj->type) + return UBUS_STATUS_INVALID_ARGUMENT; + + ret = ubus_add_object(ctx, obj); + if (ret) + return ret; + } + + /* use a second buffer, ubus_invoke() overwrites the primary one */ + memset(&b2, 0, sizeof(b2)); + blob_buf_init(&b2, 0); + blobmsg_add_u32(&b2, "object", obj->id); + if (pattern) + blobmsg_add_string(&b2, "pattern", pattern); + + ret = ubus_invoke(ctx, UBUS_SYSTEM_OBJECT_EVENT, "register", b2.head, + NULL, NULL, 0); + blob_buf_free(&b2); + + return ret; +} + +int ubus_send_event(struct ubus_context *ctx, const char *id, + struct blob_attr *data) +{ + struct ubus_request req; + void *s; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, UBUS_SYSTEM_OBJECT_EVENT); + blob_put_string(&b, UBUS_ATTR_METHOD, "send"); + s = blob_nest_start(&b, UBUS_ATTR_DATA); + blobmsg_add_string(&b, "id", id); + blobmsg_add_field(&b, BLOBMSG_TYPE_TABLE, "data", blob_data(data), blob_len(data)); + blob_nest_end(&b, s); + + if (ubus_start_request(ctx, &req, b.head, UBUS_MSG_INVOKE, UBUS_SYSTEM_OBJECT_EVENT) < 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + return ubus_complete_request(ctx, &req, 0); +} + +static void ubus_default_connection_lost(struct ubus_context *ctx) +{ + if (ctx->sock.registered) + uloop_end(); +} + +static int _ubus_connect(struct ubus_context *ctx, const char *path) +{ + ctx->sock.fd = -1; + ctx->sock.cb = ubus_handle_data; + ctx->connection_lost = ubus_default_connection_lost; + ctx->pending_timer.cb = ubus_process_pending_msg; + + ctx->msgbuf.data = calloc(UBUS_MSG_CHUNK_SIZE, sizeof(char)); + if (!ctx->msgbuf.data) + return -1; + ctx->msgbuf_data_len = UBUS_MSG_CHUNK_SIZE; + + INIT_LIST_HEAD(&ctx->requests); + INIT_LIST_HEAD(&ctx->pending); + avl_init(&ctx->objects, ubus_cmp_id, false, NULL); + if (ubus_reconnect(ctx, path)) { + free(ctx->msgbuf.data); + return -1; + } + + return 0; +} + +static void ubus_auto_reconnect_cb(struct uloop_timeout *timeout) +{ + struct ubus_auto_conn *conn = container_of(timeout, struct ubus_auto_conn, timer); + + if (!ubus_reconnect(&conn->ctx, conn->path)) + ubus_add_uloop(&conn->ctx); + else + uloop_timeout_set(timeout, 1000); +} + +static void ubus_auto_disconnect_cb(struct ubus_context *ctx) +{ + struct ubus_auto_conn *conn = container_of(ctx, struct ubus_auto_conn, ctx); + + conn->timer.cb = ubus_auto_reconnect_cb; + uloop_timeout_set(&conn->timer, 1000); +} + +static void ubus_auto_connect_cb(struct uloop_timeout *timeout) +{ + struct ubus_auto_conn *conn = container_of(timeout, struct ubus_auto_conn, timer); + + if (_ubus_connect(&conn->ctx, conn->path)) { + uloop_timeout_set(timeout, 1000); + fprintf(stderr, "failed to connect to ubus\n"); + return; + } + conn->ctx.connection_lost = ubus_auto_disconnect_cb; + if (conn->cb) + conn->cb(&conn->ctx); + ubus_add_uloop(&conn->ctx); +} + +void ubus_auto_connect(struct ubus_auto_conn *conn) +{ + conn->timer.cb = ubus_auto_connect_cb; + ubus_auto_connect_cb(&conn->timer); +} + +struct ubus_context *ubus_connect(const char *path) +{ + struct ubus_context *ctx; + + ctx = calloc(1, sizeof(*ctx)); + if (!ctx) + return NULL; + + if (_ubus_connect(ctx, path)) { + free(ctx); + ctx = NULL; + } + + return ctx; +} + +void ubus_free(struct ubus_context *ctx) +{ + blob_buf_free(&b); + close(ctx->sock.fd); + free(ctx->msgbuf.data); + free(ctx); +} diff --git a/3P/ubus/libubus.h b/3P/ubus/libubus.h new file mode 100644 index 00000000..54f4436b --- /dev/null +++ b/3P/ubus/libubus.h @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LIBUBUS_H +#define __LIBUBUS_H + +#include +#include +#include +#include +#include +#include "ubusmsg.h" +#include "ubus_common.h" + +#define UBUS_MAX_NOTIFY_PEERS 16 + +struct ubus_context; +struct ubus_msg_src; +struct ubus_object; +struct ubus_request; +struct ubus_request_data; +struct ubus_object_data; +struct ubus_event_handler; +struct ubus_subscriber; +struct ubus_notify_request; + +struct ubus_msghdr_buf { + struct ubus_msghdr hdr; + struct blob_attr *data; +}; + +typedef void (*ubus_lookup_handler_t)(struct ubus_context *ctx, + struct ubus_object_data *obj, + void *priv); +typedef int (*ubus_handler_t)(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, + const char *method, struct blob_attr *msg); +typedef void (*ubus_state_handler_t)(struct ubus_context *ctx, struct ubus_object *obj); +typedef void (*ubus_remove_handler_t)(struct ubus_context *ctx, + struct ubus_subscriber *obj, uint32_t id); +typedef void (*ubus_event_handler_t)(struct ubus_context *ctx, struct ubus_event_handler *ev, + const char *type, struct blob_attr *msg); +typedef void (*ubus_data_handler_t)(struct ubus_request *req, + int type, struct blob_attr *msg); +typedef void (*ubus_fd_handler_t)(struct ubus_request *req, int fd); +typedef void (*ubus_complete_handler_t)(struct ubus_request *req, int ret); +typedef void (*ubus_notify_complete_handler_t)(struct ubus_notify_request *req, + int idx, int ret); +typedef void (*ubus_connect_handler_t)(struct ubus_context *ctx); + +#define UBUS_OBJECT_TYPE(_name, _methods) \ + { \ + .name = _name, \ + .id = 0, \ + .n_methods = ARRAY_SIZE(_methods), \ + .methods = _methods \ + } + +#define __UBUS_METHOD_NOARG(_name, _handler) \ + .name = _name, \ + .handler = _handler + +#define __UBUS_METHOD(_name, _handler, _policy) \ + __UBUS_METHOD_NOARG(_name, _handler), \ + .policy = _policy, \ + .n_policy = ARRAY_SIZE(_policy) + +#define UBUS_METHOD(_name, _handler, _policy) \ + { __UBUS_METHOD(_name, _handler, _policy) } + +#define UBUS_METHOD_MASK(_name, _handler, _policy, _mask) \ + { \ + __UBUS_METHOD(_name, _handler, _policy),\ + .mask = _mask \ + } + +#define UBUS_METHOD_NOARG(_name, _handler) \ + { __UBUS_METHOD_NOARG(_name, _handler) } + +struct ubus_method { + const char *name; + ubus_handler_t handler; + + unsigned long mask; + const struct blobmsg_policy *policy; + int n_policy; +}; + +struct ubus_object_type { + const char *name; + uint32_t id; + + const struct ubus_method *methods; + int n_methods; +}; + +struct ubus_object { + struct avl_node avl; + + const char *name; + uint32_t id; + + const char *path; + struct ubus_object_type *type; + + ubus_state_handler_t subscribe_cb; + bool has_subscribers; + + const struct ubus_method *methods; + int n_methods; +}; + +struct ubus_subscriber { + struct ubus_object obj; + + ubus_handler_t cb; + ubus_remove_handler_t remove_cb; +}; + +struct ubus_event_handler { + struct ubus_object obj; + + ubus_event_handler_t cb; + void *priv; +}; + +struct ubus_context { + struct list_head requests; + struct avl_tree objects; + struct list_head pending; + + struct uloop_fd sock; + struct uloop_timeout pending_timer; + + uint32_t local_id; + uint16_t request_seq; + int stack_depth; + + void (*connection_lost)(struct ubus_context *ctx); + + struct ubus_msghdr_buf msgbuf; + uint32_t msgbuf_data_len; + int msgbuf_reduction_counter; +}; + +struct ubus_object_data { + uint32_t id; + uint32_t type_id; + const char *path; + struct blob_attr *signature; +}; + +struct ubus_request_data { + uint32_t object; + uint32_t peer; + uint16_t seq; + + /* internal use */ + bool deferred; + int fd; +}; + +struct ubus_request { + struct list_head list; + + struct list_head pending; + int status_code; + bool status_msg; + bool blocked; + bool cancelled; + bool notify; + + uint32_t peer; + uint16_t seq; + + ubus_data_handler_t raw_data_cb; + ubus_data_handler_t data_cb; + ubus_fd_handler_t fd_cb; + ubus_complete_handler_t complete_cb; + + struct ubus_context *ctx; + void *priv; +}; + +struct ubus_notify_request { + struct ubus_request req; + + ubus_notify_complete_handler_t status_cb; + ubus_notify_complete_handler_t complete_cb; + + uint32_t pending; + uint32_t id[UBUS_MAX_NOTIFY_PEERS + 1]; +}; + +struct ubus_auto_conn { + struct ubus_context ctx; + struct uloop_timeout timer; + const char *path; + ubus_connect_handler_t cb; +}; + +struct ubus_context *ubus_connect(const char *path); +void ubus_auto_connect(struct ubus_auto_conn *conn); +int ubus_reconnect(struct ubus_context *ctx, const char *path); +void ubus_free(struct ubus_context *ctx); + +const char *ubus_strerror(int error); + +static inline void ubus_add_uloop(struct ubus_context *ctx) +{ + uloop_fd_add(&ctx->sock, ULOOP_BLOCKING | ULOOP_READ); +} + +/* call this for read events on ctx->sock.fd when not using uloop */ +static inline void ubus_handle_event(struct ubus_context *ctx) +{ + ctx->sock.cb(&ctx->sock, ULOOP_READ); +} + +/* ----------- raw request handling ----------- */ + +/* wait for a request to complete and return its status */ +int ubus_complete_request(struct ubus_context *ctx, struct ubus_request *req, + int timeout); + +/* complete a request asynchronously */ +void ubus_complete_request_async(struct ubus_context *ctx, + struct ubus_request *req); + +/* abort an asynchronous request */ +void ubus_abort_request(struct ubus_context *ctx, struct ubus_request *req); + +/* ----------- objects ----------- */ + +int ubus_lookup(struct ubus_context *ctx, const char *path, + ubus_lookup_handler_t cb, void *priv); + +int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id); + +/* make an object visible to remote connections */ +int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj); + +/* remove the object from the ubus connection */ +int ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj); + +/* add a subscriber notifications from another object */ +int ubus_register_subscriber(struct ubus_context *ctx, struct ubus_subscriber *obj); + +static inline int +ubus_unregister_subscriber(struct ubus_context *ctx, struct ubus_subscriber *obj) +{ + return ubus_remove_object(ctx, &obj->obj); +} + +int ubus_subscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id); +int ubus_unsubscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id); + +/* ----------- rpc ----------- */ + +/* invoke a method on a specific object */ +int ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method, + struct blob_attr *msg, ubus_data_handler_t cb, void *priv, + int timeout); + +/* asynchronous version of ubus_invoke() */ +int ubus_invoke_async(struct ubus_context *ctx, uint32_t obj, const char *method, + struct blob_attr *msg, struct ubus_request *req); + +/* send a reply to an incoming object method call */ +int ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req, + struct blob_attr *msg); + +static inline void ubus_defer_request(struct ubus_context *ctx, + struct ubus_request_data *req, + struct ubus_request_data *new_req) +{ + memcpy(new_req, req, sizeof(*req)); + req->deferred = true; +} + +static inline void ubus_request_set_fd(struct ubus_context *ctx, + struct ubus_request_data *req, int fd) +{ + req->fd = fd; +} + +void ubus_complete_deferred_request(struct ubus_context *ctx, + struct ubus_request_data *req, int ret); + +/* + * send a notification to all subscribers of an object + * if timeout < 0, no reply is expected from subscribers + */ +int ubus_notify(struct ubus_context *ctx, struct ubus_object *obj, + const char *type, struct blob_attr *msg, int timeout); + +int ubus_notify_async(struct ubus_context *ctx, struct ubus_object *obj, + const char *type, struct blob_attr *msg, + struct ubus_notify_request *req); + + +/* ----------- events ----------- */ + +int ubus_send_event(struct ubus_context *ctx, const char *id, + struct blob_attr *data); + +int ubus_register_event_handler(struct ubus_context *ctx, + struct ubus_event_handler *ev, + const char *pattern); + +static inline int ubus_unregister_event_handler(struct ubus_context *ctx, + struct ubus_event_handler *ev) +{ + return ubus_remove_object(ctx, &ev->obj); +} + +#endif diff --git a/3P/ubus/libubus/builders/cmake/CMakeLists.txt b/3P/ubus/libubus/builders/cmake/CMakeLists.txt new file mode 100644 index 00000000..5b22540b --- /dev/null +++ b/3P/ubus/libubus/builders/cmake/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 2.8.11) + +include (libubus) + +project(libubus) + +include_directories( + ../../../../ +) + +file( + GLOB + source_files + ../../../libubus.c + ../../../libubus-io.c + ../../../libubus-obj.c + ../../../libubus-sub.c + ../../../libubus-req.c +) + + +ADD_DEFINITIONS(-Wall -Werror --std=gnu99 -g3 -Wmissing-declarations) + + +add_library( + ubus + SHARED + ${source_files} +) + +target_link_libraries (ubus + LINK_PUBLIC + ubox + ) + +target_include_directories (ubus PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/3P/ubus/lua/CMakeLists.txt b/3P/ubus/lua/CMakeLists.txt new file mode 100644 index 00000000..e4821bf7 --- /dev/null +++ b/3P/ubus/lua/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(ubus C) + +SET(CMAKE_INSTALL_PREFIX /) + +IF(NOT LUA_CFLAGS) + FIND_PROGRAM(PKG_CONFIG pkg-config) + IF(PKG_CONFIG) + EXECUTE_PROCESS( + COMMAND pkg-config --silence-errors --cflags lua5.1 + OUTPUT_VARIABLE LUA_CFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + ENDIF() +ENDIF() + +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -I.. ${LUA_CFLAGS}) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) +LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) + +IF(APPLE) + SET(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") +ENDIF(APPLE) + +IF(NOT LUAPATH) + EXECUTE_PROCESS( + COMMAND lua -e "for k in string.gmatch(package.cpath .. \";\", \"([^;]+)/..so;\") do if k:sub(1,1) == \"/\" then print(k) break end end" + OUTPUT_VARIABLE LUAPATH + RESULT_VARIABLE LUA_CHECK_RES + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + IF(BUILD_LUA) + IF(NOT ${LUA_CHECK_RES} EQUAL 0 OR "${LUAPATH}" EQUAL "") + MESSAGE(SEND_ERROR "Lua was not found on your system") + ENDIF() + ENDIF() +ENDIF() + +IF(BUILD_LUA) + ADD_LIBRARY(ubus_lua MODULE ubus.c) + SET_TARGET_PROPERTIES(ubus_lua PROPERTIES + OUTPUT_NAME ubus + PREFIX "" + ) + TARGET_LINK_LIBRARIES(ubus_lua ubus) + + INSTALL(TARGETS ubus_lua + LIBRARY DESTINATION ${LUAPATH} + ) +ENDIF() diff --git a/3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/ubus.dep b/3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/ubus.dep new file mode 100644 index 00000000..e5505e75 --- /dev/null +++ b/3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/ubus.dep @@ -0,0 +1,31 @@ +ubus.o: ../../../ubus.c \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/ubus/libubus.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/avl.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/list.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/list.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blobmsg.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blob.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/utils.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/uloop.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/ubus/ubusmsg.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blob.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/ubus/ubus_common.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blobmsg_json.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/bits.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/debug.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/linkhash.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_object.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_inttypes.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_config.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/arraylist.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_util.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_tokener.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_object_iterator.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_c_version.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blobmsg.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lauxlib.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/luaconf.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lnum_config.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h diff --git a/3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/ubus.o b/3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/ubus.o new file mode 100644 index 00000000..67737c27 Binary files /dev/null and b/3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/ubus.o differ diff --git a/3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/ubus.dep b/3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/ubus.dep new file mode 100644 index 00000000..e5505e75 --- /dev/null +++ b/3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/ubus.dep @@ -0,0 +1,31 @@ +ubus.o: ../../../ubus.c \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/ubus/libubus.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/avl.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/list.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/list.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blobmsg.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blob.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/utils.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/uloop.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/ubus/ubusmsg.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blob.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/ubus/ubus_common.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blobmsg_json.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/bits.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/debug.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/linkhash.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_object.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_inttypes.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_config.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/arraylist.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_util.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_tokener.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_object_iterator.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_c_version.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blobmsg.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lauxlib.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/luaconf.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lnum_config.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h diff --git a/3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/ubus.o b/3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/ubus.o new file mode 100644 index 00000000..8d27c4c5 Binary files /dev/null and b/3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/ubus.o differ diff --git a/3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/ubus.dep b/3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/ubus.dep new file mode 100644 index 00000000..e5505e75 --- /dev/null +++ b/3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/ubus.dep @@ -0,0 +1,31 @@ +ubus.o: ../../../ubus.c \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/ubus/libubus.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/avl.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/list.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/list.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blobmsg.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blob.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/utils.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/uloop.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/ubus/ubusmsg.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blob.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/ubus/ubus_common.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blobmsg_json.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/bits.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/debug.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/linkhash.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_object.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_inttypes.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_config.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/arraylist.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_util.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_tokener.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_object_iterator.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_c_version.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blobmsg.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lauxlib.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/luaconf.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lnum_config.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h diff --git a/3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/ubus.o b/3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/ubus.o new file mode 100644 index 00000000..33d7cbc0 Binary files /dev/null and b/3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/ubus.o differ diff --git a/3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/ubus.dep b/3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/ubus.dep new file mode 100644 index 00000000..e5505e75 --- /dev/null +++ b/3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/ubus.dep @@ -0,0 +1,31 @@ +ubus.o: ../../../ubus.c \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/ubus/libubus.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/avl.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/list.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/list.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blobmsg.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blob.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/utils.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/uloop.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/ubus/ubusmsg.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blob.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/ubus/ubus_common.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blobmsg_json.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/bits.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/debug.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/linkhash.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_object.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_inttypes.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_config.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/arraylist.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_util.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_tokener.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_object_iterator.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/json-c/json_c_version.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/libubox/../libubox/blobmsg.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lauxlib.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/luaconf.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lnum_config.h \ + /home/jbnadal/sources/Audio_trunk/AwoxAudio/Libs/External/lua/src/lua.h diff --git a/3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/ubus.o b/3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/ubus.o new file mode 100644 index 00000000..8749e807 Binary files /dev/null and b/3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/ubus.o differ diff --git a/3P/ubus/lua/test.lua b/3P/ubus/lua/test.lua new file mode 100755 index 00000000..d24ac6e4 --- /dev/null +++ b/3P/ubus/lua/test.lua @@ -0,0 +1,54 @@ +#!/usr/bin/env lua + +require "ubus" +require "uloop" + +uloop.init() + +local conn = ubus.connect() +if not conn then + error("Failed to connect to ubus") +end + +local my_method = { + broken = { + hello = 1, + hello1 = { + function(req) + end, {id = "fail" } + }, + }, + test = { + hello = { + function(req, msg) + conn:reply(req, {message="foo"}); + print("Call to function 'hello'") + for k, v in pairs(msg) do + print("key=" .. k .. " value=" .. tostring(v)) + end + end, {id = ubus.INT32, msg = ubus.STRING } + }, + hello1 = { + function(req) + conn:reply(req, {message="foo1"}); + conn:reply(req, {message="foo2"}); + print("Call to function 'hello1'") + end, {id = ubus.INT32, msg = ubus.STRING } + } + } +} + +conn:add(my_method) + +local my_event = { + test = function(msg) + print("Call to test event") + for k, v in pairs(msg) do + print("key=" .. k .. " value=" .. tostring(v)) + end + end, +} + +conn:listen(my_event) + +uloop.run() diff --git a/3P/ubus/lua/test_client.lua b/3P/ubus/lua/test_client.lua new file mode 100755 index 00000000..0b60e0dc --- /dev/null +++ b/3P/ubus/lua/test_client.lua @@ -0,0 +1,41 @@ +#!/usr/bin/env lua + +require "ubus" +require "uloop" + +uloop.init() + +local conn = ubus.connect() +if not conn then + error("Failed to connect to ubusd") +end + +local namespaces = conn:objects() +for i, n in ipairs(namespaces) do + print("namespace=" .. n) + local signatures = conn:signatures(n) + for p, s in pairs(signatures) do + print("\tprocedure=" .. p) + for k, v in pairs(s) do + print("\t\tattribute=" .. k .. " type=" .. v) + end + end +end + +local status = conn:call("test", "hello", { msg = "eth0" }) + +for k, v in pairs(status) do + print("key=" .. k .. " value=" .. tostring(v)) +end + +local status = {conn:call("test", "hello1", { msg = "eth0" })} + +for a = 1, #status do + for k, v in pairs(status[a]) do + print("key=" .. k .. " value=" .. tostring(v)) + end +end + +conn:send("test", { foo = "bar"}) + +uloop.run() diff --git a/3P/ubus/lua/ubus.c b/3P/ubus/lua/ubus.c new file mode 100644 index 00000000..92fb0a15 --- /dev/null +++ b/3P/ubus/lua/ubus.c @@ -0,0 +1,742 @@ +/* + * Copyright (C) 2012 Jo-Philipp Wich + * Copyright (C) 2012 John Crispin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#define MODNAME "ubus" +#define METANAME MODNAME ".meta" + +static lua_State *state; + +struct ubus_lua_connection { + int timeout; + struct blob_buf buf; + struct ubus_context *ctx; +}; + +struct ubus_lua_object { + struct ubus_object o; + int r; +}; + +struct ubus_lua_event { + struct ubus_event_handler e; + int r; +}; + +static int +ubus_lua_parse_blob(lua_State *L, struct blob_attr *attr, bool table); + +static int +ubus_lua_parse_blob_array(lua_State *L, struct blob_attr *attr, int len, bool table) +{ + int rv; + int idx = 1; + int rem = len; + struct blob_attr *pos; + + lua_newtable(L); + + __blob_for_each_attr(pos, attr, rem) + { + rv = ubus_lua_parse_blob(L, pos, table); + + if (rv > 1) + lua_rawset(L, -3); + else if (rv > 0) + lua_rawseti(L, -2, idx++); + } + + return 1; +} + +static int +ubus_lua_parse_blob(lua_State *L, struct blob_attr *attr, bool table) +{ + int len; + int off = 0; + void *data; + + if (!blobmsg_check_attr(attr, false)) + return 0; + + if (table && blobmsg_name(attr)[0]) + { + lua_pushstring(L, blobmsg_name(attr)); + off++; + } + + data = blobmsg_data(attr); + len = blobmsg_data_len(attr); + + switch (blob_id(attr)) + { + case BLOBMSG_TYPE_BOOL: + lua_pushboolean(L, *(uint8_t *)data); + break; + + case BLOBMSG_TYPE_INT16: + lua_pushinteger(L, be16_to_cpu(*(uint16_t *)data)); + break; + + case BLOBMSG_TYPE_INT32: + lua_pushinteger(L, be32_to_cpu(*(uint32_t *)data)); + break; + + case BLOBMSG_TYPE_INT64: + lua_pushnumber(L, (double) be64_to_cpu(*(uint64_t *)data)); + break; + + case BLOBMSG_TYPE_STRING: + lua_pushstring(L, data); + break; + + case BLOBMSG_TYPE_ARRAY: + ubus_lua_parse_blob_array(L, data, len, false); + break; + + case BLOBMSG_TYPE_TABLE: + ubus_lua_parse_blob_array(L, data, len, true); + break; + + default: + lua_pushnil(L); + break; + } + + return off + 1; +} + + +static bool +ubus_lua_format_blob_is_array(lua_State *L) +{ + lua_Integer prv = 0; + lua_Integer cur = 0; + + /* Find out whether table is array-like */ + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) + { +#ifdef LUA_TINT + if (lua_type(L, -2) != LUA_TNUMBER && lua_type(L, -2) != LUA_TINT) +#else + if (lua_type(L, -2) != LUA_TNUMBER) +#endif + { + lua_pop(L, 2); + return false; + } + + cur = lua_tointeger(L, -2); + + if ((cur - 1) != prv) + { + lua_pop(L, 2); + return false; + } + + prv = cur; + } + + return true; +} + +static int +ubus_lua_format_blob_array(lua_State *L, struct blob_buf *b, bool table); + +static int +ubus_lua_format_blob(lua_State *L, struct blob_buf *b, bool table) +{ + void *c; + bool rv = true; + const char *key = table ? lua_tostring(L, -2) : NULL; + + switch (lua_type(L, -1)) + { + case LUA_TBOOLEAN: + blobmsg_add_u8(b, key, (uint8_t)lua_toboolean(L, -1)); + break; + +#ifdef LUA_TINT + case LUA_TINT: +#endif + case LUA_TNUMBER: + blobmsg_add_u32(b, key, (uint32_t)lua_tointeger(L, -1)); + break; + + case LUA_TSTRING: + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + blobmsg_add_string(b, key, lua_tostring(L, -1)); + break; + + case LUA_TTABLE: + if (ubus_lua_format_blob_is_array(L)) + { + c = blobmsg_open_array(b, key); + rv = ubus_lua_format_blob_array(L, b, false); + blobmsg_close_array(b, c); + } + else + { + c = blobmsg_open_table(b, key); + rv = ubus_lua_format_blob_array(L, b, true); + blobmsg_close_table(b, c); + } + break; + + default: + rv = false; + break; + } + + return rv; +} + +static int +ubus_lua_format_blob_array(lua_State *L, struct blob_buf *b, bool table) +{ + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) + { + if (!ubus_lua_format_blob(L, b, table)) + { + lua_pop(L, 1); + return false; + } + } + + return true; +} + + +static int +ubus_lua_connect(lua_State *L) +{ + struct ubus_lua_connection *c; + const char *sockpath = luaL_optstring(L, 1, NULL); + int timeout = luaL_optint(L, 2, 30); + + if ((c = lua_newuserdata(L, sizeof(*c))) != NULL && + (c->ctx = ubus_connect(sockpath)) != NULL) + { + ubus_add_uloop(c->ctx); + c->timeout = timeout; + memset(&c->buf, 0, sizeof(c->buf)); + luaL_getmetatable(L, METANAME); + lua_setmetatable(L, -2); + return 1; + } + + /* NB: no errors from ubus_connect() yet */ + lua_pushnil(L); + lua_pushinteger(L, UBUS_STATUS_UNKNOWN_ERROR); + return 2; +} + + +static void +ubus_lua_objects_cb(struct ubus_context *c, struct ubus_object_data *o, void *p) +{ + lua_State *L = (lua_State *)p; + + lua_pushstring(L, o->path); + lua_rawseti(L, -2, lua_objlen(L, -2) + 1); +} + +static int +ubus_lua_objects(lua_State *L) +{ + int rv; + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + + lua_newtable(L); + rv = ubus_lookup(c->ctx, NULL, ubus_lua_objects_cb, L); + + if (rv != UBUS_STATUS_OK) + { + lua_pop(L, 1); + lua_pushnil(L); + lua_pushinteger(L, rv); + return 2; + } + + return 1; +} + +static int +ubus_method_handler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct ubus_lua_object *o = container_of(obj, struct ubus_lua_object, o); + int rv = 0; + + lua_getglobal(state, "__ubus_cb"); + lua_rawgeti(state, -1, o->r); + lua_getfield(state, -1, method); + lua_remove(state, -2); + lua_remove(state, -2); + + if (lua_isfunction(state, -1)) { + lua_pushlightuserdata(state, req); + if (!msg) + lua_pushnil(state); + else + ubus_lua_parse_blob_array(state, blob_data(msg), blob_len(msg), true); + lua_call(state, 2, 1); + if (lua_isnumber(state, -1)) + rv = lua_tonumber(state, -1); + } else + lua_pop(state, 1); + + return rv; +} + +static int lua_gettablelen(lua_State *L, int index) +{ + int cnt = 0; + + lua_pushnil(L); + index -= 1; + while (lua_next(L, index) != 0) { + cnt++; + lua_pop(L, 1); + } + + return cnt; +} + +static int ubus_lua_reply(lua_State *L) +{ + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + struct ubus_request_data *req; + + luaL_checktype(L, 3, LUA_TTABLE); + blob_buf_init(&c->buf, 0); + + if (!ubus_lua_format_blob_array(L, &c->buf, true)) + { + lua_pushnil(L); + lua_pushinteger(L, UBUS_STATUS_INVALID_ARGUMENT); + return 2; + } + + req = lua_touserdata(L, 2); + ubus_send_reply(c->ctx, req, c->buf.head); + + return 0; +} + +static int ubus_lua_load_methods(lua_State *L, struct ubus_method *m) +{ + struct blobmsg_policy *p; + int plen; + int pidx = 0; + + /* get the function pointer */ + lua_pushinteger(L, 1); + lua_gettable(L, -2); + + /* get the policy table */ + lua_pushinteger(L, 2); + lua_gettable(L, -3); + + /* check if the method table is valid */ + if ((lua_type(L, -2) != LUA_TFUNCTION) || + (lua_type(L, -1) != LUA_TTABLE) || + lua_objlen(L, -1)) { + lua_pop(L, 2); + return 1; + } + + /* store function pointer */ + lua_pushvalue(L, -2); + lua_setfield(L, -6, lua_tostring(L, -5)); + + m->name = lua_tostring(L, -4); + m->handler = ubus_method_handler; + + plen = lua_gettablelen(L, -1); + + /* exit if policy table is empty */ + if (!plen) { + lua_pop(L, 2); + return 0; + } + + /* setup the policy pointers */ + p = malloc(sizeof(struct blobmsg_policy) * plen); + memset(p, 0, sizeof(struct blobmsg_policy) * plen); + m->policy = p; + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + int val = lua_tointeger(L, -1); + + /* check if the policy is valid */ + if ((lua_type(L, -2) != LUA_TSTRING) || + (lua_type(L, -1) != LUA_TNUMBER) || + (val < 0) || + (val > BLOBMSG_TYPE_LAST)) { + lua_pop(L, 1); + continue; + } + p[pidx].name = lua_tostring(L, -2); + p[pidx].type = val; + lua_pop(L, 1); + pidx++; + } + + m->n_policy = pidx; + lua_pop(L, 2); + + return 0; +} + +static struct ubus_object* ubus_lua_load_object(lua_State *L) +{ + struct ubus_lua_object *obj = NULL; + int mlen = lua_gettablelen(L, -1); + struct ubus_method *m; + int midx = 0; + + /* setup object pointers */ + obj = malloc(sizeof(struct ubus_lua_object)); + memset(obj, 0, sizeof(struct ubus_lua_object)); + obj->o.name = lua_tostring(L, -2); + + /* setup method pointers */ + m = malloc(sizeof(struct ubus_method) * mlen); + memset(m, 0, sizeof(struct ubus_method) * mlen); + obj->o.methods = m; + + /* setup type pointers */ + obj->o.type = malloc(sizeof(struct ubus_object_type)); + memset(obj->o.type, 0, sizeof(struct ubus_object_type)); + obj->o.type->name = lua_tostring(L, -2); + obj->o.type->id = 0; + obj->o.type->methods = obj->o.methods; + + /* create the callback lookup table */ + lua_createtable(L, 1, 0); + lua_getglobal(L, "__ubus_cb"); + lua_pushvalue(L, -2); + obj->r = luaL_ref(L, -2); + lua_pop(L, 1); + + /* scan each method */ + lua_pushnil(L); + while (lua_next(L, -3) != 0) { + /* check if it looks like a method */ + if ((lua_type(L, -2) != LUA_TSTRING) || + (lua_type(L, -1) != LUA_TTABLE) || + !lua_objlen(L, -1)) { + lua_pop(L, 1); + continue; + } + + if (!ubus_lua_load_methods(L, &m[midx])) + midx++; + lua_pop(L, 1); + } + + obj->o.type->n_methods = obj->o.n_methods = midx; + + /* pop the callback table */ + lua_pop(L, 1); + + return &obj->o; +} + +static int ubus_lua_add(lua_State *L) +{ + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + + /* verify top level object */ + if (lua_istable(L, 1)) { + lua_pushstring(L, "you need to pass a table"); + lua_error(L); + return 0; + } + + /* scan each object */ + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + struct ubus_object *obj = NULL; + + /* check if the object has a table of methods */ + if ((lua_type(L, -2) == LUA_TSTRING) && (lua_type(L, -1) == LUA_TTABLE)) { + obj = ubus_lua_load_object(L); + + if (obj) + ubus_add_object(c->ctx, obj); + } + lua_pop(L, 1); + } + + return 0; +} + +static void +ubus_lua_signatures_cb(struct ubus_context *c, struct ubus_object_data *o, void *p) +{ + lua_State *L = (lua_State *)p; + + if (!o->signature) + return; + + ubus_lua_parse_blob_array(L, blob_data(o->signature), blob_len(o->signature), true); +} + +static int +ubus_lua_signatures(lua_State *L) +{ + int rv; + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + const char *path = luaL_checkstring(L, 2); + + rv = ubus_lookup(c->ctx, path, ubus_lua_signatures_cb, L); + + if (rv != UBUS_STATUS_OK) + { + lua_pop(L, 1); + lua_pushnil(L); + lua_pushinteger(L, rv); + return 2; + } + + return 1; +} + + +static void +ubus_lua_call_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + lua_State *L = (lua_State *)req->priv; + + if (!msg) + lua_pushnil(L); + + ubus_lua_parse_blob_array(L, blob_data(msg), blob_len(msg), true); +} + +static int +ubus_lua_call(lua_State *L) +{ + int rv, top; + uint32_t id; + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + const char *path = luaL_checkstring(L, 2); + const char *func = luaL_checkstring(L, 3); + + luaL_checktype(L, 4, LUA_TTABLE); + blob_buf_init(&c->buf, 0); + + if (!ubus_lua_format_blob_array(L, &c->buf, true)) + { + lua_pushnil(L); + lua_pushinteger(L, UBUS_STATUS_INVALID_ARGUMENT); + return 2; + } + + rv = ubus_lookup_id(c->ctx, path, &id); + + if (rv) + { + lua_pushnil(L); + lua_pushinteger(L, rv); + return 2; + } + + top = lua_gettop(L); + rv = ubus_invoke(c->ctx, id, func, c->buf.head, ubus_lua_call_cb, L, c->timeout * 1000); + + if (rv != UBUS_STATUS_OK) + { + lua_pop(L, 1); + lua_pushnil(L); + lua_pushinteger(L, rv); + return 2; + } + + return lua_gettop(L) - top; +} + +static void +ubus_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, + const char *type, struct blob_attr *msg) +{ + struct ubus_lua_event *listener = container_of(ev, struct ubus_lua_event, e); + + lua_getglobal(state, "__ubus_cb_event"); + lua_rawgeti(state, -1, listener->r); + + if (lua_isfunction(state, -1)) { + ubus_lua_parse_blob_array(state, blob_data(msg), blob_len(msg), true); + lua_call(state, 1, 0); + } +} + +static struct ubus_event_handler* +ubus_lua_load_event(lua_State *L) +{ + struct ubus_lua_event* event = NULL; + + event = malloc(sizeof(struct ubus_lua_event)); + memset(event, 0, sizeof(struct ubus_lua_event)); + event->e.cb = ubus_event_handler; + + /* update the he callback lookup table */ + lua_getglobal(L, "__ubus_cb_event"); + lua_pushvalue(L, -2); + event->r = luaL_ref(L, -2); + lua_setfield(L, -1, lua_tostring(L, -3)); + + return &event->e; +} + +static int +ubus_lua_listen(lua_State *L) { + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + + /* verify top level object */ + luaL_checktype(L, 2, LUA_TTABLE); + + /* scan each object */ + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + struct ubus_event_handler *listener; + + /* check if the key is a string and the value is a method */ + if ((lua_type(L, -2) == LUA_TSTRING) && (lua_type(L, -1) == LUA_TFUNCTION)) { + listener = ubus_lua_load_event(L); + if(listener != NULL) { + ubus_register_event_handler(c->ctx, listener, lua_tostring(L, -2)); + } + } + lua_pop(L, 1); + } + return 0; +} + +static int +ubus_lua_send(lua_State *L) +{ + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + const char *event = luaL_checkstring(L, 2); + + if (*event == 0) + return luaL_argerror(L, 2, "no event name"); + + // Event content convert to ubus form + luaL_checktype(L, 3, LUA_TTABLE); + blob_buf_init(&c->buf, 0); + + if (!ubus_lua_format_blob_array(L, &c->buf, true)) { + lua_pushnil(L); + lua_pushinteger(L, UBUS_STATUS_INVALID_ARGUMENT); + return 2; + } + + // Send the event + ubus_send_event(c->ctx, event, c->buf.head); + + return 0; +} + + + +static int +ubus_lua__gc(lua_State *L) +{ + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + + if (c->ctx != NULL) + { + ubus_free(c->ctx); + memset(c, 0, sizeof(*c)); + } + + return 0; +} + +static const luaL_Reg ubus[] = { + { "connect", ubus_lua_connect }, + { "objects", ubus_lua_objects }, + { "add", ubus_lua_add }, + { "reply", ubus_lua_reply }, + { "signatures", ubus_lua_signatures }, + { "call", ubus_lua_call }, + { "close", ubus_lua__gc }, + { "listen", ubus_lua_listen }, + { "send", ubus_lua_send }, + { "__gc", ubus_lua__gc }, + { NULL, NULL }, +}; + +/* avoid missing prototype warning */ +int luaopen_ubus(lua_State *L); + +int +luaopen_ubus(lua_State *L) +{ + /* create metatable */ + luaL_newmetatable(L, METANAME); + + /* metatable.__index = metatable */ + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + + /* fill metatable */ + luaL_register(L, NULL, ubus); + lua_pop(L, 1); + + /* create module */ + luaL_register(L, MODNAME, ubus); + + /* set some enum defines */ + lua_pushinteger(L, BLOBMSG_TYPE_ARRAY); + lua_setfield(L, -2, "ARRAY"); + lua_pushinteger(L, BLOBMSG_TYPE_TABLE); + lua_setfield(L, -2, "TABLE"); + lua_pushinteger(L, BLOBMSG_TYPE_STRING); + lua_setfield(L, -2, "STRING"); + lua_pushinteger(L, BLOBMSG_TYPE_INT64); + lua_setfield(L, -2, "INT64"); + lua_pushinteger(L, BLOBMSG_TYPE_INT32); + lua_setfield(L, -2, "INT32"); + lua_pushinteger(L, BLOBMSG_TYPE_INT16); + lua_setfield(L, -2, "INT16"); + lua_pushinteger(L, BLOBMSG_TYPE_INT8); + lua_setfield(L, -2, "INT8"); + lua_pushinteger(L, BLOBMSG_TYPE_BOOL); + lua_setfield(L, -2, "BOOLEAN"); + + /* used in our callbacks */ + state = L; + + /* create the callback table */ + lua_createtable(L, 1, 0); + lua_setglobal(L, "__ubus_cb"); + + /* create the event table */ + lua_createtable(L, 1, 0); + lua_setglobal(L, "__ubus_cb_event"); + + return 0; +} diff --git a/3P/ubus/systemd/CMakeLists.txt b/3P/ubus/systemd/CMakeLists.txt new file mode 100644 index 00000000..54b754b6 --- /dev/null +++ b/3P/ubus/systemd/CMakeLists.txt @@ -0,0 +1,7 @@ +CONFIGURE_FILE(ubus.socket.in ubus.socket) +CONFIGURE_FILE(ubus.service.in ubus.service) + +# Cmakes pkgconfig support is very limited, so for now just hardcode +SET(SYSTEMD_SYSUNIT_DIR "${SYSTEMD_PREFIX}/lib/systemd/system") +INSTALL(FILES ${CMAKE_BINARY_DIR}/systemd/ubus.socket ${CMAKE_BINARY_DIR}/systemd/ubus.service + DESTINATION ${SYSTEMD_SYSUNIT_DIR}) diff --git a/3P/ubus/systemd/ubus.service.in b/3P/ubus/systemd/ubus.service.in new file mode 100644 index 00000000..185a42fc --- /dev/null +++ b/3P/ubus/systemd/ubus.service.in @@ -0,0 +1,6 @@ +[Unit] +Description=OpenWrt micro bus +Requires=ubus.socket + +[Service] +ExecStart=@UBUSD_BINARY@ diff --git a/3P/ubus/systemd/ubus.socket.in b/3P/ubus/systemd/ubus.socket.in new file mode 100644 index 00000000..364beb7a --- /dev/null +++ b/3P/ubus/systemd/ubus.socket.in @@ -0,0 +1,8 @@ +[Unit] +Description=OpenWrt micro bus socket + +[Socket] +ListenStream=@UBUS_UNIX_SOCKET@ + +[Install] +WantedBy=sockets.target diff --git a/3P/ubus/ubus_common.h b/3P/ubus/ubus_common.h new file mode 100644 index 00000000..4bb99279 --- /dev/null +++ b/3P/ubus/ubus_common.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __UBUS_COMMON_H +#define __UBUS_COMMON_H + +#define UBUS_SIGNATURE_METHOD (BLOBMSG_TYPE_LAST + 1) +#define UBUS_SIGNATURE_END (BLOBMSG_TYPE_LAST + 2) + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +#endif diff --git a/3P/ubus/ubusd.c b/3P/ubus/ubusd.c new file mode 100644 index 00000000..89031053 --- /dev/null +++ b/3P/ubus/ubusd.c @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#ifdef FreeBSD +#include +#endif +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ubusd.h" + +static struct ubus_msg_buf *ubus_msg_ref(struct ubus_msg_buf *ub) +{ + if (ub->refcount == ~0) + return ubus_msg_new(ub->data, ub->len, false); + + ub->refcount++; + return ub; +} + +struct ubus_msg_buf *ubus_msg_new(void *data, int len, bool shared) +{ + struct ubus_msg_buf *ub; + int buflen = sizeof(*ub); + + if (!shared) + buflen += len; + + ub = calloc(1, buflen); + if (!ub) + return NULL; + + ub->fd = -1; + + if (shared) { + ub->refcount = ~0; + ub->data = data; + } else { + ub->refcount = 1; + ub->data = (void *) (ub + 1); + if (data) + memcpy(ub + 1, data, len); + } + + ub->len = len; + return ub; +} + +void ubus_msg_free(struct ubus_msg_buf *ub) +{ + switch (ub->refcount) { + case 1: + case ~0: + if (ub->fd >= 0) + close(ub->fd); + + free(ub); + break; + default: + ub->refcount--; + break; + } +} + +static int ubus_msg_writev(int fd, struct ubus_msg_buf *ub, int offset) +{ + static struct iovec iov[2]; + static struct { + struct cmsghdr h; + int fd; + } fd_buf = { + .h = { + .cmsg_len = sizeof(fd_buf), + .cmsg_level = SOL_SOCKET, + .cmsg_type = SCM_RIGHTS, + }, + }; + struct msghdr msghdr = { + .msg_iov = iov, + .msg_iovlen = ARRAY_SIZE(iov), + .msg_control = &fd_buf, + .msg_controllen = sizeof(fd_buf), + }; + + fd_buf.fd = ub->fd; + if (ub->fd < 0) { + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + } + + if (offset < sizeof(ub->hdr)) { + iov[0].iov_base = ((char *) &ub->hdr) + offset; + iov[0].iov_len = sizeof(ub->hdr) - offset; + iov[1].iov_base = (char *) ub->data; + iov[1].iov_len = ub->len; + + return sendmsg(fd, &msghdr, 0); + } else { + offset -= sizeof(ub->hdr); + return write(fd, ((char *) ub->data) + offset, ub->len - offset); + } +} + +static void ubus_msg_enqueue(struct ubus_client *cl, struct ubus_msg_buf *ub) +{ + if (cl->tx_queue[cl->txq_tail]) + return; + + cl->tx_queue[cl->txq_tail] = ubus_msg_ref(ub); + cl->txq_tail = (cl->txq_tail + 1) % ARRAY_SIZE(cl->tx_queue); +} + +/* takes the msgbuf reference */ +void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub, bool free) +{ + int written; + + if (!cl->tx_queue[cl->txq_cur]) { + written = ubus_msg_writev(cl->sock.fd, ub, 0); + if (written >= ub->len + sizeof(ub->hdr)) + goto out; + + if (written < 0) + written = 0; + + cl->txq_ofs = written; + + /* get an event once we can write to the socket again */ + uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_WRITE | ULOOP_EDGE_TRIGGER); + } + ubus_msg_enqueue(cl, ub); + +out: + if (free) + ubus_msg_free(ub); +} + +static struct ubus_msg_buf *ubus_msg_head(struct ubus_client *cl) +{ + return cl->tx_queue[cl->txq_cur]; +} + +static void ubus_msg_dequeue(struct ubus_client *cl) +{ + struct ubus_msg_buf *ub = ubus_msg_head(cl); + + if (!ub) + return; + + ubus_msg_free(ub); + cl->txq_ofs = 0; + cl->tx_queue[cl->txq_cur] = NULL; + cl->txq_cur = (cl->txq_cur + 1) % ARRAY_SIZE(cl->tx_queue); +} + +static void handle_client_disconnect(struct ubus_client *cl) +{ + while (ubus_msg_head(cl)) + ubus_msg_dequeue(cl); + + ubusd_proto_free_client(cl); + if (cl->pending_msg_fd >= 0) + close(cl->pending_msg_fd); + uloop_fd_delete(&cl->sock); + close(cl->sock.fd); + free(cl); +} + +static void client_cb(struct uloop_fd *sock, unsigned int events) +{ + struct ubus_client *cl = container_of(sock, struct ubus_client, sock); + struct ubus_msg_buf *ub; + static struct iovec iov; + static struct { + struct cmsghdr h; + int fd; + } fd_buf = { + .h = { + .cmsg_type = SCM_RIGHTS, + .cmsg_level = SOL_SOCKET, + .cmsg_len = sizeof(fd_buf), + } + }; + struct msghdr msghdr = { + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + /* first try to tx more pending data */ + while ((ub = ubus_msg_head(cl))) { + int written; + + written = ubus_msg_writev(sock->fd, ub, cl->txq_ofs); + if (written < 0) { + switch(errno) { + case EINTR: + case EAGAIN: + break; + default: + goto disconnect; + } + break; + } + + cl->txq_ofs += written; + if (cl->txq_ofs < ub->len + sizeof(ub->hdr)) + break; + + ubus_msg_dequeue(cl); + } + + /* prevent further ULOOP_WRITE events if we don't have data + * to send anymore */ + if (!ubus_msg_head(cl) && (events & ULOOP_WRITE)) + uloop_fd_add(sock, ULOOP_READ | ULOOP_EDGE_TRIGGER); + +retry: + if (!sock->eof && cl->pending_msg_offset < sizeof(cl->hdrbuf)) { + int offset = cl->pending_msg_offset; + int bytes; + + fd_buf.fd = -1; + + iov.iov_base = &cl->hdrbuf + offset; + iov.iov_len = sizeof(cl->hdrbuf) - offset; + + if (cl->pending_msg_fd < 0) { + msghdr.msg_control = &fd_buf; + msghdr.msg_controllen = sizeof(fd_buf); + } else { + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + } + + bytes = recvmsg(sock->fd, &msghdr, 0); + if (bytes < 0) + goto out; + + if (fd_buf.fd >= 0) + cl->pending_msg_fd = fd_buf.fd; + + cl->pending_msg_offset += bytes; + if (cl->pending_msg_offset < sizeof(cl->hdrbuf)) + goto out; + + if (blob_pad_len(&cl->hdrbuf.data) > UBUS_MAX_MSGLEN) + goto disconnect; + + cl->pending_msg = ubus_msg_new(NULL, blob_raw_len(&cl->hdrbuf.data), false); + if (!cl->pending_msg) + goto disconnect; + + memcpy(&cl->pending_msg->hdr, &cl->hdrbuf.hdr, sizeof(cl->hdrbuf.hdr)); + memcpy(cl->pending_msg->data, &cl->hdrbuf.data, sizeof(cl->hdrbuf.data)); + } + + ub = cl->pending_msg; + if (ub) { + int offset = cl->pending_msg_offset - sizeof(ub->hdr); + int len = blob_raw_len(ub->data) - offset; + int bytes = 0; + + if (len > 0) { + bytes = read(sock->fd, (char *) ub->data + offset, len); + if (bytes <= 0) + goto out; + } + + if (bytes < len) { + cl->pending_msg_offset += bytes; + goto out; + } + + /* accept message */ + ub->fd = cl->pending_msg_fd; + cl->pending_msg_fd = -1; + cl->pending_msg_offset = 0; + cl->pending_msg = NULL; + ubusd_proto_receive_message(cl, ub); + goto retry; + } + +out: + if (!sock->eof || ubus_msg_head(cl)) + return; + +disconnect: + handle_client_disconnect(cl); +} + +static bool get_next_connection(int fd) +{ + struct ubus_client *cl; + int client_fd; + + client_fd = accept(fd, NULL, 0); + if (client_fd < 0) { + switch (errno) { + case ECONNABORTED: + case EINTR: + return true; + default: + return false; + } + } + + cl = ubusd_proto_new_client(client_fd, client_cb); + if (cl) + uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_EDGE_TRIGGER); + else + close(client_fd); + + return true; +} + +static void server_cb(struct uloop_fd *fd, unsigned int events) +{ + bool next; + + do { + next = get_next_connection(fd->fd); + } while (next); +} + +static struct uloop_fd server_fd = { + .cb = server_cb, +}; + +static int usage(const char *progname) +{ + fprintf(stderr, "Usage: %s []\n" + "Options: \n" + " -s : Set the unix domain socket to listen on\n" + "\n", progname); + return 1; +} + +int main(int argc, char **argv) +{ + const char *ubus_socket = UBUS_UNIX_SOCKET; + int ret = 0; + int ch; + + signal(SIGPIPE, SIG_IGN); + + uloop_init(); + + while ((ch = getopt(argc, argv, "s:")) != -1) { + switch (ch) { + case 's': + ubus_socket = optarg; + break; + default: + return usage(argv[0]); + } + } + + unlink(ubus_socket); + umask(0177); + server_fd.fd = usock(USOCK_UNIX | USOCK_SERVER | USOCK_NONBLOCK, ubus_socket, NULL); + if (server_fd.fd < 0) { + perror("usock"); + ret = -1; + goto out; + } + uloop_fd_add(&server_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER); + + uloop_run(); + unlink(ubus_socket); + +out: + uloop_done(); + return ret; +} diff --git a/3P/ubus/ubusd.h b/3P/ubus/ubusd.h new file mode 100644 index 00000000..9cdb9d5c --- /dev/null +++ b/3P/ubus/ubusd.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __UBUSD_H +#define __UBUSD_H + +#include +#include +#include +#include "ubus_common.h" +#include "ubusd_id.h" +#include "ubusd_obj.h" +#include "ubusmsg.h" + +#define UBUSD_CLIENT_BACKLOG 32 +#define UBUS_OBJ_HASH_BITS 4 + +extern __thread struct blob_buf b; + +struct ubus_msg_buf { + uint32_t refcount; /* ~0: uses external data buffer */ + struct ubus_msghdr hdr; + struct blob_attr *data; + int fd; + int len; +}; + +struct ubus_client { + struct ubus_id id; + struct uloop_fd sock; + + struct list_head objects; + + struct ubus_msg_buf *tx_queue[UBUSD_CLIENT_BACKLOG]; + unsigned int txq_cur, txq_tail, txq_ofs; + + struct ubus_msg_buf *pending_msg; + int pending_msg_offset; + int pending_msg_fd; + struct { + struct ubus_msghdr hdr; + struct blob_attr data; + } hdrbuf; +}; + +struct ubus_path { + struct list_head list; + const char name[]; +}; + +struct ubus_msg_buf *ubus_msg_new(void *data, int len, bool shared); +void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub, bool free); +void ubus_msg_free(struct ubus_msg_buf *ub); + +struct ubus_client *ubusd_proto_new_client(int fd, uloop_fd_handler cb); +void ubusd_proto_receive_message(struct ubus_client *cl, struct ubus_msg_buf *ub); +void ubusd_proto_free_client(struct ubus_client *cl); + +void ubusd_event_init(void); +void ubusd_event_cleanup_object(struct ubus_object *obj); +void ubusd_send_obj_event(struct ubus_object *obj, bool add); + + +#endif diff --git a/3P/ubus/ubusd_event.c b/3P/ubus/ubusd_event.c new file mode 100644 index 00000000..8ec92018 --- /dev/null +++ b/3P/ubus/ubusd_event.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include "ubusd.h" + +static __thread struct avl_tree patterns; +static __thread struct ubus_object *event_obj; +static __thread int event_seq = 0; +static __thread int obj_event_seq = 1; + +struct event_source { + struct list_head list; + struct ubus_object *obj; + struct avl_node avl; + bool partial; +}; + +static void ubusd_delete_event_source(struct event_source *evs) +{ + list_del(&evs->list); + avl_delete(&patterns, &evs->avl); + free(evs); +} + +void ubusd_event_cleanup_object(struct ubus_object *obj) +{ + struct event_source *ev; + + while (!list_empty(&obj->events)) { + ev = list_first_entry(&obj->events, struct event_source, list); + ubusd_delete_event_source(ev); + } +} + +enum { + EVREG_PATTERN, + EVREG_OBJECT, + EVREG_LAST, +}; + +static struct blobmsg_policy evr_policy[] = { + [EVREG_PATTERN] = { .name = "pattern", .type = BLOBMSG_TYPE_STRING }, + [EVREG_OBJECT] = { .name = "object", .type = BLOBMSG_TYPE_INT32 }, +}; + +static int ubusd_alloc_event_pattern(struct ubus_client *cl, struct blob_attr *msg) +{ + struct event_source *ev; + struct ubus_object *obj; + struct blob_attr *attr[EVREG_LAST]; + char *pattern, *name; + uint32_t id; + bool partial = false; + int len; + + blobmsg_parse(evr_policy, EVREG_LAST, attr, blob_data(msg), blob_len(msg)); + if (!attr[EVREG_OBJECT] || !attr[EVREG_PATTERN]) + return UBUS_STATUS_INVALID_ARGUMENT; + + id = blobmsg_get_u32(attr[EVREG_OBJECT]); + if (id < UBUS_SYSTEM_OBJECT_MAX) + return UBUS_STATUS_PERMISSION_DENIED; + + obj = ubusd_find_object(id); + if (!obj) + return UBUS_STATUS_NOT_FOUND; + + if (obj->client != cl) + return UBUS_STATUS_PERMISSION_DENIED; + + pattern = blobmsg_data(attr[EVREG_PATTERN]); + + len = strlen(pattern); + if (pattern[len - 1] == '*') { + partial = true; + pattern[len - 1] = 0; + len--; + } + + ev = calloc(1, sizeof(*ev) + len + 1); + if (!ev) + return UBUS_STATUS_NO_DATA; + + list_add(&ev->list, &obj->events); + ev->obj = obj; + ev->partial = partial; + name = (char *) (ev + 1); + strcpy(name, pattern); + ev->avl.key = name; + avl_insert(&patterns, &ev->avl); + + return 0; +} + +typedef struct ubus_msg_buf *(*event_fill_cb)(void *priv, const char *id); + +static void ubusd_send_event_msg(struct ubus_msg_buf **ub, struct ubus_client *cl, + struct ubus_object *obj, const char *id, + event_fill_cb fill_cb, void *cb_priv) +{ + uint32_t *objid_ptr; + + /* do not loop back events */ + if (obj->client == cl) + return; + + /* do not send duplicate events */ + if (obj->event_seen == obj_event_seq) + return; + + obj->event_seen = obj_event_seq; + + if (!*ub) { + *ub = fill_cb(cb_priv, id); + (*ub)->hdr.type = UBUS_MSG_INVOKE; + (*ub)->hdr.peer = 0; + } + + objid_ptr = blob_data(blob_data((*ub)->data)); + *objid_ptr = htonl(obj->id.id); + + (*ub)->hdr.seq = ++event_seq; + ubus_msg_send(obj->client, *ub, false); +} + +static bool strmatch_len(const char *s1, const char *s2, int *len) +{ + for (*len = 0; s1[*len] == s2[*len]; (*len)++) + if (!s1[*len]) + return true; + + return false; +} + +static int ubusd_send_event(struct ubus_client *cl, const char *id, + event_fill_cb fill_cb, void *cb_priv) +{ + struct ubus_msg_buf *ub = NULL; + struct event_source *ev; + int match_len = 0; + + obj_event_seq++; + + /* + * Since this tree is sorted alphabetically, we can only expect to find + * matching entries as long as the number of matching characters + * between the pattern string and our string is monotonically increasing. + */ + avl_for_each_element(&patterns, ev, avl) { + const char *key = ev->avl.key; + int cur_match_len; + bool full_match; + + full_match = strmatch_len(id, key, &cur_match_len); + if (cur_match_len < match_len) + break; + + match_len = cur_match_len; + + if (!full_match) { + if (!ev->partial) + continue; + + if (match_len != strlen(key)) + continue; + } + + ubusd_send_event_msg(&ub, cl, ev->obj, id, fill_cb, cb_priv); + } + + if (ub) + ubus_msg_free(ub); + + return 0; +} + +enum { + EVMSG_ID, + EVMSG_DATA, + EVMSG_LAST, +}; + +static __thread struct blobmsg_policy ev_policy[] = { + [EVMSG_ID] = { .name = "id", .type = BLOBMSG_TYPE_STRING }, + [EVMSG_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE }, +}; + +static struct ubus_msg_buf * +ubusd_create_event_from_msg(void *priv, const char *id) +{ + struct blob_attr *msg = priv; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, 0); + blob_put_string(&b, UBUS_ATTR_METHOD, id); + blob_put(&b, UBUS_ATTR_DATA, blobmsg_data(msg), blobmsg_data_len(msg)); + + return ubus_msg_new(b.head, blob_raw_len(b.head), true); +} + +static int ubusd_forward_event(struct ubus_client *cl, struct blob_attr *msg) +{ + struct blob_attr *data; + struct blob_attr *attr[EVMSG_LAST]; + const char *id; + + blobmsg_parse(ev_policy, EVMSG_LAST, attr, blob_data(msg), blob_len(msg)); + if (!attr[EVMSG_ID] || !attr[EVMSG_DATA]) + return UBUS_STATUS_INVALID_ARGUMENT; + + id = blobmsg_data(attr[EVMSG_ID]); + data = attr[EVMSG_DATA]; + + if (!strncmp(id, "ubus.", 5)) + return UBUS_STATUS_PERMISSION_DENIED; + + return ubusd_send_event(cl, id, ubusd_create_event_from_msg, data); +} + +static int ubusd_event_recv(struct ubus_client *cl, const char *method, struct blob_attr *msg) +{ + if (!strcmp(method, "register")) + return ubusd_alloc_event_pattern(cl, msg); + + if (!strcmp(method, "send")) + return ubusd_forward_event(cl, msg); + + return UBUS_STATUS_INVALID_COMMAND; +} + +static struct ubus_msg_buf * +ubusd_create_object_event_msg(void *priv, const char *id) +{ + struct ubus_object *obj = priv; + void *s; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, 0); + blob_put_string(&b, UBUS_ATTR_METHOD, id); + s = blob_nest_start(&b, UBUS_ATTR_DATA); + blobmsg_add_u32(&b, "id", obj->id.id); + blobmsg_add_string(&b, "path", obj->path.key); + blob_nest_end(&b, s); + + return ubus_msg_new(b.head, blob_raw_len(b.head), true); +} + +void ubusd_send_obj_event(struct ubus_object *obj, bool add) +{ + const char *id = add ? "ubus.object.add" : "ubus.object.remove"; + + ubusd_send_event(NULL, id, ubusd_create_object_event_msg, obj); +} + +void ubusd_event_init(void) +{ + ubus_init_string_tree(&patterns, true); + event_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_EVENT); + event_obj->recv_msg = ubusd_event_recv; +} + diff --git a/3P/ubus/ubusd_id.c b/3P/ubus/ubusd_id.c new file mode 100644 index 00000000..1d1892ab --- /dev/null +++ b/3P/ubus/ubusd_id.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "ubusmsg.h" +#include "ubusd_id.h" + +static __thread int random_fd = -1; + +static int ubus_cmp_id(const void *k1, const void *k2, void *ptr) +{ + const uint32_t *id1 = k1, *id2 = k2; + + if (*id1 < *id2) + return -1; + else + return *id1 > *id2; +} + +void ubus_init_string_tree(struct avl_tree *tree, bool dup) +{ + avl_init(tree, avl_strcmp, dup, NULL); +} + +void ubus_init_id_tree(struct avl_tree *tree) +{ + if (random_fd < 0) { + random_fd = open("/dev/urandom", O_RDONLY); + if (random_fd < 0) { + perror("open"); + exit(1); + } + } + + avl_init(tree, ubus_cmp_id, false, NULL); +} + +bool ubus_alloc_id(struct avl_tree *tree, struct ubus_id *id, uint32_t val) +{ + id->avl.key = &id->id; + if (val) { + id->id = val; + return avl_insert(tree, &id->avl) == 0; + } + + do { + if (read(random_fd, &id->id, sizeof(id->id)) != sizeof(id->id)) + return false; + + if (id->id < UBUS_SYSTEM_OBJECT_MAX) + continue; + } while (avl_insert(tree, &id->avl) != 0); + + return true; +} + diff --git a/3P/ubus/ubusd_id.h b/3P/ubus/ubusd_id.h new file mode 100644 index 00000000..0c852485 --- /dev/null +++ b/3P/ubus/ubusd_id.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __UBUSD_ID_H +#define __UBUSD_ID_H + +#include +#include + +struct ubus_id { + struct avl_node avl; + uint32_t id; +}; + +void ubus_init_id_tree(struct avl_tree *tree); +void ubus_init_string_tree(struct avl_tree *tree, bool dup); +bool ubus_alloc_id(struct avl_tree *tree, struct ubus_id *id, uint32_t val); + +static inline void ubus_free_id(struct avl_tree *tree, struct ubus_id *id) +{ + avl_delete(tree, &id->avl); +} + +static inline struct ubus_id *ubus_find_id(struct avl_tree *tree, uint32_t id) +{ + struct avl_node *avl; + + avl = avl_find(tree, &id); + if (!avl) + return NULL; + + return container_of(avl, struct ubus_id, avl); +} + +#endif diff --git a/3P/ubus/ubusd_obj.c b/3P/ubus/ubusd_obj.c new file mode 100644 index 00000000..bef026a1 --- /dev/null +++ b/3P/ubus/ubusd_obj.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ubusd.h" +#include "ubusd_obj.h" + +__thread struct avl_tree obj_types; +__thread struct avl_tree objects; +__thread struct avl_tree path; + +static void ubus_unref_object_type(struct ubus_object_type *type) +{ + struct ubus_method *m; + + if (--type->refcount > 0) + return; + + while (!list_empty(&type->methods)) { + m = list_first_entry(&type->methods, struct ubus_method, list); + list_del(&m->list); + free(m); + } + + ubus_free_id(&obj_types, &type->id); + free(type); +} + +static bool ubus_create_obj_method(struct ubus_object_type *type, struct blob_attr *attr) +{ + struct ubus_method *m; + int bloblen = blob_raw_len(attr); + + m = calloc(1, sizeof(*m) + bloblen); + if (!m) + return false; + + list_add_tail(&m->list, &type->methods); + memcpy(m->data, attr, bloblen); + m->name = blobmsg_name(m->data); + + return true; +} + +static struct ubus_object_type *ubus_create_obj_type(struct blob_attr *sig) +{ + struct ubus_object_type *type; + struct blob_attr *pos; + int rem; + + type = calloc(1, sizeof(*type)); + type->refcount = 1; + + if (!ubus_alloc_id(&obj_types, &type->id, 0)) + goto error_free; + + INIT_LIST_HEAD(&type->methods); + + blob_for_each_attr(pos, sig, rem) { + if (!blobmsg_check_attr(pos, true)) + goto error_unref; + + if (!ubus_create_obj_method(type, pos)) + goto error_unref; + } + + return type; + +error_unref: + ubus_unref_object_type(type); + return NULL; + +error_free: + free(type); + return NULL; +} + +static struct ubus_object_type *ubus_get_obj_type(uint32_t obj_id) +{ + struct ubus_object_type *type; + struct ubus_id *id; + + id = ubus_find_id(&obj_types, obj_id); + if (!id) + return NULL; + + type = container_of(id, struct ubus_object_type, id); + type->refcount++; + return type; +} + +struct ubus_object *ubusd_create_object_internal(struct ubus_object_type *type, uint32_t id) +{ + struct ubus_object *obj; + + obj = calloc(1, sizeof(*obj)); + if (!obj) + return NULL; + + if (!ubus_alloc_id(&objects, &obj->id, id)) + goto error_free; + + obj->type = type; + INIT_LIST_HEAD(&obj->list); + INIT_LIST_HEAD(&obj->events); + INIT_LIST_HEAD(&obj->subscribers); + INIT_LIST_HEAD(&obj->target_list); + if (type) + type->refcount++; + + return obj; + +error_free: + free(obj); + return NULL; +} + +struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr **attr) +{ + struct ubus_object *obj; + struct ubus_object_type *type = NULL; + + if (attr[UBUS_ATTR_OBJTYPE]) + type = ubus_get_obj_type(blob_get_u32(attr[UBUS_ATTR_OBJTYPE])); + else if (attr[UBUS_ATTR_SIGNATURE]) + type = ubus_create_obj_type(attr[UBUS_ATTR_SIGNATURE]); + + obj = ubusd_create_object_internal(type, 0); + if (type) + ubus_unref_object_type(type); + + if (!obj) + return NULL; + + if (attr[UBUS_ATTR_OBJPATH]) { + obj->path.key = strdup(blob_data(attr[UBUS_ATTR_OBJPATH])); + if (!obj->path.key) + goto free; + + if (avl_insert(&path, &obj->path) != 0) { + free((void *) obj->path.key); + obj->path.key = NULL; + goto free; + } + ubusd_send_obj_event(obj, true); + } + + obj->client = cl; + list_add(&obj->list, &cl->objects); + + return obj; + +free: + ubusd_free_object(obj); + return NULL; +} + +void ubus_subscribe(struct ubus_object *obj, struct ubus_object *target) +{ + struct ubus_subscription *s; + bool first = list_empty(&target->subscribers); + + s = calloc(1, sizeof(*s)); + if (!s) + return; + + s->subscriber = obj; + s->target = target; + list_add(&s->list, &target->subscribers); + list_add(&s->target_list, &obj->target_list); + + if (first) + ubus_notify_subscription(target); +} + +void ubus_unsubscribe(struct ubus_subscription *s) +{ + struct ubus_object *obj = s->target; + + list_del(&s->list); + list_del(&s->target_list); + free(s); + + if (list_empty(&obj->subscribers)) + ubus_notify_subscription(obj); +} + +void ubusd_free_object(struct ubus_object *obj) +{ + struct ubus_subscription *s, *tmp; + + list_for_each_entry_safe(s, tmp, &obj->target_list, target_list) { + ubus_unsubscribe(s); + } + list_for_each_entry_safe(s, tmp, &obj->subscribers, list) { + ubus_notify_unsubscribe(s); + } + + ubusd_event_cleanup_object(obj); + if (obj->path.key) { + ubusd_send_obj_event(obj, false); + avl_delete(&path, &obj->path); + free((void *) obj->path.key); + } + if (!list_empty(&obj->list)) + list_del(&obj->list); + ubus_free_id(&objects, &obj->id); + if (obj->type) + ubus_unref_object_type(obj->type); + free(obj); +} + +static void __constructor ubusd_obj_init(void) +{ + ubus_init_id_tree(&objects); + ubus_init_id_tree(&obj_types); + ubus_init_string_tree(&path, false); + ubusd_event_init(); +} diff --git a/3P/ubus/ubusd_obj.h b/3P/ubus/ubusd_obj.h new file mode 100644 index 00000000..9a119402 --- /dev/null +++ b/3P/ubus/ubusd_obj.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __UBUSD_OBJ_H +#define __UBUSD_OBJ_H + +#include "ubusd_id.h" + +extern __thread struct avl_tree obj_types; +extern __thread struct avl_tree objects; +extern __thread struct avl_tree path; + +struct ubus_client; +struct ubus_msg_buf; + +struct ubus_object_type { + struct ubus_id id; + int refcount; + struct list_head methods; +}; + +struct ubus_method { + struct list_head list; + const char *name; + struct blob_attr data[]; +}; + +struct ubus_subscription { + struct list_head list, target_list; + struct ubus_object *subscriber, *target; +}; + +struct ubus_object { + struct ubus_id id; + struct list_head list; + + struct list_head events; + + struct list_head subscribers, target_list; + + struct ubus_object_type *type; + struct avl_node path; + + struct ubus_client *client; + int (*recv_msg)(struct ubus_client *client, const char *method, struct blob_attr *msg); + + int event_seen; + unsigned int invoke_seq; +}; + +struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr **attr); +struct ubus_object *ubusd_create_object_internal(struct ubus_object_type *type, uint32_t id); +void ubusd_free_object(struct ubus_object *obj); + +static inline struct ubus_object *ubusd_find_object(uint32_t objid) +{ + struct ubus_object *obj; + struct ubus_id *id; + + id = ubus_find_id(&objects, objid); + if (!id) + return NULL; + + obj = container_of(id, struct ubus_object, id); + return obj; +} + +void ubus_subscribe(struct ubus_object *obj, struct ubus_object *target); +void ubus_unsubscribe(struct ubus_subscription *s); +void ubus_notify_unsubscribe(struct ubus_subscription *s); +void ubus_notify_subscription(struct ubus_object *obj); + +#endif diff --git a/3P/ubus/ubusd_proto.c b/3P/ubus/ubusd_proto.c new file mode 100644 index 00000000..aa08bca0 --- /dev/null +++ b/3P/ubus/ubusd_proto.c @@ -0,0 +1,522 @@ +/* + * Copyright (C) 2011-2014 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include "ubusd.h" + +__thread struct blob_buf b; +static __thread struct ubus_msg_buf *retmsg; +static __thread int *retmsg_data; +static __thread struct avl_tree clients; + +static __thread struct blob_attr *attrbuf[UBUS_ATTR_MAX]; + +typedef int (*ubus_cmd_cb)(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr); + +static const struct blob_attr_info ubus_policy[UBUS_ATTR_MAX] = { + [UBUS_ATTR_SIGNATURE] = { .type = BLOB_ATTR_NESTED }, + [UBUS_ATTR_OBJTYPE] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_OBJPATH] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_OBJID] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_STATUS] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_METHOD] = { .type = BLOB_ATTR_STRING }, +}; + +static struct blob_attr **ubus_parse_msg(struct blob_attr *msg) +{ + blob_parse(msg, attrbuf, ubus_policy, UBUS_ATTR_MAX); + return attrbuf; +} + +static void ubus_msg_close_fd(struct ubus_msg_buf *ub) +{ + if (ub->fd < 0) + return; + + close(ub->fd); + ub->fd = -1; +} + +static void ubus_msg_init(struct ubus_msg_buf *ub, uint8_t type, uint16_t seq, uint32_t peer) +{ + ub->hdr.version = 0; + ub->hdr.type = type; + ub->hdr.seq = seq; + ub->hdr.peer = peer; +} + +static struct ubus_msg_buf *ubus_msg_from_blob(bool shared) +{ + return ubus_msg_new(b.head, blob_raw_len(b.head), shared); +} + +static struct ubus_msg_buf *ubus_reply_from_blob(struct ubus_msg_buf *ub, bool shared) +{ + struct ubus_msg_buf *new; + + new = ubus_msg_from_blob(shared); + if (!new) + return NULL; + + ubus_msg_init(new, UBUS_MSG_DATA, ub->hdr.seq, ub->hdr.peer); + return new; +} + +static void +ubus_send_msg_from_blob(struct ubus_client *cl, struct ubus_msg_buf *ub, + uint8_t type) +{ + ub = ubus_reply_from_blob(ub, true); + if (!ub) + return; + + ub->hdr.type = type; + ubus_msg_send(cl, ub, true); +} + +static bool ubusd_send_hello(struct ubus_client *cl) +{ + struct ubus_msg_buf *ub; + + blob_buf_init(&b, 0); + ub = ubus_msg_from_blob(true); + if (!ub) + return false; + + ubus_msg_init(ub, UBUS_MSG_HELLO, 0, cl->id.id); + ubus_msg_send(cl, ub, true); + return true; +} + +static int ubusd_send_pong(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + ub->hdr.type = UBUS_MSG_DATA; + ubus_msg_send(cl, ub, false); + return 0; +} + +static int ubusd_handle_remove_object(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj; + + if (!attr[UBUS_ATTR_OBJID]) + return UBUS_STATUS_INVALID_ARGUMENT; + + obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!obj) + return UBUS_STATUS_NOT_FOUND; + + if (obj->client != cl) + return UBUS_STATUS_PERMISSION_DENIED; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); + + /* check if we're removing the object type as well */ + if (obj->type && obj->type->refcount == 1) + blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id.id); + + ubusd_free_object(obj); + ubus_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); + + return 0; +} + +static int ubusd_handle_add_object(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj; + + obj = ubusd_create_object(cl, attr); + if (!obj) + return UBUS_STATUS_INVALID_ARGUMENT; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); + if (attr[UBUS_ATTR_SIGNATURE]) + blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id.id); + + ubus_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); + return 0; +} + +static void ubusd_send_obj(struct ubus_client *cl, struct ubus_msg_buf *ub, struct ubus_object *obj) +{ + struct ubus_method *m; + void *s; + + blob_buf_init(&b, 0); + + if (obj->path.key) + blob_put_string(&b, UBUS_ATTR_OBJPATH, obj->path.key); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); + blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id.id); + + s = blob_nest_start(&b, UBUS_ATTR_SIGNATURE); + list_for_each_entry(m, &obj->type->methods, list) + blobmsg_add_blob(&b, m->data); + blob_nest_end(&b, s); + + ubus_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); +} + +static int ubusd_handle_lookup(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj; + char *objpath; + bool found = false; + int len; + + if (!attr[UBUS_ATTR_OBJPATH]) { + avl_for_each_element(&path, obj, path) + ubusd_send_obj(cl, ub, obj); + return 0; + } + + objpath = blob_data(attr[UBUS_ATTR_OBJPATH]); + len = strlen(objpath); + if (objpath[len - 1] != '*') { + obj = avl_find_element(&path, objpath, obj, path); + if (!obj) + return UBUS_STATUS_NOT_FOUND; + + ubusd_send_obj(cl, ub, obj); + return 0; + } + + objpath[--len] = 0; + + obj = avl_find_ge_element(&path, objpath, obj, path); + if (!obj) + return UBUS_STATUS_NOT_FOUND; + + while (!strncmp(objpath, obj->path.key, len)) { + found = true; + ubusd_send_obj(cl, ub, obj); + if (obj == avl_last_element(&path, obj, path)) + break; + obj = avl_next_element(obj, path); + } + + if (!found) + return UBUS_STATUS_NOT_FOUND; + + return 0; +} + +static void +ubusd_forward_invoke(struct ubus_object *obj, const char *method, + struct ubus_msg_buf *ub, struct blob_attr *data) +{ + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); + blob_put_string(&b, UBUS_ATTR_METHOD, method); + if (data) + blob_put(&b, UBUS_ATTR_DATA, blob_data(data), blob_len(data)); + + ubus_send_msg_from_blob(obj->client, ub, UBUS_MSG_INVOKE); +} + +static int ubusd_handle_invoke(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj = NULL; + struct ubus_id *id; + const char *method; + + if (!attr[UBUS_ATTR_METHOD] || !attr[UBUS_ATTR_OBJID]) + return UBUS_STATUS_INVALID_ARGUMENT; + + id = ubus_find_id(&objects, blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!id) + return UBUS_STATUS_NOT_FOUND; + + obj = container_of(id, struct ubus_object, id); + + method = blob_data(attr[UBUS_ATTR_METHOD]); + + if (!obj->client) + return obj->recv_msg(cl, method, attr[UBUS_ATTR_DATA]); + + ub->hdr.peer = cl->id.id; + blob_buf_init(&b, 0); + ubusd_forward_invoke(obj, method, ub, attr[UBUS_ATTR_DATA]); + ubus_msg_free(ub); + + return -1; +} + +static int ubusd_handle_notify(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj = NULL; + struct ubus_subscription *s; + struct ubus_id *id; + const char *method; + bool no_reply = false; + void *c; + + if (!attr[UBUS_ATTR_METHOD] || !attr[UBUS_ATTR_OBJID]) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (attr[UBUS_ATTR_NO_REPLY]) + no_reply = blob_get_int8(attr[UBUS_ATTR_NO_REPLY]); + + id = ubus_find_id(&objects, blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!id) + return UBUS_STATUS_NOT_FOUND; + + obj = container_of(id, struct ubus_object, id); + if (obj->client != cl) + return UBUS_STATUS_PERMISSION_DENIED; + + if (!no_reply) { + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, id->id); + c = blob_nest_start(&b, UBUS_ATTR_SUBSCRIBERS); + list_for_each_entry(s, &obj->subscribers, list) { + blob_put_int32(&b, 0, s->subscriber->id.id); + } + blob_nest_end(&b, c); + blob_put_int32(&b, UBUS_ATTR_STATUS, 0); + ubus_send_msg_from_blob(cl, ub, UBUS_MSG_STATUS); + } + + ub->hdr.peer = cl->id.id; + method = blob_data(attr[UBUS_ATTR_METHOD]); + list_for_each_entry(s, &obj->subscribers, list) { + blob_buf_init(&b, 0); + if (no_reply) + blob_put_int8(&b, UBUS_ATTR_NO_REPLY, 1); + ubusd_forward_invoke(s->subscriber, method, ub, attr[UBUS_ATTR_DATA]); + } + ubus_msg_free(ub); + + return -1; +} + +static struct ubus_client *ubusd_get_client_by_id(uint32_t id) +{ + struct ubus_id *clid; + + clid = ubus_find_id(&clients, id); + if (!clid) + return NULL; + + return container_of(clid, struct ubus_client, id); +} + +static int ubusd_handle_response(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj; + + if (!attr[UBUS_ATTR_OBJID] || + (ub->hdr.type == UBUS_MSG_STATUS && !attr[UBUS_ATTR_STATUS]) || + (ub->hdr.type == UBUS_MSG_DATA && !attr[UBUS_ATTR_DATA])) + goto error; + + obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!obj) + goto error; + + if (cl != obj->client) + goto error; + + cl = ubusd_get_client_by_id(ub->hdr.peer); + if (!cl) + goto error; + + ub->hdr.peer = blob_get_u32(attr[UBUS_ATTR_OBJID]); + ubus_msg_send(cl, ub, true); + return -1; + +error: + ubus_msg_free(ub); + return -1; +} + +static int ubusd_handle_add_watch(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj, *target; + + if (!attr[UBUS_ATTR_OBJID] || !attr[UBUS_ATTR_TARGET]) + return UBUS_STATUS_INVALID_ARGUMENT; + + obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!obj) + return UBUS_STATUS_NOT_FOUND; + + if (cl != obj->client) + return UBUS_STATUS_INVALID_ARGUMENT; + + target = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_TARGET])); + if (!target) + return UBUS_STATUS_NOT_FOUND; + + if (cl == target->client) + return UBUS_STATUS_INVALID_ARGUMENT; + + ubus_subscribe(obj, target); + return 0; +} + +static int ubusd_handle_remove_watch(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) +{ + struct ubus_object *obj; + struct ubus_subscription *s; + uint32_t id; + + if (!attr[UBUS_ATTR_OBJID] || !attr[UBUS_ATTR_TARGET]) + return UBUS_STATUS_INVALID_ARGUMENT; + + obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID])); + if (!obj) + return UBUS_STATUS_NOT_FOUND; + + if (cl != obj->client) + return UBUS_STATUS_INVALID_ARGUMENT; + + id = blob_get_u32(attr[UBUS_ATTR_TARGET]); + list_for_each_entry(s, &obj->target_list, target_list) { + if (s->target->id.id != id) + continue; + + ubus_unsubscribe(s); + return 0; + } + + return UBUS_STATUS_NOT_FOUND; +} + +static const ubus_cmd_cb handlers[__UBUS_MSG_LAST] = { + [UBUS_MSG_PING] = ubusd_send_pong, + [UBUS_MSG_ADD_OBJECT] = ubusd_handle_add_object, + [UBUS_MSG_REMOVE_OBJECT] = ubusd_handle_remove_object, + [UBUS_MSG_LOOKUP] = ubusd_handle_lookup, + [UBUS_MSG_INVOKE] = ubusd_handle_invoke, + [UBUS_MSG_STATUS] = ubusd_handle_response, + [UBUS_MSG_DATA] = ubusd_handle_response, + [UBUS_MSG_SUBSCRIBE] = ubusd_handle_add_watch, + [UBUS_MSG_UNSUBSCRIBE] = ubusd_handle_remove_watch, + [UBUS_MSG_NOTIFY] = ubusd_handle_notify, +}; + +void ubusd_proto_receive_message(struct ubus_client *cl, struct ubus_msg_buf *ub) +{ + ubus_cmd_cb cb = NULL; + int ret; + + retmsg->hdr.seq = ub->hdr.seq; + retmsg->hdr.peer = ub->hdr.peer; + + if (ub->hdr.type < __UBUS_MSG_LAST) + cb = handlers[ub->hdr.type]; + + if (ub->hdr.type != UBUS_MSG_STATUS) + ubus_msg_close_fd(ub); + + if (cb) + ret = cb(cl, ub, ubus_parse_msg(ub->data)); + else + ret = UBUS_STATUS_INVALID_COMMAND; + + if (ret == -1) + return; + + ubus_msg_free(ub); + + *retmsg_data = htonl(ret); + ubus_msg_send(cl, retmsg, false); +} + +struct ubus_client *ubusd_proto_new_client(int fd, uloop_fd_handler cb) +{ + struct ubus_client *cl; + + cl = calloc(1, sizeof(*cl)); + if (!cl) + return NULL; + + INIT_LIST_HEAD(&cl->objects); + cl->sock.fd = fd; + cl->sock.cb = cb; + cl->pending_msg_fd = -1; + + if (!ubus_alloc_id(&clients, &cl->id, 0)) + goto free; + + if (!ubusd_send_hello(cl)) + goto delete; + + return cl; + +delete: + ubus_free_id(&clients, &cl->id); +free: + free(cl); + return NULL; +} + +void ubusd_proto_free_client(struct ubus_client *cl) +{ + struct ubus_object *obj; + + while (!list_empty(&cl->objects)) { + obj = list_first_entry(&cl->objects, struct ubus_object, list); + ubusd_free_object(obj); + } + + ubus_free_id(&clients, &cl->id); +} + +void ubus_notify_subscription(struct ubus_object *obj) +{ + bool active = !list_empty(&obj->subscribers); + struct ubus_msg_buf *ub; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); + blob_put_int8(&b, UBUS_ATTR_ACTIVE, active); + + ub = ubus_msg_from_blob(false); + ubus_msg_init(ub, UBUS_MSG_NOTIFY, ++obj->invoke_seq, 0); + ubus_msg_send(obj->client, ub, true); +} + +void ubus_notify_unsubscribe(struct ubus_subscription *s) +{ + struct ubus_msg_buf *ub; + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_OBJID, s->subscriber->id.id); + blob_put_int32(&b, UBUS_ATTR_TARGET, s->target->id.id); + + ub = ubus_msg_from_blob(false); + ubus_msg_init(ub, UBUS_MSG_UNSUBSCRIBE, ++s->subscriber->invoke_seq, 0); + ubus_msg_send(s->subscriber->client, ub, true); + + ubus_unsubscribe(s); +} + +static void __constructor ubusd_proto_init(void) +{ + ubus_init_id_tree(&clients); + + blob_buf_init(&b, 0); + blob_put_int32(&b, UBUS_ATTR_STATUS, 0); + + retmsg = ubus_msg_from_blob(false); + if (!retmsg) + exit(1); + + retmsg->hdr.type = UBUS_MSG_STATUS; + retmsg_data = blob_data(blob_data(retmsg->data)); +} diff --git a/3P/ubus/ubusmsg.h b/3P/ubus/ubusmsg.h new file mode 100644 index 00000000..0a27b42a --- /dev/null +++ b/3P/ubus/ubusmsg.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2011 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __UBUSMSG_H +#define __UBUSMSG_H + +#include +#include + +#define __packetdata __attribute__((packed)) __attribute__((__aligned__(4))) + +#define UBUS_MSG_CHUNK_SIZE 65536 + +#define UBUS_SYSTEM_OBJECT_EVENT 1 +#define UBUS_SYSTEM_OBJECT_MAX 1024 + +struct ubus_msghdr { + uint8_t version; + uint8_t type; + uint16_t seq; + uint32_t peer; +} __packetdata; + +enum ubus_msg_type { + /* initial server message */ + UBUS_MSG_HELLO, + + /* generic command response */ + UBUS_MSG_STATUS, + + /* data message response */ + UBUS_MSG_DATA, + + /* ping request */ + UBUS_MSG_PING, + + /* look up one or more objects */ + UBUS_MSG_LOOKUP, + + /* invoke a method on a single object */ + UBUS_MSG_INVOKE, + + UBUS_MSG_ADD_OBJECT, + UBUS_MSG_REMOVE_OBJECT, + + /* + * subscribe/unsubscribe to object notifications + * The unsubscribe message is sent from ubusd when + * the object disappears + */ + UBUS_MSG_SUBSCRIBE, + UBUS_MSG_UNSUBSCRIBE, + + /* + * send a notification to all subscribers of an object. + * when sent from the server, it indicates a subscription + * status change + */ + UBUS_MSG_NOTIFY, + + /* must be last */ + __UBUS_MSG_LAST, +}; + +enum ubus_msg_attr { + UBUS_ATTR_UNSPEC, + + UBUS_ATTR_STATUS, + + UBUS_ATTR_OBJPATH, + UBUS_ATTR_OBJID, + UBUS_ATTR_METHOD, + + UBUS_ATTR_OBJTYPE, + UBUS_ATTR_SIGNATURE, + + UBUS_ATTR_DATA, + UBUS_ATTR_TARGET, + + UBUS_ATTR_ACTIVE, + UBUS_ATTR_NO_REPLY, + + UBUS_ATTR_SUBSCRIBERS, + + /* must be last */ + UBUS_ATTR_MAX, +}; + +enum ubus_msg_status { + UBUS_STATUS_OK, + UBUS_STATUS_INVALID_COMMAND, + UBUS_STATUS_INVALID_ARGUMENT, + UBUS_STATUS_METHOD_NOT_FOUND, + UBUS_STATUS_NOT_FOUND, + UBUS_STATUS_NO_DATA, + UBUS_STATUS_PERMISSION_DENIED, + UBUS_STATUS_TIMEOUT, + UBUS_STATUS_NOT_SUPPORTED, + UBUS_STATUS_UNKNOWN_ERROR, + UBUS_STATUS_CONNECTION_FAILED, + __UBUS_STATUS_LAST +}; + +#endif diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..10bfb546 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,31 @@ +# CMakeLists files in this project can +# refer to the root source directory of the project as ${HELLO_SOURCE_DIR} and +# to the root binary directory of the project as ${HELLO_BINARY_DIR}. +cmake_minimum_required (VERSION 2.8.11) + +project (Domo) + +set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/build/bin") +set(LIBRARY_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/build/lib") +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake-modules/") + +add_subdirectory (3P) +#add_subdirectory (lib) +#add_subdirectory (src) + + +add_custom_target (deploy + COMMAND echo "Deploying to ${PI}" + + COMMAND mkdir -p ${CMAKE_SOURCE_DIR}/build/install + COMMAND mkdir -p ${CMAKE_SOURCE_DIR}/build/install/bin/ + COMMAND mkdir -p ${CMAKE_SOURCE_DIR}/build/install/lib/ + + #COMMAND cp ${CMAKE_SOURCE_DIR}/scripts/Domo.sh ${CMAKE_SOURCE_DIR}/build/install + COMMAND cp ${CMAKE_SOURCE_DIR}/build/bin/* ${CMAKE_SOURCE_DIR}/build/install/bin/ + COMMAND cp ${CMAKE_SOURCE_DIR}/build/lib/*.so ${CMAKE_SOURCE_DIR}/build/install/lib/ + COMMAND cp -a ${CMAKE_SOURCE_DIR}/build/html ${CMAKE_SOURCE_DIR}/build/install/ + COMMAND cp -a ${CMAKE_SOURCE_DIR}/build/rsc ${CMAKE_SOURCE_DIR}/build/install/ + + COMMAND rsync -av --delete ${CMAKE_SOURCE_DIR}/build/install/* root@${PI}:/opt/Domo/ +) diff --git a/README.md b/README.md index 420006f0..e9cf1291 100644 --- a/README.md +++ b/README.md @@ -22,3 +22,6 @@ https://github.com/xpepermint/angular-ui-switch UI en fullscreen sur iOS. http://blog.initlabs.com/post/81716286465/how-to-display-websites-in-fullscreen-mode-in-ios7 + + +http://git.openwrt.org/?p=project/ubus.git diff --git a/build.sh b/build.sh new file mode 100755 index 00000000..7ad887e7 --- /dev/null +++ b/build.sh @@ -0,0 +1,11 @@ +#!/bin/sh +if [ -e build ]; then +echo "Clean" +rm -rf build/* +fi + +PI_ADDR=192.168.1.6 + +cd build +cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain-RaspberryPi.cmake -DPI=$PI_ADDR .. +make diff --git a/cmake-modules/alarmd.cmake b/cmake-modules/alarmd.cmake new file mode 100644 index 00000000..9ef7e5bd --- /dev/null +++ b/cmake-modules/alarmd.cmake @@ -0,0 +1,2 @@ + +set(ALARM_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/lib/alarmes") diff --git a/cmake-modules/libcivetweb.cmake b/cmake-modules/libcivetweb.cmake new file mode 100644 index 00000000..54c85402 --- /dev/null +++ b/cmake-modules/libcivetweb.cmake @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8.11) + +ADD_DEFINITIONS(-DLINUX -DUSE_STACK_SIZE=102400) +ADD_DEFINITIONS(-DUSE_WEBSOCKET) +ADD_DEFINITIONS(-DUSE_WEBSOCKET) +ADD_DEFINITIONS(-DNO_CGI) + +set(CIVETWEB_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/3P/civetweb/include") + +include_directories(${CIVETWEB_INCLUDE_PATH}) + diff --git a/cmake-modules/libjson-c.cmake b/cmake-modules/libjson-c.cmake new file mode 100644 index 00000000..5d18a683 --- /dev/null +++ b/cmake-modules/libjson-c.cmake @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 2.8.11) + +set(JSON_C_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/3P/json") diff --git a/cmake-modules/libjsoncpp.cmake b/cmake-modules/libjsoncpp.cmake new file mode 100644 index 00000000..3c491121 --- /dev/null +++ b/cmake-modules/libjsoncpp.cmake @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 2.8.11) + +set(JSON_CPP_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/3P/jsoncpp/include") + +include_directories(${JSON_CPP_INCLUDE_PATH}) diff --git a/cmake-modules/libubox.cmake b/cmake-modules/libubox.cmake new file mode 100644 index 00000000..c92834f0 --- /dev/null +++ b/cmake-modules/libubox.cmake @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 2.8.11) + +set(UBOX_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/3P/") + +include_directories(${UBOX_INCLUDE_PATH}) + diff --git a/cmake-modules/libubus.cmake b/cmake-modules/libubus.cmake new file mode 100644 index 00000000..e4881b9b --- /dev/null +++ b/cmake-modules/libubus.cmake @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.8.11) + +SET(UBUS_UNIX_SOCKET "/var/run/ubus.sock") +SET(UBUS_MAX_MSGLEN 1048576) + +ADD_DEFINITIONS( -DUBUS_UNIX_SOCKET="${UBUS_UNIX_SOCKET}") +ADD_DEFINITIONS( -DUBUS_MAX_MSGLEN=${UBUS_MAX_MSGLEN}) + +set(UBUS_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/3P/ubus") + +include_directories(${UBUS_INCLUDE_PATH}) + diff --git a/cmake-modules/libubuscpp.cmake b/cmake-modules/libubuscpp.cmake new file mode 100644 index 00000000..9f242d8a --- /dev/null +++ b/cmake-modules/libubuscpp.cmake @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 2.8.11) + +set(UBUS_CPP_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/lib/ubuscpp/include") + +include_directories(${UBUS_CPP_INCLUDE_PATH}) diff --git a/cmake-modules/libwolfssl.cmake b/cmake-modules/libwolfssl.cmake new file mode 100644 index 00000000..eade7c30 --- /dev/null +++ b/cmake-modules/libwolfssl.cmake @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.8.11) + +ADD_DEFINITIONS(-D_POSIX_THREADS -DHAVE_THREAD_LS -DNDEBUG -pthread -DNO_DSA -DNO_PSK -DNO_DH -DNO_MD4 -DNO_HC128 -DNO_RABBIT -DHAVE_HASHDRBG -DNO_PWDBASED -DUSE_FAST_MATH) + + +ADD_DEFINITIONS(-DOPENSSL_EXTRA -DHAVE_ERRNO_H -DHAVE_GETHOSTBYNAME -DHAVE_GETHOSTBYNAME + -DHAVE_INET_NTOA -DHAVE_LIMITS_H -DHAVE_MEMSET -DHAVE_SOCKET -DHAVE_STDDEF_H + -DHAVE_STDLIB_H -DHAVE_STRING_H -DHAVE_SYS_STAT_H -DHAVE_SYS_TYPES_H) + +set(WOLFSSL_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/3P/wolfssl/" "${CMAKE_SOURCE_DIR}/3P/wolfssl/wolfssl") + +include_directories(${WOLFSSL_INCLUDE_PATH}) diff --git a/cmake-modules/wiringPi.cmake b/cmake-modules/wiringPi.cmake new file mode 100644 index 00000000..84decd4a --- /dev/null +++ b/cmake-modules/wiringPi.cmake @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 2.8.11) + +set(WIRINGPI_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/3P/wiringPi/wiringPi") + +include_directories(${WIRINGPI_INCLUDE_PATH}) diff --git a/cmake-modules/wiringPiDev.cmake b/cmake-modules/wiringPiDev.cmake new file mode 100644 index 00000000..35885976 --- /dev/null +++ b/cmake-modules/wiringPiDev.cmake @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 2.8.11) + +set(WIRINGPIDEV_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/3P/wiringPi/devLib") diff --git a/cmake/Toolchain-Local.cmake b/cmake/Toolchain-Local.cmake new file mode 100644 index 00000000..bf8c6a1c --- /dev/null +++ b/cmake/Toolchain-Local.cmake @@ -0,0 +1,12 @@ +# this one is important +SET(CMAKE_SYSTEM_NAME Linux) +#this one not so much +SET(CMAKE_SYSTEM_VERSION 1) + +# specify the cross compiler + +# search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/Toolchain-RaspberryPi.cmake b/cmake/Toolchain-RaspberryPi.cmake new file mode 100644 index 00000000..66b19d9d --- /dev/null +++ b/cmake/Toolchain-RaspberryPi.cmake @@ -0,0 +1,24 @@ +# this one is important +SET(CMAKE_SYSTEM_NAME Linux) + +# +#this one not so much +SET(CMAKE_SYSTEM_VERSION 1) + +# specify the cross compiler + +SET(CMAKE_C_COMPILER + /home/jbnadal/sources/Domo/firmware/board/raspberrypi/cross/usr/bin/arm-buildroot-linux-gnueabihf-gcc) +SET(CMAKE_CXX_COMPILER + /home/jbnadal/sources/Domo/firmware/board/raspberrypi/cross/usr/bin/arm-buildroot-linux-gnueabihf-g++) + +# OSX +# SET(CMAKE_SYSTEM_NAME Linux) +# SET(CMAKE_C_COMPILER /Users/jbnadal/xtools/rpi/bin/arm-linux-gnueabihf-gcc) +# SET(CMAKE_CXX_COMPILER /Users/jbnadal/xtools/rpi/bin/arm-linux-gnueabihf-g++) + +# search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)