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
5a5f69788Scraigm * Common Development and Distribution License (the "License").
6a5f69788Scraigm * 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 */
21a5f69788Scraigm
227c478bd9Sstevel@tonic-gate /*
23a574db85Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
247c478bd9Sstevel@tonic-gate * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate
27*462453d2SMatthew Ahrens /*
28*462453d2SMatthew Ahrens * Copyright (c) 2011 by Delphix. All rights reserved.
29*462453d2SMatthew Ahrens */
30*462453d2SMatthew Ahrens
317c478bd9Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */
327c478bd9Sstevel@tonic-gate /* All Rights Reserved */
337c478bd9Sstevel@tonic-gate
347257d1b4Sraf #pragma weak _pclose = pclose
357257d1b4Sraf #pragma weak _popen = popen
367c478bd9Sstevel@tonic-gate
377257d1b4Sraf #include "lint.h"
387c478bd9Sstevel@tonic-gate #include "mtlib.h"
397c478bd9Sstevel@tonic-gate #include "file64.h"
407c478bd9Sstevel@tonic-gate #include <sys/types.h>
417c478bd9Sstevel@tonic-gate #include <stdio.h>
427c478bd9Sstevel@tonic-gate #include <stdlib.h>
437c478bd9Sstevel@tonic-gate #include <wait.h>
447c478bd9Sstevel@tonic-gate #include <signal.h>
457c478bd9Sstevel@tonic-gate #include <fcntl.h>
467c478bd9Sstevel@tonic-gate #include <unistd.h>
477c478bd9Sstevel@tonic-gate #include <errno.h>
487c478bd9Sstevel@tonic-gate #include <thread.h>
49657b1f3dSraf #include <pthread.h>
507c478bd9Sstevel@tonic-gate #include <synch.h>
517c478bd9Sstevel@tonic-gate #include <spawn.h>
526a5408e6SRichard Lowe #include <paths.h>
537c478bd9Sstevel@tonic-gate #include "stdiom.h"
547c478bd9Sstevel@tonic-gate #include "mse.h"
557c478bd9Sstevel@tonic-gate #include "libc.h"
567c478bd9Sstevel@tonic-gate
577c478bd9Sstevel@tonic-gate static mutex_t popen_lock = DEFAULTMUTEX;
587c478bd9Sstevel@tonic-gate
597c478bd9Sstevel@tonic-gate typedef struct node {
607c478bd9Sstevel@tonic-gate pid_t pid;
617c478bd9Sstevel@tonic-gate int fd;
627c478bd9Sstevel@tonic-gate struct node *next;
637c478bd9Sstevel@tonic-gate } node_t;
647c478bd9Sstevel@tonic-gate
657c478bd9Sstevel@tonic-gate static node_t *head = NULL;
66657b1f3dSraf static void _insert_nolock(pid_t, int, node_t *);
677c478bd9Sstevel@tonic-gate
68657b1f3dSraf /*
69657b1f3dSraf * Cancellation cleanup handler.
70657b1f3dSraf * If we were cancelled in waitpid(), create a daemon thread to
71657b1f3dSraf * reap our abandoned child. No other thread can do this for us.
72657b1f3dSraf */
73657b1f3dSraf static void
cleanup(void * arg)74657b1f3dSraf cleanup(void *arg)
75657b1f3dSraf {
76657b1f3dSraf extern const sigset_t maskset;
77657b1f3dSraf extern void *reapchild(void *); /* see port/stdio/system.c */
78657b1f3dSraf
79a574db85Sraf /*
80a574db85Sraf * We have been cancelled. There is no need to restore
81a574db85Sraf * the original sigmask after blocking all signals because
82a574db85Sraf * pthread_exit() will block all signals while we exit.
83a574db85Sraf */
84657b1f3dSraf (void) thr_sigsetmask(SIG_SETMASK, &maskset, NULL);
85657b1f3dSraf (void) thr_create(NULL, 0, reapchild, arg, THR_DAEMON, NULL);
86657b1f3dSraf }
877c478bd9Sstevel@tonic-gate
887c478bd9Sstevel@tonic-gate FILE *
popen(const char * cmd,const char * mode)897c478bd9Sstevel@tonic-gate popen(const char *cmd, const char *mode)
907c478bd9Sstevel@tonic-gate {
917c478bd9Sstevel@tonic-gate pid_t pid;
92*462453d2SMatthew Ahrens int myfd, fd;
936a5408e6SRichard Lowe const char *shpath = _PATH_BSHELL;
947c478bd9Sstevel@tonic-gate FILE *iop;
957c478bd9Sstevel@tonic-gate node_t *curr;
96657b1f3dSraf node_t *node;
977c478bd9Sstevel@tonic-gate posix_spawn_file_actions_t fact;
98*462453d2SMatthew Ahrens posix_spawnattr_t attr;
997c478bd9Sstevel@tonic-gate int error;
1007c478bd9Sstevel@tonic-gate
101657b1f3dSraf if ((node = lmalloc(sizeof (node_t))) == NULL)
1027c478bd9Sstevel@tonic-gate return (NULL);
103657b1f3dSraf if ((error = posix_spawnattr_init(&attr)) != 0) {
104657b1f3dSraf lfree(node, sizeof (node_t));
105657b1f3dSraf errno = error;
106657b1f3dSraf return (NULL);
107657b1f3dSraf }
108657b1f3dSraf if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
109657b1f3dSraf lfree(node, sizeof (node_t));
110657b1f3dSraf (void) posix_spawnattr_destroy(&attr);
111657b1f3dSraf errno = error;
112657b1f3dSraf return (NULL);
113657b1f3dSraf }
114*462453d2SMatthew Ahrens
115*462453d2SMatthew Ahrens if (access(shpath, X_OK)) /* XPG4 Requirement: */
116*462453d2SMatthew Ahrens shpath = ""; /* force child to fail immediately */
117*462453d2SMatthew Ahrens
118*462453d2SMatthew Ahrens
119*462453d2SMatthew Ahrens /*
120*462453d2SMatthew Ahrens * fdopen() can fail (if the fd is too high or we are out of memory),
121*462453d2SMatthew Ahrens * but we don't want to have any way to fail after creating the child
122*462453d2SMatthew Ahrens * process. So we fdopen() a dummy fd (myfd), and once we get the real
123*462453d2SMatthew Ahrens * fd from posix_spawn_pipe_np(), we dup2() the real fd onto the dummy.
124*462453d2SMatthew Ahrens */
125*462453d2SMatthew Ahrens myfd = open("/dev/null", O_RDWR);
126*462453d2SMatthew Ahrens if (myfd == -1) {
127657b1f3dSraf error = errno;
128657b1f3dSraf lfree(node, sizeof (node_t));
129657b1f3dSraf (void) posix_spawnattr_destroy(&attr);
130657b1f3dSraf (void) posix_spawn_file_actions_destroy(&fact);
131657b1f3dSraf errno = error;
132657b1f3dSraf return (NULL);
133657b1f3dSraf }
134*462453d2SMatthew Ahrens iop = fdopen(myfd, mode);
135*462453d2SMatthew Ahrens if (iop == NULL) {
136657b1f3dSraf error = errno;
137657b1f3dSraf lfree(node, sizeof (node_t));
138657b1f3dSraf (void) posix_spawnattr_destroy(&attr);
139657b1f3dSraf (void) posix_spawn_file_actions_destroy(&fact);
140*462453d2SMatthew Ahrens (void) close(myfd);
141657b1f3dSraf errno = error;
142a5f69788Scraigm return (NULL);
143a5f69788Scraigm }
144a5f69788Scraigm
1457c478bd9Sstevel@tonic-gate lmutex_lock(&popen_lock);
1467c478bd9Sstevel@tonic-gate
1477c478bd9Sstevel@tonic-gate /* in the child, close all pipes from other popen's */
148657b1f3dSraf for (curr = head; curr != NULL && error == 0; curr = curr->next) {
149657b1f3dSraf /*
150*462453d2SMatthew Ahrens * The fd may no longer be open if an iob previously returned
151657b1f3dSraf * by popen() was closed with fclose() rather than pclose(),
152*462453d2SMatthew Ahrens * or if close(fileno(iob)) was called. Use fcntl() to check
153*462453d2SMatthew Ahrens * if the fd is still open, so that these programming errors
154*462453d2SMatthew Ahrens * won't cause us to malfunction here.
155657b1f3dSraf */
156*462453d2SMatthew Ahrens if (fcntl(curr->fd, F_GETFD) >= 0) {
1577c478bd9Sstevel@tonic-gate error = posix_spawn_file_actions_addclose(&fact,
158*462453d2SMatthew Ahrens curr->fd);
159*462453d2SMatthew Ahrens }
1607c478bd9Sstevel@tonic-gate }
161f9f6ed06SRoger A. Faulkner /*
162f9f6ed06SRoger A. Faulkner * See the comments in port/stdio/system.c for why these
163f9f6ed06SRoger A. Faulkner * non-portable posix_spawn() attributes are being used.
164f9f6ed06SRoger A. Faulkner */
165*462453d2SMatthew Ahrens if (error == 0) {
166657b1f3dSraf error = posix_spawnattr_setflags(&attr,
167f9f6ed06SRoger A. Faulkner POSIX_SPAWN_NOSIGCHLD_NP |
168f9f6ed06SRoger A. Faulkner POSIX_SPAWN_WAITPID_NP |
169f9f6ed06SRoger A. Faulkner POSIX_SPAWN_NOEXECERR_NP);
170*462453d2SMatthew Ahrens }
171*462453d2SMatthew Ahrens if (error != 0) {
1727c478bd9Sstevel@tonic-gate lmutex_unlock(&popen_lock);
173657b1f3dSraf lfree(node, sizeof (node_t));
174657b1f3dSraf (void) posix_spawnattr_destroy(&attr);
1757c478bd9Sstevel@tonic-gate (void) posix_spawn_file_actions_destroy(&fact);
176a5f69788Scraigm (void) fclose(iop);
1777c478bd9Sstevel@tonic-gate errno = error;
1787c478bd9Sstevel@tonic-gate return (NULL);
1797c478bd9Sstevel@tonic-gate }
180*462453d2SMatthew Ahrens error = posix_spawn_pipe_np(&pid, &fd, cmd, *mode != 'r', &fact, &attr);
181657b1f3dSraf (void) posix_spawnattr_destroy(&attr);
1827c478bd9Sstevel@tonic-gate (void) posix_spawn_file_actions_destroy(&fact);
183*462453d2SMatthew Ahrens if (error != 0) {
1847c478bd9Sstevel@tonic-gate lmutex_unlock(&popen_lock);
185657b1f3dSraf lfree(node, sizeof (node_t));
186a5f69788Scraigm (void) fclose(iop);
187657b1f3dSraf errno = error;
1887c478bd9Sstevel@tonic-gate return (NULL);
1897c478bd9Sstevel@tonic-gate }
190*462453d2SMatthew Ahrens _insert_nolock(pid, myfd, node);
1917c478bd9Sstevel@tonic-gate
1927c478bd9Sstevel@tonic-gate lmutex_unlock(&popen_lock);
1937c478bd9Sstevel@tonic-gate
194*462453d2SMatthew Ahrens /*
195*462453d2SMatthew Ahrens * myfd is the one that we fdopen()'ed; make it refer to the
196*462453d2SMatthew Ahrens * pipe to the child.
197*462453d2SMatthew Ahrens */
198*462453d2SMatthew Ahrens (void) dup2(fd, myfd);
199*462453d2SMatthew Ahrens (void) close(fd);
200*462453d2SMatthew Ahrens
2017c478bd9Sstevel@tonic-gate _SET_ORIENTATION_BYTE(iop);
2027c478bd9Sstevel@tonic-gate
2037c478bd9Sstevel@tonic-gate return (iop);
2047c478bd9Sstevel@tonic-gate }
2057c478bd9Sstevel@tonic-gate
206a574db85Sraf /*
207a574db85Sraf * pclose() is a cancellation point.
208a574db85Sraf */
2097c478bd9Sstevel@tonic-gate int
pclose(FILE * ptr)2107c478bd9Sstevel@tonic-gate pclose(FILE *ptr)
2117c478bd9Sstevel@tonic-gate {
2127c478bd9Sstevel@tonic-gate pid_t pid;
2137c478bd9Sstevel@tonic-gate int status;
2147c478bd9Sstevel@tonic-gate
2157c478bd9Sstevel@tonic-gate pid = _delete(fileno(ptr));
2167c478bd9Sstevel@tonic-gate
2177c478bd9Sstevel@tonic-gate /* mark this pipe closed */
2187c478bd9Sstevel@tonic-gate (void) fclose(ptr);
2197c478bd9Sstevel@tonic-gate
220657b1f3dSraf if (pid <= 0) {
221657b1f3dSraf errno = ECHILD;
2227c478bd9Sstevel@tonic-gate return (-1);
223657b1f3dSraf }
2247c478bd9Sstevel@tonic-gate
225657b1f3dSraf /*
226a574db85Sraf * waitpid() is a cancellation point.
227a574db85Sraf * This causes pclose() to be a cancellation point.
228657b1f3dSraf *
229657b1f3dSraf * If we have already been cancelled (pclose() was called from
230657b1f3dSraf * a cancellation cleanup handler), attempt to reap the process
231657b1f3dSraf * w/o waiting, and if that fails just call cleanup(pid).
232657b1f3dSraf */
233657b1f3dSraf
234657b1f3dSraf if (_thrp_cancelled()) {
235a574db85Sraf /* waitpid(..., WNOHANG) is not a cancellation point */
236657b1f3dSraf if (waitpid(pid, &status, WNOHANG) == pid)
237657b1f3dSraf return (status);
238657b1f3dSraf cleanup((void *)(uintptr_t)pid);
239657b1f3dSraf errno = ECHILD;
240657b1f3dSraf return (-1);
241657b1f3dSraf }
242657b1f3dSraf
243657b1f3dSraf pthread_cleanup_push(cleanup, (void *)(uintptr_t)pid);
244a574db85Sraf while (waitpid(pid, &status, 0) < 0) {
2457c478bd9Sstevel@tonic-gate if (errno != EINTR) {
2467c478bd9Sstevel@tonic-gate status = -1;
2477c478bd9Sstevel@tonic-gate break;
2487c478bd9Sstevel@tonic-gate }
2497c478bd9Sstevel@tonic-gate }
250657b1f3dSraf pthread_cleanup_pop(0);
2517c478bd9Sstevel@tonic-gate
2527c478bd9Sstevel@tonic-gate return (status);
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate
2557c478bd9Sstevel@tonic-gate
256657b1f3dSraf static void
_insert_nolock(pid_t pid,int fd,node_t * new)257657b1f3dSraf _insert_nolock(pid_t pid, int fd, node_t *new)
2587c478bd9Sstevel@tonic-gate {
2597c478bd9Sstevel@tonic-gate node_t *prev;
2607c478bd9Sstevel@tonic-gate node_t *curr;
2617c478bd9Sstevel@tonic-gate
262657b1f3dSraf for (prev = curr = head; curr != NULL; curr = curr->next) {
263657b1f3dSraf /*
264657b1f3dSraf * curr->fd can equal fd if a previous iob returned by
265657b1f3dSraf * popen() was closed with fclose() rather than pclose(),
266a574db85Sraf * or if close(fileno(iob)) was called. Don't let these
267a574db85Sraf * programming errors cause us to malfunction here.
268657b1f3dSraf */
269657b1f3dSraf if (curr->fd == fd) {
270a574db85Sraf /* make a lame attempt to reap the forgotten child */
271657b1f3dSraf (void) waitpid(curr->pid, NULL, WNOHANG);
272657b1f3dSraf curr->pid = pid;
273657b1f3dSraf lfree(new, sizeof (node_t));
274657b1f3dSraf return;
275657b1f3dSraf }
2767c478bd9Sstevel@tonic-gate prev = curr;
277657b1f3dSraf }
2787c478bd9Sstevel@tonic-gate
2797c478bd9Sstevel@tonic-gate new->pid = pid;
2807c478bd9Sstevel@tonic-gate new->fd = fd;
2817c478bd9Sstevel@tonic-gate new->next = NULL;
2827c478bd9Sstevel@tonic-gate
2837c478bd9Sstevel@tonic-gate if (head == NULL)
2847c478bd9Sstevel@tonic-gate head = new;
2857c478bd9Sstevel@tonic-gate else
2867c478bd9Sstevel@tonic-gate prev->next = new;
2877c478bd9Sstevel@tonic-gate }
2887c478bd9Sstevel@tonic-gate
2897c478bd9Sstevel@tonic-gate /*
2907c478bd9Sstevel@tonic-gate * _insert() and _delete() are used by p2open() in libgen.
2917c478bd9Sstevel@tonic-gate */
2927c478bd9Sstevel@tonic-gate int
_insert(pid_t pid,int fd)2937c478bd9Sstevel@tonic-gate _insert(pid_t pid, int fd)
2947c478bd9Sstevel@tonic-gate {
295657b1f3dSraf node_t *node;
296657b1f3dSraf
297657b1f3dSraf if ((node = lmalloc(sizeof (node_t))) == NULL)
298657b1f3dSraf return (-1);
2997c478bd9Sstevel@tonic-gate
3007c478bd9Sstevel@tonic-gate lmutex_lock(&popen_lock);
301657b1f3dSraf _insert_nolock(pid, fd, node);
3027c478bd9Sstevel@tonic-gate lmutex_unlock(&popen_lock);
3037c478bd9Sstevel@tonic-gate
304657b1f3dSraf return (0);
3057c478bd9Sstevel@tonic-gate }
3067c478bd9Sstevel@tonic-gate
3077c478bd9Sstevel@tonic-gate
3087c478bd9Sstevel@tonic-gate pid_t
_delete(int fd)3097c478bd9Sstevel@tonic-gate _delete(int fd)
3107c478bd9Sstevel@tonic-gate {
3117c478bd9Sstevel@tonic-gate node_t *prev;
3127c478bd9Sstevel@tonic-gate node_t *curr;
3137c478bd9Sstevel@tonic-gate pid_t pid;
3147c478bd9Sstevel@tonic-gate
3157c478bd9Sstevel@tonic-gate lmutex_lock(&popen_lock);
3167c478bd9Sstevel@tonic-gate
3177c478bd9Sstevel@tonic-gate for (prev = curr = head; curr != NULL; curr = curr->next) {
3187c478bd9Sstevel@tonic-gate if (curr->fd == fd) {
3197c478bd9Sstevel@tonic-gate if (curr == head)
3207c478bd9Sstevel@tonic-gate head = curr->next;
3217c478bd9Sstevel@tonic-gate else
3227c478bd9Sstevel@tonic-gate prev->next = curr->next;
323657b1f3dSraf lmutex_unlock(&popen_lock);
3247c478bd9Sstevel@tonic-gate pid = curr->pid;
3257c478bd9Sstevel@tonic-gate lfree(curr, sizeof (node_t));
3267c478bd9Sstevel@tonic-gate return (pid);
3277c478bd9Sstevel@tonic-gate }
3287c478bd9Sstevel@tonic-gate prev = curr;
3297c478bd9Sstevel@tonic-gate }
3307c478bd9Sstevel@tonic-gate
3317c478bd9Sstevel@tonic-gate lmutex_unlock(&popen_lock);
3327c478bd9Sstevel@tonic-gate
3337c478bd9Sstevel@tonic-gate return (-1);
3347c478bd9Sstevel@tonic-gate }
335