]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
libnftables: Support buffering output and error
authorPhil Sutter <phil@nwl.cc>
Tue, 10 Apr 2018 17:00:22 +0000 (19:00 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 11 Apr 2018 07:57:28 +0000 (09:57 +0200)
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@nwl.cc>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/nftables.h
include/nftables/nftables.h
src/libnftables.c

index 1b368971bee9adda083f79e97478f0746aec1d48..5c181be5a4ec39bfc57d0aa8c15f55b67407ec89 100644 (file)
@@ -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 {
index 1e9306822eb7e7d96f45a8f4c4ebab50b2552294..652e0ca99182abe75d0b603beb076512482464c5 100644 (file)
@@ -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);
index d6622d51aba338705e2c91eb3d4a609d5f5eccaf..73363e3a32f870c1437ff2eb41d5192dbccce7d3 100644 (file)
@@ -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);