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  /* 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
455static void
456do_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