diff --git a/3P/libubox/.gitignore b/3P/libubox/.gitignore index 0c59f966..5ffd2358 100644 --- a/3P/libubox/.gitignore +++ b/3P/libubox/.gitignore @@ -8,3 +8,4 @@ CMakeFiles install_manifest.txt jshn *-example +tests.* diff --git a/3P/libubox/CMakeLists.txt b/3P/libubox/CMakeLists.txt new file mode 100644 index 00000000..57804cf0 --- /dev/null +++ b/3P/libubox/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 2.6) +INCLUDE(CheckLibraryExists) +INCLUDE(CheckFunctionExists) + +PROJECT(ubox C) +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations) + +OPTION(BUILD_LUA "build Lua plugin" ON) +OPTION(BUILD_EXAMPLES "build examples" ON) + +INCLUDE(FindPkgConfig) +PKG_SEARCH_MODULE(JSONC json-c) +IF(JSONC_FOUND) + ADD_DEFINITIONS(-DJSONC) + INCLUDE_DIRECTORIES(${JSONC_INCLUDE_DIRS}) +ENDIF() + +SET(SOURCES avl.c avl-cmp.c blob.c blobmsg.c uloop.c usock.c ustream.c ustream-fd.c vlist.c utils.c safe_list.c runqueue.c md5.c kvlist.c ulog.c base64.c) + +ADD_LIBRARY(ubox SHARED ${SOURCES}) +ADD_LIBRARY(ubox-static STATIC ${SOURCES}) +SET_TARGET_PROPERTIES(ubox-static PROPERTIES OUTPUT_NAME ubox) + +SET(LIBS) +CHECK_FUNCTION_EXISTS(clock_gettime HAVE_GETTIME) +IF(NOT HAVE_GETTIME) + CHECK_LIBRARY_EXISTS(rt clock_gettime "" NEED_GETTIME) + IF(NEED_GETTIME) + TARGET_LINK_LIBRARIES(ubox rt) + ENDIF() +ENDIF() + +FILE(GLOB headers *.h) +INSTALL(FILES ${headers} + DESTINATION include/libubox +) +INSTALL(TARGETS ubox ubox-static + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +ADD_SUBDIRECTORY(lua) +ADD_SUBDIRECTORY(examples) + +find_library(json NAMES json-c) +IF(EXISTS ${json}) + ADD_LIBRARY(blobmsg_json SHARED blobmsg_json.c) + TARGET_LINK_LIBRARIES(blobmsg_json ubox ${json}) + + ADD_LIBRARY(blobmsg_json-static STATIC blobmsg_json.c) + SET_TARGET_PROPERTIES(blobmsg_json-static + PROPERTIES OUTPUT_NAME blobmsg_json) + + ADD_EXECUTABLE(jshn jshn.c) + TARGET_LINK_LIBRARIES(jshn blobmsg_json ${json}) + + ADD_LIBRARY(json_script SHARED json_script.c) + TARGET_LINK_LIBRARIES(json_script ubox) + + INSTALL(TARGETS blobmsg_json blobmsg_json-static jshn json_script + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + ) + + FILE(GLOB scripts sh/*.sh) + INSTALL(FILES ${scripts} + DESTINATION share/libubox + ) + +ENDIF() diff --git a/3P/libubox/base64.c b/3P/libubox/base64.c new file mode 100644 index 00000000..4186ce84 --- /dev/null +++ b/3P/libubox/base64.c @@ -0,0 +1,327 @@ +/* + * base64 - libubox base64 functions + * + * Copyright (C) 2015 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $OpenBSD: base64.c,v 1.7 2013/12/31 02:32:56 tedu Exp $ */ + +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include +#include +#include +#include +#include "utils.h" + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int b64_encode(const void *_src, size_t srclength, + void *dest, size_t targsize) +{ + const unsigned char *src = _src; + char *target = dest; + size_t datalength = 0; + u_char input[3] = {0}; + u_char output[4]; + int i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int b64_decode(const void *_src, void *dest, size_t targsize) +{ + const char *src = _src; + unsigned char *target = dest; + int tarindex, state, ch; + u_char nextbyte; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = (unsigned char)*src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + nextbyte = ((pos - Base64) & 0x0f) << 4; + if (tarindex + 1 < targsize) + target[tarindex+1] = nextbyte; + else if (nextbyte) + return (-1); + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + nextbyte = ((pos - Base64) & 0x03) << 6; + if (tarindex + 1 < targsize) + target[tarindex+1] = nextbyte; + else if (nextbyte) + return (-1); + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = (unsigned char)*src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for (; ch != '\0'; ch = (unsigned char)*src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = (unsigned char)*src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (; ch != '\0'; ch = (unsigned char)*src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && tarindex < targsize && + target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + /* Null-terminate if we have room left */ + if (tarindex < targsize) + target[tarindex] = 0; + + return (tarindex); +} diff --git a/3P/libubox/blobmsg.c b/3P/libubox/blobmsg.c index 9fe96e44..80b984a1 100644 --- a/3P/libubox/blobmsg.c +++ b/3P/libubox/blobmsg.c @@ -262,7 +262,6 @@ blobmsg_alloc_string_buffer(struct blob_buf *buf, const char *name, unsigned int 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); diff --git a/3P/libubox/blobmsg_json.c b/3P/libubox/blobmsg_json.c index 8f208e0c..2d1d80dd 100644 --- a/3P/libubox/blobmsg_json.c +++ b/3P/libubox/blobmsg_json.c @@ -17,6 +17,12 @@ #include "blobmsg.h" #include "blobmsg_json.h" +#ifdef JSONC + #include +#else + #include +#endif + bool blobmsg_add_object(struct blob_buf *b, json_object *obj) { json_object_object_foreach(obj, key, val) { @@ -43,9 +49,6 @@ bool blobmsg_add_json_element(struct blob_buf *b, const char *name, json_object 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); @@ -66,6 +69,9 @@ bool blobmsg_add_json_element(struct blob_buf *b, const char *name, json_object case json_type_int: blobmsg_add_u32(b, name, json_object_get_int(obj)); break; + case json_type_null: + blobmsg_add_field(b, BLOBMSG_TYPE_UNSPEC, name, NULL, 0); + break; default: return false; } @@ -76,7 +82,7 @@ static bool __blobmsg_add_json(struct blob_buf *b, json_object *obj) { bool ret = false; - if (is_error(obj)) + if (!obj) return false; if (json_object_get_type(obj) != json_type_object) diff --git a/3P/libubox/blobmsg_json.h b/3P/libubox/blobmsg_json.h index e8036eb0..cd9ed33e 100644 --- a/3P/libubox/blobmsg_json.h +++ b/3P/libubox/blobmsg_json.h @@ -16,17 +16,13 @@ #ifndef __BLOBMSG_JSON_H #define __BLOBMSG_JSON_H -#ifdef JSONC - #include -#else - #include -#endif +struct json_object; #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_object(struct blob_buf *b, struct json_object *obj); +bool blobmsg_add_json_element(struct blob_buf *b, const char *name, struct json_object *obj); bool blobmsg_add_json_from_string(struct blob_buf *b, const char *str); bool blobmsg_add_json_from_file(struct blob_buf *b, const char *file); diff --git a/3P/libubox/examples/CMakeLists.txt b/3P/libubox/examples/CMakeLists.txt index 2126d298..a6355356 100644 --- a/3P/libubox/examples/CMakeLists.txt +++ b/3P/libubox/examples/CMakeLists.txt @@ -1,24 +1,23 @@ cmake_minimum_required(VERSION 2.6) -PROJECT(ubox-examples C) -ADD_DEFINITIONS(-O1 -Wall -Werror --std=gnu99 -g3) +IF (BUILD_EXAMPLES) + PROJECT(ubox-examples C) + ADD_DEFINITIONS(-O1 -Wall -Werror --std=gnu99 -g3) -IF(APPLE) - INCLUDE_DIRECTORIES(/opt/local/include) - LINK_DIRECTORIES(/opt/local/lib) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) + LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) + + FIND_LIBRARY(json NAMES json-c json) + + ADD_EXECUTABLE(blobmsg-example blobmsg-example.c) + TARGET_LINK_LIBRARIES(blobmsg-example ubox blobmsg_json ${json}) + + ADD_EXECUTABLE(ustream-example ustream-example.c) + TARGET_LINK_LIBRARIES(ustream-example ubox) + + ADD_EXECUTABLE(runqueue-example runqueue-example.c) + TARGET_LINK_LIBRARIES(runqueue-example ubox) + + ADD_EXECUTABLE(json_script-example json_script-example.c) + TARGET_LINK_LIBRARIES(json_script-example ubox blobmsg_json json_script ${json}) ENDIF() - -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 index 69ddce89..01b05181 100644 --- a/3P/libubox/examples/blobmsg-example.c +++ b/3P/libubox/examples/blobmsg-example.c @@ -1,4 +1,5 @@ #include +#include #include "blobmsg.h" #include "blobmsg_json.h" diff --git a/3P/libubox/examples/json_script-example.c b/3P/libubox/examples/json_script-example.c new file mode 100644 index 00000000..4d252a9a --- /dev/null +++ b/3P/libubox/examples/json_script-example.c @@ -0,0 +1,84 @@ +#include +#include + +#include +#include "blobmsg.h" +#include "blobmsg_json.h" +#include "json_script.h" + +struct json_script_ctx jctx; +struct blob_buf b_vars; +struct blob_buf b_script; + +static void handle_command(struct json_script_ctx *ctx, const char *name, + struct blob_attr *data, struct blob_attr *vars) +{ + struct blob_attr *cur; + int rem; + + fprintf(stdout, "%s", name); + blobmsg_for_each_attr(cur, data, rem) + fprintf(stdout, " %s", (char *) blobmsg_data(cur)); + fprintf(stdout, "\n"); +} + +static struct json_script_file * +handle_file(struct json_script_ctx *ctx, const char *filename) +{ + json_object *obj; + + obj = json_object_from_file(filename); + if (!obj) { + fprintf(stderr, "load JSON data from %s failed.\n", filename); + return NULL; + } + + blob_buf_init(&b_script, 0); + blobmsg_add_json_element(&b_script, "", obj); + json_object_put(obj); + + return json_script_file_from_blobmsg(filename, + blob_data(b_script.head), blob_len(b_script.head)); +} + +static void usage(const char *prog, int exit_code) +{ + fprintf(stderr, "Usage: %s [VARNAME=value] \n", prog); + exit(exit_code); +} + +int main(int argc, char *argv[]) +{ + int i; + char *file = NULL; + const char *prog = argv[0]; + + blobmsg_buf_init(&b_vars); + blobmsg_buf_init(&b_script); + + json_script_init(&jctx); + jctx.handle_command = handle_command; + jctx.handle_file = handle_file; + + for (i = 1; i < argc; i++) { + char *sep = strchr(argv[i], '='); + if (sep) { + *sep = '\0'; + blobmsg_add_string(&b_vars, argv[i], sep + 1); + } else if (!file) { + file = argv[i]; + } else { + usage(prog, -1); + } + } + if (i < argc || !file) + usage(prog, -2); + + json_script_run(&jctx, file, b_vars.head); + + json_script_free(&jctx); + blob_buf_free(&b_script); + blob_buf_free(&b_vars); + + return 0; +} diff --git a/3P/libubox/examples/json_script-example.json b/3P/libubox/examples/json_script-example.json new file mode 100644 index 00000000..5328e599 --- /dev/null +++ b/3P/libubox/examples/json_script-example.json @@ -0,0 +1,38 @@ +[ + [ "exec", "%EXECVAR%", "/%%/" ], + [ "if", + [ "eq", "EQVAR", "eqval" ], + [ "exec_if", "%VAR%", "%%", "jk" ] + ], + [ "case", "CASEVAR", { + "caseval0": ["cmd_case_0", "cmd_case_arg0", "case_cmd_arg1"], + "caseval1": ["cmd_case_1", "cmd_case_arg0", "case_cmd_arg1"] + } ], + + [ "if", + [ "and", [ "eq", "EQVAR", "eqval" ], + [ "has", "HASVAR" ], + [ "regex", "REGEXVAR0", "regexval" ], + [ "regex", "REGEXVAR1", [ "regexval10", "regexval11" ] ], + [ "not", [ "eq", "NOTEQVAR", "noteqval" ] ] ], + [ "exec_if_and", "%ANDVAR%" ] + ], + + [ "if", + [ "or", [ "eq", "EQVAR", "eqval" ], + [ "has", "HASVAR" ], + [ "regex", "REGEXVAR0", "regexval" ], + [ "regex", "REGEXVAR1", [ "regexval10", "regexval11" ] ], + [ "not", [ "eq", "NOTEQVAR", "noteqval" ] ] ], + [ "exec_if_or", "%ORVAR%" ] + ], + + [ "if", + [ "isdir", "%ISDIRVAR%" ], + [ "exec_isdir", "%ISDIRVAR%" ] + ], + + [ "return", "foobar" ], + + [ "exec_non_reachable", "Arghhh" ] +] diff --git a/3P/libubox/examples/json_script-tests.sh b/3P/libubox/examples/json_script-tests.sh new file mode 100644 index 00000000..7120a7e8 --- /dev/null +++ b/3P/libubox/examples/json_script-tests.sh @@ -0,0 +1,287 @@ +JSON_SCRIPT=tests.json +JSON_SCRIPT_BIN=./json_script-example +FILE_STDOUT=tests.stdout +FILE_STDERR=tests.stderr +FILE_EXPECTED=tests.expected + +call_json_script() { + #export LD_PRELOAD=../libjson_script.so + $JSON_SCRIPT_BIN "$@" "$JSON_SCRIPT" >"$FILE_STDOUT" 2>"$FILE_STDERR" +} + +assertStdioEquals() { + local expected="$1" + local file_stdio="$2" + + echo "$expected" >"$FILE_EXPECTED" + if [ -z "$expected" ]; then + # we are expecting empty output, but we deliberately added a newline + # with echo above, so adding another echo to compensate for that + echo >>"$file_stdio" + fi + diff -up "$FILE_EXPECTED" "$file_stdio" >/dev/null 2>&1 || { + cat >&2 <"$JSON_SCRIPT" <<-EOF + [ + [ ] + [ ] + ] + EOF + call_json_script + assertStderrEquals "load JSON data from $JSON_SCRIPT failed." +} + +test_expr_eq() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "eq", "VAR", "foo" ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=foo" + assertStdoutEquals "echo bar" + call_json_script "VAR=xxx" + assertStdoutEquals "echo baz" +} + +test_expr_has() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "has", "VAR" ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=foo" + assertStdoutEquals "echo bar" + call_json_script + assertStdoutEquals "echo baz" +} + +test_expr_regex_single() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "regex", "VAR", ".ell." ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=hello" + assertStdoutEquals "echo bar" + call_json_script "VAR=.ell." + assertStdoutEquals "echo bar" + call_json_script + assertStdoutEquals "echo baz" + call_json_script "VAR=" + assertStdoutEquals "echo baz" + call_json_script "VAR=hell" + assertStdoutEquals "echo baz" +} + +test_expr_regex_multi() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "regex", "VAR", [ ".ell.", "w.rld" ] ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=hello" + assertStdoutEquals "echo bar" + call_json_script "VAR=world" + assertStdoutEquals "echo bar" + call_json_script "VAR=.ell." + assertStdoutEquals "echo bar" + call_json_script "VAR=w.rld" + assertStdoutEquals "echo bar" + call_json_script + assertStdoutEquals "echo baz" + call_json_script "VAR=" + assertStdoutEquals "echo baz" + call_json_script "VAR=hell" + assertStdoutEquals "echo baz" +} + +test_expr_not() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "not", [ "has", "VAR" ] ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=foo" + assertStdoutEquals "echo baz" + call_json_script + assertStdoutEquals "echo bar" +} + +test_expr_and() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "and", [ "eq", "EQVAR", "eqval" ], + [ "regex", "REGEXVAR", "regex..." ] + ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "EQVAR=eqval" "REGEXVAR=regexval" + assertStdoutEquals "echo bar" + call_json_script "EQVAR=foo" + assertStdoutEquals "echo baz" + call_json_script "REGEXVAR=regex***" + assertStdoutEquals "echo baz" + call_json_script + assertStdoutEquals "echo baz" +} + +test_expr_or() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "or", [ "not", [ "eq", "EQVAR", "eqval" ] ], + [ "regex", "REGEXVAR", [ "regexva.[0-9]", "regexva.[a-z]" ] ] + ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "EQVAR=eqval" "REGEXVAR=regexval1" + assertStdoutEquals "echo bar" + call_json_script "EQVAR=neq" "REGEXVAR=sxc" + assertStdoutEquals "echo bar" + call_json_script "REGEXVAR=sxc" + assertStdoutEquals "echo bar" + call_json_script "EQVAR=foo" + assertStdoutEquals "echo bar" + call_json_script + assertStdoutEquals "echo bar" + call_json_script "EQVAR=eqval" "REGEXVAR=regexval" + assertStdoutEquals "echo baz" +} + +test_expr_isdir() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "isdir", "%VAR%" ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=/" + assertStdoutEquals "echo bar" + call_json_script "VAR=$(mktemp -u)" + assertStdoutEquals "echo baz" + call_json_script + assertStdoutEquals "echo baz" +} + +test_cmd_case() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "case", "CASEVAR", { + "0": [ "echo", "foo" ], + "1": [ + [ "echo", "bar" ], + [ "echo", "baz" ] + ], + "%VAR%": [ "echo", "quz" ] + } ] + ] + EOF + call_json_script "CASEVAR=0" + assertStdoutEquals "echo foo" + call_json_script "CASEVAR=1" + assertStdoutEquals "echo bar +echo baz" + call_json_script "CASEVAR=%VAR%" + assertStdoutEquals "echo quz" + call_json_script "CASEVAR=" + assertStdoutEquals "" + call_json_script + assertStdoutEquals "" + call_json_script "CASEVAR=xxx" "VAR=xxx" + assertStdoutEquals "" +} + +test_cmd_if() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "if", + [ "eq", "VAR", "foo" ], + [ "echo", "bar" ], + [ "echo", "baz" ] + ] + ] + EOF + call_json_script "VAR=foo" + assertStdoutEquals "echo bar" + call_json_script "VAR=xxx" + assertStdoutEquals "echo baz" +} + +test_cmd_cb() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "exec", "%VAR%", "/%VAS%%%/" ] + ] + EOF + call_json_script + assertStdoutEquals "exec /%/" + call_json_script "VAR=" + assertStdoutEquals "exec /%/" + call_json_script "VAR=qux" "VAS=3" + assertStdoutEquals "exec qux /3%/" +} + +test_cmd_return() { + cat >"$JSON_SCRIPT" <<-EOF + [ + [ "heh", "%HEHVAR%" ], + [ "%VAR%", "%VAR%" ], + [ "return" ], + [ "exec_non_reachable", "Arghhh" ] + ] + EOF + call_json_script "HEHVAR=dude" "VAR=ow" + assertStdoutEquals "heh dude +%VAR% ow" +} + +. ./shunit2 diff --git a/3P/libubox/examples/runqueue-example.c b/3P/libubox/examples/runqueue-example.c index 1ae184a9..13ab864e 100644 --- a/3P/libubox/examples/runqueue-example.c +++ b/3P/libubox/examples/runqueue-example.c @@ -16,11 +16,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include #include #include #include +#include "uloop.h" #include "runqueue.h" static struct runqueue q; diff --git a/3P/libubox/examples/shunit2 b/3P/libubox/examples/shunit2 new file mode 100644 index 00000000..d6e75033 --- /dev/null +++ b/3P/libubox/examples/shunit2 @@ -0,0 +1,1067 @@ +#! /bin/sh +# $Id$ +# vim:et:ft=sh:sts=2:sw=2 +# +# Copyright 2008 Kate Ward. All Rights Reserved. +# Released under the LGPL (GNU Lesser General Public License) +# +# shUnit2 -- Unit testing framework for Unix shell scripts. +# http://code.google.com/p/shunit2/ +# +# Author: kate.ward@forestent.com (Kate Ward) +# +# shUnit2 is a xUnit based unit test framework for Bourne shell scripts. It is +# based on the popular JUnit unit testing framework for Java. + +# return if shunit already loaded +[ -n "${SHUNIT_VERSION:-}" ] && exit 0 +SHUNIT_VERSION='2.1.7pre' + +# return values that scripts can use +SHUNIT_TRUE=0 +SHUNIT_FALSE=1 +SHUNIT_ERROR=2 + +# logging functions +_shunit_warn() { echo "shunit2:WARN $@" >&2; } +_shunit_error() { echo "shunit2:ERROR $@" >&2; } +_shunit_fatal() { echo "shunit2:FATAL $@" >&2; exit ${SHUNIT_ERROR}; } + +# determine some reasonable command defaults +__SHUNIT_UNAME_S=`uname -s` +case "${__SHUNIT_UNAME_S}" in + BSD) __SHUNIT_EXPR_CMD='gexpr' ;; + *) __SHUNIT_EXPR_CMD='expr' ;; +esac + +# commands a user can override if needed +SHUNIT_EXPR_CMD=${SHUNIT_EXPR_CMD:-${__SHUNIT_EXPR_CMD}} + +# enable strict mode by default +SHUNIT_STRICT=${SHUNIT_STRICT:-${SHUNIT_TRUE}} + +# specific shell checks +if [ -n "${ZSH_VERSION:-}" ]; then + setopt |grep "^shwordsplit$" >/dev/null + if [ $? -ne ${SHUNIT_TRUE} ]; then + _shunit_fatal 'zsh shwordsplit option is required for proper operation' + fi + if [ -z "${SHUNIT_PARENT:-}" ]; then + _shunit_fatal "zsh does not pass \$0 through properly. please declare \ +\"SHUNIT_PARENT=\$0\" before calling shUnit2" + fi +fi + +# +# constants +# + +__SHUNIT_ASSERT_MSG_PREFIX='ASSERT:' +__SHUNIT_MODE_SOURCED='sourced' +__SHUNIT_MODE_STANDALONE='standalone' +__SHUNIT_PARENT=${SHUNIT_PARENT:-$0} + +# set the constants readonly +__shunit_constants=`set |grep '^__SHUNIT_' |cut -d= -f1` +echo "${__shunit_constants}" |grep '^Binary file' >/dev/null && \ + __shunit_constants=`set |grep -a '^__SHUNIT_' |cut -d= -f1` +for __shunit_const in ${__shunit_constants}; do + if [ -z "${ZSH_VERSION:-}" ]; then + readonly ${__shunit_const} + else + case ${ZSH_VERSION} in + [123].*) readonly ${__shunit_const} ;; + *) readonly -g ${__shunit_const} # declare readonly constants globally + esac + fi +done +unset __shunit_const __shunit_constants + +# +# internal variables +# + +# variables +__shunit_lineno='' # line number of executed test +__shunit_mode=${__SHUNIT_MODE_SOURCED} # operating mode +__shunit_reportGenerated=${SHUNIT_FALSE} # is report generated +__shunit_script='' # filename of unittest script (standalone mode) +__shunit_skip=${SHUNIT_FALSE} # is skipping enabled +__shunit_suite='' # suite of tests to execute + +# counts of tests +__shunit_testSuccess=${SHUNIT_TRUE} +__shunit_testsTotal=0 +__shunit_testsPassed=0 +__shunit_testsFailed=0 + +# counts of asserts +__shunit_assertsTotal=0 +__shunit_assertsPassed=0 +__shunit_assertsFailed=0 +__shunit_assertsSkipped=0 + +# macros +_SHUNIT_LINENO_='eval __shunit_lineno=""; if [ "${1:-}" = "--lineno" ]; then [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi' + +#----------------------------------------------------------------------------- +# private functions + +#----------------------------------------------------------------------------- +# assert functions +# + +# Assert that two values are equal to one another. +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertEquals() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertEquals() requires two or three arguments; $# given" + _shunit_error "1: ${1:+$1} 2: ${2:+$2} 3: ${3:+$3}${4:+ 4: $4}" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_expected_=$1 + shunit_actual_=$2 + + shunit_return=${SHUNIT_TRUE} + if [ "${shunit_expected_}" = "${shunit_actual_}" ]; then + _shunit_assertPass + else + failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}" + shunit_return=${SHUNIT_FALSE} + fi + + unset shunit_message_ shunit_expected_ shunit_actual_ + return ${shunit_return} +} +_ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"' + +# Assert that two values are not equal to one another. +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertNotEquals() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertNotEquals() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_expected_=$1 + shunit_actual_=$2 + + shunit_return=${SHUNIT_TRUE} + if [ "${shunit_expected_}" != "${shunit_actual_}" ]; then + _shunit_assertPass + else + failSame "${shunit_message_}" "$@" + shunit_return=${SHUNIT_FALSE} + fi + + unset shunit_message_ shunit_expected_ shunit_actual_ + return ${shunit_return} +} +_ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"' + +# Assert that a value is null (i.e. an empty string) +# +# Args: +# message: string: failure message [optional] +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertNull() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 1 -o $# -gt 2 ]; then + _shunit_error "assertNull() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + assertTrue "${shunit_message_}" "[ -z '$1' ]" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"' + +# Assert that a value is not null (i.e. a non-empty string) +# +# Args: +# message: string: failure message [optional] +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertNotNull() +{ + ${_SHUNIT_LINENO_} + if [ $# -gt 2 ]; then # allowing 0 arguments as $1 might actually be null + _shunit_error "assertNotNull() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"` + test -n "${shunit_actual_}" + assertTrue "${shunit_message_}" $? + shunit_return=$? + + unset shunit_actual_ shunit_message_ + return ${shunit_return} +} +_ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"' + +# Assert that two values are the same (i.e. equal to one another). +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertSame() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + assertEquals "${shunit_message_}" "$1" "$2" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"' + +# Assert that two values are not the same (i.e. not equal to one another). +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertNotSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "assertNotSame() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_:-}$1" + shift + fi + assertNotEquals "${shunit_message_}" "$1" "$2" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"' + +# Assert that a value or shell test condition is true. +# +# In shell, a value of 0 is true and a non-zero value is false. Any integer +# value passed can thereby be tested. +# +# Shell supports much more complicated tests though, and a means to support +# them was needed. As such, this function tests that conditions are true or +# false through evaluation rather than just looking for a true or false. +# +# The following test will succeed: +# assertTrue 0 +# assertTrue "[ 34 -gt 23 ]" +# The folloing test will fail with a message: +# assertTrue 123 +# assertTrue "test failed" "[ -r '/non/existant/file' ]" +# +# Args: +# message: string: failure message [optional] +# condition: string: integer value or shell conditional statement +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertTrue() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 1 -o $# -gt 2 ]; then + _shunit_error "assertTrue() takes one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_condition_=$1 + + # see if condition is an integer, i.e. a return value + shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` + shunit_return=${SHUNIT_TRUE} + if [ -z "${shunit_condition_}" ]; then + # null condition + shunit_return=${SHUNIT_FALSE} + elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] + then + # possible return value. treating 0 as true, and non-zero as false. + [ ${shunit_condition_} -ne 0 ] && shunit_return=${SHUNIT_FALSE} + else + # (hopefully) a condition + ( eval ${shunit_condition_} ) >/dev/null 2>&1 + [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE} + fi + + # record the test + if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then + _shunit_assertPass + else + _shunit_assertFail "${shunit_message_}" + fi + + unset shunit_message_ shunit_condition_ shunit_match_ + return ${shunit_return} +} +_ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"' + +# Assert that a value or shell test condition is false. +# +# In shell, a value of 0 is true and a non-zero value is false. Any integer +# value passed can thereby be tested. +# +# Shell supports much more complicated tests though, and a means to support +# them was needed. As such, this function tests that conditions are true or +# false through evaluation rather than just looking for a true or false. +# +# The following test will succeed: +# assertFalse 1 +# assertFalse "[ 'apples' = 'oranges' ]" +# The folloing test will fail with a message: +# assertFalse 0 +# assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]" +# +# Args: +# message: string: failure message [optional] +# condition: string: integer value or shell conditional statement +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +assertFalse() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 1 -o $# -gt 2 ]; then + _shunit_error "assertFalse() quires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 2 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_condition_=$1 + + # see if condition is an integer, i.e. a return value + shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` + shunit_return=${SHUNIT_TRUE} + if [ -z "${shunit_condition_}" ]; then + # null condition + shunit_return=${SHUNIT_FALSE} + elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] + then + # possible return value. treating 0 as true, and non-zero as false. + [ ${shunit_condition_} -eq 0 ] && shunit_return=${SHUNIT_FALSE} + else + # (hopefully) a condition + ( eval ${shunit_condition_} ) >/dev/null 2>&1 + [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE} + fi + + # record the test + if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then + _shunit_assertPass + else + _shunit_assertFail "${shunit_message_}" + fi + + unset shunit_message_ shunit_condition_ shunit_match_ + return ${shunit_return} +} +_ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"' + +#----------------------------------------------------------------------------- +# failure functions +# + +# Records a test failure. +# +# Args: +# message: string: failure message [optional] +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +fail() +{ + ${_SHUNIT_LINENO_} + if [ $# -gt 1 ]; then + _shunit_error "fail() requires zero or one arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 1 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + + _shunit_assertFail "${shunit_message_}" + + unset shunit_message_ + return ${SHUNIT_FALSE} +} +_FAIL_='eval fail --lineno "${LINENO:-}"' + +# Records a test failure, stating two values were not equal. +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +failNotEquals() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "failNotEquals() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + shunit_expected_=$1 + shunit_actual_=$2 + + _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>" + + unset shunit_message_ shunit_expected_ shunit_actual_ + return ${SHUNIT_FALSE} +} +_FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"' + +# Records a test failure, stating two values should have been the same. +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +failSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "failSame() requires two or three arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + + _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same" + + unset shunit_message_ + return ${SHUNIT_FALSE} +} +_FAIL_SAME_='eval failSame --lineno "${LINENO:-}"' + +# Records a test failure, stating two values were not equal. +# +# This is functionally equivalent to calling failNotEquals(). +# +# Args: +# message: string: failure message [optional] +# expected: string: expected value +# actual: string: actual value +# Returns: +# integer: success (TRUE/FALSE/ERROR constant) +failNotSame() +{ + ${_SHUNIT_LINENO_} + if [ $# -lt 2 -o $# -gt 3 ]; then + _shunit_error "failNotEquals() requires one or two arguments; $# given" + return ${SHUNIT_ERROR} + fi + _shunit_shouldSkip && return ${SHUNIT_TRUE} + + shunit_message_=${__shunit_lineno} + if [ $# -eq 3 ]; then + shunit_message_="${shunit_message_}$1" + shift + fi + failNotEquals "${shunit_message_}" "$1" "$2" + shunit_return=$? + + unset shunit_message_ + return ${shunit_return} +} +_FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"' + +#----------------------------------------------------------------------------- +# skipping functions +# + +# Force remaining assert and fail functions to be "skipped". +# +# This function forces the remaining assert and fail functions to be "skipped", +# i.e. they will have no effect. Each function skipped will be recorded so that +# the total of asserts and fails will not be altered. +# +# Args: +# None +startSkipping() +{ + __shunit_skip=${SHUNIT_TRUE} +} + +# Resume the normal recording behavior of assert and fail calls. +# +# Args: +# None +endSkipping() +{ + __shunit_skip=${SHUNIT_FALSE} +} + +# Returns the state of assert and fail call skipping. +# +# Args: +# None +# Returns: +# boolean: (TRUE/FALSE constant) +isSkipping() +{ + return ${__shunit_skip} +} + +#----------------------------------------------------------------------------- +# suite functions +# + +# Stub. This function should contains all unit test calls to be made. +# +# DEPRECATED (as of 2.1.0) +# +# This function can be optionally overridden by the user in their test suite. +# +# If this function exists, it will be called when shunit2 is sourced. If it +# does not exist, shunit2 will search the parent script for all functions +# beginning with the word 'test', and they will be added dynamically to the +# test suite. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#suite() { :; } # DO NOT UNCOMMENT THIS FUNCTION + +# Adds a function name to the list of tests schedule for execution. +# +# This function should only be called from within the suite() function. +# +# Args: +# function: string: name of a function to add to current unit test suite +suite_addTest() +{ + shunit_func_=${1:-} + + __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}" + __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1` + + unset shunit_func_ +} + +# Stub. This function will be called once before any tests are run. +# +# Common one-time environment preparation tasks shared by all tests can be +# defined here. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#oneTimeSetUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION + +# Stub. This function will be called once after all tests are finished. +# +# Common one-time environment cleanup tasks shared by all tests can be defined +# here. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#oneTimeTearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION + +# Stub. This function will be called before each test is run. +# +# Common environment preparation tasks shared by all tests can be defined here. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#setUp() { :; } + +# Note: see _shunit_mktempFunc() for actual implementation +# Stub. This function will be called after each test is run. +# +# Common environment cleanup tasks shared by all tests can be defined here. +# +# This function should be overridden by the user in their unit test suite. +# Note: see _shunit_mktempFunc() for actual implementation +# +# Args: +# None +#tearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION + +#------------------------------------------------------------------------------ +# internal shUnit2 functions +# + +# Create a temporary directory to store various run-time files in. +# +# This function is a cross-platform temporary directory creation tool. Not all +# OSes have the mktemp function, so one is included here. +# +# Args: +# None +# Outputs: +# string: the temporary directory that was created +_shunit_mktempDir() +{ + # try the standard mktemp function + ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return + + # the standard mktemp didn't work. doing our own. + if [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then + _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 "${_shunit_file_}" +#! /bin/sh +exit ${SHUNIT_TRUE} +EOF + chmod +x "${_shunit_file_}" + done + + unset _shunit_file_ +} + +# Final cleanup function to leave things as we found them. +# +# Besides removing the temporary directory, this function is in charge of the +# final exit code of the unit test. The exit code is based on how the script +# was ended (e.g. normal exit, or via Ctrl-C). +# +# Args: +# name: string: name of the trap called (specified when trap defined) +_shunit_cleanup() +{ + _shunit_name_=$1 + + case ${_shunit_name_} in + EXIT) _shunit_signal_=0 ;; + INT) _shunit_signal_=2 ;; + TERM) _shunit_signal_=15 ;; + *) + _shunit_warn "unrecognized trap value (${_shunit_name_})" + _shunit_signal_=0 + ;; + esac + + # do our work + rm -fr "${__shunit_tmpDir}" + + # exit for all non-EXIT signals + if [ ${_shunit_name_} != 'EXIT' ]; then + _shunit_warn "trapped and now handling the (${_shunit_name_}) signal" + # disable EXIT trap + trap 0 + # add 128 to signal and exit + exit `expr ${_shunit_signal_} + 128` + elif [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ] ; then + _shunit_assertFail 'Unknown failure encountered running a test' + _shunit_generateReport + exit ${SHUNIT_ERROR} + fi + + unset _shunit_name_ _shunit_signal_ +} + +# The actual running of the tests happens here. +# +# Args: +# None +_shunit_execSuite() +{ + for _shunit_test_ in ${__shunit_suite}; do + __shunit_testSuccess=${SHUNIT_TRUE} + + # disable skipping + endSkipping + + # execute the per-test setup function + setUp + + # execute the test + echo "${_shunit_test_}" + eval ${_shunit_test_} + + # execute the per-test tear-down function + tearDown + + # update stats + if [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then + __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1` + else + __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1` + fi + done + + unset _shunit_test_ +} + +# Generates the user friendly report with appropriate OK/FAILED message. +# +# Args: +# None +# Output: +# string: the report of successful and failed tests, as well as totals. +_shunit_generateReport() +{ + _shunit_ok_=${SHUNIT_TRUE} + + # if no exit code was provided one, determine an appropriate one + [ ${__shunit_testsFailed} -gt 0 \ + -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \ + && _shunit_ok_=${SHUNIT_FALSE} + + echo + if [ ${__shunit_testsTotal} -eq 1 ]; then + echo "Ran ${__shunit_testsTotal} test." + else + echo "Ran ${__shunit_testsTotal} tests." + fi + + _shunit_failures_='' + _shunit_skipped_='' + [ ${__shunit_assertsFailed} -gt 0 ] \ + && _shunit_failures_="failures=${__shunit_assertsFailed}" + [ ${__shunit_assertsSkipped} -gt 0 ] \ + && _shunit_skipped_="skipped=${__shunit_assertsSkipped}" + + if [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then + _shunit_msg_='OK' + [ -n "${_shunit_skipped_}" ] \ + && _shunit_msg_="${_shunit_msg_} (${_shunit_skipped_})" + else + _shunit_msg_="FAILED (${_shunit_failures_}" + [ -n "${_shunit_skipped_}" ] \ + && _shunit_msg_="${_shunit_msg_},${_shunit_skipped_}" + _shunit_msg_="${_shunit_msg_})" + fi + + echo + echo ${_shunit_msg_} + __shunit_reportGenerated=${SHUNIT_TRUE} + + unset _shunit_failures_ _shunit_msg_ _shunit_ok_ _shunit_skipped_ +} + +# Test for whether a function should be skipped. +# +# Args: +# None +# Returns: +# boolean: whether the test should be skipped (TRUE/FALSE constant) +_shunit_shouldSkip() +{ + [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE} + _shunit_assertSkip +} + +# Records a successful test. +# +# Args: +# None +_shunit_assertPass() +{ + __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1` + __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` +} + +# Records a test failure. +# +# Args: +# message: string: failure message to provide user +_shunit_assertFail() +{ + _shunit_msg_=$1 + + __shunit_testSuccess=${SHUNIT_FALSE} + __shunit_assertsFailed=`expr ${__shunit_assertsFailed} + 1` + __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` + echo "${__SHUNIT_ASSERT_MSG_PREFIX}${_shunit_msg_}" + + unset _shunit_msg_ +} + +# Records a skipped test. +# +# Args: +# None +_shunit_assertSkip() +{ + __shunit_assertsSkipped=`expr ${__shunit_assertsSkipped} + 1` + __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` +} + +# Prepare a script filename for sourcing. +# +# Args: +# script: string: path to a script to source +# Returns: +# string: filename prefixed with ./ (if necessary) +_shunit_prepForSourcing() +{ + _shunit_script_=$1 + case "${_shunit_script_}" in + /*|./*) echo "${_shunit_script_}" ;; + *) echo "./${_shunit_script_}" ;; + esac + unset _shunit_script_ +} + +# Escape a character in a string. +# +# Args: +# c: string: unescaped character +# s: string: to escape character in +# Returns: +# string: with escaped character(s) +_shunit_escapeCharInStr() +{ + [ -n "$2" ] || return # no point in doing work on an empty string + + # Note: using shorter variable names to prevent conflicts with + # _shunit_escapeCharactersInString(). + _shunit_c_=$1 + _shunit_s_=$2 + + + # escape the character + echo ''${_shunit_s_}'' |sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g' + + unset _shunit_c_ _shunit_s_ +} + +# Escape a character in a string. +# +# Args: +# str: string: to escape characters in +# Returns: +# string: with escaped character(s) +_shunit_escapeCharactersInString() +{ + [ -n "$1" ] || return # no point in doing work on an empty string + + _shunit_str_=$1 + + # Note: using longer variable names to prevent conflicts with + # _shunit_escapeCharInStr(). + for _shunit_char_ in '"' '$' "'" '`'; do + _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"` + done + + echo "${_shunit_str_}" + unset _shunit_char_ _shunit_str_ +} + +# Extract list of functions to run tests against. +# +# Args: +# script: string: name of script to extract functions from +# Returns: +# string: of function names +_shunit_extractTestFunctions() +{ + _shunit_script_=$1 + + # extract the lines with test function names, strip of anything besides the + # function name, and output everything on a single line. + _shunit_regex_='^[ ]*(function )*test[A-Za-z0-9_]* *\(\)' + egrep "${_shunit_regex_}" "${_shunit_script_}" \ + |sed 's/^[^A-Za-z0-9_]*//;s/^function //;s/\([A-Za-z0-9_]*\).*/\1/g' \ + |xargs + + unset _shunit_regex_ _shunit_script_ +} + +#------------------------------------------------------------------------------ +# main +# + +# determine the operating mode +if [ $# -eq 0 ]; then + __shunit_script=${__SHUNIT_PARENT} + __shunit_mode=${__SHUNIT_MODE_SOURCED} +else + __shunit_script=$1 + [ -r "${__shunit_script}" ] || \ + _shunit_fatal "unable to read from ${__shunit_script}" + __shunit_mode=${__SHUNIT_MODE_STANDALONE} +fi + +# create a temporary storage location +__shunit_tmpDir=`_shunit_mktempDir` + +# provide a public temporary directory for unit test scripts +# TODO(kward): document this +SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp" +mkdir "${SHUNIT_TMPDIR}" + +# setup traps to clean up after ourselves +trap '_shunit_cleanup EXIT' 0 +trap '_shunit_cleanup INT' 2 +trap '_shunit_cleanup TERM' 15 + +# create phantom functions to work around issues with Cygwin +_shunit_mktempFunc +PATH="${__shunit_tmpDir}:${PATH}" + +# make sure phantom functions are executable. this will bite if /tmp (or the +# current $TMPDIR) points to a path on a partition that was mounted with the +# 'noexec' option. the noexec command was created with _shunit_mktempFunc(). +noexec 2>/dev/null || _shunit_fatal \ + 'please declare TMPDIR with path on partition with exec permission' + +# we must manually source the tests in standalone mode +if [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then + . "`_shunit_prepForSourcing \"${__shunit_script}\"`" +fi + +# execute the oneTimeSetUp function (if it exists) +oneTimeSetUp + +# execute the suite function defined in the parent test script +# deprecated as of 2.1.0 +suite + +# if no suite function was defined, dynamically build a list of functions +if [ -z "${__shunit_suite}" ]; then + shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"` + for shunit_func_ in ${shunit_funcs_}; do + suite_addTest ${shunit_func_} + done +fi +unset shunit_func_ shunit_funcs_ + +# execute the tests +_shunit_execSuite + +# execute the oneTimeTearDown function (if it exists) +oneTimeTearDown + +# generate the report +_shunit_generateReport + +# that's it folks +[ ${__shunit_testsFailed} -eq 0 ] +exit $? diff --git a/3P/libubox/jshn.c b/3P/libubox/jshn.c index cec48a07..69cb06f9 100644 --- a/3P/libubox/jshn.c +++ b/3P/libubox/jshn.c @@ -164,7 +164,7 @@ 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) { + if (!obj || json_object_get_type(obj) != json_type_object) { fprintf(stderr, "Failed to parse message data\n"); return 1; } @@ -179,8 +179,8 @@ 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); + keys = alloca(var_prefix_len + strlen(prefix) + sizeof("K_") + 1); + sprintf(keys, "%sK_%s", var_prefix, prefix); return getenv(keys); } @@ -188,15 +188,15 @@ static void get_var(const char *prefix, const char **name, char **var, char **ty { char *tmpname, *varname; - tmpname = alloca(var_prefix_len + strlen(prefix) + 1 + strlen(*name) + 1 + sizeof("TYPE_")); + tmpname = alloca(var_prefix_len + strlen(prefix) + 1 + strlen(*name) + 1 + sizeof("T_")); sprintf(tmpname, "%s%s_%s", var_prefix, prefix, *name); *var = getenv(tmpname); - sprintf(tmpname, "%sTYPE_%s_%s", var_prefix, prefix, *name); + sprintf(tmpname, "%sT_%s_%s", var_prefix, prefix, *name); *type = getenv(tmpname); - sprintf(tmpname, "%sNAME_%s_%s", var_prefix, prefix, *name); + sprintf(tmpname, "%sN_%s_%s", var_prefix, prefix, *name); varname = getenv(tmpname); if (varname) *name = varname; @@ -258,18 +258,31 @@ static int jshn_format(bool no_newline, bool indent) { json_object *obj; const char *output; + char *blobmsg_output = NULL; + int ret = -1; + + if (!(obj = json_object_new_object())) + return -1; + + jshn_add_objects(obj, "J_V", false); + if (!(output = json_object_to_json_string(obj))) + goto out; - 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); + if (!blobmsg_add_json_from_string(&b, output)) + goto out; + if (!(blobmsg_output = blobmsg_format_json_indent(b.head, 1, 0))) + goto out; + output = blobmsg_output; } fprintf(stdout, "%s%s", output, no_newline ? "" : "\n"); + free(blobmsg_output); + ret = 0; + +out: json_object_put(obj); - return 0; + return ret; } static int usage(const char *progname) diff --git a/3P/libubox/json_script.c b/3P/libubox/json_script.c index 5c35d871..b5d414d7 100644 --- a/3P/libubox/json_script.c +++ b/3P/libubox/json_script.c @@ -32,6 +32,7 @@ struct json_handler { static int json_process_expr(struct json_call *call, struct blob_attr *cur); static int json_process_cmd(struct json_call *call, struct blob_attr *cur); +static int eval_string(struct json_call *call, struct blob_buf *buf, const char *name, const char *pattern); struct json_script_file * json_script_file_from_blobmsg(const char *name, void *data, int len) @@ -333,12 +334,40 @@ static int handle_expr_or(struct json_call *call, struct blob_attr *expr) static int handle_expr_not(struct json_call *call, struct blob_attr *expr) { struct blob_attr *tb[3]; + int ret; json_get_tuple(expr, tb, BLOBMSG_TYPE_ARRAY, 0); if (!tb[1]) return -1; - return json_process_expr(call, tb[1]); + ret = json_process_expr(call, tb[1]); + if (ret < 0) + return ret; + return !ret; +} + +static int handle_expr_isdir(struct json_call *call, struct blob_attr *expr) +{ + static struct blob_buf b; + struct blob_attr *tb[3]; + const char *pattern, *path; + struct stat s; + int ret; + + json_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0); + if (!tb[1] || blobmsg_type(tb[1]) != BLOBMSG_TYPE_STRING) + return -1; + pattern = blobmsg_data(tb[1]); + + blob_buf_init(&b, 0); + ret = eval_string(call, &b, NULL, pattern); + if (ret < 0) + return ret; + path = blobmsg_data(blob_data(b.head)); + ret = stat(path, &s); + if (ret < 0) + return 0; + return S_ISDIR(s.st_mode); } static const struct json_handler expr[] = { @@ -348,6 +377,7 @@ static const struct json_handler expr[] = { { "and", handle_expr_and }, { "or", handle_expr_or }, { "not", handle_expr_not }, + { "isdir", handle_expr_isdir }, }; static int @@ -416,7 +446,7 @@ static int eval_string(struct json_call *call, struct blob_buf *buf, const char } if (cur_var) { - if (next > str) { + if (end > str) { cur = msg_find_var(call, str); if (!cur) continue; @@ -434,7 +464,7 @@ static int eval_string(struct json_call *call, struct blob_buf *buf, const char cur_len = end - str; } - dest = blobmsg_realloc_string_buffer(buf, cur_len + 1); + dest = blobmsg_realloc_string_buffer(buf, len + cur_len + 1); memcpy(dest + len, cur, cur_len); len += cur_len; } @@ -480,8 +510,8 @@ static int cmd_process_strings(struct json_call *call, struct blob_attr *attr) continue; if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) { - ctx->handle_error(ctx, "Invalid argument in command", attr); - return -1; + blobmsg_add_blob(&ctx->buf, cur); + continue; } ret = cmd_add_string(call, blobmsg_data(cur)); @@ -537,6 +567,9 @@ static int json_process_cmd(struct json_call *call, struct blob_attr *block) } blobmsg_for_each_attr(cur, block, rem) { + if (ctx->abort) + break; + switch(blobmsg_type(cur)) { case BLOBMSG_TYPE_STRING: if (!i) @@ -556,7 +589,7 @@ static int json_process_cmd(struct json_call *call, struct blob_attr *block) 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; + static unsigned int _seq = 0; struct json_call call = { .ctx = ctx, .vars = vars, @@ -567,6 +600,8 @@ void json_script_run_file(struct json_script_ctx *ctx, struct json_script_file * if (!call.seq) call.seq = ++_seq; + ctx->abort = false; + __json_script_run(&call, file, NULL); } @@ -592,8 +627,7 @@ static void __json_script_file_free(struct json_script_file *f) next = f->next; free(f); - if (next) - return __json_script_file_free(next); + __json_script_file_free(next); } void diff --git a/3P/libubox/json_script.h b/3P/libubox/json_script.h index 9475baa5..66563e9d 100644 --- a/3P/libubox/json_script.h +++ b/3P/libubox/json_script.h @@ -28,6 +28,7 @@ struct json_script_ctx { struct blob_buf buf; uint32_t run_seq; + bool abort; /* * handle_command: handle a command that was not recognized by the @@ -99,6 +100,18 @@ void json_script_run(struct json_script_ctx *ctx, const char *filename, void json_script_run_file(struct json_script_ctx *ctx, struct json_script_file *file, struct blob_attr *vars); + +/* + * json_script_abort - abort current json script run + * + * to be called from a script context callback + */ +static inline void +json_script_abort(struct json_script_ctx *ctx) +{ + ctx->abort = true; +} + /* * json_script_eval_string - evaluate a string and store the result * diff --git a/3P/libubox/lua/CMakeLists.txt b/3P/libubox/lua/CMakeLists.txt index 04abe0ca..34c9ab18 100644 --- a/3P/libubox/lua/CMakeLists.txt +++ b/3P/libubox/lua/CMakeLists.txt @@ -5,21 +5,13 @@ 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() + pkg_search_module(LUA lua5.1 lua-5.1) 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) 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 deleted file mode 100644 index 1809ca2e..00000000 --- a/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/uloop.dep +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index a5dba2ef..00000000 Binary files a/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-debug-shared/uloop.o and /dev/null differ diff --git a/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/uloop.dep b/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/uloop.dep deleted file mode 100644 index 1809ca2e..00000000 --- a/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/uloop.dep +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index f79ef714..00000000 Binary files a/3P/libubox/lua/builders/linux-gcc/_rt3052d_Cabasse_Stream1_64-release-shared/uloop.o and /dev/null differ diff --git a/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/uloop.dep b/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/uloop.dep deleted file mode 100644 index 1809ca2e..00000000 --- a/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/uloop.dep +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index ce9d92ec..00000000 Binary files a/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-debug-shared/uloop.o and /dev/null differ diff --git a/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/uloop.dep b/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/uloop.dep deleted file mode 100644 index 1809ca2e..00000000 --- a/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/uloop.dep +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 5924e5d8..00000000 Binary files a/3P/libubox/lua/builders/linux-gcc/_rt3052d_StriimSOUND-release-shared/uloop.o and /dev/null differ diff --git a/3P/libubox/lua/uloop.c b/3P/libubox/lua/uloop.c index 2a0a516b..782b5a51 100644 --- a/3P/libubox/lua/uloop.c +++ b/3P/libubox/lua/uloop.c @@ -43,6 +43,26 @@ struct lua_uloop_process { static lua_State *state; +static void * +ul_create_userdata(lua_State *L, size_t size, const luaL_Reg *reg, lua_CFunction gc) +{ + void *ret = lua_newuserdata(L, size); + + memset(ret, 0, size); + lua_createtable(L, 0, 2); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, gc); + lua_setfield(L, -2, "__gc"); + lua_pushvalue(L, -1); + lua_setmetatable(L, -3); + lua_pushvalue(L, -2); + luaI_openlib(L, NULL, reg, 1); + lua_pushvalue(L, -2); + + return ret; +} + static void ul_timer_cb(struct uloop_timeout *t) { struct lua_uloop_timeout *tout = container_of(t, struct lua_uloop_timeout, t); @@ -77,9 +97,9 @@ static int ul_timer_set(lua_State *L) 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"); @@ -120,22 +140,10 @@ static int ul_timer(lua_State *L) 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 = ul_create_userdata(L, sizeof(*tout), timer_m, ul_timer_free); tout->r = ref; tout->t.cb = ul_timer_cb; + if (set) uloop_timeout_set(&tout->t, set); @@ -181,7 +189,7 @@ static int get_sock_fd(lua_State* L, int idx) { 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*/ @@ -238,21 +246,7 @@ static int ul_ufd_add(lua_State *L) 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 = ul_create_userdata(L, sizeof(*ufd), ufd_m, ul_ufd_delete); ufd->r = ref; ufd->fd.fd = fd; ufd->fd_r = fd_ref; @@ -263,6 +257,32 @@ static int ul_ufd_add(lua_State *L) return 1; } +static int ul_process_free(lua_State *L) +{ + struct lua_uloop_process *proc = lua_touserdata(L, 1); + + /* 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); + + if (proc->r != LUA_NOREF) { + uloop_process_delete(&proc->p); + + lua_getglobal(state, "__uloop_cb"); + luaL_unref(state, -1, proc->r); + lua_remove(state, -1); + } + + return 1; +} + +static const luaL_Reg process_m[] = { + { "delete", ul_process_free }, + { NULL, NULL } +}; + static void ul_process_cb(struct uloop_process *p, int ret) { struct lua_uloop_process *proc = container_of(p, struct lua_uloop_process, p); @@ -271,6 +291,7 @@ static void ul_process_cb(struct uloop_process *p, int ret) lua_rawgeti(state, -1, proc->r); luaL_unref(state, -2, proc->r); + proc->r = LUA_NOREF; lua_remove(state, -2); lua_pushinteger(state, ret >> 8); lua_call(state, 1, 0); @@ -330,9 +351,7 @@ static int ul_process(lua_State *L) lua_pushvalue(L, -2); ref = luaL_ref(L, -2); - proc = lua_newuserdata(L, sizeof(*proc)); - memset(proc, 0, sizeof(*proc)); - + proc = ul_create_userdata(L, sizeof(*proc), process_m, ul_process_free); proc->r = ref; proc->p.pid = pid; proc->p.cb = ul_process_cb; diff --git a/3P/libubox/md5.c b/3P/libubox/md5.c index c9064dce..781dbd16 100644 --- a/3P/libubox/md5.c +++ b/3P/libubox/md5.c @@ -1,284 +1,336 @@ /* - * md5.c - Compute MD5 checksum of strings according to the - * definition of MD5 in RFC 1321 from April 1992. + * Copyright (C) 2014 Felix Fietkau * - * Written by Ulrich Drepper , 1995. + * 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. * - * 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 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). * - * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. */ -#include "blob.h" /* TODO: better include for bswap_32 compat */ - -#include -#include - -#include -#include +#include +#include +#include "utils.h" #include "md5.h" +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + */ #if __BYTE_ORDER == __LITTLE_ENDIAN -#define SWAP_LE32(x) (x) +#define SET(n) \ + (*(uint32_t *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) #else -#define SWAP_LE32(x) bswap_32(x) +#define SET(n) \ + (block[(n)] = \ + (uint32_t)ptr[(n) * 4] | \ + ((uint32_t)ptr[(n) * 4 + 1] << 8) | \ + ((uint32_t)ptr[(n) * 4 + 2] << 16) | \ + ((uint32_t)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (block[(n)]) #endif -/* Initialize structure containing state of computation. - * (RFC 1321, 3.3: Step 3) +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There are no alignment requirements. */ +static const void *body(md5_ctx_t *ctx, const void *data, unsigned long size) +{ + const unsigned char *ptr; + uint32_t a, b, c, d; + uint32_t saved_a, saved_b, saved_c, saved_d; +#if __BYTE_ORDER != __LITTLE_ENDIAN + uint32_t block[16]; +#endif + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + void md5_begin(md5_ctx_t *ctx) { - ctx->A = 0x67452301; - ctx->B = 0xefcdab89; - ctx->C = 0x98badcfe; - ctx->D = 0x10325476; + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; - ctx->total = 0; - ctx->buflen = 0; + ctx->lo = 0; + ctx->hi = 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) +void md5_hash(const void *data, size_t size, md5_ctx_t *ctx) { - uint32_t correct_words[16]; - const uint32_t *words = buffer; + uint32_t saved_lo; + unsigned long used, available; - 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 - }; + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; - 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 */ - }; + used = saved_lo & 0x3f; - static const char S_array[] = { - 7, 12, 17, 22, - 5, 9, 14, 20, - 4, 11, 16, 23, - 6, 10, 15, 21 - }; + if (used) { + available = 64 - used; - 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; + if (size < available) { + memcpy(&ctx->buffer[used], data, size); + return; } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); } + + if (size >= 64) { + data = body(ctx, data, size & ~((size_t) 0x3f)); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); } -/* 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; + unsigned char *result = resbuf; + unsigned long used, available; - /* Pad data to block size. */ + used = ctx->lo & 0x3f; - buf[ctx->buflen++] = 0x80; - memset(buf + ctx->buflen, 0, 128 - ctx->buflen); + ctx->buffer[used++] = 0x80; - /* Put the 64-bit file length in *bits* at the end of the buffer. */ - ctx->total <<= 3; - if (ctx->buflen > 56) - buf += 64; + available = 64 - used; - for (i = 0; i < 8; i++) - buf[56 + i] = ctx->total >> (i*8); + if (available < 8) { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } - /* Process last bytes. */ - if (buf != ctx->buffer) - md5_hash_block(ctx->buffer, ctx); - md5_hash_block(buf, ctx); + memset(&ctx->buffer[used], 0, available - 8); - /* 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); + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; + + body(ctx, ctx->buffer, 64); + + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; + + memset(ctx, 0, sizeof(*ctx)); } -int md5sum(char *file, uint32_t *md5) +int md5sum(char *file, void *md5_buf) { char buf[256]; md5_ctx_t ctx; - int len, fd; int ret = 0; + FILE *f; - memset(md5, 0, sizeof(*md5) * 4); - - fd = open(file, O_RDONLY); - if (fd < 0) + f = fopen(file, "r"); + if (!f) return -1; md5_begin(&ctx); do { - len = read(fd, buf, sizeof(buf)); - if (len < 0) { - if (errno == EINTR) - continue; - - ret = -1; - goto out; - } + int len = fread(buf, 1, sizeof(buf), f); if (!len) break; md5_hash(buf, len, &ctx); + ret += len; } while(1); - md5_end(md5, &ctx); -out: - close(fd); + md5_end(md5_buf, &ctx); + fclose(f); return ret; } diff --git a/3P/libubox/md5.h b/3P/libubox/md5.h index 63059891..b2f1b80c 100644 --- a/3P/libubox/md5.h +++ b/3P/libubox/md5.h @@ -1,36 +1,58 @@ /* - * Copyright (C) 2013 Felix Fietkau - * Copyright (C) 2013 John Crispin + * Copyright (C) 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 + * 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. * - * 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. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. */ -#ifndef __PROCD_MD5_H -#define __PROCD_MD5_H +#ifndef _LIBUBOX_MD5_H +#define _LIBUBOX_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]; + uint32_t lo, hi; + uint32_t a, b, c, d; + unsigned char buffer[64]; } 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); +extern void md5_begin(md5_ctx_t *ctx); +extern void md5_hash(const void *data, size_t length, md5_ctx_t *ctx); +extern void md5_end(void *resbuf, md5_ctx_t *ctx); +int md5sum(char *file, void *md5_buf); #endif diff --git a/3P/libubox/sh/jshn.sh b/3P/libubox/sh/jshn.sh index 5db1667d..bf76edbe 100644 --- a/3P/libubox/sh/jshn.sh +++ b/3P/libubox/sh/jshn.sh @@ -42,7 +42,7 @@ _json_inc() { # var=$1 # dest=$2 - eval "${JSON_PREFIX}$1=\$(( \${${JSON_PREFIX}$1:-0} + 1))${2:+; $2=\"\$${JSON_PREFIX}$1\"}" + let "${JSON_PREFIX}$1 += 1" "$2 = ${JSON_PREFIX}$1" } _json_add_generic() { @@ -52,20 +52,18 @@ _json_add_generic() { # cur=$4 local var - if [ "${4%%[0-9]*}" = "JSON_ARRAY" ]; then - _json_inc "SEQ_$4" var + if [ "${4%%[0-9]*}" = "J_A" ]; then + _json_inc "S_$4" var else - local name="${2//[^a-zA-Z0-9_]/_}" - [[ "$name" == "$2" ]] || export -- "${JSON_PREFIX}NAME_${4}_${name}=$2" - var="$name" + var="${2//[^a-zA-Z0-9_]/_}" + [[ "$var" == "$2" ]] || export -- "${JSON_PREFIX}N_${4}_${var}=$2" fi - local cur_var= export -- \ "${JSON_PREFIX}${4}_$var=$3" \ - "${JSON_PREFIX}TYPE_${4}_$var=$1" + "${JSON_PREFIX}T_${4}_$var=$1" _jshn_append "JSON_UNSET" "${4}_$var" - _jshn_append "KEYS_$4" "$var" + _jshn_append "K_$4" "$var" } _json_add_table() { @@ -77,10 +75,10 @@ _json_add_table() { _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" + local table="J_$3$seq" + _json_set_var "U_$table" "$cur" + export -- "${JSON_PREFIX}K_$table=" + unset "${JSON_PREFIX}S_$table" _json_set_var JSON_CUR "$table" _jshn_append "JSON_UNSET" "$table" @@ -91,7 +89,7 @@ _json_close_table() { local _s_cur _json_get_var _s_cur JSON_CUR - _json_get_var "${JSON_PREFIX}JSON_CUR" "UP_$_s_cur" + _json_get_var "${JSON_PREFIX}JSON_CUR" "U_$_s_cur" } json_set_namespace() { @@ -106,13 +104,13 @@ json_cleanup() { local unset tmp _json_get_var unset JSON_UNSET - for tmp in $unset JSON_VAR; do + for tmp in $unset J_V; do unset \ - ${JSON_PREFIX}UP_$tmp \ - ${JSON_PREFIX}KEYS_$tmp \ - ${JSON_PREFIX}SEQ_$tmp \ - ${JSON_PREFIX}TYPE_$tmp \ - ${JSON_PREFIX}NAME_$tmp \ + ${JSON_PREFIX}U_$tmp \ + ${JSON_PREFIX}K_$tmp \ + ${JSON_PREFIX}S_$tmp \ + ${JSON_PREFIX}T_$tmp \ + ${JSON_PREFIX}N_$tmp \ ${JSON_PREFIX}$tmp done @@ -124,15 +122,14 @@ json_cleanup() { json_init() { json_cleanup + export -n ${JSON_PREFIX}JSON_SEQ=0 export -- \ - ${JSON_PREFIX}JSON_SEQ=0 \ - ${JSON_PREFIX}JSON_CUR="JSON_VAR" \ - ${JSON_PREFIX}KEYS_JSON_VAR= \ - ${JSON_PREFIX}TYPE_JSON_VAR= + ${JSON_PREFIX}JSON_CUR="J_V" \ + ${JSON_PREFIX}K_J_V= } json_add_object() { - _json_add_table "$1" object TABLE + _json_add_table "$1" object T } json_close_object() { @@ -140,7 +137,7 @@ json_close_object() { } json_add_array() { - _json_add_table "$1" array ARRAY + _json_add_table "$1" array A } json_close_array() { @@ -186,7 +183,7 @@ json_get_type() { local __cur _json_get_var __cur JSON_CUR - local __var="${JSON_PREFIX}TYPE_${__cur}_${2//[^a-zA-Z0-9_]/_}" + local __var="${JSON_PREFIX}T_${__cur}_${2//[^a-zA-Z0-9_]/_}" eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]" } @@ -199,7 +196,7 @@ json_get_keys() { else _json_get_var _tbl_cur JSON_CUR fi - local __var="${JSON_PREFIX}KEYS_${_tbl_cur}" + local __var="${JSON_PREFIX}K_${_tbl_cur}" eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]" } @@ -252,12 +249,12 @@ json_select() { local cur [ -z "$1" ] && { - _json_set_var JSON_CUR "JSON_VAR" + _json_set_var JSON_CUR "J_V" return 0 } [[ "$1" == ".." ]] && { _json_get_var cur JSON_CUR - _json_get_var cur "UP_$cur" + _json_get_var cur "U_$cur" _json_set_var JSON_CUR "$cur" return 0 } diff --git a/3P/libubox/ulog.c b/3P/libubox/ulog.c new file mode 100644 index 00000000..296605dd --- /dev/null +++ b/3P/libubox/ulog.c @@ -0,0 +1,173 @@ +/* + * ulog - simple logging functions + * + * Copyright (C) 2015 Jo-Philipp Wich + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ulog.h" + +#include +#include +#include +#include +#include + +static int _ulog_channels = -1; +static int _ulog_facility = -1; +static int _ulog_threshold = LOG_DEBUG; +static int _ulog_initialized = 0; +static const char *_ulog_ident = NULL; + +static const char *ulog_default_ident(void) +{ + FILE *self; + static char line[64]; + char *p = NULL; + + if ((self = fopen("/proc/self/status", "r")) != NULL) { + while (fgets(line, sizeof(line), self)) { + if (!strncmp(line, "Name:", 5)) { + strtok(line, "\t\n"); + p = strtok(NULL, "\t\n"); + break; + } + } + fclose(self); + } + + return p; +} + +static void ulog_defaults(void) +{ + char *env; + + if (_ulog_initialized) + return; + + env = getenv("PREINIT"); + + if (_ulog_channels < 0) { + if (env && !strcmp(env, "1")) + _ulog_channels = ULOG_KMSG; + else if (isatty(1)) + _ulog_channels = ULOG_STDIO; + else + _ulog_channels = ULOG_SYSLOG; + } + + if (_ulog_facility < 0) { + if (env && !strcmp(env, "1")) + _ulog_facility = LOG_DAEMON; + else if (isatty(1)) + _ulog_facility = LOG_USER; + else + _ulog_facility = LOG_DAEMON; + } + + if (_ulog_ident == NULL && _ulog_channels != ULOG_STDIO) + _ulog_ident = ulog_default_ident(); + + if (_ulog_channels & ULOG_SYSLOG) + openlog(_ulog_ident, 0, _ulog_facility); + + _ulog_initialized = 1; +} + +static void ulog_kmsg(int priority, const char *fmt, va_list ap) +{ + FILE *kmsg; + + if ((kmsg = fopen("/dev/kmsg", "r+")) != NULL) { + fprintf(kmsg, "<%u>", priority); + + if (_ulog_ident) + fprintf(kmsg, "%s: ", _ulog_ident); + + vfprintf(kmsg, fmt, ap); + fclose(kmsg); + } +} + +static void ulog_stdio(int priority, const char *fmt, va_list ap) +{ + FILE *out = stderr; + + if (_ulog_ident) + fprintf(out, "%s: ", _ulog_ident); + + vfprintf(out, fmt, ap); +} + +static void ulog_syslog(int priority, const char *fmt, va_list ap) +{ + vsyslog(priority, fmt, ap); +} + +void ulog_open(int channels, int facility, const char *ident) +{ + ulog_close(); + + _ulog_channels = channels; + _ulog_facility = facility; + _ulog_ident = ident; +} + +void ulog_close(void) +{ + if (!_ulog_initialized) + return; + + if (_ulog_channels & ULOG_SYSLOG) + closelog(); + + _ulog_initialized = 0; +} + +void ulog_threshold(int threshold) +{ + _ulog_threshold = threshold; +} + +void ulog(int priority, const char *fmt, ...) +{ + va_list ap; + + if (priority > _ulog_threshold) + return; + + ulog_defaults(); + + if (_ulog_channels & ULOG_KMSG) + { + va_start(ap, fmt); + ulog_kmsg(priority, fmt, ap); + va_end(ap); + } + + if (_ulog_channels & ULOG_STDIO) + { + va_start(ap, fmt); + ulog_stdio(priority, fmt, ap); + va_end(ap); + } + + if (_ulog_channels & ULOG_SYSLOG) + { + va_start(ap, fmt); + ulog_syslog(priority, fmt, ap); + va_end(ap); + } +} diff --git a/3P/libubox/ulog.h b/3P/libubox/ulog.h new file mode 100644 index 00000000..4818b1a8 --- /dev/null +++ b/3P/libubox/ulog.h @@ -0,0 +1,42 @@ +/* + * ulog - simple logging functions + * + * Copyright (C) 2015 Jo-Philipp Wich + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __LIBUBOX_ULOG_H +#define __LIBUBOX_ULOG_H + +#include + +enum { + ULOG_KMSG = (1 << 0), + ULOG_SYSLOG = (1 << 1), + ULOG_STDIO = (1 << 2) +}; + +void ulog_open(int channels, int facility, const char *ident); +void ulog_close(void); + +void ulog_threshold(int threshold); + +void ulog(int priority, const char *fmt, ...); + +#define ULOG_INFO(fmt, ...) ulog(LOG_INFO, fmt, ## __VA_ARGS__) +#define ULOG_NOTE(fmt, ...) ulog(LOG_NOTICE, fmt, ## __VA_ARGS__) +#define ULOG_WARN(fmt, ...) ulog(LOG_WARNING, fmt, ## __VA_ARGS__) +#define ULOG_ERR(fmt, ...) ulog(LOG_ERR, fmt, ## __VA_ARGS__) + +#endif diff --git a/3P/libubox/uloop.c b/3P/libubox/uloop.c index 6b484125..4c272ce9 100644 --- a/3P/libubox/uloop.c +++ b/3P/libubox/uloop.c @@ -53,13 +53,12 @@ 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 struct list_head timeouts = LIST_HEAD_INIT(timeouts); +static __thread struct list_head processes = LIST_HEAD_INIT(processes); static __thread int poll_fd = -1; __thread bool uloop_cancelled = false; -__thread bool uloop_handle_sigchld = true; -static __thread bool do_sigchld = false; +__thread static bool do_sigchld = false; static __thread struct uloop_fd_event cur_fds[ULOOP_MAX_EVENTS]; static __thread int cur_fd, cur_nfds; @@ -213,10 +212,7 @@ 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; @@ -457,14 +453,14 @@ int uloop_timeout_set(struct uloop_timeout *timeout, int msecs) if (timeout->pending) uloop_timeout_cancel(timeout); - uloop_gettime(&timeout->time); + uloop_gettime(time); time->tv_sec += msecs / 1000; time->tv_usec += (msecs % 1000) * 1000; if (time->tv_usec > 1000000) { time->tv_sec++; - time->tv_usec %= 1000000; + time->tv_usec -= 1000000; } return uloop_timeout_add(timeout); @@ -562,31 +558,61 @@ static void uloop_sigchld(int signo) do_sigchld = true; } -static void uloop_setup_signals(bool add) +static void uloop_install_handler(int signum, void (*handler)(int), struct sigaction* old, bool add) { - static struct sigaction old_sigint, old_sigchld; struct sigaction s; + struct sigaction *act; - memset(&s, 0, sizeof(struct sigaction)); + act = NULL; + sigaction(signum, NULL, &s); if (add) { - s.sa_handler = uloop_handle_sigint; - s.sa_flags = 0; - } else { - s = old_sigint; + if (s.sa_handler == SIG_DFL) { /* Do not override existing custom signal handlers */ + memcpy(old, &s, sizeof(struct sigaction)); + s.sa_handler = handler; + s.sa_flags = 0; + act = &s; + } + } + else if (s.sa_handler == handler) { /* Do not restore if someone modified our handler */ + act = old; } - sigaction(SIGINT, &s, &old_sigint); + if (act != NULL) + sigaction(signum, act, NULL); +} - if (!uloop_handle_sigchld) - return; +static void uloop_ignore_signal(int signum, bool ignore) +{ + struct sigaction s; + void *new_handler = NULL; - if (add) - s.sa_handler = uloop_sigchld; - else - s = old_sigchld; + sigaction(signum, NULL, &s); - sigaction(SIGCHLD, &s, &old_sigchld); + if (ignore) { + if (s.sa_handler == SIG_DFL) /* Ignore only if there isn't any custom handler */ + new_handler = SIG_IGN; + } else { + if (s.sa_handler == SIG_IGN) /* Restore only if noone modified our SIG_IGN */ + new_handler = SIG_DFL; + } + + if (new_handler) { + s.sa_handler = new_handler; + s.sa_flags = 0; + sigaction(signum, &s, NULL); + } +} + +static void uloop_setup_signals(bool add) +{ + static struct sigaction old_sigint, old_sigchld, old_sigterm; + + uloop_install_handler(SIGINT, uloop_handle_sigint, &old_sigint, add); + uloop_install_handler(SIGTERM, uloop_handle_sigint, &old_sigterm, add); + uloop_install_handler(SIGCHLD, uloop_sigchld, &old_sigchld, add); + + uloop_ignore_signal(SIGPIPE, add); } static int uloop_get_next_timeout(struct timeval *tv) @@ -654,11 +680,13 @@ void uloop_run(void) { uloop_gettime(&tv); uloop_process_timeouts(&tv); - if (uloop_cancelled) - break; if (do_sigchld) uloop_handle_processes(); + + if (uloop_cancelled) + break; + uloop_gettime(&tv); uloop_run_events(uloop_get_next_timeout(&tv)); } diff --git a/3P/libubox/usock.c b/3P/libubox/usock.c index 04ed4ee5..0ce53904 100644 --- a/3P/libubox/usock.c +++ b/3P/libubox/usock.c @@ -20,14 +20,18 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include #include "usock.h" +#include "utils.h" static void usock_set_flags(int sock, unsigned int type) { @@ -38,7 +42,7 @@ static void usock_set_flags(int sock, unsigned int type) 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) +static int usock_connect(int type, struct sockaddr *sa, int sa_len, int family, int socktype, bool server) { int sock; @@ -46,6 +50,8 @@ static int usock_connect(struct sockaddr *sa, int sa_len, int family, int sockty if (sock < 0) return -1; + usock_set_flags(sock, type); + if (server) { const int one = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); @@ -62,9 +68,11 @@ static int usock_connect(struct sockaddr *sa, int sa_len, int family, int sockty return -1; } -static int usock_unix(const char *host, int socktype, bool server) +static int usock_unix(int type, const char *host) { struct sockaddr_un sun = {.sun_family = AF_UNIX}; + bool server = !!(type & USOCK_SERVER); + int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; if (strlen(host) >= sizeof(sun.sun_path)) { errno = EINVAL; @@ -72,11 +80,65 @@ static int usock_unix(const char *host, int socktype, bool server) } strcpy(sun.sun_path, host); - return usock_connect((struct sockaddr*)&sun, sizeof(sun), AF_UNIX, socktype, server); + return usock_connect(type, (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) +static int +usock_inet_notimeout(int type, struct addrinfo *result, void *addr) { + struct addrinfo *rp; + int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; + bool server = !!(type & USOCK_SERVER); + int sock; + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sock = usock_connect(type, rp->ai_addr, rp->ai_addrlen, rp->ai_family, socktype, server); + if (sock >= 0) { + if (addr) + memcpy(addr, rp->ai_addr, rp->ai_addrlen); + return sock; + } + } + + return -1; +} + +static int poll_restart(struct pollfd *fds, int nfds, int timeout) +{ + struct timespec ts, cur; + int msec = timeout % 1000; + int ret; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + ts.tv_nsec += msec * 1000000; + if (ts.tv_nsec > 1000000000) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + ts.tv_sec += timeout / 1000; + + while (1) { + ret = poll(fds, nfds, timeout); + if (ret == EAGAIN) + continue; + + if (ret != EINTR) + return ret; + + clock_gettime(CLOCK_MONOTONIC, &cur); + timeout = (ts.tv_sec - cur.tv_sec) * 1000; + timeout += (ts.tv_nsec - cur.tv_nsec) / 1000000; + if (timeout <= 0) + return 0; + } +} + +int usock_inet_timeout(int type, const char *host, const char *service, + void *addr, int timeout) +{ + int socktype = ((type & 0xff) == USOCK_TCP) ? SOCK_STREAM : SOCK_DGRAM; + bool server = !!(type & USOCK_SERVER); struct addrinfo *result, *rp; struct addrinfo hints = { .ai_family = (type & USOCK_IPV6ONLY) ? AF_INET6 : @@ -86,34 +148,146 @@ static int usock_inet(int type, const char *host, const char *service, int sockt | ((type & USOCK_SERVER) ? AI_PASSIVE : 0) | ((type & USOCK_NUMERIC) ? AI_NUMERICHOST : 0), }; + struct addrinfo *rp_v6 = NULL; + struct addrinfo *rp_v4 = NULL; + struct pollfd pfds[2] = { + { .fd = -1, .events = POLLOUT }, + { .fd = -1, .events = POLLOUT }, + }; int sock = -1; + int i; if (getaddrinfo(host, service, &hints, &result)) return -1; - 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; + if (timeout <= 0 || server) { + sock = usock_inet_notimeout(type, result, addr); + goto free_addrinfo; } + for (rp = result; rp != NULL; rp = rp->ai_next) { + if (rp->ai_family == AF_INET6 && !rp_v6) + rp_v6 = rp; + if (rp->ai_family == AF_INET && !rp_v4) + rp_v4 = rp; + } + + if (!rp_v6 && !rp_v4) + goto out; + + if (rp_v6) { + rp = rp_v6; + pfds[0].fd = usock_connect(type | USOCK_NONBLOCK, rp->ai_addr, + rp->ai_addrlen, rp->ai_family, + socktype, server); + if (pfds[0].fd < 0) { + rp_v6 = NULL; + goto try_v4; + } + + if (timeout > 300) { + if (poll_restart(pfds, 1, 300) == 1) { + rp = rp_v6; + sock = pfds[0].fd; + goto out; + } + } + timeout -= 300; + } + +try_v4: + if (rp_v4) { + rp = rp_v4; + pfds[1].fd = usock_connect(type | USOCK_NONBLOCK, rp->ai_addr, + rp->ai_addrlen, rp->ai_family, + socktype, server); + if (pfds[1].fd < 0) { + rp_v4 = NULL; + if (!rp_v6) + goto out; + goto wait; + } + } + +wait: + poll_restart(pfds + !rp_v6, !!rp_v6 + !!rp_v4, timeout); + if (pfds[0].revents & POLLOUT) { + rp = rp_v6; + sock = pfds[0].fd; + goto out; + } + + if (pfds[1].revents & POLLOUT) { + rp = rp_v4; + sock = pfds[1].fd; + goto out; + } + +out: + for (i = 0; i < 2; i++) { + int fd = pfds[i].fd; + if (fd >= 0 && fd != sock) + close(fd); + } + + if (!(type & USOCK_NONBLOCK)) + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) & ~O_NONBLOCK); + + if (addr && sock >= 0) + memcpy(addr, rp->ai_addr, rp->ai_addrlen); +free_addrinfo: freeaddrinfo(result); return sock; } +const char *usock_port(int port) +{ + static char buffer[sizeof("65535\0")]; + + if (port < 0 || port > 65535) + return NULL; + + snprintf(buffer, sizeof(buffer), "%u", port); + + return buffer; +} + int usock(int type, const char *host, const char *service) { - int 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); + sock = usock_unix(type, host); else - sock = usock_inet(type, host, service, socktype, server); + sock = usock_inet(type, host, service, NULL); if (sock < 0) return -1; - usock_set_flags(sock, type); return sock; } + +int usock_wait_ready(int fd, int msecs) { + struct pollfd fds[1]; + int res; + + fds[0].fd = fd; + fds[0].events = POLLOUT; + + res = poll(fds, 1, msecs); + if (res < 0) { + return errno; + } else if (res == 0) { + return -ETIMEDOUT; + } else { + int err = 0; + socklen_t optlen = sizeof(err); + + res = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &optlen); + if (res) + return errno; + if (err) + return err; + } + + return 0; +} diff --git a/3P/libubox/usock.h b/3P/libubox/usock.h index 5df4362c..5f2b84b0 100644 --- a/3P/libubox/usock.h +++ b/3P/libubox/usock.h @@ -30,6 +30,25 @@ #define USOCK_IPV4ONLY 0x4000 #define USOCK_UNIX 0x8000 +const char *usock_port(int port); int usock(int type, const char *host, const char *service); +int usock_inet_timeout(int type, const char *host, const char *service, + void *addr, int timeout); +static inline int +usock_inet(int type, const char *host, const char *service, void *addr) +{ + return usock_inet_timeout(type, host, service, addr, -1); +} + +/** + * Wait for a socket to become ready. + * + * This may be useful for users of USOCK_NONBLOCK to wait (with a timeout) + * for a socket. + * + * @param fd file descriptor of socket + * @param msecs timeout in microseconds + */ +int usock_wait_ready(int fd, int msecs); #endif /* USOCK_H_ */ diff --git a/3P/libubox/ustream-fd.c b/3P/libubox/ustream-fd.c index 4abb5305..b546fa1c 100644 --- a/3P/libubox/ustream-fd.c +++ b/3P/libubox/ustream-fd.c @@ -25,7 +25,7 @@ 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; + unsigned int flags = ULOOP_EDGE_TRIGGER | ULOOP_ERROR_CB; if (!s->read_blocked && !s->eof) flags |= ULOOP_READ; @@ -50,6 +50,9 @@ static void ustream_fd_read_pending(struct ustream_fd *sf, bool *more) char *buf; do { + if (s->read_blocked) + break; + buf = ustream_reserve(s, 1, &buflen); if (!buf) break; @@ -59,7 +62,7 @@ static void ustream_fd_read_pending(struct ustream_fd *sf, bool *more) if (errno == EINTR) continue; - if (errno == EAGAIN) + if (errno == EAGAIN || errno == ENOTCONN) return; len = 0; @@ -93,7 +96,7 @@ static int ustream_fd_write(struct ustream *s, const char *buf, int buflen, bool if (errno == EINTR) continue; - if (errno == EAGAIN || errno == EWOULDBLOCK) + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOTCONN) break; return -1; @@ -119,10 +122,17 @@ static bool __ustream_fd_poll(struct ustream_fd *sf, unsigned int events) ustream_fd_read_pending(sf, &more); if (events & ULOOP_WRITE) { - if (!ustream_write_pending(s)) + bool no_more = ustream_write_pending(s); + if (no_more) ustream_fd_set_uloop(s, false); } + if (sf->fd.error && !s->write_error) { + ustream_state_change(s); + s->write_error = true; + ustream_fd_set_uloop(s, false); + } + return more; } diff --git a/3P/libubox/ustream.c b/3P/libubox/ustream.c index 828a025b..e7ee9f07 100644 --- a/3P/libubox/ustream.c +++ b/3P/libubox/ustream.c @@ -145,21 +145,26 @@ static bool ustream_should_move(struct ustream_buf_list *l, struct ustream_buf * int maxlen; int offset; + /* nothing to squeeze */ if (buf->data == buf->head) return false; maxlen = buf->end - buf->head; offset = buf->data - buf->head; + /* less than half is available */ if (offset > maxlen / 2) return true; + /* less than 32 bytes data but takes more than 1/4 space */ if (buf->tail - buf->data < 32 && offset > maxlen / 4) return true; + /* more buf is already in list or can be allocated */ if (buf != l->tail || ustream_can_alloc(l)) return false; + /* no need to move if len is available at the tail */ return (buf->end - buf->tail < len); } @@ -255,13 +260,14 @@ static bool ustream_prepare_buf(struct ustream *s, struct ustream_buf_list *l, i if (l == &s->r) ustream_fixup_string(s, buf); } + /* some chunks available at the tail */ if (buf->tail != buf->end) return true; - } - - if (buf && buf->next) { - l->data_tail = buf->next; - return true; + /* next buf available */ + if (buf->next) { + l->data_tail = buf->next; + return true; + } } if (!ustream_can_alloc(l)) @@ -333,6 +339,26 @@ char *ustream_get_read_buf(struct ustream *s, int *buflen) return data; } +int ustream_read(struct ustream *s, char *buf, int buflen) +{ + char *chunk; + int chunk_len; + int len = 0; + + do { + chunk = ustream_get_read_buf(s, &chunk_len); + if (!chunk) + break; + if (chunk_len > buflen - len) + chunk_len = buflen - len; + memcpy(buf + len, chunk, chunk_len); + ustream_consume(s, chunk_len); + len += chunk_len; + } while (len < buflen); + + return len; +} + static void ustream_write_error(struct ustream *s) { if (!s->write_error) diff --git a/3P/libubox/ustream.h b/3P/libubox/ustream.h index 64317441..53e08280 100644 --- a/3P/libubox/ustream.h +++ b/3P/libubox/ustream.h @@ -143,6 +143,11 @@ void ustream_free(struct ustream *s); /* ustream_consume: remove data from the head of the read buffer */ void ustream_consume(struct ustream *s, int len); +/* + * ustream_read: read and consume data in read buffer into caller-specified + * area. Return length of data read. + */ +int ustream_read(struct ustream *s, char *buf, int buflen); /* ustream_write: add data to the write buffer */ int ustream_write(struct ustream *s, const char *buf, int len, bool more); int ustream_printf(struct ustream *s, const char *format, ...); diff --git a/3P/libubox/utils.c b/3P/libubox/utils.c index 078a8a19..8fd19f47 100644 --- a/3P/libubox/utils.c +++ b/3P/libubox/utils.c @@ -54,41 +54,50 @@ void *__calloc_a(size_t len, ...) } #ifdef __APPLE__ -#include +#include /* host_get_clock_service() */ +#include /* mach_port_deallocate() */ +#include /* mach_host_self(), mach_task_self() */ +#include /* clock_get_time() */ -static void clock_gettime_realtime(struct timespec *tv) +static clock_serv_t clock_realtime; +static clock_serv_t clock_monotonic; + +static void __constructor clock_name_init(void) { - struct timeval _tv; + mach_port_t host_self = mach_host_self(); - gettimeofday(&_tv, NULL); - tv->tv_sec = _tv.tv_sec; - tv->tv_nsec = _tv.tv_usec * 1000; + host_get_clock_service(host_self, CLOCK_REALTIME, &clock_realtime); + host_get_clock_service(host_self, CLOCK_MONOTONIC, &clock_monotonic); } -static void clock_gettime_monotonic(struct timespec *tv) +static void __destructor clock_name_dealloc(void) { - mach_timebase_info_data_t info; - float sec; - uint64_t val; + mach_port_t self = mach_task_self(); - 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; + mach_port_deallocate(self, clock_realtime); + mach_port_deallocate(self, clock_monotonic); } -void clock_gettime(int type, struct timespec *tv) +int clock_gettime(int type, struct timespec *tv) { - if (type == CLOCK_REALTIME) - return clock_gettime_realtime(tv); - else - return clock_gettime_monotonic(tv); + int retval = -1; + mach_timespec_t mts; + + switch (type) { + case CLOCK_REALTIME: + retval = clock_get_time(clock_realtime, &mts); + break; + case CLOCK_MONOTONIC: + retval = clock_get_time(clock_monotonic, &mts); + break; + default: + goto out; + } + + tv->tv_sec = mts.tv_sec; + tv->tv_nsec = mts.tv_nsec; +out: + return retval; } #endif diff --git a/3P/libubox/utils.h b/3P/libubox/utils.h index 9688a6a4..d00e76b2 100644 --- a/3P/libubox/utils.h +++ b/3P/libubox/utils.h @@ -58,10 +58,11 @@ extern int __BUILD_BUG_ON_CONDITION_FAILED; #ifdef __APPLE__ -#define CLOCK_REALTIME 0 -#define CLOCK_MONOTONIC 1 +#include +#define CLOCK_REALTIME CALENDAR_CLOCK +#define CLOCK_MONOTONIC SYSTEM_CLOCK -void clock_gettime(int type, struct timespec *tv); +int clock_gettime(int type, struct timespec *tv); #endif @@ -159,6 +160,10 @@ static inline uint16_t __u_bswap16(uint16_t val) #define __constructor __attribute__((constructor)) #endif +#ifndef __destructor +#define __destructor __attribute__((destructor)) +#endif + #ifndef __hidden #define __hidden __attribute__((visibility("hidden"))) #endif @@ -179,4 +184,12 @@ static inline bool bitfield_test(unsigned long *bits, int bit) return !!(bits[bit / BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))); } +int b64_encode(const void *src, size_t src_len, + void *dest, size_t dest_len); + +int b64_decode(const void *src, void *dest, size_t dest_len); + +#define B64_ENCODE_LEN(_len) ((((_len) + 2) / 3) * 4 + 1) +#define B64_DECODE_LEN(_len) (((_len) / 4) * 3 + 1) + #endif