xref: /illumos-gate/usr/src/lib/libc/port/stdio/popen.c (revision a5f69788de7ac07553de47f7fec8c05a9a94c105)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*a5f69788Scraigm  * Common Development and Distribution License (the "License").
6*a5f69788Scraigm  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21*a5f69788Scraigm 
227c478bd9Sstevel@tonic-gate /*
23*a5f69788Scraigm  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*	Copyright (c) 1988 AT&T	*/
287c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
297c478bd9Sstevel@tonic-gate 
30*a5f69788Scraigm #pragma ident	"%Z%%M%	%I%	%E% SMI"
31*a5f69788Scraigm 
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #pragma weak pclose = _pclose
347c478bd9Sstevel@tonic-gate #pragma weak popen = _popen
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate #include "synonyms.h"
377c478bd9Sstevel@tonic-gate #include "mtlib.h"
387c478bd9Sstevel@tonic-gate #include "file64.h"
397c478bd9Sstevel@tonic-gate #include <sys/types.h>
407c478bd9Sstevel@tonic-gate #include <stdio.h>
417c478bd9Sstevel@tonic-gate #include <stdlib.h>
427c478bd9Sstevel@tonic-gate #include <wait.h>
437c478bd9Sstevel@tonic-gate #include <signal.h>
447c478bd9Sstevel@tonic-gate #include <fcntl.h>
457c478bd9Sstevel@tonic-gate #include <unistd.h>
467c478bd9Sstevel@tonic-gate #include <errno.h>
477c478bd9Sstevel@tonic-gate #include <thread.h>
487c478bd9Sstevel@tonic-gate #include <synch.h>
497c478bd9Sstevel@tonic-gate #include <spawn.h>
507c478bd9Sstevel@tonic-gate #include "stdiom.h"
517c478bd9Sstevel@tonic-gate #include "mse.h"
527c478bd9Sstevel@tonic-gate #include "libc.h"
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate #define	tst(a, b) (*mode == 'r'? (b) : (a))
557c478bd9Sstevel@tonic-gate #define	RDR	0
567c478bd9Sstevel@tonic-gate #define	WTR	1
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate static int _insert_nolock(pid_t, int);
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate extern	int __xpg4;	/* defined in _xpg4.c; 0 if not xpg4-compiled program */
617c478bd9Sstevel@tonic-gate extern const char **environ;
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate static mutex_t popen_lock = DEFAULTMUTEX;
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate typedef struct node {
667c478bd9Sstevel@tonic-gate 	pid_t	pid;
677c478bd9Sstevel@tonic-gate 	int	fd;
687c478bd9Sstevel@tonic-gate 	struct	node	*next;
697c478bd9Sstevel@tonic-gate } node_t;
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate static	node_t  *head = NULL;
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate FILE *
757c478bd9Sstevel@tonic-gate popen(const char *cmd, const char *mode)
767c478bd9Sstevel@tonic-gate {
777c478bd9Sstevel@tonic-gate 	int	p[2];
787c478bd9Sstevel@tonic-gate 	pid_t	pid;
797c478bd9Sstevel@tonic-gate 	int	myside, yourside;
807c478bd9Sstevel@tonic-gate 	const char *shpath;
817c478bd9Sstevel@tonic-gate 	FILE	*iop;
827c478bd9Sstevel@tonic-gate 	int	stdio;
837c478bd9Sstevel@tonic-gate 	node_t	*curr;
847c478bd9Sstevel@tonic-gate 	char	*argvec[4];
857c478bd9Sstevel@tonic-gate 	posix_spawn_file_actions_t fact;
867c478bd9Sstevel@tonic-gate 	int	error;
877c478bd9Sstevel@tonic-gate 	static const char *sun_path = "/bin/sh";
887c478bd9Sstevel@tonic-gate 	static const char *xpg4_path = "/usr/xpg4/bin/sh";
897c478bd9Sstevel@tonic-gate 	static const char *shell = "sh";
907c478bd9Sstevel@tonic-gate 	static const char *sh_flg = "-c";
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	if (pipe(p) < 0)
937c478bd9Sstevel@tonic-gate 		return (NULL);
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate 	shpath = __xpg4? xpg4_path : sun_path;
967c478bd9Sstevel@tonic-gate 	if (access(shpath, X_OK))	/* XPG4 Requirement: */
977c478bd9Sstevel@tonic-gate 		shpath = "";		/* force child to fail immediately */
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate 	myside = tst(p[WTR], p[RDR]);
1007c478bd9Sstevel@tonic-gate 	yourside = tst(p[RDR], p[WTR]);
1017c478bd9Sstevel@tonic-gate 	/* myside and yourside reverse roles in child */
1027c478bd9Sstevel@tonic-gate 	stdio = tst(0, 1);
1037c478bd9Sstevel@tonic-gate 
104*a5f69788Scraigm 	/* This will fail more quickly if we run out of fds */
105*a5f69788Scraigm 	if ((iop = fdopen(myside, mode)) == NULL) {
106*a5f69788Scraigm 		(void) close(yourside);
107*a5f69788Scraigm 		(void) close(myside);
108*a5f69788Scraigm 		return (NULL);
109*a5f69788Scraigm 	}
110*a5f69788Scraigm 
1117c478bd9Sstevel@tonic-gate 	lmutex_lock(&popen_lock);
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate 	/* in the child, close all pipes from other popen's */
1147c478bd9Sstevel@tonic-gate 	if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
1157c478bd9Sstevel@tonic-gate 		lmutex_unlock(&popen_lock);
116*a5f69788Scraigm 		(void) fclose(iop);
1177c478bd9Sstevel@tonic-gate 		(void) close(yourside);
1187c478bd9Sstevel@tonic-gate 		errno = error;
1197c478bd9Sstevel@tonic-gate 		return (NULL);
1207c478bd9Sstevel@tonic-gate 	}
1217c478bd9Sstevel@tonic-gate 	for (curr = head; curr != NULL && error == 0; curr = curr->next)
1227c478bd9Sstevel@tonic-gate 		error = posix_spawn_file_actions_addclose(&fact, curr->fd);
1237c478bd9Sstevel@tonic-gate 	if (error == 0)
1247c478bd9Sstevel@tonic-gate 		error =  posix_spawn_file_actions_addclose(&fact, myside);
1257c478bd9Sstevel@tonic-gate 	if (yourside != stdio) {
1267c478bd9Sstevel@tonic-gate 		if (error == 0)
1277c478bd9Sstevel@tonic-gate 			error = posix_spawn_file_actions_adddup2(&fact,
1287c478bd9Sstevel@tonic-gate 				yourside, stdio);
1297c478bd9Sstevel@tonic-gate 		if (error == 0)
1307c478bd9Sstevel@tonic-gate 			error = posix_spawn_file_actions_addclose(&fact,
1317c478bd9Sstevel@tonic-gate 				yourside);
1327c478bd9Sstevel@tonic-gate 	}
1337c478bd9Sstevel@tonic-gate 	if (error) {
1347c478bd9Sstevel@tonic-gate 		lmutex_unlock(&popen_lock);
1357c478bd9Sstevel@tonic-gate 		(void) posix_spawn_file_actions_destroy(&fact);
136*a5f69788Scraigm 		(void) fclose(iop);
1377c478bd9Sstevel@tonic-gate 		(void) close(yourside);
1387c478bd9Sstevel@tonic-gate 		errno = error;
1397c478bd9Sstevel@tonic-gate 		return (NULL);
1407c478bd9Sstevel@tonic-gate 	}
1417c478bd9Sstevel@tonic-gate 	argvec[0] = (char *)shell;
1427c478bd9Sstevel@tonic-gate 	argvec[1] = (char *)sh_flg;
1437c478bd9Sstevel@tonic-gate 	argvec[2] = (char *)cmd;
1447c478bd9Sstevel@tonic-gate 	argvec[3] = NULL;
1457c478bd9Sstevel@tonic-gate 	error = posix_spawn(&pid, shpath, &fact, NULL,
1467c478bd9Sstevel@tonic-gate 		(char *const *)argvec, (char *const *)environ);
1477c478bd9Sstevel@tonic-gate 	(void) posix_spawn_file_actions_destroy(&fact);
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate 	(void) close(yourside);
1507c478bd9Sstevel@tonic-gate 	if ((errno = error) != 0 || _insert_nolock(pid, myside) == -1) {
1517c478bd9Sstevel@tonic-gate 		lmutex_unlock(&popen_lock);
152*a5f69788Scraigm 		(void) fclose(iop);
1537c478bd9Sstevel@tonic-gate 		return (NULL);
1547c478bd9Sstevel@tonic-gate 	}
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	lmutex_unlock(&popen_lock);
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	_SET_ORIENTATION_BYTE(iop);
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate 	return (iop);
1617c478bd9Sstevel@tonic-gate }
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate int
1647c478bd9Sstevel@tonic-gate pclose(FILE *ptr)
1657c478bd9Sstevel@tonic-gate {
1667c478bd9Sstevel@tonic-gate 	pid_t	pid;
1677c478bd9Sstevel@tonic-gate 	int status;
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 	pid = _delete(fileno(ptr));
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate 	/* mark this pipe closed */
1727c478bd9Sstevel@tonic-gate 	(void) fclose(ptr);
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	if (pid == -1)
1757c478bd9Sstevel@tonic-gate 		return (-1);
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 	while (waitpid(pid, &status, 0) < 0) {
1787c478bd9Sstevel@tonic-gate 		/* If waitpid fails with EINTR, restart the waitpid call */
1797c478bd9Sstevel@tonic-gate 		if (errno != EINTR) {
1807c478bd9Sstevel@tonic-gate 			status = -1;
1817c478bd9Sstevel@tonic-gate 			break;
1827c478bd9Sstevel@tonic-gate 		}
1837c478bd9Sstevel@tonic-gate 	}
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 	return (status);
1867c478bd9Sstevel@tonic-gate }
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate static int
1907c478bd9Sstevel@tonic-gate _insert_nolock(pid_t pid, int fd)
1917c478bd9Sstevel@tonic-gate {
1927c478bd9Sstevel@tonic-gate 	node_t	*prev;
1937c478bd9Sstevel@tonic-gate 	node_t	*curr;
1947c478bd9Sstevel@tonic-gate 	node_t	*new;
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	for (prev = curr = head; curr != NULL; curr = curr->next)
1977c478bd9Sstevel@tonic-gate 		prev = curr;
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	if ((new = lmalloc(sizeof (node_t))) == NULL)
2007c478bd9Sstevel@tonic-gate 		return (-1);
2017c478bd9Sstevel@tonic-gate 
2027c478bd9Sstevel@tonic-gate 	new->pid = pid;
2037c478bd9Sstevel@tonic-gate 	new->fd = fd;
2047c478bd9Sstevel@tonic-gate 	new->next = NULL;
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	if (head == NULL)
2077c478bd9Sstevel@tonic-gate 		head = new;
2087c478bd9Sstevel@tonic-gate 	else
2097c478bd9Sstevel@tonic-gate 		prev->next = new;
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 	return (0);
2127c478bd9Sstevel@tonic-gate }
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate /*
2157c478bd9Sstevel@tonic-gate  * _insert() and _delete() are used by p2open() in libgen.
2167c478bd9Sstevel@tonic-gate  */
2177c478bd9Sstevel@tonic-gate int
2187c478bd9Sstevel@tonic-gate _insert(pid_t pid, int fd)
2197c478bd9Sstevel@tonic-gate {
2207c478bd9Sstevel@tonic-gate 	int rc;
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	lmutex_lock(&popen_lock);
2237c478bd9Sstevel@tonic-gate 	rc = _insert_nolock(pid, fd);
2247c478bd9Sstevel@tonic-gate 	lmutex_unlock(&popen_lock);
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate 	return (rc);
2277c478bd9Sstevel@tonic-gate }
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate pid_t
2317c478bd9Sstevel@tonic-gate _delete(int fd)
2327c478bd9Sstevel@tonic-gate {
2337c478bd9Sstevel@tonic-gate 	node_t	*prev;
2347c478bd9Sstevel@tonic-gate 	node_t	*curr;
2357c478bd9Sstevel@tonic-gate 	pid_t	pid;
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate 	lmutex_lock(&popen_lock);
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate 	for (prev = curr = head; curr != NULL; curr = curr->next) {
2407c478bd9Sstevel@tonic-gate 		if (curr->fd == fd) {
2417c478bd9Sstevel@tonic-gate 			if (curr == head)
2427c478bd9Sstevel@tonic-gate 				head = curr->next;
2437c478bd9Sstevel@tonic-gate 			else
2447c478bd9Sstevel@tonic-gate 				prev->next = curr->next;
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 			pid = curr->pid;
2477c478bd9Sstevel@tonic-gate 			lfree(curr, sizeof (node_t));
2487c478bd9Sstevel@tonic-gate 			lmutex_unlock(&popen_lock);
2497c478bd9Sstevel@tonic-gate 			return (pid);
2507c478bd9Sstevel@tonic-gate 		}
2517c478bd9Sstevel@tonic-gate 		prev = curr;
2527c478bd9Sstevel@tonic-gate 	}
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 	lmutex_unlock(&popen_lock);
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 	return (-1);
2577c478bd9Sstevel@tonic-gate }
258