From a8563ec8515d87259590a7aad182922def8e2cf2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Aug 2009 16:39:10 -0700 Subject: upload-pack: add a trigger for post-upload-pack hook After upload-pack successfully finishes its operation, post-upload-pack hook can be called for logging purposes. The hook is passed various pieces of information, one per line, from its standard input. Currently the following items can be fed to the hook, but more types of information may be added in the future: want SHA-1:: 40-byte hexadecimal object name the client asked to include in the resulting pack. Can occur one or more times in the input. have SHA-1:: 40-byte hexadecimal object name the client asked to exclude from the resulting pack, claiming to have them already. Can occur zero or more times in the input. time float:: Number of seconds spent for creating the packfile. size decimal:: Size of the resulting packfile in bytes. Signed-off-by: Junio C Hamano --- upload-pack.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 2 deletions(-) (limited to 'upload-pack.c') diff --git a/upload-pack.c b/upload-pack.c index 4d8be834ff..857440d579 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -141,8 +141,62 @@ static int do_rev_list(int fd, void *create_full_pack) return 0; } +static int feed_msg_to_hook(int fd, const char *fmt, ...) +{ + int cnt; + char buf[1024]; + va_list params; + + va_start(params, fmt); + cnt = vsprintf(buf, fmt, params); + va_end(params); + return write_in_full(fd, buf, cnt) != cnt; +} + +static int feed_obj_to_hook(const char *label, struct object_array *oa, int i, int fd) +{ + return feed_msg_to_hook(fd, "%s %s\n", label, + sha1_to_hex(oa->objects[i].item->sha1)); +} + +static int run_post_upload_pack_hook(size_t total, struct timeval *tv) +{ + const char *argv[2]; + struct child_process proc; + int err, i; + + argv[0] = "hooks/post-upload-pack"; + argv[1] = NULL; + + if (access(argv[0], X_OK) < 0) + return 0; + + memset(&proc, 0, sizeof(proc)); + proc.argv = argv; + proc.in = -1; + proc.stdout_to_stderr = 1; + err = start_command(&proc); + if (err) + return err; + for (i = 0; !err && i < want_obj.nr; i++) + err |= feed_obj_to_hook("want", &want_obj, i, proc.in); + for (i = 0; !err && i < have_obj.nr; i++) + err |= feed_obj_to_hook("have", &have_obj, i, proc.in); + if (!err) + err |= feed_msg_to_hook(proc.in, "time %ld.%06ld\n", + (long)tv->tv_sec, (long)tv->tv_usec); + if (!err) + err |= feed_msg_to_hook(proc.in, "size %ld\n", (long)total); + if (close(proc.in)) + err = 1; + if (finish_command(&proc)) + err = 1; + return err; +} + static void create_pack_file(void) { + struct timeval start_tv, tv; struct async rev_list; struct child_process pack_objects; int create_full_pack = (nr_our_refs == want_obj.nr && !have_obj.nr); @@ -150,10 +204,12 @@ static void create_pack_file(void) char abort_msg[] = "aborting due to possible repository " "corruption on the remote side."; int buffered = -1; - ssize_t sz; + ssize_t sz, total_sz; const char *argv[10]; int arg = 0; + gettimeofday(&start_tv, NULL); + total_sz = 0; if (shallow_nr) { rev_list.proc = do_rev_list; rev_list.data = 0; @@ -262,7 +318,7 @@ static void create_pack_file(void) sz = xread(pack_objects.out, cp, sizeof(data) - outsz); if (0 < sz) - ; + total_sz += sz; else if (sz == 0) { close(pack_objects.out); pack_objects.out = -1; @@ -314,6 +370,16 @@ static void create_pack_file(void) } if (use_sideband) packet_flush(1); + + gettimeofday(&tv, NULL); + tv.tv_sec -= start_tv.tv_sec; + if (tv.tv_usec < start_tv.tv_usec) { + tv.tv_sec--; + tv.tv_usec += 1000000; + } + tv.tv_usec -= start_tv.tv_usec; + if (run_post_upload_pack_hook(total_sz, &tv)) + warning("post-upload-hook failed"); return; fail: -- cgit v1.2.1 From 11cae066b2433fcec807810b45f317f8203df358 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 28 Aug 2009 22:19:45 -0700 Subject: upload-pack: feed "kind [clone|fetch]" to post-upload-pack hook A request to clone the repository does not give any "have" but asks for all the refs we offer with "want". When a request does not ask to clone the repository fully, but asks to fetch some refs into an empty repository, it will not give any "have" but its "want" won't ask for all the refs we offer. If we suppose (and I would say this is a rather big if) that it makes sense to distinguish these two cases, a hook cannot reliably do this alone. The hook can detect lack of "have" and bunch of "want", but there is no direct way to tell if the other end asked for all refs we offered, or merely most of them. Between the time we talked with the other end and the time the hook got called, we may have acquired more refs or lost some refs in the repository by concurrent operations. Given that we plan to introduce selective advertisement of refs with a protocol extension, it would become even more difficult for hooks to guess between these two cases. This adds "kind [clone|fetch]" to hook's input, as a stable interface to allow the hooks to tell these cases apart. Signed-off-by: Junio C Hamano --- upload-pack.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'upload-pack.c') diff --git a/upload-pack.c b/upload-pack.c index 857440d579..8e82179c93 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -187,6 +187,10 @@ static int run_post_upload_pack_hook(size_t total, struct timeval *tv) (long)tv->tv_sec, (long)tv->tv_usec); if (!err) err |= feed_msg_to_hook(proc.in, "size %ld\n", (long)total); + if (!err) + err |= feed_msg_to_hook(proc.in, "kind %s\n", + (nr_our_refs == want_obj.nr && !have_obj.nr) + ? "clone" : "fetch"); if (close(proc.in)) err = 1; if (finish_command(&proc)) -- cgit v1.2.1