(root)/
glibc-2.38/
posix/
tst-getopt-cancel.c
       1  /* Copyright (C) 2017-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  /* fprintf is a cancellation point, but getopt is not supposed to be a
      19     cancellation point, even when it prints error messages.  */
      20  
      21  /* Note: getopt.h must be included first in this file, so we get the
      22     GNU getopt rather than the POSIX one.  */
      23  #include <getopt.h>
      24  
      25  #include <stdbool.h>
      26  #include <stdio.h>
      27  #include <stdlib.h>
      28  
      29  #include <fcntl.h>
      30  #include <pthread.h>
      31  #include <unistd.h>
      32  
      33  #include <support/support.h>
      34  #include <support/temp_file.h>
      35  #include <support/xthread.h>
      36  #include <support/xunistd.h>
      37  
      38  static bool
      39  check_stderr (bool expect_errmsg, FILE *stderr_trapped)
      40  {
      41    static char *lineptr = 0;
      42    static size_t linesz = 0;
      43  
      44    bool got_errmsg = false;
      45    rewind (stderr_trapped);
      46    while (getline (&lineptr, &linesz, stderr_trapped) > 0)
      47      {
      48        got_errmsg = true;
      49        fputs (lineptr, stdout);
      50      }
      51    rewind (stderr_trapped);
      52    xftruncate (fileno (stderr_trapped), 0);
      53    return got_errmsg == expect_errmsg;
      54  }
      55  
      56  struct test_short
      57  {
      58    const char *label;
      59    const char *opts;
      60    const char *const argv[8];
      61    int argc;
      62    bool expect_errmsg;
      63  };
      64  
      65  struct test_long
      66  {
      67    const char *label;
      68    const char *opts;
      69    const struct option longopts[4];
      70    const char *const argv[8];
      71    int argc;
      72    bool expect_errmsg;
      73  };
      74  
      75  #define DEFINE_TEST_DRIVER(test_type, getopt_call)			\
      76    struct test_type##_tdata						\
      77    {									\
      78      pthread_mutex_t *sync;						\
      79      const struct test_type *tcase;					\
      80      bool ok;								\
      81    };									\
      82  									\
      83    static void *								\
      84    test_type##_threadproc (void *data)					\
      85    {									\
      86      struct test_type##_tdata *tdata = data;				\
      87      const struct test_type *tc = tdata->tcase;				\
      88  									\
      89      xpthread_mutex_lock (tdata->sync);					\
      90      xpthread_mutex_unlock (tdata->sync);				\
      91  									\
      92      /* At this point, this thread has a cancellation pending.		\
      93         We should still be able to get all the way through a getopt	\
      94         loop without being cancelled.					\
      95         Setting optind to 0 forces getopt to reinitialize itself.  */	\
      96      optind = 0;								\
      97      opterr = 1;								\
      98      optopt = 0;								\
      99      while (getopt_call != -1)						\
     100        ;									\
     101      tdata->ok = true;							\
     102  									\
     103      pthread_testcancel();						\
     104      return 0;								\
     105    }									\
     106  									\
     107    static bool								\
     108    do_##test_type (const struct test_type *tcase, FILE *stderr_trapped)	\
     109    {									\
     110      pthread_mutex_t sync;						\
     111      struct test_type##_tdata tdata;					\
     112  									\
     113      printf("begin: %s\n", tcase->label);				\
     114  									\
     115      xpthread_mutex_init (&sync, 0);					\
     116      xpthread_mutex_lock (&sync);					\
     117  									\
     118      tdata.sync = &sync;							\
     119      tdata.tcase = tcase;						\
     120      tdata.ok = false;							\
     121  									\
     122      pthread_t thr = xpthread_create (0, test_type##_threadproc,		\
     123  				     (void *)&tdata);			\
     124      xpthread_cancel (thr);						\
     125      xpthread_mutex_unlock (&sync);					\
     126      void *rv = xpthread_join (thr);					\
     127  									\
     128      xpthread_mutex_destroy (&sync);					\
     129  									\
     130      bool ok = true;							\
     131      if (!check_stderr (tcase->expect_errmsg, stderr_trapped))		\
     132        {									\
     133  	ok = false;							\
     134  	printf("FAIL: %s: stderr not as expected\n", tcase->label);	\
     135        }									\
     136      if (!tdata.ok)							\
     137        {									\
     138  	ok = false;							\
     139  	printf("FAIL: %s: did not complete loop\n", tcase->label);	\
     140        }									\
     141      if (rv != PTHREAD_CANCELED)						\
     142        {									\
     143  	ok = false;							\
     144  	printf("FAIL: %s: thread was not cancelled\n", tcase->label);	\
     145        }									\
     146      if (ok)								\
     147        printf ("pass: %s\n", tcase->label);				\
     148      return ok;								\
     149    }
     150  
     151  DEFINE_TEST_DRIVER (test_short,
     152  		    getopt (tc->argc, (char *const *)tc->argv, tc->opts))
     153  DEFINE_TEST_DRIVER (test_long,
     154  		    getopt_long (tc->argc, (char *const *)tc->argv,
     155  				 tc->opts, tc->longopts, 0))
     156  
     157  /* Caution: all option strings must begin with a '+' or '-' so that
     158     getopt does not attempt to permute the argument vector (which is in
     159     read-only memory).  */
     160  const struct test_short tests_short[] = {
     161    { "no errors",
     162      "+ab:c", { "program", "-ac", "-b", "x", 0 }, 4, false },
     163    { "invalid option",
     164      "+ab:c", { "program", "-d", 0 },		 2, true },
     165    { "missing argument",
     166      "+ab:c", { "program", "-b", 0 },		 2, true },
     167    { 0 }
     168  };
     169  
     170  const struct test_long tests_long[] = {
     171    { "no errors (long)",
     172      "+ab:c", { { "alpha",   no_argument,       0, 'a' },
     173  	       { "bravo",   required_argument, 0, 'b' },
     174  	       { "charlie", no_argument,       0, 'c' },
     175  	       { 0 } },
     176      { "program", "-a", "--charlie", "--bravo=x", 0 }, 4, false },
     177  
     178    { "invalid option (long)",
     179      "+ab:c", { { "alpha",   no_argument,       0, 'a' },
     180  	       { "bravo",   required_argument, 0, 'b' },
     181  	       { "charlie", no_argument,       0, 'c' },
     182  	       { 0 } },
     183      { "program", "-a", "--charlie", "--dingo", 0 }, 4, true },
     184  
     185    { "unwanted argument",
     186      "+ab:c", { { "alpha",   no_argument,       0, 'a' },
     187  	       { "bravo",   required_argument, 0, 'b' },
     188  	       { "charlie", no_argument,       0, 'c' },
     189  	       { 0 } },
     190      { "program", "-a", "--charlie=dingo", "--bravo=x", 0 }, 4, true },
     191  
     192    { "missing argument",
     193      "+ab:c", { { "alpha",   no_argument,       0, 'a' },
     194  	       { "bravo",   required_argument, 0, 'b' },
     195  	       { "charlie", no_argument,       0, 'c' },
     196  	       { 0 } },
     197      { "program", "-a", "--charlie", "--bravo", 0 }, 4, true },
     198  
     199    { "ambiguous options",
     200      "+uvw", { { "veni", no_argument, 0, 'u' },
     201  	      { "vedi", no_argument, 0, 'v' },
     202  	      { "veci", no_argument, 0, 'w' } },
     203      { "program", "--ve", 0 }, 2, true },
     204  
     205    { "no errors (long W)",
     206      "+ab:cW;", { { "alpha",   no_argument,	 0, 'a' },
     207  		 { "bravo",   required_argument, 0, 'b' },
     208  		 { "charlie", no_argument,	 0, 'c' },
     209  		 { 0 } },
     210      { "program", "-a", "-W", "charlie", "-W", "bravo=x", 0 }, 6, false },
     211  
     212    { "missing argument (W itself)",
     213      "+ab:cW;", { { "alpha",   no_argument,	 0, 'a' },
     214  		 { "bravo",   required_argument, 0, 'b' },
     215  		 { "charlie", no_argument,	 0, 'c' },
     216  		 { 0 } },
     217      { "program", "-a", "-W", "charlie", "-W", 0 }, 5, true },
     218  
     219    { "missing argument (W longopt)",
     220      "+ab:cW;", { { "alpha",   no_argument,	 0, 'a' },
     221  		 { "bravo",   required_argument, 0, 'b' },
     222  		 { "charlie", no_argument,	 0, 'c' },
     223  		 { 0 } },
     224      { "program", "-a", "-W", "charlie", "-W", "bravo", 0 }, 6, true },
     225  
     226    { "unwanted argument (W longopt)",
     227      "+ab:cW;", { { "alpha",   no_argument,	 0, 'a' },
     228  		 { "bravo",   required_argument, 0, 'b' },
     229  		 { "charlie", no_argument,	 0, 'c' },
     230  		 { 0 } },
     231      { "program", "-a", "-W", "charlie=dingo", "-W", "bravo=x", 0 }, 6, true },
     232  
     233    { "ambiguous options (W)",
     234      "+uvwW;", { { "veni", no_argument, 0, 'u' },
     235  		{ "vedi", no_argument, 0, 'v' },
     236  		{ "veci", no_argument, 0, 'w' } },
     237      { "program", "-W", "ve", 0 }, 3, true },
     238  
     239    { 0 }
     240  };
     241  
     242  static int
     243  do_test (void)
     244  {
     245    int stderr_trap = create_temp_file ("stderr", 0);
     246    if (stderr_trap < 0)
     247      {
     248        perror ("create_temp_file");
     249        return 1;
     250      }
     251    FILE *stderr_trapped = fdopen(stderr_trap, "r+");
     252    if (!stderr_trapped)
     253      {
     254        perror ("fdopen");
     255        return 1;
     256      }
     257    int old_stderr = dup (fileno (stderr));
     258    if (old_stderr < 0)
     259      {
     260        perror ("dup");
     261        return 1;
     262      }
     263    if (dup2 (stderr_trap, 2) < 0)
     264      {
     265        perror ("dup2");
     266        return 1;
     267      }
     268    rewind (stderr);
     269  
     270    bool success = true;
     271  
     272    for (const struct test_short *tcase = tests_short; tcase->label; tcase++)
     273      success = do_test_short (tcase, stderr_trapped) && success;
     274  
     275    for (const struct test_long *tcase = tests_long; tcase->label; tcase++)
     276      success = do_test_long (tcase, stderr_trapped) && success;
     277  
     278    dup2 (old_stderr, 2);
     279    close (old_stderr);
     280    fclose (stderr_trapped);
     281  
     282    return success ? 0 : 1;
     283  }
     284  
     285  #include <support/test-driver.c>