// generic signal handler static void signal_handler(int signo) { #define err signo if (SIGALRM == signo) { kill_helper(); bb_error_msg_and_die("timed out"); } // SIGCHLD. reap zombies if (safe_waitpid(G.helper_pid, &err, WNOHANG) > 0) { if (WIFSIGNALED(err)) bb_error_msg_and_die("helper killed by signal %u", WTERMSIG(err)); if (WIFEXITED(err)) { G.helper_pid = 0; if (WEXITSTATUS(err)) bb_error_msg_and_die("helper exited (%u)", WEXITSTATUS(err)); } } #undef err }
/* * This function is called when there is socketpair input from a helper. * This is the answer from the helper. * We read the request from the socketpair, and find the associated continuation, * and dispatch to that continuation. * * This function should process only a single answer, and then go back * to the select call to get called again. This is not most efficient, * but is is most fair. * */ static void handle_helper_answer(struct pluto_crypto_worker *w) { struct pluto_crypto_req rr; ssize_t actlen; struct pluto_crypto_req_cont *cn; DBG(DBG_CONTROL, DBG_log("crypto helper %d has finished work (pcw_work now %d)", w->pcw_helpernum, w->pcw_work)); /* read from the socketpair in one gulp */ errno = 0; actlen = read(w->pcw_master_fd, (void *)&rr, sizeof(rr)); if (actlen != sizeof(rr)) { if (actlen == -1) { loglog(RC_LOG_SERIOUS, "read from crypto helper %d failed: %s. Killing helper.", w->pcw_helpernum, strerror(errno)); kill_helper(w); } else if (actlen == 0) { /* EOF: mark worker as dead. */ w->pcw_dead = TRUE; } else if (errno == 0) { loglog(RC_LOG_SERIOUS, "read from crypto helper %d failed with short length %zd of %zu. Killing helper.", w->pcw_helpernum, actlen, sizeof(rr)); kill_helper(w); } else { loglog(RC_LOG_SERIOUS, "read from crypto helper %d failed with short length %zd of %zu (errno=%s). Killing helper.", w->pcw_helpernum, actlen, sizeof(rr), strerror(errno)); kill_helper(w); } return; } if (rr.pcr_len != sizeof(rr)) { loglog(RC_LOG_SERIOUS, "crypto helper %d screwed up length: %zu != %zu; killing it", w->pcw_helpernum, rr.pcr_len, sizeof(rr)); kill_helper(w); return; } DBG(DBG_CONTROL, DBG_log("crypto helper %d replies to request ID %u", w->pcw_helpernum, rr.pcr_id)); /* worker w can accept more work now that we have read from its socketpair */ w->pcw_work--; /* * if there is work queued, then send it off after reading, since this * avoids the most deadlocks */ crypto_send_backlog(w); /* now match up request to continuation, and invoke it */ for (cn = w->pcw_active.tqh_first; cn != NULL && rr.pcr_id != cn->pcrc_id; cn = cn->pcrc_list.tqe_next) ; if (cn == NULL) { loglog(RC_LOG_SERIOUS, "failed to find crypto continuation associated with request ID %u performed by crypto helper %d", rr.pcr_id, w->pcw_helpernum); return; } /* unlink it */ TAILQ_REMOVE(&w->pcw_active, cn, pcrc_list); passert(cn->pcrc_func != NULL); DBG(DBG_CONTROL, DBG_log("calling continuation function %p", cn->pcrc_func)); reply_stream = cn->pcrc_reply_stream; if (pbs_offset(&reply_stream) != 0) { memcpy(reply_stream.start, cn->pcrc_reply_buffer, pbs_offset(&reply_stream)); pfree(cn->pcrc_reply_buffer); } cn->pcrc_reply_buffer = NULL; /* call the continuation (skip if suppressed) */ cn->pcrc_pcr = &rr; reset_cur_state(); if (cn->pcrc_serialno != SOS_NOBODY) (*cn->pcrc_func)(cn, &rr); /* now free up the continuation */ pfree(cn); }