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