#pragma weak _pclose = pclose
#pragma weak _popen = popen
#include "lint.h"
#include "mtlib.h"
#include "file64.h"
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <wait.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <thread.h>
#include <pthread.h>
#include <synch.h>
#include <spawn.h>
#include <paths.h>
#include "stdiom.h"
#include "mse.h"
#include "libc.h"
static mutex_t popen_lock = DEFAULTMUTEX;
typedef struct node {
pid_t pid;
int fd;
struct node *next;
} node_t;
static node_t *head = NULL;
static void _insert_nolock(pid_t, int, node_t *);
static void
cleanup(void *arg)
{
extern const sigset_t maskset;
extern void *reapchild(void *);
(void) thr_sigsetmask(SIG_SETMASK, &maskset, NULL);
(void) thr_create(NULL, 0, reapchild, arg, THR_DAEMON, NULL);
}
FILE *
popen(const char *cmd, const char *mode)
{
pid_t pid;
int myfd, fd;
const char *shpath = _PATH_BSHELL;
FILE *iop;
node_t *curr;
node_t *node;
posix_spawn_file_actions_t fact;
posix_spawnattr_t attr;
int error;
if ((node = lmalloc(sizeof (node_t))) == NULL)
return (NULL);
if ((error = posix_spawnattr_init(&attr)) != 0) {
lfree(node, sizeof (node_t));
errno = error;
return (NULL);
}
if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
lfree(node, sizeof (node_t));
(void) posix_spawnattr_destroy(&attr);
errno = error;
return (NULL);
}
if (access(shpath, X_OK))
shpath = "";
myfd = open("/dev/null", O_RDWR);
if (myfd == -1) {
error = errno;
lfree(node, sizeof (node_t));
(void) posix_spawnattr_destroy(&attr);
(void) posix_spawn_file_actions_destroy(&fact);
errno = error;
return (NULL);
}
iop = fdopen(myfd, mode);
if (iop == NULL) {
error = errno;
lfree(node, sizeof (node_t));
(void) posix_spawnattr_destroy(&attr);
(void) posix_spawn_file_actions_destroy(&fact);
(void) close(myfd);
errno = error;
return (NULL);
}
lmutex_lock(&popen_lock);
for (curr = head; curr != NULL && error == 0; curr = curr->next) {
if (fcntl(curr->fd, F_GETFD) >= 0) {
error = posix_spawn_file_actions_addclose(&fact,
curr->fd);
}
}
if (error == 0) {
error = posix_spawnattr_setflags(&attr,
POSIX_SPAWN_NOSIGCHLD_NP |
POSIX_SPAWN_WAITPID_NP |
POSIX_SPAWN_NOEXECERR_NP);
}
if (error != 0) {
lmutex_unlock(&popen_lock);
lfree(node, sizeof (node_t));
(void) posix_spawnattr_destroy(&attr);
(void) posix_spawn_file_actions_destroy(&fact);
(void) fclose(iop);
errno = error;
return (NULL);
}
error = posix_spawn_pipe_np(&pid, &fd, cmd, *mode != 'r', &fact, &attr);
(void) posix_spawnattr_destroy(&attr);
(void) posix_spawn_file_actions_destroy(&fact);
if (error != 0) {
lmutex_unlock(&popen_lock);
lfree(node, sizeof (node_t));
(void) fclose(iop);
errno = error;
return (NULL);
}
_insert_nolock(pid, myfd, node);
lmutex_unlock(&popen_lock);
(void) dup2(fd, myfd);
(void) close(fd);
_SET_ORIENTATION_BYTE(iop);
return (iop);
}
int
pclose(FILE *ptr)
{
pid_t pid;
int status;
pid = _delete(fileno(ptr));
(void) fclose(ptr);
if (pid <= 0) {
errno = ECHILD;
return (-1);
}
if (_thrp_cancelled()) {
if (waitpid(pid, &status, WNOHANG) == pid)
return (status);
cleanup((void *)(uintptr_t)pid);
errno = ECHILD;
return (-1);
}
pthread_cleanup_push(cleanup, (void *)(uintptr_t)pid);
while (waitpid(pid, &status, 0) < 0) {
if (errno != EINTR) {
status = -1;
break;
}
}
pthread_cleanup_pop(0);
return (status);
}
static void
_insert_nolock(pid_t pid, int fd, node_t *new)
{
node_t *prev;
node_t *curr;
for (prev = curr = head; curr != NULL; curr = curr->next) {
if (curr->fd == fd) {
(void) waitpid(curr->pid, NULL, WNOHANG);
curr->pid = pid;
lfree(new, sizeof (node_t));
return;
}
prev = curr;
}
new->pid = pid;
new->fd = fd;
new->next = NULL;
if (head == NULL)
head = new;
else
prev->next = new;
}
int
_insert(pid_t pid, int fd)
{
node_t *node;
if ((node = lmalloc(sizeof (node_t))) == NULL)
return (-1);
lmutex_lock(&popen_lock);
_insert_nolock(pid, fd, node);
lmutex_unlock(&popen_lock);
return (0);
}
pid_t
_delete(int fd)
{
node_t *prev;
node_t *curr;
pid_t pid;
lmutex_lock(&popen_lock);
for (prev = curr = head; curr != NULL; curr = curr->next) {
if (curr->fd == fd) {
if (curr == head)
head = curr->next;
else
prev->next = curr->next;
lmutex_unlock(&popen_lock);
pid = curr->pid;
lfree(curr, sizeof (node_t));
return (pid);
}
prev = curr;
}
lmutex_unlock(&popen_lock);
return (-1);
}