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