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 31struct tcsd_thread_mgr *tm = NULL; 32 33TSS_RESULT 34tcsd_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 60TSS_RESULT 61tcsd_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 88TSS_RESULT 89tcsd_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 161out_unlock: 162 MUTEX_UNLOCK(tm->lock); 163#endif 164out: 165 /* cleanup in case of error */ 166 if (rc != TCS_SUCCESS) { 167 if (hostname != NULL) { 168 tm->thread_data[thread_num].hostname = NULL; 169 free(hostname); 170 } 171 close(socket); 172 } 173 return rc; 174} 175 176/* Since we don't want any of the worker threads to catch any signals, we must mask off any 177 * potential signals here after creating the threads. If any of the created threads catch a signal, 178 * they'd eventually call join on themselves, causing a deadlock. 179 */ 180void 181thread_signal_init() 182{ 183 sigset_t thread_sigmask; 184 int rc; 185 186 if ((rc = sigfillset(&thread_sigmask))) { 187 LogError("sigfillset failed: error=%d: %s", rc, strerror(rc)); 188 LogError("worker thread %ld is exiting prematurely", (long)THREAD_ID); 189 THREAD_EXIT(NULL); 190 } 191 192 if ((rc = THREAD_SET_SIGNAL_MASK(SIG_BLOCK, &thread_sigmask, NULL))) { 193 LogError("Setting thread sigmask failed: error=%d: %s", rc, strerror(rc)); 194 LogError("worker thread %ld is exiting prematurely", (long)THREAD_ID); 195 THREAD_EXIT(NULL); 196 } 197} 198#if 0 199void * 200tcsd_thread_run(void *v) 201{ 202 struct tcsd_thread_data *data = (struct tcsd_thread_data *)v; 203 BYTE buffer[TCSD_TXBUF_SIZE]; 204 struct tcsd_packet_hdr *ret_buf = NULL; 205 TSS_RESULT result; 206 int sizeToSend, sent_total, sent; 207 UINT64 offset; 208#ifndef TCSD_SINGLE_THREAD_DEBUG 209 int rc; 210 211 thread_signal_init(); 212#endif 213 214 if ((data->buf_size = recv(data->sock, buffer, TCSD_TXBUF_SIZE, 0)) < 0) { 215 LogError("Failed Receive: %s", strerror(errno)); 216 goto done; 217 } 218 LogDebug("Rx'd packet"); 219 220 data->buf = buffer; 221 222 while (1) { 223 sent_total = 0; 224 if (data->buf_size > TCSD_TXBUF_SIZE) { 225 LogError("Packet received from socket %d was too large (%u bytes)", 226 data->sock, data->buf_size); 227 goto done; 228 } else if (data->buf_size < (int)((2 * sizeof(UINT32)) + sizeof(UINT16))) { 229 LogError("Packet received from socket %d was too small (%u bytes)", 230 data->sock, data->buf_size); 231 goto done; 232 } 233 234 if ((result = getTCSDPacket(data, &ret_buf)) != TSS_SUCCESS) { 235 /* something internal to the TCSD went wrong in preparing the packet 236 * to return to the TSP. Use our already allocated buffer to return a 237 * TSS_E_INTERNAL_ERROR return code to the TSP. In the non-error path, 238 * these LoadBlob's are done in getTCSDPacket(). 239 */ 240 offset = 0; 241 /* load result */ 242 LoadBlob_UINT32(&offset, result, buffer); 243 /* load packet size */ 244 LoadBlob_UINT32(&offset, sizeof(struct tcsd_packet_hdr), buffer); 245 /* load num parms */ 246 LoadBlob_UINT16(&offset, 0, buffer); 247 248 sizeToSend = sizeof(struct tcsd_packet_hdr); 249 LogDebug("Sending 0x%X bytes back", sizeToSend); 250 251 while (sent_total < sizeToSend) { 252 if ((sent = send(data->sock, 253 &data->buf[sent_total], 254 sizeToSend - sent_total, 0)) < 0) { 255 LogError("Packet send to TSP failed: send: %s. Thread exiting.", 256 strerror(errno)); 257 goto done; 258 } 259 sent_total += sent; 260 } 261 } else { 262 sizeToSend = Decode_UINT32((BYTE *)&(ret_buf->packet_size)); 263 264 LogDebug("Sending 0x%X bytes back", sizeToSend); 265 266 while (sent_total < sizeToSend) { 267 if ((sent = send(data->sock, 268 &(((BYTE *)ret_buf)[sent_total]), 269 sizeToSend - sent_total, 0)) < 0) { 270 LogError("response to TSP failed: send: %s. Thread exiting.", 271 strerror(errno)); 272 free(ret_buf); 273 ret_buf = NULL; 274 goto done; 275 } 276 sent_total += sent; 277 } 278 free(ret_buf); 279 ret_buf = NULL; 280 } 281 282 if (tm->shutdown) { 283 LogDebug("Thread %zd exiting via shutdown signal!", THREAD_ID); 284 break; 285 } 286 287 /* receive the next packet */ 288 if ((data->buf_size = recv(data->sock, buffer, TCSD_TXBUF_SIZE, 0)) < 0) { 289 LogError("TSP has closed its connection: %s. Thread exiting.", 290 strerror(errno)); 291 break; 292 } else if (data->buf_size == 0) { 293 LogDebug("The TSP has closed the socket's connection. Thread exiting."); 294 break; 295 } 296 } 297 298done: 299 /* Closing connection to TSP */ 300 close(data->sock); 301 data->sock = -1; 302 data->buf = NULL; 303 data->buf_size = -1; 304 /* If the connection was not shut down cleanly, free TCS resources here */ 305 if (data->context != NULL_TCS_HANDLE) { 306 TCS_CloseContext_Internal(data->context); 307 data->context = NULL_TCS_HANDLE; 308 } 309 310#ifndef TCSD_SINGLE_THREAD_DEBUG 311 MUTEX_LOCK(tm->lock); 312 tm->num_active_threads--; 313 /* if we're not in shutdown mode, then nobody is waiting to join this thread, so 314 * detach it so that its resources are free at THREAD_EXIT() time. */ 315 if (!tm->shutdown) { 316 if ((rc = THREAD_DETACH(*(data->thread_id)))) { 317 LogError("Thread detach failed (errno %d)." 318 " Resources may not be properly released.", rc); 319 } 320 } 321 free(data->hostname); 322 data->hostname = NULL; 323 data->thread_id = THREAD_NULL; 324 MUTEX_UNLOCK(tm->lock); 325 THREAD_EXIT(NULL); 326#else 327 return NULL; 328#endif 329} 330#else 331void * 332tcsd_thread_run(void *v) 333{ 334 struct tcsd_thread_data *data = (struct tcsd_thread_data *)v; 335 BYTE *buffer; 336 int recv_size, send_size; 337 TSS_RESULT result; 338 UINT64 offset; 339#ifndef TCSD_SINGLE_THREAD_DEBUG 340 int rc; 341 342 thread_signal_init(); 343#endif 344 345 data->comm.buf_size = TCSD_INIT_TXBUF_SIZE; 346 data->comm.buf = calloc(1, data->comm.buf_size); 347 while (data->comm.buf) { 348 /* get the packet header to get the size of the incoming packet */ 349 buffer = data->comm.buf; 350 recv_size = sizeof(struct tcsd_packet_hdr); 351 if ((recv_size = recv_from_socket(data->sock, buffer, recv_size)) < 0) 352 break; 353 buffer += sizeof(struct tcsd_packet_hdr); /* increment the buffer pointer */ 354 355 /* check the packet size */ 356 recv_size = Decode_UINT32(data->comm.buf); 357 if (recv_size < (int)sizeof(struct tcsd_packet_hdr)) { 358 LogError("Packet to receive from socket %d is too small (%d bytes)", 359 data->sock, recv_size); 360 break; 361 } 362 363 if (recv_size > data->comm.buf_size ) { 364 BYTE *new_buffer; 365 366 LogDebug("Increasing communication buffer to %d bytes.", recv_size); 367 new_buffer = realloc(data->comm.buf, recv_size); 368 if (new_buffer == NULL) { 369 LogError("realloc of %d bytes failed.", recv_size); 370 break; 371 } 372 buffer = new_buffer + sizeof(struct tcsd_packet_hdr); 373 data->comm.buf_size = recv_size; 374 data->comm.buf = new_buffer; 375 } 376 377 /* get the rest of the packet */ 378 recv_size -= sizeof(struct tcsd_packet_hdr); /* already received the header */ 379 if ((recv_size = recv_from_socket(data->sock, buffer, recv_size)) < 0) 380 break; 381 LogDebug("Rx'd packet"); 382 383 /* create a platform version of the tcsd header */ 384 offset = 0; 385 UnloadBlob_UINT32(&offset, &data->comm.hdr.packet_size, data->comm.buf); 386 UnloadBlob_UINT32(&offset, &data->comm.hdr.u.result, data->comm.buf); 387 UnloadBlob_UINT32(&offset, &data->comm.hdr.num_parms, data->comm.buf); 388 UnloadBlob_UINT32(&offset, &data->comm.hdr.type_size, data->comm.buf); 389 UnloadBlob_UINT32(&offset, &data->comm.hdr.type_offset, data->comm.buf); 390 UnloadBlob_UINT32(&offset, &data->comm.hdr.parm_size, data->comm.buf); 391 UnloadBlob_UINT32(&offset, &data->comm.hdr.parm_offset, data->comm.buf); 392 393 if ((result = getTCSDPacket(data)) != TSS_SUCCESS) { 394 /* something internal to the TCSD went wrong in preparing the packet 395 * to return to the TSP. Use our already allocated buffer to return a 396 * TSS_E_INTERNAL_ERROR return code to the TSP. In the non-error path, 397 * these LoadBlob's are done in getTCSDPacket(). 398 */ 399 /* set everything to zero, fill in what is non-zero */ 400 memset(data->comm.buf, 0, data->comm.buf_size); 401 offset = 0; 402 /* load packet size */ 403 LoadBlob_UINT32(&offset, sizeof(struct tcsd_packet_hdr), data->comm.buf); 404 /* load result */ 405 LoadBlob_UINT32(&offset, result, data->comm.buf); 406 } 407 send_size = Decode_UINT32(data->comm.buf); 408 LogDebug("Sending 0x%X bytes back", send_size); 409 send_size = send_to_socket(data->sock, data->comm.buf, send_size); 410 if (send_size < 0) 411 break; 412 413 /* check for shutdown */ 414 if (tm->shutdown) { 415 LogDebug("Thread %ld exiting via shutdown signal!", THREAD_ID); 416 break; 417 } 418 } 419 420 LogDebug("Thread exiting."); 421 422 /* Closing connection to TSP */ 423 close(data->sock); 424 data->sock = -1; 425 free(data->comm.buf); 426 data->comm.buf = NULL; 427 data->comm.buf_size = -1; 428 /* If the connection was not shut down cleanly, free TCS resources here */ 429 if (data->context != NULL_TCS_HANDLE) { 430 TCS_CloseContext_Internal(data->context); 431 data->context = NULL_TCS_HANDLE; 432 } 433 if(data->hostname != NULL) { 434 free(data->hostname); 435 data->hostname = NULL; 436 } 437 438#ifndef TCSD_SINGLE_THREAD_DEBUG 439 pthread_mutex_lock(&(tm->lock)); 440 tm->num_active_threads--; 441 /* if we're not in shutdown mode, then nobody is waiting to join this thread, so 442 * detach it so that its resources are free at pthread_exit() time. */ 443 if (!tm->shutdown) { 444 if ((rc = pthread_detach(*(data->thread_id)))) { 445 LogError("pthread_detach failed (errno %d)." 446 " Resources may not be properly released.", rc); 447 } 448 } 449 free(data->thread_id); 450 data->thread_id = THREAD_NULL; 451 pthread_mutex_unlock(&(tm->lock)); 452 pthread_exit(NULL); 453#endif 454 return NULL; 455} 456 457#endif 458