Blame posix/tst-getopt-cancel.c

Packit 6c4009
/* Copyright (C) 2017-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
/* fprintf is a cancellation point, but getopt is not supposed to be a
Packit 6c4009
   cancellation point, even when it prints error messages.  */
Packit 6c4009
Packit 6c4009
/* Note: getopt.h must be included first in this file, so we get the
Packit 6c4009
   GNU getopt rather than the POSIX one.  */
Packit 6c4009
#include <getopt.h>
Packit 6c4009
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <pthread.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
Packit 6c4009
#include <support/support.h>
Packit 6c4009
#include <support/temp_file.h>
Packit 6c4009
#include <support/xthread.h>
Packit 6c4009
Packit 6c4009
static bool
Packit 6c4009
check_stderr (bool expect_errmsg, FILE *stderr_trapped)
Packit 6c4009
{
Packit 6c4009
  static char *lineptr = 0;
Packit 6c4009
  static size_t linesz = 0;
Packit 6c4009
Packit 6c4009
  bool got_errmsg = false;
Packit 6c4009
  rewind (stderr_trapped);
Packit 6c4009
  while (getline (&lineptr, &linesz, stderr_trapped) > 0)
Packit 6c4009
    {
Packit 6c4009
      got_errmsg = true;
Packit 6c4009
      fputs (lineptr, stdout);
Packit 6c4009
    }
Packit 6c4009
  rewind (stderr_trapped);
Packit 6c4009
  ftruncate (fileno (stderr_trapped), 0);
Packit 6c4009
  return got_errmsg == expect_errmsg;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
struct test_short
Packit 6c4009
{
Packit 6c4009
  const char *label;
Packit 6c4009
  const char *opts;
Packit 6c4009
  const char *const argv[8];
Packit 6c4009
  int argc;
Packit 6c4009
  bool expect_errmsg;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
struct test_long
Packit 6c4009
{
Packit 6c4009
  const char *label;
Packit 6c4009
  const char *opts;
Packit 6c4009
  const struct option longopts[4];
Packit 6c4009
  const char *const argv[8];
Packit 6c4009
  int argc;
Packit 6c4009
  bool expect_errmsg;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
#define DEFINE_TEST_DRIVER(test_type, getopt_call)			\
Packit 6c4009
  struct test_type##_tdata						\
Packit 6c4009
  {									\
Packit 6c4009
    pthread_mutex_t *sync;						\
Packit 6c4009
    const struct test_type *tcase;					\
Packit 6c4009
    bool ok;								\
Packit 6c4009
  };									\
Packit 6c4009
									\
Packit 6c4009
  static void *								\
Packit 6c4009
  test_type##_threadproc (void *data)					\
Packit 6c4009
  {									\
Packit 6c4009
    struct test_type##_tdata *tdata = data;				\
Packit 6c4009
    const struct test_type *tc = tdata->tcase;				\
Packit 6c4009
									\
Packit 6c4009
    xpthread_mutex_lock (tdata->sync);					\
Packit 6c4009
    xpthread_mutex_unlock (tdata->sync);				\
Packit 6c4009
									\
Packit 6c4009
    /* At this point, this thread has a cancellation pending.		\
Packit 6c4009
       We should still be able to get all the way through a getopt	\
Packit 6c4009
       loop without being cancelled.					\
Packit 6c4009
       Setting optind to 0 forces getopt to reinitialize itself.  */	\
Packit 6c4009
    optind = 0;								\
Packit 6c4009
    opterr = 1;								\
Packit 6c4009
    optopt = 0;								\
Packit 6c4009
    while (getopt_call != -1)						\
Packit 6c4009
      ;									\
Packit 6c4009
    tdata->ok = true;							\
Packit 6c4009
									\
Packit 6c4009
    pthread_testcancel();						\
Packit 6c4009
    return 0;								\
Packit 6c4009
  }									\
Packit 6c4009
									\
Packit 6c4009
  static bool								\
Packit 6c4009
  do_##test_type (const struct test_type *tcase, FILE *stderr_trapped)	\
Packit 6c4009
  {									\
Packit 6c4009
    pthread_mutex_t sync;						\
Packit 6c4009
    struct test_type##_tdata tdata;					\
Packit 6c4009
									\
Packit 6c4009
    printf("begin: %s\n", tcase->label);				\
Packit 6c4009
									\
Packit 6c4009
    xpthread_mutex_init (&sync, 0);					\
Packit 6c4009
    xpthread_mutex_lock (&sync);					\
Packit 6c4009
									\
Packit 6c4009
    tdata.sync = &sync;							\
Packit 6c4009
    tdata.tcase = tcase;						\
Packit 6c4009
    tdata.ok = false;							\
Packit 6c4009
									\
Packit 6c4009
    pthread_t thr = xpthread_create (0, test_type##_threadproc,		\
Packit 6c4009
				     (void *)&tdata);			\
Packit 6c4009
    xpthread_cancel (thr);						\
Packit 6c4009
    xpthread_mutex_unlock (&sync);					\
Packit 6c4009
    void *rv = xpthread_join (thr);					\
Packit 6c4009
									\
Packit 6c4009
    xpthread_mutex_destroy (&sync);					\
Packit 6c4009
									\
Packit 6c4009
    bool ok = true;							\
Packit 6c4009
    if (!check_stderr (tcase->expect_errmsg, stderr_trapped))		\
Packit 6c4009
      {									\
Packit 6c4009
	ok = false;							\
Packit 6c4009
	printf("FAIL: %s: stderr not as expected\n", tcase->label);	\
Packit 6c4009
      }									\
Packit 6c4009
    if (!tdata.ok)							\
Packit 6c4009
      {									\
Packit 6c4009
	ok = false;							\
Packit 6c4009
	printf("FAIL: %s: did not complete loop\n", tcase->label);	\
Packit 6c4009
      }									\
Packit 6c4009
    if (rv != PTHREAD_CANCELED)						\
Packit 6c4009
      {									\
Packit 6c4009
	ok = false;							\
Packit 6c4009
	printf("FAIL: %s: thread was not cancelled\n", tcase->label);	\
Packit 6c4009
      }									\
Packit 6c4009
    if (ok)								\
Packit 6c4009
      printf ("pass: %s\n", tcase->label);				\
Packit 6c4009
    return ok;								\
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
DEFINE_TEST_DRIVER (test_short,
Packit 6c4009
		    getopt (tc->argc, (char *const *)tc->argv, tc->opts))
Packit 6c4009
DEFINE_TEST_DRIVER (test_long,
Packit 6c4009
		    getopt_long (tc->argc, (char *const *)tc->argv,
Packit 6c4009
				 tc->opts, tc->longopts, 0))
Packit 6c4009
Packit 6c4009
/* Caution: all option strings must begin with a '+' or '-' so that
Packit 6c4009
   getopt does not attempt to permute the argument vector (which is in
Packit 6c4009
   read-only memory).  */
Packit 6c4009
const struct test_short tests_short[] = {
Packit 6c4009
  { "no errors",
Packit 6c4009
    "+ab:c", { "program", "-ac", "-b", "x", 0 }, 4, false },
Packit 6c4009
  { "invalid option",
Packit 6c4009
    "+ab:c", { "program", "-d", 0 },		 2, true },
Packit 6c4009
  { "missing argument",
Packit 6c4009
    "+ab:c", { "program", "-b", 0 },		 2, true },
Packit 6c4009
  { 0 }
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
const struct test_long tests_long[] = {
Packit 6c4009
  { "no errors (long)",
Packit 6c4009
    "+ab:c", { { "alpha",   no_argument,       0, 'a' },
Packit 6c4009
	       { "bravo",   required_argument, 0, 'b' },
Packit 6c4009
	       { "charlie", no_argument,       0, 'c' },
Packit 6c4009
	       { 0 } },
Packit 6c4009
    { "program", "-a", "--charlie", "--bravo=x", 0 }, 4, false },
Packit 6c4009
Packit 6c4009
  { "invalid option (long)",
Packit 6c4009
    "+ab:c", { { "alpha",   no_argument,       0, 'a' },
Packit 6c4009
	       { "bravo",   required_argument, 0, 'b' },
Packit 6c4009
	       { "charlie", no_argument,       0, 'c' },
Packit 6c4009
	       { 0 } },
Packit 6c4009
    { "program", "-a", "--charlie", "--dingo", 0 }, 4, true },
Packit 6c4009
Packit 6c4009
  { "unwanted argument",
Packit 6c4009
    "+ab:c", { { "alpha",   no_argument,       0, 'a' },
Packit 6c4009
	       { "bravo",   required_argument, 0, 'b' },
Packit 6c4009
	       { "charlie", no_argument,       0, 'c' },
Packit 6c4009
	       { 0 } },
Packit 6c4009
    { "program", "-a", "--charlie=dingo", "--bravo=x", 0 }, 4, true },
Packit 6c4009
Packit 6c4009
  { "missing argument",
Packit 6c4009
    "+ab:c", { { "alpha",   no_argument,       0, 'a' },
Packit 6c4009
	       { "bravo",   required_argument, 0, 'b' },
Packit 6c4009
	       { "charlie", no_argument,       0, 'c' },
Packit 6c4009
	       { 0 } },
Packit 6c4009
    { "program", "-a", "--charlie", "--bravo", 0 }, 4, true },
Packit 6c4009
Packit 6c4009
  { "ambiguous options",
Packit 6c4009
    "+uvw", { { "veni", no_argument, 0, 'u' },
Packit 6c4009
	      { "vedi", no_argument, 0, 'v' },
Packit 6c4009
	      { "veci", no_argument, 0, 'w' } },
Packit 6c4009
    { "program", "--ve", 0 }, 2, true },
Packit 6c4009
Packit 6c4009
  { "no errors (long W)",
Packit 6c4009
    "+ab:cW;", { { "alpha",   no_argument,	 0, 'a' },
Packit 6c4009
		 { "bravo",   required_argument, 0, 'b' },
Packit 6c4009
		 { "charlie", no_argument,	 0, 'c' },
Packit 6c4009
		 { 0 } },
Packit 6c4009
    { "program", "-a", "-W", "charlie", "-W", "bravo=x", 0 }, 6, false },
Packit 6c4009
Packit 6c4009
  { "missing argument (W itself)",
Packit 6c4009
    "+ab:cW;", { { "alpha",   no_argument,	 0, 'a' },
Packit 6c4009
		 { "bravo",   required_argument, 0, 'b' },
Packit 6c4009
		 { "charlie", no_argument,	 0, 'c' },
Packit 6c4009
		 { 0 } },
Packit 6c4009
    { "program", "-a", "-W", "charlie", "-W", 0 }, 5, true },
Packit 6c4009
Packit 6c4009
  { "missing argument (W longopt)",
Packit 6c4009
    "+ab:cW;", { { "alpha",   no_argument,	 0, 'a' },
Packit 6c4009
		 { "bravo",   required_argument, 0, 'b' },
Packit 6c4009
		 { "charlie", no_argument,	 0, 'c' },
Packit 6c4009
		 { 0 } },
Packit 6c4009
    { "program", "-a", "-W", "charlie", "-W", "bravo", 0 }, 6, true },
Packit 6c4009
Packit 6c4009
  { "unwanted argument (W longopt)",
Packit 6c4009
    "+ab:cW;", { { "alpha",   no_argument,	 0, 'a' },
Packit 6c4009
		 { "bravo",   required_argument, 0, 'b' },
Packit 6c4009
		 { "charlie", no_argument,	 0, 'c' },
Packit 6c4009
		 { 0 } },
Packit 6c4009
    { "program", "-a", "-W", "charlie=dingo", "-W", "bravo=x", 0 }, 6, true },
Packit 6c4009
Packit 6c4009
  { "ambiguous options (W)",
Packit 6c4009
    "+uvwW;", { { "veni", no_argument, 0, 'u' },
Packit 6c4009
		{ "vedi", no_argument, 0, 'v' },
Packit 6c4009
		{ "veci", no_argument, 0, 'w' } },
Packit 6c4009
    { "program", "-W", "ve", 0 }, 3, true },
Packit 6c4009
Packit 6c4009
  { 0 }
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
do_test (void)
Packit 6c4009
{
Packit 6c4009
  int stderr_trap = create_temp_file ("stderr", 0);
Packit 6c4009
  if (stderr_trap < 0)
Packit 6c4009
    {
Packit 6c4009
      perror ("create_temp_file");
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
  FILE *stderr_trapped = fdopen(stderr_trap, "r+");
Packit 6c4009
  if (!stderr_trapped)
Packit 6c4009
    {
Packit 6c4009
      perror ("fdopen");
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
  int old_stderr = dup (fileno (stderr));
Packit 6c4009
  if (old_stderr < 0)
Packit 6c4009
    {
Packit 6c4009
      perror ("dup");
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
  if (dup2 (stderr_trap, 2) < 0)
Packit 6c4009
    {
Packit 6c4009
      perror ("dup2");
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
  rewind (stderr);
Packit 6c4009
Packit 6c4009
  bool success = true;
Packit 6c4009
Packit 6c4009
  for (const struct test_short *tcase = tests_short; tcase->label; tcase++)
Packit 6c4009
    success = do_test_short (tcase, stderr_trapped) && success;
Packit 6c4009
Packit 6c4009
  for (const struct test_long *tcase = tests_long; tcase->label; tcase++)
Packit 6c4009
    success = do_test_long (tcase, stderr_trapped) && success;
Packit 6c4009
Packit 6c4009
  dup2 (old_stderr, 2);
Packit 6c4009
  close (old_stderr);
Packit 6c4009
  fclose (stderr_trapped);
Packit 6c4009
Packit 6c4009
  return success ? 0 : 1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
#include <support/test-driver.c>