From 7a6543c2dfeef874a34086c8f3eeb1dbdf1ce822 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Tue, 9 Feb 2016 17:03:54 +0900 Subject: [PATCH] tests: Add test for cancellation * tests/gpg/t-cancel.c: New file. * tests/gpg/Makefile.am (tests_skipped): New variable, default to t-genkey and t-cancel. (noinst_PROGRAMS): Add $(tests_skipped). * tests/gpg/.gitignore: Add t-cancel. Signed-off-by: Daiki Ueno --- tests/gpg/.gitignore | 1 + tests/gpg/Makefile.am | 12 +- tests/gpg/t-cancel.c | 265 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 tests/gpg/t-cancel.c diff --git a/tests/gpg/.gitignore b/tests/gpg/.gitignore index d79ace74..cd193f78 100644 --- a/tests/gpg/.gitignore +++ b/tests/gpg/.gitignore @@ -6,6 +6,7 @@ gpg.conf pubring.gpg pubring.gpg~ secring.gpg +t-cancel t-decrypt t-decrypt-verify t-edit diff --git a/tests/gpg/Makefile.am b/tests/gpg/Makefile.am index 98778438..2538f631 100644 --- a/tests/gpg/Makefile.am +++ b/tests/gpg/Makefile.am @@ -62,9 +62,17 @@ AM_CPPFLAGS = -I$(top_builddir)/src @GPG_ERROR_CFLAGS@ AM_LDFLAGS = -no-install LDADD = ../../src/libgpgme.la t_thread1_LDADD = ../../src/libgpgme-pthread.la -lpthread +t_cancel_LDADD = ../../src/libgpgme-pthread.la -lpthread + +# We don't run t-genkey and t-cancel in the test suite, because it +# takes too long +tests_skipped = t-genkey +if !HAVE_W32_SYSTEM +tests_skipped += t-cancel +endif + +noinst_PROGRAMS = $(c_tests) $(tests_skipped) -# We don't run t-genkey in the test suite, because it takes too long -noinst_PROGRAMS = $(c_tests) t-genkey clean-local: -$(top_srcdir)/tests/start-stop-agent --stop diff --git a/tests/gpg/t-cancel.c b/tests/gpg/t-cancel.c new file mode 100644 index 00000000..af98af98 --- /dev/null +++ b/tests/gpg/t-cancel.c @@ -0,0 +1,265 @@ +/* t-thread-cancel.c - Regression test. + Copyright (C) 2000 Werner Koch (dd9jn) + Copyright (C) 2001, 2003, 2004 g10 Code GmbH + + This file is part of GPGME. + + GPGME is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + GPGME is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +/* We need to include config.h so that we know whether we are building + with large file system (LFS) support. */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "t-support.h" + +struct op_result +{ + int done; + gpgme_error_t err; +}; + +static struct op_result op_result; + +struct one_fd +{ + int fd; + int dir; + gpgme_io_cb_t fnc; + void *fnc_data; +}; + +#define FDLIST_MAX 32 +static struct one_fd fdlist[FDLIST_MAX]; + +static pthread_mutex_t lock; + +static gpgme_error_t +add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc, void *fnc_data, + void **r_tag) +{ + struct one_fd *fds = data; + int i; + + pthread_mutex_lock (&lock); + for (i = 0; i < FDLIST_MAX; i++) + { + if (fds[i].fd == -1) + { + fds[i].fd = fd; + fds[i].dir = dir; + fds[i].fnc = fnc; + fds[i].fnc_data = fnc_data; + break; + } + } + pthread_mutex_unlock (&lock); + if (i == FDLIST_MAX) + return gpgme_err_make (GPG_ERR_SOURCE_USER_1, GPG_ERR_GENERAL); + *r_tag = &fds[i]; + return 0; +} + +static void +remove_io_cb (void *tag) +{ + struct one_fd *fd = tag; + + pthread_mutex_lock (&lock); + fd->fd = -1; + pthread_mutex_unlock (&lock); +} + +static void +io_event (void *data, gpgme_event_io_t type, void *type_data) +{ + struct op_result *result = data; + + if (type == GPGME_EVENT_DONE) + { + result->done = 1; + result->err = * (gpgme_error_t *) type_data; + } +} + + +static int +do_select (void) +{ + fd_set rfds; + fd_set wfds; + int i, n; + int any = 0; + struct timeval tv; + + pthread_mutex_lock (&lock); + FD_ZERO (&rfds); + FD_ZERO (&wfds); + for (i = 0; i < FDLIST_MAX; i++) + if (fdlist[i].fd != -1) + FD_SET (fdlist[i].fd, fdlist[i].dir ? &rfds : &wfds); + pthread_mutex_unlock (&lock); + + tv.tv_sec = 0; + tv.tv_usec = 1000; + + do + { + n = select (FD_SETSIZE, &rfds, &wfds, NULL, &tv); + } + while (n < 0 && errno == EINTR); + + if (n < 0) + return n; /* Error or timeout. */ + + pthread_mutex_lock (&lock); + for (i = 0; i < FDLIST_MAX && n; i++) + { + if (fdlist[i].fd != -1) + { + if (FD_ISSET (fdlist[i].fd, fdlist[i].dir ? &rfds : &wfds)) + { + assert (n); + n--; + any = 1; + (*fdlist[i].fnc) (fdlist[i].fnc_data, fdlist[i].fd); + } + } + } + pthread_mutex_unlock (&lock); + return any; +} + +static int +my_wait (void) +{ + int n; + + do + { + n = do_select (); + } + while (n >= 0 && !op_result.done); + return 0; +} + + +static struct gpgme_io_cbs io_cbs = + { + add_io_cb, + fdlist, + remove_io_cb, + io_event, + &op_result + }; + + +static void * +thread_cancel (void *data) +{ + gpgme_ctx_t ctx = data; + gpgme_error_t err; + + usleep (100000); + err = gpgme_cancel (ctx); + fail_if_err (err); + + return NULL; +} + +int +main (void) +{ + gpgme_ctx_t ctx; + gpgme_error_t err; + gpgme_engine_info_t info; + int i; + pthread_mutexattr_t attr; + pthread_t tcancel; + const char *parms = "\n" + "Key-Type: RSA\n" + "Key-Length: 2048\n" + "Subkey-Type: RSA\n" + "Subkey-Length: 2048\n" + "Name-Real: Joe Tester\n" + "Name-Comment: (pp=abc)\n" + "Name-Email: joe@foo.bar\n" + "Expire-Date: 0\n" + "Passphrase: abc\n" + "\n"; + + init_gpgme (GPGME_PROTOCOL_OpenPGP); + + err = gpgme_get_engine_info (&info); + fail_if_err (err); + + /* The mutex must be recursive, since remove_io_cb (which acquires a + lock) can be called while holding a lock acquired in do_select. */ + pthread_mutexattr_init (&attr); + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init (&lock, &attr); + pthread_mutexattr_destroy (&attr); + + for (i = 0; i < FDLIST_MAX; i++) + fdlist[i].fd = -1; + + err = gpgme_new (&ctx); + fail_if_err (err); + gpgme_set_armor (ctx, 1); + gpgme_set_io_cbs (ctx, &io_cbs); + op_result.done = 0; + + pthread_create (&tcancel, NULL, thread_cancel, ctx); + + err = gpgme_op_genkey_start (ctx, parms, NULL, NULL); + fail_if_err (err); + + my_wait (); + + pthread_join (tcancel, NULL); + + if (op_result.err) + { + if (gpgme_err_code (op_result.err) == GPG_ERR_CANCELED) + fputs ("Successfully cancelled\n", stdout); + else + { + fprintf (stderr, + "%s:%i: Operation finished with unexpected error: %s\n", + __FILE__, __LINE__, gpgme_strerror (op_result.err)); + exit (1); + } + } + else + fputs ("Successfully finished before cancellation\n", stdout); + + gpgme_release (ctx); + + return 0; +}