Line data Source code
1 : /* t-thread-cancel.c - Regression test.
2 : Copyright (C) 2000 Werner Koch (dd9jn)
3 : Copyright (C) 2001, 2003, 2004 g10 Code GmbH
4 :
5 : This file is part of GPGME.
6 :
7 : GPGME is free software; you can redistribute it and/or modify it
8 : under the terms of the GNU Lesser General Public License as
9 : published by the Free Software Foundation; either version 2.1 of
10 : the License, or (at your option) any later version.
11 :
12 : GPGME is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : Lesser General Public License for more details.
16 :
17 : You should have received a copy of the GNU Lesser General Public
18 : License along with this program; if not, write to the Free Software
19 : Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 : 02111-1307, USA. */
21 :
22 : /* We need to include config.h so that we know whether we are building
23 : with large file system (LFS) support. */
24 : #ifdef HAVE_CONFIG_H
25 : #include <config.h>
26 : #endif
27 :
28 : #include <stdlib.h>
29 : #include <stdio.h>
30 : #include <string.h>
31 : #include <assert.h>
32 : #include <limits.h>
33 : #include <ctype.h>
34 : #include <errno.h>
35 : #ifdef HAVE_SYS_TIME_H
36 : # include <sys/time.h>
37 : #endif
38 : #include <sys/types.h>
39 : #include <unistd.h>
40 : #include <pthread.h>
41 : #ifdef HAVE_SYS_SELECT_H
42 : # include <sys/select.h>
43 : #endif
44 :
45 : #include <gpgme.h>
46 :
47 : #include "t-support.h"
48 :
49 : struct op_result
50 : {
51 : int done;
52 : gpgme_error_t err;
53 : };
54 :
55 : static struct op_result op_result;
56 :
57 : struct one_fd
58 : {
59 : int fd;
60 : int dir;
61 : gpgme_io_cb_t fnc;
62 : void *fnc_data;
63 : };
64 :
65 : #define FDLIST_MAX 32
66 : static struct one_fd fdlist[FDLIST_MAX];
67 :
68 : static pthread_mutex_t lock;
69 :
70 : static gpgme_error_t
71 0 : add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc, void *fnc_data,
72 : void **r_tag)
73 : {
74 0 : struct one_fd *fds = data;
75 : int i;
76 :
77 0 : pthread_mutex_lock (&lock);
78 0 : for (i = 0; i < FDLIST_MAX; i++)
79 : {
80 0 : if (fds[i].fd == -1)
81 : {
82 0 : fds[i].fd = fd;
83 0 : fds[i].dir = dir;
84 0 : fds[i].fnc = fnc;
85 0 : fds[i].fnc_data = fnc_data;
86 0 : break;
87 : }
88 : }
89 0 : pthread_mutex_unlock (&lock);
90 0 : if (i == FDLIST_MAX)
91 0 : return gpgme_err_make (GPG_ERR_SOURCE_USER_1, GPG_ERR_GENERAL);
92 0 : *r_tag = &fds[i];
93 0 : return 0;
94 : }
95 :
96 : static void
97 0 : remove_io_cb (void *tag)
98 : {
99 0 : struct one_fd *fd = tag;
100 :
101 0 : pthread_mutex_lock (&lock);
102 0 : fd->fd = -1;
103 0 : pthread_mutex_unlock (&lock);
104 0 : }
105 :
106 : static void
107 0 : io_event (void *data, gpgme_event_io_t type, void *type_data)
108 : {
109 0 : struct op_result *result = data;
110 :
111 0 : if (type == GPGME_EVENT_DONE)
112 : {
113 0 : result->done = 1;
114 0 : result->err = * (gpgme_error_t *) type_data;
115 : }
116 0 : }
117 :
118 :
119 : static int
120 0 : do_select (void)
121 : {
122 : fd_set rfds;
123 : fd_set wfds;
124 : int i, n;
125 0 : int any = 0;
126 : struct timeval tv;
127 :
128 0 : pthread_mutex_lock (&lock);
129 0 : FD_ZERO (&rfds);
130 0 : FD_ZERO (&wfds);
131 0 : for (i = 0; i < FDLIST_MAX; i++)
132 0 : if (fdlist[i].fd != -1)
133 0 : FD_SET (fdlist[i].fd, fdlist[i].dir ? &rfds : &wfds);
134 0 : pthread_mutex_unlock (&lock);
135 :
136 0 : tv.tv_sec = 0;
137 0 : tv.tv_usec = 1000;
138 :
139 : do
140 : {
141 0 : n = select (FD_SETSIZE, &rfds, &wfds, NULL, &tv);
142 : }
143 0 : while (n < 0 && errno == EINTR);
144 :
145 0 : if (n < 0)
146 0 : return n; /* Error or timeout. */
147 :
148 0 : pthread_mutex_lock (&lock);
149 0 : for (i = 0; i < FDLIST_MAX && n; i++)
150 : {
151 0 : if (fdlist[i].fd != -1)
152 : {
153 0 : if (FD_ISSET (fdlist[i].fd, fdlist[i].dir ? &rfds : &wfds))
154 : {
155 0 : assert (n);
156 0 : n--;
157 0 : any = 1;
158 0 : (*fdlist[i].fnc) (fdlist[i].fnc_data, fdlist[i].fd);
159 : }
160 : }
161 : }
162 0 : pthread_mutex_unlock (&lock);
163 0 : return any;
164 : }
165 :
166 : static int
167 0 : my_wait (void)
168 : {
169 : int n;
170 :
171 : do
172 : {
173 0 : n = do_select ();
174 : }
175 0 : while (n >= 0 && !op_result.done);
176 0 : return 0;
177 : }
178 :
179 :
180 : static struct gpgme_io_cbs io_cbs =
181 : {
182 : add_io_cb,
183 : fdlist,
184 : remove_io_cb,
185 : io_event,
186 : &op_result
187 : };
188 :
189 :
190 : static void *
191 0 : thread_cancel (void *data)
192 : {
193 0 : gpgme_ctx_t ctx = data;
194 : gpgme_error_t err;
195 :
196 0 : usleep (100000);
197 0 : err = gpgme_cancel (ctx);
198 0 : fail_if_err (err);
199 :
200 0 : return NULL;
201 : }
202 :
203 : int
204 0 : main (void)
205 : {
206 : gpgme_ctx_t ctx;
207 : gpgme_error_t err;
208 : gpgme_engine_info_t info;
209 : int i;
210 : pthread_mutexattr_t attr;
211 : pthread_t tcancel;
212 0 : const char *parms = "<GnupgKeyParms format=\"internal\">\n"
213 : "Key-Type: RSA\n"
214 : "Key-Length: 2048\n"
215 : "Subkey-Type: RSA\n"
216 : "Subkey-Length: 2048\n"
217 : "Name-Real: Joe Tester\n"
218 : "Name-Comment: (pp=abc)\n"
219 : "Name-Email: joe@foo.bar\n"
220 : "Expire-Date: 0\n"
221 : "Passphrase: abc\n"
222 : "</GnupgKeyParms>\n";
223 :
224 0 : init_gpgme (GPGME_PROTOCOL_OpenPGP);
225 :
226 0 : err = gpgme_get_engine_info (&info);
227 0 : fail_if_err (err);
228 :
229 : /* The mutex must be recursive, since remove_io_cb (which acquires a
230 : lock) can be called while holding a lock acquired in do_select. */
231 0 : pthread_mutexattr_init (&attr);
232 0 : pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
233 0 : pthread_mutex_init (&lock, &attr);
234 0 : pthread_mutexattr_destroy (&attr);
235 :
236 0 : for (i = 0; i < FDLIST_MAX; i++)
237 0 : fdlist[i].fd = -1;
238 :
239 0 : err = gpgme_new (&ctx);
240 0 : fail_if_err (err);
241 0 : gpgme_set_armor (ctx, 1);
242 0 : gpgme_set_io_cbs (ctx, &io_cbs);
243 0 : op_result.done = 0;
244 :
245 0 : pthread_create (&tcancel, NULL, thread_cancel, ctx);
246 :
247 0 : err = gpgme_op_genkey_start (ctx, parms, NULL, NULL);
248 0 : fail_if_err (err);
249 :
250 0 : my_wait ();
251 :
252 0 : pthread_join (tcancel, NULL);
253 :
254 0 : if (op_result.err)
255 : {
256 0 : if (gpgme_err_code (op_result.err) == GPG_ERR_CANCELED)
257 0 : fputs ("Successfully cancelled\n", stdout);
258 : else
259 : {
260 0 : fprintf (stderr,
261 : "%s:%i: Operation finished with unexpected error: %s\n",
262 : __FILE__, __LINE__, gpgme_strerror (op_result.err));
263 0 : exit (1);
264 : }
265 : }
266 : else
267 0 : fputs ("Successfully finished before cancellation\n", stdout);
268 :
269 0 : gpgme_release (ctx);
270 :
271 0 : return 0;
272 : }
|