summaryrefslogtreecommitdiff
path: root/mit-pthreads/gen/popen.c
diff options
context:
space:
mode:
Diffstat (limited to 'mit-pthreads/gen/popen.c')
-rw-r--r--mit-pthreads/gen/popen.c117
1 files changed, 117 insertions, 0 deletions
diff --git a/mit-pthreads/gen/popen.c b/mit-pthreads/gen/popen.c
new file mode 100644
index 00000000000..c15fbdce1fe
--- /dev/null
+++ b/mit-pthreads/gen/popen.c
@@ -0,0 +1,117 @@
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+static pid_t *pids = NULL;
+static int pids_size = 0;
+static int pids_top = 0;
+static pthread_mutex_t pids_lock = PTHREAD_MUTEX_INITIALIZER;
+
+FILE *popen(const char *cmd, const char *mode)
+{
+ int fds[2], parent_fd, child_fd, child_target, new_size, i;
+ pid_t pid, *new_pids;
+
+ /* Verify the mode. */
+ if ((*mode != 'r' && *mode != 'w') || mode[1] != 0)
+ return NULL;
+
+ /* Generate fds, and choose the parent and child fds. */
+ if (pipe(fds) < 0)
+ return NULL;
+ parent_fd = (*mode == 'r') ? fds[0] : fds[1];
+ child_fd = (*mode == 'r') ? fds[1] : fds[0];
+
+ /* Ensure that there is space in the pid table. */
+ pthread_mutex_lock(&pids_lock);
+ if (pids_size <= parent_fd) {
+ new_size = parent_fd + 1;
+ if ((new_pids = malloc(new_size * sizeof(pid_t))) == NULL) {
+ pthread_mutex_unlock(&pids_lock);
+ close(parent_fd);
+ close(child_fd);
+ return NULL;
+ }
+ if (pids) {
+ memcpy(new_pids, pids, pids_size * sizeof(pid_t));
+ free(pids);
+ }
+ while (pids_size < new_size)
+ new_pids[pids_size++] = -1;
+ pids = new_pids;
+ }
+ pthread_mutex_unlock(&pids_lock);
+
+ /* Fork off a child process. */
+ switch (pid = fork()) {
+ case -1: /* Failed to fork. */
+ close(parent_fd);
+ close(child_fd);
+ return NULL;
+ break;
+ case 0: /* Child */
+ /*
+ * Set the child fd to stdout or stdin as appropriate,
+ * and close the parent fd.
+ */
+ child_target = (*mode == 'r') ? STDOUT_FILENO : STDIN_FILENO;
+ if (child_fd != child_target) {
+ dup2(child_fd, child_target);
+ close(child_fd);
+ }
+ close(parent_fd);
+
+ /* Close all parent fds from previous popens(). */
+ for (i = 0; i < pids_top; i++) {
+ if (pids[i] != -1)
+ close(i);
+ }
+
+ execl("/bin/sh", "sh", "-c", cmd, NULL);
+ exit(1);
+ default:
+ break;
+ }
+
+ /* Record the parent fd in the pids table. */
+ pthread_mutex_lock(&pids_lock);
+ pids[parent_fd] = pid;
+ if (pids_top < parent_fd + 1)
+ pids_top = parent_fd + 1;
+ pthread_mutex_unlock(&pids_lock);
+
+ /* Close the child fd and return a stdio buffer for the parent fd. */
+ close(child_fd);
+ return fdopen(parent_fd, mode);
+}
+
+int pclose(fp)
+ FILE *fp;
+{
+ pid_t pid, result;
+ int fd, pstat;
+
+ fd = fileno(fp);
+ pthread_mutex_lock(&pids_lock);
+ /* Make sure this is a popened file. */
+ if ((pids_top <= fd) || ((pid = pids[fd]) == -1)) {
+ pthread_mutex_unlock(&pids_lock);
+ return -1;
+ }
+ pids[fd] = -1;
+ while (pids_top > 0 && pids[pids_top - 1] == -1)
+ pids_top--;
+ pthread_mutex_unlock(&pids_lock);
+
+ fclose(fp);
+
+ /* Wait for the subprocess to quit. */
+ return (((result = waitpid(pid, &pstat, 0)) == -1) ? -1 : pstat);
+}
+