Home | History | Annotate | Download | only in stdio
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1988 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     31 
     32 #pragma weak _pclose = pclose
     33 #pragma weak _popen = popen
     34 
     35 #include "lint.h"
     36 #include "mtlib.h"
     37 #include "file64.h"
     38 #include <sys/types.h>
     39 #include <stdio.h>
     40 #include <stdlib.h>
     41 #include <wait.h>
     42 #include <signal.h>
     43 #include <fcntl.h>
     44 #include <unistd.h>
     45 #include <errno.h>
     46 #include <thread.h>
     47 #include <pthread.h>
     48 #include <synch.h>
     49 #include <spawn.h>
     50 #include "stdiom.h"
     51 #include "mse.h"
     52 #include "libc.h"
     53 
     54 #define	tst(a, b) (*mode == 'r'? (b) : (a))
     55 #define	RDR	0
     56 #define	WTR	1
     57 
     58 extern	int __xpg4;	/* defined in _xpg4.c; 0 if not xpg4-compiled program */
     59 extern const char **_environ;
     60 
     61 static mutex_t popen_lock = DEFAULTMUTEX;
     62 
     63 typedef struct node {
     64 	pid_t	pid;
     65 	int	fd;
     66 	struct	node	*next;
     67 } node_t;
     68 
     69 static	node_t  *head = NULL;
     70 static	void	_insert_nolock(pid_t, int, node_t *);
     71 
     72 /*
     73  * Cancellation cleanup handler.
     74  * If we were cancelled in waitpid(), create a daemon thread to
     75  * reap our abandoned child.  No other thread can do this for us.
     76  */
     77 static void
     78 cleanup(void *arg)
     79 {
     80 	extern const sigset_t maskset;
     81 	extern void *reapchild(void *);		/* see port/stdio/system.c */
     82 
     83 	/*
     84 	 * We have been cancelled.  There is no need to restore
     85 	 * the original sigmask after blocking all signals because
     86 	 * pthread_exit() will block all signals while we exit.
     87 	 */
     88 	(void) thr_sigsetmask(SIG_SETMASK, &maskset, NULL);
     89 	(void) thr_create(NULL, 0, reapchild, arg, THR_DAEMON, NULL);
     90 }
     91 
     92 FILE *
     93 popen(const char *cmd, const char *mode)
     94 {
     95 	int	p[2];
     96 	pid_t	pid;
     97 	int	myside;
     98 	int	yourside;
     99 	int	fd;
    100 	const char *shpath;
    101 	FILE	*iop;
    102 	int	stdio;
    103 	node_t	*curr;
    104 	char	*argvec[4];
    105 	node_t	*node;
    106 	posix_spawnattr_t attr;
    107 	posix_spawn_file_actions_t fact;
    108 	int	error;
    109 	static const char *sun_path = "/bin/sh";
    110 	static const char *xpg4_path = "/usr/xpg4/bin/sh";
    111 	static const char *shell = "sh";
    112 	static const char *sh_flg = "-c";
    113 
    114 	if ((node = lmalloc(sizeof (node_t))) == NULL)
    115 		return (NULL);
    116 	if ((error = posix_spawnattr_init(&attr)) != 0) {
    117 		lfree(node, sizeof (node_t));
    118 		errno = error;
    119 		return (NULL);
    120 	}
    121 	if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
    122 		lfree(node, sizeof (node_t));
    123 		(void) posix_spawnattr_destroy(&attr);
    124 		errno = error;
    125 		return (NULL);
    126 	}
    127 	if (pipe(p) < 0) {
    128 		error = errno;
    129 		lfree(node, sizeof (node_t));
    130 		(void) posix_spawnattr_destroy(&attr);
    131 		(void) posix_spawn_file_actions_destroy(&fact);
    132 		errno = error;
    133 		return (NULL);
    134 	}
    135 
    136 	shpath = __xpg4? xpg4_path : sun_path;
    137 	if (access(shpath, X_OK))	/* XPG4 Requirement: */
    138 		shpath = "";		/* force child to fail immediately */
    139 
    140 	myside = tst(p[WTR], p[RDR]);
    141 	yourside = tst(p[RDR], p[WTR]);
    142 	/* myside and yourside reverse roles in child */
    143 	stdio = tst(0, 1);
    144 
    145 	/* This will fail more quickly if we run out of fds */
    146 	if ((iop = fdopen(myside, mode)) == NULL) {
    147 		error = errno;
    148 		lfree(node, sizeof (node_t));
    149 		(void) posix_spawnattr_destroy(&attr);
    150 		(void) posix_spawn_file_actions_destroy(&fact);
    151 		(void) close(yourside);
    152 		(void) close(myside);
    153 		errno = error;
    154 		return (NULL);
    155 	}
    156 
    157 	lmutex_lock(&popen_lock);
    158 
    159 	/* in the child, close all pipes from other popen's */
    160 	for (curr = head; curr != NULL && error == 0; curr = curr->next) {
    161 		/*
    162 		 * These conditions may apply if a previous iob returned
    163 		 * by popen() was closed with fclose() rather than pclose(),
    164 		 * or if close(fileno(iob)) was called.  Don't let these
    165 		 * programming errors cause us to malfunction here.
    166 		 */
    167 		if ((fd = curr->fd) != myside && fd != yourside &&
    168 		    fcntl(fd, F_GETFD) >= 0)
    169 			error = posix_spawn_file_actions_addclose(&fact, fd);
    170 	}
    171 	if (error == 0)
    172 		error =  posix_spawn_file_actions_addclose(&fact, myside);
    173 	if (yourside != stdio) {
    174 		if (error == 0)
    175 			error = posix_spawn_file_actions_adddup2(&fact,
    176 			    yourside, stdio);
    177 		if (error == 0)
    178 			error = posix_spawn_file_actions_addclose(&fact,
    179 			    yourside);
    180 	}
    181 	if (error == 0)
    182 		error = posix_spawnattr_setflags(&attr,
    183 		    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
    184 	if (error) {
    185 		lmutex_unlock(&popen_lock);
    186 		lfree(node, sizeof (node_t));
    187 		(void) posix_spawnattr_destroy(&attr);
    188 		(void) posix_spawn_file_actions_destroy(&fact);
    189 		(void) fclose(iop);
    190 		(void) close(yourside);
    191 		errno = error;
    192 		return (NULL);
    193 	}
    194 	argvec[0] = (char *)shell;
    195 	argvec[1] = (char *)sh_flg;
    196 	argvec[2] = (char *)cmd;
    197 	argvec[3] = NULL;
    198 	error = posix_spawn(&pid, shpath, &fact, &attr,
    199 	    (char *const *)argvec, (char *const *)_environ);
    200 	(void) posix_spawnattr_destroy(&attr);
    201 	(void) posix_spawn_file_actions_destroy(&fact);
    202 	(void) close(yourside);
    203 	if (error) {
    204 		lmutex_unlock(&popen_lock);
    205 		lfree(node, sizeof (node_t));
    206 		(void) fclose(iop);
    207 		errno = error;
    208 		return (NULL);
    209 	}
    210 	_insert_nolock(pid, myside, node);
    211 
    212 	lmutex_unlock(&popen_lock);
    213 
    214 	_SET_ORIENTATION_BYTE(iop);
    215 
    216 	return (iop);
    217 }
    218 
    219 /*
    220  * pclose() is a cancellation point.
    221  */
    222 int
    223 pclose(FILE *ptr)
    224 {
    225 	pid_t	pid;
    226 	int status;
    227 
    228 	pid = _delete(fileno(ptr));
    229 
    230 	/* mark this pipe closed */
    231 	(void) fclose(ptr);
    232 
    233 	if (pid <= 0) {
    234 		errno = ECHILD;
    235 		return (-1);
    236 	}
    237 
    238 	/*
    239 	 * waitpid() is a cancellation point.
    240 	 * This causes pclose() to be a cancellation point.
    241 	 *
    242 	 * If we have already been cancelled (pclose() was called from
    243 	 * a cancellation cleanup handler), attempt to reap the process
    244 	 * w/o waiting, and if that fails just call cleanup(pid).
    245 	 */
    246 
    247 	if (_thrp_cancelled()) {
    248 		/* waitpid(..., WNOHANG) is not a cancellation point */
    249 		if (waitpid(pid, &status, WNOHANG) == pid)
    250 			return (status);
    251 		cleanup((void *)(uintptr_t)pid);
    252 		errno = ECHILD;
    253 		return (-1);
    254 	}
    255 
    256 	pthread_cleanup_push(cleanup, (void *)(uintptr_t)pid);
    257 	while (waitpid(pid, &status, 0) < 0) {
    258 		if (errno != EINTR) {
    259 			status = -1;
    260 			break;
    261 		}
    262 	}
    263 	pthread_cleanup_pop(0);
    264 
    265 	return (status);
    266 }
    267 
    268 
    269 static void
    270 _insert_nolock(pid_t pid, int fd, node_t *new)
    271 {
    272 	node_t	*prev;
    273 	node_t	*curr;
    274 
    275 	for (prev = curr = head; curr != NULL; curr = curr->next) {
    276 		/*
    277 		 * curr->fd can equal fd if a previous iob returned by
    278 		 * popen() was closed with fclose() rather than pclose(),
    279 		 * or if close(fileno(iob)) was called.  Don't let these
    280 		 * programming errors cause us to malfunction here.
    281 		 */
    282 		if (curr->fd == fd) {
    283 			/* make a lame attempt to reap the forgotten child */
    284 			(void) waitpid(curr->pid, NULL, WNOHANG);
    285 			curr->pid = pid;
    286 			lfree(new, sizeof (node_t));
    287 			return;
    288 		}
    289 		prev = curr;
    290 	}
    291 
    292 	new->pid = pid;
    293 	new->fd = fd;
    294 	new->next = NULL;
    295 
    296 	if (head == NULL)
    297 		head = new;
    298 	else
    299 		prev->next = new;
    300 }
    301 
    302 /*
    303  * _insert() and _delete() are used by p2open() in libgen.
    304  */
    305 int
    306 _insert(pid_t pid, int fd)
    307 {
    308 	node_t *node;
    309 
    310 	if ((node = lmalloc(sizeof (node_t))) == NULL)
    311 		return (-1);
    312 
    313 	lmutex_lock(&popen_lock);
    314 	_insert_nolock(pid, fd, node);
    315 	lmutex_unlock(&popen_lock);
    316 
    317 	return (0);
    318 }
    319 
    320 
    321 pid_t
    322 _delete(int fd)
    323 {
    324 	node_t	*prev;
    325 	node_t	*curr;
    326 	pid_t	pid;
    327 
    328 	lmutex_lock(&popen_lock);
    329 
    330 	for (prev = curr = head; curr != NULL; curr = curr->next) {
    331 		if (curr->fd == fd) {
    332 			if (curr == head)
    333 				head = curr->next;
    334 			else
    335 				prev->next = curr->next;
    336 			lmutex_unlock(&popen_lock);
    337 			pid = curr->pid;
    338 			lfree(curr, sizeof (node_t));
    339 			return (pid);
    340 		}
    341 		prev = curr;
    342 	}
    343 
    344 	lmutex_unlock(&popen_lock);
    345 
    346 	return (-1);
    347 }
    348