2018-11-20 02:46:32 +01:00
|
|
|
#include <ccan/io/io.h>
|
|
|
|
/* To reach into io_plan: not a public header! */
|
|
|
|
#include <ccan/io/backend.h>
|
2019-04-08 11:58:44 +02:00
|
|
|
#include <ccan/str/hex/hex.h>
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
#include <common/daemon.h>
|
2018-11-20 02:46:32 +01:00
|
|
|
#include <common/utils.h>
|
|
|
|
#include <lightningd/json.h>
|
|
|
|
#include <lightningd/json_stream.h>
|
2019-02-18 03:44:29 +01:00
|
|
|
#include <lightningd/log.h>
|
2018-11-20 02:46:32 +01:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
struct json_stream {
|
|
|
|
#if DEVELOPER
|
|
|
|
/* tal_arr of types (JSMN_OBJECT/JSMN_ARRAY) we're enclosed in. */
|
|
|
|
jsmntype_t *wrapping;
|
|
|
|
#endif
|
|
|
|
/* True if we haven't yet put an element in current wrapping */
|
|
|
|
bool empty;
|
|
|
|
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
/* True if we ran out of memory: don't touch outbuf! */
|
|
|
|
bool oom;
|
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
/* Who is writing to this buffer now; NULL if nobody is. */
|
|
|
|
struct command *writer;
|
|
|
|
|
|
|
|
/* Who is io_writing from this buffer now: NULL if nobody is. */
|
|
|
|
struct io_conn *reader;
|
2018-11-20 02:46:32 +01:00
|
|
|
struct io_plan *(*reader_cb)(struct io_conn *conn,
|
|
|
|
struct json_stream *js,
|
|
|
|
void *arg);
|
2018-11-20 02:46:32 +01:00
|
|
|
void *reader_arg;
|
|
|
|
size_t len_read;
|
|
|
|
|
2019-02-18 03:44:29 +01:00
|
|
|
/* Where to log I/O */
|
|
|
|
struct log *log;
|
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
/* Current command's output. */
|
|
|
|
MEMBUF(char) outbuf;
|
|
|
|
};
|
|
|
|
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
static void free_json_stream_membuf(struct json_stream *js)
|
2018-11-20 02:46:32 +01:00
|
|
|
{
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
free(membuf_cleanup(&js->outbuf));
|
2018-11-20 02:46:32 +01:00
|
|
|
}
|
|
|
|
|
2019-02-18 03:44:29 +01:00
|
|
|
struct json_stream *new_json_stream(const tal_t *ctx,
|
|
|
|
struct command *writer,
|
|
|
|
struct log *log)
|
2018-11-20 02:46:32 +01:00
|
|
|
{
|
|
|
|
struct json_stream *js = tal(ctx, struct json_stream);
|
|
|
|
|
|
|
|
js->writer = writer;
|
|
|
|
js->reader = NULL;
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
/* We don't use tal here, because we handle failure externally (tal
|
|
|
|
* helpfully aborts with a msg, which is usually right) */
|
|
|
|
membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
|
|
|
|
tal_add_destructor(js, free_json_stream_membuf);
|
2018-11-20 02:46:32 +01:00
|
|
|
#if DEVELOPER
|
|
|
|
js->wrapping = tal_arr(js, jsmntype_t, 0);
|
|
|
|
#endif
|
|
|
|
js->empty = true;
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
js->oom = false;
|
2019-02-18 03:44:29 +01:00
|
|
|
js->log = log;
|
2018-11-20 02:46:32 +01:00
|
|
|
return js;
|
|
|
|
}
|
|
|
|
|
2019-05-31 04:01:07 +02:00
|
|
|
struct json_stream *json_stream_dup(const tal_t *ctx,
|
|
|
|
struct json_stream *original,
|
|
|
|
struct log *log)
|
2018-12-12 19:09:06 +01:00
|
|
|
{
|
|
|
|
size_t num_elems = membuf_num_elems(&original->outbuf);
|
|
|
|
char *elems = membuf_elems(&original->outbuf);
|
|
|
|
struct json_stream *js = tal_dup(ctx, struct json_stream, original);
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
|
|
|
|
if (!js->oom) {
|
|
|
|
char *newelems = malloc(sizeof(*elems) * num_elems);
|
|
|
|
if (!newelems)
|
|
|
|
js->oom = true;
|
|
|
|
else {
|
|
|
|
memcpy(newelems, elems, sizeof(*elems) * num_elems);
|
|
|
|
tal_add_destructor(js, free_json_stream_membuf);
|
|
|
|
membuf_init(&js->outbuf, newelems, num_elems,
|
|
|
|
membuf_realloc);
|
|
|
|
membuf_added(&js->outbuf, num_elems);
|
|
|
|
}
|
|
|
|
}
|
2019-05-31 04:01:07 +02:00
|
|
|
js->log = log;
|
2018-12-12 19:09:06 +01:00
|
|
|
return js;
|
|
|
|
}
|
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
bool json_stream_still_writing(const struct json_stream *js)
|
|
|
|
{
|
|
|
|
return js->writer != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_stream_close(struct json_stream *js, struct command *writer)
|
|
|
|
{
|
|
|
|
/* FIXME: We use writer == NULL for malformed: make writer a void *?
|
|
|
|
* I used to assert(writer); here. */
|
|
|
|
assert(js->writer == writer);
|
|
|
|
|
|
|
|
js->writer = NULL;
|
|
|
|
}
|
|
|
|
|
2019-05-23 12:09:17 +02:00
|
|
|
void json_stream_log_suppress(struct json_stream *js, const char *cmd_name)
|
|
|
|
{
|
|
|
|
/* Really shouldn't be used for anything else */
|
|
|
|
assert(streq(cmd_name, "getlog"));
|
|
|
|
js->log = NULL;
|
|
|
|
}
|
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
/* FIXME: This, or something prettier (io_replan?) belong in ccan/io! */
|
|
|
|
static void adjust_io_write(struct io_conn *conn, ptrdiff_t delta)
|
|
|
|
{
|
|
|
|
conn->plan[IO_OUT].arg.u1.cp += delta;
|
|
|
|
}
|
|
|
|
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
/* Make sure js->outbuf has room for len: return pointer, or NULL on OOM. */
|
2019-04-08 11:58:44 +02:00
|
|
|
static char *mkroom(struct json_stream *js, size_t len)
|
2018-11-20 02:46:32 +01:00
|
|
|
{
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
ptrdiff_t delta;
|
|
|
|
assert(!js->oom);
|
|
|
|
|
|
|
|
delta = membuf_prepare_space(&js->outbuf, len);
|
|
|
|
if (membuf_num_space(&js->outbuf) < len) {
|
|
|
|
char msg[100];
|
|
|
|
|
|
|
|
/* Be a little paranoid: avoid allocations here */
|
|
|
|
snprintf(msg, sizeof(msg),
|
|
|
|
"Out of memory allocating JSON membuf len %zu+%zu",
|
|
|
|
membuf_num_elems(&js->outbuf), len);
|
|
|
|
|
|
|
|
/* Clean it up immediately, in case we need the mem. */
|
|
|
|
js->oom = true;
|
|
|
|
free_json_stream_membuf(js);
|
|
|
|
tal_del_destructor(js, free_json_stream_membuf);
|
|
|
|
send_backtrace(msg);
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-11-20 02:46:32 +01:00
|
|
|
|
|
|
|
/* If io_write is in progress, we shift it to point to new buffer pos */
|
|
|
|
if (js->reader)
|
|
|
|
adjust_io_write(js->reader, delta);
|
2019-04-08 11:58:44 +02:00
|
|
|
|
|
|
|
return membuf_space(&js->outbuf);
|
2018-11-20 02:46:32 +01:00
|
|
|
}
|
|
|
|
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
/* Also called when we're oom, so it will kill reader. */
|
2018-11-20 02:46:32 +01:00
|
|
|
static void js_written_some(struct json_stream *js)
|
|
|
|
{
|
|
|
|
/* Wake the stream reader. FIXME: Could have a flag here to optimize */
|
|
|
|
io_wake(js);
|
|
|
|
}
|
|
|
|
|
2018-12-08 01:35:56 +01:00
|
|
|
void json_stream_append_part(struct json_stream *js, const char *str, size_t len)
|
2018-11-20 02:46:32 +01:00
|
|
|
{
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
if (js->oom || !mkroom(js, len))
|
|
|
|
return;
|
2018-11-20 02:46:32 +01:00
|
|
|
memcpy(membuf_add(&js->outbuf, len), str, len);
|
|
|
|
js_written_some(js);
|
|
|
|
}
|
|
|
|
|
2018-12-08 01:35:56 +01:00
|
|
|
void json_stream_append(struct json_stream *js, const char *str)
|
|
|
|
{
|
|
|
|
json_stream_append_part(js, str, strlen(str));
|
|
|
|
}
|
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
static void json_stream_append_vfmt(struct json_stream *js,
|
|
|
|
const char *fmt, va_list ap)
|
|
|
|
{
|
|
|
|
size_t fmtlen;
|
|
|
|
va_list ap2;
|
|
|
|
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
if (js->oom)
|
|
|
|
return;
|
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
/* Make a copy in case we need it below. */
|
|
|
|
va_copy(ap2, ap);
|
|
|
|
|
|
|
|
/* Try printing in place first. */
|
|
|
|
fmtlen = vsnprintf(membuf_space(&js->outbuf),
|
|
|
|
membuf_num_space(&js->outbuf), fmt, ap);
|
|
|
|
|
|
|
|
/* Horrible subtlety: vsnprintf *will* NUL terminate, even if it means
|
|
|
|
* chopping off the last character. So if fmtlen ==
|
|
|
|
* membuf_num_space(&jcon->outbuf), the result was truncated! */
|
|
|
|
if (fmtlen >= membuf_num_space(&js->outbuf)) {
|
|
|
|
/* Make room for NUL terminator, even though we don't want it */
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
char *p = mkroom(js, fmtlen + 1);
|
|
|
|
if (!p)
|
|
|
|
goto oom;
|
|
|
|
vsprintf(p, fmt, ap2);
|
2018-11-20 02:46:32 +01:00
|
|
|
}
|
|
|
|
membuf_added(&js->outbuf, fmtlen);
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
|
|
|
|
oom:
|
2018-11-20 02:46:32 +01:00
|
|
|
js_written_some(js);
|
|
|
|
va_end(ap2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PRINTF_FMT(2,3)
|
|
|
|
json_stream_append_fmt(struct json_stream *js, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
json_stream_append_vfmt(js, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void check_fieldname(const struct json_stream *js,
|
|
|
|
const char *fieldname)
|
|
|
|
{
|
|
|
|
#if DEVELOPER
|
|
|
|
size_t n = tal_count(js->wrapping);
|
|
|
|
if (n == 0)
|
|
|
|
/* Can't have a fieldname if not in anything! */
|
|
|
|
assert(!fieldname);
|
|
|
|
else if (js->wrapping[n-1] == JSMN_ARRAY)
|
|
|
|
/* No fieldnames in arrays. */
|
|
|
|
assert(!fieldname);
|
|
|
|
else
|
|
|
|
/* Must have fieldnames in objects. */
|
|
|
|
assert(fieldname);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
/* Caller must call js_written_some() if extra is non-zero returns non-NULL!
|
|
|
|
* Can return NULL, beware:
|
2019-04-08 11:58:44 +02:00
|
|
|
*/
|
|
|
|
static char *json_start_member(struct json_stream *js,
|
|
|
|
const char *fieldname, size_t extra)
|
2018-11-20 02:46:32 +01:00
|
|
|
{
|
2019-04-08 11:58:44 +02:00
|
|
|
char *dest;
|
|
|
|
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
if (js->oom)
|
|
|
|
return NULL;
|
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
/* Prepend comma if required. */
|
|
|
|
if (!js->empty)
|
2019-04-08 11:58:44 +02:00
|
|
|
extra++;
|
2018-11-20 02:46:32 +01:00
|
|
|
|
|
|
|
check_fieldname(js, fieldname);
|
|
|
|
if (fieldname)
|
2019-04-08 11:58:44 +02:00
|
|
|
extra += 1 + strlen(fieldname) + 2;
|
|
|
|
|
|
|
|
if (!extra) {
|
|
|
|
dest = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
dest = mkroom(js, extra);
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
if (!dest)
|
|
|
|
goto out;
|
2019-04-08 11:58:44 +02:00
|
|
|
|
|
|
|
if (!js->empty)
|
|
|
|
*(dest++) = ',';
|
|
|
|
if (fieldname) {
|
|
|
|
*(dest++) = '"';
|
|
|
|
memcpy(dest, fieldname, strlen(fieldname));
|
|
|
|
dest += strlen(fieldname);
|
|
|
|
*(dest++) = '"';
|
|
|
|
*(dest++) = ':';
|
|
|
|
}
|
|
|
|
membuf_added(&js->outbuf, extra);
|
|
|
|
|
|
|
|
out:
|
2018-11-20 02:46:32 +01:00
|
|
|
js->empty = false;
|
2019-04-08 11:58:44 +02:00
|
|
|
return dest;
|
2018-11-20 02:46:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void js_indent(struct json_stream *js, jsmntype_t type)
|
|
|
|
{
|
|
|
|
#if DEVELOPER
|
2019-01-15 04:51:27 +01:00
|
|
|
tal_arr_expand(&js->wrapping, type);
|
2018-11-20 02:46:32 +01:00
|
|
|
#endif
|
|
|
|
js->empty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void js_unindent(struct json_stream *js, jsmntype_t type)
|
|
|
|
{
|
|
|
|
#if DEVELOPER
|
2019-04-08 11:58:44 +02:00
|
|
|
size_t indent = tal_count(js->wrapping);
|
|
|
|
assert(indent > 0);
|
|
|
|
assert(js->wrapping[indent-1] == type);
|
|
|
|
tal_resize(&js->wrapping, indent-1);
|
2018-11-20 02:46:32 +01:00
|
|
|
#endif
|
|
|
|
js->empty = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_array_start(struct json_stream *js, const char *fieldname)
|
|
|
|
{
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
char *dest = json_start_member(js, fieldname, 1);
|
|
|
|
if (dest)
|
|
|
|
dest[0] = '[';
|
2019-04-08 11:58:44 +02:00
|
|
|
js_written_some(js);
|
2018-11-20 02:46:32 +01:00
|
|
|
js_indent(js, JSMN_ARRAY);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_array_end(struct json_stream *js)
|
|
|
|
{
|
|
|
|
js_unindent(js, JSMN_ARRAY);
|
|
|
|
json_stream_append(js, "]");
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_object_start(struct json_stream *js, const char *fieldname)
|
|
|
|
{
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
char *dest = json_start_member(js, fieldname, 1);
|
|
|
|
if (dest)
|
|
|
|
dest[0] = '{';
|
2019-04-08 11:58:44 +02:00
|
|
|
js_written_some(js);
|
2018-11-20 02:46:32 +01:00
|
|
|
js_indent(js, JSMN_OBJECT);
|
|
|
|
}
|
|
|
|
|
|
|
|
void json_object_end(struct json_stream *js)
|
|
|
|
{
|
|
|
|
js_unindent(js, JSMN_OBJECT);
|
|
|
|
json_stream_append(js, "}");
|
|
|
|
}
|
|
|
|
|
|
|
|
void PRINTF_FMT(3,4)
|
|
|
|
json_add_member(struct json_stream *js, const char *fieldname,
|
|
|
|
const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
|
2019-04-08 11:58:44 +02:00
|
|
|
json_start_member(js, fieldname, 0);
|
2018-11-20 02:46:32 +01:00
|
|
|
va_start(ap, fmt);
|
|
|
|
json_stream_append_vfmt(js, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
2019-04-08 11:58:44 +02:00
|
|
|
void json_add_hex(struct json_stream *js, const char *fieldname,
|
|
|
|
const void *data, size_t len)
|
|
|
|
{
|
|
|
|
/* Size without NUL term */
|
|
|
|
size_t hexlen = hex_str_size(len) - 1;
|
|
|
|
char *dest;
|
|
|
|
|
2019-04-08 11:58:44 +02:00
|
|
|
dest = json_start_member(js, fieldname, 1 + hexlen + 1);
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
if (dest) {
|
|
|
|
dest[0] = '"';
|
|
|
|
if (!hex_encode(data, len, dest + 1, hexlen + 1))
|
|
|
|
abort();
|
|
|
|
dest[1+hexlen] = '"';
|
|
|
|
}
|
2019-04-08 11:58:44 +02:00
|
|
|
js_written_some(js);
|
|
|
|
}
|
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
/* This is where we read the json_stream and write it to conn */
|
|
|
|
static struct io_plan *json_stream_output_write(struct io_conn *conn,
|
|
|
|
struct json_stream *js)
|
|
|
|
{
|
lightningd: hang up on clients if they make us run out of memory.
This happened with the 800M JSON for the MCP listchannels on the raspberry
pi, and tal calls abort() by default.
We switch to raw malloc here; we could override the error hook for
tal, but this is neater since we're doing low-level things anyway,
I tested it manually with this patch:
diff --git a/lightningd/json_stream.c b/lightningd/json_stream.c
index cec9f5771..206ba37c0 100644
--- a/lightningd/json_stream.c
+++ b/lightningd/json_stream.c
@@ -43,6 +43,14 @@ static void free_json_stream_membuf(struct json_stream *js)
free(membuf_cleanup(&js->outbuf));
}
+static void *membuf_realloc_hack(struct membuf *mb, void *rawelems,
+ size_t newsize)
+{
+ if (newsize > 1000000000)
+ return NULL;
+ return realloc(rawelems, newsize);
+}
+
struct json_stream *new_json_stream(const tal_t *ctx,
struct command *writer,
struct log *log)
@@ -53,7 +61,7 @@ struct json_stream *new_json_stream(const tal_t *ctx,
js->reader = NULL;
/* We don't use tal here, because we handle failure externally (tal
* helpfully aborts with a msg, which is usually right) */
- membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc);
+ membuf_init(&js->outbuf, malloc(64), 64, membuf_realloc_hack);
tal_add_destructor(js, free_json_stream_membuf);
#if DEVELOPER
js->wrapping = tal_arr(js, jsmntype_t, 0);
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2019-05-16 21:57:17 +02:00
|
|
|
/* Out of memory? Nothing we can do but close conn */
|
|
|
|
if (js->oom)
|
|
|
|
return io_close(conn);
|
|
|
|
|
2018-11-20 02:46:32 +01:00
|
|
|
/* For when we've just done some output */
|
|
|
|
membuf_consume(&js->outbuf, js->len_read);
|
|
|
|
|
|
|
|
/* Get how much we can write out from js */
|
|
|
|
js->len_read = membuf_num_elems(&js->outbuf);
|
|
|
|
|
|
|
|
/* Nothing in buffer? */
|
|
|
|
if (js->len_read == 0) {
|
|
|
|
/* We're not doing io_write now, unset. */
|
|
|
|
js->reader = NULL;
|
|
|
|
if (!json_stream_still_writing(js))
|
2018-11-20 02:46:32 +01:00
|
|
|
return js->reader_cb(conn, js, js->reader_arg);
|
2018-11-20 02:46:32 +01:00
|
|
|
return io_out_wait(conn, js, json_stream_output_write, js);
|
|
|
|
}
|
|
|
|
|
|
|
|
js->reader = conn;
|
2019-04-08 01:52:19 +02:00
|
|
|
if (js->log)
|
|
|
|
log_io(js->log, LOG_IO_OUT, "",
|
|
|
|
membuf_elems(&js->outbuf), js->len_read);
|
2018-11-20 02:46:32 +01:00
|
|
|
return io_write(conn,
|
|
|
|
membuf_elems(&js->outbuf), js->len_read,
|
|
|
|
json_stream_output_write, js);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct io_plan *json_stream_output_(struct json_stream *js,
|
|
|
|
struct io_conn *conn,
|
|
|
|
struct io_plan *(*cb)(struct io_conn *conn,
|
2018-11-20 02:46:32 +01:00
|
|
|
struct json_stream *js,
|
2018-11-20 02:46:32 +01:00
|
|
|
void *arg),
|
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
assert(!js->reader);
|
|
|
|
|
|
|
|
js->reader_cb = cb;
|
|
|
|
js->reader_arg = arg;
|
|
|
|
|
|
|
|
js->len_read = 0;
|
|
|
|
return json_stream_output_write(conn, js);
|
|
|
|
}
|