1 #include "cado.h" // IWYU pragma: keep
2 // IWYU pragma: no_include <bits/types/struct_rusage.h>
3 #include <stdio.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <stdlib.h>
8 #include <sys/time.h> // IWYU pragma: keep
9 #include <sys/wait.h> // IWYU pragma: keep
10 #ifdef HAVE_GETRUSAGE
11 #include <sys/resource.h> // IWYU pragma: keep
12 #endif
13 #include <pthread.h>
14
15 #include "macros.h" // MAYBE_UNUSED
16 #include "cado_popen.h"
17
18 /* We need to close file descriptors underlying other popen()-ed calls on
19 * the children processes. Not the underlying streams though, since
20 * closing is done in userland, and only once.
21 */
22 static struct {
23 pthread_mutex_t m[1];
24 int n;
25 struct { int fd; pid_t kid; } p[1024];
26 } popenlist[1] = {{{PTHREAD_MUTEX_INITIALIZER}, 0, {{0,0},}}};
27
cado_fd_popen(const char * command,const char * mode)28 int cado_fd_popen(const char * command, const char * mode)
29 {
30 /* Is this a pipe for reading or for writing ? */
31 int imode = -1; /* 0: reading; 1: writing */
32 if (strcmp(mode, "r") == 0) {
33 imode = 0;
34 } else if (strcmp(mode, "rb") == 0) {
35 imode = 0;
36 } else if (strcmp(mode, "w") == 0) {
37 imode = 1;
38 } else if (strcmp(mode, "wb") == 0) {
39 imode = 1;
40 } else {
41 fprintf(stderr, "Please fix %s\n", __func__);
42 abort();
43 }
44 int pipefd[2];
45 if (pipe(pipefd) < 0) {
46 perror("pipe");
47 return -1;
48 }
49 /* pipefd[0] is the read end, pipefd[1] is the write end */
50 pthread_mutex_lock(popenlist->m);
51
52 popenlist->p[popenlist->n].fd = pipefd[imode];
53 pid_t * kid = &(popenlist->p[popenlist->n].kid);
54 popenlist->n++;
55
56 pid_t child = fork();
57 if (child < 0) {
58 pthread_mutex_unlock(popenlist->m);
59 perror("fork");
60 return -1;
61 }
62 if (child) {
63 /* I'm the father. I only want to use pipefd[imode]. */
64 close(pipefd[!imode]);
65 *kid = child;
66 pthread_mutex_unlock(popenlist->m);
67 /*
68 fprintf(stderr, "%s child %d (%s) through fd %d\n",
69 imode ? "Writing to" : "Reading from",
70 child, command, pipefd[imode]);
71 */
72 return pipefd[imode];
73 } else {
74 /* if father wants to read, we close our standard input
75 * (0==imode), and bind our standard output (1==!imode) to the
76 * other end. */
77 /* We still have the lock here. We don't care much about
78 * releasing it, since we're going to do exec() anyway */
79 close(imode);
80 close(pipefd[imode]);
81 dup2(pipefd[!imode], !imode);
82 for(int i = 0 ; i < popenlist->n - 1 ; i++) {
83 int fd = popenlist->p[i].fd;
84 int kid = popenlist->p[i].kid;
85 int rc = close(fd);
86 if (rc < 0) {
87 fprintf(stderr, "Process %d closing fd %d (%s pid %d)"
88 ": %s\n",
89 getpid(), fd,
90 imode ? "->" : "<-", kid, strerror(errno));
91 }
92 }
93 popenlist->n = 0; /* who cares, we're exec()'ing anyway */
94 /*
95 fprintf(stderr, "Child process %d (parent %d) executing %s\n",
96 getpid(),
97 getppid(),
98 command);
99 */
100 execl("/bin/sh", "sh", "-c", command, NULL);
101 perror("execl() failed");
102 fprintf (stderr, "execl command size is %zu\n",
103 strlen (command) + strlen ("sh -c "));
104 exit(EXIT_FAILURE);
105 }
106 return -1;
107 }
108
cado_popen(const char * command,const char * mode)109 FILE * cado_popen(const char * command, const char * mode)
110 {
111 int fd = cado_fd_popen(command, mode);
112 if (fd < 0) return NULL;
113 return fdopen(fd, mode);
114 }
115
116 /* remove the fd from our list of popen'ed files, and return the kid id.
117 */
reap_fd(int fd)118 static int reap_fd(int fd)
119 {
120 pid_t kid = 0;
121 pthread_mutex_lock(popenlist->m);
122 int nn = 0;
123 for(int i = 0 ; i < popenlist->n ; i++) {
124 popenlist->p[nn] = popenlist->p[i];
125 if (popenlist->p[i].fd == fd) {
126 if (kid) {
127 fprintf(stderr, "Error: two or more child processes (%d and %d) hold a reference to fd %d\n", kid, popenlist->p[i].kid, fd);
128 abort();
129 }
130 ASSERT_ALWAYS(kid == 0);
131 kid = popenlist->p[i].kid;
132 } else {
133 nn++;
134 }
135 }
136 popenlist->n = nn;
137 pthread_mutex_unlock(popenlist->m);
138 return kid;
139 }
140
141 #ifdef HAVE_GETRUSAGE
cado_fd_pclose2(int fd,struct rusage * nr)142 int cado_fd_pclose2(int fd, struct rusage * nr)
143 #else
144 int cado_fd_pclose2(int fd, void * nr MAYBE_UNUSED)
145 #endif
146 {
147 int kid = reap_fd(fd);
148 /* close the kid's fd, which will trigger its termination -- at least
149 * if it's reading from it. If it's writing to it, at this point we
150 * expect the child's source to have been consumed, and thus the
151 * write end of the pipe to have been closed already */
152 close(fd);
153 /* we must wait() for the kid now */
154 int status, error;
155 #ifdef HAVE_GETRUSAGE
156 struct rusage r[1];
157 error = wait4(kid, &status, 0, r);
158 #else
159 /* if we have no getrusage(), we probably don't have wait4 either */
160 error = waitpid(kid, &status, 0);
161 #endif
162 if (error == -1) {
163 return -1;
164 }
165 char long_status[80] = {'\0'};
166
167 if (WIFEXITED(status)) {
168 snprintf(long_status, sizeof(long_status),
169 "exited with code %d", WEXITSTATUS(status));
170 } else if (WIFSIGNALED(status)) {
171 snprintf(long_status, sizeof(long_status),
172 "terminated by signal %d%s",
173 WEXITSTATUS(status), WCOREDUMP(status) ? ", with core" : "");
174 } else {
175 snprintf(long_status, sizeof(long_status), "[weird status %d]", status);
176 }
177
178 /*
179 double u = r->ru_utime.tv_sec + (r->ru_utime.tv_usec / 1.0e6);
180 double s = r->ru_stime.tv_sec + (r->ru_stime.tv_usec / 1.0e6);
181 fprintf(stderr, "Child process %d %s, having spent %.2fs+%.2fs on cpu\n",
182 kid, long_status, u, s);
183 */
184 #ifdef HAVE_GETRUSAGE
185 if (nr) memcpy(nr, r, sizeof(struct rusage));
186 #endif
187 /* Linux man page:
188 "returns the exit status of the command as returned by wait4(2)"
189 */
190 return status;
191 }
192
193 #ifdef HAVE_GETRUSAGE
cado_pclose2(FILE * stream,struct rusage * nr)194 int cado_pclose2(FILE * stream, struct rusage * nr)
195 #else
196 int cado_pclose2(FILE * stream, void * nr MAYBE_UNUSED)
197 #endif
198 {
199 int fd = fileno(stream);
200 fclose(stream);
201 return cado_fd_pclose2(fd, nr);
202 }
203
204