From 6bc8cdeef306ad4d3ac6912dfc4fcd3f405e87f4 Mon Sep 17 00:00:00 2001 From: NADAL Jean-Baptiste Date: Fri, 26 Feb 2016 22:16:27 +0100 Subject: [PATCH] Import ubus tools. --- 3P/CMakeLists.txt | 14 + 3P/json/.gitignore | 49 + 3P/json/0.11 | 0 3P/json/AUTHORS | 5 + 3P/json/Android.configure.mk | 39 + 3P/json/COPYING | 42 + 3P/json/ChangeLog | 175 +++ 3P/json/Doxyfile | 1153 +++++++++++++++++ 3P/json/Makefile.am | 92 ++ 3P/json/Makefile.am.inc | 2 + 3P/json/NEWS | 0 3P/json/README | 44 + 3P/json/README-WIN32.html | 50 + 3P/json/README.html | 34 + 3P/json/RELEASE_CHECKLIST.txt | 61 + 3P/json/arraylist.c | 101 ++ 3P/json/arraylist.h | 56 + 3P/json/autogen.sh | 13 + 3P/json/bits.h | 28 + 3P/json/builders/cmake/CMakeLists.txt | 29 + 3P/json/builders/linux-gcc/Makefile | 31 + 3P/json/config.h.in | 148 +++ 3P/json/config.h.win32 | 94 ++ 3P/json/configure.in | 69 + 3P/json/debug.c | 98 ++ 3P/json/debug.h | 72 + 3P/json/json-c-uninstalled.pc.in | 11 + 3P/json/json-c.pc.in | 11 + 3P/json/json-c.vcproj | 179 +++ 3P/json/json.h | 34 + 3P/json/json.pc.in | 11 + 3P/json/json_c_version.c | 20 + 3P/json/json_c_version.h | 22 + 3P/json/json_config.h.in | 3 + 3P/json/json_inttypes.h | 28 + 3P/json/json_object.c | 782 +++++++++++ 3P/json/json_object.h | 562 ++++++++ 3P/json/json_object_iterator.c | 168 +++ 3P/json/json_object_iterator.h | 239 ++++ 3P/json/json_object_private.h | 47 + 3P/json/json_tokener.c | 792 +++++++++++ 3P/json/json_tokener.h | 209 +++ 3P/json/json_util.c | 299 +++++ 3P/json/json_util.h | 41 + 3P/json/libjson.c | 26 + 3P/json/linkhash.c | 233 ++++ 3P/json/linkhash.h | 292 +++++ 3P/json/printbuf.c | 192 +++ 3P/json/printbuf.h | 77 ++ 3P/json/tests/Makefile.am | 59 + 3P/json/tests/parse_flags.c | 50 + 3P/json/tests/parse_flags.h | 4 + 3P/json/tests/parse_int64.test | 12 + 3P/json/tests/test-defs.sh | 128 ++ 3P/json/tests/test1.c | 134 ++ 3P/json/tests/test1.expected | 36 + 3P/json/tests/test1.test | 22 + 3P/json/tests/test1Formatted_plain.expected | 36 + 3P/json/tests/test1Formatted_pretty.expected | 59 + 3P/json/tests/test1Formatted_spaced.expected | 36 + 3P/json/tests/test2.c | 34 + 3P/json/tests/test2.expected | 1 + 3P/json/tests/test2.test | 22 + 3P/json/tests/test2Formatted_plain.expected | 1 + 3P/json/tests/test2Formatted_pretty.expected | 23 + 3P/json/tests/test2Formatted_spaced.expected | 1 + 3P/json/tests/test4.c | 53 + 3P/json/tests/test4.expected | 3 + 3P/json/tests/test4.test | 12 + 3P/json/tests/testReplaceExisting.c | 78 ++ 3P/json/tests/testReplaceExisting.expected | 15 + 3P/json/tests/testReplaceExisting.test | 12 + 3P/json/tests/test_cast.c | 106 ++ 3P/json/tests/test_cast.expected | 56 + 3P/json/tests/test_cast.test | 12 + 3P/json/tests/test_locale.c | 31 + 3P/json/tests/test_locale.expected | 2 + 3P/json/tests/test_locale.test | 12 + 3P/json/tests/test_null.c | 57 + 3P/json/tests/test_null.expected | 3 + 3P/json/tests/test_null.test | 12 + 3P/json/tests/test_parse.c | 290 +++++ 3P/json/tests/test_parse.expected | 57 + 3P/json/tests/test_parse.test | 12 + 3P/json/tests/test_parse_int64.c | 115 ++ 3P/json/tests/test_parse_int64.expected | 29 + 3P/json/tests/test_printbuf.c | 166 +++ 3P/json/tests/test_printbuf.expected | 32 + 3P/json/tests/test_printbuf.test | 12 + 3P/json/tests/test_set_serializer.c | 71 + 3P/json/tests/test_set_serializer.expected | 10 + 3P/json/tests/test_set_serializer.test | 12 + 3P/libubox/.gitignore | 10 + 3P/libubox/avl-cmp.c | 24 + 3P/libubox/avl-cmp.h | 21 + 3P/libubox/avl.c | 735 +++++++++++ 3P/libubox/avl.h | 560 ++++++++ 3P/libubox/blob.c | 287 ++++ 3P/libubox/blob.h | 257 ++++ 3P/libubox/blobmsg.c | 320 +++++ 3P/libubox/blobmsg.h | 241 ++++ 3P/libubox/blobmsg_json.c | 321 +++++ 3P/libubox/blobmsg_json.h | 49 + 3P/libubox/builders/cmake/CMakeLists.txt | 43 + 3P/libubox/examples/CMakeLists.txt | 24 + 3P/libubox/examples/blobmsg-example.c | 139 ++ 3P/libubox/examples/runqueue-example.c | 112 ++ 3P/libubox/examples/uloop-example.lua | 78 ++ 3P/libubox/examples/uloop_pid_test.sh | 11 + 3P/libubox/examples/ustream-example.c | 148 +++ 3P/libubox/jshn.c | 308 +++++ 3P/libubox/json_script.c | 651 ++++++++++ 3P/libubox/json_script.h | 123 ++ 3P/libubox/kvlist.c | 96 ++ 3P/libubox/kvlist.h | 54 + 3P/libubox/list.h | 208 +++ 3P/libubox/lua/CMakeLists.txt | 52 + .../uloop.dep | 8 + .../uloop.o | Bin 0 -> 101140 bytes .../uloop.dep | 8 + .../uloop.o | Bin 0 -> 13392 bytes .../uloop.dep | 8 + .../_rt3052d_StriimSOUND-debug-shared/uloop.o | Bin 0 -> 101588 bytes .../uloop.dep | 8 + .../uloop.o | Bin 0 -> 13468 bytes 3P/libubox/lua/uloop.c | 419 ++++++ 3P/libubox/md5.c | 284 ++++ 3P/libubox/md5.h | 36 + 3P/libubox/runqueue.c | 282 ++++ 3P/libubox/runqueue.h | 115 ++ 3P/libubox/safe_list.c | 121 ++ 3P/libubox/safe_list.h | 62 + 3P/libubox/sh/jshn.sh | 283 ++++ 3P/libubox/uloop.c | 680 ++++++++++ 3P/libubox/uloop.h | 109 ++ 3P/libubox/usock.c | 119 ++ 3P/libubox/usock.h | 35 + 3P/libubox/ustream-fd.c | 163 +++ 3P/libubox/ustream.c | 514 ++++++++ 3P/libubox/ustream.h | 214 +++ 3P/libubox/utils.c | 94 ++ 3P/libubox/utils.h | 182 +++ 3P/libubox/vlist.c | 82 ++ 3P/libubox/vlist.h | 75 ++ 3P/ubus/.gitignore | 12 + 3P/ubus/CMakeLists.txt | 57 + 3P/ubus/cli.c | 381 ++++++ 3P/ubus/examples/CMakeLists.txt | 12 + 3P/ubus/examples/client.c | 239 ++++ 3P/ubus/examples/count.c | 48 + 3P/ubus/examples/count.h | 19 + 3P/ubus/examples/server.c | 260 ++++ 3P/ubus/libubus-internal.h | 32 + 3P/ubus/libubus-io.c | 409 ++++++ 3P/ubus/libubus-obj.c | 257 ++++ 3P/ubus/libubus-req.c | 468 +++++++ 3P/ubus/libubus-sub.c | 69 + 3P/ubus/libubus.c | 359 +++++ 3P/ubus/libubus.h | 327 +++++ 3P/ubus/libubus/builders/cmake/CMakeLists.txt | 36 + 3P/ubus/lua/CMakeLists.txt | 52 + .../ubus.dep | 31 + .../ubus.o | Bin 0 -> 140404 bytes .../ubus.dep | 31 + .../ubus.o | Bin 0 -> 18452 bytes .../ubus.dep | 31 + .../_rt3052d_StriimSOUND-debug-shared/ubus.o | Bin 0 -> 140788 bytes .../ubus.dep | 31 + .../ubus.o | Bin 0 -> 18452 bytes 3P/ubus/lua/test.lua | 54 + 3P/ubus/lua/test_client.lua | 41 + 3P/ubus/lua/ubus.c | 742 +++++++++++ 3P/ubus/systemd/CMakeLists.txt | 7 + 3P/ubus/systemd/ubus.service.in | 6 + 3P/ubus/systemd/ubus.socket.in | 8 + 3P/ubus/ubus_common.h | 24 + 3P/ubus/ubusd.c | 394 ++++++ 3P/ubus/ubusd.h | 74 ++ 3P/ubus/ubusd_event.c | 272 ++++ 3P/ubus/ubusd_id.c | 71 + 3P/ubus/ubusd_id.h | 45 + 3P/ubus/ubusd_obj.c | 228 ++++ 3P/ubus/ubusd_obj.h | 83 ++ 3P/ubus/ubusd_proto.c | 522 ++++++++ 3P/ubus/ubusmsg.h | 114 ++ CMakeLists.txt | 31 + README.md | 3 + build.sh | 11 + cmake-modules/alarmd.cmake | 2 + cmake-modules/libcivetweb.cmake | 11 + cmake-modules/libjson-c.cmake | 3 + cmake-modules/libjsoncpp.cmake | 5 + cmake-modules/libubox.cmake | 6 + cmake-modules/libubus.cmake | 12 + cmake-modules/libubuscpp.cmake | 5 + cmake-modules/libwolfssl.cmake | 12 + cmake-modules/wiringPi.cmake | 5 + cmake-modules/wiringPiDev.cmake | 3 + cmake/Toolchain-Local.cmake | 12 + cmake/Toolchain-RaspberryPi.cmake | 24 + 200 files changed, 23469 insertions(+) create mode 100644 3P/CMakeLists.txt create mode 100644 3P/json/.gitignore create mode 100644 3P/json/0.11 create mode 100644 3P/json/AUTHORS create mode 100644 3P/json/Android.configure.mk create mode 100644 3P/json/COPYING create mode 100644 3P/json/ChangeLog create mode 100644 3P/json/Doxyfile create mode 100644 3P/json/Makefile.am create mode 100644 3P/json/Makefile.am.inc create mode 100644 3P/json/NEWS create mode 100644 3P/json/README create mode 100644 3P/json/README-WIN32.html create mode 100644 3P/json/README.html create mode 100644 3P/json/RELEASE_CHECKLIST.txt create mode 100644 3P/json/arraylist.c create mode 100644 3P/json/arraylist.h create mode 100755 3P/json/autogen.sh create mode 100644 3P/json/bits.h create mode 100644 3P/json/builders/cmake/CMakeLists.txt create mode 100644 3P/json/builders/linux-gcc/Makefile create mode 100644 3P/json/config.h.in create mode 100644 3P/json/config.h.win32 create mode 100644 3P/json/configure.in create mode 100644 3P/json/debug.c create mode 100644 3P/json/debug.h create mode 100644 3P/json/json-c-uninstalled.pc.in create mode 100644 3P/json/json-c.pc.in create mode 100644 3P/json/json-c.vcproj create mode 100644 3P/json/json.h create mode 100644 3P/json/json.pc.in create mode 100644 3P/json/json_c_version.c create mode 100644 3P/json/json_c_version.h create mode 100644 3P/json/json_config.h.in create mode 100644 3P/json/json_inttypes.h create mode 100644 3P/json/json_object.c create mode 100644 3P/json/json_object.h create mode 100644 3P/json/json_object_iterator.c create mode 100644 3P/json/json_object_iterator.h create mode 100644 3P/json/json_object_private.h create mode 100644 3P/json/json_tokener.c create mode 100644 3P/json/json_tokener.h create mode 100644 3P/json/json_util.c create mode 100644 3P/json/json_util.h create mode 100644 3P/json/libjson.c create mode 100644 3P/json/linkhash.c create mode 100644 3P/json/linkhash.h create mode 100644 3P/json/printbuf.c create mode 100644 3P/json/printbuf.h create mode 100644 3P/json/tests/Makefile.am create mode 100644 3P/json/tests/parse_flags.c create mode 100644 3P/json/tests/parse_flags.h create mode 100755 3P/json/tests/parse_int64.test create mode 100755 3P/json/tests/test-defs.sh create mode 100644 3P/json/tests/test1.c create mode 100644 3P/json/tests/test1.expected create mode 100755 3P/json/tests/test1.test create mode 100644 3P/json/tests/test1Formatted_plain.expected create mode 100644 3P/json/tests/test1Formatted_pretty.expected create mode 100644 3P/json/tests/test1Formatted_spaced.expected create mode 100644 3P/json/tests/test2.c create mode 100644 3P/json/tests/test2.expected create mode 100755 3P/json/tests/test2.test create mode 100644 3P/json/tests/test2Formatted_plain.expected create mode 100644 3P/json/tests/test2Formatted_pretty.expected create mode 100644 3P/json/tests/test2Formatted_spaced.expected create mode 100644 3P/json/tests/test4.c create mode 100644 3P/json/tests/test4.expected create mode 100755 3P/json/tests/test4.test create mode 100644 3P/json/tests/testReplaceExisting.c create mode 100644 3P/json/tests/testReplaceExisting.expected create mode 100755 3P/json/tests/testReplaceExisting.test create mode 100644 3P/json/tests/test_cast.c create mode 100644 3P/json/tests/test_cast.expected create mode 100755 3P/json/tests/test_cast.test create mode 100644 3P/json/tests/test_locale.c create mode 100644 3P/json/tests/test_locale.expected create mode 100755 3P/json/tests/test_locale.test create mode 100644 3P/json/tests/test_null.c create mode 100644 3P/json/tests/test_null.expected create mode 100755 3P/json/tests/test_null.test create mode 100644 3P/json/tests/test_parse.c create mode 100644 3P/json/tests/test_parse.expected create mode 100755 3P/json/tests/test_parse.test create mode 100644 3P/json/tests/test_parse_int64.c create mode 100644 3P/json/tests/test_parse_int64.expected create mode 100644 3P/json/tests/test_printbuf.c create mode 100644 3P/json/tests/test_printbuf.expected create mode 100755 3P/json/tests/test_printbuf.test create mode 100644 3P/json/tests/test_set_serializer.c create mode 100644 3P/json/tests/test_set_serializer.expected create mode 100755 3P/json/tests/test_set_serializer.test create mode 100644 3P/libubox/.gitignore create mode 100644 3P/libubox/avl-cmp.c create mode 100644 3P/libubox/avl-cmp.h create mode 100644 3P/libubox/avl.c create mode 100644 3P/libubox/avl.h create mode 100644 3P/libubox/blob.c create mode 100644 3P/libubox/blob.h create mode 100644 3P/libubox/blobmsg.c create mode 100644 3P/libubox/blobmsg.h create mode 100644 3P/libubox/blobmsg_json.c create mode 100644 3P/libubox/blobmsg_json.h create mode 100644 3P/libubox/builders/cmake/CMakeLists.txt create mode 100644 3P/libubox/examples/CMakeLists.txt create mode 100644 3P/libubox/examples/blobmsg-example.c create mode 100644 3P/libubox/examples/runqueue-example.c create mode 100755 3P/libubox/examples/uloop-example.lua create mode 100755 3P/libubox/examples/uloop_pid_test.sh create mode 100644 3P/libubox/examples/ustream-example.c create mode 100644 3P/libubox/jshn.c create mode 100644 3P/libubox/json_script.c create mode 100644 3P/libubox/json_script.h create mode 100644 3P/libubox/kvlist.c create mode 100644 3P/libubox/kvlist.h create mode 100644 3P/libubox/list.h create mode 100644 3P/libubox/lua/CMakeLists.txt create mode 100644 3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/uloop.dep create mode 100644 3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/uloop.o create mode 100644 3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/uloop.dep create mode 100644 3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/uloop.o create mode 100644 3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/uloop.dep create mode 100644 3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/uloop.o create mode 100644 3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/uloop.dep create mode 100644 3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/uloop.o create mode 100644 3P/libubox/lua/uloop.c create mode 100644 3P/libubox/md5.c create mode 100644 3P/libubox/md5.h create mode 100644 3P/libubox/runqueue.c create mode 100644 3P/libubox/runqueue.h create mode 100644 3P/libubox/safe_list.c create mode 100644 3P/libubox/safe_list.h create mode 100644 3P/libubox/sh/jshn.sh create mode 100644 3P/libubox/uloop.c create mode 100644 3P/libubox/uloop.h create mode 100644 3P/libubox/usock.c create mode 100644 3P/libubox/usock.h create mode 100644 3P/libubox/ustream-fd.c create mode 100644 3P/libubox/ustream.c create mode 100644 3P/libubox/ustream.h create mode 100644 3P/libubox/utils.c create mode 100644 3P/libubox/utils.h create mode 100644 3P/libubox/vlist.c create mode 100644 3P/libubox/vlist.h create mode 100644 3P/ubus/.gitignore create mode 100644 3P/ubus/CMakeLists.txt create mode 100644 3P/ubus/cli.c create mode 100644 3P/ubus/examples/CMakeLists.txt create mode 100644 3P/ubus/examples/client.c create mode 100644 3P/ubus/examples/count.c create mode 100644 3P/ubus/examples/count.h create mode 100644 3P/ubus/examples/server.c create mode 100644 3P/ubus/libubus-internal.h create mode 100644 3P/ubus/libubus-io.c create mode 100644 3P/ubus/libubus-obj.c create mode 100644 3P/ubus/libubus-req.c create mode 100644 3P/ubus/libubus-sub.c create mode 100644 3P/ubus/libubus.c create mode 100644 3P/ubus/libubus.h create mode 100644 3P/ubus/libubus/builders/cmake/CMakeLists.txt create mode 100644 3P/ubus/lua/CMakeLists.txt create mode 100644 3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/ubus.dep create mode 100644 3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/ubus.o create mode 100644 3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/ubus.dep create mode 100644 3P/ubus/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/ubus.o create mode 100644 3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/ubus.dep create mode 100644 3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/ubus.o create mode 100644 3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/ubus.dep create mode 100644 3P/ubus/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/ubus.o create mode 100755 3P/ubus/lua/test.lua create mode 100755 3P/ubus/lua/test_client.lua create mode 100644 3P/ubus/lua/ubus.c create mode 100644 3P/ubus/systemd/CMakeLists.txt create mode 100644 3P/ubus/systemd/ubus.service.in create mode 100644 3P/ubus/systemd/ubus.socket.in create mode 100644 3P/ubus/ubus_common.h create mode 100644 3P/ubus/ubusd.c create mode 100644 3P/ubus/ubusd.h create mode 100644 3P/ubus/ubusd_event.c create mode 100644 3P/ubus/ubusd_id.c create mode 100644 3P/ubus/ubusd_id.h create mode 100644 3P/ubus/ubusd_obj.c create mode 100644 3P/ubus/ubusd_obj.h create mode 100644 3P/ubus/ubusd_proto.c create mode 100644 3P/ubus/ubusmsg.h create mode 100644 CMakeLists.txt create mode 100755 build.sh create mode 100644 cmake-modules/alarmd.cmake create mode 100644 cmake-modules/libcivetweb.cmake create mode 100644 cmake-modules/libjson-c.cmake create mode 100644 cmake-modules/libjsoncpp.cmake create mode 100644 cmake-modules/libubox.cmake create mode 100644 cmake-modules/libubus.cmake create mode 100644 cmake-modules/libubuscpp.cmake create mode 100644 cmake-modules/libwolfssl.cmake create mode 100644 cmake-modules/wiringPi.cmake create mode 100644 cmake-modules/wiringPiDev.cmake create mode 100644 cmake/Toolchain-Local.cmake create mode 100644 cmake/Toolchain-RaspberryPi.cmake 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 0000000000000000000000000000000000000000..a5dba2ef91c322b922b6c0879cea4ab7fcf8f542 GIT binary patch literal 101140 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f79ef714e40f976b632816d708dae4daf6ad5fd2 GIT binary patch literal 13392 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 biCu=?M~vf#i25x=>~l;t`qQEIJdytej@t|c literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ce9d92ec7914ecaef6f418b03d9fc42e77b4d930 GIT binary patch literal 101588 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 JX!(hq{|%S&^kV=3 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5924e5d8f2cfc2d162d198ff312a45a4ccce3d12 GIT binary patch literal 13468 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 W4-xXMMC@}+H5%tc6b0>hBL54yt|E{C literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..67737c2793d69fda99bf83530bde75ef2c15c5e0 GIT binary patch literal 140404 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�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 G{{I76r?w^l literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8d27c4c57cc4f748a125b062204b8c4c786c550c GIT binary patch literal 18452 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..33d7cbc0aa724c6b245ca75588c8d45a52ec345e GIT binary patch literal 140788 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8749e80781f03896d6ff3300ac1b9767829d6cd2 GIT binary patch literal 18452 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 b3DJq`OMWlm`_2A9ypQ-brXCT8i7Nj;pNTVX literal 0 HcmV?d00001 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)