1 /* Pexecute test program,
2    Copyright (C) 2005-2019 Free Software Foundation, Inc.
3    Written by Ian Lance Taylor <ian@airs.com>.
4 
5    This file is part of GNU libiberty.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    This program 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 General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "ansidecl.h"
26 #include "libiberty.h"
27 #include <stdio.h>
28 #include <signal.h>
29 #include <errno.h>
30 #ifdef HAVE_STRING_H
31 #include <string.h>
32 #endif
33 #include <sys/types.h>
34 #ifdef HAVE_STDLIB_H
35 #include <stdlib.h>
36 #endif
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif
40 #ifdef HAVE_SYS_WAIT_H
41 #include <sys/wait.h>
42 #endif
43 #ifdef HAVE_SYS_TIME_H
44 #include <sys/time.h>
45 #endif
46 #ifdef HAVE_SYS_RESOURCE_H
47 #include <sys/resource.h>
48 #endif
49 
50 #ifndef WIFSIGNALED
51 #define WIFSIGNALED(S) (((S) & 0xff) != 0 && ((S) & 0xff) != 0x7f)
52 #endif
53 #ifndef WTERMSIG
54 #define WTERMSIG(S) ((S) & 0x7f)
55 #endif
56 #ifndef WIFEXITED
57 #define WIFEXITED(S) (((S) & 0xff) == 0)
58 #endif
59 #ifndef WEXITSTATUS
60 #define WEXITSTATUS(S) (((S) & 0xff00) >> 8)
61 #endif
62 #ifndef WSTOPSIG
63 #define WSTOPSIG WEXITSTATUS
64 #endif
65 #ifndef WCOREDUMP
66 #define WCOREDUMP(S) ((S) & WCOREFLG)
67 #endif
68 #ifndef WCOREFLG
69 #define WCOREFLG 0200
70 #endif
71 
72 #ifndef EXIT_SUCCESS
73 #define EXIT_SUCCESS 0
74 #endif
75 
76 #ifndef EXIT_FAILURE
77 #define EXIT_FAILURE 1
78 #endif
79 
80 /* When this program is run with no arguments, it runs some tests of
81    the libiberty pexecute functions.  As a test program, it simply
82    invokes itself with various arguments.
83 
84    argv[1]:
85      *empty string*      Run tests, exit with success status
86      exit                Exit success
87      error               Exit error
88      abort               Abort
89      echo                Echo remaining arguments, exit success
90      echoerr             Echo next arg to stdout, next to stderr, repeat
91      copy                Copy stdin to stdout
92      write               Write stdin to file named in next argument
93 */
94 
95 static void fatal_error (int, const char *, int) ATTRIBUTE_NORETURN;
96 static void error (int, const char *);
97 static void check_line (int, FILE *, const char *);
98 static void do_cmd (int, char **) ATTRIBUTE_NORETURN;
99 
100 /* The number of errors we have seen.  */
101 
102 static int error_count;
103 
104 /* Print a fatal error and exit.  LINE is the line number where we
105    detected the error, ERRMSG is the error message to print, and ERR
106    is 0 or an errno value to print.  */
107 
108 static void
fatal_error(int line,const char * errmsg,int err)109 fatal_error (int line, const char *errmsg, int err)
110 {
111   fprintf (stderr, "test-pexecute:%d: %s", line, errmsg);
112   if (errno != 0)
113     fprintf (stderr, ": %s", xstrerror (err));
114   fprintf (stderr, "\n");
115   exit (EXIT_FAILURE);
116 }
117 
118 #define FATAL_ERROR(ERRMSG, ERR) fatal_error (__LINE__, ERRMSG, ERR)
119 
120 /* Print an error message and bump the error count.  LINE is the line
121    number where we detected the error, ERRMSG is the error to
122    print.  */
123 
124 static void
error(int line,const char * errmsg)125 error (int line, const char *errmsg)
126 {
127   fprintf (stderr, "test-pexecute:%d: %s\n", line, errmsg);
128   ++error_count;
129 }
130 
131 #define ERROR(ERRMSG) error (__LINE__, ERRMSG)
132 
133 /* Check a line in a file.  */
134 
135 static void
check_line(int line,FILE * e,const char * str)136 check_line (int line, FILE *e, const char *str)
137 {
138   const char *p;
139   int c;
140   char buf[1000];
141 
142   p = str;
143   while (1)
144     {
145       c = getc (e);
146 
147       if (*p == '\0')
148 	{
149 	  if (c != '\n')
150 	    {
151 	      snprintf (buf, sizeof buf, "got '%c' when expecting newline", c);
152 	      fatal_error (line, buf, 0);
153 	    }
154 	  c = getc (e);
155 	  if (c != EOF)
156 	    {
157 	      snprintf (buf, sizeof buf, "got '%c' when expecting EOF", c);
158 	      fatal_error (line, buf, 0);
159 	    }
160 	  return;
161 	}
162 
163       if (c != *p)
164 	{
165 	  snprintf (buf, sizeof buf, "expected '%c', got '%c'", *p, c);
166 	  fatal_error (line, buf, 0);
167 	}
168 
169       ++p;
170     }
171 }
172 
173 #define CHECK_LINE(E, STR) check_line (__LINE__, E, STR)
174 
175 /* Main function for the pexecute tester.  Run the tests.  */
176 
177 int
main(int argc,char ** argv)178 main (int argc, char **argv)
179 {
180   int trace;
181   struct pex_obj *test_pex_tmp;
182   int test_pex_status;
183   FILE *test_pex_file;
184   struct pex_obj *pex1;
185   char *subargv[10];
186   int status;
187   FILE *e;
188   int statuses[10];
189 
190   trace = 0;
191   if (argc > 1 && strcmp (argv[1], "-t") == 0)
192     {
193       trace = 1;
194       --argc;
195       ++argv;
196     }
197 
198   if (argc > 1)
199     do_cmd (argc, argv);
200 
201 #define TEST_PEX_INIT(FLAGS, TEMPBASE)					\
202   (((test_pex_tmp = pex_init (FLAGS, "test-pexecute", TEMPBASE))	\
203     != NULL)								\
204    ? test_pex_tmp							\
205    : (FATAL_ERROR ("pex_init failed", 0), NULL))
206 
207 #define TEST_PEX_RUN(PEXOBJ, FLAGS, EXECUTABLE, ARGV, OUTNAME, ERRNAME)	\
208   do									\
209     {									\
210       int err;								\
211       const char *pex_run_err;						\
212       if (trace)							\
213 	fprintf (stderr, "Line %d: running %s %s\n",			\
214 		 __LINE__, EXECUTABLE, ARGV[0]);			\
215       pex_run_err = pex_run (PEXOBJ, FLAGS, EXECUTABLE, ARGV, OUTNAME,	\
216 			     ERRNAME, &err);				\
217       if (pex_run_err != NULL)						\
218 	FATAL_ERROR (pex_run_err, err);					\
219     }									\
220   while (0)
221 
222 #define TEST_PEX_GET_STATUS_1(PEXOBJ)					\
223   (pex_get_status (PEXOBJ, 1, &test_pex_status)				\
224    ? test_pex_status							\
225    : (FATAL_ERROR ("pex_get_status failed", errno), 1))
226 
227 #define TEST_PEX_GET_STATUS(PEXOBJ, COUNT, VECTOR)			\
228   do									\
229     {									\
230       if (!pex_get_status (PEXOBJ, COUNT, VECTOR))			\
231 	FATAL_ERROR ("pex_get_status failed", errno);			\
232     }									\
233   while (0)
234 
235 #define TEST_PEX_READ_OUTPUT(PEXOBJ)					\
236   ((test_pex_file = pex_read_output (PEXOBJ, 0)) != NULL		\
237    ? test_pex_file							\
238    : (FATAL_ERROR ("pex_read_output failed", errno), NULL))
239 
240   remove ("temp.x");
241   remove ("temp.y");
242 
243   memset (subargv, 0, sizeof subargv);
244 
245   subargv[0] = "./test-pexecute";
246 
247   pex1 = TEST_PEX_INIT (PEX_USE_PIPES, NULL);
248   subargv[1] = "exit";
249   subargv[2] = NULL;
250   TEST_PEX_RUN (pex1, PEX_LAST, "./test-pexecute", subargv, NULL, NULL);
251   status = TEST_PEX_GET_STATUS_1 (pex1);
252   if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS)
253     ERROR ("exit failed");
254   pex_free (pex1);
255 
256   pex1 = TEST_PEX_INIT (PEX_USE_PIPES, NULL);
257   subargv[1] = "error";
258   subargv[2] = NULL;
259   TEST_PEX_RUN (pex1, PEX_LAST, "./test-pexecute", subargv, NULL, NULL);
260   status = TEST_PEX_GET_STATUS_1 (pex1);
261   if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_FAILURE)
262     ERROR ("error test failed");
263   pex_free (pex1);
264 
265   /* We redirect stderr to a file to avoid an error message which is
266      printed on mingw32 when the child calls abort.  */
267   pex1 = TEST_PEX_INIT (PEX_USE_PIPES, NULL);
268   subargv[1] = "abort";
269   subargv[2] = NULL;
270   TEST_PEX_RUN (pex1, PEX_LAST, "./test-pexecute", subargv, NULL, "temp.z");
271   status = TEST_PEX_GET_STATUS_1 (pex1);
272   if (!WIFSIGNALED (status) || WTERMSIG (status) != SIGABRT)
273     ERROR ("abort failed");
274   pex_free (pex1);
275   remove ("temp.z");
276 
277   pex1 = TEST_PEX_INIT (PEX_USE_PIPES, "temp");
278   subargv[1] = "echo";
279   subargv[2] = "foo";
280   subargv[3] = NULL;
281   TEST_PEX_RUN (pex1, 0, "./test-pexecute", subargv, NULL, NULL);
282   e = TEST_PEX_READ_OUTPUT (pex1);
283   CHECK_LINE (e, "foo");
284   if (TEST_PEX_GET_STATUS_1 (pex1) != 0)
285     ERROR ("echo exit status failed");
286   pex_free (pex1);
287 
288   /* Check empty parameters don't get lost.  */
289   pex1 = TEST_PEX_INIT (PEX_USE_PIPES, "temp");
290   subargv[1] = "echo";
291   subargv[2] = "foo";
292   subargv[3] = "";
293   subargv[4] = "bar";
294   subargv[5] = NULL;
295   TEST_PEX_RUN (pex1, 0, "./test-pexecute", subargv, NULL, NULL);
296   e = TEST_PEX_READ_OUTPUT (pex1);
297   CHECK_LINE (e, "foo  bar");  /* Two spaces!  */
298   if (TEST_PEX_GET_STATUS_1 (pex1) != 0)
299     ERROR ("echo exit status failed");
300   pex_free (pex1);
301 
302   pex1 = TEST_PEX_INIT (PEX_USE_PIPES, "temp");
303   subargv[1] = "echo";
304   subargv[2] = "bar";
305   subargv[3] = NULL;
306   TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", NULL);
307   subargv[1] = "copy";
308   subargv[2] = NULL;
309   TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
310   e = TEST_PEX_READ_OUTPUT (pex1);
311   CHECK_LINE (e, "bar");
312   TEST_PEX_GET_STATUS (pex1, 2, statuses);
313   if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
314       || !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
315     ERROR ("copy exit status failed");
316   pex_free (pex1);
317   if (fopen ("temp.x", "r") != NULL || fopen ("temp.y", "r") != NULL)
318     ERROR ("temporary files exist");
319 
320   pex1 = TEST_PEX_INIT (0, "temp");
321   subargv[1] = "echo";
322   subargv[2] = "bar";
323   subargv[3] = NULL;
324   TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", NULL);
325   subargv[1] = "copy";
326   subargv[2] = NULL;
327   TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
328   e = TEST_PEX_READ_OUTPUT (pex1);
329   CHECK_LINE (e, "bar");
330   TEST_PEX_GET_STATUS (pex1, 2, statuses);
331   if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
332       || !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
333     ERROR ("copy exit status failed");
334   pex_free (pex1);
335   if (fopen ("temp.x", "r") != NULL || fopen ("temp.y", "r") != NULL)
336     ERROR ("temporary files exist");
337 
338   pex1 = TEST_PEX_INIT (PEX_SAVE_TEMPS, "temp");
339   subargv[1] = "echo";
340   subargv[2] = "quux";
341   subargv[3] = NULL;
342   TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", NULL);
343   subargv[1] = "copy";
344   subargv[2] = NULL;
345   TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
346   e = TEST_PEX_READ_OUTPUT (pex1);
347   CHECK_LINE (e, "quux");
348   TEST_PEX_GET_STATUS (pex1, 2, statuses);
349   if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
350       || !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
351     ERROR ("copy temp exit status failed");
352   e = fopen ("temp.x", "r");
353   if (e == NULL)
354     FATAL_ERROR ("fopen temp.x failed in copy temp", errno);
355   CHECK_LINE (e, "quux");
356   fclose (e);
357   e = fopen ("temp.y", "r");
358   if (e == NULL)
359     FATAL_ERROR ("fopen temp.y failed in copy temp", errno);
360   CHECK_LINE (e, "quux");
361   fclose (e);
362   pex_free (pex1);
363   remove ("temp.x");
364   remove ("temp.y");
365 
366   pex1 = TEST_PEX_INIT (PEX_USE_PIPES, "temp");
367   subargv[1] = "echoerr";
368   subargv[2] = "one";
369   subargv[3] = "two";
370   subargv[4] = NULL;
371   TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", "temp2.x");
372   subargv[1] = "write";
373   subargv[2] = "temp2.y";
374   subargv[3] = NULL;
375   TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
376   TEST_PEX_GET_STATUS (pex1, 2, statuses);
377   if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
378       || !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
379     ERROR ("echoerr exit status failed");
380   pex_free (pex1);
381   if (fopen ("temp.x", "r") != NULL || fopen ("temp.y", "r") != NULL)
382     ERROR ("temporary files exist");
383   e = fopen ("temp2.x", "r");
384   if (e == NULL)
385     FATAL_ERROR ("fopen temp2.x failed in echoerr", errno);
386   CHECK_LINE (e, "two");
387   fclose (e);
388   e = fopen ("temp2.y", "r");
389   if (e == NULL)
390     FATAL_ERROR ("fopen temp2.y failed in echoerr", errno);
391   CHECK_LINE (e, "one");
392   fclose (e);
393   remove ("temp2.x");
394   remove ("temp2.y");
395 
396   /* Test the old pexecute interface.  */
397   {
398     int pid1, pid2;
399     char *errmsg_fmt;
400     char *errmsg_arg;
401     char errbuf1[1000];
402     char errbuf2[1000];
403 
404     subargv[1] = "echo";
405     subargv[2] = "oldpexecute";
406     subargv[3] = NULL;
407     pid1 = pexecute ("./test-pexecute", subargv, "test-pexecute", "temp",
408 		     &errmsg_fmt, &errmsg_arg, PEXECUTE_FIRST);
409     if (pid1 < 0)
410       {
411 	snprintf (errbuf1, sizeof errbuf1, errmsg_fmt, errmsg_arg);
412 	snprintf (errbuf2, sizeof errbuf2, "pexecute 1 failed: %s", errbuf1);
413 	FATAL_ERROR (errbuf2, 0);
414       }
415 
416     subargv[1] = "write";
417     subargv[2] = "temp.y";
418     subargv[3] = NULL;
419     pid2 = pexecute ("./test-pexecute", subargv, "test-pexecute", "temp",
420 		     &errmsg_fmt, &errmsg_arg, PEXECUTE_LAST);
421     if (pid2 < 0)
422       {
423 	snprintf (errbuf1, sizeof errbuf1, errmsg_fmt, errmsg_arg);
424 	snprintf (errbuf2, sizeof errbuf2, "pexecute 2 failed: %s", errbuf1);
425 	FATAL_ERROR (errbuf2, 0);
426       }
427 
428     if (pwait (pid1, &status, 0) < 0)
429       FATAL_ERROR ("write pwait 1 failed", errno);
430     if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS)
431       ERROR ("write exit status 1 failed");
432 
433     if (pwait (pid2, &status, 0) < 0)
434       FATAL_ERROR ("write pwait 1 failed", errno);
435     if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS)
436       ERROR ("write exit status 2 failed");
437 
438     e = fopen ("temp.y", "r");
439     if (e == NULL)
440       FATAL_ERROR ("fopen temp.y failed in copy temp", errno);
441     CHECK_LINE (e, "oldpexecute");
442     fclose (e);
443 
444     remove ("temp.y");
445   }
446 
447   if (trace)
448     fprintf (stderr, "Exiting with status %d\n", error_count);
449 
450   return error_count;
451 }
452 
453 /* Execute one of the special testing commands.  */
454 
455 static void
do_cmd(int argc,char ** argv)456 do_cmd (int argc, char **argv)
457 {
458   const char *s;
459 
460   /* Try to prevent generating a core dump.  */
461 #ifdef RLIMIT_CORE
462  {
463    struct rlimit r;
464 
465    r.rlim_cur = 0;
466    r.rlim_max = 0;
467    setrlimit (RLIMIT_CORE, &r);
468  }
469 #endif
470 
471   s = argv[1];
472   if (strcmp (s, "exit") == 0)
473     exit (EXIT_SUCCESS);
474   else if (strcmp (s, "echo") == 0)
475     {
476       int i;
477 
478       for (i = 2; i < argc; ++i)
479 	{
480 	  if (i > 2)
481 	    putchar (' ');
482 	  fputs (argv[i], stdout);
483 	}
484       putchar ('\n');
485       exit (EXIT_SUCCESS);
486     }
487   else if (strcmp (s, "echoerr") == 0)
488     {
489       int i;
490 
491       for (i = 2; i < argc; ++i)
492 	{
493 	  if (i > 3)
494 	    putc (' ', (i & 1) == 0 ? stdout : stderr);
495 	  fputs (argv[i], (i & 1) == 0 ? stdout : stderr);
496 	}
497       putc ('\n', stdout);
498       putc ('\n', stderr);
499       exit (EXIT_SUCCESS);
500     }
501   else if (strcmp (s, "error") == 0)
502     exit (EXIT_FAILURE);
503   else if (strcmp (s, "abort") == 0)
504     abort ();
505   else if (strcmp (s, "copy") == 0)
506     {
507       int c;
508 
509       while ((c = getchar ()) != EOF)
510 	putchar (c);
511       exit (EXIT_SUCCESS);
512     }
513   else if (strcmp (s, "write") == 0)
514     {
515       FILE *e;
516       int c;
517 
518       e = fopen (argv[2], "w");
519       if (e == NULL)
520 	FATAL_ERROR ("fopen for write failed", errno);
521       while ((c = getchar ()) != EOF)
522 	putc (c, e);
523       if (fclose (e) != 0)
524 	FATAL_ERROR ("fclose for write failed", errno);
525       exit (EXIT_SUCCESS);
526     }
527   else
528     {
529       char buf[1000];
530 
531       snprintf (buf, sizeof buf, "unrecognized command %s", argv[1]);
532       FATAL_ERROR (buf, 0);
533     }
534 
535   exit (EXIT_FAILURE);
536 }
537