Merge libubox with the version 2016.02.26 from its git.

This commit is contained in:
2016-02-26 23:13:29 +01:00
parent 6d5f11268b
commit 737ecc15d0
39 changed files with 2971 additions and 486 deletions

View File

@@ -8,3 +8,4 @@ CMakeFiles
install_manifest.txt
jshn
*-example
tests.*

71
3P/libubox/CMakeLists.txt Normal file
View File

@@ -0,0 +1,71 @@
cmake_minimum_required(VERSION 2.6)
INCLUDE(CheckLibraryExists)
INCLUDE(CheckFunctionExists)
PROJECT(ubox C)
ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations)
OPTION(BUILD_LUA "build Lua plugin" ON)
OPTION(BUILD_EXAMPLES "build examples" ON)
INCLUDE(FindPkgConfig)
PKG_SEARCH_MODULE(JSONC json-c)
IF(JSONC_FOUND)
ADD_DEFINITIONS(-DJSONC)
INCLUDE_DIRECTORIES(${JSONC_INCLUDE_DIRS})
ENDIF()
SET(SOURCES avl.c avl-cmp.c blob.c blobmsg.c uloop.c usock.c ustream.c ustream-fd.c vlist.c utils.c safe_list.c runqueue.c md5.c kvlist.c ulog.c base64.c)
ADD_LIBRARY(ubox SHARED ${SOURCES})
ADD_LIBRARY(ubox-static STATIC ${SOURCES})
SET_TARGET_PROPERTIES(ubox-static PROPERTIES OUTPUT_NAME ubox)
SET(LIBS)
CHECK_FUNCTION_EXISTS(clock_gettime HAVE_GETTIME)
IF(NOT HAVE_GETTIME)
CHECK_LIBRARY_EXISTS(rt clock_gettime "" NEED_GETTIME)
IF(NEED_GETTIME)
TARGET_LINK_LIBRARIES(ubox rt)
ENDIF()
ENDIF()
FILE(GLOB headers *.h)
INSTALL(FILES ${headers}
DESTINATION include/libubox
)
INSTALL(TARGETS ubox ubox-static
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
)
ADD_SUBDIRECTORY(lua)
ADD_SUBDIRECTORY(examples)
find_library(json NAMES json-c)
IF(EXISTS ${json})
ADD_LIBRARY(blobmsg_json SHARED blobmsg_json.c)
TARGET_LINK_LIBRARIES(blobmsg_json ubox ${json})
ADD_LIBRARY(blobmsg_json-static STATIC blobmsg_json.c)
SET_TARGET_PROPERTIES(blobmsg_json-static
PROPERTIES OUTPUT_NAME blobmsg_json)
ADD_EXECUTABLE(jshn jshn.c)
TARGET_LINK_LIBRARIES(jshn blobmsg_json ${json})
ADD_LIBRARY(json_script SHARED json_script.c)
TARGET_LINK_LIBRARIES(json_script ubox)
INSTALL(TARGETS blobmsg_json blobmsg_json-static jshn json_script
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
FILE(GLOB scripts sh/*.sh)
INSTALL(FILES ${scripts}
DESTINATION share/libubox
)
ENDIF()

327
3P/libubox/base64.c Normal file
View File

@@ -0,0 +1,327 @@
/*
* base64 - libubox base64 functions
*
* Copyright (C) 2015 Felix Fietkau <nbd@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* $OpenBSD: base64.c,v 1.7 2013/12/31 02:32:56 tedu Exp $ */
/*
* Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/*
* Portions Copyright (c) 1995 by International Business Machines, Inc.
*
* International Business Machines, Inc. (hereinafter called IBM) grants
* permission under its copyrights to use, copy, modify, and distribute this
* Software with or without fee, provided that the above copyright notice and
* all paragraphs of this notice appear in all copies, and that the name of IBM
* not be used in connection with the marketing of any product incorporating
* the Software or modifications thereof, without specific, written prior
* permission.
*
* To the extent it has a right to do so, IBM grants an immunity from suit
* under its patents, if any, for the use, sale or manufacture of products to
* the extent that such products are used for performing Domain Name System
* dynamic updates in TCP/IP networks by means of the Software. No immunity is
* granted for any product per se or for any other function of any product.
*
* THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
* DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
* IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
*/
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utils.h"
static const char Base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char Pad64 = '=';
/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
The following encoding technique is taken from RFC 1521 by Borenstein
and Freed. It is reproduced here in a slightly edited form for
convenience.
A 65-character subset of US-ASCII is used, enabling 6 bits to be
represented per printable character. (The extra 65th character, "=",
is used to signify a special processing function.)
The encoding process represents 24-bit groups of input bits as output
strings of 4 encoded characters. Proceeding from left to right, a
24-bit input group is formed by concatenating 3 8-bit input groups.
These 24 bits are then treated as 4 concatenated 6-bit groups, each
of which is translated into a single digit in the base64 alphabet.
Each 6-bit group is used as an index into an array of 64 printable
characters. The character referenced by the index is placed in the
output string.
Table 1: The Base64 Alphabet
Value Encoding Value Encoding Value Encoding Value Encoding
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w (pad) =
15 P 32 g 49 x
16 Q 33 h 50 y
Special processing is performed if fewer than 24 bits are available
at the end of the data being encoded. A full encoding quantum is
always completed at the end of a quantity. When fewer than 24 input
bits are available in an input group, zero bits are added (on the
right) to form an integral number of 6-bit groups. Padding at the
end of the data is performed using the '=' character.
Since all base64 input is an integral number of octets, only the
-------------------------------------------------
following cases can arise:
(1) the final quantum of encoding input is an integral
multiple of 24 bits; here, the final unit of encoded
output will be an integral multiple of 4 characters
with no "=" padding,
(2) the final quantum of encoding input is exactly 8 bits;
here, the final unit of encoded output will be two
characters followed by two "=" padding characters, or
(3) the final quantum of encoding input is exactly 16 bits;
here, the final unit of encoded output will be three
characters followed by one "=" padding character.
*/
int b64_encode(const void *_src, size_t srclength,
void *dest, size_t targsize)
{
const unsigned char *src = _src;
char *target = dest;
size_t datalength = 0;
u_char input[3] = {0};
u_char output[4];
int i;
while (2 < srclength) {
input[0] = *src++;
input[1] = *src++;
input[2] = *src++;
srclength -= 3;
output[0] = input[0] >> 2;
output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
output[3] = input[2] & 0x3f;
if (datalength + 4 > targsize)
return (-1);
target[datalength++] = Base64[output[0]];
target[datalength++] = Base64[output[1]];
target[datalength++] = Base64[output[2]];
target[datalength++] = Base64[output[3]];
}
/* Now we worry about padding. */
if (0 != srclength) {
/* Get what's left. */
input[0] = input[1] = input[2] = '\0';
for (i = 0; i < srclength; i++)
input[i] = *src++;
output[0] = input[0] >> 2;
output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
if (datalength + 4 > targsize)
return (-1);
target[datalength++] = Base64[output[0]];
target[datalength++] = Base64[output[1]];
if (srclength == 1)
target[datalength++] = Pad64;
else
target[datalength++] = Base64[output[2]];
target[datalength++] = Pad64;
}
if (datalength >= targsize)
return (-1);
target[datalength] = '\0'; /* Returned value doesn't count \0. */
return (datalength);
}
/* skips all whitespace anywhere.
converts characters, four at a time, starting at (or after)
src from base - 64 numbers into three 8 bit bytes in the target area.
it returns the number of data bytes stored at the target, or -1 on error.
*/
int b64_decode(const void *_src, void *dest, size_t targsize)
{
const char *src = _src;
unsigned char *target = dest;
int tarindex, state, ch;
u_char nextbyte;
char *pos;
state = 0;
tarindex = 0;
while ((ch = (unsigned char)*src++) != '\0') {
if (isspace(ch)) /* Skip whitespace anywhere. */
continue;
if (ch == Pad64)
break;
pos = strchr(Base64, ch);
if (pos == 0) /* A non-base64 character. */
return (-1);
switch (state) {
case 0:
if (target) {
if (tarindex >= targsize)
return (-1);
target[tarindex] = (pos - Base64) << 2;
}
state = 1;
break;
case 1:
if (target) {
if (tarindex >= targsize)
return (-1);
target[tarindex] |= (pos - Base64) >> 4;
nextbyte = ((pos - Base64) & 0x0f) << 4;
if (tarindex + 1 < targsize)
target[tarindex+1] = nextbyte;
else if (nextbyte)
return (-1);
}
tarindex++;
state = 2;
break;
case 2:
if (target) {
if (tarindex >= targsize)
return (-1);
target[tarindex] |= (pos - Base64) >> 2;
nextbyte = ((pos - Base64) & 0x03) << 6;
if (tarindex + 1 < targsize)
target[tarindex+1] = nextbyte;
else if (nextbyte)
return (-1);
}
tarindex++;
state = 3;
break;
case 3:
if (target) {
if (tarindex >= targsize)
return (-1);
target[tarindex] |= (pos - Base64);
}
tarindex++;
state = 0;
break;
}
}
/*
* We are done decoding Base-64 chars. Let's see if we ended
* on a byte boundary, and/or with erroneous trailing characters.
*/
if (ch == Pad64) { /* We got a pad char. */
ch = (unsigned char)*src++; /* Skip it, get next. */
switch (state) {
case 0: /* Invalid = in first position */
case 1: /* Invalid = in second position */
return (-1);
case 2: /* Valid, means one byte of info */
/* Skip any number of spaces. */
for (; ch != '\0'; ch = (unsigned char)*src++)
if (!isspace(ch))
break;
/* Make sure there is another trailing = sign. */
if (ch != Pad64)
return (-1);
ch = (unsigned char)*src++; /* Skip the = */
/* Fall through to "single trailing =" case. */
/* FALLTHROUGH */
case 3: /* Valid, means two bytes of info */
/*
* We know this char is an =. Is there anything but
* whitespace after it?
*/
for (; ch != '\0'; ch = (unsigned char)*src++)
if (!isspace(ch))
return (-1);
/*
* Now make sure for cases 2 and 3 that the "extra"
* bits that slopped past the last full byte were
* zeros. If we don't check them, they become a
* subliminal channel.
*/
if (target && tarindex < targsize &&
target[tarindex] != 0)
return (-1);
}
} else {
/*
* We ended by seeing the end of the string. Make sure we
* have no partial bytes lying around.
*/
if (state != 0)
return (-1);
}
/* Null-terminate if we have room left */
if (tarindex < targsize)
target[tarindex] = 0;
return (tarindex);
}

View File

@@ -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);

View File

@@ -17,6 +17,12 @@
#include "blobmsg.h"
#include "blobmsg_json.h"
#ifdef JSONC
#include <json.h>
#else
#include <json/json.h>
#endif
bool blobmsg_add_object(struct blob_buf *b, json_object *obj)
{
json_object_object_foreach(obj, key, val) {
@@ -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)

View File

@@ -16,17 +16,13 @@
#ifndef __BLOBMSG_JSON_H
#define __BLOBMSG_JSON_H
#ifdef JSONC
#include <json.h>
#else
#include <json/json.h>
#endif
struct json_object;
#include <stdbool.h>
#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);

View File

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

View File

@@ -1,4 +1,5 @@
#include <stdio.h>
#include <inttypes.h>
#include "blobmsg.h"
#include "blobmsg_json.h"

View File

@@ -0,0 +1,84 @@
#include <stdio.h>
#include <stdlib.h>
#include <json.h>
#include "blobmsg.h"
#include "blobmsg_json.h"
#include "json_script.h"
struct json_script_ctx jctx;
struct blob_buf b_vars;
struct blob_buf b_script;
static void handle_command(struct json_script_ctx *ctx, const char *name,
struct blob_attr *data, struct blob_attr *vars)
{
struct blob_attr *cur;
int rem;
fprintf(stdout, "%s", name);
blobmsg_for_each_attr(cur, data, rem)
fprintf(stdout, " %s", (char *) blobmsg_data(cur));
fprintf(stdout, "\n");
}
static struct json_script_file *
handle_file(struct json_script_ctx *ctx, const char *filename)
{
json_object *obj;
obj = json_object_from_file(filename);
if (!obj) {
fprintf(stderr, "load JSON data from %s failed.\n", filename);
return NULL;
}
blob_buf_init(&b_script, 0);
blobmsg_add_json_element(&b_script, "", obj);
json_object_put(obj);
return json_script_file_from_blobmsg(filename,
blob_data(b_script.head), blob_len(b_script.head));
}
static void usage(const char *prog, int exit_code)
{
fprintf(stderr, "Usage: %s [VARNAME=value] <filename_json_script>\n", prog);
exit(exit_code);
}
int main(int argc, char *argv[])
{
int i;
char *file = NULL;
const char *prog = argv[0];
blobmsg_buf_init(&b_vars);
blobmsg_buf_init(&b_script);
json_script_init(&jctx);
jctx.handle_command = handle_command;
jctx.handle_file = handle_file;
for (i = 1; i < argc; i++) {
char *sep = strchr(argv[i], '=');
if (sep) {
*sep = '\0';
blobmsg_add_string(&b_vars, argv[i], sep + 1);
} else if (!file) {
file = argv[i];
} else {
usage(prog, -1);
}
}
if (i < argc || !file)
usage(prog, -2);
json_script_run(&jctx, file, b_vars.head);
json_script_free(&jctx);
blob_buf_free(&b_script);
blob_buf_free(&b_vars);
return 0;
}

View File

@@ -0,0 +1,38 @@
[
[ "exec", "%EXECVAR%", "/%%/" ],
[ "if",
[ "eq", "EQVAR", "eqval" ],
[ "exec_if", "%VAR%", "%%", "jk" ]
],
[ "case", "CASEVAR", {
"caseval0": ["cmd_case_0", "cmd_case_arg0", "case_cmd_arg1"],
"caseval1": ["cmd_case_1", "cmd_case_arg0", "case_cmd_arg1"]
} ],
[ "if",
[ "and", [ "eq", "EQVAR", "eqval" ],
[ "has", "HASVAR" ],
[ "regex", "REGEXVAR0", "regexval" ],
[ "regex", "REGEXVAR1", [ "regexval10", "regexval11" ] ],
[ "not", [ "eq", "NOTEQVAR", "noteqval" ] ] ],
[ "exec_if_and", "%ANDVAR%" ]
],
[ "if",
[ "or", [ "eq", "EQVAR", "eqval" ],
[ "has", "HASVAR" ],
[ "regex", "REGEXVAR0", "regexval" ],
[ "regex", "REGEXVAR1", [ "regexval10", "regexval11" ] ],
[ "not", [ "eq", "NOTEQVAR", "noteqval" ] ] ],
[ "exec_if_or", "%ORVAR%" ]
],
[ "if",
[ "isdir", "%ISDIRVAR%" ],
[ "exec_isdir", "%ISDIRVAR%" ]
],
[ "return", "foobar" ],
[ "exec_non_reachable", "Arghhh" ]
]

View File

@@ -0,0 +1,287 @@
JSON_SCRIPT=tests.json
JSON_SCRIPT_BIN=./json_script-example
FILE_STDOUT=tests.stdout
FILE_STDERR=tests.stderr
FILE_EXPECTED=tests.expected
call_json_script() {
#export LD_PRELOAD=../libjson_script.so
$JSON_SCRIPT_BIN "$@" "$JSON_SCRIPT" >"$FILE_STDOUT" 2>"$FILE_STDERR"
}
assertStdioEquals() {
local expected="$1"
local file_stdio="$2"
echo "$expected" >"$FILE_EXPECTED"
if [ -z "$expected" ]; then
# we are expecting empty output, but we deliberately added a newline
# with echo above, so adding another echo to compensate for that
echo >>"$file_stdio"
fi
diff -up "$FILE_EXPECTED" "$file_stdio" >/dev/null 2>&1 || {
cat >&2 <<EOF
|--- expecting
$expected<
|--- actual
$(cat $file_stdio)<
|--- END
EOF
exit 1
}
}
assertStdoutEquals() {
assertStdioEquals "$1" "$FILE_STDOUT"
}
assertStderrEquals() {
assertStdioEquals "$1" "$FILE_STDERR"
}
test_bad_json() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ ]
[ ]
]
EOF
call_json_script
assertStderrEquals "load JSON data from $JSON_SCRIPT failed."
}
test_expr_eq() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ "if",
[ "eq", "VAR", "foo" ],
[ "echo", "bar" ],
[ "echo", "baz" ]
]
]
EOF
call_json_script "VAR=foo"
assertStdoutEquals "echo bar"
call_json_script "VAR=xxx"
assertStdoutEquals "echo baz"
}
test_expr_has() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ "if",
[ "has", "VAR" ],
[ "echo", "bar" ],
[ "echo", "baz" ]
]
]
EOF
call_json_script "VAR=foo"
assertStdoutEquals "echo bar"
call_json_script
assertStdoutEquals "echo baz"
}
test_expr_regex_single() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ "if",
[ "regex", "VAR", ".ell." ],
[ "echo", "bar" ],
[ "echo", "baz" ]
]
]
EOF
call_json_script "VAR=hello"
assertStdoutEquals "echo bar"
call_json_script "VAR=.ell."
assertStdoutEquals "echo bar"
call_json_script
assertStdoutEquals "echo baz"
call_json_script "VAR="
assertStdoutEquals "echo baz"
call_json_script "VAR=hell"
assertStdoutEquals "echo baz"
}
test_expr_regex_multi() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ "if",
[ "regex", "VAR", [ ".ell.", "w.rld" ] ],
[ "echo", "bar" ],
[ "echo", "baz" ]
]
]
EOF
call_json_script "VAR=hello"
assertStdoutEquals "echo bar"
call_json_script "VAR=world"
assertStdoutEquals "echo bar"
call_json_script "VAR=.ell."
assertStdoutEquals "echo bar"
call_json_script "VAR=w.rld"
assertStdoutEquals "echo bar"
call_json_script
assertStdoutEquals "echo baz"
call_json_script "VAR="
assertStdoutEquals "echo baz"
call_json_script "VAR=hell"
assertStdoutEquals "echo baz"
}
test_expr_not() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ "if",
[ "not", [ "has", "VAR" ] ],
[ "echo", "bar" ],
[ "echo", "baz" ]
]
]
EOF
call_json_script "VAR=foo"
assertStdoutEquals "echo baz"
call_json_script
assertStdoutEquals "echo bar"
}
test_expr_and() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ "if",
[ "and", [ "eq", "EQVAR", "eqval" ],
[ "regex", "REGEXVAR", "regex..." ]
],
[ "echo", "bar" ],
[ "echo", "baz" ]
]
]
EOF
call_json_script "EQVAR=eqval" "REGEXVAR=regexval"
assertStdoutEquals "echo bar"
call_json_script "EQVAR=foo"
assertStdoutEquals "echo baz"
call_json_script "REGEXVAR=regex***"
assertStdoutEquals "echo baz"
call_json_script
assertStdoutEquals "echo baz"
}
test_expr_or() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ "if",
[ "or", [ "not", [ "eq", "EQVAR", "eqval" ] ],
[ "regex", "REGEXVAR", [ "regexva.[0-9]", "regexva.[a-z]" ] ]
],
[ "echo", "bar" ],
[ "echo", "baz" ]
]
]
EOF
call_json_script "EQVAR=eqval" "REGEXVAR=regexval1"
assertStdoutEquals "echo bar"
call_json_script "EQVAR=neq" "REGEXVAR=sxc"
assertStdoutEquals "echo bar"
call_json_script "REGEXVAR=sxc"
assertStdoutEquals "echo bar"
call_json_script "EQVAR=foo"
assertStdoutEquals "echo bar"
call_json_script
assertStdoutEquals "echo bar"
call_json_script "EQVAR=eqval" "REGEXVAR=regexval"
assertStdoutEquals "echo baz"
}
test_expr_isdir() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ "if",
[ "isdir", "%VAR%" ],
[ "echo", "bar" ],
[ "echo", "baz" ]
]
]
EOF
call_json_script "VAR=/"
assertStdoutEquals "echo bar"
call_json_script "VAR=$(mktemp -u)"
assertStdoutEquals "echo baz"
call_json_script
assertStdoutEquals "echo baz"
}
test_cmd_case() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ "case", "CASEVAR", {
"0": [ "echo", "foo" ],
"1": [
[ "echo", "bar" ],
[ "echo", "baz" ]
],
"%VAR%": [ "echo", "quz" ]
} ]
]
EOF
call_json_script "CASEVAR=0"
assertStdoutEquals "echo foo"
call_json_script "CASEVAR=1"
assertStdoutEquals "echo bar
echo baz"
call_json_script "CASEVAR=%VAR%"
assertStdoutEquals "echo quz"
call_json_script "CASEVAR="
assertStdoutEquals ""
call_json_script
assertStdoutEquals ""
call_json_script "CASEVAR=xxx" "VAR=xxx"
assertStdoutEquals ""
}
test_cmd_if() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ "if",
[ "eq", "VAR", "foo" ],
[ "echo", "bar" ],
[ "echo", "baz" ]
]
]
EOF
call_json_script "VAR=foo"
assertStdoutEquals "echo bar"
call_json_script "VAR=xxx"
assertStdoutEquals "echo baz"
}
test_cmd_cb() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ "exec", "%VAR%", "/%VAS%%%/" ]
]
EOF
call_json_script
assertStdoutEquals "exec /%/"
call_json_script "VAR="
assertStdoutEquals "exec /%/"
call_json_script "VAR=qux" "VAS=3"
assertStdoutEquals "exec qux /3%/"
}
test_cmd_return() {
cat >"$JSON_SCRIPT" <<-EOF
[
[ "heh", "%HEHVAR%" ],
[ "%VAR%", "%VAR%" ],
[ "return" ],
[ "exec_non_reachable", "Arghhh" ]
]
EOF
call_json_script "HEHVAR=dude" "VAR=ow"
assertStdoutEquals "heh dude
%VAR% ow"
}
. ./shunit2

