popen.c revision 59f081ed215eb7d3fbf19cce3474b2987eaf3225
1022N/A/*
1022N/A * CDDL HEADER START
1022N/A *
1022N/A * The contents of this file are subject to the terms of the
1022N/A * Common Development and Distribution License (the "License").
1022N/A * You may not use this file except in compliance with the License.
1022N/A *
1022N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1022N/A * or http://www.opensolaris.org/os/licensing.
1022N/A * See the License for the specific language governing permissions
1022N/A * and limitations under the License.
1022N/A *
1022N/A * When distributing Covered Code, include this CDDL HEADER in each
1022N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1022N/A * If applicable, add the following below this CDDL HEADER, with the
1022N/A * fields enclosed by brackets "[]" replaced with your own identifying
1022N/A * information: Portions Copyright [yyyy] [name of copyright owner]
1022N/A *
1022N/A * CDDL HEADER END
1022N/A */
1022N/A
1022N/A/*
1022N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
1022N/A * Use is subject to license terms.
3232N/A */
1022N/A
1022N/A/* Copyright (c) 1988 AT&T */
1022N/A/* All Rights Reserved */
1022N/A
1022N/A#pragma ident "%Z%%M% %I% %E% SMI"
1022N/A
1022N/A#pragma weak _pclose = pclose
1056N/A#pragma weak _popen = popen
4134N/A
1022N/A#include "lint.h"
1056N/A#include "mtlib.h"
1022N/A#include "file64.h"
1022N/A#include <sys/types.h>
1022N/A#include <stdio.h>
1022N/A#include <stdlib.h>
1022N/A#include <wait.h>
1022N/A#include <signal.h>
1022N/A#include <fcntl.h>
1022N/A#include <unistd.h>
1022N/A#include <errno.h>
1022N/A#include <thread.h>
1022N/A#include <pthread.h>
1022N/A#include <synch.h>
1022N/A#include <spawn.h>
1022N/A#include "stdiom.h"
1056N/A#include "mse.h"
1056N/A#include "libc.h"
1056N/A
1056N/A#define tst(a, b) (*mode == 'r'? (b) : (a))
1056N/A#define RDR 0
1056N/A#define WTR 1
1056N/A
1056N/Aextern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */
1056N/Aextern const char **_environ;
1056N/A
1056N/Astatic mutex_t popen_lock = DEFAULTMUTEX;
1056N/A
1056N/Atypedef struct node {
1056N/A pid_t pid;
1056N/A int fd;
1056N/A struct node *next;
1056N/A} node_t;
1056N/A
1056N/Astatic node_t *head = NULL;
1056N/Astatic void _insert_nolock(pid_t, int, node_t *);
1056N/A
1056N/A/*
1056N/A * Cancellation cleanup handler.
1056N/A * If we were cancelled in waitpid(), create a daemon thread to
1056N/A * reap our abandoned child. No other thread can do this for us.
1056N/A */
1056N/Astatic void
1056N/Acleanup(void *arg)
1056N/A{
1056N/A extern const sigset_t maskset;
1056N/A extern void *reapchild(void *); /* see port/stdio/system.c */
1056N/A
1056N/A /*
1056N/A * We have been cancelled. There is no need to restore
1056N/A * the original sigmask after blocking all signals because
1056N/A * pthread_exit() will block all signals while we exit.
1056N/A */
1056N/A (void) thr_sigsetmask(SIG_SETMASK, &maskset, NULL);
1231N/A (void) thr_create(NULL, 0, reapchild, arg, THR_DAEMON, NULL);
1231N/A}
1231N/A
1231N/AFILE *
1231N/Apopen(const char *cmd, const char *mode)
1231N/A{
1231N/A int p[2];
1056N/A pid_t pid;
1056N/A int myside;
1056N/A int yourside;
1056N/A int fd;
1056N/A const char *shpath;
1056N/A FILE *iop;
1056N/A int stdio;
1056N/A node_t *curr;
1056N/A char *argvec[4];
1056N/A node_t *node;
1056N/A posix_spawnattr_t attr;
1056N/A posix_spawn_file_actions_t fact;
1056N/A int error;
1056N/A static const char *sun_path = "/bin/sh";
1056N/A static const char *xpg4_path = "/usr/xpg4/bin/sh";
1056N/A static const char *shell = "sh";
1056N/A static const char *sh_flg = "-c";
1056N/A
1056N/A if ((node = lmalloc(sizeof (node_t))) == NULL)
1056N/A return (NULL);
1056N/A if ((error = posix_spawnattr_init(&attr)) != 0) {
1056N/A lfree(node, sizeof (node_t));
1056N/A errno = error;
1056N/A return (NULL);
1056N/A }
1056N/A if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
1056N/A lfree(node, sizeof (node_t));
1056N/A (void) posix_spawnattr_destroy(&attr);
1056N/A errno = error;
1056N/A return (NULL);
1056N/A }
1056N/A if (pipe(p) < 0) {
1231N/A error = errno;
1231N/A lfree(node, sizeof (node_t));
1231N/A (void) posix_spawnattr_destroy(&attr);
1231N/A (void) posix_spawn_file_actions_destroy(&fact);
1231N/A errno = error;
1231N/A return (NULL);
1239N/A }
1239N/A
1239N/A shpath = __xpg4? xpg4_path : sun_path;
1239N/A if (access(shpath, X_OK)) /* XPG4 Requirement: */
1056N/A shpath = ""; /* force child to fail immediately */
1056N/A
1056N/A myside = tst(p[WTR], p[RDR]);
1056N/A yourside = tst(p[RDR], p[WTR]);
1056N/A /* myside and yourside reverse roles in child */
1056N/A stdio = tst(0, 1);
1056N/A
1056N/A /* This will fail more quickly if we run out of fds */
1056N/A if ((iop = fdopen(myside, mode)) == NULL) {
1056N/A error = errno;
1056N/A lfree(node, sizeof (node_t));
1056N/A (void) posix_spawnattr_destroy(&attr);
1056N/A (void) posix_spawn_file_actions_destroy(&fact);
1056N/A (void) close(yourside);
1056N/A (void) close(myside);
1056N/A errno = error;
1056N/A return (NULL);
1056N/A }
1056N/A
1056N/A lmutex_lock(&popen_lock);
1056N/A
1056N/A /* in the child, close all pipes from other popen's */
1056N/A for (curr = head; curr != NULL && error == 0; curr = curr->next) {
1056N/A /*
1056N/A * These conditions may apply if a previous iob returned
1022N/A * by popen() was closed with fclose() rather than pclose(),
1022N/A * or if close(fileno(iob)) was called. Don't let these
1022N/A * programming errors cause us to malfunction here.
1022N/A */
1022N/A if ((fd = curr->fd) != myside && fd != yourside &&
1022N/A fcntl(fd, F_GETFD) >= 0)
1022N/A error = posix_spawn_file_actions_addclose(&fact, fd);
1022N/A }
1022N/A if (error == 0)
1022N/A error = posix_spawn_file_actions_addclose(&fact, myside);
1022N/A if (yourside != stdio) {
1022N/A if (error == 0)
1022N/A error = posix_spawn_file_actions_adddup2(&fact,
1022N/A yourside, stdio);
1022N/A if (error == 0)
1022N/A error = posix_spawn_file_actions_addclose(&fact,
1022N/A yourside);
1022N/A }
1022N/A if (error == 0)
1022N/A error = posix_spawnattr_setflags(&attr,
1022N/A POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
1056N/A if (error) {
1022N/A lmutex_unlock(&popen_lock);
1022N/A lfree(node, sizeof (node_t));
1022N/A (void) posix_spawnattr_destroy(&attr);
1022N/A (void) posix_spawn_file_actions_destroy(&fact);
1022N/A (void) fclose(iop);
1022N/A (void) close(yourside);
1022N/A errno = error;
1056N/A return (NULL);
1022N/A }
1022N/A argvec[0] = (char *)shell;
1022N/A argvec[1] = (char *)sh_flg;
1022N/A argvec[2] = (char *)cmd;
1022N/A argvec[3] = NULL;
1022N/A error = posix_spawn(&pid, shpath, &fact, &attr,
1022N/A (char *const *)argvec, (char *const *)_environ);
1022N/A (void) posix_spawnattr_destroy(&attr);
1022N/A (void) posix_spawn_file_actions_destroy(&fact);
1022N/A (void) close(yourside);
1022N/A if (error) {
1022N/A lmutex_unlock(&popen_lock);
1022N/A lfree(node, sizeof (node_t));
1022N/A (void) fclose(iop);
1022N/A errno = error;
1022N/A return (NULL);
1022N/A }
1022N/A _insert_nolock(pid, myside, node);
1022N/A
1022N/A lmutex_unlock(&popen_lock);
1022N/A
1022N/A _SET_ORIENTATION_BYTE(iop);
1022N/A
1022N/A return (iop);
1022N/A}
1022N/A
1022N/A/*
1022N/A * pclose() is a cancellation point.
1022N/A */
1056N/Aint
1056N/Apclose(FILE *ptr)
1056N/A{
1056N/A pid_t pid;
1056N/A int status;
1056N/A
1056N/A pid = _delete(fileno(ptr));
1056N/A
1056N/A /* mark this pipe closed */
1056N/A (void) fclose(ptr);
1056N/A
1056N/A if (pid <= 0) {
1056N/A errno = ECHILD;
1056N/A return (-1);
1056N/A }
1056N/A
1056N/A /*
1056N/A * waitpid() is a cancellation point.
1056N/A * This causes pclose() to be a cancellation point.
1056N/A *
1056N/A * If we have already been cancelled (pclose() was called from
1056N/A * a cancellation cleanup handler), attempt to reap the process
1056N/A * w/o waiting, and if that fails just call cleanup(pid).
1056N/A */
1056N/A
1056N/A if (_thrp_cancelled()) {
1056N/A /* waitpid(..., WNOHANG) is not a cancellation point */
1056N/A if (waitpid(pid, &status, WNOHANG) == pid)
1056N/A return (status);
1056N/A cleanup((void *)(uintptr_t)pid);
1056N/A errno = ECHILD;
1056N/A return (-1);
1056N/A }
1056N/A
1056N/A pthread_cleanup_push(cleanup, (void *)(uintptr_t)pid);
1056N/A while (waitpid(pid, &status, 0) < 0) {
1056N/A if (errno != EINTR) {
1056N/A status = -1;
1056N/A break;
1056N/A }
1032N/A }
1032N/A pthread_cleanup_pop(0);
1032N/A
1032N/A return (status);
1032N/A}
1032N/A
1032N/A
1032N/Astatic void
1032N/A_insert_nolock(pid_t pid, int fd, node_t *new)
1032N/A{
1032N/A node_t *prev;
1032N/A node_t *curr;
1032N/A
1032N/A for (prev = curr = head; curr != NULL; curr = curr->next) {
1032N/A /*
1032N/A * curr->fd can equal fd if a previous iob returned by
1032N/A * popen() was closed with fclose() rather than pclose(),
1032N/A * or if close(fileno(iob)) was called. Don't let these
1032N/A * programming errors cause us to malfunction here.
1032N/A */
1032N/A if (curr->fd == fd) {
1032N/A /* make a lame attempt to reap the forgotten child */
1032N/A (void) waitpid(curr->pid, NULL, WNOHANG);
1032N/A curr->pid = pid;
1032N/A lfree(new, sizeof (node_t));
1032N/A return;
1032N/A }
1032N/A prev = curr;
1032N/A }
1032N/A
1032N/A new->pid = pid;
1032N/A new->fd = fd;
1032N/A new->next = NULL;
1032N/A
1032N/A if (head == NULL)
1032N/A head = new;
1056N/A else
1056N/A prev->next = new;
1056N/A}
1056N/A
1056N/A/*
1056N/A * _insert() and _delete() are used by p2open() in libgen.
1056N/A */
1056N/Aint
1056N/A_insert(pid_t pid, int fd)
1056N/A{
1056N/A node_t *node;
1056N/A
1056N/A if ((node = lmalloc(sizeof (node_t))) == NULL)
1056N/A return (-1);
1056N/A
1056N/A lmutex_lock(&popen_lock);
1022N/A _insert_nolock(pid, fd, node);
1022N/A lmutex_unlock(&popen_lock);
1022N/A
1056N/A return (0);
1022N/A}
1022N/A
1022N/A
1022N/Apid_t
1022N/A_delete(int fd)
1022N/A{
1022N/A node_t *prev;
1022N/A node_t *curr;
1022N/A pid_t pid;
1022N/A
1022N/A lmutex_lock(&popen_lock);
1022N/A
1022N/A for (prev = curr = head; curr != NULL; curr = curr->next) {
1022N/A if (curr->fd == fd) {
1022N/A if (curr == head)
1022N/A head = curr->next;
1022N/A else
1022N/A prev->next = curr->next;
1022N/A lmutex_unlock(&popen_lock);
1056N/A pid = curr->pid;
1056N/A lfree(curr, sizeof (node_t));
1056N/A return (pid);
1056N/A }
1056N/A prev = curr;
1056N/A }
1056N/A
1056N/A lmutex_unlock(&popen_lock);
1056N/A
1056N/A return (-1);
1056N/A}
1056N/A