/src/binutils-gdb/libiberty/pex-common.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Common code for executing a program in a sub-process. |
2 | | Copyright (C) 2005-2025 Free Software Foundation, Inc. |
3 | | Written by Ian Lance Taylor <ian@airs.com>. |
4 | | |
5 | | This file is part of the libiberty library. |
6 | | Libiberty is free software; you can redistribute it and/or |
7 | | modify it under the terms of the GNU Library General Public |
8 | | License as published by the Free Software Foundation; either |
9 | | version 2 of the License, or (at your option) any later version. |
10 | | |
11 | | Libiberty is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | Library General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU Library General Public |
17 | | License along with libiberty; see the file COPYING.LIB. If not, |
18 | | write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, |
19 | | Boston, MA 02110-1301, USA. */ |
20 | | |
21 | | #include "config.h" |
22 | | #include "libiberty.h" |
23 | | #include "pex-common.h" |
24 | | |
25 | | #include <stdio.h> |
26 | | #include <errno.h> |
27 | | #ifdef NEED_DECLARATION_ERRNO |
28 | | extern int errno; |
29 | | #endif |
30 | | #ifdef HAVE_STDLIB_H |
31 | | #include <stdlib.h> |
32 | | #endif |
33 | | #ifdef HAVE_STRING_H |
34 | | #include <string.h> |
35 | | #endif |
36 | | #ifdef HAVE_UNISTD_H |
37 | | #include <unistd.h> |
38 | | #endif |
39 | | |
40 | | extern int mkstemps (char *, int); |
41 | | |
42 | | /* This file contains subroutines for the program execution routines |
43 | | (pex_init, pex_run, etc.). This file is compiled on all |
44 | | systems. */ |
45 | | |
46 | | static void pex_add_remove (struct pex_obj *, const char *, int); |
47 | | static int pex_get_status_and_time (struct pex_obj *, int, const char **, |
48 | | int *); |
49 | | |
50 | | /* Initialize a pex_obj structure. */ |
51 | | |
52 | | struct pex_obj * |
53 | | pex_init_common (int flags, const char *pname, const char *tempbase, |
54 | | const struct pex_funcs *funcs) |
55 | 0 | { |
56 | 0 | struct pex_obj *obj; |
57 | |
|
58 | 0 | obj = XNEW (struct pex_obj); |
59 | 0 | obj->flags = flags; |
60 | 0 | obj->pname = pname; |
61 | 0 | obj->tempbase = tempbase; |
62 | 0 | obj->next_input = STDIN_FILE_NO; |
63 | 0 | obj->next_input_name = NULL; |
64 | 0 | obj->next_input_name_allocated = 0; |
65 | 0 | obj->stderr_pipe = -1; |
66 | 0 | obj->count = 0; |
67 | 0 | obj->children = NULL; |
68 | 0 | obj->status = NULL; |
69 | 0 | obj->time = NULL; |
70 | 0 | obj->number_waited = 0; |
71 | 0 | obj->input_file = NULL; |
72 | 0 | obj->read_output = NULL; |
73 | 0 | obj->read_err = NULL; |
74 | 0 | obj->remove_count = 0; |
75 | 0 | obj->remove = NULL; |
76 | 0 | obj->funcs = funcs; |
77 | 0 | obj->sysdep = NULL; |
78 | 0 | return obj; |
79 | 0 | } |
80 | | |
81 | | /* Add a file to be removed when we are done. */ |
82 | | |
83 | | static void |
84 | | pex_add_remove (struct pex_obj *obj, const char *name, int allocated) |
85 | 0 | { |
86 | 0 | char *add; |
87 | |
|
88 | 0 | ++obj->remove_count; |
89 | 0 | obj->remove = XRESIZEVEC (char *, obj->remove, obj->remove_count); |
90 | 0 | if (allocated) |
91 | 0 | add = (char *) name; |
92 | 0 | else |
93 | 0 | add = xstrdup (name); |
94 | 0 | obj->remove[obj->remove_count - 1] = add; |
95 | 0 | } |
96 | | |
97 | | /* Generate a temporary file name based on OBJ, FLAGS, and NAME. |
98 | | Return NULL if we were unable to reserve a temporary filename. |
99 | | |
100 | | If non-NULL, the result is either allocated with malloc, or the |
101 | | same pointer as NAME. */ |
102 | | static char * |
103 | | temp_file (struct pex_obj *obj, int flags, char *name) |
104 | 0 | { |
105 | 0 | if (name == NULL) |
106 | 0 | { |
107 | 0 | if (obj->tempbase == NULL) |
108 | 0 | { |
109 | 0 | name = make_temp_file (NULL); |
110 | 0 | } |
111 | 0 | else |
112 | 0 | { |
113 | 0 | int len = strlen (obj->tempbase); |
114 | 0 | int out; |
115 | |
|
116 | 0 | if (len >= 6 |
117 | 0 | && strcmp (obj->tempbase + len - 6, "XXXXXX") == 0) |
118 | 0 | name = xstrdup (obj->tempbase); |
119 | 0 | else |
120 | 0 | name = concat (obj->tempbase, "XXXXXX", NULL); |
121 | |
|
122 | 0 | out = mkstemps (name, 0); |
123 | 0 | if (out < 0) |
124 | 0 | { |
125 | 0 | free (name); |
126 | 0 | return NULL; |
127 | 0 | } |
128 | | |
129 | | /* This isn't obj->funcs->close because we got the |
130 | | descriptor from mkstemps, not from a function in |
131 | | obj->funcs. Calling close here is just like what |
132 | | make_temp_file does. */ |
133 | 0 | close (out); |
134 | 0 | } |
135 | 0 | } |
136 | 0 | else if ((flags & PEX_SUFFIX) != 0) |
137 | 0 | { |
138 | 0 | if (obj->tempbase == NULL) |
139 | 0 | name = make_temp_file (name); |
140 | 0 | else |
141 | 0 | name = concat (obj->tempbase, name, NULL); |
142 | 0 | } |
143 | | |
144 | 0 | return name; |
145 | 0 | } |
146 | | |
147 | | |
148 | | /* As for pex_run (), but permits the environment for the child process |
149 | | to be specified. */ |
150 | | |
151 | | const char * |
152 | | pex_run_in_environment (struct pex_obj *obj, int flags, const char *executable, |
153 | | char * const * argv, char * const * env, |
154 | | const char *orig_outname, const char *errname, |
155 | | int *err) |
156 | 0 | { |
157 | 0 | const char *errmsg; |
158 | 0 | int in, out, errdes; |
159 | 0 | char *outname; |
160 | 0 | int outname_allocated; |
161 | 0 | int p[2]; |
162 | 0 | int toclose; |
163 | 0 | pid_t pid; |
164 | |
|
165 | 0 | in = -1; |
166 | 0 | out = -1; |
167 | 0 | errdes = -1; |
168 | 0 | outname = (char *) orig_outname; |
169 | 0 | outname_allocated = 0; |
170 | | |
171 | | /* If the user called pex_input_file, close the file now. */ |
172 | 0 | if (obj->input_file) |
173 | 0 | { |
174 | 0 | if (fclose (obj->input_file) == EOF) |
175 | 0 | { |
176 | 0 | errmsg = "closing pipeline input file"; |
177 | 0 | goto error_exit; |
178 | 0 | } |
179 | 0 | obj->input_file = NULL; |
180 | 0 | } |
181 | | |
182 | | /* Set IN. */ |
183 | | |
184 | 0 | if (obj->next_input_name != NULL) |
185 | 0 | { |
186 | | /* We have to make sure that the previous process has completed |
187 | | before we try to read the file. */ |
188 | 0 | if (!pex_get_status_and_time (obj, 0, &errmsg, err)) |
189 | 0 | goto error_exit; |
190 | | |
191 | 0 | in = obj->funcs->open_read (obj, obj->next_input_name, |
192 | 0 | (flags & PEX_BINARY_INPUT) != 0); |
193 | 0 | if (in < 0) |
194 | 0 | { |
195 | 0 | *err = errno; |
196 | 0 | errmsg = "open temporary file"; |
197 | 0 | goto error_exit; |
198 | 0 | } |
199 | 0 | if (obj->next_input_name_allocated) |
200 | 0 | { |
201 | 0 | free (obj->next_input_name); |
202 | 0 | obj->next_input_name_allocated = 0; |
203 | 0 | } |
204 | 0 | obj->next_input_name = NULL; |
205 | 0 | } |
206 | 0 | else |
207 | 0 | { |
208 | 0 | in = obj->next_input; |
209 | 0 | if (in < 0) |
210 | 0 | { |
211 | 0 | *err = 0; |
212 | 0 | errmsg = "pipeline already complete"; |
213 | 0 | goto error_exit; |
214 | 0 | } |
215 | 0 | } |
216 | | |
217 | | /* Set OUT and OBJ->NEXT_INPUT/OBJ->NEXT_INPUT_NAME. */ |
218 | | |
219 | 0 | if ((flags & PEX_LAST) != 0) |
220 | 0 | { |
221 | 0 | if (outname == NULL) |
222 | 0 | out = STDOUT_FILE_NO; |
223 | 0 | else if ((flags & PEX_SUFFIX) != 0) |
224 | 0 | { |
225 | 0 | outname = concat (obj->tempbase, outname, NULL); |
226 | 0 | outname_allocated = 1; |
227 | 0 | } |
228 | 0 | obj->next_input = -1; |
229 | 0 | } |
230 | 0 | else if ((obj->flags & PEX_USE_PIPES) == 0) |
231 | 0 | { |
232 | 0 | outname = temp_file (obj, flags, outname); |
233 | 0 | if (! outname) |
234 | 0 | { |
235 | 0 | *err = 0; |
236 | 0 | errmsg = "could not create temporary file"; |
237 | 0 | goto error_exit; |
238 | 0 | } |
239 | | |
240 | 0 | if (outname != orig_outname) |
241 | 0 | outname_allocated = 1; |
242 | |
|
243 | 0 | if ((obj->flags & PEX_SAVE_TEMPS) == 0) |
244 | 0 | { |
245 | 0 | pex_add_remove (obj, outname, outname_allocated); |
246 | 0 | outname_allocated = 0; |
247 | 0 | } |
248 | | |
249 | | /* Hand off ownership of outname to the next stage. */ |
250 | 0 | obj->next_input_name = outname; |
251 | 0 | obj->next_input_name_allocated = outname_allocated; |
252 | 0 | outname_allocated = 0; |
253 | 0 | } |
254 | 0 | else |
255 | 0 | { |
256 | 0 | if (obj->funcs->pipe (obj, p, (flags & PEX_BINARY_OUTPUT) != 0) < 0) |
257 | 0 | { |
258 | 0 | *err = errno; |
259 | 0 | errmsg = "pipe"; |
260 | 0 | goto error_exit; |
261 | 0 | } |
262 | | |
263 | 0 | out = p[WRITE_PORT]; |
264 | 0 | obj->next_input = p[READ_PORT]; |
265 | 0 | } |
266 | | |
267 | 0 | if (out < 0) |
268 | 0 | { |
269 | 0 | out = obj->funcs->open_write (obj, outname, |
270 | 0 | (flags & PEX_BINARY_OUTPUT) != 0, |
271 | 0 | (flags & PEX_STDOUT_APPEND) != 0); |
272 | 0 | if (out < 0) |
273 | 0 | { |
274 | 0 | *err = errno; |
275 | 0 | errmsg = "open temporary output file"; |
276 | 0 | goto error_exit; |
277 | 0 | } |
278 | 0 | } |
279 | | |
280 | 0 | if (outname_allocated) |
281 | 0 | { |
282 | 0 | free (outname); |
283 | 0 | outname_allocated = 0; |
284 | 0 | } |
285 | | |
286 | | /* Set ERRDES. */ |
287 | |
|
288 | 0 | if (errname != NULL && (flags & PEX_STDERR_TO_PIPE) != 0) |
289 | 0 | { |
290 | 0 | *err = 0; |
291 | 0 | errmsg = "both ERRNAME and PEX_STDERR_TO_PIPE specified."; |
292 | 0 | goto error_exit; |
293 | 0 | } |
294 | | |
295 | 0 | if (obj->stderr_pipe != -1) |
296 | 0 | { |
297 | 0 | *err = 0; |
298 | 0 | errmsg = "PEX_STDERR_TO_PIPE used in the middle of pipeline"; |
299 | 0 | goto error_exit; |
300 | 0 | } |
301 | | |
302 | 0 | if (errname == NULL) |
303 | 0 | { |
304 | 0 | if (flags & PEX_STDERR_TO_PIPE) |
305 | 0 | { |
306 | 0 | if (obj->funcs->pipe (obj, p, (flags & PEX_BINARY_ERROR) != 0) < 0) |
307 | 0 | { |
308 | 0 | *err = errno; |
309 | 0 | errmsg = "pipe"; |
310 | 0 | goto error_exit; |
311 | 0 | } |
312 | | |
313 | 0 | errdes = p[WRITE_PORT]; |
314 | 0 | obj->stderr_pipe = p[READ_PORT]; |
315 | 0 | } |
316 | 0 | else |
317 | 0 | { |
318 | 0 | errdes = STDERR_FILE_NO; |
319 | 0 | } |
320 | 0 | } |
321 | 0 | else |
322 | 0 | { |
323 | 0 | errdes = obj->funcs->open_write (obj, errname, |
324 | 0 | (flags & PEX_BINARY_ERROR) != 0, |
325 | 0 | (flags & PEX_STDERR_APPEND) != 0); |
326 | 0 | if (errdes < 0) |
327 | 0 | { |
328 | 0 | *err = errno; |
329 | 0 | errmsg = "open error file"; |
330 | 0 | goto error_exit; |
331 | 0 | } |
332 | 0 | } |
333 | | |
334 | | /* If we are using pipes, the child process has to close the next |
335 | | input pipe. */ |
336 | | |
337 | 0 | if ((obj->flags & PEX_USE_PIPES) == 0) |
338 | 0 | toclose = -1; |
339 | 0 | else |
340 | 0 | toclose = obj->next_input; |
341 | | |
342 | | /* Run the program. */ |
343 | |
|
344 | 0 | pid = obj->funcs->exec_child (obj, flags, executable, argv, env, |
345 | 0 | in, out, errdes, toclose, &errmsg, err); |
346 | 0 | if (pid < 0) |
347 | 0 | goto error_exit; |
348 | | |
349 | 0 | ++obj->count; |
350 | 0 | obj->children = XRESIZEVEC (pid_t, obj->children, obj->count); |
351 | 0 | obj->children[obj->count - 1] = pid; |
352 | |
|
353 | 0 | return NULL; |
354 | | |
355 | 0 | error_exit: |
356 | 0 | if (in >= 0 && in != STDIN_FILE_NO) |
357 | 0 | obj->funcs->close (obj, in); |
358 | 0 | if (out >= 0 && out != STDOUT_FILE_NO) |
359 | 0 | obj->funcs->close (obj, out); |
360 | 0 | if (errdes >= 0 && errdes != STDERR_FILE_NO) |
361 | 0 | obj->funcs->close (obj, errdes); |
362 | 0 | if (outname_allocated) |
363 | 0 | free (outname); |
364 | 0 | return errmsg; |
365 | 0 | } |
366 | | |
367 | | /* Run a program. */ |
368 | | |
369 | | const char * |
370 | | pex_run (struct pex_obj *obj, int flags, const char *executable, |
371 | | char * const * argv, const char *orig_outname, const char *errname, |
372 | | int *err) |
373 | 0 | { |
374 | 0 | return pex_run_in_environment (obj, flags, executable, argv, NULL, |
375 | 0 | orig_outname, errname, err); |
376 | 0 | } |
377 | | |
378 | | /* Return a FILE pointer for a temporary file to fill with input for |
379 | | the pipeline. */ |
380 | | FILE * |
381 | | pex_input_file (struct pex_obj *obj, int flags, const char *in_name) |
382 | 0 | { |
383 | 0 | char *name = (char *) in_name; |
384 | 0 | FILE *f; |
385 | | |
386 | | /* This must be called before the first pipeline stage is run, and |
387 | | there must not have been any other input selected. */ |
388 | 0 | if (obj->count != 0 |
389 | 0 | || (obj->next_input >= 0 && obj->next_input != STDIN_FILE_NO) |
390 | 0 | || obj->next_input_name) |
391 | 0 | { |
392 | 0 | errno = EINVAL; |
393 | 0 | return NULL; |
394 | 0 | } |
395 | | |
396 | 0 | name = temp_file (obj, flags, name); |
397 | 0 | if (! name) |
398 | 0 | return NULL; |
399 | | |
400 | 0 | f = fopen (name, (flags & PEX_BINARY_OUTPUT) ? "wb" : "w"); |
401 | 0 | if (! f) |
402 | 0 | { |
403 | 0 | free (name); |
404 | 0 | return NULL; |
405 | 0 | } |
406 | | |
407 | 0 | obj->input_file = f; |
408 | 0 | obj->next_input_name = name; |
409 | 0 | obj->next_input_name_allocated = (name != in_name); |
410 | |
|
411 | 0 | return f; |
412 | 0 | } |
413 | | |
414 | | /* Return a stream for a pipe connected to the standard input of the |
415 | | first stage of the pipeline. */ |
416 | | FILE * |
417 | | pex_input_pipe (struct pex_obj *obj, int binary) |
418 | 0 | { |
419 | 0 | int p[2]; |
420 | 0 | FILE *f; |
421 | | |
422 | | /* You must call pex_input_pipe before the first pex_run or pex_one. */ |
423 | 0 | if (obj->count > 0) |
424 | 0 | goto usage_error; |
425 | | |
426 | | /* You must be using pipes. Implementations that don't support |
427 | | pipes clear this flag before calling pex_init_common. */ |
428 | 0 | if (! (obj->flags & PEX_USE_PIPES)) |
429 | 0 | goto usage_error; |
430 | | |
431 | | /* If we have somehow already selected other input, that's a |
432 | | mistake. */ |
433 | 0 | if ((obj->next_input >= 0 && obj->next_input != STDIN_FILE_NO) |
434 | 0 | || obj->next_input_name) |
435 | 0 | goto usage_error; |
436 | | |
437 | 0 | if (obj->funcs->pipe (obj, p, binary != 0) < 0) |
438 | 0 | return NULL; |
439 | | |
440 | 0 | f = obj->funcs->fdopenw (obj, p[WRITE_PORT], binary != 0); |
441 | 0 | if (! f) |
442 | 0 | { |
443 | 0 | int saved_errno = errno; |
444 | 0 | obj->funcs->close (obj, p[READ_PORT]); |
445 | 0 | obj->funcs->close (obj, p[WRITE_PORT]); |
446 | 0 | errno = saved_errno; |
447 | 0 | return NULL; |
448 | 0 | } |
449 | | |
450 | 0 | obj->next_input = p[READ_PORT]; |
451 | |
|
452 | 0 | return f; |
453 | | |
454 | 0 | usage_error: |
455 | 0 | errno = EINVAL; |
456 | 0 | return NULL; |
457 | 0 | } |
458 | | |
459 | | /* Return a FILE pointer for the output of the last program |
460 | | executed. */ |
461 | | |
462 | | FILE * |
463 | | pex_read_output (struct pex_obj *obj, int binary) |
464 | 0 | { |
465 | 0 | if (obj->next_input_name != NULL) |
466 | 0 | { |
467 | 0 | const char *errmsg; |
468 | 0 | int err; |
469 | | |
470 | | /* We have to make sure that the process has completed before we |
471 | | try to read the file. */ |
472 | 0 | if (!pex_get_status_and_time (obj, 0, &errmsg, &err)) |
473 | 0 | { |
474 | 0 | errno = err; |
475 | 0 | return NULL; |
476 | 0 | } |
477 | | |
478 | 0 | obj->read_output = fopen (obj->next_input_name, binary ? "rb" : "r"); |
479 | |
|
480 | 0 | if (obj->next_input_name_allocated) |
481 | 0 | { |
482 | 0 | free (obj->next_input_name); |
483 | 0 | obj->next_input_name_allocated = 0; |
484 | 0 | } |
485 | 0 | obj->next_input_name = NULL; |
486 | 0 | } |
487 | 0 | else |
488 | 0 | { |
489 | 0 | int o; |
490 | |
|
491 | 0 | o = obj->next_input; |
492 | 0 | if (o < 0 || o == STDIN_FILE_NO) |
493 | 0 | return NULL; |
494 | 0 | obj->read_output = obj->funcs->fdopenr (obj, o, binary); |
495 | 0 | obj->next_input = -1; |
496 | 0 | } |
497 | | |
498 | 0 | return obj->read_output; |
499 | 0 | } |
500 | | |
501 | | FILE * |
502 | | pex_read_err (struct pex_obj *obj, int binary) |
503 | 0 | { |
504 | 0 | int o; |
505 | | |
506 | 0 | o = obj->stderr_pipe; |
507 | 0 | if (o < 0 || o == STDIN_FILE_NO) |
508 | 0 | return NULL; |
509 | 0 | obj->read_err = obj->funcs->fdopenr (obj, o, binary); |
510 | 0 | obj->stderr_pipe = -1; |
511 | 0 | return obj->read_err; |
512 | 0 | } |
513 | | |
514 | | /* Get the exit status and, if requested, the resource time for all |
515 | | the child processes. Return 0 on failure, 1 on success. */ |
516 | | |
517 | | static int |
518 | | pex_get_status_and_time (struct pex_obj *obj, int done, const char **errmsg, |
519 | | int *err) |
520 | 0 | { |
521 | 0 | int ret; |
522 | 0 | int i; |
523 | |
|
524 | 0 | if (obj->number_waited == obj->count) |
525 | 0 | return 1; |
526 | | |
527 | 0 | obj->status = XRESIZEVEC (int, obj->status, obj->count); |
528 | 0 | if ((obj->flags & PEX_RECORD_TIMES) != 0) |
529 | 0 | obj->time = XRESIZEVEC (struct pex_time, obj->time, obj->count); |
530 | |
|
531 | 0 | ret = 1; |
532 | 0 | for (i = obj->number_waited; i < obj->count; ++i) |
533 | 0 | { |
534 | 0 | if (obj->funcs->wait (obj, obj->children[i], &obj->status[i], |
535 | 0 | obj->time == NULL ? NULL : &obj->time[i], |
536 | 0 | done, errmsg, err) < 0) |
537 | 0 | ret = 0; |
538 | 0 | } |
539 | 0 | obj->number_waited = i; |
540 | |
|
541 | 0 | return ret; |
542 | 0 | } |
543 | | |
544 | | /* Get exit status of executed programs. */ |
545 | | |
546 | | int |
547 | | pex_get_status (struct pex_obj *obj, int count, int *vector) |
548 | 0 | { |
549 | 0 | if (obj->status == NULL) |
550 | 0 | { |
551 | 0 | const char *errmsg; |
552 | 0 | int err; |
553 | |
|
554 | 0 | if (!pex_get_status_and_time (obj, 0, &errmsg, &err)) |
555 | 0 | return 0; |
556 | 0 | } |
557 | | |
558 | 0 | if (count > obj->count) |
559 | 0 | { |
560 | 0 | memset (vector + obj->count, 0, (count - obj->count) * sizeof (int)); |
561 | 0 | count = obj->count; |
562 | 0 | } |
563 | |
|
564 | 0 | memcpy (vector, obj->status, count * sizeof (int)); |
565 | |
|
566 | 0 | return 1; |
567 | 0 | } |
568 | | |
569 | | /* Get process times of executed programs. */ |
570 | | |
571 | | int |
572 | | pex_get_times (struct pex_obj *obj, int count, struct pex_time *vector) |
573 | 0 | { |
574 | 0 | if (obj->status == NULL) |
575 | 0 | { |
576 | 0 | const char *errmsg; |
577 | 0 | int err; |
578 | |
|
579 | 0 | if (!pex_get_status_and_time (obj, 0, &errmsg, &err)) |
580 | 0 | return 0; |
581 | 0 | } |
582 | | |
583 | 0 | if (obj->time == NULL) |
584 | 0 | return 0; |
585 | | |
586 | 0 | if (count > obj->count) |
587 | 0 | { |
588 | 0 | memset (vector + obj->count, 0, |
589 | 0 | (count - obj->count) * sizeof (struct pex_time)); |
590 | 0 | count = obj->count; |
591 | 0 | } |
592 | |
|
593 | 0 | memcpy (vector, obj->time, count * sizeof (struct pex_time)); |
594 | |
|
595 | 0 | return 1; |
596 | 0 | } |
597 | | |
598 | | /* Free a pex_obj structure. */ |
599 | | |
600 | | void |
601 | | pex_free (struct pex_obj *obj) |
602 | 0 | { |
603 | | /* Close pipe file descriptors corresponding to child's stdout and |
604 | | stderr so that the child does not hang trying to output something |
605 | | while we're waiting for it. */ |
606 | 0 | if (obj->next_input >= 0 && obj->next_input != STDIN_FILE_NO) |
607 | 0 | obj->funcs->close (obj, obj->next_input); |
608 | 0 | if (obj->stderr_pipe >= 0 && obj->stderr_pipe != STDIN_FILE_NO) |
609 | 0 | obj->funcs->close (obj, obj->stderr_pipe); |
610 | 0 | if (obj->read_output != NULL) |
611 | 0 | fclose (obj->read_output); |
612 | 0 | if (obj->read_err != NULL) |
613 | 0 | fclose (obj->read_err); |
614 | | |
615 | | /* If the caller forgot to wait for the children, we do it here, to |
616 | | avoid zombies. */ |
617 | 0 | if (obj->status == NULL) |
618 | 0 | { |
619 | 0 | const char *errmsg; |
620 | 0 | int err; |
621 | |
|
622 | 0 | obj->flags &= ~ PEX_RECORD_TIMES; |
623 | 0 | pex_get_status_and_time (obj, 1, &errmsg, &err); |
624 | 0 | } |
625 | |
|
626 | 0 | if (obj->next_input_name_allocated) |
627 | 0 | free (obj->next_input_name); |
628 | 0 | free (obj->children); |
629 | 0 | free (obj->status); |
630 | 0 | free (obj->time); |
631 | |
|
632 | 0 | if (obj->remove_count > 0) |
633 | 0 | { |
634 | 0 | int i; |
635 | |
|
636 | 0 | for (i = 0; i < obj->remove_count; ++i) |
637 | 0 | { |
638 | 0 | remove (obj->remove[i]); |
639 | 0 | free (obj->remove[i]); |
640 | 0 | } |
641 | 0 | free (obj->remove); |
642 | 0 | } |
643 | |
|
644 | 0 | if (obj->funcs->cleanup != NULL) |
645 | 0 | obj->funcs->cleanup (obj); |
646 | |
|
647 | 0 | free (obj); |
648 | 0 | } |