From 462453d2d0c563559a4caf186db76954e563bd1a Mon Sep 17 00:00:00 2001 From: Matthew Ahrens Date: Tue, 18 Oct 2011 12:21:34 -0700 Subject: [PATCH] 1633 implement posix_spawn_pipe_np() Reviewed by: Adam Leventhal Reviewed by: Eric Schrock Reviewed by: Dan McDonald Reviewed by: Gordon Ross Approved by: Eric Schrock --- usr/src/head/spawn.h | 13 ++ usr/src/lib/libc/port/mapfile-vers | 7 + usr/src/lib/libc/port/stdio/popen.c | 99 ++++++------- usr/src/lib/libc/port/threads/spawn.c | 70 +++++++++ usr/src/man/man3c/Makefile | 1 + usr/src/man/man3c/posix_spawn.3c | 5 +- usr/src/man/man3c/posix_spawn_pipe_np.3c | 133 ++++++++++++++++++ usr/src/man/man3lib/libc.3lib | 8 +- .../pkg/manifests/system-library.man3c.inc | 1 + 9 files changed, 277 insertions(+), 60 deletions(-) create mode 100644 usr/src/man/man3c/posix_spawn_pipe_np.3c diff --git a/usr/src/head/spawn.h b/usr/src/head/spawn.h index 865e9b30a169..a577c92009a7 100644 --- a/usr/src/head/spawn.h +++ b/usr/src/head/spawn.h @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2011 by Delphix. All rights reserved. + */ + #ifndef _SPAWN_H #define _SPAWN_H @@ -154,6 +158,14 @@ extern int posix_spawnattr_getsigdefault( */ #if !defined(__XOPEN_OR_POSIX) || defined(__EXTENSIONS__) +extern int posix_spawn_pipe_np( + pid_t *_RESTRICT_KYWD pidp, + int *_RESTRICT_KYWD fdp, + const char *_RESTRICT_KYWD cmd, + boolean_t write, + posix_spawn_file_actions_t *_RESTRICT_KYWD fact, + posix_spawnattr_t *_RESTRICT_KYWD attr); + extern int posix_spawn_file_actions_addclosefrom_np( posix_spawn_file_actions_t *file_actions, int lowfiledes); @@ -198,6 +210,7 @@ extern int posix_spawnattr_getschedpolicy(); extern int posix_spawnattr_setsigdefault(); extern int posix_spawnattr_getsigdefault(); #if !defined(__XOPEN_OR_POSIX) || defined(__EXTENSIONS__) +extern int posix_spawn_pipe_np(); extern int posix_spawn_file_actions_addclosefrom_np(); extern int posix_spawnattr_setsigignore_np(); extern int posix_spawnattr_getsigignore_np(); diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers index 566983ac74d2..59999885a5f8 100644 --- a/usr/src/lib/libc/port/mapfile-vers +++ b/usr/src/lib/libc/port/mapfile-vers @@ -24,6 +24,8 @@ # Copyright 2010 Nexenta Systems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2011 by Delphix. All rights reserved. +# # # MAPFILE HEADER START @@ -88,6 +90,11 @@ $if _x86 && _ELF64 $add amd64 $endif +SYMBOL_VERSION ILLUMOS_0.2 { # Illumos additions + protected: + posix_spawn_pipe_np; +} ILLUMOS_0.1; + SYMBOL_VERSION ILLUMOS_0.1 { # Illumos additions protected: timegm; diff --git a/usr/src/lib/libc/port/stdio/popen.c b/usr/src/lib/libc/port/stdio/popen.c index 2a86d3614124..03365d04ef5f 100644 --- a/usr/src/lib/libc/port/stdio/popen.c +++ b/usr/src/lib/libc/port/stdio/popen.c @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2011 by Delphix. All rights reserved. + */ + /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ @@ -50,13 +54,6 @@ #include "mse.h" #include "libc.h" -#define tst(a, b) (*mode == 'r'? (b) : (a)) -#define RDR 0 -#define WTR 1 - -extern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */ -extern const char **_environ; - static mutex_t popen_lock = DEFAULTMUTEX; typedef struct node { @@ -91,22 +88,15 @@ cleanup(void *arg) FILE * popen(const char *cmd, const char *mode) { - int p[2]; pid_t pid; - int myside; - int yourside; - int fd; + int myfd, fd; const char *shpath = _PATH_BSHELL; FILE *iop; - int stdio; node_t *curr; - char *argvec[4]; node_t *node; - posix_spawnattr_t attr; posix_spawn_file_actions_t fact; + posix_spawnattr_t attr; int error; - static const char *shell = "sh"; - static const char *sh_flg = "-c"; if ((node = lmalloc(sizeof (node_t))) == NULL) return (NULL); @@ -121,7 +111,19 @@ popen(const char *cmd, const char *mode) errno = error; return (NULL); } - if (pipe(p) < 0) { + + if (access(shpath, X_OK)) /* XPG4 Requirement: */ + shpath = ""; /* force child to fail immediately */ + + + /* + * fdopen() can fail (if the fd is too high or we are out of memory), + * but we don't want to have any way to fail after creating the child + * process. So we fdopen() a dummy fd (myfd), and once we get the real + * fd from posix_spawn_pipe_np(), we dup2() the real fd onto the dummy. + */ + myfd = open("/dev/null", O_RDWR); + if (myfd == -1) { error = errno; lfree(node, sizeof (node_t)); (void) posix_spawnattr_destroy(&attr); @@ -129,23 +131,13 @@ popen(const char *cmd, const char *mode) errno = error; return (NULL); } - - if (access(shpath, X_OK)) /* XPG4 Requirement: */ - shpath = ""; /* force child to fail immediately */ - - myside = tst(p[WTR], p[RDR]); - yourside = tst(p[RDR], p[WTR]); - /* myside and yourside reverse roles in child */ - stdio = tst(0, 1); - - /* This will fail more quickly if we run out of fds */ - if ((iop = fdopen(myside, mode)) == NULL) { + iop = fdopen(myfd, mode); + if (iop == NULL) { error = errno; lfree(node, sizeof (node_t)); (void) posix_spawnattr_destroy(&attr); (void) posix_spawn_file_actions_destroy(&fact); - (void) close(yourside); - (void) close(myside); + (void) close(myfd); errno = error; return (NULL); } @@ -155,64 +147,57 @@ popen(const char *cmd, const char *mode) /* in the child, close all pipes from other popen's */ for (curr = head; curr != NULL && error == 0; curr = curr->next) { /* - * These conditions may apply if a previous iob returned + * The fd may no longer be open if an iob previously returned * by popen() was closed with fclose() rather than pclose(), - * or if close(fileno(iob)) was called. Don't let these - * programming errors cause us to malfunction here. + * or if close(fileno(iob)) was called. Use fcntl() to check + * if the fd is still open, so that these programming errors + * won't cause us to malfunction here. */ - if ((fd = curr->fd) != myside && fd != yourside && - fcntl(fd, F_GETFD) >= 0) - error = posix_spawn_file_actions_addclose(&fact, fd); - } - if (error == 0) - error = posix_spawn_file_actions_addclose(&fact, myside); - if (yourside != stdio) { - if (error == 0) - error = posix_spawn_file_actions_adddup2(&fact, - yourside, stdio); - if (error == 0) + if (fcntl(curr->fd, F_GETFD) >= 0) { error = posix_spawn_file_actions_addclose(&fact, - yourside); + curr->fd); + } } /* * See the comments in port/stdio/system.c for why these * non-portable posix_spawn() attributes are being used. */ - if (error == 0) + if (error == 0) { error = posix_spawnattr_setflags(&attr, POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP | POSIX_SPAWN_NOEXECERR_NP); - if (error) { + } + if (error != 0) { lmutex_unlock(&popen_lock); lfree(node, sizeof (node_t)); (void) posix_spawnattr_destroy(&attr); (void) posix_spawn_file_actions_destroy(&fact); (void) fclose(iop); - (void) close(yourside); errno = error; return (NULL); } - argvec[0] = (char *)shell; - argvec[1] = (char *)sh_flg; - argvec[2] = (char *)cmd; - argvec[3] = NULL; - error = posix_spawn(&pid, shpath, &fact, &attr, - (char *const *)argvec, (char *const *)_environ); + error = posix_spawn_pipe_np(&pid, &fd, cmd, *mode != 'r', &fact, &attr); (void) posix_spawnattr_destroy(&attr); (void) posix_spawn_file_actions_destroy(&fact); - (void) close(yourside); - if (error) { + if (error != 0) { lmutex_unlock(&popen_lock); lfree(node, sizeof (node_t)); (void) fclose(iop); errno = error; return (NULL); } - _insert_nolock(pid, myside, node); + _insert_nolock(pid, myfd, node); lmutex_unlock(&popen_lock); + /* + * myfd is the one that we fdopen()'ed; make it refer to the + * pipe to the child. + */ + (void) dup2(fd, myfd); + (void) close(fd); + _SET_ORIENTATION_BYTE(iop); return (iop); diff --git a/usr/src/lib/libc/port/threads/spawn.c b/usr/src/lib/libc/port/threads/spawn.c index 5ac0a1ab2a32..7f12a5866fd6 100644 --- a/usr/src/lib/libc/port/threads/spawn.c +++ b/usr/src/lib/libc/port/threads/spawn.c @@ -24,6 +24,10 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2011 by Delphix. All rights reserved. + */ + #include "lint.h" #include "thr_uberdata.h" #include @@ -77,6 +81,8 @@ typedef struct file_attr { extern int getdents64(int, dirent64_t *, size_t); #endif +extern const char **_environ; + /* * Support function: * Close all open file descriptors greater than or equal to lowfd. @@ -879,3 +885,67 @@ posix_spawnattr_getsigmask( *sigmask = sap->sa_sigmask; return (0); } + +/* + * Spawn a process to run "sh -c ". Return the child's pid (in + * *pidp), and a file descriptor (in *fdp) for reading or writing to the + * child process, depending on the 'write' argument. + * Return 0 on success; otherwise return an error code. + */ +int +posix_spawn_pipe_np(pid_t *pidp, int *fdp, + const char *cmd, boolean_t write, + posix_spawn_file_actions_t *fact, posix_spawnattr_t *attr) +{ + int p[2]; + int myside, yourside, stdio; + const char *shpath = _PATH_BSHELL; + const char *argvec[4] = { "sh", "-c", cmd, NULL }; + int error; + + if (pipe(p) < 0) + return (errno); + + if (access(shpath, X_OK)) /* XPG4 Requirement: */ + shpath = ""; /* force child to fail immediately */ + + if (write) { + /* + * Data is read from p[0] and written to p[1]. + * 'stdio' is the fd in the child process that should be + * connected to the pipe. + */ + myside = p[1]; + yourside = p[0]; + stdio = STDIN_FILENO; + } else { + myside = p[0]; + yourside = p[1]; + stdio = STDOUT_FILENO; + } + + error = posix_spawn_file_actions_addclose(fact, myside); + if (yourside != stdio) { + if (error == 0) { + error = posix_spawn_file_actions_adddup2(fact, + yourside, stdio); + } + if (error == 0) { + error = posix_spawn_file_actions_addclose(fact, + yourside); + } + } + + if (error) + return (error); + error = posix_spawn(pidp, shpath, fact, attr, + (char *const *)argvec, (char *const *)_environ); + (void) close(yourside); + if (error) { + (void) close(myside); + return (error); + } + + *fdp = myside; + return (0); +} diff --git a/usr/src/man/man3c/Makefile b/usr/src/man/man3c/Makefile index e81e80e5eace..4f16272ed28d 100644 --- a/usr/src/man/man3c/Makefile +++ b/usr/src/man/man3c/Makefile @@ -256,6 +256,7 @@ MANFILES = __fbufsize.3c \ posix_spawn_file_actions_addclosefrom_np.3c \ posix_spawn_file_actions_adddup2.3c \ posix_spawn_file_actions_destroy.3c \ + posix_spawn_pipe_np.3c \ posix_spawnattr_destroy.3c \ posix_spawnattr_getflags.3c \ posix_spawnattr_getpgroup.3c \ diff --git a/usr/src/man/man3c/posix_spawn.3c b/usr/src/man/man3c/posix_spawn.3c index 44407e68e945..7d9ad894c980 100644 --- a/usr/src/man/man3c/posix_spawn.3c +++ b/usr/src/man/man3c/posix_spawn.3c @@ -9,6 +9,7 @@ .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License. .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner] +.\" Copyright 2011 by Delphix. All rights reserved. .TH POSIX_SPAWN 3C "Feb 20, 2009" .SH NAME posix_spawn, posix_spawnp \- spawn a process @@ -388,7 +389,9 @@ Standard See \fBstandards\fR(5). \fBposix_spawn_file_actions_adddup2\fR(3C), \fBposix_spawn_file_actions_addopen\fR(3C), \fBposix_spawn_file_actions_destroy\fR(3C), -\fBposix_spawn_file_actions_init\fR(3C), \fBposix_spawnattr_destroy\fR(3C), +\fBposix_spawn_file_actions_init\fR(3C), +\fBposix_spawn_pipe_np\fR(3C), +\fBposix_spawnattr_destroy\fR(3C), \fBposix_spawnattr_getflags\fR(3C), \fBposix_spawnattr_getpgroup\fR(3C), \fBposix_spawnattr_getschedparam\fR(3C), \fBposix_spawnattr_getschedpolicy\fR(3C), diff --git a/usr/src/man/man3c/posix_spawn_pipe_np.3c b/usr/src/man/man3c/posix_spawn_pipe_np.3c new file mode 100644 index 000000000000..68515a5992bf --- /dev/null +++ b/usr/src/man/man3c/posix_spawn_pipe_np.3c @@ -0,0 +1,133 @@ +'\" te +.\" Copyright (c) 2001, the Institute of Electrical and Electronics Engineers, Inc. and The Open Group. All Rights Reserved. +.\" Portions Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved. +.\" Sun Microsystems, Inc. gratefully acknowledges The Open Group for permission to reproduce portions of its copyrighted documentation. Original documentation from The Open Group can be obtained online at http://www.opengroup.org/bookstore/. +.\" The Institute of Electrical and Electronics Engineers and The Open Group, have given us permission to reprint portions of their documentation. In the following statement, the phrase "this text" refers to portions of the system documentation. Portions of this text +.\" are reprinted and reproduced in electronic form in the Sun OS Reference Manual, from IEEE Std 1003.1, 2004 Edition, Standard for Information Technology -- Portable Operating System Interface (POSIX), The Open Group Base Specifications Issue 6, Copyright (C) 2001-2004 by the Institute of Electrical +.\" and Electronics Engineers, Inc and The Open Group. In the event of any discrepancy between these versions and the original IEEE and The Open Group Standard, the original IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at http://www.opengroup.org/unix/online.html. +.\" This notice shall appear on any product containing this material. +.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. +.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License. +.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner] +.\" Copyright 2011 by Delphix. All rights reserved. +.TH POSIX_SPAWN_PIPE_NP 3C "Oct 14, 2011" +.SH NAME +posix_spawn_pipe_np \- spawn a process with connected pipe +.SH SYNOPSIS +.LP +.nf +#include + +\fBint\fR \fBposix_spawn_pipe_np\fR(\fBpid_t *restrict\fR \fIpid\fR, + \fBint *restrict\fR \fIfdp\fR, + \fBconst char *restrict\fR \fIcmd\fR, + \fBboolean_t\fR \fIwrite\fR, + \fBconst posix_spawn_file_actions_t *\fR\fIfile_actions\fR, + \fBconst posix_spawnattr_t *restrict\fR \fIattr\fR); +.fi + +.SH DESCRIPTION +.sp +.LP +The \fBposix_spawn_pipe_np()\fR function creates a new process +(child process) to run \fBsh -c \fIcmd\fR\fR, and connects the new +process's standard input or output to a file descriptor. +.sp +.LP +The new process's environment (the \fIenviron\fR global variable) is +inherited from the current process. +.sp +.LP +The \fIcmd\fR argument is the string for the child shell process to execute. +.sp +.LP +If the \fIwrite\fR argument is set, the returned file descriptor will be +connected to the new process's standard input, and will be opened for +writing. Otherwise, the file descriptor will be connected to the new +process's standard output, and will be opened for reading. +.sp +.LP +If \fIfile_actions\fR is not \fINULL\fR, then the file descriptors open +in the child process are those open in the calling process as modified +by the spawn file actions object pointed to by \fIfile_actions\fR and +the \fBFD_CLOEXEC\fR flag of each remaining open file descriptor after +the spawn file actions have been processed. See \fBposix_spawn\fR(3C) +for details on the behavior of \fIfile_actions\fR. +.sp +.LP +If \fIattr\fR is not \fINULL\fR, then the signal mask, signal default or +ignore actions, and the effective user and group IDs for the child +process are changed as specified in the attributes object referenced by +\fIattr\fR. See \fBposix_spawn\fR(3C) for details on the behavior of +\fIattr\fR. +.sp +.LP +All process attributes, other than those influenced by the attributes set in +the object referenced by \fIattr\fR as specified above or by the file +descriptor manipulations specified in \fIfile_actions\fR appear in the new +process image as though \fBfork()\fR had been called to create a child process +and then a member of the \fBexec\fR family of functions had been called by the +child process to execute \fB/bin/sh\fR. +.sp +.LP +The fork handlers are not run when \fBposix_spawn_pipe_np()\fR is called. +.SH RETURN VALUES +.sp +.LP +Upon successful completion, \fBposix_spawn_pipe_np()\fR returns the +process ID of the child process to the parent process in the variable +pointed to by a non-null \fIpidp\fR argument, returns the file +descriptor which is connected to the child process in the variable +pointed to by a non-null \fIfdp\fR argument, and returns zero as the +function return value. Otherwise, no child process is created, the value +stored into the variable pointed to by a non-null \fIpidp\fR and +\fIfdp\fR is unspecified, and an error number is returned as the +function return value to indicate the error. +.SH ERRORS +.sp +.LP +See \fBposix_spawn\fB(3C). + +.SH ATTRIBUTES +.sp +.LP +See \fBattributes\fR(5) for descriptions of the following attributes: +.sp + +.sp +.TS +box; +c | c +l | l . +ATTRIBUTE TYPE ATTRIBUTE VALUE +_ +Interface Stability Committed +_ +MT-Level MT-Safe +.TE + +.SH SEE ALSO +.sp +.LP +\fBexec\fR(2), \fBfork\fR(2), \fBopen\fR(2), +\fBpopen\fR(3C), \fBposix_spawn\fR(3C), +\fBposix_spawn_file_actions_addclose\fR(3C), +\fBposix_spawn_file_actions_adddup2\fR(3C), +\fBposix_spawn_file_actions_addopen\fR(3C), +\fBposix_spawn_file_actions_destroy\fR(3C), +\fBposix_spawn_file_actions_init\fR(3C), +\fBposix_spawnattr_destroy\fR(3C), +\fBposix_spawnattr_getflags\fR(3C), \fBposix_spawnattr_getpgroup\fR(3C), +\fBposix_spawnattr_getschedparam\fR(3C), +\fBposix_spawnattr_getschedpolicy\fR(3C), +\fBposix_spawnattr_getsigdefault\fR(3C), +\fBposix_spawnattr_getsigignore_np\fR(3C), +\fBposix_spawnattr_getsigmask\fR(3C), \fBposix_spawnattr_init\fR(3C), +\fBposix_spawnattr_setflags\fR(3C), \fBposix_spawnattr_setpgroup\fR(3C), +\fBposix_spawnattr_setschedparam\fR(3C), +\fBposix_spawnattr_setschedpolicy\fR(3C), +\fBposix_spawnattr_setsigdefault\fR(3C), +\fBposix_spawnattr_setsigignore_np\fR(3C), +\fBposix_spawnattr_setsigmask\fR(3C), +\fBsystem\fR(3C), \fBwait\fR(3C), +\fBattributes\fR(5), \fBstandards\fR(5) diff --git a/usr/src/man/man3lib/libc.3lib b/usr/src/man/man3lib/libc.3lib index 6814320f2dc4..036786751ac7 100644 --- a/usr/src/man/man3lib/libc.3lib +++ b/usr/src/man/man3lib/libc.3lib @@ -3,6 +3,7 @@ .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. .\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with .\" the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner] +.\" Copyright 2011 by Delphix. All rights reserved. .TH LIBC 3LIB "Sep 15, 2009" .SH NAME libc \- C library @@ -425,8 +426,11 @@ l l . \fBposix_memalign\fR \fBposix_openpt\fR \fBposix_spawn\fR \fBposix_spawn_file_actions_addclose\fR \fBposix_spawn_file_actions_addclosefrom_np\fR \fBposix_spawn_file_actions_adddup2\fR -\fBposix_spawn_file_actions_addopen\fR \fBposix_spawn_file_actions_destroy\fR -\fBposix_spawn_file_actions_init\fR \fBposix_spawnattr_destroy\fR +\fBposix_spawn_file_actions_addopen\fR +\fBposix_spawn_file_actions_destroy\fR +\fBposix_spawn_file_actions_init\fR +\fBposix_spawn_pipe_np\fR +\fBposix_spawnattr_destroy\fR \fBposix_spawnattr_getflags\fR \fBposix_spawnattr_getpgroup\fR \fBposix_spawnattr_getschedparam\fR \fBposix_spawnattr_getschedpolicy\fR \fBposix_spawnattr_getsigdefault\fR \fBposix_spawnattr_getsigignore_np\fR diff --git a/usr/src/pkg/manifests/system-library.man3c.inc b/usr/src/pkg/manifests/system-library.man3c.inc index e9a45d273b3e..57c67eef9f2a 100644 --- a/usr/src/pkg/manifests/system-library.man3c.inc +++ b/usr/src/pkg/manifests/system-library.man3c.inc @@ -618,6 +618,7 @@ file path=usr/share/man/man3c/posix_spawn_file_actions_adddup2.3c file path=usr/share/man/man3c/posix_spawn_file_actions_addopen.3c file path=usr/share/man/man3c/posix_spawn_file_actions_destroy.3c file path=usr/share/man/man3c/posix_spawn_file_actions_init.3c +file path=usr/share/man/man3c/posix_spawn_pipe_np.3c file path=usr/share/man/man3c/posix_spawnattr_destroy.3c file path=usr/share/man/man3c/posix_spawnattr_getflags.3c file path=usr/share/man/man3c/posix_spawnattr_getpgroup.3c