1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-11-13 20:53:02 +01:00
git/write_or_die.c

104 lines
2.1 KiB
C
Raw Normal View History

#include "cache.h"
write_or_die: raise SIGPIPE when we get EPIPE The write_or_die function will always die on an error, including EPIPE. However, it currently treats EPIPE specially by suppressing any error message, and by exiting with exit code 0. Suppressing the error message makes some sense; a pipe death may just be a sign that the other side is not interested in what we have to say. However, exiting with a successful error code is not a good idea, as write_or_die is frequently used in cases where we want to be careful about having written all of the output, and we may need to signal to our caller that we have done so (e.g., you would not want a push whose other end has hung up to report success). This distinction doesn't typically matter in git, because we do not ignore SIGPIPE in the first place. Which means that we will not get EPIPE, but instead will just die when we get a SIGPIPE. But it's possible for a default handler to be set by a parent process, or for us to add a callsite inside one of our few SIGPIPE-ignoring blocks of code. This patch converts write_or_die to actually raise SIGPIPE when we see EPIPE, rather than exiting with zero. This brings the behavior in line with the "normal" case that we die from SIGPIPE (and any callers who want to check why we died will see the same thing). We also give the same treatment to other related functions, including write_or_whine_pipe and maybe_flush_or_die. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 21:01:36 +01:00
static void check_pipe(int err)
{
if (err == EPIPE) {
signal(SIGPIPE, SIG_DFL);
raise(SIGPIPE);
/* Should never happen, but just in case... */
exit(141);
}
}
/*
* Some cases use stdio, but want to flush after the write
* to get error handling (and to get better interactive
* behaviour - not buffering excessively).
*
* Of course, if the flush happened within the write itself,
* we've already lost the error code, and cannot report it any
* more. So we just ignore that case instead (and hope we get
* the right error code on the flush).
*
* If the file handle is stdout, and stdout is a file, then skip the
* flush entirely since it's not needed.
*/
void maybe_flush_or_die(FILE *f, const char *desc)
{
static int skip_stdout_flush = -1;
struct stat st;
char *cp;
if (f == stdout) {
if (skip_stdout_flush < 0) {
cp = getenv("GIT_FLUSH");
if (cp)
skip_stdout_flush = (atoi(cp) == 0);
else if ((fstat(fileno(stdout), &st) == 0) &&
S_ISREG(st.st_mode))
skip_stdout_flush = 1;
else
skip_stdout_flush = 0;
}
if (skip_stdout_flush && !ferror(f))
return;
}
if (fflush(f)) {
write_or_die: raise SIGPIPE when we get EPIPE The write_or_die function will always die on an error, including EPIPE. However, it currently treats EPIPE specially by suppressing any error message, and by exiting with exit code 0. Suppressing the error message makes some sense; a pipe death may just be a sign that the other side is not interested in what we have to say. However, exiting with a successful error code is not a good idea, as write_or_die is frequently used in cases where we want to be careful about having written all of the output, and we may need to signal to our caller that we have done so (e.g., you would not want a push whose other end has hung up to report success). This distinction doesn't typically matter in git, because we do not ignore SIGPIPE in the first place. Which means that we will not get EPIPE, but instead will just die when we get a SIGPIPE. But it's possible for a default handler to be set by a parent process, or for us to add a callsite inside one of our few SIGPIPE-ignoring blocks of code. This patch converts write_or_die to actually raise SIGPIPE when we see EPIPE, rather than exiting with zero. This brings the behavior in line with the "normal" case that we die from SIGPIPE (and any callers who want to check why we died will see the same thing). We also give the same treatment to other related functions, including write_or_whine_pipe and maybe_flush_or_die. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 21:01:36 +01:00
check_pipe(errno);
die_errno("write failure on '%s'", desc);
}
}
void fprintf_or_die(FILE *f, const char *fmt, ...)
{
va_list ap;
int ret;
va_start(ap, fmt);
ret = vfprintf(f, fmt, ap);
va_end(ap);
if (ret < 0) {
check_pipe(errno);
die_errno("write error");
}
}
void fsync_or_die(int fd, const char *msg)
{
if (fsync(fd) < 0) {
die_errno("fsync error on '%s'", msg);
}
}
void write_or_die(int fd, const void *buf, size_t count)
{
if (write_in_full(fd, buf, count) < 0) {
write_or_die: raise SIGPIPE when we get EPIPE The write_or_die function will always die on an error, including EPIPE. However, it currently treats EPIPE specially by suppressing any error message, and by exiting with exit code 0. Suppressing the error message makes some sense; a pipe death may just be a sign that the other side is not interested in what we have to say. However, exiting with a successful error code is not a good idea, as write_or_die is frequently used in cases where we want to be careful about having written all of the output, and we may need to signal to our caller that we have done so (e.g., you would not want a push whose other end has hung up to report success). This distinction doesn't typically matter in git, because we do not ignore SIGPIPE in the first place. Which means that we will not get EPIPE, but instead will just die when we get a SIGPIPE. But it's possible for a default handler to be set by a parent process, or for us to add a callsite inside one of our few SIGPIPE-ignoring blocks of code. This patch converts write_or_die to actually raise SIGPIPE when we see EPIPE, rather than exiting with zero. This brings the behavior in line with the "normal" case that we die from SIGPIPE (and any callers who want to check why we died will see the same thing). We also give the same treatment to other related functions, including write_or_whine_pipe and maybe_flush_or_die. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 21:01:36 +01:00
check_pipe(errno);
die_errno("write error");
}
}
int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg)
{
if (write_in_full(fd, buf, count) < 0) {
write_or_die: raise SIGPIPE when we get EPIPE The write_or_die function will always die on an error, including EPIPE. However, it currently treats EPIPE specially by suppressing any error message, and by exiting with exit code 0. Suppressing the error message makes some sense; a pipe death may just be a sign that the other side is not interested in what we have to say. However, exiting with a successful error code is not a good idea, as write_or_die is frequently used in cases where we want to be careful about having written all of the output, and we may need to signal to our caller that we have done so (e.g., you would not want a push whose other end has hung up to report success). This distinction doesn't typically matter in git, because we do not ignore SIGPIPE in the first place. Which means that we will not get EPIPE, but instead will just die when we get a SIGPIPE. But it's possible for a default handler to be set by a parent process, or for us to add a callsite inside one of our few SIGPIPE-ignoring blocks of code. This patch converts write_or_die to actually raise SIGPIPE when we see EPIPE, rather than exiting with zero. This brings the behavior in line with the "normal" case that we die from SIGPIPE (and any callers who want to check why we died will see the same thing). We also give the same treatment to other related functions, including write_or_whine_pipe and maybe_flush_or_die. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-02-20 21:01:36 +01:00
check_pipe(errno);
fprintf(stderr, "%s: write error (%s)\n",
msg, strerror(errno));
return 0;
}
return 1;
}
int write_or_whine(int fd, const void *buf, size_t count, const char *msg)
{
if (write_in_full(fd, buf, count) < 0) {
fprintf(stderr, "%s: write error (%s)\n",
msg, strerror(errno));
return 0;
}
return 1;
}