diff options
Diffstat (limited to 'src/tcsd/tcsd_threads.c')
-rw-r--r-- | src/tcsd/tcsd_threads.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/src/tcsd/tcsd_threads.c b/src/tcsd/tcsd_threads.c new file mode 100644 index 0000000..66a1ac7 --- /dev/null +++ b/src/tcsd/tcsd_threads.c @@ -0,0 +1,457 @@ + +/* + * Licensed Materials - Property of IBM + * + * trousers - An open source TCG Software Stack + * + * (C) Copyright International Business Machines Corp. 2004-2006 + * + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <syslog.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include "trousers/tss.h" +#include "trousers_types.h" +#include "tcs_int_literals.h" +#include "tcs_tsp.h" +#include "tcs_utils.h" +#include "tcsd_wrap.h" +#include "tcsd.h" +#include "tcslog.h" +#include "rpc_tcstp_tcs.h" + +struct tcsd_thread_mgr *tm = NULL; + +TSS_RESULT +tcsd_threads_final() +{ + int rc; + UINT32 i; + + MUTEX_LOCK(tm->lock); + + tm->shutdown = 1; + + MUTEX_UNLOCK(tm->lock); + + /* wait for all currently running threads to exit */ + for (i = 0; i < tm->max_threads; i++) { + if (tm->thread_data[i].thread_id != THREAD_NULL) { + if ((rc = THREAD_JOIN(*(tm->thread_data[i].thread_id), NULL))) { + LogError("Thread join failed: error: %d", rc); + } + } + } + + free(tm->thread_data); + free(tm); + + return TSS_SUCCESS; +} + +TSS_RESULT +tcsd_threads_init(void) +{ + /* allocate the thread mgmt structure */ + tm = calloc(1, sizeof(struct tcsd_thread_mgr)); + if (tm == NULL) { + LogError("malloc of %zd bytes failed.", sizeof(struct tcsd_thread_mgr)); + return TCSERR(TSS_E_OUTOFMEMORY); + } + /* initialize mutex */ + MUTEX_INIT(tm->lock); + + /* set the max threads variable from config */ + tm->max_threads = tcsd_options.num_threads; + + /* allocate each thread's data structure */ + tm->thread_data = calloc(tcsd_options.num_threads, sizeof(struct tcsd_thread_data)); + if (tm->thread_data == NULL) { + LogError("malloc of %zu bytes failed.", + tcsd_options.num_threads * sizeof(struct tcsd_thread_data)); + free(tm); + return TCSERR(TSS_E_OUTOFMEMORY); + } + + return TSS_SUCCESS; +} + + +TSS_RESULT +tcsd_thread_create(int socket, char *hostname) +{ + UINT32 thread_num = -1; + int rc = TCS_SUCCESS; +#ifndef TCSD_SINGLE_THREAD_DEBUG + THREAD_ATTR_DECLARE(tcsd_thread_attr); + + /* init the thread attribute */ + if ((rc = THREAD_ATTR_INIT(tcsd_thread_attr))) { + LogError("Initializing thread attribute failed: error=%d: %s", rc, strerror(rc)); + rc = TCSERR(TSS_E_INTERNAL_ERROR); + goto out; + } + /* make all threads joinable */ + if ((rc = THREAD_ATTR_SETJOINABLE(tcsd_thread_attr))) { + LogError("Making thread attribute joinable failed: error=%d: %s", rc, strerror(rc)); + rc = TCSERR(TSS_E_INTERNAL_ERROR); + goto out; + } + + MUTEX_LOCK(tm->lock); +#endif + if (tm->num_active_threads == tm->max_threads) { + if (hostname != NULL) { + LogError("max number of connections reached (%d), new connection" + " from %s refused.", tm->max_threads, hostname); + } else { + LogError("max number of connections reached (%d), new connection" + " refused.", tm->max_threads); + } + rc = TCSERR(TSS_E_CONNECTION_FAILED); +#ifndef TCSD_SINGLE_THREAD_DEBUG + goto out_unlock; +#else + goto out; +#endif + } + + /* search for an open slot to store the thread data in */ + for (thread_num = 0; thread_num < tm->max_threads; thread_num++) { + if (tm->thread_data[thread_num].thread_id == THREAD_NULL) + break; + } + + DBG_ASSERT(thread_num != tm->max_threads); + + tm->thread_data[thread_num].sock = socket; + tm->thread_data[thread_num].context = NULL_TCS_HANDLE; + if (hostname != NULL) + tm->thread_data[thread_num].hostname = hostname; + +#ifdef TCSD_SINGLE_THREAD_DEBUG + (void)tcsd_thread_run((void *)(&(tm->thread_data[thread_num]))); +#else + tm->thread_data[thread_num].thread_id = calloc(1, sizeof(THREAD_TYPE)); + if (tm->thread_data[thread_num].thread_id == NULL) { + rc = TCSERR(TSS_E_OUTOFMEMORY); + LogError("malloc of %zd bytes failed.", sizeof(THREAD_TYPE)); + goto out_unlock; + } + + if ((rc = THREAD_CREATE(tm->thread_data[thread_num].thread_id, + &tcsd_thread_attr, + tcsd_thread_run, + (void *)(&(tm->thread_data[thread_num]))))) { + LogError("Thread create failed: %d", rc); + rc = TCSERR(TSS_E_INTERNAL_ERROR); + goto out_unlock; + } + + tm->num_active_threads++; + +out_unlock: + MUTEX_UNLOCK(tm->lock); +#endif +out: + /* cleanup in case of error */ + if (rc != TCS_SUCCESS) { + if (hostname != NULL) { + tm->thread_data[thread_num].hostname = NULL; + free(hostname); + } + close(socket); + } + return rc; +} + +/* Since we don't want any of the worker threads to catch any signals, we must mask off any + * potential signals here after creating the threads. If any of the created threads catch a signal, + * they'd eventually call join on themselves, causing a deadlock. + */ +void +thread_signal_init() +{ + sigset_t thread_sigmask; + int rc; + + if ((rc = sigfillset(&thread_sigmask))) { + LogError("sigfillset failed: error=%d: %s", rc, strerror(rc)); + LogError("worker thread %ld is exiting prematurely", THREAD_ID); + THREAD_EXIT(NULL); + } + + if ((rc = THREAD_SET_SIGNAL_MASK(SIG_BLOCK, &thread_sigmask, NULL))) { + LogError("Setting thread sigmask failed: error=%d: %s", rc, strerror(rc)); + LogError("worker thread %ld is exiting prematurely", THREAD_ID); + THREAD_EXIT(NULL); + } +} +#if 0 +void * +tcsd_thread_run(void *v) +{ + struct tcsd_thread_data *data = (struct tcsd_thread_data *)v; + BYTE buffer[TCSD_TXBUF_SIZE]; + struct tcsd_packet_hdr *ret_buf = NULL; + TSS_RESULT result; + int sizeToSend, sent_total, sent; + UINT64 offset; +#ifndef TCSD_SINGLE_THREAD_DEBUG + int rc; + + thread_signal_init(); +#endif + + if ((data->buf_size = recv(data->sock, buffer, TCSD_TXBUF_SIZE, 0)) < 0) { + LogError("Failed Receive: %s", strerror(errno)); + goto done; + } + LogDebug("Rx'd packet"); + + data->buf = buffer; + + while (1) { + sent_total = 0; + if (data->buf_size > TCSD_TXBUF_SIZE) { + LogError("Packet received from socket %d was too large (%u bytes)", + data->sock, data->buf_size); + goto done; + } else if (data->buf_size < (int)((2 * sizeof(UINT32)) + sizeof(UINT16))) { + LogError("Packet received from socket %d was too small (%u bytes)", + data->sock, data->buf_size); + goto done; + } + + if ((result = getTCSDPacket(data, &ret_buf)) != TSS_SUCCESS) { + /* something internal to the TCSD went wrong in preparing the packet + * to return to the TSP. Use our already allocated buffer to return a + * TSS_E_INTERNAL_ERROR return code to the TSP. In the non-error path, + * these LoadBlob's are done in getTCSDPacket(). + */ + offset = 0; + /* load result */ + LoadBlob_UINT32(&offset, result, buffer); + /* load packet size */ + LoadBlob_UINT32(&offset, sizeof(struct tcsd_packet_hdr), buffer); + /* load num parms */ + LoadBlob_UINT16(&offset, 0, buffer); + + sizeToSend = sizeof(struct tcsd_packet_hdr); + LogDebug("Sending 0x%X bytes back", sizeToSend); + + while (sent_total < sizeToSend) { + if ((sent = send(data->sock, + &data->buf[sent_total], + sizeToSend - sent_total, 0)) < 0) { + LogError("Packet send to TSP failed: send: %s. Thread exiting.", + strerror(errno)); + goto done; + } + sent_total += sent; + } + } else { + sizeToSend = Decode_UINT32((BYTE *)&(ret_buf->packet_size)); + + LogDebug("Sending 0x%X bytes back", sizeToSend); + + while (sent_total < sizeToSend) { + if ((sent = send(data->sock, + &(((BYTE *)ret_buf)[sent_total]), + sizeToSend - sent_total, 0)) < 0) { + LogError("response to TSP failed: send: %s. Thread exiting.", + strerror(errno)); + free(ret_buf); + ret_buf = NULL; + goto done; + } + sent_total += sent; + } + free(ret_buf); + ret_buf = NULL; + } + + if (tm->shutdown) { + LogDebug("Thread %zd exiting via shutdown signal!", THREAD_ID); + break; + } + + /* receive the next packet */ + if ((data->buf_size = recv(data->sock, buffer, TCSD_TXBUF_SIZE, 0)) < 0) { + LogError("TSP has closed its connection: %s. Thread exiting.", + strerror(errno)); + break; + } else if (data->buf_size == 0) { + LogDebug("The TSP has closed the socket's connection. Thread exiting."); + break; + } + } + +done: + /* Closing connection to TSP */ + close(data->sock); + data->sock = -1; + data->buf = NULL; + data->buf_size = -1; + /* If the connection was not shut down cleanly, free TCS resources here */ + if (data->context != NULL_TCS_HANDLE) { + TCS_CloseContext_Internal(data->context); + data->context = NULL_TCS_HANDLE; + } + +#ifndef TCSD_SINGLE_THREAD_DEBUG + MUTEX_LOCK(tm->lock); + tm->num_active_threads--; + /* if we're not in shutdown mode, then nobody is waiting to join this thread, so + * detach it so that its resources are free at THREAD_EXIT() time. */ + if (!tm->shutdown) { + if ((rc = THREAD_DETACH(*(data->thread_id)))) { + LogError("Thread detach failed (errno %d)." + " Resources may not be properly released.", rc); + } + } + free(data->hostname); + data->hostname = NULL; + data->thread_id = THREAD_NULL; + MUTEX_UNLOCK(tm->lock); + THREAD_EXIT(NULL); +#else + return NULL; +#endif +} +#else +void * +tcsd_thread_run(void *v) +{ + struct tcsd_thread_data *data = (struct tcsd_thread_data *)v; + BYTE *buffer; + int recv_size, send_size; + TSS_RESULT result; + UINT64 offset; +#ifndef TCSD_SINGLE_THREAD_DEBUG + int rc; + + thread_signal_init(); +#endif + + data->comm.buf_size = TCSD_INIT_TXBUF_SIZE; + data->comm.buf = calloc(1, data->comm.buf_size); + while (data->comm.buf) { + /* get the packet header to get the size of the incoming packet */ + buffer = data->comm.buf; + recv_size = sizeof(struct tcsd_packet_hdr); + if ((recv_size = recv_from_socket(data->sock, buffer, recv_size)) < 0) + break; + buffer += sizeof(struct tcsd_packet_hdr); /* increment the buffer pointer */ + + /* check the packet size */ + recv_size = Decode_UINT32(data->comm.buf); + if (recv_size < (int)sizeof(struct tcsd_packet_hdr)) { + LogError("Packet to receive from socket %d is too small (%d bytes)", + data->sock, recv_size); + break; + } + + if (recv_size > (int) data->comm.buf_size ) { + BYTE *new_buffer; + + LogDebug("Increasing communication buffer to %d bytes.", recv_size); + new_buffer = realloc(data->comm.buf, recv_size); + if (new_buffer == NULL) { + LogError("realloc of %d bytes failed.", recv_size); + break; + } + buffer = new_buffer + sizeof(struct tcsd_packet_hdr); + data->comm.buf_size = recv_size; + data->comm.buf = new_buffer; + } + + /* get the rest of the packet */ + recv_size -= sizeof(struct tcsd_packet_hdr); /* already received the header */ + if ((recv_size = recv_from_socket(data->sock, buffer, recv_size)) < 0) + break; + LogDebug("Rx'd packet"); + + /* create a platform version of the tcsd header */ + offset = 0; + UnloadBlob_UINT32(&offset, &data->comm.hdr.packet_size, data->comm.buf); + UnloadBlob_UINT32(&offset, &data->comm.hdr.u.result, data->comm.buf); + UnloadBlob_UINT32(&offset, &data->comm.hdr.num_parms, data->comm.buf); + UnloadBlob_UINT32(&offset, &data->comm.hdr.type_size, data->comm.buf); + UnloadBlob_UINT32(&offset, &data->comm.hdr.type_offset, data->comm.buf); + UnloadBlob_UINT32(&offset, &data->comm.hdr.parm_size, data->comm.buf); + UnloadBlob_UINT32(&offset, &data->comm.hdr.parm_offset, data->comm.buf); + + if ((result = getTCSDPacket(data)) != TSS_SUCCESS) { + /* something internal to the TCSD went wrong in preparing the packet + * to return to the TSP. Use our already allocated buffer to return a + * TSS_E_INTERNAL_ERROR return code to the TSP. In the non-error path, + * these LoadBlob's are done in getTCSDPacket(). + */ + /* set everything to zero, fill in what is non-zero */ + memset(data->comm.buf, 0, data->comm.buf_size); + offset = 0; + /* load packet size */ + LoadBlob_UINT32(&offset, sizeof(struct tcsd_packet_hdr), data->comm.buf); + /* load result */ + LoadBlob_UINT32(&offset, result, data->comm.buf); + } + send_size = Decode_UINT32(data->comm.buf); + LogDebug("Sending 0x%X bytes back", send_size); + send_size = send_to_socket(data->sock, data->comm.buf, send_size); + if (send_size < 0) + break; + + /* check for shutdown */ + if (tm->shutdown) { + LogDebug("Thread %ld exiting via shutdown signal!", THREAD_ID); + break; + } + } + + LogDebug("Thread exiting."); + + /* Closing connection to TSP */ + close(data->sock); + data->sock = -1; + free(data->comm.buf); + data->comm.buf = NULL; + data->comm.buf_size = -1; + /* If the connection was not shut down cleanly, free TCS resources here */ + if (data->context != NULL_TCS_HANDLE) { + TCS_CloseContext_Internal(data->context); + data->context = NULL_TCS_HANDLE; + } + if(data->hostname != NULL) { + free(data->hostname); + data->hostname = NULL; + } + +#ifndef TCSD_SINGLE_THREAD_DEBUG + pthread_mutex_lock(&(tm->lock)); + tm->num_active_threads--; + /* if we're not in shutdown mode, then nobody is waiting to join this thread, so + * detach it so that its resources are free at pthread_exit() time. */ + if (!tm->shutdown) { + if ((rc = pthread_detach(*(data->thread_id)))) { + LogError("pthread_detach failed (errno %d)." + " Resources may not be properly released.", rc); + } + } + free(data->thread_id); + data->thread_id = THREAD_NULL; + pthread_mutex_unlock(&(tm->lock)); + pthread_exit(NULL); +#endif + return NULL; +} + +#endif |