View File

@@ -16,11 +16,11 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <libubox/uloop.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "uloop.h"
#include "runqueue.h"
static struct runqueue q;

1067
3P/libubox/examples/shunit2 Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -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
*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
@@ -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);
@@ -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;

View File

@@ -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 <nbd@openwrt.org>
*
* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 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 <solar at openwall.com>
*
* This software was written by Alexander Peslyak in 2001. No copyright is
* claimed, and the software is hereby placed in the public domain.
* In case this attempt to disclaim copyright and place the software in the
* public domain is deemed null and void, then the software is
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* (This is a heavily cut-down "BSD license".)
*
* This differs from Colin Plumb's older public domain implementation in that
* no exactly 32-bit integer data type is required (any 32-bit or wider
* unsigned integer data type will do), there's no compile-time endianness
* configuration, and the function prototypes match OpenSSL's. No code from
* Colin Plumb's implementation has been reused; this comment merely compares
* the properties of the two independent implementations.
*
* The primary goals of this implementation are portability and ease of use.
* It is meant to be fast, but not as fast as possible. Some known
* optimizations are not included to reduce source code size and avoid
* compile-time configuration.
*/
#include "blob.h" /* TODO: better include for bswap_32 compat */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include "utils.h"
#include "md5.h"
/*
* The basic MD5 functions.
*
* F and G are optimized compared to their RFC 1321 definitions for
* architectures that lack an AND-NOT instruction, just like in Colin Plumb's
* implementation.
*/
#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
#define H(x, y, z) (((x) ^ (y)) ^ (z))
#define H2(x, y, z) ((x) ^ ((y) ^ (z)))
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
/*
* The MD5 transformation for all four rounds.
*/
#define STEP(f, a, b, c, d, x, t, s) \
(a) += f((b), (c), (d)) + (x) + (t); \
(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
(a) += (b);
/*
* SET reads 4 input bytes in little-endian byte order and stores them
* in a properly aligned word in host byte order.
*/
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define 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]);
if (size < available) {
memcpy(&ctx->buffer[used], data, size);
return;
}
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;
memcpy(&ctx->buffer[used], data, available);
data = (const unsigned char *)data + available;
size -= available;
body(ctx, ctx->buffer, 64);
}
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;
if (size >= 64) {
data = body(ctx, data, size & ~((size_t) 0x3f));
size &= 0x3f;
}
ctx->A += A;
ctx->B += B;
ctx->C += C;
ctx->D += D;
memcpy(ctx->buffer, data, size);
}
/* Feed data through a temporary buffer to call md5_hash_aligned_block()
* with chunks of data that are 4-byte aligned and a multiple of 64 bytes.
* This function's internal buffer remembers previous data until it has 64
* bytes worth to pass on. Call md5_end() to flush this buffer. */
void md5_hash(const void *buffer, size_t len, md5_ctx_t *ctx)
{
char *buf = (char *)buffer;
/* RFC 1321 specifies the possible length of the file up to 2^64 bits,
* Here we only track the number of bytes. */
ctx->total += len;
// Process all input.
while (len) {
unsigned i = 64 - ctx->buflen;
// Copy data into aligned buffer.
if (i > len)
i = len;
memcpy(ctx->buffer + ctx->buflen, buf, i);
len -= i;
ctx->buflen += i;
buf += i;
// When buffer fills up, process it.
if (ctx->buflen == 64) {
md5_hash_block(ctx->buffer, ctx);
ctx->buflen = 0;
}
}
}
/* Process the remaining bytes in the buffer and put result from CTX
* in first 16 bytes following RESBUF. The result is always in little
* endian byte order, so that a byte-wise output yields to the wanted
* ASCII representation of the message digest.
*
* IMPORTANT: On some systems it is required that RESBUF is correctly
* aligned for a 32 bits value.
*/
void md5_end(void *resbuf, md5_ctx_t *ctx)
{
char *buf = ctx->buffer;
int i;
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;
}

