diff options
Diffstat (limited to 'scd/command.c')
-rw-r--r-- | scd/command.c | 276 |
1 files changed, 152 insertions, 124 deletions
diff --git a/scd/command.c b/scd/command.c index 1b7a8f67e..805164d0f 100644 --- a/scd/command.c +++ b/scd/command.c @@ -62,9 +62,26 @@ && (c)->reader_slot == locked_session->ctrl_backlink->reader_slot) +/* This structure is used to keep track of open readers (slots). */ +struct slot_status_s +{ + int valid; /* True if the other objects are valid. */ + int slot; /* Slot number of the reader or -1 if not open. */ + + int reset_failed; /* A reset failed. */ + + int any; /* Flag indicating whether any status check has been + done. This is set once to indicate that the status + tracking for the slot has been initialized. */ + unsigned int status; /* Last status of the slot. */ + unsigned int changed; /* Last change counter of teh slot. */ +}; + + /* Data used to associate an Assuan context with local server data. This object describes the local properties of one session. */ -struct server_local_s { +struct server_local_s +{ /* We keep a list of all active sessions with the anchor at SESSION_LIST (see below). This field is used for linking. */ struct server_local_s *next_session; @@ -86,6 +103,10 @@ struct server_local_s { }; +/* The table with information on all used slots. */ +static struct slot_status_s slot_table[10]; + + /* To keep track of all running sessions, we link all active server contexts and the anchor in this variable. */ static struct server_local_s *session_list; @@ -94,6 +115,13 @@ static struct server_local_s *session_list; in this variable. */ static struct server_local_s *locked_session; +/* While doing a reset we need to make sure that the ticker does not + call scd_update_reader_status_file while we are using it. */ +static pth_mutex_t status_file_update_lock = PTH_MUTEX_INIT; + + +/*-- Local prototypes --*/ +static void update_reader_status_file (void); @@ -107,7 +135,9 @@ update_card_removed (int slot, int value) for (sl=session_list; sl; sl = sl->next_session) if (sl->ctrl_backlink && sl->ctrl_backlink->reader_slot == slot) - sl->card_removed = value; + { + sl->card_removed = value; + } if (value) application_notify_card_removed (slot); } @@ -126,69 +156,52 @@ has_option (const char *line, const char *name) } -/* Reset the card and free the application context. With DO_CLOSE set - to true and this is the last session with a reference to the - reader, close the reader and don't do just a reset. */ +/* Reset the card and free the application context. With SEND_RESET + set to true actually send a RESET to the reader. */ static void -do_reset (ctrl_t ctrl, int do_close) +do_reset (ctrl_t ctrl, int send_reset) { int slot = ctrl->reader_slot; + if (!(slot == -1 || (slot >= 0 && slot < DIM(slot_table)))) + BUG (); + if (ctrl->app_ctx) { release_application (ctrl->app_ctx); ctrl->app_ctx = NULL; } - if (ctrl->reader_slot != -1) - { - struct server_local_s *sl; - /* If we are the only session with the reader open we may close - it. If not, do a reset unless a lock is held on the - reader. */ - for (sl=session_list; sl; sl = sl->next_session) - if (sl != ctrl->server_local - && sl->ctrl_backlink->reader_slot == ctrl->reader_slot) - break; - if (sl) /* There is another session with the reader open. */ - { - if ( IS_LOCKED (ctrl) ) /* If it is locked, release it. */ - ctrl->reader_slot = -1; - else - { - if (do_close) /* Always mark reader unused. */ - ctrl->reader_slot = -1; - else if (apdu_reset (ctrl->reader_slot)) /* Reset only if - not locked */ - { - /* The reset failed. Mark the reader as closed. */ - ctrl->reader_slot = -1; - } - - if (locked_session && ctrl->server_local == locked_session) - { - locked_session = NULL; - log_debug ("implicitly unlocking due to RESET\n"); - } - } - } - else /* No other session has the reader open. */ + if (slot != -1 && send_reset && !IS_LOCKED (ctrl) ) + { + if (apdu_reset (slot)) { - if (do_close || apdu_reset (ctrl->reader_slot)) - { - apdu_close_reader (ctrl->reader_slot); - ctrl->reader_slot = -1; - } - if ( IS_LOCKED (ctrl) ) - { - log_debug ("WARNING: cleaning up stale session lock\n"); - locked_session = NULL; - } + slot_table[slot].reset_failed = 1; } } + ctrl->reader_slot = -1; + + /* If we hold a lock, unlock now. */ + if (locked_session && ctrl->server_local == locked_session) + { + locked_session = NULL; + log_info ("implicitly unlocking due to RESET\n"); + } - /* Reset card removed flag for the current reader. */ + /* Reset card removed flag for the current reader. We need to take + the lock here so that the ticker thread won't concurrently try to + update the file. Note that the update function will set the card + removed flag and we will later reset it - not a particualar nice + way of implementing it but it works. */ + if (!pth_mutex_acquire (&status_file_update_lock, 0, NULL)) + { + log_error ("failed to acquire status_fle_update lock\n"); + return; + } + update_reader_status_file (); update_card_removed (slot, 0); + if (!pth_mutex_release (&status_file_update_lock)) + log_error ("failed to release status_file_update lock\n"); } @@ -197,7 +210,7 @@ reset_notify (assuan_context_t ctx) { ctrl_t ctrl = assuan_get_pointer (ctx); - do_reset (ctrl, 0); + do_reset (ctrl, 1); } @@ -226,18 +239,22 @@ option_handler (assuan_context_t ctx, const char *key, const char *value) static int get_reader_slot (void) { - struct server_local_s *sl; - int slot= -1; + struct slot_status_s *ss; - for (sl=session_list; sl; sl = sl->next_session) - if (sl->ctrl_backlink - && (slot = sl->ctrl_backlink->reader_slot) != -1) - break; + ss = &slot_table[0]; /* One reader for now. */ - if (slot == -1) - slot = apdu_open_reader (opt.reader_port); + /* Initialize the item if needed. */ + if (!ss->valid) + { + ss->slot = -1; + ss->valid = 1; + } + + /* Try to open the reader. */ + if (ss->slot == -1) + ss->slot = apdu_open_reader (opt.reader_port); - return slot; + return ss->slot; } /* If the card has not yet been opened, do it. Note that this @@ -349,7 +366,7 @@ cmd_serialno (assuan_context_t ctx, char *line) { if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); - do_reset (ctrl, 0); + do_reset (ctrl, 1); } if ((rc = open_card (ctrl, *line? line:NULL))) @@ -1305,7 +1322,7 @@ cmd_getinfo (assuan_context_t ctx, char *line) /* RESTART - Restart the current connection; this is a kind of warn reset. It + Restart the current connection; this is a kind of warm reset. It deletes the context used by this connection but does not send a RESET to the card. Thus the card itself won't get reset. @@ -1462,7 +1479,7 @@ scd_command_handler (int fd) } /* Cleanup. */ - do_reset (&ctrl, 1); + do_reset (&ctrl, 0); /* Release the server object. */ if (session_list == ctrl.server_local) @@ -1532,77 +1549,88 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...) } -/* This function is called by the ticker thread to check for changes - of the reader stati. It updates the reader status files and if - requested by the caller also send a signal to the caller. */ -void -scd_update_reader_status_file (void) +/* This is the core of scd_update_reader_status_file but the caller + needs to take care of the locking. */ +static void +update_reader_status_file (void) { - static struct { - int any; - unsigned int status; - unsigned int changed; - } last[10]; - int slot; - int used; + int idx; unsigned int status, changed; /* Note, that we only try to get the status, because it does not make sense to wait here for a operation to complete. If we are busy working with a card, delays in the status file update should be acceptable. */ - for (slot=0; (slot < DIM(last) - &&!apdu_enum_reader (slot, &used)); slot++) - if (used && !apdu_get_status (slot, 0, &status, &changed)) - { - if (!last[slot].any || last[slot].status != status - || last[slot].changed != changed ) - { - char *fname; - char templ[50]; - FILE *fp; - struct server_local_s *sl; - - log_info ("updating status of slot %d to 0x%04X\n", slot, status); - - sprintf (templ, "reader_%d.status", slot); - fname = make_filename (opt.homedir, templ, NULL ); - fp = fopen (fname, "w"); - if (fp) - { - fprintf (fp, "%s\n", - (status & 1)? "USABLE": - (status & 4)? "ACTIVE": - (status & 2)? "PRESENT": "NOCARD"); - fclose (fp); - } - xfree (fname); - - /* Set the card removed flag for all current sessions. We - will set this on any card change because a reset or - SERIALNO request must be done in any case. */ - if (last[slot].any) - update_card_removed (slot, 1); - - last[slot].any = 1; - last[slot].status = status; - last[slot].changed = changed; + for (idx=0; idx < DIM(slot_table); idx++) + { + struct slot_status_s *ss = slot_table + idx; + if (!ss->valid || ss->slot == -1) + continue; /* Not valid or reader not yet open. */ + + if ( apdu_get_status (ss->slot, 0, &status, &changed) ) + continue; /* Get status failed. */ - /* Send a signal to all clients who applied for it. */ - for (sl=session_list; sl; sl = sl->next_session) - if (sl->event_signal && sl->assuan_ctx) - { - pid_t pid = assuan_get_pid (sl->assuan_ctx); - int signo = sl->event_signal; + if (!ss->any || ss->status != status || ss->changed != changed ) + { + char *fname; + char templ[50]; + FILE *fp; + struct server_local_s *sl; - log_info ("client pid is %d, sending signal %d\n", - pid, signo); + log_info ("updating status of slot %d to 0x%04X\n", + ss->slot, status); + + sprintf (templ, "reader_%d.status", ss->slot); + fname = make_filename (opt.homedir, templ, NULL ); + fp = fopen (fname, "w"); + if (fp) + { + fprintf (fp, "%s\n", + (status & 1)? "USABLE": + (status & 4)? "ACTIVE": + (status & 2)? "PRESENT": "NOCARD"); + fclose (fp); + } + xfree (fname); + + /* Set the card removed flag for all current sessions. We + will set this on any card change because a reset or + SERIALNO request must be done in any case. */ + if (ss->any) + update_card_removed (ss->slot, 1); + + ss->any = 1; + ss->status = status; + ss->changed = changed; + + /* Send a signal to all clients who applied for it. */ + for (sl=session_list; sl; sl = sl->next_session) + if (sl->event_signal && sl->assuan_ctx) + { + pid_t pid = assuan_get_pid (sl->assuan_ctx); + int signo = sl->event_signal; + + log_info ("client pid is %d, sending signal %d\n", + pid, signo); #ifndef HAVE_W32_SYSTEM - if (pid != (pid_t)(-1) && pid && signo > 0) - kill (pid, signo); + if (pid != (pid_t)(-1) && pid && signo > 0) + kill (pid, signo); #endif - } - } - } + } + } + } +} + +/* This function is called by the ticker thread to check for changes + of the reader stati. It updates the reader status files and if + requested by the caller also send a signal to the caller. */ +void +scd_update_reader_status_file (void) +{ + if (!pth_mutex_acquire (&status_file_update_lock, 1, NULL)) + return; /* locked - give up. */ + update_reader_status_file (); + if (!pth_mutex_release (&status_file_update_lock)) + log_error ("failed to release status_file_update lock\n"); } |