[nft PATCH 4/8] libnftables: Support buffering output and error

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When integrating libnftables into Python code using ctypes module,
having to use a FILE pointer for output becomes a show-stopper.
Therefore make Python hackers' lives (a little) less painful by
providing convenience functions to setup buffering output and error
streams using fopencookie() and retrieving the buffers.

Signed-off-by: Phil Sutter <phil@xxxxxx>
---
 include/nftables.h          |   9 +++
 include/nftables/nftables.h |   7 +++
 src/libnftables.c           | 137 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 153 insertions(+)

diff --git a/include/nftables.h b/include/nftables.h
index 1b368971bee9a..5c181be5a4ec3 100644
--- a/include/nftables.h
+++ b/include/nftables.h
@@ -7,6 +7,13 @@
 #include <utils.h>
 #include <nftables/nftables.h>
 
+struct cookie {
+	char *buf;
+	size_t buflen;
+	size_t pos;
+	FILE *orig_fp;
+};
+
 struct output_ctx {
 	unsigned int numeric;
 	unsigned int stateless;
@@ -15,6 +22,8 @@ struct output_ctx {
 	unsigned int echo;
 	FILE *output_fp;
 	FILE *error_fp;
+	struct cookie *output_cookie;
+	struct cookie *error_cookie;
 };
 
 struct nft_cache {
diff --git a/include/nftables/nftables.h b/include/nftables/nftables.h
index 1e9306822eb7e..652e0ca99182a 100644
--- a/include/nftables/nftables.h
+++ b/include/nftables/nftables.h
@@ -57,7 +57,14 @@ bool nft_ctx_output_get_echo(struct nft_ctx *ctx);
 void nft_ctx_output_set_echo(struct nft_ctx *ctx, bool val);
 
 FILE *nft_ctx_set_output(struct nft_ctx *ctx, FILE *fp);
+int nft_ctx_buffer_output(struct nft_ctx *ctx);
+int nft_ctx_unbuffer_output(struct nft_ctx *ctx);
+const char *nft_ctx_get_output_buffer(struct nft_ctx *ctx);
+
 FILE *nft_ctx_set_error(struct nft_ctx *ctx, FILE *fp);
+int nft_ctx_buffer_error(struct nft_ctx *ctx);
+int nft_ctx_unbuffer_error(struct nft_ctx *ctx);
+const char *nft_ctx_get_error_buffer(struct nft_ctx *ctx);
 
 int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path);
 void nft_ctx_clear_include_paths(struct nft_ctx *ctx);
diff --git a/src/libnftables.c b/src/libnftables.c
index d6622d51aba33..73363e3a32f87 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -177,11 +177,148 @@ struct nft_ctx *nft_ctx_new(uint32_t flags)
 	return ctx;
 }
 
+static void free_cookie(struct cookie *cookie)
+{
+	if (cookie) {
+		free(cookie->buf);
+		free(cookie);
+	}
+}
+
+static ssize_t cookie_write(void *cptr, const char *buf, size_t buflen)
+{
+	struct cookie *cookie = cptr;
+
+	if (!cookie->buflen) {
+		cookie->buflen = buflen + 1;
+		cookie->buf = xmalloc(cookie->buflen);
+	} else if (cookie->pos + buflen >= cookie->buflen) {
+		size_t newlen = cookie->buflen * 2;
+
+		while (newlen <= cookie->pos + buflen)
+			newlen *= 2;
+
+		cookie->buf = xrealloc(cookie->buf, newlen);
+		cookie->buflen = newlen;
+	}
+	memcpy(cookie->buf + cookie->pos, buf, buflen);
+	cookie->pos += buflen;
+	cookie->buf[cookie->pos] = '\0';
+
+	return buflen;
+}
+
+static int init_cookie(struct cookie **cpptr, FILE **fp)
+{
+	struct cookie *cookie;
+	cookie_io_functions_t cookie_fops = {
+		.write = cookie_write,
+	};
+	FILE *cookie_fp;
+
+	if (!cpptr || !fp)
+		return 1;
+
+	cookie = *cpptr;
+
+	if (cookie) { /* just rewind buffer */
+		if (cookie->buflen) {
+			cookie->pos = 0;
+			cookie->buf[0] = '\0';
+		}
+		return 0;
+	}
+
+	cookie = xzalloc(sizeof(*cookie));
+	cookie->orig_fp = *fp;
+
+	cookie_fp = fopencookie(cookie, "w", cookie_fops);
+	if (!cookie_fp) {
+		free(cookie);
+		return 1;
+	}
+
+	*cpptr = cookie;
+	*fp = cookie_fp;
+	return 0;
+}
+
+int nft_ctx_buffer_output(struct nft_ctx *ctx)
+{
+	struct output_ctx *octx = &ctx->output;
+
+	return init_cookie(&octx->output_cookie, &octx->output_fp);
+}
+
+int nft_ctx_unbuffer_output(struct nft_ctx *ctx)
+{
+	if (!ctx->output.output_cookie)
+		return 1;
+
+	ctx->output.output_fp = ctx->output.output_cookie->orig_fp;
+	free_cookie(ctx->output.output_cookie);
+	ctx->output.output_cookie = NULL;
+	return 0;
+}
+
+int nft_ctx_buffer_error(struct nft_ctx *ctx)
+{
+	struct output_ctx *octx = &ctx->output;
+
+	return init_cookie(&octx->error_cookie, &octx->error_fp);
+}
+
+int nft_ctx_unbuffer_error(struct nft_ctx *ctx)
+{
+	if (!ctx->output.error_cookie)
+		return 1;
+
+	ctx->output.error_fp = ctx->output.error_cookie->orig_fp;
+	free_cookie(ctx->output.error_cookie);
+	ctx->output.error_cookie = NULL;
+	return 0;
+}
+
+static const char *get_cookie_buffer(struct cookie *cookie, FILE *cookie_fp)
+{
+	if (!cookie)
+		return NULL;
+
+	fflush(cookie_fp);
+
+	/* This is a bit tricky: Rewind the buffer for future use and return
+	 * the old content at the same time.
+	 * Therefore just reset buffer position, don't change it's content. And
+	 * return an empty string if buffer position is zero. */
+
+	if (!cookie->buflen || !cookie->pos)
+		return "";
+
+	cookie->pos = 0;
+	return cookie->buf;
+}
+
+const char *nft_ctx_get_output_buffer(struct nft_ctx *ctx)
+{
+	struct output_ctx *octx = &ctx->output;
+
+	return get_cookie_buffer(octx->output_cookie, octx->output_fp);
+}
+
+const char *nft_ctx_get_error_buffer(struct nft_ctx *ctx)
+{
+	struct output_ctx *octx = &ctx->output;
+
+	return get_cookie_buffer(octx->error_cookie, octx->error_fp);
+}
+
 void nft_ctx_free(struct nft_ctx *ctx)
 {
 	if (ctx->nf_sock)
 		netlink_close_sock(ctx->nf_sock);
 
+	free_cookie(ctx->output.output_cookie);
+	free_cookie(ctx->output.error_cookie);
 	iface_cache_release();
 	cache_release(&ctx->cache);
 	nft_ctx_clear_include_paths(ctx);
-- 
2.16.1

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux