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