1/* Pexecute test program,
2   Copyright (C) 2005 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
95static void fatal_error (int, const char *, int) ATTRIBUTE_NORETURN;
96static void error (int, const char *);
97static void check_line (int, FILE *, const char *);
98static void do_cmd (int, char **) ATTRIBUTE_NORETURN;
99
100/* The number of errors we have seen.  */
101
102static 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
108static void
109fatal_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
124static void
125error (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
135static void
136check_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
177int
178main (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  pex1 = TEST_PEX_INIT (PEX_USE_PIPES, "temp");
289  subargv[1] = "echo";
290  subargv[2] = "bar";
291  subargv[3] = NULL;
292  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", NULL);
293  subargv[1] = "copy";
294  subargv[2] = NULL;
295  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
296  e = TEST_PEX_READ_OUTPUT (pex1);
297  CHECK_LINE (e, "bar");
298  TEST_PEX_GET_STATUS (pex1, 2, statuses);
299  if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
300      || !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
301    ERROR ("copy exit status failed");
302  pex_free (pex1);
303  if (fopen ("temp.x", "r") != NULL || fopen ("temp.y", "r") != NULL)
304    ERROR ("temporary files exist");
305
306  pex1 = TEST_PEX_INIT (0, "temp");
307  subargv[1] = "echo";
308  subargv[2] = "bar";
309  subargv[3] = NULL;
310  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", NULL);
311  subargv[1] = "copy";
312  subargv[2] = NULL;
313  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
314  e = TEST_PEX_READ_OUTPUT (pex1);
315  CHECK_LINE (e, "bar");
316  TEST_PEX_GET_STATUS (pex1, 2, statuses);
317  if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
318      || !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
319    ERROR ("copy exit status failed");
320  pex_free (pex1);
321  if (fopen ("temp.x", "r") != NULL || fopen ("temp.y", "r") != NULL)
322    ERROR ("temporary files exist");
323
324  pex1 = TEST_PEX_INIT (PEX_SAVE_TEMPS, "temp");
325  subargv[1] = "echo";
326  subargv[2] = "quux";
327  subargv[3] = NULL;
328  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", NULL);
329  subargv[1] = "copy";
330  subargv[2] = NULL;
331  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
332  e = TEST_PEX_READ_OUTPUT (pex1);
333  CHECK_LINE (e, "quux");
334  TEST_PEX_GET_STATUS (pex1, 2, statuses);
335  if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
336      || !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
337    ERROR ("copy temp exit status failed");
338  e = fopen ("temp.x", "r");
339  if (e == NULL)
340    FATAL_ERROR ("fopen temp.x failed in copy temp", errno);
341  CHECK_LINE (e, "quux");
342  fclose (e);
343  e = fopen ("temp.y", "r");
344  if (e == NULL)
345    FATAL_ERROR ("fopen temp.y failed in copy temp", errno);
346  CHECK_LINE (e, "quux");
347  fclose (e);
348  pex_free (pex1);
349  remove ("temp.x");
350  remove ("temp.y");
351
352  pex1 = TEST_PEX_INIT (PEX_USE_PIPES, "temp");
353  subargv[1] = "echoerr";
354  subargv[2] = "one";
355  subargv[3] = "two";
356  subargv[4] = NULL;
357  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".x", "temp2.x");
358  subargv[1] = "write";
359  subargv[2] = "temp2.y";
360  subargv[3] = NULL;
361  TEST_PEX_RUN (pex1, PEX_SUFFIX, "./test-pexecute", subargv, ".y", NULL);
362  TEST_PEX_GET_STATUS (pex1, 2, statuses);
363  if (!WIFEXITED (statuses[0]) || WEXITSTATUS (statuses[0]) != EXIT_SUCCESS
364      || !WIFEXITED (statuses[1]) || WEXITSTATUS (statuses[1]) != EXIT_SUCCESS)
365    ERROR ("echoerr exit status failed");
366  pex_free (pex1);
367  if (fopen ("temp.x", "r") != NULL || fopen ("temp.y", "r") != NULL)
368    ERROR ("temporary files exist");
369  e = fopen ("temp2.x", "r");
370  if (e == NULL)
371    FATAL_ERROR ("fopen temp2.x failed in echoerr", errno);
372  CHECK_LINE (e, "two");
373  fclose (e);
374  e = fopen ("temp2.y", "r");
375  if (e == NULL)
376    FATAL_ERROR ("fopen temp2.y failed in echoerr", errno);
377  CHECK_LINE (e, "one");
378  fclose (e);
379  remove ("temp2.x");
380  remove ("temp2.y");
381
382  /* Test the old pexecute interface.  */
383  {
384    int pid1, pid2;
385    char *errmsg_fmt;
386    char *errmsg_arg;
387    char errbuf1[1000];
388    char errbuf2[1000];
389
390    subargv[1] = "echo";
391    subargv[2] = "oldpexecute";
392    subargv[3] = NULL;
393    pid1 = pexecute ("./test-pexecute", subargv, "test-pexecute", "temp",
394		     &errmsg_fmt, &errmsg_arg, PEXECUTE_FIRST);
395    if (pid1 < 0)
396      {
397	snprintf (errbuf1, sizeof errbuf1, errmsg_fmt, errmsg_arg);
398	snprintf (errbuf2, sizeof errbuf2, "pexecute 1 failed: %s", errbuf1);
399	FATAL_ERROR (errbuf2, 0);
400      }
401
402    subargv[1] = "write";
403    subargv[2] = "temp.y";
404    subargv[3] = NULL;
405    pid2 = pexecute ("./test-pexecute", subargv, "test-pexecute", "temp",
406		     &errmsg_fmt, &errmsg_arg, PEXECUTE_LAST);
407    if (pid2 < 0)
408      {
409	snprintf (errbuf1, sizeof errbuf1, errmsg_fmt, errmsg_arg);
410	snprintf (errbuf2, sizeof errbuf2, "pexecute 2 failed: %s", errbuf1);
411	FATAL_ERROR (errbuf2, 0);
412      }
413
414    if (pwait (pid1, &status, 0) < 0)
415      FATAL_ERROR ("write pwait 1 failed", errno);
416    if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS)
417      ERROR ("write exit status 1 failed");
418
419    if (pwait (pid2, &status, 0) < 0)
420      FATAL_ERROR ("write pwait 1 failed", errno);
421    if (!WIFEXITED (status) || WEXITSTATUS (status) != EXIT_SUCCESS)
422      ERROR ("write exit status 2 failed");
423
424    e = fopen ("temp.y", "r");
425    if (e == NULL)
426      FATAL_ERROR ("fopen temp.y failed in copy temp", errno);
427    CHECK_LINE (e, "oldpexecute");
428    fclose (e);
429
430    remove ("temp.y");
431  }
432
433  if (trace)
434    fprintf (stderr, "Exiting with status %d\n", error_count);
435
436  return error_count;
437}
438
439/* Execute one of the special testing commands.  */
440
441static void
442do_cmd (int argc, char **argv)
443{
444  const char *s;
445
446  /* Try to prevent generating a core dump.  */
447#ifdef RLIMIT_CORE
448 {
449   struct rlimit r;
450
451   r.rlim_cur = 0;
452   r.rlim_max = 0;
453   setrlimit (RLIMIT_CORE, &r);
454 }
455#endif
456
457  s = argv[1];
458  if (strcmp (s, "exit") == 0)
459    exit (EXIT_SUCCESS);
460  else if (strcmp (s, "echo") == 0)
461    {
462      int i;
463
464      for (i = 2; i < argc; ++i)
465	{
466	  if (i > 2)
467	    putchar (' ');
468	  fputs (argv[i], stdout);
469	}
470      putchar ('\n');
471      exit (EXIT_SUCCESS);
472    }
473  else if (strcmp (s, "echoerr") == 0)
474    {
475      int i;
476
477      for (i = 2; i < argc; ++i)
478	{
479	  if (i > 3)
480	    putc (' ', (i & 1) == 0 ? stdout : stderr);
481	  fputs (argv[i], (i & 1) == 0 ? stdout : stderr);
482	}
483      putc ('\n', stdout);
484      putc ('\n', stderr);
485      exit (EXIT_SUCCESS);
486    }
487  else if (strcmp (s, "error") == 0)
488    exit (EXIT_FAILURE);
489  else if (strcmp (s, "abort") == 0)
490    abort ();
491  else if (strcmp (s, "copy") == 0)
492    {
493      int c;
494
495      while ((c = getchar ()) != EOF)
496	putchar (c);
497      exit (EXIT_SUCCESS);
498    }
499  else if (strcmp (s, "write") == 0)
500    {
501      FILE *e;
502      int c;
503
504      e = fopen (argv[2], "w");
505      if (e == NULL)
506	FATAL_ERROR ("fopen for write failed", errno);
507      while ((c = getchar ()) != EOF)
508	putc (c, e);
509      if (fclose (e) != 0)
510	FATAL_ERROR ("fclose for write failed", errno);
511      exit (EXIT_SUCCESS);
512    }
513  else
514    {
515      char buf[1000];
516
517      snprintf (buf, sizeof buf, "unrecognized command %s", argv[1]);
518      FATAL_ERROR (buf, 0);
519    }
520
521  exit (EXIT_FAILURE);
522}
523