diff options
author | Werner Koch <wk@gnupg.org> | 2006-11-20 17:49:41 +0100 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2006-11-20 17:49:41 +0100 |
commit | 5885142c83823b153689acd272103403127a3cd3 (patch) | |
tree | 514e8325f9201775785b3249b176e9ac051776ff /scd | |
parent | No need for thsi script. Use ./autogen.sh --build-w32 instead. (diff) | |
download | gnupg2-5885142c83823b153689acd272103403127a3cd3.tar.xz gnupg2-5885142c83823b153689acd272103403127a3cd3.zip |
Made some PIN pads work.
Some cleanups for 64 bit CPUs.
Diffstat (limited to 'scd')
-rw-r--r-- | scd/ChangeLog | 34 | ||||
-rw-r--r-- | scd/apdu.c | 22 | ||||
-rw-r--r-- | scd/app-openpgp.c | 49 | ||||
-rw-r--r-- | scd/app-p15.c | 3 | ||||
-rw-r--r-- | scd/ccid-driver.c | 73 | ||||
-rw-r--r-- | scd/command.c | 58 | ||||
-rw-r--r-- | scd/iso7816.c | 2 | ||||
-rw-r--r-- | scd/scdaemon.c | 49 | ||||
-rw-r--r-- | scd/scdaemon.h | 9 |
9 files changed, 225 insertions, 74 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog index f637e5ad8..3c1c373a6 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,37 @@ +2006-11-20 Werner Koch <wk@g10code.com> + + * app-openpgp.c (verify_chv2): Support for keypads (only CHV2). + + * ccid-driver.c (ccid_transceive_secure): Made it work for Kaan + and SCM. + +2006-11-17 Werner Koch <wk@g10code.com> + + * ccid-driver.c (scan_or_find_devices): Use DEBUGOUT_2 instead of + log_debug. Removed few other log_debug. + + * iso7816.c (iso7816_check_keypad): Allow for a SW of 0. + + * command.c (pin_cb): New mode to prompt for a keypad entry. + + * scdaemon.c (main) <gpgconf-list>: Add disable-keypad. + +2006-11-15 Werner Koch <wk@g10code.com> + + * app-p15.c (read_ef_odf): Cast one printf arg. + + * scdaemon.h (struct server_control_s): Add field THREAD_STARTUP. + * command.c (scd_command_handler): Add new arg CTRL. + * scdaemon.c (scd_init_default_ctrl): Made static. + (scd_deinit_default_ctrl): New. + (start_connection_thread): Call init/deinit of ctrl. + (handle_connections): Allocate CTRL. + + * apdu.c (PCSC_ERR_MASK): New. + (reset_pcsc_reader, pcsc_get_status, pcsc_send_apdu) + (close_pcsc_reader, open_pcsc_reader): Use it after shifting error + values. Reported by Henrik Nordstrom. Fixes bug #724. + 2006-10-24 Werner Koch <wk@g10code.com> * scdaemon.h (GCRY_MD_USER_TLS_MD5SHA1): New. diff --git a/scd/apdu.c b/scd/apdu.c index 242849adb..e83ce3c3e 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -222,6 +222,11 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn); #define PCSC_E_READER_UNAVAILABLE 0x80100017 #define PCSC_W_REMOVED_CARD 0x80100069 +/* The PC/SC error is defined as a long as per specs. Due to left + shifts bit 31 will get sign extended. We use this mask to fix + it. */ +#define PCSC_ERR_MASK(a) ((a) & 0xffffffff) + struct pcsc_io_request_s { @@ -739,7 +744,7 @@ pcsc_error_to_sw (long ec) { int rc; - switch (ec) + switch ( PCSC_ERR_MASK (ec) ) { case 0: rc = 0; break; @@ -834,7 +839,8 @@ reset_pcsc_reader (int slot) sw = SW_HOST_GENERAL_ERROR; goto command_failed; } - err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8]; + err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) + | (msgbuf[7] << 8 ) | msgbuf[8]); if (err) { log_error ("PC/SC RESET failed: %s (0x%lx)\n", @@ -981,7 +987,8 @@ pcsc_get_status (int slot, unsigned int *status) goto command_failed; } len -= 4; /* Already read the error code. */ - err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8]; + err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) + | (msgbuf[7] << 8 ) | msgbuf[8]); if (err) { log_error ("pcsc_status failed: %s (0x%lx)\n", @@ -1151,7 +1158,8 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, goto command_failed; } len -= 4; /* Already read the error code. */ - err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8]; + err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) + | (msgbuf[7] << 8 ) | msgbuf[8]); if (err) { log_error ("pcsc_transmit failed: %s (0x%lx)\n", @@ -1283,7 +1291,8 @@ close_pcsc_reader (int slot) goto command_failed; } len -= 4; /* Already read the error code. */ - err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8]; + err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) + | (msgbuf[7] << 8 ) | msgbuf[8]); if (err) log_error ("pcsc_close failed: %s (0x%lx)\n", pcsc_error_string (err), err); @@ -1470,7 +1479,8 @@ open_pcsc_reader (const char *portstr) (unsigned long)len); goto command_failed; } - err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8]; + err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16) + | (msgbuf[7] << 8 ) | msgbuf[8]); if (err) { log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err)); diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 466f37c57..6c2eb82fb 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -1291,27 +1291,52 @@ verify_chv2 (app_t app, { char *pinvalue; iso7816_pininfo_t pininfo; + int did_keypad = 0; memset (&pininfo, 0, sizeof pininfo); pininfo.mode = 1; pininfo.minlen = 6; - rc = pincb (pincb_arg, "PIN", &pinvalue); - if (rc) + if (!opt.disable_keypad + && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) ) { - log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc)); - return rc; + /* The reader supports the verify command through the keypad. */ + did_keypad = 1; + rc = pincb (pincb_arg, + _("||Please enter your PIN at the reader's keypad"), + NULL); + if (rc) + { + log_info (_("PIN callback returned error: %s\n"), + gpg_strerror (rc)); + return rc; + } + rc = iso7816_verify_kp (app->slot, 0x82, "", 0, &pininfo); + /* Dismiss the prompt. */ + pincb (pincb_arg, NULL, NULL); } - - if (strlen (pinvalue) < 6) + else { - log_error (_("PIN for CHV%d is too short;" - " minimum length is %d\n"), 2, 6); - xfree (pinvalue); - return gpg_error (GPG_ERR_BAD_PIN); + /* The reader has no keypad or we don't want to use it. */ + rc = pincb (pincb_arg, "PIN", &pinvalue); + if (rc) + { + log_info (_("PIN callback returned error: %s\n"), + gpg_strerror (rc)); + return rc; + } + + if (strlen (pinvalue) < 6) + { + log_error (_("PIN for CHV%d is too short;" + " minimum length is %d\n"), 2, 6); + xfree (pinvalue); + return gpg_error (GPG_ERR_BAD_PIN); + } + + rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue)); } - rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue)); if (rc) { log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc)); @@ -1321,7 +1346,7 @@ verify_chv2 (app_t app, } app->did_chv2 = 1; - if (!app->did_chv1 && !app->force_chv1) + if (!app->did_chv1 && !app->force_chv1 && !did_keypad) { rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue)); if (gpg_err_code (rc) == GPG_ERR_BAD_PIN) diff --git a/scd/app-p15.c b/scd/app-p15.c index f11de5902..475226270 100644 --- a/scd/app-p15.c +++ b/scd/app-p15.c @@ -692,7 +692,8 @@ read_ef_odf (app_t app, unsigned short odf_fid) } if (buflen) - log_info ("warning: %u bytes of garbage detected at end of ODF\n", buflen); + log_info ("warning: %u bytes of garbage detected at end of ODF\n", + (unsigned int)buflen); xfree (buffer); return 0; diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index e990f757a..35fedfd55 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -200,7 +200,8 @@ enum { VENDOR_CHERRY = 0x046a, VENDOR_SCM = 0x04e6, VENDOR_OMNIKEY= 0x076b, - VENDOR_GEMPC = 0x08e6 + VENDOR_GEMPC = 0x08e6, + VENDOR_KAAN = 0x0d46 }; /* A list and a table with special transport descriptions. */ @@ -990,11 +991,10 @@ scan_or_find_devices (int readerno, const char *readerid, fd = open (transports[i].name, O_RDWR); if (fd == -1) { - log_debug ("failed to open `%s': %s\n", + DEBUGOUT_2 ("failed to open `%s': %s\n", transports[i].name, strerror (errno)); continue; } - log_debug ("opened `%s': fd=%d\n", transports[i].name, fd); rid = malloc (strlen (transports[i].name) + 30 + 10); if (!rid) @@ -1047,7 +1047,6 @@ scan_or_find_devices (int readerno, const char *readerid, } free (rid); close (fd); - log_debug ("closed fd %d\n", fd); } if (scan_mode) @@ -1208,10 +1207,7 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid) if (idev) usb_close (idev); if (dev_fd != -1) - { - close (dev_fd); - log_debug ("closed fd %d\n", dev_fd); - } + close (dev_fd); free (*handle); *handle = NULL; } @@ -1254,7 +1250,6 @@ do_close_reader (ccid_driver_t handle) if (handle->dev_fd != -1) { close (handle->dev_fd); - log_debug ("closed fd %d\n", handle->dev_fd); handle->dev_fd = -1; } } @@ -1324,10 +1319,7 @@ ccid_shutdown_reader (ccid_driver_t handle) usb_close (handle->idev); handle->idev = NULL; if (handle->dev_fd != -1) - { - close (handle->dev_fd); - log_debug ("closed fd %d\n", handle->dev_fd); - } + close (handle->dev_fd); handle->dev_fd = -1; } @@ -2369,10 +2361,24 @@ ccid_transceive_secure (ccid_driver_t handle, || pinlen_min > pinlen_max) return CCID_DRIVER_ERR_INV_VALUE; - /* We have only tested this with an SCM reader so better don't risk - anything and do not allow the use with other readers. */ - if (handle->id_vendor != VENDOR_SCM) - return CCID_DRIVER_ERR_NOT_SUPPORTED; + /* We have only tested a few readers so better don't risk anything + and do not allow the use with other readers. */ + switch (handle->id_vendor) + { + case VENDOR_SCM: /* Tested with SPR 532. */ + case VENDOR_KAAN: /* Tested with KAAN Advanced (1.02). */ + break; + /* The CHERRY XX44 does not yet work. I have not investigated it + closer because there is another problem: It echos a "*" for + each entered character and we somehow need to arrange that it + doesn't get to the tty at all. Given thate are running + without a control terminal there is not much we can do about. + A weird hack using pinentry comes in mind but I doubnt that + this is a clean solution. Need to contact Cherry. + */ + default: + return CCID_DRIVER_ERR_NOT_SUPPORTED; + } if (testmode) return 0; /* Success */ @@ -2390,7 +2396,7 @@ ccid_transceive_secure (ccid_driver_t handle, msg[0] = PC_to_RDR_Secure; msg[5] = 0; /* slot */ msg[6] = seqno = handle->seqno++; - msg[7] = 4; /* bBWI */ + msg[7] = 0; /* bBWI */ msg[8] = 0; /* RFU */ msg[9] = 0; /* RFU */ msg[10] = 0; /* Perform PIN verification. */ @@ -2411,8 +2417,8 @@ ccid_transceive_secure (ccid_driver_t handle, msg[14] = 0x00; /* bmPINLengthFormat: Units are bytes, position is 0. */ } - msg[15] = pinlen_min; /* wPINMaxExtraDigit-Minimum. */ - msg[16] = pinlen_max; /* wPINMaxExtraDigit-Maximum. */ + msg[15] = pinlen_max; /* wPINMaxExtraDigit-Maximum. */ + msg[16] = pinlen_min; /* wPINMaxExtraDigit-Minimum. */ msg[17] = 0x02; /* bEntryValidationCondition: Validation key pressed */ if (pinlen_min && pinlen_max && pinlen_min == pinlen_max) @@ -2424,13 +2430,14 @@ ccid_transceive_secure (ccid_driver_t handle, /* bTeoProlog follows: */ msg[22] = handle->nonnull_nad? ((1 << 4) | 0): 0; msg[23] = ((handle->t1_ns & 1) << 6); /* I-block */ - msg[24] = 4; /* apdulen. */ + msg[24] = 0; /* The apdulen will be filled in by the reader. */ /* APDU follows: */ msg[25] = apdu_buf[0]; /* CLA */ msg[26] = apdu_buf[1]; /* INS */ msg[27] = apdu_buf[2]; /* P1 */ msg[28] = apdu_buf[3]; /* P2 */ msglen = 29; + /* An EDC is not required. */ set_msg_len (msg, msglen - 10); DEBUGOUT ("sending"); @@ -2444,12 +2451,30 @@ ccid_transceive_secure (ccid_driver_t handle, msg = recv_buffer; rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen, - RDR_to_PC_DataBlock, seqno, 5000, 0); + RDR_to_PC_DataBlock, seqno, 30000, 0); if (rc) return rc; tpdu = msg + 10; tpdulen = msglen - 10; + + if (handle->apdu_level) + { + if (resp) + { + if (tpdulen > maxresplen) + { + DEBUGOUT_2 ("provided buffer too short for received data " + "(%u/%u)\n", + (unsigned int)tpdulen, (unsigned int)maxresplen); + return CCID_DRIVER_ERR_INV_VALUE; + } + + memcpy (resp, tpdu, tpdulen); + *nresp = tpdulen; + } + return 0; + } if (tpdulen < 4) { @@ -2595,7 +2620,7 @@ main (int argc, char **argv) { int rc; ccid_driver_t ccid; - unsigned int slotstat; + int slotstat; unsigned char result[512]; size_t resultlen; int no_pinpad = 0; @@ -2623,7 +2648,7 @@ main (int argc, char **argv) } else if ( !strcmp (*argv, "--debug")) { - ccid_set_debug_level (1); + ccid_set_debug_level (ccid_set_debug_level (-1)+1); argc--; argv++; } else if ( !strcmp (*argv, "--no-poll")) diff --git a/scd/command.c b/scd/command.c index 5116461d4..0dbd32df8 100644 --- a/scd/command.c +++ b/scd/command.c @@ -721,6 +721,31 @@ pin_cb (void *opaque, const char *info, char **retstr) unsigned char *value; size_t valuelen; + if (!retstr) + { + /* We prompt for keypad entry. To make sure that the popup has + been show we use an inquire and not just a status message. + We ignore any value returned. */ + if (info) + { + log_debug ("prompting for keypad entry '%s'\n", info); + rc = asprintf (&command, "POPUPKEYPADPROMPT %s", info); + if (rc < 0) + return gpg_error (gpg_err_code_from_errno (errno)); + rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN); + free (command); + } + else + { + log_debug ("dismiss keypad entry prompt\n"); + rc = assuan_inquire (ctx, "DISMISSKEYPADPROMPT", + &value, &valuelen, MAXLEN_PIN); + } + if (!rc) + xfree (value); + return rc; + } + *retstr = NULL; log_debug ("asking for PIN '%s'\n", info); @@ -1584,14 +1609,10 @@ register_commands (assuan_context_t ctx) /* Startup the server. If FD is given as -1 this is simple pipe server, otherwise it is a regular server. */ void -scd_command_handler (int fd) +scd_command_handler (ctrl_t ctrl, int fd) { int rc; assuan_context_t ctx; - struct server_control_s ctrl; - - memset (&ctrl, 0, sizeof ctrl); - scd_init_default_ctrl (&ctrl); if (fd == -1) { @@ -1622,20 +1643,20 @@ scd_command_handler (int fd) /* Allocate and initialize the server object. Put it into the list of active sessions. */ - ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local); - ctrl.server_local->next_session = session_list; - session_list = ctrl.server_local; - ctrl.server_local->ctrl_backlink = &ctrl; - ctrl.server_local->assuan_ctx = ctx; + ctrl->server_local = xcalloc (1, sizeof *ctrl->server_local); + ctrl->server_local->next_session = session_list; + session_list = ctrl->server_local; + ctrl->server_local->ctrl_backlink = ctrl; + ctrl->server_local->assuan_ctx = ctx; if (DBG_ASSUAN) assuan_set_log_stream (ctx, log_get_stream ()); /* We open the reader right at startup so that the ticker is able to update the status file. */ - if (ctrl.reader_slot == -1) + if (ctrl->reader_slot == -1) { - ctrl.reader_slot = get_reader_slot (); + ctrl->reader_slot = get_reader_slot (); } /* Command processing loop. */ @@ -1661,23 +1682,24 @@ scd_command_handler (int fd) } /* Cleanup. */ - do_reset (&ctrl, 0); + do_reset (ctrl, 0); /* Release the server object. */ - if (session_list == ctrl.server_local) - session_list = ctrl.server_local->next_session; + if (session_list == ctrl->server_local) + session_list = ctrl->server_local->next_session; else { struct server_local_s *sl; for (sl=session_list; sl->next_session; sl = sl->next_session) - if (sl->next_session == ctrl.server_local) + if (sl->next_session == ctrl->server_local) break; if (!sl->next_session) BUG (); - sl->next_session = ctrl.server_local->next_session; + sl->next_session = ctrl->server_local->next_session; } - xfree (ctrl.server_local); + xfree (ctrl->server_local); + ctrl->server_local = NULL; /* Release the Assuan context. */ assuan_deinit_server (ctx); diff --git a/scd/iso7816.c b/scd/iso7816.c index 250ee40d5..943961da7 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -235,7 +235,7 @@ iso7816_check_keypad (int slot, int command, iso7816_pininfo_t *pininfo) sw = apdu_check_keypad (slot, command, pininfo->mode, pininfo->minlen, pininfo->maxlen, pininfo->padlen); - return map_sw (sw); + return iso7816_map_sw (sw); } diff --git a/scd/scdaemon.c b/scd/scdaemon.c index 0ee3f4bc6..6993b75e6 100644 --- a/scd/scdaemon.c +++ b/scd/scdaemon.c @@ -600,7 +600,7 @@ main (int argc, char **argv ) printf ("disable-ccid:%lu:\n", GC_OPT_FLAG_NONE ); #endif printf ("allow-admin:%lu:\n", GC_OPT_FLAG_NONE ); - + printf ("disable-keypad:%lu:\n", GC_OPT_FLAG_NONE ); scd_exit (0); } @@ -615,6 +615,7 @@ main (int argc, char **argv ) if (pipe_server) { /* This is the simple pipe based server */ + ctrl_t ctrl; pth_attr_t tattr; int fd = -1; @@ -656,10 +657,19 @@ main (int argc, char **argv ) pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 512*1024); pth_attr_set (tattr, PTH_ATTR_NAME, "pipe-connection"); - if (!pth_spawn (tattr, start_connection_thread, (void*)(-1))) + ctrl = xtrycalloc (1, sizeof *ctrl); + if ( !ctrl ) + { + log_error ("error allocating connection control data: %s\n", + strerror (errno) ); + scd_exit (2); + } + ctrl->thread_startup.fd = -1; + if ( !pth_spawn (tattr, start_connection_thread, ctrl) ) { log_error ("error spawning pipe connection handler: %s\n", strerror (errno) ); + xfree (ctrl); scd_exit (2); } @@ -810,12 +820,18 @@ scd_exit (int rc) } -void +static void scd_init_default_ctrl (ctrl_t ctrl) { ctrl->reader_slot = -1; } +static void +scd_deinit_default_ctrl (ctrl_t ctrl) +{ + +} + /* Return the name of the socket to be used to connect to this process. If no socket is available, return NULL. */ @@ -1007,23 +1023,26 @@ create_server_socket (int is_standard_name, const char *name) static void * start_connection_thread (void *arg) { - int fd = (int)arg; + ctrl_t ctrl = arg; + scd_init_default_ctrl (ctrl); if (opt.verbose) - log_info (_("handler for fd %d started\n"), fd); + log_info (_("handler for fd %d started\n"), ctrl->thread_startup.fd); - scd_command_handler (fd); + scd_command_handler (ctrl, ctrl->thread_startup.fd); if (opt.verbose) - log_info (_("handler for fd %d terminated\n"), fd); + log_info (_("handler for fd %d terminated\n"), ctrl->thread_startup.fd); /* If this thread is the pipe connection thread, flag that a shutdown is required. With the next ticker event and given that no other connections are running the shutdown will then happen. */ - if (fd == -1) + if (ctrl->thread_startup.fd == -1) shutdown_pending = 1; + scd_deinit_default_ctrl (ctrl); + xfree (ctrl); return NULL; } @@ -1137,23 +1156,33 @@ handle_connections (int listen_fd) if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset)) { + ctrl_t ctrl; + plen = sizeof paddr; fd = pth_accept (listen_fd, (struct sockaddr *)&paddr, &plen); if (fd == -1) { log_error ("accept failed: %s\n", strerror (errno)); } + else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) ) + { + log_error ("error allocating connection control data: %s\n", + strerror (errno) ); + close (fd); + } else { char threadname[50]; + snprintf (threadname, sizeof threadname-1, "conn fd=%d", fd); threadname[sizeof threadname -1] = 0; pth_attr_set (tattr, PTH_ATTR_NAME, threadname); - - if (!pth_spawn (tattr, start_connection_thread, (void*)fd)) + ctrl->thread_startup.fd = fd; + if (!pth_spawn (tattr, start_connection_thread, ctrl)) { log_error ("error spawning connection handler: %s\n", strerror (errno) ); + xfree (ctrl); close (fd); } } diff --git a/scd/scdaemon.h b/scd/scdaemon.h index 2d20b0231..7e5b9fb9b 100644 --- a/scd/scdaemon.h +++ b/scd/scdaemon.h @@ -90,6 +90,12 @@ struct app_ctx_s; struct server_control_s { + /* Private data used to fire up the connection thread. We use this + structure do avoid an extra allocation for just a few bytes. */ + struct { + int fd; + } thread_startup; + /* Local data of the server; used only in command.c. */ struct server_local_s *server_local; @@ -115,11 +121,10 @@ typedef struct app_ctx_s *app_t; /*-- scdaemon.c --*/ void scd_exit (int rc); -void scd_init_default_ctrl (ctrl_t ctrl); const char *scd_get_socket_name (void); /*-- command.c --*/ -void scd_command_handler (int); +void scd_command_handler (ctrl_t, int); void send_status_info (ctrl_t ctrl, const char *keyword, ...) GNUPG_GCC_A_SENTINEL(1); void scd_update_reader_status_file (void); |