-#include <stdio.h>
-#include <signal.h>
-#include <errno.h>
-#define tst(a,b) (*mode == 'r'? (b) : (a))
-#define RDR 0
-#define WTR 1
-static int popen_pid[20];
-static char *sccsid = "@(#)popen.c 1.5 %G%";
-
-# ifndef VMUNIX
-# define vfork fork
-# endif VMUNIX
-#ifndef SIGRETRO
-#define sigchild()
-#endif
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)popen.c 5.13 (Berkeley) %G%";
+#endif /* not lint */
+
+#include "rcv.h"
+#include <sys/signal.h>
+#include <sys/wait.h>
+
+#define READ 0
+#define WRITE 1
+static int *pid;
FILE *
-popen(cmd,mode)
-char *cmd;
-char *mode;
+Popen(cmd, mode)
+ char *cmd;
+ char *mode;
{
int p[2];
- register myside, hisside, pid;
+ int myside, hisside, fd0, fd1;
- if(pipe(p) < 0)
+ if (pid == 0)
+ pid = (int *) malloc((unsigned) sizeof (int) * getdtablesize());
+ if (pipe(p) < 0)
return NULL;
- myside = tst(p[WTR], p[RDR]);
- hisside = tst(p[RDR], p[WTR]);
- if((pid = vfork()) == 0) {
- /* myside and hisside reverse roles in child */
- sigchild();
- close(myside);
- dup2(hisside, tst(0, 1));
- close(hisside);
- execl("/bin/csh", "sh", "-c", cmd, 0);
- _exit(1);
+ if (*mode == 'r') {
+ myside = p[READ];
+ fd0 = -1;
+ hisside = fd1 = p[WRITE];
+ } else {
+ myside = p[WRITE];
+ hisside = fd0 = p[READ];
+ fd1 = -1;
}
- if(pid == -1)
+ if ((pid[myside] = start_command(cmd, 0, fd0, fd1, NOSTR)) < 0) {
+ close(p[READ]);
+ close(p[WRITE]);
return NULL;
- popen_pid[myside] = pid;
+ }
close(hisside);
- return(fdopen(myside, mode));
+ return fdopen(myside, mode);
}
-pclose(ptr)
-FILE *ptr;
+Pclose(ptr)
+ FILE *ptr;
{
- register f, r;
- int status;
- extern int errno;
+ int i;
+ int omask;
- f = fileno(ptr);
+ i = fileno(ptr);
fclose(ptr);
-# ifdef VMUNIX
- sighold(SIGINT);
- sighold(SIGQUIT);
- sighold(SIGHUP);
-# endif VMUNIX
- while((r = wait(&status)) != popen_pid[f] && r != -1 && errno != EINTR)
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGHUP));
+ i = wait_child(pid[i]);
+ sigsetmask(omask);
+ return i;
+}
+
+/*
+ * Run a command without a shell, with optional arguments and splicing
+ * of stdin and stdout. The command name can be a sequence of words.
+ * Signals must be handled by the caller.
+ * "Mask" contains the signals to ignore in the new process.
+ * SIGINT is enabled unless it's in the mask.
+ */
+/*VARARGS4*/
+run_command(cmd, mask, infd, outfd, a0, a1, a2)
+ char *cmd;
+ int mask, infd, outfd;
+ char *a0, *a1, *a2;
+{
+ int pid;
+
+ if ((pid = start_command(cmd, mask, infd, outfd, a0, a1, a2)) < 0)
+ return -1;
+ return wait_command(pid);
+}
+
+/*VARARGS4*/
+start_command(cmd, mask, infd, outfd, a0, a1, a2)
+ char *cmd;
+ int mask, infd, outfd;
+ char *a0, *a1, *a2;
+{
+ int pid;
+
+ if ((pid = vfork()) < 0) {
+ perror("fork");
+ return -1;
+ }
+ if (pid == 0) {
+ char *argv[100];
+ int i = getrawlist(cmd, argv, sizeof argv / sizeof *argv);
+
+ if ((argv[i++] = a0) != NOSTR &&
+ (argv[i++] = a1) != NOSTR &&
+ (argv[i++] = a2) != NOSTR)
+ argv[i] = NOSTR;
+ prepare_child(mask, infd, outfd);
+ execvp(argv[0], argv);
+ perror(argv[0]);
+ _exit(1);
+ }
+ return pid;
+}
+
+prepare_child(mask, infd, outfd)
+ int mask, infd, outfd;
+{
+ int i;
+
+ if (infd >= 0)
+ dup2(infd, 0);
+ if (outfd >= 0)
+ dup2(outfd, 1);
+ for (i = getdtablesize(); --i > 2;)
+ close(i);
+ for (i = 1; i <= NSIG; i++)
+ if (mask & sigmask(i))
+ (void) signal(i, SIG_IGN);
+ if ((mask & sigmask(SIGINT)) == 0)
+ (void) signal(SIGINT, SIG_DFL);
+ (void) sigsetmask(0);
+}
+
+wait_command(pid)
+ int pid;
+{
+
+ if (wait_child(pid) < 0) {
+ printf("Fatal error in process.\n");
+ return -1;
+ }
+ return 0;
+}
+
+struct child {
+ int pid;
+ char done;
+ char free;
+ union wait status;
+ struct child *link;
+};
+static struct child *child;
+
+struct child *
+findchild(pid)
+ int pid;
+{
+ register struct child **cpp;
+
+ for (cpp = &child; *cpp != NULL && (*cpp)->pid != pid;
+ cpp = &(*cpp)->link)
+ ;
+ if (*cpp == NULL) {
+ *cpp = (struct child *) malloc(sizeof (struct child));
+ (*cpp)->pid = pid;
+ (*cpp)->done = (*cpp)->free = 0;
+ (*cpp)->link = NULL;
+ }
+ return *cpp;
+}
+
+delchild(cp)
+ register struct child *cp;
+{
+ register struct child **cpp;
+
+ for (cpp = &child; *cpp != cp; cpp = &(*cpp)->link)
;
- if(r == -1)
- status = -1;
-# ifdef VMUNIX
- sigrelse(SIGINT);
- sigrelse(SIGQUIT);
- sigrelse(SIGHUP);
-# endif VMUNIX
- return(status);
+ *cpp = cp->link;
+ free((char *) cp);
+}
+
+sigchild()
+{
+ int pid;
+ union wait status;
+ register struct child *cp;
+
+ while ((pid = wait3(&status, WNOHANG, (struct timeval *)0)) > 0) {
+ cp = findchild(pid);
+ if (cp->free)
+ delchild(cp);
+ else {
+ cp->done = 1;
+ cp->status = status;
+ }
+ }
+}
+
+union wait wait_status;
+
+/*
+ * Wait for a specific child to die.
+ */
+wait_child(pid)
+ int pid;
+{
+ int mask = sigblock(sigmask(SIGCHLD));
+ register struct child *cp = findchild(pid);
+
+ while (!cp->done)
+ sigpause(mask);
+ wait_status = cp->status;
+ delchild(cp);
+ sigsetmask(mask);
+ return wait_status.w_status ? -1 : 0;
+}
+
+/*
+ * Mark a child as don't care.
+ */
+free_child(pid)
+ int pid;
+{
+ int mask = sigblock(sigmask(SIGCHLD));
+ register struct child *cp = findchild(pid);
+
+ if (cp->done)
+ delchild(cp);
+ else
+ cp->free = 1;
+ sigsetmask(mask);
}