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