(root)/
glibc-2.38/
support/
tst-support_capture_subprocess.c
       1  /* Test capturing output from a subprocess.
       2     Copyright (C) 2017-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <stdbool.h>
      20  #include <stdio.h>
      21  #include <stdlib.h>
      22  #include <string.h>
      23  #include <support/capture_subprocess.h>
      24  #include <support/check.h>
      25  #include <support/support.h>
      26  #include <support/temp_file.h>
      27  #include <sys/wait.h>
      28  #include <unistd.h>
      29  #include <paths.h>
      30  #include <getopt.h>
      31  #include <limits.h>
      32  #include <errno.h>
      33  #include <array_length.h>
      34  
      35  /* Nonzero if the program gets called via 'exec'.  */
      36  static int restart;
      37  
      38  /* Hold the four initial argument used to respawn the process.  */
      39  static char *initial_argv[5];
      40  
      41  /* Write one byte at *P to FD and advance *P.  Do nothing if *P is
      42     '\0'.  */
      43  static void
      44  transfer (const unsigned char **p, int fd)
      45  {
      46    if (**p != '\0')
      47      {
      48        TEST_VERIFY (write (fd, *p, 1) == 1);
      49        ++*p;
      50      }
      51  }
      52  
      53  /* Determine the order in which stdout and stderr are written.  */
      54  enum write_mode { out_first, err_first, interleave,
      55                    write_mode_last =  interleave };
      56  
      57  static const char *
      58  write_mode_to_str (enum write_mode mode)
      59  {
      60    switch (mode)
      61      {
      62      case out_first:  return "out_first";
      63      case err_first:  return "err_first";
      64      case interleave: return "interleave";
      65      default:         return "write_mode_last";
      66      }
      67  }
      68  
      69  static enum write_mode
      70  str_to_write_mode (const char *mode)
      71  {
      72    if (strcmp (mode, "out_first") == 0)
      73      return out_first;
      74    else if (strcmp (mode, "err_first") == 0)
      75      return err_first;
      76    else if (strcmp (mode, "interleave") == 0)
      77      return interleave;
      78    return write_mode_last;
      79  }
      80  
      81  /* Describe what to write in the subprocess.  */
      82  struct test
      83  {
      84    char *out;
      85    char *err;
      86    enum write_mode write_mode;
      87    int signal;
      88    int status;
      89  };
      90  
      91  _Noreturn static void
      92  test_common (const struct test *test)
      93  {
      94    bool mode_ok = false;
      95    switch (test->write_mode)
      96      {
      97      case out_first:
      98        TEST_VERIFY (fputs (test->out, stdout) >= 0);
      99        TEST_VERIFY (fflush (stdout) == 0);
     100        TEST_VERIFY (fputs (test->err, stderr) >= 0);
     101        TEST_VERIFY (fflush (stderr) == 0);
     102        mode_ok = true;
     103        break;
     104      case err_first:
     105        TEST_VERIFY (fputs (test->err, stderr) >= 0);
     106        TEST_VERIFY (fflush (stderr) == 0);
     107        TEST_VERIFY (fputs (test->out, stdout) >= 0);
     108        TEST_VERIFY (fflush (stdout) == 0);
     109        mode_ok = true;
     110        break;
     111      case interleave:
     112        {
     113          const unsigned char *pout = (const unsigned char *) test->out;
     114          const unsigned char *perr = (const unsigned char *) test->err;
     115          do
     116            {
     117              transfer (&pout, STDOUT_FILENO);
     118              transfer (&perr, STDERR_FILENO);
     119            }
     120          while (*pout != '\0' || *perr != '\0');
     121        }
     122        mode_ok = true;
     123        break;
     124      }
     125    TEST_VERIFY (mode_ok);
     126  
     127    if (test->signal != 0)
     128      raise (test->signal);
     129    exit (test->status);
     130  }
     131  
     132  static int
     133  parse_int (const char *str)
     134  {
     135    char *endptr;
     136    long int ret;
     137    errno = 0;
     138    ret = strtol (str, &endptr, 10);
     139    TEST_COMPARE (errno, 0);
     140    TEST_VERIFY (ret >= 0 && ret <= INT_MAX);
     141    return ret;
     142  }
     143  
     144  /* For use with support_capture_subprogram.  */
     145  _Noreturn static void
     146  handle_restart (char *out, char *err, const char *write_mode,
     147  		const char *signal, const char *status)
     148  {
     149    struct test test =
     150      {
     151        out,
     152        err,
     153        str_to_write_mode (write_mode),
     154        parse_int (signal),
     155        parse_int (status)
     156      };
     157    test_common (&test);
     158  }
     159  
     160  /* For use with support_capture_subprocess.  */
     161  _Noreturn static void
     162  callback (void *closure)
     163  {
     164    const struct test *test = closure;
     165    test_common (test);
     166  }
     167  
     168  /* Create a heap-allocated random string of letters.  */
     169  static char *
     170  random_string (size_t length)
     171  {
     172    char *result = xmalloc (length + 1);
     173    for (size_t i = 0; i < length; ++i)
     174      result[i] = 'a' + (rand () % 26);
     175    result[length] = '\0';
     176    return result;
     177  }
     178  
     179  /* Check that the specific stream from the captured subprocess matches
     180     expectations.  */
     181  static void
     182  check_stream (const char *what, const struct xmemstream *stream,
     183                const char *expected)
     184  {
     185    if (strcmp (stream->buffer, expected) != 0)
     186      {
     187        support_record_failure ();
     188        printf ("error: captured %s data incorrect\n"
     189                "  expected: %s\n"
     190                "  actual:   %s\n",
     191                what, expected, stream->buffer);
     192      }
     193    if (stream->length != strlen (expected))
     194      {
     195        support_record_failure ();
     196        printf ("error: captured %s data length incorrect\n"
     197                "  expected: %zu\n"
     198                "  actual:   %zu\n",
     199                what, strlen (expected), stream->length);
     200      }
     201  }
     202  
     203  static struct support_capture_subprocess
     204  do_subprocess (struct test *test)
     205  {
     206    return support_capture_subprocess (callback, test);
     207  }
     208  
     209  static struct support_capture_subprocess
     210  do_subprogram (const struct test *test)
     211  {
     212    /* Three digits per byte plus null terminator.  */
     213    char signalstr[3 * sizeof(int) + 1];
     214    snprintf (signalstr, sizeof (signalstr), "%d", test->signal);
     215    char statusstr[3 * sizeof(int) + 1];
     216    snprintf (statusstr, sizeof (statusstr), "%d", test->status);
     217  
     218    int argc = 0;
     219    enum {
     220      /* 4 elements from initial_argv (path to ld.so, '--library-path', the
     221         path', and application name'), 2 for restart argument ('--direct',
     222         '--restart'), 5 arguments plus NULL.  */
     223      argv_size = 12
     224    };
     225    char *args[argv_size];
     226  
     227    for (char **arg = initial_argv; *arg != NULL; arg++)
     228      args[argc++] = *arg;
     229  
     230    args[argc++] = (char*) "--direct";
     231    args[argc++] = (char*) "--restart";
     232  
     233    args[argc++] = test->out;
     234    args[argc++] = test->err;
     235    args[argc++] = (char*) write_mode_to_str (test->write_mode);
     236    args[argc++] = signalstr;
     237    args[argc++] = statusstr;
     238    args[argc]   = NULL;
     239    TEST_VERIFY (argc < argv_size);
     240  
     241    return support_capture_subprogram (args[0], args);
     242  }
     243  
     244  enum test_type
     245  {
     246    subprocess,
     247    subprogram,
     248  };
     249  
     250  static int
     251  do_multiple_tests (enum test_type type)
     252  {
     253    const int lengths[] = {0, 1, 17, 512, 20000, -1};
     254  
     255    /* Test multiple combinations of support_capture_sub{process,program}.
     256  
     257       length_idx_stdout: Index into the lengths array above,
     258         controls how many bytes are written by the subprocess to
     259         standard output.
     260       length_idx_stderr: Same for standard error.
     261       write_mode: How standard output and standard error writes are
     262         ordered.
     263       signal: Exit with no signal if zero, with SIGTERM if one.
     264       status: Process exit status: 0 if zero, 3 if one.  */
     265    for (int length_idx_stdout = 0; lengths[length_idx_stdout] >= 0;
     266         ++length_idx_stdout)
     267      for (int length_idx_stderr = 0; lengths[length_idx_stderr] >= 0;
     268           ++length_idx_stderr)
     269        for (int write_mode = 0; write_mode < write_mode_last; ++write_mode)
     270          for (int signal = 0; signal < 2; ++signal)
     271            for (int status = 0; status < 2; ++status)
     272              {
     273                struct test test =
     274                  {
     275                    .out = random_string (lengths[length_idx_stdout]),
     276                    .err = random_string (lengths[length_idx_stderr]),
     277                    .write_mode = write_mode,
     278                    .signal = signal * SIGTERM, /* 0 or SIGTERM.  */
     279                    .status = status * 3,       /* 0 or 3.  */
     280                  };
     281                TEST_VERIFY (strlen (test.out) == lengths[length_idx_stdout]);
     282                TEST_VERIFY (strlen (test.err) == lengths[length_idx_stderr]);
     283  
     284  	      struct support_capture_subprocess result
     285  		= type == subprocess ? do_subprocess (&test)
     286  				     : do_subprogram (&test);
     287  
     288                check_stream ("stdout", &result.out, test.out);
     289                check_stream ("stderr", &result.err, test.err);
     290  
     291                /* Allowed output for support_capture_subprocess_check.  */
     292                int check_allow = 0;
     293                if (lengths[length_idx_stdout] > 0)
     294                  check_allow |= sc_allow_stdout;
     295                if (lengths[length_idx_stderr] > 0)
     296                  check_allow |= sc_allow_stderr;
     297                if (check_allow == 0)
     298                  check_allow = sc_allow_none;
     299  
     300                if (test.signal != 0)
     301                  {
     302                    TEST_VERIFY (WIFSIGNALED (result.status));
     303                    TEST_VERIFY (WTERMSIG (result.status) == test.signal);
     304                    support_capture_subprocess_check (&result, "signal",
     305                                                      -SIGTERM, check_allow);
     306                  }
     307                else
     308                  {
     309                    TEST_VERIFY (WIFEXITED (result.status));
     310                    TEST_VERIFY (WEXITSTATUS (result.status) == test.status);
     311                    support_capture_subprocess_check (&result, "exit",
     312                                                      test.status, check_allow);
     313                  }
     314                support_capture_subprocess_free (&result);
     315                free (test.out);
     316                free (test.err);
     317              }
     318    return 0;
     319  }
     320  
     321  static int
     322  do_test (int argc, char *argv[])
     323  {
     324    /* We must have either:
     325  
     326       - one or four parameters if called initially:
     327         + argv[1]: path for ld.so        optional
     328         + argv[2]: "--library-path"      optional
     329         + argv[3]: the library path      optional
     330         + argv[4]: the application name
     331  
     332       - six parameters left if called through re-execution:
     333         + argv[1]: the application name
     334         + argv[2]: the stdout to print
     335         + argv[3]: the stderr to print
     336         + argv[4]: the write mode to use
     337         + argv[5]: the signal to issue
     338         + argv[6]: the exit status code to use
     339  
     340       * When built with --enable-hardcoded-path-in-tests or issued without
     341         using the loader directly.
     342    */
     343  
     344    if (argc != (restart ? 6 : 5) && argc != (restart ? 6 : 2))
     345      FAIL_EXIT1 ("wrong number of arguments (%d)", argc);
     346  
     347    if (restart)
     348      {
     349        handle_restart (argv[1],  /* stdout  */
     350  		      argv[2],  /* stderr  */
     351  		      argv[3],  /* write_mode  */
     352  		      argv[4],  /* signal  */
     353  		      argv[5]); /* status  */
     354      }
     355  
     356    initial_argv[0] = argv[1]; /* path for ld.so  */
     357    initial_argv[1] = argv[2]; /* "--library-path"  */
     358    initial_argv[2] = argv[3]; /* the library path  */
     359    initial_argv[3] = argv[4]; /* the application name  */
     360    initial_argv[4] = NULL;
     361  
     362    do_multiple_tests (subprocess);
     363    do_multiple_tests (subprogram);
     364  
     365    return 0;
     366  }
     367  
     368  #define CMDLINE_OPTIONS \
     369    { "restart", no_argument, &restart, 1 },
     370  #define TEST_FUNCTION_ARGV do_test
     371  #include <support/test-driver.c>