/* * QBufPipe_Wait */ void QBufPipe_Wait( qbufPipe_t *pipe, int (*read)( qbufPipe_t *, unsigned( ** )(const void *), bool ), unsigned (**cmdHandlers)( const void * ), unsigned timeout_msec ) { while( !pipe->terminated ) { int res; bool result = false; while( Sys_Atomic_CAS( &pipe->cmdbuf_len, 0, 0, pipe->cmdbuf_mutex ) == true ) { QMutex_Lock( pipe->nonempty_mutex ); result = QCondVar_Wait( pipe->nonempty_condvar, pipe->nonempty_mutex, timeout_msec ); // don't hold the mutex, changes to cmdbuf_len are atomic anyway QMutex_Unlock( pipe->nonempty_mutex ); break; } // we're guaranteed at this point that either cmdbuf_len is > 0 // or that waiting on the condition variable has timed out res = read( pipe, cmdHandlers, result ); if( res < 0 ) { // done return; } } }
/* * QBufPipe_Finish * * Blocks until the reader thread handles all commands * or terminates with an error. */ void QBufPipe_Finish( qbufPipe_t *pipe ) { while( Sys_Atomic_CAS( &pipe->cmdbuf_len, 0, 0, pipe->cmdbuf_mutex ) == false && !pipe->terminated ) { QMutex_Lock( pipe->nonempty_mutex ); QBufPipe_Wake( pipe ); QMutex_Unlock( pipe->nonempty_mutex ); QThread_Yield(); } }
static void wswcurl_crypto_lockcallback( int mode, int type, const char *file, int line ) { ( void )file; ( void )line; if( mode & CRYPTO_LOCK ) { QMutex_Lock( crypto_mutexes[type] ); } else { QMutex_Unlock( crypto_mutexes[type] ); } }
void _Mem_Free( void *data, int musthave, int canthave, const char *filename, int fileline ) { void *base; memheader_t *mem; mempool_t *pool; if( data == NULL ) //_Mem_Error( "Mem_Free: data == NULL (called at %s:%i)", filename, fileline ); return; mem = ( memheader_t * )( (qbyte *) data - sizeof( memheader_t ) ); assert( mem->sentinel1 == MEMHEADER_SENTINEL1 ); assert( *( (qbyte *) mem + sizeof( memheader_t ) + mem->size ) == MEMHEADER_SENTINEL2 ); if( mem->sentinel1 != MEMHEADER_SENTINEL1 ) _Mem_Error( "Mem_Free: trashed header sentinel 1 (alloc at %s:%i, free at %s:%i)", mem->filename, mem->fileline, filename, fileline ); if( *( (qbyte *)mem + sizeof( memheader_t ) + mem->size ) != MEMHEADER_SENTINEL2 ) _Mem_Error( "Mem_Free: trashed header sentinel 2 (alloc at %s:%i, free at %s:%i)", mem->filename, mem->fileline, filename, fileline ); pool = mem->pool; if( musthave && ( ( pool->flags & musthave ) != musthave ) ) _Mem_Error( "Mem_Free: bad pool flags (musthave) (alloc at %s:%i)", filename, fileline ); if( canthave && ( pool->flags & canthave ) ) _Mem_Error( "Mem_Free: bad pool flags (canthave) (alloc at %s:%i)", filename, fileline ); if( developerMemory && developerMemory->integer ) Com_DPrintf( "Mem_Free: pool %s, alloc %s:%i, free %s:%i, size %i bytes\n", pool->name, mem->filename, mem->fileline, filename, fileline, mem->size ); QMutex_Lock( memMutex ); // unlink memheader from doubly linked list if( ( mem->prev ? mem->prev->next != mem : pool->chain != mem ) || ( mem->next && mem->next->prev != mem ) ) _Mem_Error( "Mem_Free: not allocated or double freed (free at %s:%i)", filename, fileline ); if( mem->prev ) mem->prev->next = mem->next; else pool->chain = mem->next; if( mem->next ) mem->next->prev = mem->prev; // memheader has been unlinked, do the actual free now pool->totalsize -= mem->size; base = mem->baseaddress; pool->realsize -= mem->realsize; QMutex_Unlock( memMutex ); #ifdef MEMTRASH memset( mem, 0xBF, sizeof( memheader_t ) + mem->size + sizeof( int ) ); #endif free( base ); }
size_t wswcurl_urldecode( const char *src, char *dst, size_t size ) { int unesc_len; char *curl_unesc; assert( src ); assert( dst ); if( !src || !dst ) { return 0; } if( !curldummy ) { return 0; } QMutex_Lock( curldummy_mutex ); curl_unesc = qcurl_easy_unescape( curldummy, src, 0, &unesc_len ); QMutex_Lock( curldummy_mutex ); Q_strncpyz( dst, curl_unesc, size ); qcurl_free( curl_unesc ); return (size_t)unesc_len; }
void Com_BeginRedirect( int target, char *buffer, int buffersize, void ( *flush )(int, const char*, const void*), const void *extra ) { if( !target || !buffer || !buffersize || !flush ) return; QMutex_Lock( com_print_mutex ); rd_target = target; rd_buffer = buffer; rd_buffersize = buffersize; rd_flush = flush; rd_extra = extra; *rd_buffer = 0; }
/* * CL_ResolveMasterAddress */ static void CL_ResolveMasterAddress( const char *master ) { netadr_t adr, *padr; if( master && *master ) { NET_StringToAddress( master, &adr ); QMutex_Lock( resolveLock ); padr = ( netadr_t * )malloc( sizeof( adr ) ); *padr = adr; Trie_Insert( serverlist_masters_trie, master, padr ); QMutex_Unlock( resolveLock ); } }
void wswcurl_urlencode( const char *src, char *dst, size_t size ) { char *curl_esc; assert( src ); assert( dst ); if( !src || !dst ) { return; } if( !curldummy ) { return; } QMutex_Lock( curldummy_mutex ); curl_esc = qcurl_easy_escape( curldummy, src, 0 ); QMutex_Unlock( curldummy_mutex ); Q_strncpyz( dst, curl_esc, size ); qcurl_free( curl_esc ); }
/* * CL_GetServers_f */ void CL_GetServers_f( void ) { netadr_t adr, *padr; char *requeststring; int i; char *modname, *master; trie_error_t err; filter_allow_full = qfalse; filter_allow_empty = qfalse; for( i = 2; i < Cmd_Argc(); i++ ) { if( !Q_stricmp( "full", Cmd_Argv( i ) ) ) filter_allow_full = qtrue; if( !Q_stricmp( "empty", Cmd_Argv( i ) ) ) filter_allow_empty = qtrue; } if( !Q_stricmp( Cmd_Argv( 1 ), "local" ) ) { if( localQueryTimeStamp + LAN_SERVER_PINGING_TIMEOUT > Sys_Milliseconds() ) { return; } padr = &adr; localQueryTimeStamp = Sys_Milliseconds(); // send a broadcast packet Com_DPrintf( "pinging broadcast...\n" ); // erm... modname isn't sent in local queries? requeststring = va( "info %i %s %s", SERVERBROWSER_PROTOCOL_VERSION, filter_allow_full ? "full" : "", filter_allow_empty ? "empty" : "" ); for( i = 0; i < NUM_BROADCAST_PORTS; i++ ) { NET_BroadcastAddress( padr, PORT_SERVER + i ); Netchan_OutOfBandPrint( &cls.socket_udp, padr, requeststring ); } return; } //get what master master = Cmd_Argv( 2 ); if( !master || !( *master ) ) return; modname = Cmd_Argv( 3 ); // never allow anyone to use DEFAULT_BASEGAME as mod name if( !modname || !modname[0] || !Q_stricmp( modname, DEFAULT_BASEGAME ) ) modname = APPLICATION; assert( modname[0] ); // check memory cache QMutex_Lock( resolveLock ); err = Trie_Find( serverlist_masters_trie, master, TRIE_EXACT_MATCH, (void **)&padr ); QMutex_Unlock( resolveLock ); if( err == TRIE_OK && ( padr->type == NA_IP || padr->type == NA_IP6 ) ) { const char *cmdname; socket_t *socket; if ( padr->type == NA_IP ) { cmdname = "getservers"; socket = &cls.socket_udp; } else { cmdname = "getserversExt"; socket = &cls.socket_udp6; } // create the message requeststring = va( "%s %c%s %i %s %s", cmdname, toupper( modname[0] ), modname+1, SERVERBROWSER_PROTOCOL_VERSION, filter_allow_full ? "full" : "", filter_allow_empty ? "empty" : "" ); if( NET_GetAddressPort( padr ) == 0 ) NET_SetAddressPort( padr, PORT_MASTER ); Netchan_OutOfBandPrint( socket, padr, requeststring ); Com_DPrintf( "quering %s...%s: %s\n", master, NET_AddressToString(padr), requeststring ); } else { Com_Printf( "Bad address: %s\n", master ); } }
/* * QBufPipe_WriteCmd * * Add new command to buffer. Never allow the distance between the reader * and the writer to grow beyond the size of the buffer. * * Note that there are race conditions here but in the worst case we're going * to erroneously drop cmd's instead of stepping on the reader's toes. */ void QBufPipe_WriteCmd( qbufPipe_t *pipe, const void *cmd, unsigned cmd_size ) { void *buf; unsigned write_remains; bool was_empty; if( !pipe ) { return; } if( pipe->terminated ) { return; } assert( pipe->bufSize >= pipe->write_pos ); if( pipe->bufSize < pipe->write_pos ) { pipe->write_pos = 0; } was_empty = Sys_Atomic_CAS( &pipe->cmdbuf_len, 0, 0, pipe->cmdbuf_mutex ) == true; write_remains = pipe->bufSize - pipe->write_pos; if( sizeof( int ) > write_remains ) { while( pipe->cmdbuf_len + cmd_size + write_remains > pipe->bufSize ) { if( pipe->blockWrite ) { QThread_Yield(); continue; } return; } // not enough space to enpipe even the reset cmd, rewind QBufPipe_BufLenAdd( pipe, write_remains ); // atomic pipe->write_pos = 0; } else if( cmd_size > write_remains ) { int *cmd; while( pipe->cmdbuf_len + sizeof( int ) + cmd_size + write_remains > pipe->bufSize ) { if( pipe->blockWrite ) { QThread_Yield(); continue; } return; } // explicit pointer reset cmd cmd = QBufPipe_AllocCmd( pipe, sizeof( int ) ); *cmd = -1; QBufPipe_BufLenAdd( pipe, sizeof( *cmd ) + write_remains ); // atomic pipe->write_pos = 0; } else { while( pipe->cmdbuf_len + cmd_size > pipe->bufSize ) { if( pipe->blockWrite ) { QThread_Yield(); continue; } return; } } buf = QBufPipe_AllocCmd( pipe, cmd_size ); memcpy( buf, cmd, cmd_size ); QBufPipe_BufLenAdd( pipe, cmd_size ); // atomic // wake the other thread waiting for signal if( was_empty ) { QMutex_Lock( pipe->nonempty_mutex ); QBufPipe_Wake( pipe ); QMutex_Unlock( pipe->nonempty_mutex ); } }
/* * Com_Printf * * Both client and server can use this, and it will output * to the apropriate place. */ void Com_Printf( const char *format, ... ) { va_list argptr; char msg[MAX_PRINTMSG]; time_t timestamp; char timestamp_str[MAX_PRINTMSG]; struct tm *timestampptr; timestamp = time( NULL ); timestampptr = gmtime( ×tamp ); strftime( timestamp_str, MAX_PRINTMSG, "%Y-%m-%dT%H:%M:%SZ ", timestampptr ); va_start( argptr, format ); Q_vsnprintfz( msg, sizeof( msg ), format, argptr ); va_end( argptr ); if( rd_target ) { if( (int)( strlen( msg ) + strlen( rd_buffer ) ) > ( rd_buffersize - 1 ) ) { rd_flush( rd_target, rd_buffer, rd_extra ); *rd_buffer = 0; } strcat( rd_buffer, msg ); return; } QMutex_Lock( com_print_mutex ); Con_Print( msg ); // also echo to debugging console Sys_ConsoleOutput( msg ); // logconsole if( logconsole && logconsole->modified ) { logconsole->modified = qfalse; if( log_file ) { FS_FCloseFile( log_file ); log_file = 0; } if( logconsole->string && logconsole->string[0] ) { size_t name_size; char *name; name_size = strlen( logconsole->string ) + strlen( ".log" ) + 1; name = ( char* )Mem_TempMalloc( name_size ); Q_strncpyz( name, logconsole->string, name_size ); COM_DefaultExtension( name, ".log", name_size ); if( FS_FOpenFile( name, &log_file, ( logconsole_append && logconsole_append->integer ? FS_APPEND : FS_WRITE ) ) == -1 ) { log_file = 0; Com_Printf( "Couldn't open: %s\n", name ); } Mem_TempFree( name ); } } if( log_file ) { if( logconsole_timestamp && logconsole_timestamp->integer ) FS_Printf( log_file, "%s", timestamp_str ); FS_Printf( log_file, "%s", msg ); if( logconsole_flush && logconsole_flush->integer ) FS_Flush( log_file ); // force it to save every time } QMutex_Unlock( com_print_mutex ); }
void *_Mem_AllocExt( mempool_t *pool, size_t size, size_t alignment, int z, int musthave, int canthave, const char *filename, int fileline ) { void *base; size_t realsize; memheader_t *mem; if( size <= 0 ) return NULL; // default to 16-bytes alignment if( !alignment ) alignment = MEMALIGNMENT_DEFAULT; assert( pool != NULL ); if( pool == NULL ) _Mem_Error( "Mem_Alloc: pool == NULL (alloc at %s:%i)", filename, fileline ); if( musthave && ( ( pool->flags & musthave ) != musthave ) ) _Mem_Error( "Mem_Alloc: bad pool flags (musthave) (alloc at %s:%i)", filename, fileline ); if( canthave && ( pool->flags & canthave ) ) _Mem_Error( "Mem_Alloc: bad pool flags (canthave) (alloc at %s:%i)", filename, fileline ); if( developerMemory && developerMemory->integer ) Com_DPrintf( "Mem_Alloc: pool %s, file %s:%i, size %i bytes\n", pool->name, filename, fileline, size ); QMutex_Lock( memMutex ); pool->totalsize += size; realsize = sizeof( memheader_t ) + size + alignment + sizeof( int ); pool->realsize += realsize; base = malloc( realsize ); if( base == NULL ) _Mem_Error( "Mem_Alloc: out of memory (alloc at %s:%i)", filename, fileline ); // calculate address that aligns the end of the memheader_t to the specified alignment mem = ( memheader_t * )((((size_t)base + sizeof( memheader_t ) + (alignment-1)) & ~(alignment-1)) - sizeof( memheader_t )); mem->baseaddress = base; mem->filename = filename; mem->fileline = fileline; mem->size = size; mem->realsize = realsize; mem->pool = pool; mem->sentinel1 = MEMHEADER_SENTINEL1; // we have to use only a single byte for this sentinel, because it may not be aligned, and some platforms can't use unaligned accesses *( (qbyte *) mem + sizeof( memheader_t ) + mem->size ) = MEMHEADER_SENTINEL2; // append to head of list mem->next = pool->chain; mem->prev = NULL; pool->chain = mem; if( mem->next ) mem->next->prev = mem; QMutex_Unlock( memMutex ); if( z ) memset( (void *)( (qbyte *) mem + sizeof( memheader_t ) ), 0, mem->size ); return (void *)( (qbyte *) mem + sizeof( memheader_t ) ); }
void wswcurl_delete(wswcurl_req *req) { if ( (!req) || (!wswcurl_isvalidhandle(req)) ) { return; } // if (req->callback_done && req->active ) // req->callback_done ( req, 0, req->customp ); if (req->txhead) { curl_slist_free_all(req->txhead); req->txhead = NULL; } if (req->post) { curl_formfree(req->post); req->post = NULL; req->post_last = NULL; } if (req->url) { WFREE(req->url); } if( req->bhead ) { chained_buffer_t *cb = req->bhead; chained_buffer_t *next = cb->next; while( cb ) { WFREE( cb ); cb = next; if( cb ) next = cb->next; } } // remove from list QMutex_Lock( http_requests_mutex ); if (req->curl) { if (curlmulti && req->status && req->status != WSTATUS_QUEUED) { curl_multi_remove_handle(curlmulti, req->curl); curlmulti_num_handles--; } curl_easy_cleanup(req->curl); req->curl = NULL; } if (http_requests_hnode == req) http_requests_hnode = req->prev; if (http_requests == req) http_requests = req->next; if (req->prev) req->prev->next = req->next; if (req->next) req->next->prev = req->prev; QMutex_Unlock( http_requests_mutex ); WFREE(req); }
wswcurl_req *wswcurl_create( const char *iface, const char *furl, ... ) { wswcurl_req *retreq; CURL *curl; CURLcode res; char url[4 * 1024]; // 4kb url buffer? va_list arg; const char *proxy = http_proxy->string; const char *proxy_userpwd = http_proxyuserpwd->string; // Prepare url formatting with variable arguments va_start( arg, furl ); Q_vsnprintfz( url, sizeof( url ), furl, arg ); va_end( arg ); // Initialize structure if( !(curl = curl_easy_init()) ) { return NULL; } // allocate, copy retreq = ( wswcurl_req * )WMALLOC( sizeof( wswcurl_req ) ); memset( retreq, 0, sizeof( *retreq ) ); retreq->curl = curl; retreq->url = ( char* )WMALLOC( strlen( url ) + 1 ); memcpy( retreq->url, url, strlen( url ) + 1 ); CURLSETOPT( curl, res, CURLOPT_URL, retreq->url ); CURLSETOPT( curl, res, CURLOPT_WRITEFUNCTION, wswcurl_write ); CURLSETOPT( curl, res, CURLOPT_NOPROGRESS, 1 ); CURLSETOPT( curl, res, CURLOPT_FOLLOWLOCATION, 1 ); CURLSETOPT( curl, res, CURLOPT_HEADERFUNCTION, wswcurl_readheader ); CURLSETOPT( curl, res, CURLOPT_CONNECTTIMEOUT, WCONNECTTIMEOUT ); #if defined( APPLICATION ) && defined( APP_VERSION_STR ) && defined( OSNAME ) && defined( CPUSTRING ) CURLSETOPT( curl, res, CURLOPT_USERAGENT, APPLICATION"/"APP_VERSION_STR" (compatible; N; "OSNAME"; "CPUSTRING")" ); #endif CURLSETOPT( curl, res, CURLOPT_WRITEDATA, ( void * )retreq ); CURLSETOPT( curl, res, CURLOPT_WRITEHEADER, ( void * )retreq ); CURLSETOPT( curl, res, CURLOPT_PRIVATE, ( void * )retreq ); if( iface && *iface ) { CURLSETOPT( curl, res, CURLOPT_INTERFACE, ( void * )iface ); } if( developer->integer ) { CURLSETOPT( curl, res, CURLOPT_DEBUGFUNCTION, &wswcurl_debug_callback ); CURLSETOPT( curl, res, CURLOPT_DEBUGDATA, ( void * )retreq ); CURLSETOPT( curl, res, CURLOPT_VERBOSE, 1 ); } // HTTP proxy settings if( proxy && *proxy ) { CURLSETOPT( curl, res, CURLOPT_PROXYTYPE, CURLPROXY_HTTP ); CURLSETOPT( curl, res, CURLOPT_PROXY, proxy ); if( proxy_userpwd && *proxy_userpwd ) { CURLSETOPT( curl, res, CURLOPT_PROXYUSERPWD, proxy_userpwd ); } } wswcurl_set_timeout( retreq, WTIMEOUT ); // link QMutex_Lock( http_requests_mutex ); retreq->prev = NULL; retreq->next = http_requests; if( retreq->next ) { retreq->next->prev = retreq; } else { http_requests_hnode = retreq; } http_requests = retreq; CURLDBG((va(" CURL CREATE %s\n", url))); QMutex_Unlock( http_requests_mutex ); return retreq; }
int wswcurl_perform() { int ret = 0; wswcurl_req *r, *next; if (!curlmulti) return 0; // process requests in FIFO manner QMutex_Lock( http_requests_mutex ); // check for timed out requests and requests that need to be paused r = http_requests_hnode; while( r ) { next = r->prev; if (r->status == WSTATUS_QUEUED) { // queued if (curlmulti_num_handles < WMAXMULTIHANDLES) { if (curl_multi_add_handle(curlmulti, r->curl)) { CURLDBG(("OOPS: CURL MULTI ADD HANDLE FAIL!!!")); } r->status = WSTATUS_STARTED; r->last_action = wswcurl_now(); curlmulti_num_handles++; } else { // stay in queue } } // handle pauses for synchronous requests if( r->status == WSTATUS_STARTED && !r->callback_read ) { if( r->rxreceived >= r->rxreturned + WMAXBUFFERING ) { wswcurl_pause( r ); } } // handle timeouts if( r->status == WSTATUS_STARTED ) { time_t now = wswcurl_now(); if( r->paused ) { // paused r->last_action = now; } else if( r->timeout && ( r->last_action + r->timeout <= now ) ) { // timed out r->respcode = -1; r->status = -CURLE_OPERATION_TIMEDOUT; if( r->callback_done ) { r->callback_done( r, r->status, r->customp ); } } } r = next; } QMutex_Unlock( http_requests_mutex ); //CURLDBG(("CURL BEFORE MULTI_PERFORM\n")); while ( curl_multi_perform(curlmulti, &ret) == CURLM_CALL_MULTI_PERFORM) { CURLDBG((" CURL MULTI LOOP\n")); } ret += wswcurl_checkmsg(); //CURLDBG(("CURL after checkmsg\n")); return ret; }