Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 6 Dec 2009 22:14:58 +0000 (UTC)
From:      Jilles Tjoelker <jilles@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r200189 - in stable/8: lib/libc/gen tools/regression/lib/libc/gen
Message-ID:  <200912062214.nB6MEwRg033175@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jilles
Date: Sun Dec  6 22:14:58 2009
New Revision: 200189
URL: http://svn.freebsd.org/changeset/base/200189

Log:
  MFC r198406: wordexp(3): fix some bugs with signals and long outputs
  
  * retry various system calls on EINTR
  * retry the rest after a short read (common if there is more than about 1K
    of output)
  * block SIGCHLD like system(3) does (note that this does not and cannot
    work fully in threaded programs, they will need to be careful with wait
    functions)
  
  PR:		90580

Modified:
  stable/8/lib/libc/gen/wordexp.c
  stable/8/tools/regression/lib/libc/gen/test-wordexp.c
Directory Properties:
  stable/8/lib/libc/   (props changed)
  stable/8/lib/libc/stdtime/   (props changed)
  stable/8/tools/regression/lib/libc/   (props changed)

Modified: stable/8/lib/libc/gen/wordexp.c
==============================================================================
--- stable/8/lib/libc/gen/wordexp.c	Sun Dec  6 22:01:45 2009	(r200188)
+++ stable/8/lib/libc/gen/wordexp.c	Sun Dec  6 22:14:58 2009	(r200189)
@@ -28,8 +28,10 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <paths.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -73,6 +75,24 @@ wordexp(const char * __restrict words, w
 	return (0);
 }
 
+static size_t
+we_read_fully(int fd, char *buffer, size_t len)
+{
+	size_t done;
+	ssize_t nread;
+
+	done = 0;
+	do {
+		nread = _read(fd, buffer + done, len - done);
+		if (nread == -1 && errno == EINTR)
+			continue;
+		if (nread <= 0)
+			break;
+		done += nread;
+	} while (done != len);
+	return done;
+}
+
 /*
  * we_askshell --
  *	Use the `wordexp' /bin/sh builtin function to do most of the work
@@ -90,20 +110,31 @@ we_askshell(const char *words, wordexp_t
 	size_t sofs;			/* Offset into we->we_strings */
 	size_t vofs;			/* Offset into we->we_wordv */
 	pid_t pid;			/* Process ID of child */
+	pid_t wpid;			/* waitpid return value */
 	int status;			/* Child exit status */
+	int error;			/* Our return value */
+	int serrno;			/* errno to return */
 	char *ifs;			/* IFS env. var. */
 	char *np, *p;			/* Handy pointers */
 	char *nstrings;			/* Temporary for realloc() */
 	char **nwv;			/* Temporary for realloc() */
+	sigset_t newsigblock, oldsigblock;
 
+	serrno = errno;
 	if ((ifs = getenv("IFS")) == NULL)
 		ifs = " \t\n";
 
 	if (pipe(pdes) < 0)
 		return (WRDE_NOSPACE);	/* XXX */
