summaryrefslogtreecommitdiff
path: root/build/rpmfc.c
diff options
context:
space:
mode:
authorPanu Matilainen <pmatilai@redhat.com>2010-07-02 12:21:00 +0300
committerPanu Matilainen <pmatilai@redhat.com>2010-07-02 12:23:43 +0300
commit375a6b5630b8e37e1d3f0c7ecbe10fe460c4d420 (patch)
tree7f3346e7fdd8ccc872328720de6b353a65644474 /build/rpmfc.c
parent8fe6438fdec279dbf6ea1a8346511f125c92ff3b (diff)
downloadlibrpm-tizen-375a6b5630b8e37e1d3f0c7ecbe10fe460c4d420.tar.gz
librpm-tizen-375a6b5630b8e37e1d3f0c7ecbe10fe460c4d420.tar.bz2
librpm-tizen-375a6b5630b8e37e1d3f0c7ecbe10fe460c4d420.zip
Rewrite getOutputFrom() in a race-free way (supposedly ;)
- Use a self-pipe to handle signal race on select(). pselect() would work too but this is more portable and avoids other signal hassles. - Use non-blocking IO for communicating with the child to avoid spin-happy timeouts, just check all fd's properly before trying to use them - Avoid leaking memory from readBuff on errors
Diffstat (limited to 'build/rpmfc.c')
-rw-r--r--build/rpmfc.c180
1 files changed, 108 insertions, 72 deletions
diff --git a/build/rpmfc.c b/build/rpmfc.c
index 3c915f5dc..1a1dcbd19 100644
--- a/build/rpmfc.c
+++ b/build/rpmfc.c
@@ -129,6 +129,40 @@ static int rpmfcExpandAppend(ARGV_t * argvp, ARGV_const_t av)
return 0;
}
+static int _sigpipe[2] = { -1, -1 };
+
+static void sigpipe_handler(int sig)
+{
+ char sigc = sig;
+ int xx; /* shut up gcc, we can't handle an error here */
+ xx = write(_sigpipe[1], &sigc, 1);
+}
+
+static int sigpipe_init(void)
+{
+ if (pipe(_sigpipe) < 0)
+ return -1;
+ /* Set close on exec and non-blocking (must not get stuck in sighandler) */
+ fcntl(_sigpipe[0], F_SETFL, (fcntl(_sigpipe[0], F_GETFL)|O_NONBLOCK));
+ fcntl(_sigpipe[0], F_SETFD, (fcntl(_sigpipe[0], F_GETFD)|FD_CLOEXEC));
+ fcntl(_sigpipe[1], F_SETFL, (fcntl(_sigpipe[1], F_GETFL)|O_NONBLOCK));
+ fcntl(_sigpipe[1], F_SETFD, (fcntl(_sigpipe[1], F_GETFD)|FD_CLOEXEC));
+ /* XXX SIGPIPE too, but NSPR disables it already, dont mess with it */
+ signal(SIGCHLD, sigpipe_handler);
+ return _sigpipe[0];
+}
+
+static void sigpipe_finish(void)
+{
+ signal(SIGCHLD, SIG_DFL);
+ close(_sigpipe[0]);
+ close(_sigpipe[1]);
+ _sigpipe[0] = -1;
+ _sigpipe[1] = -1;
+}
+
+#define max(x,y) ((x) > (y) ? (x) : (y))
+
/** \ingroup rpmbuild
* Return output from helper script.
* @todo Use poll(2) rather than select(2), if available.
@@ -144,32 +178,31 @@ static StringBuf getOutputFrom(const char * dir, ARGV_t argv,
int failNonZero)
{
pid_t child, reaped;
- int toProg[2];
- int fromProg[2];
+ int toProg[2] = { -1, -1 };
+ int fromProg[2] = { -1, -1 };
int status;
- void *oldhandler;
StringBuf readBuff;
- int done;
+ int myerrno = 0;
+ int sigpipe = sigpipe_init();
+ int ret = 1; /* assume failure */
- /* FIX: cast? */
- oldhandler = signal(SIGPIPE, SIG_IGN);
-
- toProg[0] = toProg[1] = 0;
- fromProg[0] = fromProg[1] = 0;
- if (pipe(toProg) < 0 || pipe(fromProg) < 0) {
+ if (sigpipe < 0 || pipe(toProg) < 0 || pipe(fromProg) < 0) {
rpmlog(RPMLOG_ERR, _("Couldn't create pipe for %s: %m\n"), argv[0]);
return NULL;
}
- if (!(child = fork())) {
- (void) close(toProg[1]);
- (void) close(fromProg[0]);
+ child = fork();
+ if (child == 0) {
+ /* NSPR messes with SIGPIPE, reset to default for the kids */
+ signal(SIGPIPE, SIG_DFL);
+ close(toProg[1]);
+ close(fromProg[0]);
- (void) dup2(toProg[0], STDIN_FILENO); /* Make stdin the in pipe */
- (void) dup2(fromProg[1], STDOUT_FILENO); /* Make stdout the out pipe */
+ dup2(toProg[0], STDIN_FILENO); /* Make stdin the in pipe */
+ close(toProg[0]);
- (void) close(toProg[0]);
- (void) close(fromProg[1]);
+ dup2(fromProg[1], STDOUT_FILENO); /* Make stdout the out pipe */
+ close(fromProg[1]);
if (dir && chdir(dir)) {
rpmlog(RPMLOG_ERR, _("Couldn't chdir to %s: %s\n"),
@@ -181,8 +214,7 @@ static StringBuf getOutputFrom(const char * dir, ARGV_t argv,
argv[0], (unsigned)getpid());
unsetenv("MALLOC_CHECK_");
- (void) execvp(argv[0], (char *const *)argv);
- /* XXX this error message is probably not seen. */
+ execvp(argv[0], (char *const *)argv);
rpmlog(RPMLOG_ERR, _("Couldn't exec %s: %s\n"),
argv[0], strerror(errno));
_exit(EXIT_FAILURE);
@@ -193,95 +225,99 @@ static StringBuf getOutputFrom(const char * dir, ARGV_t argv,
return NULL;
}
- (void) close(toProg[0]);
- (void) close(fromProg[1]);
+ close(toProg[0]);
+ close(fromProg[1]);
- /* Do not block reading or writing from/to prog. */
- (void) fcntl(fromProg[0], F_SETFL, O_NONBLOCK);
- (void) fcntl(toProg[1], F_SETFL, O_NONBLOCK);
-
readBuff = newStringBuf();
- do {
+ while (1) {
fd_set ibits, obits;
- struct timeval tv;
- int nfd, nbw, nbr;
+ int nfd = 0;
int rc;
+ char buf[BUFSIZ+1];
- done = 0;
-top:
FD_ZERO(&ibits);
FD_ZERO(&obits);
- if (fromProg[0] >= 0) {
- FD_SET(fromProg[0], &ibits);
- }
- if (toProg[1] >= 0) {
+
+ FD_SET(sigpipe, &ibits);
+ nfd = max(nfd, sigpipe);
+ FD_SET(fromProg[0], &ibits);
+ nfd = max(nfd, fromProg[0]);
+
+ if (writeBytesLeft > 0) {
FD_SET(toProg[1], &obits);
+ nfd = max(nfd, toProg[1]);
}
- /* XXX values set to limit spinning with perl doing ~100 forks/sec. */
- tv.tv_sec = 0;
- tv.tv_usec = 10000;
- nfd = ((fromProg[0] > toProg[1]) ? fromProg[0] : toProg[1]);
- if ((rc = select(nfd, &ibits, &obits, NULL, &tv)) < 0) {
- if (errno == EINTR)
- goto top;
+
+ rc = select(nfd + 1, &ibits, &obits, NULL, NULL);
+ if (rc < 0 && errno == EINTR) continue;
+ if (rc < 0) {
+ myerrno = errno;
break;
}
- /* Write any data to program */
- if (toProg[1] >= 0 && FD_ISSET(toProg[1], &obits)) {
- if (writePtr && writeBytesLeft > 0) {
- if ((nbw = write(toProg[1], writePtr,
- (1024<writeBytesLeft) ? 1024 : writeBytesLeft)) < 0) {
- if (errno != EAGAIN) {
- rpmlog(RPMLOG_ERR, _("%s: failure writing to %s: %m\n"),
- __func__, argv[0]);
- exit(EXIT_FAILURE);
- }
- nbw = 0;
+ /* Child exited, we're done */
+ if (FD_ISSET(sigpipe, &ibits)) {
+ while (read(sigpipe, buf, sizeof(buf)) > 0) {};
+ break;
+ }
+
+ /* Write data to child */
+ if (writeBytesLeft > 0 && FD_ISSET(toProg[1], &obits)) {
+ size_t nb = (1024 < writeBytesLeft) ? 1024 : writeBytesLeft;
+ int nbw = write(toProg[1], writePtr, nb);
+ if (nbw < 0 && errno == EINTR) continue;
+ if (nbw < 0) {
+ myerrno = errno;
+ break;
}
writeBytesLeft -= nbw;
writePtr += nbw;
- } else if (toProg[1] >= 0) { /* close write fd */
- (void) close(toProg[1]);
- toProg[1] = -1;
- }
+ /* Close write-side pipe to notify child on EOF */
+ if (writeBytesLeft == 0) {
+ close(toProg[1]);
+ toProg[1] = -1;
+ }
}
- /* Read any data from prog */
- { char buf[BUFSIZ+1];
+ /* Read when we get data back from the child */
+ if (FD_ISSET(fromProg[0], &ibits)) {
+ int nbr;
while ((nbr = read(fromProg[0], buf, sizeof(buf)-1)) > 0) {
buf[nbr] = '\0';
appendStringBuf(readBuff, buf);
}
}
-
- /* terminate on (non-blocking) EOF or error */
- done = (nbr == 0 || (nbr < 0 && errno != EAGAIN));
-
- } while (!done);
+ }
/* Clean up */
if (toProg[1] >= 0)
- (void) close(toProg[1]);
+ close(toProg[1]);
if (fromProg[0] >= 0)
- (void) close(fromProg[0]);
- /* FIX: cast? */
- (void) signal(SIGPIPE, oldhandler);
+ close(fromProg[0]);
/* Collect status from prog */
reaped = waitpid(child, &status, 0);
rpmlog(RPMLOG_DEBUG, "\twaitpid(%d) rc %d status %x\n",
(unsigned)child, (unsigned)reaped, status);
+ sigpipe_finish();
if (failNonZero && (!WIFEXITED(status) || WEXITSTATUS(status))) {
- rpmlog(RPMLOG_ERR, _("%s failed\n"), argv[0]);
- return NULL;
+ rpmlog(RPMLOG_ERR, _("%s failed: %x\n"), argv[0], status);
+ goto exit;
}
- if (writeBytesLeft) {
- rpmlog(RPMLOG_ERR, _("failed to write all data to %s\n"), argv[0]);
- return NULL;
+ if (writeBytesLeft || myerrno) {
+ rpmlog(RPMLOG_ERR, _("failed to write all data to %s: %s\n"),
+ argv[0], strerror(myerrno));
+ goto exit;
}
+ ret = 0;
+
+exit:
+ if (ret) {
+ readBuff = freeStringBuf(readBuff);
+ }
+
return readBuff;
}