Home | History | Annotate | Line # | Download | only in tcsd
      1 
      2 /*
      3  * Licensed Materials - Property of IBM
      4  *
      5  * trousers - An open source TCG Software Stack
      6  *
      7  * (C) Copyright International Business Machines Corp. 2004-2006
      8  *
      9  */
     10 
     11 
     12 #include <stdlib.h>
     13 #include <stdio.h>
     14 #include <syslog.h>
     15 #include <errno.h>
     16 #include <string.h>
     17 #include <unistd.h>
     18 #include <sys/types.h>
     19 #include <sys/socket.h>
     20 
     21 #include "trousers/tss.h"
     22 #include "trousers_types.h"
     23 #include "tcs_int_literals.h"
     24 #include "tcs_tsp.h"
     25 #include "tcs_utils.h"
     26 #include "tcsd_wrap.h"
     27 #include "tcsd.h"
     28 #include "tcslog.h"
     29 #include "rpc_tcstp_tcs.h"
     30 
     31 struct tcsd_thread_mgr *tm = NULL;
     32 
     33 TSS_RESULT
     34 tcsd_threads_final()
     35 {
     36 	int rc;
     37 	UINT32 i;
     38 
     39 	MUTEX_LOCK(tm->lock);
     40 
     41 	tm->shutdown = 1;
     42 
     43 	MUTEX_UNLOCK(tm->lock);
     44 
     45 	/* wait for all currently running threads to exit */
     46 	for (i = 0; i < tm->max_threads; i++) {
     47 		if (tm->thread_data[i].thread_id != THREAD_NULL) {
     48 			if ((rc = THREAD_JOIN(*(tm->thread_data[i].thread_id), NULL))) {
     49 				LogError("Thread join failed: error: %d", rc);
     50 			}
     51 		}
     52 	}
     53 
     54 	free(tm->thread_data);
     55 	free(tm);
     56 
     57 	return TSS_SUCCESS;
     58 }
     59 
     60 TSS_RESULT
     61 tcsd_threads_init(void)
     62 {
     63 	/* allocate the thread mgmt structure */
     64 	tm = calloc(1, sizeof(struct tcsd_thread_mgr));
     65 	if (tm == NULL) {
     66 		LogError("malloc of %zd bytes failed.", sizeof(struct tcsd_thread_mgr));
     67 		return TCSERR(TSS_E_OUTOFMEMORY);
     68 	}
     69 	/* initialize mutex */
     70 	MUTEX_INIT(tm->lock);
     71 
     72 	/* set the max threads variable from config */
     73 	tm->max_threads = tcsd_options.num_threads;
     74 
     75 	/* allocate each thread's data structure */
     76 	tm->thread_data = calloc(tcsd_options.num_threads, sizeof(struct tcsd_thread_data));
     77 	if (tm->thread_data == NULL) {
     78 		LogError("malloc of %zu bytes failed.",
     79 			 tcsd_options.num_threads * sizeof(struct tcsd_thread_data));
     80 		free(tm);
     81 		return TCSERR(TSS_E_OUTOFMEMORY);
     82 	}
     83 
     84 	return TSS_SUCCESS;
     85 }
     86 
     87 
     88 TSS_RESULT
     89 tcsd_thread_create(int socket, char *hostname)
     90 {
     91 	UINT32 thread_num = -1;
     92 	int rc = TCS_SUCCESS;
     93 #ifndef TCSD_SINGLE_THREAD_DEBUG
     94 	THREAD_ATTR_DECLARE(tcsd_thread_attr);
     95 
     96 	/* init the thread attribute */
     97 	if ((rc = THREAD_ATTR_INIT(tcsd_thread_attr))) {
     98 		LogError("Initializing thread attribute failed: error=%d: %s", rc, strerror(rc));
     99 		rc = TCSERR(TSS_E_INTERNAL_ERROR);
    100 		goto out;
    101 	}
    102 	/* make all threads joinable */
    103 	if ((rc = THREAD_ATTR_SETJOINABLE(tcsd_thread_attr))) {
    104 		LogError("Making thread attribute joinable failed: error=%d: %s", rc, strerror(rc));
    105 		rc = TCSERR(TSS_E_INTERNAL_ERROR);
    106 		goto out;
    107 	}
    108 
    109 	MUTEX_LOCK(tm->lock);
    110 #endif
    111 	if (tm->num_active_threads == tm->max_threads) {
    112 		if (hostname != NULL) {
    113 			LogError("max number of connections reached (%d), new connection"
    114 				 " from %s refused.", tm->max_threads, hostname);
    115 		} else {
    116 			LogError("max number of connections reached (%d), new connection"
    117 				 " refused.", tm->max_threads);
    118 		}
    119 		rc = TCSERR(TSS_E_CONNECTION_FAILED);
    120 #ifndef TCSD_SINGLE_THREAD_DEBUG
    121 		goto out_unlock;
    122 #else
    123 		goto out;
    124 #endif
    125 	}
    126 
    127 	/* search for an open slot to store the thread data in */
    128 	for (thread_num = 0; thread_num < tm->max_threads; thread_num++) {
    129 		if (tm->thread_data[thread_num].thread_id == THREAD_NULL)
    130 			break;
    131 	}
    132 
    133 	DBG_ASSERT(thread_num != tm->max_threads);
    134 
    135 	tm->thread_data[thread_num].sock = socket;
    136 	tm->thread_data[thread_num].context = NULL_TCS_HANDLE;
    137 	if (hostname != NULL)
    138 		tm->thread_data[thread_num].hostname = hostname;
    139 
    140 #ifdef TCSD_SINGLE_THREAD_DEBUG
    141 	(void)tcsd_thread_run((void *)(&(tm->thread_data[thread_num])));
    142 #else
    143 	tm->thread_data[thread_num].thread_id = calloc(1, sizeof(THREAD_TYPE));
    144 	if (tm->thread_data[thread_num].thread_id == NULL) {
    145 		rc = TCSERR(TSS_E_OUTOFMEMORY);
    146 		LogError("malloc of %zd bytes failed.", sizeof(THREAD_TYPE));
    147 		goto out_unlock;
    148 	}
    149 
    150 	if ((rc = THREAD_CREATE(tm->thread_data[thread_num].thread_id,
    151 				 &tcsd_thread_attr,
    152 				 tcsd_thread_run,
    153 				 (void *)(&(tm->thread_data[thread_num]))))) {
    154 		LogError("Thread create failed: %d", rc);
    155 		rc = TCSERR(TSS_E_INTERNAL_ERROR);
    156 		goto out_unlock;
    157 	}
    158 
    159 	tm->num_active_threads++;
    160 
    161 out_unlock:
    162 	MUTEX_UNLOCK(tm->lock);
    163 #endif
    164 out:
    165 	/* cleanup in case of error */
    166 	if (rc != TCS_SUCCESS) {
    167 		if (hostname != NULL) {
    168 			if (thread_num != -1)
    169 				tm->thread_data[thread_num].hostname = NULL;
    170 			free(hostname);
    171 		}
    172 		close(socket);
    173 	}
    174 	return rc;
    175 }
    176 
    177 /* Since we don't want any of the worker threads to catch any signals, we must mask off any
    178  * potential signals here after creating the threads.  If any of the created threads catch a signal,
    179  * they'd eventually call join on themselves, causing a deadlock.
    180  */
    181 void
    182 thread_signal_init()
    183 {
    184 	sigset_t thread_sigmask;
    185 	int rc;
    186 
    187 	if ((rc = sigfillset(&thread_sigmask))) {
    188 		LogError("sigfillset failed: error=%d: %s", rc, strerror(rc));
    189 		LogError("worker thread %ld is exiting prematurely", (long)THREAD_ID);
    190 		THREAD_EXIT(NULL);
    191 	}
    192 
    193 	if ((rc = THREAD_SET_SIGNAL_MASK(SIG_BLOCK, &thread_sigmask, NULL))) {
    194 		LogError("Setting thread sigmask failed: error=%d: %s", rc, strerror(rc));
    195 		LogError("worker thread %ld is exiting prematurely", (long)THREAD_ID);
    196 		THREAD_EXIT(NULL);
    197 	}
    198 }
    199 
    200 void *
    201 tcsd_thread_run(void *v)
    202 {
    203 	struct tcsd_thread_data *data = (struct tcsd_thread_data *)v;
    204 	BYTE *buffer;
    205 	int recd_so_far, empty_space, total_recv_size, recv_chunk_size, send_size;
    206 	TSS_RESULT result;
    207 	UINT64 offset;
    208 #ifndef TCSD_SINGLE_THREAD_DEBUG
    209 	int rc;
    210 
    211 	thread_signal_init();
    212 #endif
    213 
    214 	data->comm.buf_size = TCSD_INIT_TXBUF_SIZE;
    215 	data->comm.buf = calloc(1, data->comm.buf_size);
    216 	while (data->comm.buf) {
    217 		/* get the packet header to get the size of the incoming packet */
    218 		if (recv_from_socket(data->sock, data->comm.buf,
    219 				     sizeof(struct tcsd_packet_hdr)) < 0)
    220 			break;
    221 
    222 		recd_so_far = sizeof(struct tcsd_packet_hdr);
    223 
    224 		/* check the packet size */
    225 		total_recv_size = Decode_UINT32(data->comm.buf);
    226 		if (total_recv_size < (int)sizeof(struct tcsd_packet_hdr)) {
    227 			LogError("Packet to receive from socket %d is too small (%d bytes)",
    228 				 data->sock, total_recv_size);
    229 			break;
    230 		}
    231 
    232 		LogDebug("total_recv_size %d, buf_size %u, recd_so_far %d", total_recv_size,
    233 			 data->comm.buf_size, recd_so_far);
    234 
    235 		empty_space = data->comm.buf_size - recd_so_far;
    236 
    237 		/* instead of blindly allocating recv_size bytes off the bat, stage the realloc
    238 		 * and wait for the data to come in over the socket. This protects against
    239 		 * trivially asking tcsd to alloc 2GB */
    240 		while (total_recv_size > (int) data->comm.buf_size) {
    241 			BYTE *new_buffer;
    242 			int new_bufsize;
    243 
    244 			if ((int)data->comm.buf_size + TCSD_INCR_TXBUF_SIZE < total_recv_size) {
    245 				new_bufsize = data->comm.buf_size + TCSD_INCR_TXBUF_SIZE;
    246 				recv_chunk_size = empty_space + TCSD_INCR_TXBUF_SIZE;
    247 			} else {
    248 				new_bufsize = total_recv_size;
    249 				recv_chunk_size = total_recv_size - recd_so_far;
    250 			}
    251 
    252 			LogDebug("Increasing communication buffer to %d bytes.", new_bufsize);
    253 			new_buffer = realloc(data->comm.buf, new_bufsize);
    254 			if (new_buffer == NULL) {
    255 				LogError("realloc of %d bytes failed.", new_bufsize);
    256 				data->comm.buf = NULL;
    257 				goto no_mem_error;
    258 			}
    259 
    260 			data->comm.buf_size = new_bufsize;
    261 			data->comm.buf = new_buffer;
    262 			buffer = data->comm.buf + recd_so_far;
    263 
    264 			LogDebug("recv_chunk_size %d recd_so_far %d", recv_chunk_size, recd_so_far);
    265 			if (recv_from_socket(data->sock, buffer, recv_chunk_size) < 0) {
    266 				result = TCSERR(TSS_E_INTERNAL_ERROR);
    267 				goto error;
    268 			}
    269 
    270 			recd_so_far += recv_chunk_size;
    271 			empty_space = 0;
    272 		}
    273 
    274 		if (recd_so_far < total_recv_size) {
    275 			buffer = data->comm.buf + recd_so_far;
    276 			recv_chunk_size = total_recv_size - recd_so_far;
    277 
    278 			LogDebug("recv_chunk_size %d recd_so_far %d", recv_chunk_size, recd_so_far);
    279 
    280 			if (recv_from_socket(data->sock, buffer, recv_chunk_size) < 0) {
    281 				result = TCSERR(TSS_E_INTERNAL_ERROR);
    282 				goto error;
    283 			}
    284 		}
    285 		LogDebug("Rx'd packet");
    286 
    287 		/* create a platform version of the tcsd header */
    288 		offset = 0;
    289 		UnloadBlob_UINT32(&offset, &data->comm.hdr.packet_size, data->comm.buf);
    290 		UnloadBlob_UINT32(&offset, &data->comm.hdr.u.result, data->comm.buf);
    291 		UnloadBlob_UINT32(&offset, &data->comm.hdr.num_parms, data->comm.buf);
    292 		UnloadBlob_UINT32(&offset, &data->comm.hdr.type_size, data->comm.buf);
    293 		UnloadBlob_UINT32(&offset, &data->comm.hdr.type_offset, data->comm.buf);
    294 		UnloadBlob_UINT32(&offset, &data->comm.hdr.parm_size, data->comm.buf);
    295 		UnloadBlob_UINT32(&offset, &data->comm.hdr.parm_offset, data->comm.buf);
    296 
    297 		result = getTCSDPacket(data);
    298 error:
    299 		if (result) {
    300 			/* something internal to the TCSD went wrong in preparing the packet
    301 			 * to return to the TSP.  Use our already allocated buffer to return a
    302 			 * TSS_E_INTERNAL_ERROR return code to the TSP. In the non-error path,
    303 			 * these LoadBlob's are done in getTCSDPacket().
    304 			 */
    305 			/* set everything to zero, fill in what is non-zero */
    306 			memset(data->comm.buf, 0, data->comm.buf_size);
    307 			offset = 0;
    308 			/* load packet size */
    309 			LoadBlob_UINT32(&offset, sizeof(struct tcsd_packet_hdr), data->comm.buf);
    310 			/* load result */
    311 			LoadBlob_UINT32(&offset, result, data->comm.buf);
    312 		}
    313 		send_size = Decode_UINT32(data->comm.buf);
    314 		LogDebug("Sending 0x%X bytes back", send_size);
    315 		send_size = send_to_socket(data->sock, data->comm.buf, send_size);
    316 		if (send_size < 0)
    317 			break;
    318 
    319 		/* check for shutdown */
    320 		if (tm->shutdown) {
    321 			LogDebug("Thread %ld exiting via shutdown signal!", THREAD_ID);
    322 			break;
    323 		}
    324 	}
    325 no_mem_error:
    326 	LogDebug("Thread exiting.");
    327 
    328 	/* Closing connection to TSP */
    329 	close(data->sock);
    330 	data->sock = -1;
    331 	free(data->comm.buf);
    332 	data->comm.buf = NULL;
    333 	data->comm.buf_size = -1;
    334 	/* If the connection was not shut down cleanly, free TCS resources here */
    335 	if (data->context != NULL_TCS_HANDLE) {
    336 		TCS_CloseContext_Internal(data->context);
    337 		data->context = NULL_TCS_HANDLE;
    338 	}
    339 	if(data->hostname != NULL) {
    340 		free(data->hostname);
    341 		data->hostname = NULL;
    342 	}
    343 
    344 #ifndef TCSD_SINGLE_THREAD_DEBUG
    345 	pthread_mutex_lock(&(tm->lock));
    346 	tm->num_active_threads--;
    347 	/* if we're not in shutdown mode, then nobody is waiting to join this thread, so
    348 	 * detach it so that its resources are free at pthread_exit() time. */
    349 	if (!tm->shutdown) {
    350 		if ((rc = pthread_detach(*(data->thread_id)))) {
    351 			LogError("pthread_detach failed (errno %d)."
    352 				 " Resources may not be properly released.", rc);
    353 		}
    354 	}
    355 	free(data->thread_id);
    356 	data->thread_id = THREAD_NULL;
    357 	pthread_mutex_unlock(&(tm->lock));
    358 	pthread_exit(NULL);
    359 #endif
    360 	return NULL;
    361 }
    362