A whole bunch of changes to allow building for Windows.
[gnupg.git] / agent / call-pinentry.c
1 /* call-pinnetry.c - fork of the pinentry to query stuff from the user
2  * Copyright (C) 2001, 2002, 2004 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 #include <config.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <assert.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #ifndef HAVE_W32_SYSTEM
32 #include <sys/wait.h>
33 #endif
34 #include <pth.h>
35 #include <assuan.h>
36
37 #include "agent.h"
38 #include "i18n.h"
39
40 #ifdef _POSIX_OPEN_MAX
41 #define MAX_OPEN_FDS _POSIX_OPEN_MAX
42 #else
43 #define MAX_OPEN_FDS 20
44 #endif
45
46
47 /* Because access to the pinentry must be serialized (it is and shall
48    be a global mutual dialog) we should better timeout further
49    requests after some time.  2 minutes seem to be a reasonable
50    time. */
51 #define LOCK_TIMEOUT  (1*60)
52
53 /* The assuan context of the current pinentry. */
54 static assuan_context_t entry_ctx;
55
56 /* The control variable of the connection owning the current pinentry.
57    This is only valid if ENTRY_CTX is not NULL.  Note, that we care
58    only about the value of the pointer and that it should never be
59    dereferenced.  */
60 static ctrl_t entry_owner;
61
62 /* A mutex used to serialize access to the pinentry. */
63 static pth_mutex_t entry_lock;
64
65 /* The thread ID of the popup working thread. */
66 static pth_t  popup_tid;
67
68 /* A flag used in communication between the popup working thread and
69    its stop function. */
70 static int popup_finished;
71
72
73
74 /* Data to be passed to our callbacks, */
75 struct entry_parm_s
76 {
77   int lines;
78   size_t size;
79   unsigned char *buffer;
80 };
81
82
83
84 \f
85 /* This function must be called once to initialize this module.  This
86    has to be done before a second thread is spawned.  We can't do the
87    static initialization because Pth emulation code might not be able
88    to do a static init; in particular, it is not possible for W32. */
89 void
90 initialize_module_call_pinentry (void)
91 {
92   static int initialized;
93
94   if (!initialized)
95     {
96       if (pth_mutex_init (&entry_lock))
97         initialized = 1;
98     }
99 }
100
101
102
103 static void
104 dump_mutex_state (pth_mutex_t *m)
105 {
106 #ifdef _W32_PTH_H
107   log_printf ("unknown under W32");
108 #else
109   if (!(m->mx_state & PTH_MUTEX_INITIALIZED))
110     log_printf ("not_initialized");
111   else if (!(m->mx_state & PTH_MUTEX_LOCKED))
112     log_printf ("not_locked");
113   else
114     log_printf ("locked tid=0x%lx count=%lu", (long)m->mx_owner, m->mx_count);
115 #endif
116 }
117
118
119 /* This function may be called to print infromation pertaining to the
120    current state of this module to the log. */
121 void
122 agent_query_dump_state (void)
123 {
124   log_info ("agent_query_dump_state: entry_lock=");
125   dump_mutex_state (&entry_lock);
126   log_printf ("\n");
127   log_info ("agent_query_dump_state: entry_ctx=%p pid=%ld popup_tid=%p\n",
128             entry_ctx, (long)assuan_get_pid (entry_ctx), popup_tid);
129 }
130
131 /* Called to make sure that a popup window owned by the current
132    connection gets closed. */
133 void
134 agent_reset_query (ctrl_t ctrl)
135 {
136   if (entry_ctx && popup_tid && entry_owner == ctrl)
137     {
138       agent_popup_message_stop (ctrl);
139     }
140 }
141
142
143 /* Unlock the pinentry so that another thread can start one and
144    disconnect that pinentry - we do this after the unlock so that a
145    stalled pinentry does not block other threads.  Fixme: We should
146    have a timeout in Assuan for the disconnect operation. */
147 static int 
148 unlock_pinentry (int rc)
149 {
150   assuan_context_t ctx = entry_ctx;
151
152   entry_ctx = NULL;
153   if (!pth_mutex_release (&entry_lock))
154     {
155       log_error ("failed to release the entry lock\n");
156       if (!rc)
157         rc = gpg_error (GPG_ERR_INTERNAL);
158     }
159   assuan_disconnect (ctx);
160   return rc;
161 }
162
163
164 /* To make sure we leave no secrets in our image after forking of the
165    pinentry, we use this callback. */
166 static void
167 atfork_cb (void *opaque, int where)
168 {
169   if (!where)
170     gcry_control (GCRYCTL_TERM_SECMEM);
171 }
172
173
174 /* Fork off the pin entry if this has not already been done.  Note,
175    that this function must always be used to aquire the lock for the
176    pinentry - we will serialize _all_ pinentry calls.
177  */
178 static int
179 start_pinentry (ctrl_t ctrl)
180 {
181   int rc;
182   const char *pgmname;
183   assuan_context_t ctx;
184   const char *argv[5];
185   int no_close_list[3];
186   int i;
187   pth_event_t evt;
188   const char *tmpstr;
189
190   evt = pth_event (PTH_EVENT_TIME, pth_timeout (LOCK_TIMEOUT, 0));
191   if (!pth_mutex_acquire (&entry_lock, 0, evt))
192     {
193       if (pth_event_occurred (evt))
194         rc = gpg_error (GPG_ERR_TIMEOUT);
195       else
196         rc = gpg_error (GPG_ERR_INTERNAL);
197       pth_event_free (evt, PTH_FREE_THIS);
198       log_error (_("failed to acquire the pinentry lock: %s\n"),
199                  gpg_strerror (rc));
200       return rc;
201     }
202   pth_event_free (evt, PTH_FREE_THIS);
203
204   entry_owner = ctrl;
205
206   if (entry_ctx)
207     return 0; 
208
209   if (opt.verbose)
210     log_info ("starting a new PIN Entry\n");
211       
212   if (fflush (NULL))
213     {
214       gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
215       log_error ("error flushing pending output: %s\n", strerror (errno));
216       return unlock_pinentry (tmperr);
217     }
218
219   if (!opt.pinentry_program || !*opt.pinentry_program)
220     opt.pinentry_program = gnupg_module_name (GNUPG_MODULE_NAME_PINENTRY);
221     pgmname = opt.pinentry_program;
222   if ( !(pgmname = strrchr (opt.pinentry_program, '/')))
223     pgmname = opt.pinentry_program;
224   else
225     pgmname++;
226
227   /* OS X needs the entire file name in argv[0], so that it can locate
228      the resource bundle.  For other systems we stick to the the usual
229      convention of supplying only the name of the program.  */
230 #ifdef __APPLE__
231   argv[0] = opt.pinentry_program;
232 #else /*!__APPLE__*/
233   argv[0] = pgmname;
234 #endif /*__APPLE__*/
235
236   if (ctrl->display && !opt.keep_display)
237     {
238       argv[1] = "--display";
239       argv[2] = ctrl->display;
240       argv[3] = NULL;
241     }
242   else
243     argv[1] = NULL;
244   
245   i=0;
246   if (!opt.running_detached)
247     {
248       if (log_get_fd () != -1)
249         no_close_list[i++] = log_get_fd ();
250       no_close_list[i++] = fileno (stderr);
251     }
252   no_close_list[i] = -1;
253
254   /* Connect to the pinentry and perform initial handshaking */
255   rc = assuan_pipe_connect_ext (&ctx, opt.pinentry_program, argv,
256                                 no_close_list, atfork_cb, NULL, 0);
257   if (rc)
258     {
259       log_error ("can't connect to the PIN entry module: %s\n",
260                  gpg_strerror (rc));
261       return unlock_pinentry (gpg_error (GPG_ERR_NO_PIN_ENTRY));
262     }
263   entry_ctx = ctx;
264
265   if (DBG_ASSUAN)
266     log_debug ("connection to PIN entry established\n");
267
268   rc = assuan_transact (entry_ctx, 
269                         opt.no_grab? "OPTION no-grab":"OPTION grab",
270                         NULL, NULL, NULL, NULL, NULL, NULL);
271   if (rc)
272     return unlock_pinentry (rc);
273   if (ctrl->ttyname)
274     {
275       char *optstr;
276       if (asprintf (&optstr, "OPTION ttyname=%s", ctrl->ttyname) < 0 )
277         return unlock_pinentry (out_of_core ());
278       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
279                             NULL);
280       free (optstr);
281       if (rc)
282         return unlock_pinentry (rc);
283     }
284   if (ctrl->ttytype)
285     {
286       char *optstr;
287       if (asprintf (&optstr, "OPTION ttytype=%s", ctrl->ttytype) < 0 )
288         return unlock_pinentry (out_of_core ());
289       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
290                             NULL);
291       if (rc)
292         return unlock_pinentry (rc);
293     }
294   if (ctrl->lc_ctype)
295     {
296       char *optstr;
297       if (asprintf (&optstr, "OPTION lc-ctype=%s", ctrl->lc_ctype) < 0 )
298         return unlock_pinentry (out_of_core ());
299       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
300                             NULL);
301       if (rc)
302         return unlock_pinentry (rc);
303     }
304   if (ctrl->lc_messages)
305     {
306       char *optstr;
307       if (asprintf (&optstr, "OPTION lc-messages=%s", ctrl->lc_messages) < 0 )
308         return unlock_pinentry (out_of_core ());
309       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
310                             NULL);
311       if (rc)
312         return unlock_pinentry (rc);
313     }
314
315   
316   /* Tell the pinentry the name of a file it shall touch after having
317      messed with the tty.  This is optional and only supported by
318      newer pinentries and thus we do no error checking. */
319   tmpstr = opt.pinentry_touch_file;
320   if (tmpstr && !strcmp (tmpstr, "/dev/null"))
321     tmpstr = NULL;
322   else if (!tmpstr)
323     tmpstr = get_agent_socket_name ();
324   if (tmpstr)
325     {
326       char *optstr;
327       
328       if (asprintf (&optstr, "OPTION touch-file=%s", tmpstr ) < 0 )
329         ;
330       else
331         {
332           assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
333                            NULL);
334           free (optstr);
335         }
336     }
337
338   return 0;
339 }
340
341 /* Returns True is the pinentry is currently active. If WAITSECONDS is
342    greater than zero the function will wait for this many seconds
343    before returning.  */
344 int
345 pinentry_active_p (ctrl_t ctrl, int waitseconds)
346 {
347   if (waitseconds > 0)
348     {
349       pth_event_t evt;
350       int rc;
351
352       evt = pth_event (PTH_EVENT_TIME, pth_timeout (waitseconds, 0));
353       if (!pth_mutex_acquire (&entry_lock, 0, evt))
354         {
355           if (pth_event_occurred (evt))
356             rc = gpg_error (GPG_ERR_TIMEOUT);
357           else
358             rc = gpg_error (GPG_ERR_INTERNAL);
359           pth_event_free (evt, PTH_FREE_THIS);
360           return rc;
361         }
362       pth_event_free (evt, PTH_FREE_THIS);
363     }
364   else
365     {
366       if (!pth_mutex_acquire (&entry_lock, 1, NULL))
367         return gpg_error (GPG_ERR_LOCKED);
368     }
369
370   if (!pth_mutex_release (&entry_lock))
371     log_error ("failed to release the entry lock at %d\n", __LINE__);
372   return 0;
373 }
374
375
376 static int
377 getpin_cb (void *opaque, const void *buffer, size_t length)
378 {
379   struct entry_parm_s *parm = opaque;
380
381   if (!buffer)
382     return 0;
383
384   /* we expect the pin to fit on one line */
385   if (parm->lines || length >= parm->size)
386     return gpg_error (GPG_ERR_ASS_TOO_MUCH_DATA);
387
388   /* fixme: we should make sure that the assuan buffer is allocated in
389      secure memory or read the response byte by byte */
390   memcpy (parm->buffer, buffer, length);
391   parm->buffer[length] = 0;
392   parm->lines++;
393   return 0;
394 }
395
396
397 static int
398 all_digitsp( const char *s)
399 {
400   for (; *s && *s >= '0' && *s <= '9'; s++)
401     ;
402   return !*s;
403 }  
404
405
406 \f
407 /* Call the Entry and ask for the PIN.  We do check for a valid PIN
408    number here and repeat it as long as we have invalid formed
409    numbers. */
410 int
411 agent_askpin (ctrl_t ctrl,
412               const char *desc_text, const char *prompt_text,
413               const char *initial_errtext,
414               struct pin_entry_info_s *pininfo)
415 {
416   int rc;
417   char line[ASSUAN_LINELENGTH];
418   struct entry_parm_s parm;
419   const char *errtext = NULL;
420   int is_pin = 0;
421
422   if (opt.batch)
423     return 0; /* fixme: we should return BAD PIN */
424
425   if (!pininfo || pininfo->max_length < 1)
426     return gpg_error (GPG_ERR_INV_VALUE);
427   if (!desc_text && pininfo->min_digits)
428     desc_text = _("Please enter your PIN, so that the secret key "
429                   "can be unlocked for this session");
430   else if (!desc_text)
431     desc_text = _("Please enter your passphrase, so that the secret key "
432                   "can be unlocked for this session");
433
434   if (prompt_text)
435     is_pin = !!strstr (prompt_text, "PIN");
436   else
437     is_pin = desc_text && strstr (desc_text, "PIN");
438
439   rc = start_pinentry (ctrl);
440   if (rc)
441     return rc;
442
443   snprintf (line, DIM(line)-1, "SETDESC %s", desc_text);
444   line[DIM(line)-1] = 0;
445   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
446   if (rc)
447     return unlock_pinentry (rc);
448
449   snprintf (line, DIM(line)-1, "SETPROMPT %s",
450             prompt_text? prompt_text : is_pin? "PIN:" : "Passphrase:");
451   line[DIM(line)-1] = 0;
452   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
453   if (rc)
454     return unlock_pinentry (rc);
455
456
457   if (initial_errtext)
458     { 
459       snprintf (line, DIM(line)-1, "SETERROR %s", initial_errtext);
460       line[DIM(line)-1] = 0;
461       rc = assuan_transact (entry_ctx, line,
462                             NULL, NULL, NULL, NULL, NULL, NULL);
463       if (rc)
464         return unlock_pinentry (rc);
465     }
466
467   for (;pininfo->failed_tries < pininfo->max_tries; pininfo->failed_tries++)
468     {
469       memset (&parm, 0, sizeof parm);
470       parm.size = pininfo->max_length;
471       parm.buffer = (unsigned char*)pininfo->pin;
472
473       if (errtext)
474         { 
475           /* TRANLATORS: The string is appended to an error message in
476              the pinentry.  The %s is the actual error message, the
477              two %d give the current and maximum number of tries. */
478           snprintf (line, DIM(line)-1, _("SETERROR %s (try %d of %d)"),
479                     errtext, pininfo->failed_tries+1, pininfo->max_tries);
480           line[DIM(line)-1] = 0;
481           rc = assuan_transact (entry_ctx, line,
482                                 NULL, NULL, NULL, NULL, NULL, NULL);
483           if (rc)
484             return unlock_pinentry (rc);
485           errtext = NULL;
486         }
487       
488       rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm,
489                             NULL, NULL, NULL, NULL);
490       /* Most pinentries out in the wild return the old Assuan error code
491          for canceled which gets translated to an assuan Cancel error and
492          not to the code for a user cancel.  Fix this here. */
493       if (rc && gpg_err_source (rc)
494           && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
495         rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
496
497       if (gpg_err_code (rc) == GPG_ERR_ASS_TOO_MUCH_DATA)
498         errtext = is_pin? _("PIN too long")
499                         : _("Passphrase too long");
500       else if (rc)
501         return unlock_pinentry (rc);
502
503       if (!errtext && pininfo->min_digits)
504         {
505           /* do some basic checks on the entered PIN. */
506           if (!all_digitsp (pininfo->pin))
507             errtext = _("Invalid characters in PIN");
508           else if (pininfo->max_digits
509                    && strlen (pininfo->pin) > pininfo->max_digits)
510             errtext = _("PIN too long");
511           else if (strlen (pininfo->pin) < pininfo->min_digits)
512             errtext = _("PIN too short");
513         }
514
515       if (!errtext && pininfo->check_cb)
516         {
517           /* More checks by utilizing the optional callback. */
518           pininfo->cb_errtext = NULL;
519           rc = pininfo->check_cb (pininfo);
520           if (rc == -1 && pininfo->cb_errtext)
521             errtext = pininfo->cb_errtext;
522           else if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
523                    || gpg_err_code (rc) == GPG_ERR_BAD_PIN)
524             errtext = (is_pin? _("Bad PIN")
525                        : _("Bad Passphrase"));
526           else if (rc)
527             return unlock_pinentry (rc);
528         }
529
530       if (!errtext)
531         return unlock_pinentry (0); /* okay, got a PIN or passphrase */
532     }
533
534   return unlock_pinentry (gpg_error (pininfo->min_digits? GPG_ERR_BAD_PIN
535                           : GPG_ERR_BAD_PASSPHRASE));
536 }
537
538
539 \f
540 /* Ask for the passphrase using the supplied arguments.  The returned
541    passphrase needs to be freed by the caller. */
542 int 
543 agent_get_passphrase (ctrl_t ctrl,
544                       char **retpass, const char *desc, const char *prompt,
545                       const char *errtext)
546 {
547
548   int rc;
549   char line[ASSUAN_LINELENGTH];
550   struct entry_parm_s parm;
551
552   *retpass = NULL;
553   if (opt.batch)
554     return gpg_error (GPG_ERR_BAD_PASSPHRASE); 
555
556   rc = start_pinentry (ctrl);
557   if (rc)
558     return rc;
559
560   if (!prompt)
561     prompt = desc && strstr (desc, "PIN")? "PIN": _("Passphrase");
562
563
564   if (desc)
565     snprintf (line, DIM(line)-1, "SETDESC %s", desc);
566   else
567     snprintf (line, DIM(line)-1, "RESET");
568   line[DIM(line)-1] = 0;
569   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
570   if (rc)
571     return unlock_pinentry (rc);
572
573   snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt);
574   line[DIM(line)-1] = 0;
575   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
576   if (rc)
577     return unlock_pinentry (rc);
578
579   if (errtext)
580     {
581       snprintf (line, DIM(line)-1, "SETERROR %s", errtext);
582       line[DIM(line)-1] = 0;
583       rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
584       if (rc)
585         return unlock_pinentry (rc);
586     }
587
588   memset (&parm, 0, sizeof parm);
589   parm.size = ASSUAN_LINELENGTH/2 - 5;
590   parm.buffer = gcry_malloc_secure (parm.size+10);
591   if (!parm.buffer)
592     return unlock_pinentry (out_of_core ());
593
594   assuan_begin_confidential (entry_ctx);
595   rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm,
596                         NULL, NULL, NULL, NULL);
597   /* Most pinentries out in the wild return the old Assuan error code
598      for canceled which gets translated to an assuan Cancel error and
599      not to the code for a user cancel.  Fix this here. */
600   if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
601     rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
602   if (rc)
603     xfree (parm.buffer);
604   else
605     *retpass = parm.buffer;
606   return unlock_pinentry (rc);
607 }
608
609
610 \f
611 /* Pop up the PIN-entry, display the text and the prompt and ask the
612    user to confirm this.  We return 0 for success, ie. the user
613    confirmed it, GPG_ERR_NOT_CONFIRMED for what the text says or an
614    other error. */
615 int 
616 agent_get_confirmation (ctrl_t ctrl,
617                         const char *desc, const char *ok, const char *cancel)
618 {
619   int rc;
620   char line[ASSUAN_LINELENGTH];
621
622   rc = start_pinentry (ctrl);
623   if (rc)
624     return rc;
625
626   if (desc)
627     snprintf (line, DIM(line)-1, "SETDESC %s", desc);
628   else
629     snprintf (line, DIM(line)-1, "RESET");
630   line[DIM(line)-1] = 0;
631   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
632   /* Most pinentries out in the wild return the old Assuan error code
633      for canceled which gets translated to an assuan Cancel error and
634      not to the code for a user cancel.  Fix this here. */
635   if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
636     rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
637
638   if (rc)
639     return unlock_pinentry (rc);
640
641   if (ok)
642     {
643       snprintf (line, DIM(line)-1, "SETOK %s", ok);
644       line[DIM(line)-1] = 0;
645       rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
646       if (rc)
647         return unlock_pinentry (rc);
648     }
649   if (cancel)
650     {
651       snprintf (line, DIM(line)-1, "SETCANCEL %s", cancel);
652       line[DIM(line)-1] = 0;
653       rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
654       if (rc)
655         return unlock_pinentry (rc);
656     }
657
658   rc = assuan_transact (entry_ctx, "CONFIRM", NULL, NULL, NULL, NULL, NULL, NULL);
659   if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
660     rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
661
662   return unlock_pinentry (rc);
663 }
664
665
666 /* The thread running the popup message. */
667 static void *
668 popup_message_thread (void *arg)
669 {
670   /* We use the --one-button hack instead of the MESSAGE command to
671      allow the use of old Pinentries.  Those old Pinentries will then
672      show an additional Cancel button but that is mostly a visual
673      annoyance. */
674   assuan_transact (entry_ctx, "CONFIRM --one-button", 
675                    NULL, NULL, NULL, NULL, NULL, NULL);
676   popup_finished = 1;
677   return NULL;
678 }
679
680
681 /* Pop up a message window similar to the confirm one but keep it open
682    until agent_popup_message_stop has been called.  It is crucial for
683    the caller to make sure that the stop function gets called as soon
684    as the message is not anymore required because the message is
685    system modal and all other attempts to use the pinentry will fail
686    (after a timeout). */
687 int 
688 agent_popup_message_start (ctrl_t ctrl, const char *desc, const char *ok_btn)
689 {
690   int rc;
691   char line[ASSUAN_LINELENGTH];
692   pth_attr_t tattr;
693
694   rc = start_pinentry (ctrl);
695   if (rc)
696     return rc;
697
698   if (desc)
699     snprintf (line, DIM(line)-1, "SETDESC %s", desc);
700   else
701     snprintf (line, DIM(line)-1, "RESET");
702   line[DIM(line)-1] = 0;
703   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
704   if (rc)
705     return unlock_pinentry (rc);
706
707   if (ok_btn)
708     {
709       snprintf (line, DIM(line)-1, "SETOK %s", ok_btn);
710       line[DIM(line)-1] = 0;
711       rc = assuan_transact (entry_ctx, line, NULL,NULL,NULL,NULL,NULL,NULL);
712       if (rc)
713         return unlock_pinentry (rc);
714     }
715
716   tattr = pth_attr_new();
717   pth_attr_set (tattr, PTH_ATTR_JOINABLE, 1);
718   pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024);
719   pth_attr_set (tattr, PTH_ATTR_NAME, "popup-message");
720
721   popup_finished = 0;
722   popup_tid = pth_spawn (tattr, popup_message_thread, NULL);
723   if (!popup_tid)
724     {
725       rc = gpg_error_from_syserror ();
726       log_error ("error spawning popup message handler: %s\n",
727                  strerror (errno) );
728       pth_attr_destroy (tattr);
729       return unlock_pinentry (rc);
730     }
731   pth_attr_destroy (tattr);
732
733   return 0;
734 }
735
736 /* Close a popup window. */
737 void
738 agent_popup_message_stop (ctrl_t ctrl)
739 {
740   int rc;
741   pid_t pid;
742
743   if (!popup_tid || !entry_ctx)
744     {
745       log_debug ("agent_popup_message_stop called with no active popup\n");
746       return; 
747     }
748
749   pid = assuan_get_pid (entry_ctx);
750   if (pid == (pid_t)(-1))
751     ; /* No pid available can't send a kill. */
752   else if (popup_finished)
753     ; /* Already finished and ready for joining. */
754 #ifdef HAVE_W32_SYSTEM
755 #  warning need to implement a kill mechanism for pinentry  
756 #else
757   else if (pid && ((rc=waitpid (pid, NULL, WNOHANG))==-1 || (rc == pid)) )
758     { /* The daemon already died.  No need to send a kill.  However
759          because we already waited for the process, we need to tell
760          assuan that it should not wait again (done by
761          unlock_pinentry). */
762       if (rc == pid)
763         assuan_set_flag (entry_ctx, ASSUAN_NO_WAITPID, 1);
764     }
765   else if (pid > 0)
766     kill (pid, SIGKILL);  /* Need to use SIGKILL due to bad
767                              interaction of SIGINT with Pth. */
768 #endif
769
770   /* Now wait for the thread to terminate. */
771   rc = pth_join (popup_tid, NULL);
772   if (!rc)
773     log_debug ("agent_popup_message_stop: pth_join failed: %s\n",
774                strerror (errno));
775   popup_tid = NULL;
776   entry_owner = NULL;
777
778   /* Now we can close the connection. */
779   unlock_pinentry (0);
780 }
781
782