summaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
authorNIIBE Yutaka <gniibe@fsij.org>2020-04-02 08:39:26 +0200
committerNIIBE Yutaka <gniibe@fsij.org>2020-04-02 08:39:26 +0200
commit2ccbcfec121f768574a59aa2ecff22d8b422d61b (patch)
tree15c6a997c0130805e0574f8a4a15cdec4d1d585d /scd
parentscd:p15: Cache the PIN. (diff)
downloadgnupg2-2ccbcfec121f768574a59aa2ecff22d8b422d61b.tar.xz
gnupg2-2ccbcfec121f768574a59aa2ecff22d8b422d61b.zip
scd: New command DEVINFO.
* scd/app.c (notify_cond): New condition variable. (app_send_devinfo, app_wait): New. (scd_update_reader_status_file): Kick NOTIFY_COND. (initialize_module_command): Initialize NOTIFY_COND. * scd/command.c (struct server_local_s): Add watching_status. (cmd_devinfo): New. (register_commands): Add DEVINFO command. (send_client_notifications): Write status change to DEVINFO channel. * scd/scdaemon.h (app_wait, app_send_devinfo): New. GnuPG-bug-id: 4864 Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
Diffstat (limited to 'scd')
-rw-r--r--scd/app.c55
-rw-r--r--scd/command.c208
-rw-r--r--scd/scdaemon.h2
3 files changed, 203 insertions, 62 deletions
diff --git a/scd/app.c b/scd/app.c
index 7193d0ba7..0843ed445 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -42,6 +42,9 @@ send_serialno_and_app_status (card_t card, int with_apps, ctrl_t ctrl);
* applications. */
static npth_mutex_t card_list_lock;
+/* Notification to threads which keep watching the status change. */
+static npth_cond_t notify_cond;
+
/* A list of card contexts. A card is a collection of applications
* (described by app_t) on the same physical token. */
static card_t card_top;
@@ -280,6 +283,37 @@ app_dump_state (void)
}
+gpg_error_t
+app_send_devinfo (ctrl_t ctrl)
+{
+ card_t c;
+ app_t a;
+ int no_device;
+
+ send_status_direct (ctrl, "DEVINFO_START", "");
+
+ npth_mutex_lock (&card_list_lock);
+ no_device = (card_top == NULL);
+ for (c = card_top; c; c = c->next)
+ {
+ char *serialno;
+ char card_info[80];
+
+ serialno = card_get_serialno (c);
+ snprintf (card_info, sizeof card_info, "DEVICE %s %s",
+ strcardtype (c->cardtype), serialno);
+ xfree (serialno);
+
+ for (a = c->app; a; a = a->next)
+ send_status_direct (ctrl, card_info, strapptype (a->apptype));
+ }
+ npth_mutex_unlock (&card_list_lock);
+
+ send_status_direct (ctrl, "DEVINFO_END", "");
+
+ return no_device ? gpg_error (GPG_ERR_NOT_FOUND): 0;
+}
+
/* Check whether the application NAME is allowed. This does not mean
we have support for it though. */
static int
@@ -1934,6 +1968,7 @@ scd_update_reader_status_file (void)
{
card_t card, card_next;
int periodical_check_needed = 0;
+ int reported = 0;
npth_mutex_lock (&card_list_lock);
for (card = card_top; card; card = card_next)
@@ -1968,6 +2003,7 @@ scd_update_reader_status_file (void)
{
report_change (card->slot, card->card_status, status);
send_client_notifications (card, status == 0);
+ reported++;
if (status == 0)
{
@@ -1992,6 +2028,9 @@ scd_update_reader_status_file (void)
}
}
+ if (reported)
+ npth_cond_broadcast (&notify_cond);
+
npth_mutex_unlock (&card_list_lock);
return periodical_check_needed;
@@ -2013,6 +2052,14 @@ initialize_module_command (void)
return err;
}
+ err = npth_cond_init (&notify_cond, NULL);
+ if (err)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("npth_cond_init failed: %s\n", gpg_strerror (err));
+ return err;
+ }
+
return apdu_init ();
}
@@ -2279,3 +2326,11 @@ app_do_with_keygrip (ctrl_t ctrl, int action, const char *keygrip_str,
npth_mutex_unlock (&card_list_lock);
return c;
}
+
+void
+app_wait (void)
+{
+ npth_mutex_lock (&card_list_lock);
+ npth_cond_wait (&notify_cond, &card_list_lock);
+ npth_mutex_unlock (&card_list_lock);
+}
diff --git a/scd/command.c b/scd/command.c
index 030435a9a..159c6f2dc 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -89,12 +89,14 @@ struct server_local_s
/* True if the card has been removed and a reset is required to
continue operation. */
- int card_removed;
+ unsigned int card_removed:1;
/* If set to true we will be terminate ourself at the end of the
this session. */
- int stopme;
+ unsigned int stopme:1;
+ /* If set to true, status change will be reported. */
+ unsigned int watching_status:1;
};
@@ -2127,6 +2129,77 @@ send_keyinfo (ctrl_t ctrl, int data, const char *keygrip_str,
}
+static const char hlp_devinfo[] =
+ "DEVINFO [--watch]\n"
+ "\n"
+ "Return information about devices. If the option --watch is given,\n"
+ "it keeps reporting status change until it detects no device is\n"
+ "available."
+ "The information is returned as a status line using the format:\n"
+ "\n"
+ " DEVICE <card_type> <serialno> <app_type>\n"
+ "\n"
+ "CARD_TYPE is the type of the card.\n"
+ "\n"
+ "SERIALNO is an ASCII string with the serial number of the\n"
+ " smartcard. If the serial number is not known a single\n"
+ " dash '-' is used instead.\n"
+ "\n"
+ "APP_TYPE is the type of the application.\n"
+ "\n"
+ "More information may be added in the future.";
+static gpg_error_t
+cmd_devinfo (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ int watch = 0;
+
+ if (has_option (line, "--watch"))
+ {
+ watch = 1;
+ ctrl->server_local->watching_status = 1;
+ }
+
+ /* Firstly, send information of available devices. */
+ err = app_send_devinfo (ctrl);
+
+ /* If not watching, that's all. */
+ if (!watch)
+ return err;
+
+ if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND)
+ return err;
+
+ /* Secondly, try to open device(s) available. */
+
+ /* Clear the remove flag so that the open_card is able to reread it. */
+ if (ctrl->server_local->card_removed)
+ ctrl->server_local->card_removed = 0;
+
+ if ((err = open_card (ctrl))
+ && gpg_err_code (err) != GPG_ERR_ENODEV)
+ return err;
+
+ err = 0;
+
+ /* Remove reference(s) to the card. */
+ ctrl->card_ctx = NULL;
+ ctrl->current_apptype = APPTYPE_NONE;
+ card_unref (ctrl->card_ctx);
+
+ /* Then, keep watching the status change. */
+ while (!err)
+ {
+ app_wait ();
+
+ /* Send information of available devices. */
+ err = app_send_devinfo (ctrl);
+ }
+
+ ctrl->server_local->watching_status = 0;
+ return 0;
+}
/* Return true if the command CMD implements the option OPT. */
static int
@@ -2179,6 +2252,7 @@ register_commands (assuan_context_t ctx)
{ "APDU", cmd_apdu, hlp_apdu },
{ "KILLSCD", cmd_killscd, hlp_killscd },
{ "KEYINFO", cmd_keyinfo, hlp_keyinfo },
+ { "DEVINFO", cmd_devinfo, hlp_devinfo },
{ NULL }
};
int i, rc;
@@ -2671,76 +2745,86 @@ send_client_notifications (card_t card, int removal)
struct server_local_s *sl;
for (sl=session_list; sl; sl = sl->next_session)
- if (sl->ctrl_backlink && sl->ctrl_backlink->card_ctx == card)
- {
- pid_t pid;
+ {
+ if (sl->watching_status)
+ {
+ if (removal)
+ assuan_write_status (sl->assuan_ctx, "DEVINFO_STATUS", "removal");
+ else
+ assuan_write_status (sl->assuan_ctx, "DEVINFO_STATUS", "new");
+ }
+
+ if (sl->ctrl_backlink && sl->ctrl_backlink->card_ctx == card)
+ {
+ pid_t pid;
#ifdef HAVE_W32_SYSTEM
- HANDLE handle;
+ HANDLE handle;
#else
- int signo;
+ int signo;
#endif
- if (removal)
- {
- sl->ctrl_backlink->card_ctx = NULL;
- sl->ctrl_backlink->current_apptype = APPTYPE_NONE;
- sl->card_removed = 1;
- card_unref_locked (card);
- }
+ if (removal)
+ {
+ sl->ctrl_backlink->card_ctx = NULL;
+ sl->ctrl_backlink->current_apptype = APPTYPE_NONE;
+ sl->card_removed = 1;
+ card_unref_locked (card);
+ }
- if (!sl->event_signal || !sl->assuan_ctx)
- continue;
+ if (!sl->event_signal || !sl->assuan_ctx)
+ continue;
- pid = assuan_get_pid (sl->assuan_ctx);
+ pid = assuan_get_pid (sl->assuan_ctx);
#ifdef HAVE_W32_SYSTEM
- handle = sl->event_signal;
- for (kidx=0; kidx < killidx; kidx++)
- if (killed[kidx].pid == pid
- && killed[kidx].handle == handle)
- break;
- if (kidx < killidx)
- log_info ("event %p (%p) already triggered for client %d\n",
- sl->event_signal, handle, (int)pid);
- else
- {
- log_info ("triggering event %p (%p) for client %d\n",
+ handle = sl->event_signal;
+ for (kidx=0; kidx < killidx; kidx++)
+ if (killed[kidx].pid == pid
+ && killed[kidx].handle == handle)
+ break;
+ if (kidx < killidx)
+ log_info ("event %p (%p) already triggered for client %d\n",
sl->event_signal, handle, (int)pid);
- if (!SetEvent (handle))
- log_error ("SetEvent(%p) failed: %s\n",
- sl->event_signal, w32_strerror (-1));
- if (killidx < DIM (killed))
- {
- killed[killidx].pid = pid;
- killed[killidx].handle = handle;
- killidx++;
- }
- }
+ else
+ {
+ log_info ("triggering event %p (%p) for client %d\n",
+ sl->event_signal, handle, (int)pid);
+ if (!SetEvent (handle))
+ log_error ("SetEvent(%p) failed: %s\n",
+ sl->event_signal, w32_strerror (-1));
+ if (killidx < DIM (killed))
+ {
+ killed[killidx].pid = pid;
+ killed[killidx].handle = handle;
+ killidx++;
+ }
+ }
#else /*!HAVE_W32_SYSTEM*/
- signo = sl->event_signal;
-
- if (pid != (pid_t)(-1) && pid && signo > 0)
- {
- for (kidx=0; kidx < killidx; kidx++)
- if (killed[kidx].pid == pid
- && killed[kidx].signo == signo)
- break;
- if (kidx < killidx)
- log_info ("signal %d already sent to client %d\n",
- signo, (int)pid);
- else
- {
- log_info ("sending signal %d to client %d\n",
+ signo = sl->event_signal;
+
+ if (pid != (pid_t)(-1) && pid && signo > 0)
+ {
+ for (kidx=0; kidx < killidx; kidx++)
+ if (killed[kidx].pid == pid
+ && killed[kidx].signo == signo)
+ break;
+ if (kidx < killidx)
+ log_info ("signal %d already sent to client %d\n",
signo, (int)pid);
- kill (pid, signo);
- if (killidx < DIM (killed))
- {
- killed[killidx].pid = pid;
- killed[killidx].signo = signo;
- killidx++;
- }
- }
- }
+ else
+ {
+ log_info ("sending signal %d to client %d\n",
+ signo, (int)pid);
+ kill (pid, signo);
+ if (killidx < DIM (killed))
+ {
+ killed[killidx].pid = pid;
+ killed[killidx].signo = signo;
+ killidx++;
+ }
+ }
+ }
#endif /*!HAVE_W32_SYSTEM*/
- }
+ }
+ }
}
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
index 391f16578..5ae656ca2 100644
--- a/scd/scdaemon.h
+++ b/scd/scdaemon.h
@@ -153,5 +153,7 @@ int get_active_connection_count (void);
/*-- app.c --*/
int scd_update_reader_status_file (void);
+void app_wait (void);
+gpg_error_t app_send_devinfo (ctrl_t ctrl);
#endif /*SCDAEMON_H*/