* Funny: git -p submodule summary @ 2009-01-08 15:07 Johannes Schindelin 2009-01-08 15:30 ` Johannes Schindelin ` (2 more replies) 0 siblings, 3 replies; 41+ messages in thread From: Johannes Schindelin @ 2009-01-08 15:07 UTC (permalink / raw) To: Jeff King, git Hi list, Just try this with a submodule that has more changes than fit on a screen: $ git -p submodule summary In my tests, it consistently fscks up my console. I wonder if this is related to ea27a18(spawn pager via run_command interface). *reverts that commit* Yep, that fixes it. Ciao, Dscho ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-08 15:07 Funny: git -p submodule summary Johannes Schindelin @ 2009-01-08 15:30 ` Johannes Schindelin 2009-01-09 8:38 ` Jeff King 2009-01-27 6:25 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Jeff King 2 siblings, 0 replies; 41+ messages in thread From: Johannes Schindelin @ 2009-01-08 15:30 UTC (permalink / raw) To: Jeff King, git Hi, On Thu, 8 Jan 2009, Johannes Schindelin wrote: > Just try this with a submodule that has more changes than fit on a > screen: > > $ git -p submodule summary > > In my tests, it consistently fscks up my console. Update: even if the changes do fit on a screen, the console is fscked up (I have to stty echo to get it back to normal). Ciao, Dscho ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-08 15:07 Funny: git -p submodule summary Johannes Schindelin 2009-01-08 15:30 ` Johannes Schindelin @ 2009-01-09 8:38 ` Jeff King 2009-01-09 9:22 ` Jeff King 2009-01-09 9:30 ` Junio C Hamano 2009-01-27 6:25 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Jeff King 2 siblings, 2 replies; 41+ messages in thread From: Jeff King @ 2009-01-09 8:38 UTC (permalink / raw) To: Johannes Schindelin; +Cc: git On Thu, Jan 08, 2009 at 04:07:08PM +0100, Johannes Schindelin wrote: > Just try this with a submodule that has more changes than fit on a screen: > > $ git -p submodule summary > > In my tests, it consistently fscks up my console. I wonder if this is > related to ea27a18(spawn pager via run_command interface). > > *reverts that commit* Yep, that fixes it. Hmm. I can reproduce your problem here, like this: $ git -p submodule summary but when I tried to dig deeper using strace, the problem goes away: $ strace -f -o foo.out git -p submodule summary However, I was able to get some data by stracing _just_ less: $ GIT_PAGER='strace -f -o foo.out less' git -p submodule summary and that reproduces the problem. And here's the interesting bit: open("/dev/tty", O_RDONLY|O_LARGEFILE) = 3 ... read(0, $SOME_SUBMODULE_OUTPUT, ...) write(1, $SOME_SUBMODULE_OUTPUT, ...) read(3, 0xbfd3442f, 1) = -1 EIO (Input/output error) That is, after displaying the actual output, the next thing less does is try to get input from the user on /dev/tty. But it returns EIO. At which point less just dies, leaving your terminal in a funny state. Hrm. Here's a theory. The new pager behavior goes something like this for a builtin: 1. fork, child becomes pager 2. register atexit handler to wait for pager to finish 3. run builtin 4. exit, triggering handler but for an external command (like a shell script), we exec the command, and presumably forget about out atexit handler entirely. Which means that our shell script writes all of its output and exits before less has a chance to prompt and get a response from the tty. And I'll admit to being very hazy on the details of process groups and controlling terminals, but from what I recall, perhaps the EIO is related to the process group leader being dead. Which means we could paper over it by putting less in its own pgrp. So here's a little test (in bash): $ set +m ;# disable job control, leaving bash as the pgrp leader $ git -p submodule summary A-ha. That "works" in the sense that less runs fine and show the output. So it is a pgrp issue. _But_ we still have a problem, which is that the original process has exited, and bash thinks the command is finished. So now annoyingly we have both bash and less trying to read from the terminal. So the _real_ problem is that we are not always triggering the "wait for pager to finish" code because we exec and forget about it. Which means this strategy of "git runs child pager" will never work properly. Instead, we have to use three processes: git and the pager become child processes, while the original process waits for both to exit and returns the proper exit code from git. Let me try to work up a patch. -Peff PS I believe this is related to a similar bug which I have been hunting fruitlessly for a few weeks: if you use ^C to kill git, the pager will sometimes do funny things. I also traced that to an EIO on reading from the terminal, which makes sense: we are killing git before it gets a chance to do wait_for_pager. ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-09 8:38 ` Jeff King @ 2009-01-09 9:22 ` Jeff King 2009-01-09 9:48 ` Jeff King 2009-01-09 10:09 ` Johannes Sixt 2009-01-09 9:30 ` Junio C Hamano 1 sibling, 2 replies; 41+ messages in thread From: Jeff King @ 2009-01-09 9:22 UTC (permalink / raw) To: Johannes Schindelin; +Cc: git On Fri, Jan 09, 2009 at 03:38:36AM -0500, Jeff King wrote: > So the _real_ problem is that we are not always triggering the "wait for > pager to finish" code because we exec and forget about it. Which means > this strategy of "git runs child pager" will never work properly. > Instead, we have to use three processes: git and the pager become child > processes, while the original process waits for both to exit and returns > the proper exit code from git. > > Let me try to work up a patch. Below is a patch that uses the three-process mechanism, and it fixes the problem. _But_ it is not satisfactory for inclusion, because it won't work on MINGW32. Since it is actually splitting git into two processes (one to monitor the pager and one to actually run git), it uses fork. So I think to do things right, we have to be even more complicated. When we spawn the pager, we keep git as a single process. We register the atexit() handler to wait for the pager, and intercept any death signals to do the same. Then, if we are running a builtin, it is business as usual. But if we want to exec something, instead we have to actually spawn into the three-process form. Meaning we have to use run_command to start it, and then wait for it and the pager to return. Of course, we don't know ahead of time whether exec'ing a command will work: we find out by trying. So now we will end up creating a pipe and fork()ing every time we want to see whether we can exec a command. But I suppose that only happens once or twice, so maybe the performance impact isn't relevant. This is all getting complicated enough that I am tempted to just suggest reverting ea27a18c. But even that won't fix everything, though, since MINGW32 still needs to use run_command to spawn the pager. IOW, I think the breakage you are seeing has always been broken on MINGW32. Blah. Anyway, here is the Unix-only patch. --- diff --git a/pager.c b/pager.c index f19ddbc..68ae669 100644 --- a/pager.c +++ b/pager.c @@ -28,18 +28,10 @@ static void pager_preexec(void) static const char *pager_argv[] = { "sh", "-c", NULL, NULL }; static struct child_process pager_process; -static void wait_for_pager(void) -{ - fflush(stdout); - fflush(stderr); - /* signal EOF to pager */ - close(1); - close(2); - finish_command(&pager_process); -} - void setup_pager(void) { + pid_t git_child; + int status; const char *pager = getenv("GIT_PAGER"); if (!isatty(1)) @@ -68,14 +60,24 @@ void setup_pager(void) if (start_command(&pager_process)) return; - /* original process continues, but writes to the pipe */ - dup2(pager_process.in, 1); - if (isatty(2)) - dup2(pager_process.in, 2); - close(pager_process.in); + /* now spawn the actual git process */ + git_child = fork(); + if (git_child == -1) + die("unable to fork: %s", strerror(errno)); + if (git_child == 0) { + dup2(pager_process.in, 1); + if (isatty(2)) + dup2(pager_process.in, 2); + close(pager_process.in); + return; + } - /* this makes sure that the parent terminates after the pager */ - atexit(wait_for_pager); + /* and the original process just waits for both to finish */ + close(pager_process.in); + if (waitpid(git_child, &status, 0) < 0) + die("wait failure: %s", strerror(errno)); + finish_command(&pager_process); + exit(status); } int pager_in_use(void) ^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-09 9:22 ` Jeff King @ 2009-01-09 9:48 ` Jeff King 2009-01-09 10:09 ` Johannes Sixt 1 sibling, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-09 9:48 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Junio C Hamano, git On Fri, Jan 09, 2009 at 04:22:50AM -0500, Jeff King wrote: > So I think to do things right, we have to be even more complicated. When > we spawn the pager, we keep git as a single process. We register the > atexit() handler to wait for the pager, and intercept any death signals > to do the same. Then, if we are running a builtin, it is business as > usual. But if we want to exec something, instead we have to actually > spawn into the three-process form. Meaning we have to use run_command to > start it, and then wait for it and the pager to return. Actually, this turned out to be quite a small patch. It fixes the problem you were seeing, and it should work on Windows. It incurs an extra fork() for every dashed-external that we try. There is a little noise in the patch, so let me highlight the three things that are happening (a "real" patch should break this into three patches in a series): 1. The noise is from renaming the static run_command, which conflicts with the definition in run_command.h 2. Substituting run_command for execvp. 3. run_command needs to signal "I couldn't exec this" as opposed to other errors, since we care about the difference here. I do this here with exit(127), but probably we should recognize this in wait_or_whine and hand out the same error code for posix and mingw platforms. As for the extra fork, we could do away with it if we scanned the path looking for the external before just exec'ing it. I think this is better in the long run anyway, because then we can do other setup specific to running an external command (I don't remember the details, but I ran afoul of this when I was doing pager stuff a while ago). Patch is below. --- diff --git a/git.c b/git.c index ecc8fad..fa946b9 100644 --- a/git.c +++ b/git.c @@ -2,6 +2,7 @@ #include "exec_cmd.h" #include "cache.h" #include "quote.h" +#include "run-command.h" const char git_usage_string[] = "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]"; @@ -219,7 +220,7 @@ struct cmd_struct { int option; }; -static int run_command(struct cmd_struct *p, int argc, const char **argv) +static int run_builtin(struct cmd_struct *p, int argc, const char **argv) { int status; struct stat st; @@ -384,7 +385,7 @@ static void handle_internal_command(int argc, const char **argv) struct cmd_struct *p = commands+i; if (strcmp(p->cmd, cmd)) continue; - exit(run_command(p, argc, argv)); + exit(run_builtin(p, argc, argv)); } } @@ -392,6 +393,7 @@ static void execv_dashed_external(const char **argv) { struct strbuf cmd = STRBUF_INIT; const char *tmp; + int status; strbuf_addf(&cmd, "git-%s", argv[0]); @@ -406,8 +408,13 @@ static void execv_dashed_external(const char **argv) trace_argv_printf(argv, "trace: exec:"); - /* execvp() can only ever return if it fails */ - execvp(cmd.buf, (char **)argv); + /* + * if we failed because the command was not found, it is + * OK to return. Otherwise, we just pass along the status code. + */ + status = run_command_v_opt(argv, 0); + if (status != 127 && status != -ERR_RUN_COMMAND_FORK) + exit(-status); trace_printf("trace: exec failed: %s\n", strerror(errno)); diff --git a/run-command.c b/run-command.c index c90cdc5..539609e 100644 --- a/run-command.c +++ b/run-command.c @@ -118,7 +118,7 @@ int start_command(struct child_process *cmd) } else { execvp(cmd->argv[0], (char *const*) cmd->argv); } - die("exec %s failed.", cmd->argv[0]); + exit(127); } #else int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ ^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-09 9:22 ` Jeff King 2009-01-09 9:48 ` Jeff King @ 2009-01-09 10:09 ` Johannes Sixt 2009-01-09 10:13 ` Jeff King 1 sibling, 1 reply; 41+ messages in thread From: Johannes Sixt @ 2009-01-09 10:09 UTC (permalink / raw) To: Jeff King; +Cc: Johannes Schindelin, git Jeff King schrieb: > Below is a patch that uses the three-process mechanism, and it fixes the > problem. _But_ it is not satisfactory for inclusion, because it won't > work on MINGW32. Since it is actually splitting git into two processes > (one to monitor the pager and one to actually run git), it uses fork. We have start_async()/finish_async() to replace a fork() of the sort that we have here. > IOW, I think > the breakage you are seeing has always been broken on MINGW32. Indeed. Hitting Ctrl-C while the pager was open has messed up the console since day 1. -- Hannes ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-09 10:09 ` Johannes Sixt @ 2009-01-09 10:13 ` Jeff King 2009-01-09 10:36 ` Johannes Sixt 0 siblings, 1 reply; 41+ messages in thread From: Jeff King @ 2009-01-09 10:13 UTC (permalink / raw) To: Johannes Sixt; +Cc: Johannes Schindelin, git On Fri, Jan 09, 2009 at 11:09:08AM +0100, Johannes Sixt wrote: > > Below is a patch that uses the three-process mechanism, and it fixes the > > problem. _But_ it is not satisfactory for inclusion, because it won't > > work on MINGW32. Since it is actually splitting git into two processes > > (one to monitor the pager and one to actually run git), it uses fork. > > We have start_async()/finish_async() to replace a fork() of the sort that > we have here. It looks like start_async is implemented using threads on Windows. Will that survive an execvp call? Because we don't know at this point whether we are going to actually run builtin code, or if we will exec an external. -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-09 10:13 ` Jeff King @ 2009-01-09 10:36 ` Johannes Sixt 2009-01-09 10:47 ` Jeff King 2009-01-11 11:22 ` Jeff King 0 siblings, 2 replies; 41+ messages in thread From: Johannes Sixt @ 2009-01-09 10:36 UTC (permalink / raw) To: Jeff King; +Cc: Johannes Schindelin, git Jeff King schrieb: > On Fri, Jan 09, 2009 at 11:09:08AM +0100, Johannes Sixt wrote: > >>> Below is a patch that uses the three-process mechanism, and it fixes the >>> problem. _But_ it is not satisfactory for inclusion, because it won't >>> work on MINGW32. Since it is actually splitting git into two processes >>> (one to monitor the pager and one to actually run git), it uses fork. >> We have start_async()/finish_async() to replace a fork() of the sort that >> we have here. > > It looks like start_async is implemented using threads on Windows. Will > that survive an execvp call? Because we don't know at this point whether > we are going to actually run builtin code, or if we will exec an > external. Ah, no, it would not survive. But there's a more serious problem why we cannot use start_async() in its current form: It expects that there is a *function* that produces the output; but here we don't have a function - output is produced by *returning* (from setup_pager). I'll test your other patch (that replaces the execvp in git.c by run_command). -- Hannes ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-09 10:36 ` Johannes Sixt @ 2009-01-09 10:47 ` Jeff King 2009-01-11 11:22 ` Jeff King 1 sibling, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-09 10:47 UTC (permalink / raw) To: Johannes Sixt; +Cc: Johannes Schindelin, git On Fri, Jan 09, 2009 at 11:36:41AM +0100, Johannes Sixt wrote: > Ah, no, it would not survive. > > But there's a more serious problem why we cannot use start_async() in its > current form: It expects that there is a *function* that produces the > output; but here we don't have a function - output is produced by > *returning* (from setup_pager). Good point. > I'll test your other patch (that replaces the execvp in git.c by run_command). Note that it only covers the case of external commands. Hitting ctrl-c to interrupt git will still cause funniness. For that we need to intercept signals to call wait_for_pager(). But there is a slight snag: we also intercept signals elsewhere (e.g., for tempfile cleanup). So we need to start remembering the old signal handlers everywhere and chaining to them. -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-09 10:36 ` Johannes Sixt 2009-01-09 10:47 ` Jeff King @ 2009-01-11 11:22 ` Jeff King 2009-01-11 11:25 ` [PATCH 1/4] Makefile: clean up TEST_PROGRAMS definition Jeff King ` (4 more replies) 1 sibling, 5 replies; 41+ messages in thread From: Jeff King @ 2009-01-11 11:22 UTC (permalink / raw) To: Johannes Sixt; +Cc: Junio C Hamano, Johannes Schindelin, git On Fri, Jan 09, 2009 at 11:36:41AM +0100, Johannes Sixt wrote: > I'll test your other patch (that replaces the execvp in git.c by > run_command). There is something funny with it that I have not diagnosed: aliases are broken, and "git foobar" does not return an error. Presumably just checking the "we did not exec succesfully" case is not triggering properly. However, I think the right solution is actually to refactor git.c to figure out ahead of time whether we have a builtin, external, or alias. I can work on that, but not tonight, as my git-time is up for now. But other than that, did it work for you on Windows? However, here is a 4-patch series that handles the separate signal delivery problem. It should fix the "^C makes funny things happen" problems you were seeing. Please test and let me know how it works on Windows. The patches are: 1/4: Makefile: clean up TEST_PROGRAMS definition 2/4: chain kill signals for cleanup functions 3/4: refactor signal handling for cleanup functions 4/4: pager: do wait_for_pager on signal death -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 1/4] Makefile: clean up TEST_PROGRAMS definition 2009-01-11 11:22 ` Jeff King @ 2009-01-11 11:25 ` Jeff King 2009-01-11 11:32 ` [PATCH 2/4] chain kill signals for cleanup functions Jeff King ` (3 subsequent siblings) 4 siblings, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-11 11:25 UTC (permalink / raw) To: Johannes Sixt; +Cc: Junio C Hamano, Johannes Schindelin, git We try to keep lines under 80 characters, not to mention that sticking a bunch of stuff on one line makes diffs messier. Signed-off-by: Jeff King <peff@peff.net> --- Just a cleanup I noticed while working on 2/4. Makefile | 9 ++++++++- 1 files changed, 8 insertions(+), 1 deletions(-) diff --git a/Makefile b/Makefile index dee97c1..2b873fa 100644 --- a/Makefile +++ b/Makefile @@ -1356,7 +1356,14 @@ endif ### Testing rules -TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-parse-options$X test-path-utils$X +TEST_PROGRAMS += test-chmtime$X +TEST_PROGRAMS += test-date$X +TEST_PROGRAMS += test-delta$X +TEST_PROGRAMS += test-genrandom$X +TEST_PROGRAMS += test-match-trees$X +TEST_PROGRAMS += test-parse-options$X +TEST_PROGRAMS += test-path-utils$X +TEST_PROGRAMS += test-sha1$X all:: $(TEST_PROGRAMS) -- 1.6.1.84.g8150 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH 2/4] chain kill signals for cleanup functions 2009-01-11 11:22 ` Jeff King 2009-01-11 11:25 ` [PATCH 1/4] Makefile: clean up TEST_PROGRAMS definition Jeff King @ 2009-01-11 11:32 ` Jeff King 2009-01-11 11:40 ` Jeff King 2009-01-11 11:36 ` [PATCH 3/4] refactor signal handling " Jeff King ` (2 subsequent siblings) 4 siblings, 1 reply; 41+ messages in thread From: Jeff King @ 2009-01-11 11:32 UTC (permalink / raw) To: Johannes Sixt; +Cc: Junio C Hamano, Johannes Schindelin, git If a piece of code wanted to do some cleanup before exiting (e.g., cleaing up a lockfile or a tempfile), our usual strategy was to install a signal handler that did something like this: do_cleanup(); /* actual work */ signal(signo, SIG_DFL); /* restore previous behavior */ raise(signo); /* deliver signal, killing ourselves */ For a single handler, this works fine. However, if we want to clean up two _different_ things, we run into a problem. The most recently installed handler will run, but when it removes itself as a handler, it doesn't put back the first handler. This patch introduces sigchain, a tiny library for handling a stack of signal handlers. You sigchain_push each handler, and use sigchain_pop to restore whoever was before you in the stack. Signed-off-by: Jeff King <peff@peff.net> --- I don't know if it is possible to trigger this issue with current git or not. There are several instances that catch signals in different ways, but I don't know if there are code paths that actually combine them. So maybe it is fixing a bug, but it is definitely needed for 4/4, which will install the wait_for_pager handler in most git runs. I tried to neither over- nor under-engineer. On one hand, you could actually accomplish the same thing by always keeping a "this was the last handler" pointer at each callsite when installing the new handler. But it's easy to mess up, so I think a little library helps keep the code clean. On the other hand, callsites still have to have separate clean_foo() and clean_foo_on_signal() handlers, and manually pop() and raise() in the latter. If the code just assumed you always wanted to chain to the next handler immediately, then we could cut out even more client code. The test script is probably overkill, but I wrote it to convince myself I hadn't screwed up too badly (and because we probably wouldn't even notice, as signals aren't well tested in our scripts). So I figured I might as well share it. .gitignore | 1 + Makefile | 3 +++ builtin-clone.c | 5 +++-- builtin-fetch--tool.c | 5 +++-- builtin-fetch.c | 5 +++-- diff.c | 5 +++-- http-push.c | 11 ++++++----- lockfile.c | 13 +++++++------ sigchain.c | 43 +++++++++++++++++++++++++++++++++++++++++++ sigchain.h | 9 +++++++++ t/t0005-signals.sh | 18 ++++++++++++++++++ test-sigchain.c | 22 ++++++++++++++++++++++ 12 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 sigchain.c create mode 100644 sigchain.h create mode 100755 t/t0005-signals.sh create mode 100644 test-sigchain.c diff --git a/.gitignore b/.gitignore index d9adce5..f28a54d 100644 --- a/.gitignore +++ b/.gitignore @@ -152,6 +152,7 @@ test-match-trees test-parse-options test-path-utils test-sha1 +test-sigchain common-cmds.h *.tar.gz *.dsc diff --git a/Makefile b/Makefile index 2b873fa..fd02dec 100644 --- a/Makefile +++ b/Makefile @@ -388,6 +388,7 @@ LIB_H += revision.h LIB_H += run-command.h LIB_H += sha1-lookup.h LIB_H += sideband.h +LIB_H += sigchain.h LIB_H += strbuf.h LIB_H += tag.h LIB_H += transport.h @@ -481,6 +482,7 @@ LIB_OBJS += sha1-lookup.o LIB_OBJS += sha1_name.o LIB_OBJS += shallow.o LIB_OBJS += sideband.o +LIB_OBJS += sigchain.o LIB_OBJS += strbuf.o LIB_OBJS += symlinks.o LIB_OBJS += tag.o @@ -1364,6 +1366,7 @@ TEST_PROGRAMS += test-match-trees$X TEST_PROGRAMS += test-parse-options$X TEST_PROGRAMS += test-path-utils$X TEST_PROGRAMS += test-sha1$X +TEST_PROGRAMS += test-sigchain$X all:: $(TEST_PROGRAMS) diff --git a/builtin-clone.c b/builtin-clone.c index f1a1a0c..18b9392 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -19,6 +19,7 @@ #include "strbuf.h" #include "dir.h" #include "pack-refs.h" +#include "sigchain.h" /* * Overall FIXMEs: @@ -288,7 +289,7 @@ static void remove_junk(void) static void remove_junk_on_signal(int signo) { remove_junk(); - signal(SIGINT, SIG_DFL); + sigchain_pop(signo); raise(signo); } @@ -438,7 +439,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } junk_git_dir = git_dir; atexit(remove_junk); - signal(SIGINT, remove_junk_on_signal); + sigchain_push(SIGINT, remove_junk_on_signal); setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1); diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index 469b07e..b1d7f8f 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -2,6 +2,7 @@ #include "cache.h" #include "refs.h" #include "commit.h" +#include "sigchain.h" static char *get_stdin(void) { @@ -186,7 +187,7 @@ static void remove_keep(void) static void remove_keep_on_signal(int signo) { remove_keep(); - signal(SIGINT, SIG_DFL); + sigchain_pop(signo); raise(signo); } @@ -245,7 +246,7 @@ static int fetch_native_store(FILE *fp, char buffer[1024]; int err = 0; - signal(SIGINT, remove_keep_on_signal); + sigchain_push(SIGINT, remove_keep_on_signal); atexit(remove_keep); while (fgets(buffer, sizeof(buffer), stdin)) { diff --git a/builtin-fetch.c b/builtin-fetch.c index de6f307..8c86974 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -10,6 +10,7 @@ #include "transport.h" #include "run-command.h" #include "parse-options.h" +#include "sigchain.h" static const char * const builtin_fetch_usage[] = { "git fetch [options] [<repository> <refspec>...]", @@ -58,7 +59,7 @@ static void unlock_pack(void) static void unlock_pack_on_signal(int signo) { unlock_pack(); - signal(SIGINT, SIG_DFL); + sigchain_pop(signo); raise(signo); } @@ -672,7 +673,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) ref_nr = j; } - signal(SIGINT, unlock_pack_on_signal); + sigchain_push(SIGINT, unlock_pack_on_signal); atexit(unlock_pack); exit_code = do_fetch(transport, parse_fetch_refspec(ref_nr, refs), ref_nr); diff --git a/diff.c b/diff.c index d235482..5a74012 100644 --- a/diff.c +++ b/diff.c @@ -12,6 +12,7 @@ #include "run-command.h" #include "utf8.h" #include "userdiff.h" +#include "sigchain.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -1933,7 +1934,7 @@ static void remove_tempfile(void) static void remove_tempfile_on_signal(int signo) { remove_tempfile(); - signal(SIGINT, SIG_DFL); + sigchain_pop(signo); raise(signo); } @@ -1968,7 +1969,7 @@ static void run_external_diff(const char *pgm, atexit_asked = 1; atexit(remove_tempfile); } - signal(SIGINT, remove_tempfile_on_signal); + sigchain_push(SIGINT, remove_tempfile_on_signal); } if (one && two) { diff --git a/http-push.c b/http-push.c index a4b7d08..dec395d 100644 --- a/http-push.c +++ b/http-push.c @@ -10,6 +10,7 @@ #include "exec_cmd.h" #include "remote.h" #include "list-objects.h" +#include "sigchain.h" #include <expat.h> @@ -1363,7 +1364,7 @@ static void remove_locks(void) static void remove_locks_on_signal(int signo) { remove_locks(); - signal(signo, SIG_DFL); + sigchain_pop(signo); raise(signo); } @@ -2261,10 +2262,10 @@ int main(int argc, char **argv) goto cleanup; } - signal(SIGINT, remove_locks_on_signal); - signal(SIGHUP, remove_locks_on_signal); - signal(SIGQUIT, remove_locks_on_signal); - signal(SIGTERM, remove_locks_on_signal); + sigchain_push(SIGINT, remove_locks_on_signal); + sigchain_push(SIGHUP, remove_locks_on_signal); + sigchain_push(SIGQUIT, remove_locks_on_signal); + sigchain_push(SIGTERM, remove_locks_on_signal); /* Check whether the remote has server info files */ remote->can_update_info_refs = 0; diff --git a/lockfile.c b/lockfile.c index 8589155..3cd57dc 100644 --- a/lockfile.c +++ b/lockfile.c @@ -2,6 +2,7 @@ * Copyright (c) 2005, Junio C Hamano */ #include "cache.h" +#include "sigchain.h" static struct lock_file *lock_file_list; static const char *alternate_index_output; @@ -24,7 +25,7 @@ static void remove_lock_file(void) static void remove_lock_file_on_signal(int signo) { remove_lock_file(); - signal(signo, SIG_DFL); + sigchain_pop(signo); raise(signo); } @@ -136,11 +137,11 @@ static int lock_file(struct lock_file *lk, const char *path, int flags) lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); if (0 <= lk->fd) { if (!lock_file_list) { - signal(SIGINT, remove_lock_file_on_signal); - signal(SIGHUP, remove_lock_file_on_signal); - signal(SIGTERM, remove_lock_file_on_signal); - signal(SIGQUIT, remove_lock_file_on_signal); - signal(SIGPIPE, remove_lock_file_on_signal); + sigchain_push(SIGINT, remove_lock_file_on_signal); + sigchain_push(SIGHUP, remove_lock_file_on_signal); + sigchain_push(SIGTERM, remove_lock_file_on_signal); + sigchain_push(SIGQUIT, remove_lock_file_on_signal); + sigchain_push(SIGPIPE, remove_lock_file_on_signal); atexit(remove_lock_file); } lk->owner = getpid(); diff --git a/sigchain.c b/sigchain.c new file mode 100644 index 0000000..a18d505 --- /dev/null +++ b/sigchain.c @@ -0,0 +1,43 @@ +#include "sigchain.h" +#include "cache.h" + +#define SIGCHAIN_MAX_SIGNALS 32 + +struct sigchain_signal { + sigchain_fun *old; + int n; + int alloc; +}; +static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS]; + +static void check_signum(int sig) +{ + if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS) + die("BUG: signal out of range: %d", sig); +} + +int sigchain_push(int sig, sigchain_fun f) +{ + struct sigchain_signal *s = signals + sig; + check_signum(sig); + + ALLOC_GROW(s->old, s->n + 1, s->alloc); + s->old[s->n] = signal(sig, f); + if (s->old[s->n] == SIG_ERR) + return -1; + s->n++; + return 0; +} + +int sigchain_pop(int sig) +{ + struct sigchain_signal *s = signals + sig; + check_signum(sig); + if (s->n < 1) + return 0; + + if (signal(sig, s->old[s->n - 1]) == SIG_ERR) + return -1; + s->n--; + return 0; +} diff --git a/sigchain.h b/sigchain.h new file mode 100644 index 0000000..254ebb0 --- /dev/null +++ b/sigchain.h @@ -0,0 +1,9 @@ +#ifndef SIGCHAIN_H +#define SIGCHAIN_H + +typedef void (*sigchain_fun)(int); + +int sigchain_push(int sig, sigchain_fun f); +int sigchain_pop(int sig); + +#endif /* SIGCHAIN_H */ diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh new file mode 100755 index 0000000..d900df4 --- /dev/null +++ b/t/t0005-signals.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +test_description='signals work as we expect' +. ./test-lib.sh + +cat >expect <<EOF +three +two +one +EOF + +test_expect_success 'sigchain works' ' + test-sigchain >actual + test "$?" = 130 && + test_cmp expect actual +' + +test_done diff --git a/test-sigchain.c b/test-sigchain.c new file mode 100644 index 0000000..8747dea --- /dev/null +++ b/test-sigchain.c @@ -0,0 +1,22 @@ +#include "sigchain.h" +#include "cache.h" + +#define X(f) \ +static void f(int sig) { \ + puts(#f); \ + fflush(stdout); \ + sigchain_pop(sig); \ + raise(sig); \ +} +X(one) +X(two) +X(three) +#undef X + +int main(int argc, char **argv) { + sigchain_push(SIGINT, one); + sigchain_push(SIGINT, two); + sigchain_push(SIGINT, three); + raise(SIGINT); + return 0; +} -- 1.6.1.84.g8150 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [PATCH 2/4] chain kill signals for cleanup functions 2009-01-11 11:32 ` [PATCH 2/4] chain kill signals for cleanup functions Jeff King @ 2009-01-11 11:40 ` Jeff King 0 siblings, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-11 11:40 UTC (permalink / raw) To: Johannes Sixt; +Cc: Junio C Hamano, Johannes Schindelin, git On Sun, Jan 11, 2009 at 06:32:12AM -0500, Jeff King wrote: > @@ -1968,7 +1969,7 @@ static void run_external_diff(const char *pgm, > atexit_asked = 1; > atexit(remove_tempfile); > } > - signal(SIGINT, remove_tempfile_on_signal); > + sigchain_push(SIGINT, remove_tempfile_on_signal); Hmm. Note that because we are now pushing instead of just replacing the signal handler, it might matter if it gets called multiple times (though I think most of the cleanup functions are relatively harmless if run multiple times). Most of the callsites protect against installing the signal handler twice, but I think this one should probably be moved up inside the atexit_asked condition: if (! atexit_asked && (temp[0].name == temp[0].tmp_path || temp[1].name == temp[1].tmp_path)) { atexit_asked = 1; atexit(remove_tempfile); } sigchain_push_common(remove_tempfile_on_signal); -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCH 3/4] refactor signal handling for cleanup functions 2009-01-11 11:22 ` Jeff King 2009-01-11 11:25 ` [PATCH 1/4] Makefile: clean up TEST_PROGRAMS definition Jeff King 2009-01-11 11:32 ` [PATCH 2/4] chain kill signals for cleanup functions Jeff King @ 2009-01-11 11:36 ` Jeff King 2009-01-11 11:36 ` [PATCH 4/4] pager: do wait_for_pager on signal death Jeff King 2009-01-12 10:59 ` Funny: git -p submodule summary Johannes Sixt 4 siblings, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-11 11:36 UTC (permalink / raw) To: Johannes Sixt; +Cc: Junio C Hamano, Johannes Schindelin, git The current code is very inconsistent about which signals are caught for doing cleanup of temporary files and lock files. Some callsites checked only SIGINT, while others checked a variety of death-dealing signals. This patch factors out those signals to a single function, and then calls it everywhere. For some sites, that means this is a simple clean up. For others, it is an improvement in that they will now properly clean themselves up after a larger variety of signals. Signed-off-by: Jeff King <peff@peff.net> --- I'm assuming there was no good reason _not_ to be handling those other signals at some of the "just handle SIGINT" sites. A sigchain implementation which handled "remove_lock_file" without needing "remove_lock_file_on_signal" could also call atexit(), too. So you could have a one liner register_cleanup(remove_lock_file); However, there is one case that doesn't use an atexit() handler: http-push.c. I don't know if this is a bug or an intentional omission. builtin-clone.c | 2 +- builtin-fetch--tool.c | 2 +- builtin-fetch.c | 2 +- diff.c | 2 +- http-push.c | 5 +---- lockfile.c | 6 +----- sigchain.c | 9 +++++++++ sigchain.h | 2 ++ 8 files changed, 17 insertions(+), 13 deletions(-) diff --git a/builtin-clone.c b/builtin-clone.c index 18b9392..44c8073 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -439,7 +439,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } junk_git_dir = git_dir; atexit(remove_junk); - sigchain_push(SIGINT, remove_junk_on_signal); + sigchain_push_common(remove_junk_on_signal); setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1); diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index b1d7f8f..29356d2 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -246,7 +246,7 @@ static int fetch_native_store(FILE *fp, char buffer[1024]; int err = 0; - sigchain_push(SIGINT, remove_keep_on_signal); + sigchain_push_common(remove_keep_on_signal); atexit(remove_keep); while (fgets(buffer, sizeof(buffer), stdin)) { diff --git a/builtin-fetch.c b/builtin-fetch.c index 8c86974..1e4a3d9 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -673,7 +673,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) ref_nr = j; } - sigchain_push(SIGINT, unlock_pack_on_signal); + sigchain_push_common(unlock_pack_on_signal); atexit(unlock_pack); exit_code = do_fetch(transport, parse_fetch_refspec(ref_nr, refs), ref_nr); diff --git a/diff.c b/diff.c index 5a74012..7fc8512 100644 --- a/diff.c +++ b/diff.c @@ -1969,7 +1969,7 @@ static void run_external_diff(const char *pgm, atexit_asked = 1; atexit(remove_tempfile); } - sigchain_push(SIGINT, remove_tempfile_on_signal); + sigchain_push_common(remove_tempfile_on_signal); } if (one && two) { diff --git a/http-push.c b/http-push.c index dec395d..7d5c23e 100644 --- a/http-push.c +++ b/http-push.c @@ -2262,10 +2262,7 @@ int main(int argc, char **argv) goto cleanup; } - sigchain_push(SIGINT, remove_locks_on_signal); - sigchain_push(SIGHUP, remove_locks_on_signal); - sigchain_push(SIGQUIT, remove_locks_on_signal); - sigchain_push(SIGTERM, remove_locks_on_signal); + sigchain_push_common(remove_locks_on_signal); /* Check whether the remote has server info files */ remote->can_update_info_refs = 0; diff --git a/lockfile.c b/lockfile.c index 3cd57dc..021c337 100644 --- a/lockfile.c +++ b/lockfile.c @@ -137,11 +137,7 @@ static int lock_file(struct lock_file *lk, const char *path, int flags) lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666); if (0 <= lk->fd) { if (!lock_file_list) { - sigchain_push(SIGINT, remove_lock_file_on_signal); - sigchain_push(SIGHUP, remove_lock_file_on_signal); - sigchain_push(SIGTERM, remove_lock_file_on_signal); - sigchain_push(SIGQUIT, remove_lock_file_on_signal); - sigchain_push(SIGPIPE, remove_lock_file_on_signal); + sigchain_push_common(remove_lock_file_on_signal); atexit(remove_lock_file); } lk->owner = getpid(); diff --git a/sigchain.c b/sigchain.c index a18d505..1118b99 100644 --- a/sigchain.c +++ b/sigchain.c @@ -41,3 +41,12 @@ int sigchain_pop(int sig) s->n--; return 0; } + +void sigchain_push_common(sigchain_fun f) +{ + sigchain_push(SIGINT, f); + sigchain_push(SIGHUP, f); + sigchain_push(SIGTERM, f); + sigchain_push(SIGQUIT, f); + sigchain_push(SIGPIPE, f); +} diff --git a/sigchain.h b/sigchain.h index 254ebb0..618083b 100644 --- a/sigchain.h +++ b/sigchain.h @@ -6,4 +6,6 @@ typedef void (*sigchain_fun)(int); int sigchain_push(int sig, sigchain_fun f); int sigchain_pop(int sig); +void sigchain_push_common(sigchain_fun f); + #endif /* SIGCHAIN_H */ -- 1.6.1.84.g8150 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCH 4/4] pager: do wait_for_pager on signal death 2009-01-11 11:22 ` Jeff King ` (2 preceding siblings ...) 2009-01-11 11:36 ` [PATCH 3/4] refactor signal handling " Jeff King @ 2009-01-11 11:36 ` Jeff King 2009-01-11 21:13 ` Junio C Hamano 2009-01-12 10:59 ` Funny: git -p submodule summary Johannes Sixt 4 siblings, 1 reply; 41+ messages in thread From: Jeff King @ 2009-01-11 11:36 UTC (permalink / raw) To: Johannes Sixt; +Cc: Junio C Hamano, Johannes Schindelin, git Since ea27a18 (spawn pager via run_command interface), the original git process actually does git work, and the pager is a child process (actually, on Windows it has always been that way, since Windows lacks fork). After spawning the pager, we register an atexit() handler that waits for the pager to finish. Unfortunately, that handler does not always run. In particular, if git is killed by a signal, then we exit immediately. The calling shell then thinks that git is done; however, the pager is still trying to run and impact the terminal. The result can be seen by running a long git process with a pager (e.g., "git log -p") and hitting ^C. Depending on your config, you should see the shell prompt, but pressing a key causes the pager to do any terminal de-initialization sequence. This patch just intercepts any death-dealing signals and waits for the pager before dying. Under typical less configuration, that means hitting ^C will cause git to stop generating output, but the pager will keep running. Signed-off-by: Jeff King <peff@peff.net> --- pager.c | 9 +++++++++ 1 files changed, 9 insertions(+), 0 deletions(-) diff --git a/pager.c b/pager.c index f19ddbc..4921843 100644 --- a/pager.c +++ b/pager.c @@ -1,5 +1,6 @@ #include "cache.h" #include "run-command.h" +#include "sigchain.h" /* * This is split up from the rest of git so that we can do @@ -38,6 +39,13 @@ static void wait_for_pager(void) finish_command(&pager_process); } +static void wait_for_pager_signal(int signo) +{ + wait_for_pager(); + sigchain_pop(signo); + raise(signo); +} + void setup_pager(void) { const char *pager = getenv("GIT_PAGER"); @@ -75,6 +83,7 @@ void setup_pager(void) close(pager_process.in); /* this makes sure that the parent terminates after the pager */ + sigchain_push_common(wait_for_pager_signal); atexit(wait_for_pager); } -- 1.6.1.84.g8150 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [PATCH 4/4] pager: do wait_for_pager on signal death 2009-01-11 11:36 ` [PATCH 4/4] pager: do wait_for_pager on signal death Jeff King @ 2009-01-11 21:13 ` Junio C Hamano 0 siblings, 0 replies; 41+ messages in thread From: Junio C Hamano @ 2009-01-11 21:13 UTC (permalink / raw) To: Jeff King; +Cc: Johannes Sixt, Johannes Schindelin, git Thanks; I agree with you that I do not see a good reason _not_ to be handling those other signals at some of the "just handle SIGINT" sites, and I like the direction in whcih this series is taking us very much. ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-11 11:22 ` Jeff King ` (3 preceding siblings ...) 2009-01-11 11:36 ` [PATCH 4/4] pager: do wait_for_pager on signal death Jeff King @ 2009-01-12 10:59 ` Johannes Sixt 2009-01-12 11:21 ` Jeff King 4 siblings, 1 reply; 41+ messages in thread From: Johannes Sixt @ 2009-01-12 10:59 UTC (permalink / raw) To: Jeff King; +Cc: Junio C Hamano, Johannes Schindelin, git From: Johannes Sixt <j6t@kdbg.org> Jeff King schrieb: > On Fri, Jan 09, 2009 at 11:36:41AM +0100, Johannes Sixt wrote: > >> I'll test your other patch (that replaces the execvp in git.c by >> run_command). > > There is something funny with it that I have not diagnosed: aliases are > broken, and "git foobar" does not return an error. Presumably just > checking the "we did not exec succesfully" case is not triggering > properly. However, I think the right solution is actually to refactor > git.c to figure out ahead of time whether we have a builtin, external, > or alias. I can work on that, but not tonight, as my git-time is up for > now. > > But other than that, did it work for you on Windows? It passed the test suite. This should already work better on Windows, because we already *do* look-up the program, and exit from mingw_spawnvpe() before the equivalent of fork+exec happens. > However, here is a 4-patch series that handles the separate signal > delivery problem. It should fix the "^C makes funny things happen" > problems you were seeing. Please test and let me know how it works on > Windows. It does help a bit. The interesting thing is that the only case where I can now reproduces the unwanted behavior with the unpatched version is when all output was completely read by 'less' and git already waits in wait_for_pager(), such as in 'git show'. But Ctrl-C'ing a 'git log -p' works as expected even without these patches. With the patches, the 'git show' case now works as well. > The patches are: > 1/4: Makefile: clean up TEST_PROGRAMS definition > 2/4: chain kill signals for cleanup functions > 3/4: refactor signal handling for cleanup functions > 4/4: pager: do wait_for_pager on signal death But we need to insert the patch below *before* 2/4. The test case needs a change, too,(exit code on Windows is 3, not 130) but I'll keep that in my repository, like with all other Windows related test suite changes. -- Hannes -- 8< -- From: Johannes Sixt <j6t@kdbg.org> Subject: Windows: Fix signal numbers We had defined some SIG_FOO macros that appear in the code, but that are not supported on Windows, in order to make the code compile. But a subsequent change will assert that a signal number is non-zero. We now use the signal numbers that are commonly used on POSIX systems. Signed-off-by: Johannes Sixt <j6t@kdbg.org> --- compat/mingw.h | 12 ++++++------ 1 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compat/mingw.h b/compat/mingw.h index 4f275cb..a255898 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -21,12 +21,12 @@ typedef int pid_t; #define WEXITSTATUS(x) ((x) & 0xff) #define WIFSIGNALED(x) ((unsigned)(x) > 259) -#define SIGKILL 0 -#define SIGCHLD 0 -#define SIGPIPE 0 -#define SIGHUP 0 -#define SIGQUIT 0 -#define SIGALRM 100 +#define SIGHUP 1 +#define SIGQUIT 3 +#define SIGKILL 9 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGCHLD 17 #define F_GETFD 1 #define F_SETFD 2 -- 1.6.1.rc4.959.gcece.dirty ^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-12 10:59 ` Funny: git -p submodule summary Johannes Sixt @ 2009-01-12 11:21 ` Jeff King 2009-01-12 12:00 ` Johannes Sixt 0 siblings, 1 reply; 41+ messages in thread From: Jeff King @ 2009-01-12 11:21 UTC (permalink / raw) To: Johannes Sixt; +Cc: Junio C Hamano, Johannes Schindelin, git On Mon, Jan 12, 2009 at 11:59:04AM +0100, Johannes Sixt wrote: > But we need to insert the patch below *before* 2/4. The test case needs a > change, too,(exit code on Windows is 3, not 130) but I'll keep that in my > repository, like with all other Windows related test suite changes. Hrm. How do you properly detect "killed by SIGINT" on Windows? That is the intent of that test. > -#define SIGKILL 0 > -#define SIGCHLD 0 > -#define SIGPIPE 0 > -#define SIGHUP 0 > -#define SIGQUIT 0 > -#define SIGALRM 100 > +#define SIGHUP 1 > +#define SIGQUIT 3 > +#define SIGKILL 9 > +#define SIGPIPE 13 > +#define SIGALRM 14 > +#define SIGCHLD 17 Don't these get fed to signal()? Does Windows really not care about getting bogus numbers versus 0 (which is, admittedly, bogus itself)? Or are we just ignoring the return code everywhere? -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-12 11:21 ` Jeff King @ 2009-01-12 12:00 ` Johannes Sixt 2009-01-12 12:03 ` Jeff King 0 siblings, 1 reply; 41+ messages in thread From: Johannes Sixt @ 2009-01-12 12:00 UTC (permalink / raw) To: Jeff King; +Cc: Junio C Hamano, Johannes Schindelin, git Jeff King schrieb: > Hrm. How do you properly detect "killed by SIGINT" on Windows? Good question. The exit code is 0xc000013a if the signal was generated by hitting Ctrl-C (tested with 'git --no-pager log'), but it is 3 if the signal was "generated" by 'raise(SIGINT)'. The implementation of the latter just calls _exit(3) (same for other deadly signals). Wow! >> -#define SIGKILL 0 >> -#define SIGCHLD 0 >> -#define SIGPIPE 0 >> -#define SIGHUP 0 >> -#define SIGQUIT 0 >> -#define SIGALRM 100 >> +#define SIGHUP 1 >> +#define SIGQUIT 3 >> +#define SIGKILL 9 >> +#define SIGPIPE 13 >> +#define SIGALRM 14 >> +#define SIGCHLD 17 > > Don't these get fed to signal()? Does Windows really not care about > getting bogus numbers versus 0 (which is, admittedly, bogus itself)? Or > are we just ignoring the return code everywhere? Windows's signal() does return EINVAL; appearently, we never check the return code. :-P -- Hannes ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-12 12:00 ` Johannes Sixt @ 2009-01-12 12:03 ` Jeff King 2009-01-12 12:19 ` Johannes Sixt 0 siblings, 1 reply; 41+ messages in thread From: Jeff King @ 2009-01-12 12:03 UTC (permalink / raw) To: Johannes Sixt; +Cc: Junio C Hamano, Johannes Schindelin, git On Mon, Jan 12, 2009 at 01:00:05PM +0100, Johannes Sixt wrote: > Good question. The exit code is 0xc000013a if the signal was generated by > hitting Ctrl-C (tested with 'git --no-pager log'), but it is 3 if the > signal was "generated" by 'raise(SIGINT)'. The implementation of the > latter just calls _exit(3) (same for other deadly signals). Wow! Eh? So it doesn't even run the signal handlers? Or if it is SIG_DFL (i.e., we have already run all the handlers), then it just calls _exit? > > Don't these get fed to signal()? Does Windows really not care about > > getting bogus numbers versus 0 (which is, admittedly, bogus itself)? Or > > are we just ignoring the return code everywhere? > > Windows's signal() does return EINVAL; appearently, we never check the > return code. :-P OK. That makes sense; we don't bother checking the return code because it's mostly just registering a cleanup function. The only sane thing to do on an error (besides ignore it) is to abort whatever we're doing, and I'm not sure it's worth it. I do feel a little like this is a trap waiting to spring for future coders to break Windows, but I think your change doesn't make anything _worse_ in that regard. -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-12 12:03 ` Jeff King @ 2009-01-12 12:19 ` Johannes Sixt 0 siblings, 0 replies; 41+ messages in thread From: Johannes Sixt @ 2009-01-12 12:19 UTC (permalink / raw) To: Jeff King; +Cc: Junio C Hamano, Johannes Schindelin, git Jeff King schrieb: > On Mon, Jan 12, 2009 at 01:00:05PM +0100, Johannes Sixt wrote: > >> Good question. The exit code is 0xc000013a if the signal was generated by >> hitting Ctrl-C (tested with 'git --no-pager log'), but it is 3 if the >> signal was "generated" by 'raise(SIGINT)'. The implementation of the >> latter just calls _exit(3) (same for other deadly signals). Wow! > > Eh? So it doesn't even run the signal handlers? Or if it is SIG_DFL > (i.e., we have already run all the handlers), then it just calls _exit? It's silly, but not *that* silly: It calls _exit(3) only if the handler is SIG_DFL. -- Hannes ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-09 8:38 ` Jeff King 2009-01-09 9:22 ` Jeff King @ 2009-01-09 9:30 ` Junio C Hamano 2009-01-09 9:33 ` Jeff King 1 sibling, 1 reply; 41+ messages in thread From: Junio C Hamano @ 2009-01-09 9:30 UTC (permalink / raw) To: Jeff King; +Cc: Johannes Schindelin, git Jeff King <peff@peff.net> writes: > So the _real_ problem is that we are not always triggering the "wait for > pager to finish" code because we exec and forget about it. Which means > this strategy of "git runs child pager" will never work properly. > Instead, we have to use three processes: git and the pager become child > processes, while the original process waits for both to exit and returns > the proper exit code from git. > > Let me try to work up a patch. This arrangement to have the third process could even open the possibility of having it read from git and write to pager, and not launching the pager if there is no interesting data from git to feed it with. I do not know if I like the performance implications associated with it, though. ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-09 9:30 ` Junio C Hamano @ 2009-01-09 9:33 ` Jeff King 2009-01-09 9:38 ` Junio C Hamano 0 siblings, 1 reply; 41+ messages in thread From: Jeff King @ 2009-01-09 9:33 UTC (permalink / raw) To: Junio C Hamano; +Cc: Johannes Schindelin, git On Fri, Jan 09, 2009 at 01:30:23AM -0800, Junio C Hamano wrote: > This arrangement to have the third process could even open the possibility > of having it read from git and write to pager, and not launching the pager > if there is no interesting data from git to feed it with. > > I do not know if I like the performance implications associated with it, > though. Ugh. That has definitely been a requested feature, but the thought of essentially running "cat" in our pipeline strikes me as a bit kludgey. On the other hand, we are by definition going to the pager in that case, so in theory performance is less of a consideration. But see my other mail for why a third process is hard to always do on Windows. -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: Funny: git -p submodule summary 2009-01-09 9:33 ` Jeff King @ 2009-01-09 9:38 ` Junio C Hamano 0 siblings, 0 replies; 41+ messages in thread From: Junio C Hamano @ 2009-01-09 9:38 UTC (permalink / raw) To: Jeff King; +Cc: Johannes Schindelin, git Jeff King <peff@peff.net> writes: > On Fri, Jan 09, 2009 at 01:30:23AM -0800, Junio C Hamano wrote: > >> This arrangement to have the third process could even open the possibility >> of having it read from git and write to pager, and not launching the pager >> if there is no interesting data from git to feed it with. >> >> I do not know if I like the performance implications associated with it, >> though. > > Ugh. That has definitely been a requested feature, but the thought of > essentially running "cat" in our pipeline strikes me as a bit kludgey. > > On the other hand, we are by definition going to the pager in that case, > so in theory performance is less of a consideration. > > But see my other mail for why a third process is hard to always do on > Windows. Heh, this late at night just before going to bed, I am allowed to say that I do not care about Windows at all ;-). More dedicated and competent people will solve it for us while I am sleeping. ^ permalink raw reply [flat|nested] 41+ messages in thread
* [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" 2009-01-08 15:07 Funny: git -p submodule summary Johannes Schindelin 2009-01-08 15:30 ` Johannes Schindelin 2009-01-09 8:38 ` Jeff King @ 2009-01-27 6:25 ` Jeff King 2009-01-27 6:26 ` [RFC/PATCH 1/3] git: s/run_command/run_builtin/ Jeff King ` (4 more replies) 2 siblings, 5 replies; 41+ messages in thread From: Jeff King @ 2009-01-27 6:25 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Johannes Sixt, git On Thu, Jan 08, 2009 at 04:07:08PM +0100, Johannes Schindelin wrote: > Just try this with a submodule that has more changes than fit on a screen: > > $ git -p submodule summary > > In my tests, it consistently fscks up my console. I wonder if this is > related to ea27a18(spawn pager via run_command interface). OK, here is a patch series that fixes the problem: 1/3: git: s/run_command/run_builtin/ 2/3: run_command: handle missing command errors more gracefully 3/3: git: use run_command to execute dashed externals 1 is a cleanup, 2 is infrastructure support, and 3 is the actual fix. There are two potential downsides to the fix: 1. There is an extra fork and a parent process sitting in memory for dashed externals. This is pretty necessary to any fix, since something has to wait to do pager cleanup, and we can't rely on the child to do so. 2. A failed attempt to execute a dashed external results in an extra fork. For builtins, this has no impact, since they take precedence. For aliases, though, it means we will do an extra fork before realizing that there is no dashed external and trying the alias. We can fix '2' by actually doing the PATH lookup ourselves, and only calling run_command if we know we have a match. We can also reduce the impact of both by only doing this multi-process magic if we have spawned a pager; then only a small subset of invocations needs to pay for it. I chose not to do the second optimization because it makes the code more complex and inconsistent (we now have two different ways of doing the same thing, depending on a seemingly unrelated setting) and fragile (the pager might not be the only atexit handler installed). The first (doing PATH lookup ourselves) might make sense, though. JSixt, there are some tweaks to the Windows code to report back the exec error. They look obviously correct to me, but I have no box to test (even compile test) them on. -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* [RFC/PATCH 1/3] git: s/run_command/run_builtin/ 2009-01-27 6:25 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Jeff King @ 2009-01-27 6:26 ` Jeff King 2009-01-27 6:27 ` [RFC/PATCH 2/3] run_command: handle missing command errors more gracefully Jeff King ` (3 subsequent siblings) 4 siblings, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-27 6:26 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Johannes Sixt, git There is a static function called run_command which conflicts with the library function in run-command.c; this isn't a problem currently, but prevents including run-command.h in git.c. This patch just renames the static function to something more specific and non-conflicting. Signed-off-by: Jeff King <peff@peff.net> --- git.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/git.c b/git.c index ecc8fad..45e493d 100644 --- a/git.c +++ b/git.c @@ -219,7 +219,7 @@ struct cmd_struct { int option; }; -static int run_command(struct cmd_struct *p, int argc, const char **argv) +static int run_builtin(struct cmd_struct *p, int argc, const char **argv) { int status; struct stat st; @@ -384,7 +384,7 @@ static void handle_internal_command(int argc, const char **argv) struct cmd_struct *p = commands+i; if (strcmp(p->cmd, cmd)) continue; - exit(run_command(p, argc, argv)); + exit(run_builtin(p, argc, argv)); } } -- 1.6.1.1.367.g30b36 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* [RFC/PATCH 2/3] run_command: handle missing command errors more gracefully 2009-01-27 6:25 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Jeff King 2009-01-27 6:26 ` [RFC/PATCH 1/3] git: s/run_command/run_builtin/ Jeff King @ 2009-01-27 6:27 ` Jeff King 2009-01-27 6:27 ` [RFC/PATCH 3/3] git: use run_command to execute dashed externals Jeff King ` (2 subsequent siblings) 4 siblings, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-27 6:27 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Johannes Sixt, git When run_command was asked to run a non-existant command, its behavior varied depending on the platform: - on POSIX systems, we would fork, and then after the execvp call failed, we could call die(), which prints a message to stderr and exits with code 128. - on Windows, we do a PATH lookup, realize the program isn't there, and then return ERR_RUN_COMMAND_FORK The goal of this patch is to make it clear to callers that the specific error was a missing command. To do this, we will return the error code ERR_RUN_COMMAND_EXEC, which is already defined in run-command.h, checked for in several places, but never actually gets set. The new behavior is: - on POSIX systems, we exit the forked process with code 127 (the same as the shell uses to report missing commands). The parent process recognizes this code and returns an EXEC error. The stderr message is silenced, since the caller may be speculatively trying to run a command. Instead, we use trace_printf so that somebody interested in debugging can see the error that occured. - on Windows, we check errno, which is already set correctly by mingw_spawnvpe, and report an EXEC error instead of a FORK error Thus it is safe to speculatively run a command: int r = run_command_v_opt(argv, 0); if (r == -ERR_RUN_COMMAND_EXEC) /* oops, it wasn't found; try something else */ else /* we failed for some other reason, error is in r */ Signed-off-by: Jeff King <peff@peff.net> --- run-command.c | 19 ++++++++++++++++--- 1 files changed, 16 insertions(+), 3 deletions(-) diff --git a/run-command.c b/run-command.c index db9ce59..2437798 100644 --- a/run-command.c +++ b/run-command.c @@ -118,7 +118,9 @@ int start_command(struct child_process *cmd) } else { execvp(cmd->argv[0], (char *const*) cmd->argv); } - die("exec %s failed.", cmd->argv[0]); + trace_printf("trace: exec '%s' failed: %s\n", cmd->argv[0], + strerror(errno)); + exit(127); } #else int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ @@ -197,7 +199,13 @@ int start_command(struct child_process *cmd) close(cmd->out); if (need_err) close_pair(fderr); +#ifndef __MINGW32__ return -ERR_RUN_COMMAND_FORK; +#else + return errno == ENOENT ? + -ERR_RUN_COMMAND_EXEC : + -ERR_RUN_COMMAND_FORK; +#endif } if (need_in) @@ -236,9 +244,14 @@ static int wait_or_whine(pid_t pid) if (!WIFEXITED(status)) return -ERR_RUN_COMMAND_WAITPID_NOEXIT; code = WEXITSTATUS(status); - if (code) + switch (code) { + case 127: + return -ERR_RUN_COMMAND_EXEC; + case 0: + return 0; + default: return -code; - return 0; + } } } -- 1.6.1.1.367.g30b36 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* [RFC/PATCH 3/3] git: use run_command to execute dashed externals 2009-01-27 6:25 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Jeff King 2009-01-27 6:26 ` [RFC/PATCH 1/3] git: s/run_command/run_builtin/ Jeff King 2009-01-27 6:27 ` [RFC/PATCH 2/3] run_command: handle missing command errors more gracefully Jeff King @ 2009-01-27 6:27 ` Jeff King 2009-01-27 10:06 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Johannes Sixt 2009-01-27 16:31 ` Johannes Schindelin 4 siblings, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-27 6:27 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Johannes Sixt, git We used to simply try calling execvp(); if it succeeded, then we were done and the new program was running. If it didn't, then we knew that it wasn't a valid command. Unfortunately, this interacted badly with the new pager handling. Now that git remains the parent process and the pager is spawned, git has to hang around until the pager is finished. We install an atexit handler to do this, but that handler never gets called if we successfully run execvp. You could see this behavior by running any dashed external using a pager (e.g., "git -p stash list"). The command finishes running, but the pager is still going. In the case of less, it then gets an error reading from the terminal and exits, potentially leaving the terminal in a broken state (and not showing the output). This patch just uses run_command to try running the dashed external. The parent git process then waits for the external process to complete and then handles the pager cleanup as it would for an internal command. Signed-off-by: Jeff King <peff@peff.net> --- git.c | 14 ++++++++++---- 1 files changed, 10 insertions(+), 4 deletions(-) diff --git a/git.c b/git.c index 45e493d..79a836c 100644 --- a/git.c +++ b/git.c @@ -2,6 +2,7 @@ #include "exec_cmd.h" #include "cache.h" #include "quote.h" +#include "run-command.h" const char git_usage_string[] = "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]"; @@ -392,6 +393,7 @@ static void execv_dashed_external(const char **argv) { struct strbuf cmd = STRBUF_INIT; const char *tmp; + int status; strbuf_addf(&cmd, "git-%s", argv[0]); @@ -406,10 +408,14 @@ static void execv_dashed_external(const char **argv) trace_argv_printf(argv, "trace: exec:"); - /* execvp() can only ever return if it fails */ - execvp(cmd.buf, (char **)argv); - - trace_printf("trace: exec failed: %s\n", strerror(errno)); + /* + * if we fail because the command is not found, it is + * OK to return. Otherwise, we just pass along the status code. + */ + status = run_command_v_opt(argv, 0); + if (status != -ERR_RUN_COMMAND_EXEC) + exit(status); + errno = ENOENT; /* as if we called execvp */ argv[0] = tmp; -- 1.6.1.1.367.g30b36 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" 2009-01-27 6:25 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Jeff King ` (2 preceding siblings ...) 2009-01-27 6:27 ` [RFC/PATCH 3/3] git: use run_command to execute dashed externals Jeff King @ 2009-01-27 10:06 ` Johannes Sixt 2009-01-27 12:23 ` Jeff King 2009-01-27 16:31 ` Johannes Schindelin 4 siblings, 1 reply; 41+ messages in thread From: Johannes Sixt @ 2009-01-27 10:06 UTC (permalink / raw) To: Jeff King; +Cc: Johannes Schindelin, git Jeff King schrieb: > JSixt, there are some tweaks to the Windows code to report back the exec > error. They look obviously correct to me, but I have no box to test > (even compile test) them on. Generally, I like this series, in particular since it does not degrade performance on Windows ;) But the following is needed in addition. - Note that run_command returns the negated exit code, therefore, we must negate it again in the call to exit(). Without this t6030 failed. (And negative exit codes causes grief on Windows because bash for some reason does not recognize that as failure.) - The close() calls can overwrite errno. And since fork() should not (cannot?) fail with ENOENT, it's safe to remove the #ifdef __MINGW32__. -- Hannes diff --git a/git.c b/git.c index 79a836c..35635d1 100644 --- a/git.c +++ b/git.c @@ -414,7 +414,7 @@ static void execv_dashed_external */ status = run_command_v_opt(argv, 0); if (status != -ERR_RUN_COMMAND_EXEC) - exit(status); + exit(-status); errno = ENOENT; /* as if we called execvp */ argv[0] = tmp; diff --git a/run-command.c b/run-command.c index 2437798..b05c734 100644 --- a/run-command.c +++ b/run-command.c @@ -187,27 +187,24 @@ int start_command if (s2 >= 0) dup2(s2, 2), close(s2); #endif if (cmd->pid < 0) { + int err = errno; if (need_in) close_pair(fdin); else if (cmd->in) close(cmd->in); if (need_out) close_pair(fdout); else if (cmd->out) close(cmd->out); if (need_err) close_pair(fderr); -#ifndef __MINGW32__ - return -ERR_RUN_COMMAND_FORK; -#else - return errno == ENOENT ? + return err == ENOENT ? -ERR_RUN_COMMAND_EXEC : -ERR_RUN_COMMAND_FORK; -#endif } if (need_in) close(fdin[0]); else if (cmd->in) ^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" 2009-01-27 10:06 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Johannes Sixt @ 2009-01-27 12:23 ` Jeff King 2009-01-27 12:46 ` Johannes Sixt 0 siblings, 1 reply; 41+ messages in thread From: Jeff King @ 2009-01-27 12:23 UTC (permalink / raw) To: Johannes Sixt; +Cc: Johannes Schindelin, git On Tue, Jan 27, 2009 at 11:06:40AM +0100, Johannes Sixt wrote: > - Note that run_command returns the negated exit code, therefore, we must > negate it again in the call to exit(). Without this t6030 failed. (And > negative exit codes causes grief on Windows because bash for some reason > does not recognize that as failure.) Oops, indeed. And you made me realize that I forgot to run the test script against this patchset. However, I'm not sure just negating the exit code is sufficient. run_command can return codes in the 10000 range for its own internal errors. We don't want to pass those out through exit, which will truncate them to 8 bits. > - The close() calls can overwrite errno. Good point. > And since fork() should not (cannot?) fail with ENOENT, it's safe to > remove the #ifdef __MINGW32__. Yeah, I thought of that, but I was worried it might make the code a little bit non-obvious (but it does clean up an ifdef, which is ugly, too). Thanks for your feedback. I'll squash in your fixes and repost 2/3 later today. -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" 2009-01-27 12:23 ` Jeff King @ 2009-01-27 12:46 ` Johannes Sixt 2009-01-28 7:17 ` Jeff King 0 siblings, 1 reply; 41+ messages in thread From: Johannes Sixt @ 2009-01-27 12:46 UTC (permalink / raw) To: Jeff King; +Cc: Johannes Schindelin, git Jeff King schrieb: > However, I'm not sure just negating the exit code is sufficient. > run_command can return codes in the 10000 range for its own internal > errors. We don't want to pass those out through exit, which will > truncate them to 8 bits. Exit code and start_command/finish_command's return code handling is a complete mess IMHO and deserves a clean-up series of its own. If the few codes at 10000 and above are truncated to 8 bits, then we get exit codes 16 and higher; I think that's good enough for this series. -- Hannes ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" 2009-01-27 12:46 ` Johannes Sixt @ 2009-01-28 7:17 ` Jeff King 0 siblings, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-28 7:17 UTC (permalink / raw) To: Johannes Sixt; +Cc: Johannes Schindelin, git On Tue, Jan 27, 2009 at 01:46:19PM +0100, Johannes Sixt wrote: > Exit code and start_command/finish_command's return code handling is a > complete mess IMHO and deserves a clean-up series of its own. If the few Yes, the negation is a bit confusing, just for being allowed to say if (run_command(foo) < 0) since you end up having to store and re-negate anyway to get the actual code. Plus the value of errno is untrustworthy, since we may have been doing cleanup calls. > codes at 10000 and above are truncated to 8 bits, then we get exit codes > 16 and higher; I think that's good enough for this series. I think it is nice to differentiate between an exit code from the sub-program and our own error, though. See my updated series for what I think is a reasonable one-liner fix. -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" 2009-01-27 6:25 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Jeff King ` (3 preceding siblings ...) 2009-01-27 10:06 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Johannes Sixt @ 2009-01-27 16:31 ` Johannes Schindelin 2009-01-28 7:30 ` Jeff King 4 siblings, 1 reply; 41+ messages in thread From: Johannes Schindelin @ 2009-01-27 16:31 UTC (permalink / raw) To: Jeff King; +Cc: Johannes Sixt, git Hi, On Tue, 27 Jan 2009, Jeff King wrote: > On Thu, Jan 08, 2009 at 04:07:08PM +0100, Johannes Schindelin wrote: > > > Just try this with a submodule that has more changes than fit on a > > screen: > > > > $ git -p submodule summary > > > > In my tests, it consistently fscks up my console. I wonder if this is > > related to ea27a18(spawn pager via run_command interface). > > OK, here is a patch series that fixes the problem: > > 1/3: git: s/run_command/run_builtin/ > 2/3: run_command: handle missing command errors more gracefully > 3/3: git: use run_command to execute dashed externals > > 1 is a cleanup, 2 is infrastructure support, and 3 is the actual fix. I like the patch series, well designed and concise (especially with the fixes Hannes proposed). > There are two potential downsides to the fix: > > 1. There is an extra fork and a parent process sitting in memory for > dashed externals. This is pretty necessary to any fix, since > something has to wait to do pager cleanup, and we can't rely on the > child to do so. Actually, I think this is a good thing; that way, we can catch segmentation fault properly and display an error message in the pager. That was not possible previously. > 2. A failed attempt to execute a dashed external results in an extra > fork. For builtins, this has no impact, since they take precedence. > For aliases, though, it means we will do an extra fork before > realizing that there is no dashed external and trying the alias. All the more reason to build more programs in :-) Ciao, Dscho ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" 2009-01-27 16:31 ` Johannes Schindelin @ 2009-01-28 7:30 ` Jeff King 2009-01-28 7:33 ` [PATCHv2 1/4] git: s/run_command/run_builtin/ Jeff King ` (4 more replies) 0 siblings, 5 replies; 41+ messages in thread From: Jeff King @ 2009-01-28 7:30 UTC (permalink / raw) To: Johannes Schindelin; +Cc: Johannes Sixt, Junio C Hamano, git On Tue, Jan 27, 2009 at 05:31:02PM +0100, Johannes Schindelin wrote: > I like the patch series, well designed and concise (especially with the > fixes Hannes proposed). Good. Response seems positive, so I will drop the RFC, then, and post a fixed-up series meant for inclusion. > > There are two potential downsides to the fix: > > > > 1. There is an extra fork and a parent process sitting in memory for > > dashed externals. This is pretty necessary to any fix, since > > something has to wait to do pager cleanup, and we can't rely on the > > child to do so. > > Actually, I think this is a good thing; that way, we can catch > segmentation fault properly and display an error message in the pager. > That was not possible previously. True. On the other hand, most of our externals are shell scripts. It's the builtins that segfault, and we don't have a watcher process for that. :) -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCHv2 1/4] git: s/run_command/run_builtin/ 2009-01-28 7:30 ` Jeff King @ 2009-01-28 7:33 ` Jeff King 2009-01-28 7:35 ` [PATCHv2 2/4] run_command: handle missing command errors more gracefully Jeff King ` (3 subsequent siblings) 4 siblings, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-28 7:33 UTC (permalink / raw) To: Junio C Hamano; +Cc: Johannes Schindelin, Johannes Sixt, git There is a static function called run_command which conflicts with the library function in run-command.c; this isn't a problem currently, but prevents including run-command.h in git.c. This patch just renames the static function to something more specific and non-conflicting. Signed-off-by: Jeff King <peff@peff.net> --- Same as before. git.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/git.c b/git.c index ecc8fad..45e493d 100644 --- a/git.c +++ b/git.c @@ -219,7 +219,7 @@ struct cmd_struct { int option; }; -static int run_command(struct cmd_struct *p, int argc, const char **argv) +static int run_builtin(struct cmd_struct *p, int argc, const char **argv) { int status; struct stat st; @@ -384,7 +384,7 @@ static void handle_internal_command(int argc, const char **argv) struct cmd_struct *p = commands+i; if (strcmp(p->cmd, cmd)) continue; - exit(run_command(p, argc, argv)); + exit(run_builtin(p, argc, argv)); } } -- 1.6.1.1.367.g30b36 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCHv2 2/4] run_command: handle missing command errors more gracefully 2009-01-28 7:30 ` Jeff King 2009-01-28 7:33 ` [PATCHv2 1/4] git: s/run_command/run_builtin/ Jeff King @ 2009-01-28 7:35 ` Jeff King 2009-01-28 7:36 ` [PATCHv2 3/4] run-command: help callers distinguish errors Jeff King ` (2 subsequent siblings) 4 siblings, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-28 7:35 UTC (permalink / raw) To: Junio C Hamano; +Cc: Johannes Schindelin, Johannes Sixt, git When run_command was asked to run a non-existant command, its behavior varied depending on the platform: - on POSIX systems, we would fork, and then after the execvp call failed, we could call die(), which prints a message to stderr and exits with code 128. - on Windows, we do a PATH lookup, realize the program isn't there, and then return ERR_RUN_COMMAND_FORK The goal of this patch is to make it clear to callers that the specific error was a missing command. To do this, we will return the error code ERR_RUN_COMMAND_EXEC, which is already defined in run-command.h, checked for in several places, but never actually gets set. The new behavior is: - on POSIX systems, we exit the forked process with code 127 (the same as the shell uses to report missing commands). The parent process recognizes this code and returns an EXEC error. The stderr message is silenced, since the caller may be speculatively trying to run a command. Instead, we use trace_printf so that somebody interested in debugging can see the error that occured. - on Windows, we check errno, which is already set correctly by mingw_spawnvpe, and report an EXEC error instead of a FORK error Thus it is safe to speculatively run a command: int r = run_command_v_opt(argv, 0); if (r == -ERR_RUN_COMMAND_EXEC) /* oops, it wasn't found; try something else */ else /* we failed for some other reason, error is in r */ Signed-off-by: Jeff King <peff@peff.net> --- Incorporates JSixt's fix to retain errno across system calls. run-command.c | 18 ++++++++++++++---- 1 files changed, 14 insertions(+), 4 deletions(-) diff --git a/run-command.c b/run-command.c index db9ce59..b05c734 100644 --- a/run-command.c +++ b/run-command.c @@ -118,7 +118,9 @@ int start_command(struct child_process *cmd) } else { execvp(cmd->argv[0], (char *const*) cmd->argv); } - die("exec %s failed.", cmd->argv[0]); + trace_printf("trace: exec '%s' failed: %s\n", cmd->argv[0], + strerror(errno)); + exit(127); } #else int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ @@ -187,6 +189,7 @@ int start_command(struct child_process *cmd) #endif if (cmd->pid < 0) { + int err = errno; if (need_in) close_pair(fdin); else if (cmd->in) @@ -197,7 +200,9 @@ int start_command(struct child_process *cmd) close(cmd->out); if (need_err) close_pair(fderr); - return -ERR_RUN_COMMAND_FORK; + return err == ENOENT ? + -ERR_RUN_COMMAND_EXEC : + -ERR_RUN_COMMAND_FORK; } if (need_in) @@ -236,9 +241,14 @@ static int wait_or_whine(pid_t pid) if (!WIFEXITED(status)) return -ERR_RUN_COMMAND_WAITPID_NOEXIT; code = WEXITSTATUS(status); - if (code) + switch (code) { + case 127: + return -ERR_RUN_COMMAND_EXEC; + case 0: + return 0; + default: return -code; - return 0; + } } } -- 1.6.1.1.367.g30b36 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* [PATCHv2 3/4] run-command: help callers distinguish errors 2009-01-28 7:30 ` Jeff King 2009-01-28 7:33 ` [PATCHv2 1/4] git: s/run_command/run_builtin/ Jeff King 2009-01-28 7:35 ` [PATCHv2 2/4] run_command: handle missing command errors more gracefully Jeff King @ 2009-01-28 7:36 ` Jeff King 2009-01-28 7:43 ` Jeff King 2009-01-28 7:38 ` [PATCHv2 4/4] git: use run_command to execute dashed externals Jeff King 2009-01-28 7:54 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Junio C Hamano 4 siblings, 1 reply; 41+ messages in thread From: Jeff King @ 2009-01-28 7:36 UTC (permalink / raw) To: Junio C Hamano; +Cc: Johannes Schindelin, Johannes Sixt, git run_command returns a single integer specifying either an error code or the exit status of the spawned program. The only way to tell the difference is that the error codes are outside of the allowed range of exit status values. Rather than make each caller implement the test against a magic limit, let's provide a macro. Signed-off-by: Jeff King <peff@peff.net> --- New since v1 of the series. run-command.h | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/run-command.h b/run-command.h index 0211e1d..a88b3cd 100644 --- a/run-command.h +++ b/run-command.h @@ -10,6 +10,7 @@ enum { ERR_RUN_COMMAND_WAITPID_SIGNAL, ERR_RUN_COMMAND_WAITPID_NOEXIT, }; +#define IS_RUN_COMMAND_ERR(x) (x >= ERR_RUN_COMMAND_FORK) struct child_process { const char **argv; -- 1.6.1.1.367.g30b36 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [PATCHv2 3/4] run-command: help callers distinguish errors 2009-01-28 7:36 ` [PATCHv2 3/4] run-command: help callers distinguish errors Jeff King @ 2009-01-28 7:43 ` Jeff King 2009-01-28 7:47 ` Jeff King 0 siblings, 1 reply; 41+ messages in thread From: Jeff King @ 2009-01-28 7:43 UTC (permalink / raw) To: Junio C Hamano; +Cc: Johannes Schindelin, Johannes Sixt, git On Wed, Jan 28, 2009 at 02:36:39AM -0500, Jeff King wrote: > +#define IS_RUN_COMMAND_ERR(x) (x >= ERR_RUN_COMMAND_FORK) <sigh> This should be "<= -ERR_RUN_COMMAND_FORK", since we expect the negated status. Maybe it is time I wrote a test for a failed external. -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* Re: [PATCHv2 3/4] run-command: help callers distinguish errors 2009-01-28 7:43 ` Jeff King @ 2009-01-28 7:47 ` Jeff King 0 siblings, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-28 7:47 UTC (permalink / raw) To: Junio C Hamano; +Cc: Johannes Schindelin, Johannes Sixt, git On Wed, Jan 28, 2009 at 02:43:34AM -0500, Jeff King wrote: > On Wed, Jan 28, 2009 at 02:36:39AM -0500, Jeff King wrote: > > > +#define IS_RUN_COMMAND_ERR(x) (x >= ERR_RUN_COMMAND_FORK) > > <sigh> This should be "<= -ERR_RUN_COMMAND_FORK", since we expect the > negated status. > > Maybe it is time I wrote a test for a failed external. Hmm. Actually, it is hard to write a test case for this; it only triggers if we actually have an error running a program, like fork() failing. -Peff ^ permalink raw reply [flat|nested] 41+ messages in thread
* [PATCHv2 4/4] git: use run_command to execute dashed externals 2009-01-28 7:30 ` Jeff King ` (2 preceding siblings ...) 2009-01-28 7:36 ` [PATCHv2 3/4] run-command: help callers distinguish errors Jeff King @ 2009-01-28 7:38 ` Jeff King 2009-01-28 7:54 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Junio C Hamano 4 siblings, 0 replies; 41+ messages in thread From: Jeff King @ 2009-01-28 7:38 UTC (permalink / raw) To: Junio C Hamano; +Cc: Johannes Schindelin, Johannes Sixt, git We used to simply try calling execvp(); if it succeeded, then we were done and the new program was running. If it didn't, then we knew that it wasn't a valid command. Unfortunately, this interacted badly with the new pager handling. Now that git remains the parent process and the pager is spawned, git has to hang around until the pager is finished. We install an atexit handler to do this, but that handler never gets called if we successfully run execvp. You could see this behavior by running any dashed external using a pager (e.g., "git -p stash list"). The command finishes running, but the pager is still going. In the case of less, it then gets an error reading from the terminal and exits, potentially leaving the terminal in a broken state (and not showing the output). This patch just uses run_command to try running the dashed external. The parent git process then waits for the external process to complete and then handles the pager cleanup as it would for an internal command. Signed-off-by: Jeff King <peff@peff.net> --- Incorporates negated status fix from JSixt. This version also differentiates in the exit code and stderr output whether we simply failed to exec the command versus passing along its status code. git.c | 17 +++++++++++++---- 1 files changed, 13 insertions(+), 4 deletions(-) diff --git a/git.c b/git.c index 45e493d..b02b05b 100644 --- a/git.c +++ b/git.c @@ -2,6 +2,7 @@ #include "exec_cmd.h" #include "cache.h" #include "quote.h" +#include "run-command.h" const char git_usage_string[] = "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]"; @@ -392,6 +393,7 @@ static void execv_dashed_external(const char **argv) { struct strbuf cmd = STRBUF_INIT; const char *tmp; + int status; strbuf_addf(&cmd, "git-%s", argv[0]); @@ -406,10 +408,17 @@ static void execv_dashed_external(const char **argv) trace_argv_printf(argv, "trace: exec:"); - /* execvp() can only ever return if it fails */ - execvp(cmd.buf, (char **)argv); - - trace_printf("trace: exec failed: %s\n", strerror(errno)); + /* + * if we fail because the command is not found, it is + * OK to return. Otherwise, we just pass along the status code. + */ + status = run_command_v_opt(argv, 0); + if (status != -ERR_RUN_COMMAND_EXEC) { + if (IS_RUN_COMMAND_ERR(status)) + die("unable to run '%s'", argv[0]); + exit(-status); + } + errno = ENOENT; /* as if we called execvp */ argv[0] = tmp; -- 1.6.1.1.367.g30b36 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* Re: [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" 2009-01-28 7:30 ` Jeff King ` (3 preceding siblings ...) 2009-01-28 7:38 ` [PATCHv2 4/4] git: use run_command to execute dashed externals Jeff King @ 2009-01-28 7:54 ` Junio C Hamano 4 siblings, 0 replies; 41+ messages in thread From: Junio C Hamano @ 2009-01-28 7:54 UTC (permalink / raw) To: Jeff King; +Cc: Johannes Schindelin, Johannes Sixt, git Jeff King <peff@peff.net> writes: > On Tue, Jan 27, 2009 at 05:31:02PM +0100, Johannes Schindelin wrote: > >> I like the patch series, well designed and concise (especially with the >> fixes Hannes proposed). The series looks quite sane and clean. Will queue once I finish wrapping up the push with ".have" thing. ^ permalink raw reply [flat|nested] 41+ messages in thread
end of thread, other threads:[~2009-01-28 7:56 UTC | newest] Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2009-01-08 15:07 Funny: git -p submodule summary Johannes Schindelin 2009-01-08 15:30 ` Johannes Schindelin 2009-01-09 8:38 ` Jeff King 2009-01-09 9:22 ` Jeff King 2009-01-09 9:48 ` Jeff King 2009-01-09 10:09 ` Johannes Sixt 2009-01-09 10:13 ` Jeff King 2009-01-09 10:36 ` Johannes Sixt 2009-01-09 10:47 ` Jeff King 2009-01-11 11:22 ` Jeff King 2009-01-11 11:25 ` [PATCH 1/4] Makefile: clean up TEST_PROGRAMS definition Jeff King 2009-01-11 11:32 ` [PATCH 2/4] chain kill signals for cleanup functions Jeff King 2009-01-11 11:40 ` Jeff King 2009-01-11 11:36 ` [PATCH 3/4] refactor signal handling " Jeff King 2009-01-11 11:36 ` [PATCH 4/4] pager: do wait_for_pager on signal death Jeff King 2009-01-11 21:13 ` Junio C Hamano 2009-01-12 10:59 ` Funny: git -p submodule summary Johannes Sixt 2009-01-12 11:21 ` Jeff King 2009-01-12 12:00 ` Johannes Sixt 2009-01-12 12:03 ` Jeff King 2009-01-12 12:19 ` Johannes Sixt 2009-01-09 9:30 ` Junio C Hamano 2009-01-09 9:33 ` Jeff King 2009-01-09 9:38 ` Junio C Hamano 2009-01-27 6:25 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Jeff King 2009-01-27 6:26 ` [RFC/PATCH 1/3] git: s/run_command/run_builtin/ Jeff King 2009-01-27 6:27 ` [RFC/PATCH 2/3] run_command: handle missing command errors more gracefully Jeff King 2009-01-27 6:27 ` [RFC/PATCH 3/3] git: use run_command to execute dashed externals Jeff King 2009-01-27 10:06 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Johannes Sixt 2009-01-27 12:23 ` Jeff King 2009-01-27 12:46 ` Johannes Sixt 2009-01-28 7:17 ` Jeff King 2009-01-27 16:31 ` Johannes Schindelin 2009-01-28 7:30 ` Jeff King 2009-01-28 7:33 ` [PATCHv2 1/4] git: s/run_command/run_builtin/ Jeff King 2009-01-28 7:35 ` [PATCHv2 2/4] run_command: handle missing command errors more gracefully Jeff King 2009-01-28 7:36 ` [PATCHv2 3/4] run-command: help callers distinguish errors Jeff King 2009-01-28 7:43 ` Jeff King 2009-01-28 7:47 ` Jeff King 2009-01-28 7:38 ` [PATCHv2 4/4] git: use run_command to execute dashed externals Jeff King 2009-01-28 7:54 ` [RFC/PATCH 0/3] fix "Funny: git -p submodule summary" Junio C Hamano
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.