root/popen.cpp
/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- _lock_popen_map
- _unlock_popen_map
- popen
- pclose
- dmtcp_is_popen_fp
1 /****************************************************************************
2 * Copyright (C) 2006-2013 by Jason Ansel, Kapil Arya, and Gene Cooperman *
3 * jansel@csail.mit.edu, kapil@ccs.neu.edu, gene@ccs.neu.edu *
4 * *
5 * This file is part of DMTCP. *
6 * *
7 * DMTCP is free software: you can redistribute it and/or *
8 * modify it under the terms of the GNU Lesser General Public License as *
9 * published by the Free Software Foundation, either version 3 of the *
10 * License, or (at your option) any later version. *
11 * *
12 * DMTCP is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU Lesser General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU Lesser General Public *
18 * License along with DMTCP:dmtcp/src. If not, see *
19 * <http://www.gnu.org/licenses/>. *
20 ****************************************************************************/
21
22 #include "syscallwrappers.h"
23 #include "threadsync.h"
24 #include "../jalib/jassert.h"
25
26 using namespace dmtcp;
27
28 static map<FILE*, pid_t> _dmtcpPopenPidMap;
29 typedef map<FILE*, pid_t>::iterator _dmtcpPopenPidMapIterator;
30
31 static pthread_mutex_t popen_map_lock = PTHREAD_MUTEX_INITIALIZER;
32
33 static void _lock_popen_map()
34 {
35 JASSERT(_real_pthread_mutex_lock(&popen_map_lock) == 0) (JASSERT_ERRNO);
36 }
37
38 static void _unlock_popen_map()
39 {
40 JASSERT(_real_pthread_mutex_unlock(&popen_map_lock) == 0) (JASSERT_ERRNO);
41 }
42
43 extern "C"
44 FILE *popen(const char *command, const char *mode)
45 {
46 FILE *fp;
47 int parent_fd, child_fd;
48 int pipe_fds[2];
49 pid_t child_pid;
50 char new_mode[2] = "r";
51
52 int do_read = 0;
53 int do_write = 0;
54 int do_cloexec = 0;
55 while (*mode != '\0') {
56 switch (*mode++) {
57 case 'r':
58 do_read = 1;
59 break;
60 case 'w':
61 do_write = 1;
62 break;
63 case 'e':
64 do_cloexec = 1;
65 break;
66 default:
67 errno = EINVAL;
68 return NULL;
69 }
70 }
71
72 if ((do_read ^ do_write) == 0) {
73 errno = EINVAL;
74 return NULL;
75 }
76
77 {
78 WRAPPER_EXECUTION_DISABLE_CKPT();
79 if (pipe(pipe_fds) < 0) {
80 return NULL;
81 }
82 // Mark the parent_end with FD_CLOEXEC so that if there is fork/exec while
83 // we are inside this wrapper, these fds are closed.
84 fcntl(pipe_fds[0], F_SETFD, FD_CLOEXEC);
85 fcntl(pipe_fds[1], F_SETFD, FD_CLOEXEC);
86
87 if (do_read) {
88 parent_fd = pipe_fds[0];
89 child_fd = pipe_fds[1];
90 strcpy(new_mode, "r");
91 } else {
92 parent_fd = pipe_fds[1];
93 child_fd = pipe_fds[0];
94 strcpy(new_mode, "w");
95 }
96 WRAPPER_EXECUTION_ENABLE_CKPT();
97 }
98
99 child_pid = fork();
100 if (child_pid == 0) {
101 int child_std_fd = do_read ? STDOUT_FILENO : STDIN_FILENO;
102 close(parent_fd);
103 if (child_fd != child_std_fd) {
104 dup2(child_fd, child_std_fd);
105 close(child_fd);
106 }
107 /* POSIX.2: "popen() shall ensure that any streams from previous
108 popen() calls that remain open in the parent process are closed
109 in the new child process." */
110 _dmtcpPopenPidMapIterator it;
111 for (it = _dmtcpPopenPidMap.begin(); it != _dmtcpPopenPidMap.end(); it++) {
112 int fd = fileno(it->first);
113 /* If any stream from previous popen() calls has fileno
114 child_std_end, it has been already closed by the dup2 syscall
115 above. */
116 if (fd != child_std_fd) {
117 _real_fclose(it->first);
118 }
119 }
120 _dmtcpPopenPidMap.clear();
121
122 fcntl(child_std_fd, F_SETFD, 0);
123 execl("/bin/sh", "sh", "-c", command, (char *) 0);
124 exit(127);
125 }
126 close(child_fd);
127 if (child_pid < 0) {
128 close(parent_fd);
129 return NULL;
130 }
131
132 {
133 WRAPPER_EXECUTION_DISABLE_CKPT();
134 fp = fdopen(parent_fd, new_mode);
135 if (!do_cloexec) {
136 fcntl(parent_fd, F_SETFD, 0);
137 }
138 _lock_popen_map();
139 _dmtcpPopenPidMap[fp] = child_pid;
140 _unlock_popen_map();
141 WRAPPER_EXECUTION_ENABLE_CKPT();
142 }
143
144 return fp;
145 }
146
147 extern "C"
148 int pclose(FILE *fp)
149 {
150 _dmtcpPopenPidMapIterator it;
151 int wstatus;
152 pid_t pid = -1;
153 pid_t wait_pid;
154
155 _lock_popen_map();
156 it = _dmtcpPopenPidMap.find(fp);
157 if (it != _dmtcpPopenPidMap.end()) {
158 fp = it->first;
159 pid = it->second;
160 _dmtcpPopenPidMap.erase(it);
161 }
162 _unlock_popen_map();
163
164 if (pid == -1 || _real_fclose(fp) != 0) {
165 return -1;
166 }
167
168 do {
169 wait_pid = waitpid(pid, &wstatus, 0);
170 } while (wait_pid == -1 && errno == EINTR);
171 if (wait_pid == -1) {
172 return -1;
173 }
174 return wstatus;
175 }
176
177 EXTERNC int dmtcp_is_popen_fp(FILE *fp)
178 {
179 int popen_fp = 0;
180 _lock_popen_map();
181 if (_dmtcpPopenPidMap.find(fp) != _dmtcpPopenPidMap.end()) {
182 popen_fp = 1;
183 }
184 _unlock_popen_map();
185 return popen_fp;
186 }