1 | /* Common code for executing a program in a sub-process. |
2 | Copyright (C) 2005-2024 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 | { |
56 | struct pex_obj *obj; |
57 | |
58 | obj = XNEW (struct pex_obj); |
59 | obj->flags = flags; |
60 | obj->pname = pname; |
61 | obj->tempbase = tempbase; |
62 | obj->next_input = STDIN_FILE_NO; |
63 | obj->next_input_name = NULL; |
64 | obj->next_input_name_allocated = 0; |
65 | obj->stderr_pipe = -1; |
66 | obj->count = 0; |
67 | obj->children = NULL; |
68 | obj->status = NULL; |
69 | obj->time = NULL; |
70 | obj->number_waited = 0; |
71 | obj->input_file = NULL; |
72 | obj->read_output = NULL; |
73 | obj->read_err = NULL; |
74 | obj->remove_count = 0; |
75 | obj->remove = NULL; |
76 | obj->funcs = funcs; |
77 | obj->sysdep = NULL; |
78 | return obj; |
79 | } |
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 | { |
86 | char *add; |
87 | |
88 | ++obj->remove_count; |
89 | obj->remove = XRESIZEVEC (char *, obj->remove, obj->remove_count); |
90 | if (allocated) |
91 | add = (char *) name; |
92 | else |
93 | add = xstrdup (name); |
94 | obj->remove[obj->remove_count - 1] = add; |
95 | } |
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 | { |
105 | if (name == NULL) |
106 | { |
107 | if (obj->tempbase == NULL) |
108 | { |
109 | name = make_temp_file (NULL); |
110 | } |
111 | else |
112 | { |
113 | int len = strlen (s: obj->tempbase); |
114 | int out; |
115 | |
116 | if (len >= 6 |
117 | && strcmp (s1: obj->tempbase + len - 6, s2: "XXXXXX" ) == 0) |
118 | name = xstrdup (obj->tempbase); |
119 | else |
120 | name = concat (obj->tempbase, "XXXXXX" , NULL); |
121 | |
122 | out = mkstemps (name, 0); |
123 | if (out < 0) |
124 | { |
125 | free (ptr: name); |
126 | return NULL; |
127 | } |
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 | close (fd: out); |
134 | } |
135 | } |
136 | else if ((flags & PEX_SUFFIX) != 0) |
137 | { |
138 | if (obj->tempbase == NULL) |
139 | name = make_temp_file (name); |
140 | else |
141 | name = concat (obj->tempbase, name, NULL); |
142 | } |
143 | |
144 | return name; |
145 | } |
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 | { |
157 | const char *errmsg; |
158 | int in, out, errdes; |
159 | char *outname; |
160 | int outname_allocated; |
161 | int p[2]; |
162 | int toclose; |
163 | pid_t pid; |
164 | |
165 | in = -1; |
166 | out = -1; |
167 | errdes = -1; |
168 | outname = (char *) orig_outname; |
169 | outname_allocated = 0; |
170 | |
171 | /* If the user called pex_input_file, close the file now. */ |
172 | if (obj->input_file) |
173 | { |
174 | if (fclose (stream: obj->input_file) == EOF) |
175 | { |
176 | errmsg = "closing pipeline input file" ; |
177 | goto error_exit; |
178 | } |
179 | obj->input_file = NULL; |
180 | } |
181 | |
182 | /* Set IN. */ |
183 | |
184 | if (obj->next_input_name != NULL) |
185 | { |
186 | /* We have to make sure that the previous process has completed |
187 | before we try to read the file. */ |
188 | if (!pex_get_status_and_time (obj, 0, &errmsg, err)) |
189 | goto error_exit; |
190 | |
191 | in = obj->funcs->open_read (obj, obj->next_input_name, |
192 | (flags & PEX_BINARY_INPUT) != 0); |
193 | if (in < 0) |
194 | { |
195 | *err = errno; |
196 | errmsg = "open temporary file" ; |
197 | goto error_exit; |
198 | } |
199 | if (obj->next_input_name_allocated) |
200 | { |
201 | free (ptr: obj->next_input_name); |
202 | obj->next_input_name_allocated = 0; |
203 | } |
204 | obj->next_input_name = NULL; |
205 | } |
206 | else |
207 | { |
208 | in = obj->next_input; |
209 | if (in < 0) |
210 | { |
211 | *err = 0; |
212 | errmsg = "pipeline already complete" ; |
213 | goto error_exit; |
214 | } |
215 | } |
216 | |
217 | /* Set OUT and OBJ->NEXT_INPUT/OBJ->NEXT_INPUT_NAME. */ |
218 | |
219 | if ((flags & PEX_LAST) != 0) |
220 | { |
221 | if (outname == NULL) |
222 | out = STDOUT_FILE_NO; |
223 | else if ((flags & PEX_SUFFIX) != 0) |
224 | { |
225 | outname = concat (obj->tempbase, outname, NULL); |
226 | outname_allocated = 1; |
227 | } |
228 | obj->next_input = -1; |
229 | } |
230 | else if ((obj->flags & PEX_USE_PIPES) == 0) |
231 | { |
232 | outname = temp_file (obj, flags, name: outname); |
233 | if (! outname) |
234 | { |
235 | *err = 0; |
236 | errmsg = "could not create temporary file" ; |
237 | goto error_exit; |
238 | } |
239 | |
240 | if (outname != orig_outname) |
241 | outname_allocated = 1; |
242 | |
243 | if ((obj->flags & PEX_SAVE_TEMPS) == 0) |
244 | { |
245 | pex_add_remove (obj, name: outname, allocated: outname_allocated); |
246 | outname_allocated = 0; |
247 | } |
248 | |
249 | /* Hand off ownership of outname to the next stage. */ |
250 | obj->next_input_name = outname; |
251 | obj->next_input_name_allocated = outname_allocated; |
252 | outname_allocated = 0; |
253 | } |
254 | else |
255 | { |
256 | if (obj->funcs->pipe (obj, p, (flags & PEX_BINARY_OUTPUT) != 0) < 0) |
257 | { |
258 | *err = errno; |
259 | errmsg = "pipe" ; |
260 | goto error_exit; |
261 | } |
262 | |
263 | out = p[WRITE_PORT]; |
264 | obj->next_input = p[READ_PORT]; |
265 | } |
266 | |
267 | if (out < 0) |
268 | { |
269 | out = obj->funcs->open_write (obj, outname, |
270 | (flags & PEX_BINARY_OUTPUT) != 0, |
271 | (flags & PEX_STDOUT_APPEND) != 0); |
272 | if (out < 0) |
273 | { |
274 | *err = errno; |
275 | errmsg = "open temporary output file" ; |
276 | goto error_exit; |
277 | } |
278 | } |
279 | |
280 | if (outname_allocated) |
281 | { |
282 | free (ptr: outname); |
283 | outname_allocated = 0; |
284 | } |
285 | |
286 | /* Set ERRDES. */ |
287 | |
288 | if (errname != NULL && (flags & PEX_STDERR_TO_PIPE) != 0) |
289 | { |
290 | *err = 0; |
291 | errmsg = "both ERRNAME and PEX_STDERR_TO_PIPE specified." ; |
292 | goto error_exit; |
293 | } |
294 | |
295 | if (obj->stderr_pipe != -1) |
296 | { |
297 | *err = 0; |
298 | errmsg = "PEX_STDERR_TO_PIPE used in the middle of pipeline" ; |
299 | goto error_exit; |
300 | } |
301 | |
302 | if (errname == NULL) |
303 | { |
304 | if (flags & PEX_STDERR_TO_PIPE) |
305 | { |
306 | if (obj->funcs->pipe (obj, p, (flags & PEX_BINARY_ERROR) != 0) < 0) |
307 | { |
308 | *err = errno; |
309 | errmsg = "pipe" ; |
310 | goto error_exit; |
311 | } |
312 | |
313 | errdes = p[WRITE_PORT]; |
314 | obj->stderr_pipe = p[READ_PORT]; |
315 | } |
316 | else |
317 | { |
318 | errdes = STDERR_FILE_NO; |
319 | } |
320 | } |
321 | else |
322 | { |
323 | errdes = obj->funcs->open_write (obj, errname, |
324 | (flags & PEX_BINARY_ERROR) != 0, |
325 | (flags & PEX_STDERR_APPEND) != 0); |
326 | if (errdes < 0) |
327 | { |
328 | *err = errno; |
329 | errmsg = "open error file" ; |
330 | goto error_exit; |
331 | } |
332 | } |
333 | |
334 | /* If we are using pipes, the child process has to close the next |
335 | input pipe. */ |
336 | |
337 | if ((obj->flags & PEX_USE_PIPES) == 0) |
338 | toclose = -1; |
339 | else |
340 | toclose = obj->next_input; |
341 | |
342 | /* Run the program. */ |
343 | |
344 | pid = obj->funcs->exec_child (obj, flags, executable, argv, env, |
345 | in, out, errdes, toclose, &errmsg, err); |
346 | if (pid < 0) |
347 | goto error_exit; |
348 | |
349 | ++obj->count; |
350 | obj->children = XRESIZEVEC (pid_t, obj->children, obj->count); |
351 | obj->children[obj->count - 1] = pid; |
352 | |
353 | return NULL; |
354 | |
355 | error_exit: |
356 | if (in >= 0 && in != STDIN_FILE_NO) |
357 | obj->funcs->close (obj, in); |
358 | if (out >= 0 && out != STDOUT_FILE_NO) |
359 | obj->funcs->close (obj, out); |
360 | if (errdes >= 0 && errdes != STDERR_FILE_NO) |
361 | obj->funcs->close (obj, errdes); |
362 | if (outname_allocated) |
363 | free (ptr: outname); |
364 | return errmsg; |
365 | } |
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 | { |
374 | return pex_run_in_environment (obj, flags, executable, argv, NULL, |
375 | orig_outname, errname, err); |
376 | } |
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 | { |
383 | char *name = (char *) in_name; |
384 | 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 | if (obj->count != 0 |
389 | || (obj->next_input >= 0 && obj->next_input != STDIN_FILE_NO) |
390 | || obj->next_input_name) |
391 | { |
392 | errno = EINVAL; |
393 | return NULL; |
394 | } |
395 | |
396 | name = temp_file (obj, flags, name); |
397 | if (! name) |
398 | return NULL; |
399 | |
400 | f = fopen (filename: name, modes: (flags & PEX_BINARY_OUTPUT) ? "wb" : "w" ); |
401 | if (! f) |
402 | { |
403 | free (ptr: name); |
404 | return NULL; |
405 | } |
406 | |
407 | obj->input_file = f; |
408 | obj->next_input_name = name; |
409 | obj->next_input_name_allocated = (name != in_name); |
410 | |
411 | return f; |
412 | } |
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 | { |
419 | int p[2]; |
420 | FILE *f; |
421 | |
422 | /* You must call pex_input_pipe before the first pex_run or pex_one. */ |
423 | if (obj->count > 0) |
424 | 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 | if (! (obj->flags & PEX_USE_PIPES)) |
429 | goto usage_error; |
430 | |
431 | /* If we have somehow already selected other input, that's a |
432 | mistake. */ |
433 | if ((obj->next_input >= 0 && obj->next_input != STDIN_FILE_NO) |
434 | || obj->next_input_name) |
435 | goto usage_error; |
436 | |
437 | if (obj->funcs->pipe (obj, p, binary != 0) < 0) |
438 | return NULL; |
439 | |
440 | f = obj->funcs->fdopenw (obj, p[WRITE_PORT], binary != 0); |
441 | if (! f) |
442 | { |
443 | int saved_errno = errno; |
444 | obj->funcs->close (obj, p[READ_PORT]); |
445 | obj->funcs->close (obj, p[WRITE_PORT]); |
446 | errno = saved_errno; |
447 | return NULL; |
448 | } |
449 | |
450 | obj->next_input = p[READ_PORT]; |
451 | |
452 | return f; |
453 | |
454 | usage_error: |
455 | errno = EINVAL; |
456 | return NULL; |
457 | } |
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 | { |
465 | if (obj->next_input_name != NULL) |
466 | { |
467 | const char *errmsg; |
468 | int err; |
469 | |
470 | /* We have to make sure that the process has completed before we |
471 | try to read the file. */ |
472 | if (!pex_get_status_and_time (obj, 0, &errmsg, &err)) |
473 | { |
474 | errno = err; |
475 | return NULL; |
476 | } |
477 | |
478 | obj->read_output = fopen (filename: obj->next_input_name, modes: binary ? "rb" : "r" ); |
479 | |
480 | if (obj->next_input_name_allocated) |
481 | { |
482 | free (ptr: obj->next_input_name); |
483 | obj->next_input_name_allocated = 0; |
484 | } |
485 | obj->next_input_name = NULL; |
486 | } |
487 | else |
488 | { |
489 | int o; |
490 | |
491 | o = obj->next_input; |
492 | if (o < 0 || o == STDIN_FILE_NO) |
493 | return NULL; |
494 | obj->read_output = obj->funcs->fdopenr (obj, o, binary); |
495 | obj->next_input = -1; |
496 | } |
497 | |
498 | return obj->read_output; |
499 | } |
500 | |
501 | FILE * |
502 | pex_read_err (struct pex_obj *obj, int binary) |
503 | { |
504 | int o; |
505 | |
506 | o = obj->stderr_pipe; |
507 | if (o < 0 || o == STDIN_FILE_NO) |
508 | return NULL; |
509 | obj->read_err = obj->funcs->fdopenr (obj, o, binary); |
510 | obj->stderr_pipe = -1; |
511 | return obj->read_err; |
512 | } |
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 | { |
521 | int ret; |
522 | int i; |
523 | |
524 | if (obj->number_waited == obj->count) |
525 | return 1; |
526 | |
527 | obj->status = XRESIZEVEC (int, obj->status, obj->count); |
528 | if ((obj->flags & PEX_RECORD_TIMES) != 0) |
529 | obj->time = XRESIZEVEC (struct pex_time, obj->time, obj->count); |
530 | |
531 | ret = 1; |
532 | for (i = obj->number_waited; i < obj->count; ++i) |
533 | { |
534 | if (obj->funcs->wait (obj, obj->children[i], &obj->status[i], |
535 | obj->time == NULL ? NULL : &obj->time[i], |
536 | done, errmsg, err) < 0) |
537 | ret = 0; |
538 | } |
539 | obj->number_waited = i; |
540 | |
541 | return ret; |
542 | } |
543 | |
544 | /* Get exit status of executed programs. */ |
545 | |
546 | int |
547 | pex_get_status (struct pex_obj *obj, int count, int *vector) |
548 | { |
549 | if (obj->status == NULL) |
550 | { |
551 | const char *errmsg; |
552 | int err; |
553 | |
554 | if (!pex_get_status_and_time (obj, done: 0, errmsg: &errmsg, err: &err)) |
555 | return 0; |
556 | } |
557 | |
558 | if (count > obj->count) |
559 | { |
560 | memset (s: vector + obj->count, c: 0, n: (count - obj->count) * sizeof (int)); |
561 | count = obj->count; |
562 | } |
563 | |
564 | memcpy (dest: vector, src: obj->status, n: count * sizeof (int)); |
565 | |
566 | return 1; |
567 | } |
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 | { |
574 | if (obj->status == NULL) |
575 | { |
576 | const char *errmsg; |
577 | int err; |
578 | |
579 | if (!pex_get_status_and_time (obj, done: 0, errmsg: &errmsg, err: &err)) |
580 | return 0; |
581 | } |
582 | |
583 | if (obj->time == NULL) |
584 | return 0; |
585 | |
586 | if (count > obj->count) |
587 | { |
588 | memset (s: vector + obj->count, c: 0, |
589 | n: (count - obj->count) * sizeof (struct pex_time)); |
590 | count = obj->count; |
591 | } |
592 | |
593 | memcpy (dest: vector, src: obj->time, n: count * sizeof (struct pex_time)); |
594 | |
595 | return 1; |
596 | } |
597 | |
598 | /* Free a pex_obj structure. */ |
599 | |
600 | void |
601 | pex_free (struct pex_obj *obj) |
602 | { |
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 | if (obj->next_input >= 0 && obj->next_input != STDIN_FILE_NO) |
607 | obj->funcs->close (obj, obj->next_input); |
608 | if (obj->stderr_pipe >= 0 && obj->stderr_pipe != STDIN_FILE_NO) |
609 | obj->funcs->close (obj, obj->stderr_pipe); |
610 | if (obj->read_output != NULL) |
611 | fclose (stream: obj->read_output); |
612 | if (obj->read_err != NULL) |
613 | fclose (stream: obj->read_err); |
614 | |
615 | /* If the caller forgot to wait for the children, we do it here, to |
616 | avoid zombies. */ |
617 | if (obj->status == NULL) |
618 | { |
619 | const char *errmsg; |
620 | int err; |
621 | |
622 | obj->flags &= ~ PEX_RECORD_TIMES; |
623 | pex_get_status_and_time (obj, done: 1, errmsg: &errmsg, err: &err); |
624 | } |
625 | |
626 | if (obj->next_input_name_allocated) |
627 | free (ptr: obj->next_input_name); |
628 | free (ptr: obj->children); |
629 | free (ptr: obj->status); |
630 | free (ptr: obj->time); |
631 | |
632 | if (obj->remove_count > 0) |
633 | { |
634 | int i; |
635 | |
636 | for (i = 0; i < obj->remove_count; ++i) |
637 | { |
638 | remove (filename: obj->remove[i]); |
639 | free (ptr: obj->remove[i]); |
640 | } |
641 | free (ptr: obj->remove); |
642 | } |
643 | |
644 | if (obj->funcs->cleanup != NULL) |
645 | obj->funcs->cleanup (obj); |
646 | |
647 | free (ptr: obj); |
648 | } |
649 | |