+	(void)sigemptyset(&newsigblock);
+	(void)sigaddset(&newsigblock, SIGCHLD);
+	(void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
 	if ((pid = fork()) < 0) {
+		serrno = errno;
 		_close(pdes[0]);
 		_close(pdes[1]);
+		(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
+		errno = serrno;
 		return (WRDE_NOSPACE);	/* XXX */
 	}
 	else if (pid == 0) {
@@ -114,6 +145,7 @@ we_askshell(const char *words, wordexp_t
 		int devnull;
 		char *cmd;
 
+		(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
 		_close(pdes[0]);
 		if (_dup2(pdes[1], STDOUT_FILENO) < 0)
 			_exit(1);
@@ -139,10 +171,11 @@ we_askshell(const char *words, wordexp_t
 	 * the expanded words separated by nulls.
 	 */
 	_close(pdes[1]);
-	if (_read(pdes[0], wbuf, 8) != 8 || _read(pdes[0], bbuf, 8) != 8) {
-		_close(pdes[0]);
-		_waitpid(pid, &status, 0);
-		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
+	if (we_read_fully(pdes[0], wbuf, 8) != 8 ||
+			we_read_fully(pdes[0], bbuf, 8) != 8) {
+		error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
+		serrno = errno;
+		goto cleanup;
 	}
 	wbuf[8] = bbuf[8] = '\0';
 	nwords = strtol(wbuf, NULL, 16);
@@ -162,33 +195,38 @@ we_askshell(const char *words, wordexp_t
 	if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 +
 	    (flags & WRDE_DOOFFS ?  we->we_offs : 0)) *
 	    sizeof(char *))) == NULL) {
-		_close(pdes[0]);
-		_waitpid(pid, &status, 0);
-		return (WRDE_NOSPACE);
+		error = WRDE_NOSPACE;
+		goto cleanup;
 	}
 	we->we_wordv = nwv;
 	if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) {
-		_close(pdes[0]);
-		_waitpid(pid, &status, 0);
-		return (WRDE_NOSPACE);
+		error = WRDE_NOSPACE;
+		goto cleanup;
 	}
 	for (i = 0; i < vofs; i++)
 		if (we->we_wordv[i] != NULL)
 			we->we_wordv[i] += nstrings - we->we_strings;
 	we->we_strings = nstrings;
 
-	if (_read(pdes[0], we->we_strings + sofs, nbytes) != nbytes) {
-		_close(pdes[0]);
-		_waitpid(pid, &status, 0);
-		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
+	if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) {
+		error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
+		serrno = errno;
+		goto cleanup;
 	}
 
-	if (_waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) ||
-	    WEXITSTATUS(status) != 0) {
-		_close(pdes[0]);
-		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
-	}
+	error = 0;
+cleanup:
 	_close(pdes[0]);
+	do
+		wpid = _waitpid(pid, &status, 0);
+	while (wpid < 0 && errno == EINTR);
+	(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
+	if (error != 0) {
+		errno = serrno;
+		return (error);
+	}
+	if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
+		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
 
 	/*
 	 * Break the null-terminated expanded word strings out into

Modified: stable/8/tools/regression/lib/libc/gen/test-wordexp.c
==============================================================================
--- stable/8/tools/regression/lib/libc/gen/test-wordexp.c	Sun Dec  6 22:01:45 2009	(r200188)
+++ stable/8/tools/regression/lib/libc/gen/test-wordexp.c	Sun Dec  6 22:14:58 2009	(r200189)
@@ -32,17 +32,36 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include <sys/wait.h>
+
 #include <assert.h>
+#include <errno.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <wordexp.h>
 
+static void
+chld_handler(int x)
+{
+	int status, serrno;
+
+	(void)x;
+	serrno = errno;
+	while (waitpid(-1, &status, WNOHANG) > 0)
+		;
+	errno = serrno;
+}
+
 int
 main(int argc, char *argv[])
 {
+	struct sigaction sa;
 	wordexp_t we;
 	int r;
+	int i;
+	char longdata[6 * 10000 + 1];
 
 	/* Test that the macros are there. */
 	(void)(WRDE_APPEND + WRDE_DOOFFS + WRDE_NOCMD + WRDE_REUSE +
@@ -59,6 +78,15 @@ main(int argc, char *argv[])
 	assert(we.we_wordv[2] == NULL);
 	wordfree(&we);
 
+	/* Long output. */
+	for (i = 0; i < 10000; i++)
+		snprintf(longdata + 6 * i, 7, "%05d ", i);
+	r = wordexp(longdata, &we, 0);
+	assert(r == 0);
+	assert(we.we_wordc == 10000);
+	assert(we.we_wordv[10000] == NULL);
+	wordfree(&we);
+
 	/* WRDE_DOOFFS */
 	we.we_offs = 3;
 	r = wordexp("hello world", &we, WRDE_DOOFFS);
@@ -167,6 +195,20 @@ main(int argc, char *argv[])
 	r = wordexp("test } test", &we, 0);
 	assert(r == WRDE_BADCHAR);
 
+	/* With a SIGCHLD handler that reaps all zombies. */
+	sa.sa_flags = 0;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_handler = chld_handler;
+	r = sigaction(SIGCHLD, &sa, NULL);
+	assert(r == 0);
+	r = wordexp("hello world", &we, 0);
+	assert(r == 0);
+	assert(we.we_wordc == 2);
+	assert(strcmp(we.we_wordv[0], "hello") == 0);
+	assert(strcmp(we.we_wordv[1], "world") == 0);
+	assert(we.we_wordv[2] == NULL);
+	wordfree(&we);
+
 	printf("PASS wordexp()\n");
 	printf("PASS wordfree()\n");
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200912062214.nB6MEwRg033175>