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