View File

@@ -1,36 +1,58 @@
/*
* Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2013 John Crispin <blogic@openwrt.org>
* Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>
*
* 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 <solar at openwall.com>
*
* This software was written by Alexander Peslyak in 2001. No copyright is
* claimed, and the software is hereby placed in the public domain.
* In case this attempt to disclaim copyright and place the software in the
* public domain is deemed null and void, then the software is
* Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
* general public under the following terms:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted.
*
* There's ABSOLUTELY NO WARRANTY, express or implied.
*
* See md5.c for more information.
*/
#ifndef __PROCD_MD5_H
#define __PROCD_MD5_H
#ifndef _LIBUBOX_MD5_H
#define _LIBUBOX_MD5_H
#include <stdint.h>
#include <stddef.h>
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

View File

@@ -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
}

173
3P/libubox/ulog.c Normal file
View File

@@ -0,0 +1,173 @@
/*
* ulog - simple logging functions
*
* Copyright (C) 2015 Jo-Philipp Wich <jow@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "ulog.h"
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
static int _ulog_channels = -1;
static int _ulog_facility = -1;
static int _ulog_threshold = LOG_DEBUG;
static int _ulog_initialized = 0;
static const char *_ulog_ident = NULL;
static const char *ulog_default_ident(void)
{
FILE *self;
static char line[64];
char *p = NULL;
if ((self = fopen("/proc/self/status", "r")) != NULL) {
while (fgets(line, sizeof(line), self)) {
if (!strncmp(line, "Name:", 5)) {
strtok(line, "\t\n");
p = strtok(NULL, "\t\n");
break;
}
}
fclose(self);
}
return p;
}
static void ulog_defaults(void)
{
char *env;
if (_ulog_initialized)
return;
env = getenv("PREINIT");
if (_ulog_channels < 0) {
if (env && !strcmp(env, "1"))
_ulog_channels = ULOG_KMSG;
else if (isatty(1))
_ulog_channels = ULOG_STDIO;
else
_ulog_channels = ULOG_SYSLOG;
}
if (_ulog_facility < 0) {
if (env && !strcmp(env, "1"))
_ulog_facility = LOG_DAEMON;
else if (isatty(1))
_ulog_facility = LOG_USER;
else
_ulog_facility = LOG_DAEMON;
}
if (_ulog_ident == NULL && _ulog_channels != ULOG_STDIO)
_ulog_ident = ulog_default_ident();
if (_ulog_channels & ULOG_SYSLOG)
openlog(_ulog_ident, 0, _ulog_facility);
_ulog_initialized = 1;
}
static void ulog_kmsg(int priority, const char *fmt, va_list ap)
{
FILE *kmsg;
if ((kmsg = fopen("/dev/kmsg", "r+")) != NULL) {
fprintf(kmsg, "<%u>", priority);
if (_ulog_ident)
fprintf(kmsg, "%s: ", _ulog_ident);
vfprintf(kmsg, fmt, ap);
fclose(kmsg);
}
}
static void ulog_stdio(int priority, const char *fmt, va_list ap)
{
FILE *out = stderr;
if (_ulog_ident)
fprintf(out, "%s: ", _ulog_ident);
vfprintf(out, fmt, ap);
}
static void ulog_syslog(int priority, const char *fmt, va_list ap)
{
vsyslog(priority, fmt, ap);
}
void ulog_open(int channels, int facility, const char *ident)
{
ulog_close();
_ulog_channels = channels;
_ulog_facility = facility;
_ulog_ident = ident;
}
void ulog_close(void)
{
if (!_ulog_initialized)
return;
if (_ulog_channels & ULOG_SYSLOG)
closelog();
_ulog_initialized = 0;
}
void ulog_threshold(int threshold)
{
_ulog_threshold = threshold;
}
void ulog(int priority, const char *fmt, ...)
{
va_list ap;
if (priority > _ulog_threshold)
return;
ulog_defaults();
if (_ulog_channels & ULOG_KMSG)
{
va_start(ap, fmt);
ulog_kmsg(priority, fmt, ap);
va_end(ap);
}
if (_ulog_channels & ULOG_STDIO)
{
va_start(ap, fmt);
ulog_stdio(priority, fmt, ap);
va_end(ap);
}
if (_ulog_channels & ULOG_SYSLOG)
{
va_start(ap, fmt);
ulog_syslog(priority, fmt, ap);
va_end(ap);
}
}

42
3P/libubox/ulog.h Normal file
View File

@@ -0,0 +1,42 @@
/*
* ulog - simple logging functions
*
* Copyright (C) 2015 Jo-Philipp Wich <jow@openwrt.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __LIBUBOX_ULOG_H
#define __LIBUBOX_ULOG_H
#include <syslog.h>
enum {
ULOG_KMSG = (1 << 0),
ULOG_SYSLOG = (1 << 1),
ULOG_STDIO = (1 << 2)
};
void ulog_open(int channels, int facility, const char *ident);
void ulog_close(void);
void ulog_threshold(int threshold);
void ulog(int priority, const char *fmt, ...);
#define ULOG_INFO(fmt, ...) ulog(LOG_INFO, fmt, ## __VA_ARGS__)
#define ULOG_NOTE(fmt, ...) ulog(LOG_NOTICE, fmt, ## __VA_ARGS__)
#define ULOG_WARN(fmt, ...) ulog(LOG_WARNING, fmt, ## __VA_ARGS__)
#define ULOG_ERR(fmt, ...) ulog(LOG_ERR, fmt, ## __VA_ARGS__)
#endif

View File

@@ -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;
@@ -214,9 +213,6 @@ 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;
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;
} else {
s = old_sigint;
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));
}

View File

@@ -20,14 +20,18 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <netdb.h>
#include <poll.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <poll.h>
#include "usock.h"
#include "utils.h"
static void usock_set_flags(int sock, unsigned int type)
{
@@ -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;
}

View File

@@ -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_ */

View File

@@ -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,7 +122,14 @@ 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);
}

View File

@@ -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,14 +260,15 @@ 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) {
/* next buf available */
if (buf->next) {
l->data_tail = buf->next;
return true;
}
}
if (!ustream_can_alloc(l))
return false;
@@ -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)

View File

@@ -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, ...);

View File

@@ -54,41 +54,50 @@ void *__calloc_a(size_t len, ...)
}
#ifdef __APPLE__
#include <mach/mach_time.h>
#include <mach/mach_host.h> /* host_get_clock_service() */
#include <mach/mach_port.h> /* mach_port_deallocate() */
#include <mach/mach_init.h> /* mach_host_self(), mach_task_self() */
#include <mach/clock.h> /* clock_get_time() */
static 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

View File

@@ -58,10 +58,11 @@ extern int __BUILD_BUG_ON_CONDITION_FAILED;
#ifdef __APPLE__
#define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 1
#include <mach/clock_types.h>
#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