List for cgit developers and users
 help / color / mirror / Atom feed
From: john at keeping.me.uk (John Keeping)
Subject: [PATCH] filter: basic write hooking infrastructure
Date: Sun, 12 Jan 2014 22:52:23 +0000	[thread overview]
Message-ID: <20140112225223.GW7608@serenity.lan> (raw)
In-Reply-To: <1389563881-10400-1-git-send-email-Jason@zx2c4.com>

On Sun, Jan 12, 2014 at 10:58:01PM +0100, Jason A. Donenfeld wrote:
> Filters can now call hook_write and unhook_write if they want to
> redirect writing to stdout to a different function. This saves us from
> potential file descriptor pipes and other less efficient mechanisms.
> 
> We do this instead of replacing the call in html_raw because some places
> stdlib's printf functions are used (ui-patch or within git itself),
> which has its own internal buffering, which makes it difficult to
> interlace our function calls. So, we dlsym libc's write and then
> override it in the link stage.
> 
> Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
> ---

Clever.  I've been looking at hooking in at a higher level before html.c
escapes the output.  But I think the approach taken with source_filter
may be a better way of getting the raw output to the filter.

I can't help thinking it would be easier to just replace html_raw here
though.  All our writes go through there anyway and we don't need to
worry about writing to other file descriptors.

>  cgit.c   |  2 ++
>  cgit.h   |  1 +
>  cgit.mk  |  4 +++-
>  filter.c | 30 ++++++++++++++++++++++++++++++
>  4 files changed, 36 insertions(+), 1 deletion(-)
> 
> diff --git a/cgit.c b/cgit.c
> index 4f31e58..725fd65 100644
> --- a/cgit.c
> +++ b/cgit.c
> @@ -904,6 +904,8 @@ int main(int argc, const char **argv)
>  	const char *path;
>  	int err, ttl;
>  
> +	cgit_init_filters();
> +
>  	prepare_context(&ctx);
>  	cgit_repolist.length = 0;
>  	cgit_repolist.count = 0;
> diff --git a/cgit.h b/cgit.h
> index 893c38f..db34493 100644
> --- a/cgit.h
> +++ b/cgit.h
> @@ -357,6 +357,7 @@ extern void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char
>  extern void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv);
>  extern struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype);
>  extern void cgit_cleanup_filters(void);
> +extern void cgit_init_filters(void);
>  
>  extern void cgit_prepare_repo_env(struct cgit_repo * repo);
>  
> diff --git a/cgit.mk b/cgit.mk
> index 19a76e7..9d6dea8 100644
> --- a/cgit.mk
> +++ b/cgit.mk
> @@ -61,6 +61,8 @@ $(CGIT_VERSION_OBJS): $(CGIT_PREFIX)VERSION
>  $(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \
>  	-DCGIT_VERSION='"$(CGIT_VERSION)"'
>  
> +CGIT_LIBS += -ldl
> +
>  
>  # Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not
>  # handled by that and we must handle them ourselves.
> @@ -88,4 +90,4 @@ $(CGIT_OBJS): %.o: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS $(missing_dep_dirs)
>  	$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $<
>  
>  $(CGIT_PREFIX)cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS)
> -	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
> +	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(CGIT_LIBS)
> diff --git a/filter.c b/filter.c
> index 86c1d5d..d85b1bb 100644
> --- a/filter.c
> +++ b/filter.c
> @@ -12,6 +12,10 @@
>  #include <unistd.h>
>  #include <string.h>
>  #include <stdlib.h>
> +#include <dlfcn.h>
> +
> +static ssize_t (*libc_write)(int fd, const void *buf, size_t count);
> +static ssize_t (*filter_write)(int fd, const void *buf, size_t count) = NULL;
>  
>  static int open_exec_filter(struct cgit_filter *base, va_list ap)
>  {
> @@ -192,3 +196,29 @@ void cgit_cleanup_filters(void)
>  		reap_filter(cgit_repolist.repos[i].source_filter);
>  	}
>  }
> +
> +void cgit_init_filters(void)
> +{
> +	libc_write = dlsym(RTLD_NEXT, "write");
> +	if (!libc_write)
> +		die("Could not locate libc's write function");
> +}
> +
> +ssize_t write(int fd, const void *buf, size_t count)
> +{
> +	if (fd != STDOUT_FILENO || !filter_write)
> +		return libc_write(fd, buf, count);
> +	return filter_write(fd, buf, count);
> +}
> +
> +static inline void hook_write(ssize_t (*new_write)(int fd, const void *buf, size_t count))
> +{
> +	/* We want to avoid buggy nested patterns. */
> +	assert(filter_write == NULL);
> +	filter_write = new_write;
> +}

Missing blank line here.

> +static inline void unhook_write()
> +{
> +	assert(filter_write != NULL);
> +	filter_write = NULL;
> +}


  parent reply	other threads:[~2014-01-12 22:52 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-01-12 21:58 Jason
2014-01-12 22:31 ` [PATCH v2] " Jason
2014-01-12 22:52 ` john [this message]
2014-01-12 23:10   ` [PATCH] " Jason

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20140112225223.GW7608@serenity.lan \
    --to=cgit@lists.zx2c4.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).