xref: /onnv-gate/usr/src/lib/libc/port/stdio/popen.c (revision 7635:507ea00a49bb)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51846Scraigm  * Common Development and Distribution License (the "License").
61846Scraigm  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
211846Scraigm 
220Sstevel@tonic-gate /*
235891Sraf  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*	Copyright (c) 1988 AT&T	*/
280Sstevel@tonic-gate /*	  All Rights Reserved  	*/
290Sstevel@tonic-gate 
306812Sraf #pragma weak _pclose = pclose
316812Sraf #pragma weak _popen = popen
320Sstevel@tonic-gate 
336812Sraf #include "lint.h"
340Sstevel@tonic-gate #include "mtlib.h"
350Sstevel@tonic-gate #include "file64.h"
360Sstevel@tonic-gate #include <sys/types.h>
370Sstevel@tonic-gate #include <stdio.h>
380Sstevel@tonic-gate #include <stdlib.h>
390Sstevel@tonic-gate #include <wait.h>
400Sstevel@tonic-gate #include <signal.h>
410Sstevel@tonic-gate #include <fcntl.h>
420Sstevel@tonic-gate #include <unistd.h>
430Sstevel@tonic-gate #include <errno.h>
440Sstevel@tonic-gate #include <thread.h>
453235Sraf #include <pthread.h>
460Sstevel@tonic-gate #include <synch.h>
470Sstevel@tonic-gate #include <spawn.h>
480Sstevel@tonic-gate #include "stdiom.h"
490Sstevel@tonic-gate #include "mse.h"
500Sstevel@tonic-gate #include "libc.h"
510Sstevel@tonic-gate 
520Sstevel@tonic-gate #define	tst(a, b) (*mode == 'r'? (b) : (a))
530Sstevel@tonic-gate #define	RDR	0
540Sstevel@tonic-gate #define	WTR	1
550Sstevel@tonic-gate 
560Sstevel@tonic-gate extern	int __xpg4;	/* defined in _xpg4.c; 0 if not xpg4-compiled program */
576879Sraf extern const char **_environ;
580Sstevel@tonic-gate 
590Sstevel@tonic-gate static mutex_t popen_lock = DEFAULTMUTEX;
600Sstevel@tonic-gate 
610Sstevel@tonic-gate typedef struct node {
620Sstevel@tonic-gate 	pid_t	pid;
630Sstevel@tonic-gate 	int	fd;
640Sstevel@tonic-gate 	struct	node	*next;
650Sstevel@tonic-gate } node_t;
660Sstevel@tonic-gate 
670Sstevel@tonic-gate static	node_t  *head = NULL;
683235Sraf static	void	_insert_nolock(pid_t, int, node_t *);
690Sstevel@tonic-gate 
703235Sraf /*
713235Sraf  * Cancellation cleanup handler.
723235Sraf  * If we were cancelled in waitpid(), create a daemon thread to
733235Sraf  * reap our abandoned child.  No other thread can do this for us.
743235Sraf  */
753235Sraf static void
cleanup(void * arg)763235Sraf cleanup(void *arg)
773235Sraf {
783235Sraf 	extern const sigset_t maskset;
793235Sraf 	extern void *reapchild(void *);		/* see port/stdio/system.c */
803235Sraf 
815891Sraf 	/*
825891Sraf 	 * We have been cancelled.  There is no need to restore
835891Sraf 	 * the original sigmask after blocking all signals because
845891Sraf 	 * pthread_exit() will block all signals while we exit.
855891Sraf 	 */
863235Sraf 	(void) thr_sigsetmask(SIG_SETMASK, &maskset, NULL);
873235Sraf 	(void) thr_create(NULL, 0, reapchild, arg, THR_DAEMON, NULL);
883235Sraf }
890Sstevel@tonic-gate 
900Sstevel@tonic-gate FILE *
popen(const char * cmd,const char * mode)910Sstevel@tonic-gate popen(const char *cmd, const char *mode)
920Sstevel@tonic-gate {
930Sstevel@tonic-gate 	int	p[2];
940Sstevel@tonic-gate 	pid_t	pid;
953235Sraf 	int	myside;
963235Sraf 	int	yourside;
973235Sraf 	int	fd;
980Sstevel@tonic-gate 	const char *shpath;
990Sstevel@tonic-gate 	FILE	*iop;
1000Sstevel@tonic-gate 	int	stdio;
1010Sstevel@tonic-gate 	node_t	*curr;
1020Sstevel@tonic-gate 	char	*argvec[4];
1033235Sraf 	node_t	*node;
1043235Sraf 	posix_spawnattr_t attr;
1050Sstevel@tonic-gate 	posix_spawn_file_actions_t fact;
1060Sstevel@tonic-gate 	int	error;
1070Sstevel@tonic-gate 	static const char *sun_path = "/bin/sh";
1080Sstevel@tonic-gate 	static const char *xpg4_path = "/usr/xpg4/bin/sh";
1090Sstevel@tonic-gate 	static const char *shell = "sh";
1100Sstevel@tonic-gate 	static const char *sh_flg = "-c";
1110Sstevel@tonic-gate 
1123235Sraf 	if ((node = lmalloc(sizeof (node_t))) == NULL)
1133235Sraf 		return (NULL);
1143235Sraf 	if ((error = posix_spawnattr_init(&attr)) != 0) {
1153235Sraf 		lfree(node, sizeof (node_t));
1163235Sraf 		errno = error;
1170Sstevel@tonic-gate 		return (NULL);
1183235Sraf 	}
1193235Sraf 	if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
1203235Sraf 		lfree(node, sizeof (node_t));
1213235Sraf 		(void) posix_spawnattr_destroy(&attr);
1223235Sraf 		errno = error;
1233235Sraf 		return (NULL);
1243235Sraf 	}
1253235Sraf 	if (pipe(p) < 0) {
1263235Sraf 		error = errno;
1273235Sraf 		lfree(node, sizeof (node_t));
1283235Sraf 		(void) posix_spawnattr_destroy(&attr);
1293235Sraf 		(void) posix_spawn_file_actions_destroy(&fact);
1303235Sraf 		errno = error;
1313235Sraf 		return (NULL);
1323235Sraf 	}
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate 	shpath = __xpg4? xpg4_path : sun_path;
1350Sstevel@tonic-gate 	if (access(shpath, X_OK))	/* XPG4 Requirement: */
1360Sstevel@tonic-gate 		shpath = "";		/* force child to fail immediately */
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate 	myside = tst(p[WTR], p[RDR]);
1390Sstevel@tonic-gate 	yourside = tst(p[RDR], p[WTR]);
1400Sstevel@tonic-gate 	/* myside and yourside reverse roles in child */
1410Sstevel@tonic-gate 	stdio = tst(0, 1);
1420Sstevel@tonic-gate 
1431846Scraigm 	/* This will fail more quickly if we run out of fds */
1441846Scraigm 	if ((iop = fdopen(myside, mode)) == NULL) {
1453235Sraf 		error = errno;
1463235Sraf 		lfree(node, sizeof (node_t));
1473235Sraf 		(void) posix_spawnattr_destroy(&attr);
1483235Sraf 		(void) posix_spawn_file_actions_destroy(&fact);
1491846Scraigm 		(void) close(yourside);
1501846Scraigm 		(void) close(myside);
1513235Sraf 		errno = error;
1521846Scraigm 		return (NULL);
1531846Scraigm 	}
1541846Scraigm 
1550Sstevel@tonic-gate 	lmutex_lock(&popen_lock);
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate 	/* in the child, close all pipes from other popen's */
1583235Sraf 	for (curr = head; curr != NULL && error == 0; curr = curr->next) {
1593235Sraf 		/*
1603235Sraf 		 * These conditions may apply if a previous iob returned
1613235Sraf 		 * by popen() was closed with fclose() rather than pclose(),
1625891Sraf 		 * or if close(fileno(iob)) was called.  Don't let these
1635891Sraf 		 * programming errors cause us to malfunction here.
1643235Sraf 		 */
1653235Sraf 		if ((fd = curr->fd) != myside && fd != yourside &&
1663235Sraf 		    fcntl(fd, F_GETFD) >= 0)
1673235Sraf 			error = posix_spawn_file_actions_addclose(&fact, fd);
1680Sstevel@tonic-gate 	}
1690Sstevel@tonic-gate 	if (error == 0)
1700Sstevel@tonic-gate 		error =  posix_spawn_file_actions_addclose(&fact, myside);
1710Sstevel@tonic-gate 	if (yourside != stdio) {
1720Sstevel@tonic-gate 		if (error == 0)
1730Sstevel@tonic-gate 			error = posix_spawn_file_actions_adddup2(&fact,
1745891Sraf 			    yourside, stdio);
1750Sstevel@tonic-gate 		if (error == 0)
1760Sstevel@tonic-gate 			error = posix_spawn_file_actions_addclose(&fact,
1775891Sraf 			    yourside);
1780Sstevel@tonic-gate 	}
179*7635SRoger.Faulkner@Sun.COM 	/*
180*7635SRoger.Faulkner@Sun.COM 	 * See the comments in port/stdio/system.c for why these
181*7635SRoger.Faulkner@Sun.COM 	 * non-portable posix_spawn() attributes are being used.
182*7635SRoger.Faulkner@Sun.COM 	 */
1833235Sraf 	if (error == 0)
1843235Sraf 		error = posix_spawnattr_setflags(&attr,
185*7635SRoger.Faulkner@Sun.COM 		    POSIX_SPAWN_NOSIGCHLD_NP |
186*7635SRoger.Faulkner@Sun.COM 		    POSIX_SPAWN_WAITPID_NP |
187*7635SRoger.Faulkner@Sun.COM 		    POSIX_SPAWN_NOEXECERR_NP);
1880Sstevel@tonic-gate 	if (error) {
1890Sstevel@tonic-gate 		lmutex_unlock(&popen_lock);
1903235Sraf 		lfree(node, sizeof (node_t));
1913235Sraf 		(void) posix_spawnattr_destroy(&attr);
1920Sstevel@tonic-gate 		(void) posix_spawn_file_actions_destroy(&fact);
1931846Scraigm 		(void) fclose(iop);
1940Sstevel@tonic-gate 		(void) close(yourside);
1950Sstevel@tonic-gate 		errno = error;
1960Sstevel@tonic-gate 		return (NULL);
1970Sstevel@tonic-gate 	}
1980Sstevel@tonic-gate 	argvec[0] = (char *)shell;
1990Sstevel@tonic-gate 	argvec[1] = (char *)sh_flg;
2000Sstevel@tonic-gate 	argvec[2] = (char *)cmd;
2010Sstevel@tonic-gate 	argvec[3] = NULL;
2023235Sraf 	error = posix_spawn(&pid, shpath, &fact, &attr,
2036879Sraf 	    (char *const *)argvec, (char *const *)_environ);
2043235Sraf 	(void) posix_spawnattr_destroy(&attr);
2050Sstevel@tonic-gate 	(void) posix_spawn_file_actions_destroy(&fact);
2060Sstevel@tonic-gate 	(void) close(yourside);
2073235Sraf 	if (error) {
2080Sstevel@tonic-gate 		lmutex_unlock(&popen_lock);
2093235Sraf 		lfree(node, sizeof (node_t));
2101846Scraigm 		(void) fclose(iop);
2113235Sraf 		errno = error;
2120Sstevel@tonic-gate 		return (NULL);
2130Sstevel@tonic-gate 	}
2143235Sraf 	_insert_nolock(pid, myside, node);
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate 	lmutex_unlock(&popen_lock);
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	_SET_ORIENTATION_BYTE(iop);
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	return (iop);
2210Sstevel@tonic-gate }
2220Sstevel@tonic-gate 
2235891Sraf /*
2245891Sraf  * pclose() is a cancellation point.
2255891Sraf  */
2260Sstevel@tonic-gate int
pclose(FILE * ptr)2270Sstevel@tonic-gate pclose(FILE *ptr)
2280Sstevel@tonic-gate {
2290Sstevel@tonic-gate 	pid_t	pid;
2300Sstevel@tonic-gate 	int status;
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 	pid = _delete(fileno(ptr));
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	/* mark this pipe closed */
2350Sstevel@tonic-gate 	(void) fclose(ptr);
2360Sstevel@tonic-gate 
2373235Sraf 	if (pid <= 0) {
2383235Sraf 		errno = ECHILD;
2390Sstevel@tonic-gate 		return (-1);
2403235Sraf 	}
2410Sstevel@tonic-gate 
2423235Sraf 	/*
2435891Sraf 	 * waitpid() is a cancellation point.
2445891Sraf 	 * This causes pclose() to be a cancellation point.
2453235Sraf 	 *
2463235Sraf 	 * If we have already been cancelled (pclose() was called from
2473235Sraf 	 * a cancellation cleanup handler), attempt to reap the process
2483235Sraf 	 * w/o waiting, and if that fails just call cleanup(pid).
2493235Sraf 	 */
2503235Sraf 
2513235Sraf 	if (_thrp_cancelled()) {
2525891Sraf 		/* waitpid(..., WNOHANG) is not a cancellation point */
2533235Sraf 		if (waitpid(pid, &status, WNOHANG) == pid)
2543235Sraf 			return (status);
2553235Sraf 		cleanup((void *)(uintptr_t)pid);
2563235Sraf 		errno = ECHILD;
2573235Sraf 		return (-1);
2583235Sraf 	}
2593235Sraf 
2603235Sraf 	pthread_cleanup_push(cleanup, (void *)(uintptr_t)pid);
2615891Sraf 	while (waitpid(pid, &status, 0) < 0) {
2620Sstevel@tonic-gate 		if (errno != EINTR) {
2630Sstevel@tonic-gate 			status = -1;
2640Sstevel@tonic-gate 			break;
2650Sstevel@tonic-gate 		}
2660Sstevel@tonic-gate 	}
2673235Sraf 	pthread_cleanup_pop(0);
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 	return (status);
2700Sstevel@tonic-gate }
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 
2733235Sraf static void
_insert_nolock(pid_t pid,int fd,node_t * new)2743235Sraf _insert_nolock(pid_t pid, int fd, node_t *new)
2750Sstevel@tonic-gate {
2760Sstevel@tonic-gate 	node_t	*prev;
2770Sstevel@tonic-gate 	node_t	*curr;
2780Sstevel@tonic-gate 
2793235Sraf 	for (prev = curr = head; curr != NULL; curr = curr->next) {
2803235Sraf 		/*
2813235Sraf 		 * curr->fd can equal fd if a previous iob returned by
2823235Sraf 		 * popen() was closed with fclose() rather than pclose(),
2835891Sraf 		 * or if close(fileno(iob)) was called.  Don't let these
2845891Sraf 		 * programming errors cause us to malfunction here.
2853235Sraf 		 */
2863235Sraf 		if (curr->fd == fd) {
2875891Sraf 			/* make a lame attempt to reap the forgotten child */
2883235Sraf 			(void) waitpid(curr->pid, NULL, WNOHANG);
2893235Sraf 			curr->pid = pid;
2903235Sraf 			lfree(new, sizeof (node_t));
2913235Sraf 			return;
2923235Sraf 		}
2930Sstevel@tonic-gate 		prev = curr;
2943235Sraf 	}
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 	new->pid = pid;
2970Sstevel@tonic-gate 	new->fd = fd;
2980Sstevel@tonic-gate 	new->next = NULL;
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	if (head == NULL)
3010Sstevel@tonic-gate 		head = new;
3020Sstevel@tonic-gate 	else
3030Sstevel@tonic-gate 		prev->next = new;
3040Sstevel@tonic-gate }
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate /*
3070Sstevel@tonic-gate  * _insert() and _delete() are used by p2open() in libgen.
3080Sstevel@tonic-gate  */
3090Sstevel@tonic-gate int
_insert(pid_t pid,int fd)3100Sstevel@tonic-gate _insert(pid_t pid, int fd)
3110Sstevel@tonic-gate {
3123235Sraf 	node_t *node;
3133235Sraf 
3143235Sraf 	if ((node = lmalloc(sizeof (node_t))) == NULL)
3153235Sraf 		return (-1);
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 	lmutex_lock(&popen_lock);
3183235Sraf 	_insert_nolock(pid, fd, node);
3190Sstevel@tonic-gate 	lmutex_unlock(&popen_lock);
3200Sstevel@tonic-gate 
3213235Sraf 	return (0);
3220Sstevel@tonic-gate }
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate pid_t
_delete(int fd)3260Sstevel@tonic-gate _delete(int fd)
3270Sstevel@tonic-gate {
3280Sstevel@tonic-gate 	node_t	*prev;
3290Sstevel@tonic-gate 	node_t	*curr;
3300Sstevel@tonic-gate 	pid_t	pid;
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	lmutex_lock(&popen_lock);
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	for (prev = curr = head; curr != NULL; curr = curr->next) {
3350Sstevel@tonic-gate 		if (curr->fd == fd) {
3360Sstevel@tonic-gate 			if (curr == head)
3370Sstevel@tonic-gate 				head = curr->next;
3380Sstevel@tonic-gate 			else
3390Sstevel@tonic-gate 				prev->next = curr->next;
3403235Sraf 			lmutex_unlock(&popen_lock);
3410Sstevel@tonic-gate 			pid = curr->pid;
3420Sstevel@tonic-gate 			lfree(curr, sizeof (node_t));
3430Sstevel@tonic-gate 			return (pid);
3440Sstevel@tonic-gate 		}
3450Sstevel@tonic-gate 		prev = curr;
3460Sstevel@tonic-gate 	}
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	lmutex_unlock(&popen_lock);
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	return (-1);
3510Sstevel@tonic-gate }
352