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 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 */ 181void 182thread_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 200void * 201tcsd_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); 298error: 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 } 325no_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