void pipe_initialize( stream_pipe_t* pipestream ) { stream_t* stream = (stream_t*)pipestream; memset( stream, 0, sizeof( stream_pipe_t ) ); stream_initialize( stream, system_byteorder() ); pipestream->type = STREAMTYPE_PIPE; pipestream->path = string_format( "pipe://0x" PRIfixPTR, pipestream ); pipestream->mode = STREAM_OUT | STREAM_IN | STREAM_BINARY; pipestream->sequential = true; #if FOUNDATION_PLATFORM_WINDOWS { //Inheritable by default so process can use for stdstreams SECURITY_ATTRIBUTES security_attribs = {0}; security_attribs.nLength = sizeof( SECURITY_ATTRIBUTES ); security_attribs.bInheritHandle = TRUE; security_attribs.lpSecurityDescriptor = 0; if( !CreatePipe( &pipestream->handle_read, &pipestream->handle_write, &security_attribs, 0 ) ) log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to create unnamed pipe: %s", system_error_message( GetLastError() ) ); } #elif FOUNDATION_PLATFORM_POSIX || FOUNDATION_PLATFORM_PNACL int fds[2] = { 0, 0 }; if( pipe( fds ) < 0 ) log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to create unnamed pipe: %s", system_error_message( 0 ) ); pipestream->fd_read = fds[0]; pipestream->fd_write = fds[1]; #endif pipestream->vtable = &_pipe_stream_vtable; }
bool _initialize_stackwalker() { if( _stackwalk_initialized ) return true; #if FOUNDATION_PLATFORM_WINDOWS { void* dll = LoadLibraryA( "DBGHELP.DLL" ); CallStackWalk64 = dll ? (StackWalk64Fn)GetProcAddress( dll, "StackWalk64" ) : 0; if( !CallStackWalk64 ) { log_warnf( WARNING_SYSTEM_CALL_FAIL, "Unable to load dbghelp DLL for StackWalk64" ); return false; } dll = LoadLibraryA( "NTDLL.DLL" ); CallRtlCaptureStackBackTrace = dll ? (RtlCaptureStackBackTraceFn)GetProcAddress( dll, "RtlCaptureStackBackTrace" ) : 0; if( !CallRtlCaptureStackBackTrace ) { log_warnf( WARNING_SYSTEM_CALL_FAIL, "Unable to load ntdll DLL for RtlCaptureStackBackTrace" ); return false; } } #endif _stackwalk_initialized = true; return true; }
int blast_client(network_address_t** * target, string_t* files) { int itarg, tsize = 0; int iclient, csize = 0; int ifile, fsize = 0; bool running = true; int result = BLAST_RESULT_OK; blast_reader_t* reader = 0; blast_reader_t** readers = 0; for (ifile = 0, fsize = array_size(files); ifile < fsize; ++ifile) { reader = blast_reader_open(files[ifile]); if (!reader) { log_warnf(HASH_BLAST, WARNING_SUSPICIOUS, STRING_CONST("Unable to open reader for: %.*s"), STRING_ARGS(files[ifile])); return BLAST_ERROR_UNABLE_TO_OPEN_FILE; } array_push(readers, reader); } if (array_size(readers) == 0) { log_warnf(HASH_BLAST, WARNING_INVALID_VALUE, STRING_CONST("No input files given")); return BLAST_ERROR_UNABLE_TO_OPEN_FILE; } for (itarg = 0, tsize = array_size(target); itarg < tsize; ++itarg) { blast_client_t client; if (blast_client_initialize(&client, target[itarg]) == BLAST_RESULT_OK) { client.readers = readers; array_push(clients, client); } } while (running && !blast_should_exit()) { running = false; for (iclient = 0, csize = array_size(clients); iclient < csize; ++iclient) running |= blast_client_process(clients + iclient); blast_process_system_events(); } for (iclient = 0, csize = array_size(clients); iclient < csize; ++iclient) blast_client_deallocate(&clients[iclient]); array_deallocate(clients); for (ifile = 0, fsize = array_size(readers); ifile < fsize; ++ifile) blast_reader_close(readers[ifile]); array_deallocate(readers); return result; }
int bin2hex_process_files( char const* const* input, char const* const* output, int columns ) { int result = BIN2HEX_RESULT_OK; unsigned int ifile, files_size; for( ifile = 0, files_size = array_size( input ); ( result == BIN2HEX_RESULT_OK ) && ( ifile < files_size ); ++ifile ) { char* input_filename = 0; char* output_filename = 0; stream_t* input_file = 0; stream_t* output_file = 0; input_filename = path_clean( string_clone( input[ifile] ), path_is_absolute( input[ifile] ) ); error_context_push( "parsing file", input_filename ); output_filename = path_clean( string_clone( output[ifile] ), path_is_absolute( output[ifile] ) ); log_infof( 0, "bin2hex %s -> %s", input_filename, output_filename ); input_file = stream_open( input_filename, STREAM_IN | STREAM_BINARY ); if( !input_file ) { log_warnf( 0, WARNING_BAD_DATA, "Unable to open input file: %s", input_filename ); result = BIN2HEX_RESULT_MISSING_INPUT_FILE; } else { output_file = stream_open( output_filename, STREAM_OUT ); if( !output_file ) { log_warnf( 0, WARNING_BAD_DATA, "Unable to open output file: %s", output_filename ); result = BIN2HEX_RESULT_UNABLE_TO_OPEN_OUTPUT_FILE; } } if( input_file && output_file ) result = bin2hex_process_file( input_file, output_file, columns ); stream_deallocate( input_file ); stream_deallocate( output_file ); string_deallocate( output_filename ); error_context_pop(); string_deallocate( input_filename ); } if( ( result == BIN2HEX_RESULT_OK ) && ( files_size > 0 ) ) log_info( 0, "All files generated" ); return result; }
static void _memory_deallocate_malloc(void* p) { #if FOUNDATION_SIZE_POINTER == 4 if (!p) return; # if BUILD_ENABLE_MEMORY_GUARD p = _memory_guard_verify(p); # endif # if FOUNDATION_PLATFORM_WINDOWS _aligned_free(p); # else free(*((void**)p - 1)); # endif #else uintptr_t raw_ptr; if (!p) return; # if BUILD_ENABLE_MEMORY_GUARD p = _memory_guard_verify(p); # endif raw_ptr = *((uintptr_t*)p - 1); if (raw_ptr & 1) { raw_ptr &= ~(uintptr_t)1; # if FOUNDATION_PLATFORM_WINDOWS if (VirtualFree((void*)raw_ptr, 0, MEM_RELEASE) == 0) log_warnf(HASH_MEMORY, WARNING_SYSTEM_CALL_FAIL, STRING_CONST("Failed to VirtualFree 0x%" PRIfixPTR), (uintptr_t)raw_ptr); # else uintptr_t raw_size = *((uintptr_t*)p - 2); if (munmap((void*)raw_ptr, raw_size) < 0) log_warnf(HASH_MEMORY, WARNING_SYSTEM_CALL_FAIL, STRING_CONST("Failed to munmap 0x%" PRIfixPTR " size %" PRIsize), (uintptr_t)raw_ptr, raw_size); # endif } else { # if FOUNDATION_PLATFORM_WINDOWS _aligned_free((void*)raw_ptr); # else free((void*)raw_ptr); # endif } #endif }
int main_run(void* main_arg) { int result = RENDERCOMPILE_RESULT_OK; rendercompile_input_t input = rendercompile_parse_command_line(environment_command_line()); FOUNDATION_UNUSED(main_arg); if (input.display_help) { rendercompile_print_usage(); goto exit; } resource_source_set_path(STRING_ARGS(input.source_path)); resource_compile_register(render_compile); size_t ifile, fsize; for (ifile = 0, fsize = array_size(input.input_files); ifile < fsize; ++ifile) { uuid_t uuid = string_to_uuid(STRING_ARGS(input.input_files[ifile])); if (uuid_is_null(uuid)) { char buffer[BUILD_MAX_PATHLEN]; string_t pathstr = string_copy(buffer, sizeof(buffer), STRING_ARGS(input.input_files[ifile])); pathstr = path_clean(STRING_ARGS(pathstr), sizeof(buffer)); pathstr = path_absolute(STRING_ARGS(pathstr), sizeof(buffer)); uuid = resource_import_map_lookup(STRING_ARGS(pathstr)); } if (uuid_is_null(uuid)) { log_warnf(HASH_RESOURCE, WARNING_INVALID_VALUE, STRING_CONST("Failed to lookup: %.*s"), STRING_FORMAT(input.input_files[ifile])); result = RENDERCOMPILE_RESULT_INVALID_INPUT; break; } if (resource_compile(uuid, RESOURCE_PLATFORM_ALL)) { string_const_t uuidstr = string_from_uuid_static(uuid); log_infof(HASH_RESOURCE, STRING_CONST("Successfully compiled: %.*s (%.*s)"), STRING_FORMAT(uuidstr), STRING_FORMAT(input.input_files[ifile])); } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_RESOURCE, WARNING_UNSUPPORTED, STRING_CONST("Failed to compile: %.*s (%.*s)"), STRING_FORMAT(uuidstr), STRING_FORMAT(input.input_files[ifile])); } } exit: array_deallocate(input.input_files); return result; }
static lua_module_t lua_module_load_resource(const uuid_t uuid) { lua_module_t module = {0, 0}; bool success = false; const uint32_t expected_version = 1; uint64_t platform = 0; stream_t* stream; stream = resource_stream_open_static(uuid, platform); if (stream) { resource_header_t header = resource_stream_read_header(stream); if ((header.type == HASH_LUA) && (header.version == expected_version)) { success = true; } else { log_warnf(HASH_LUA, WARNING_INVALID_VALUE, STRING_CONST("Got unexpected type/version when loading Lua module: %" PRIx64 " : %u"), header.type, header.version); } stream_deallocate(stream); stream = nullptr; } if (success) stream = resource_stream_open_dynamic(uuid, platform); if (stream) { uint32_t version = stream_read_uint32(stream); size_t size = (size_t)stream_read_uint64(stream); if (version == expected_version) { void* buffer = memory_allocate(HASH_LUA, size, 0, MEMORY_TEMPORARY); if (stream_read(stream, buffer, size) == size) { module.bytecode = buffer; module.size = size; } else { memory_deallocate(buffer); } } else { log_warnf(HASH_LUA, WARNING_INVALID_VALUE, STRING_CONST("Got unexpected type/version when loading Lua module blob: %u"), version); } stream_deallocate(stream); stream = nullptr; } return module; }
void mutex_signal( mutex_t* mutex ) { FOUNDATION_ASSERT( mutex ); #if !BUILD_DEPLOY profile_signal( mutex->name ); #endif #if FOUNDATION_PLATFORM_WINDOWS SetEvent( mutex->event ); #elif FOUNDATION_PLATFORM_POSIX mutex_lock( mutex ); mutex->pending = true; int ret = pthread_cond_broadcast( &mutex->cond ); if( ret != 0 ) log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to signal mutex '%s': %s (%d)", mutex->name, system_error_message( ret ), ret ); mutex_unlock( mutex ); #else # error mutex_signal not implemented #endif }
static int _x11_error_handler(Display* display, XErrorEvent* event) { char errmsg[512]; XGetErrorText(display, event->error_code, errmsg, sizeof(errmsg)); log_warnf(HASH_WINDOW, WARNING_SYSTEM_CALL_FAIL, STRING_CONST("X error event occurred: %s"), errmsg); return 0; }
bool mutex_unlock(mutex_t* mutex) { if (!mutex->lockcount) { log_warnf(0, WARNING_SUSPICIOUS, STRING_CONST("Unable to unlock unlocked mutex %.*s"), (int)mutex->name.length, mutex->name.str); return false; } FOUNDATION_ASSERT(mutex->lockedthread == thread_id()); --mutex->lockcount; #if !BUILD_DEPLOY profile_unlock(mutex->name.str, mutex->name.length); #endif #if FOUNDATION_PLATFORM_WINDOWS LeaveCriticalSection((CRITICAL_SECTION*)mutex->csection); #elif FOUNDATION_PLATFORM_POSIX || FOUNDATION_PLATFORM_PNACL if (pthread_mutex_unlock(&mutex->mutex) != 0) { FOUNDATION_ASSERT_FAILFORMAT("unable to unlock mutex %s", mutex->name.str); return false; } #else # error mutex_unlock not implemented #endif return true; }
static void _memory_tracker_shutdown( void ) { if( _memory_table ) hashtable_deallocate( _memory_table ); if( _memory_tags ) { bool got_leaks = false; log_debug( "Checking for memory leaks" ); for( unsigned int it = 0; it < MAX_CONCURRENT_ALLOCATIONS; ++it ) { memory_tag_t* tag = _memory_tags + it; if( tag->address ) { char* trace = stacktrace_resolve( tag->trace, 14, 0 ); log_warnf( WARNING_MEMORY, "Memory leak: %d bytes @ " STRING_FORMAT_POINTER " : tag %d\n%s", (unsigned int)tag->size, tag->address, it, trace ); string_deallocate( trace ); got_leaks = true; } } memory_deallocate( _memory_tags ); if( !got_leaks ) log_debug( "No memory leaks detected" ); } }
static void blast_client_read_ack(blast_client_t* client) { const network_address_t* address = 0; char databuf[PACKET_DATABUF_SIZE]; size_t size = udp_socket_recvfrom(client->sock, databuf, sizeof(databuf), &address); while (size > 0) { packet_t* packet = (packet_t*)databuf; if (network_address_equal(address, socket_address_remote(client->sock))) { if (packet->type == PACKET_ACK) { packet_ack_t* ack = (packet_ack_t*)packet; blast_client_process_ack(client, ack->ack, packet->timestamp); } else if (packet->type == PACKET_TERMINATE) { log_info(HASH_BLAST, STRING_CONST("Client terminating due to TERMINATE packet from server")); client->state = BLAST_STATE_FINISHED; break; } } else { char buffer[NETWORK_ADDRESS_NUMERIC_MAX_LENGTH]; string_t addr = network_address_to_string(buffer, sizeof(buffer), address, true); log_warnf(HASH_BLAST, WARNING_SUSPICIOUS, STRING_CONST("Ignoring datagram from unknown host %.*s"), STRING_FORMAT(addr)); } size = udp_socket_recvfrom(client->sock, databuf, sizeof(databuf), &address); } }
int hashify_write_file(stream_t* generated_file, string_t output_filename) { bool need_update = false; stream_t* output_file = 0; int result = HASHIFY_RESULT_OK; output_file = stream_open(STRING_ARGS(output_filename), STREAM_OUT | STREAM_IN); if (!output_file) { need_update = true; output_file = stream_open(STRING_ARGS(output_filename), STREAM_OUT); if (!output_file) { log_warnf(0, WARNING_INVALID_VALUE, STRING_CONST("Unable to open output file: %.*s"), STRING_FORMAT(output_filename)); return HASHIFY_RESULT_MISSING_OUTPUT_FILE; } } if (!need_update) need_update = !uint128_equal(stream_md5(generated_file), stream_md5(output_file)); if (need_update) { char local_buffer[1024]; size_t read = 0; size_t written = 0; uint64_t total_written = 0; stream_seek(generated_file, 0, STREAM_SEEK_BEGIN); stream_seek(output_file, 0, STREAM_SEEK_BEGIN); while (!stream_eos(generated_file)) { read = stream_read(generated_file, local_buffer, 1024); if (!read) break; written = stream_write(output_file, local_buffer, read); total_written += written; if (written != read) { log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to write to output file '%.*s': %" PRIsize " of %" PRIsize " bytes written"), STRING_FORMAT(output_filename), written, read); result = HASHIFY_RESULT_OUTPUT_FILE_WRITE_FAIL; break; } } if (result == HASHIFY_RESULT_OK) { stream_truncate(output_file, (size_t)total_written); log_infof(0, STRING_CONST(" wrote %.*s : %" PRIu64 " bytes"), STRING_FORMAT(output_filename), total_written); } } else { log_info(0, STRING_CONST(" hash file already up to date")); } stream_deallocate(output_file); return result; }
void _profile_thread_finalize( void ) { #if BUILD_ENABLE_PROFILE uint32_t block_index, last_block = 0; while( ( block_index = get_thread_profile_block() ) ) { log_warnf( 0, WARNING_SUSPICIOUS, "Profile thread cleanup, free block %u", block_index ); if( last_block == block_index ) { log_warnf( 0, WARNING_SUSPICIOUS, "Unrecoverable error, self reference in block %u", block_index ); break; } profile_end_block(); } #endif }
static size_t _socket_stream_write(stream_t* stream, const void* buffer, size_t size) { socket_stream_t* sockstream; socket_t* sock; size_t was_written = 0; size_t remain; sockstream = (socket_stream_t*)stream; sock = sockstream->socket; if ((sock->fd == NETWORK_SOCKET_INVALID) || (sock->state != SOCKETSTATE_CONNECTED) || !size || !buffer) goto exit; remain = sockstream->buffer_out_size - sockstream->write_out; do { if (size <= remain) { memcpy(sockstream->buffer_out + sockstream->write_out, buffer, (size_t)size); sockstream->write_out += (unsigned int)size; was_written += size; size = 0; break; } if (remain) { memcpy(sockstream->buffer_out + sockstream->write_out, buffer, remain); buffer = pointer_offset_const(buffer, remain); size -= remain; was_written += remain; sockstream->write_out += remain; } _socket_stream_doflush(sockstream); if (sock->state != SOCKETSTATE_CONNECTED) { log_warnf(HASH_NETWORK, WARNING_SUSPICIOUS, STRING_CONST("Socket stream (0x%" PRIfixPTR " : %d): partial write %" PRIsize " of %" PRIsize " bytes"), (uintptr_t)sock, sock->fd, was_written, size); break; } remain = sockstream->buffer_out_size - sockstream->write_out; } while (remain); exit: return was_written; }
void environment_set_current_working_directory( const char* path ) { if( !path ) return; log_debugf( "Setting current working directory to: %s", path ); #if FOUNDATION_PLATFORM_WINDOWS { wchar_t* wpath = wstring_allocate_from_string( path, 0 ); if( !SetCurrentDirectoryW( wpath ) ) log_warnf( WARNING_SUSPICIOUS, "Unable to set working directory: %ls", wpath ); wstring_deallocate( wpath ); } #elif FOUNDATION_PLATFORM_POSIX if( chdir( path ) < 0 ) log_warnf( WARNING_SYSTEM_CALL_FAIL, "Unable to set working directory: %s", path ); #else # error Not implemented #endif _environment_current_working_dir[0] = 0; }
stream_t* pipe_allocate( void ) { stream_pipe_t* pipestream = memory_allocate_zero_context( MEMORYCONTEXT_STREAM, sizeof( stream_pipe_t ), 0, MEMORY_PERSISTENT ); stream_t* stream = (stream_t*)pipestream; _stream_initialize( stream, system_byteorder() ); pipestream->type = STREAMTYPE_PIPE; pipestream->path = string_format( "pipe://" STRING_FORMAT_POINTER, pipestream ); pipestream->mode = STREAM_OUT | STREAM_IN | STREAM_BINARY; pipestream->sequential = true; #if FOUNDATION_PLATFORM_WINDOWS { //Inheritable by default so process can use for stdstreams SECURITY_ATTRIBUTES security_attribs = {0}; security_attribs.nLength = sizeof( SECURITY_ATTRIBUTES ); security_attribs.bInheritHandle = TRUE; security_attribs.lpSecurityDescriptor = 0; if( !CreatePipe( &pipestream->handle_read, &pipestream->handle_write, &security_attribs, 0 ) ) log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to create unnamed pipe: %s", system_error_message( GetLastError() ) ); } #elif FOUNDATION_PLATFORM_POSIX int fds[2] = { 0, 0 }; if( pipe( fds ) < 0 ) log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to create unnamed pipe: %s", system_error_message( 0 ) ); else { pipestream->fd_read = fds[0]; pipestream->fd_write = fds[1]; } #endif pipestream->vtable = &_pipe_stream_vtable; return stream; }
static uint64_t _pipe_stream_read( stream_t* stream, void* dest, uint64_t num ) { stream_pipe_t* pipestream = (stream_pipe_t*)stream; FOUNDATION_ASSERT( stream->type == STREAMTYPE_PIPE ); #if FOUNDATION_PLATFORM_WINDOWS if( pipestream->handle_read && ( ( pipestream->mode & STREAM_IN ) != 0 ) ) { uint64_t total_read = 0; do { unsigned long num_read = 0; if( !ReadFile( pipestream->handle_read, pointer_offset( dest, total_read ), (unsigned int)( num - total_read ), &num_read, 0 ) ) { int err = GetLastError(); if( err == ERROR_BROKEN_PIPE ) { pipestream->eos = true; break; } log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to read from pipe: %s (%d)", system_error_message( err ), err ); } else { total_read += num_read; } } while( total_read < num ); return total_read; } #elif FOUNDATION_PLATFORM_POSIX || FOUNDATION_PLATFORM_PNACL if( pipestream->fd_read && ( ( pipestream->mode & STREAM_IN ) != 0 ) ) { uint64_t total_read = 0; do { ssize_t num_read = read( pipestream->fd_read, pointer_offset( dest, total_read ), (size_t)( num - total_read ) ); if( num_read <= 0 ) { pipestream->eos = true; break; } total_read += num_read; } while( total_read < num ); return total_read; } #endif return 0; }
void* _allocate_thread_local_block(size_t size) { void* block = memory_allocate(0, size, 0, MEMORY_PERSISTENT | MEMORY_ZERO_INITIALIZED); for (int i = 0; i < 1024; ++i) { if (!atomic_loadptr(&_thread_local_blocks[i].block)) { if (atomic_cas_ptr(&_thread_local_blocks[i].block, block, 0)) { _thread_local_blocks[i].thread = thread_id(); return block; } } } log_warnf(0, WARNING_MEMORY, STRING_CONST("Unable to locate thread local memory block slot, will leak %" PRIsize " bytes"), size); return block; }
int main_run( void* main_arg ) { #if !BUILD_MONOLITHIC const char* pattern = 0; char** exe_paths = 0; unsigned int iexe, exesize; process_t* process = 0; char* process_path = 0; unsigned int* exe_flags = 0; #endif #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID || FOUNDATION_PLATFORM_PNACL int remain_counter = 0; #endif #if BUILD_DEBUG const char* build_name = "debug"; #elif BUILD_RELEASE const char* build_name = "release"; #elif BUILD_PROFILE const char* build_name = "profile"; #elif BUILD_DEPLOY const char* build_name = "deploy"; #endif int process_result = 0; object_t thread = 0; FOUNDATION_UNUSED( main_arg ); FOUNDATION_UNUSED( build_name ); log_set_suppress( HASH_TEST, ERRORLEVEL_DEBUG ); log_infof( HASH_TEST, "Foundation library v%s built for %s using %s (%s)", string_from_version_static( foundation_version() ), FOUNDATION_PLATFORM_DESCRIPTION, FOUNDATION_COMPILER_DESCRIPTION, build_name ); thread = thread_create( event_thread, "event_thread", THREAD_PRIORITY_NORMAL, 0 ); thread_start( thread, 0 ); while( !thread_is_running( thread ) ) thread_sleep( 10 ); #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID while( !_test_should_start ) { #if FOUNDATION_PLATFORM_ANDROID system_process_events(); #endif thread_sleep( 100 ); } #endif fs_remove_directory( environment_temporary_directory() ); #if BUILD_MONOLITHIC test_run_fn tests[] = { test_app_run, test_array_run, test_atomic_run, test_base64_run, test_bitbuffer_run, test_blowfish_run, test_bufferstream_run, test_config_run, test_crash_run, test_environment_run, test_error_run, test_event_run, test_fs_run, test_hash_run, test_hashmap_run, test_hashtable_run, test_library_run, test_math_run, test_md5_run, test_mutex_run, test_objectmap_run, test_path_run, test_pipe_run, test_process_run, test_profile_run, test_radixsort_run, test_random_run, test_regex_run, test_ringbuffer_run, test_semaphore_run, test_stacktrace_run, test_stream_run, //stream test closes stdin test_string_run, test_system_run, test_time_run, test_uuid_run, 0 }; #if FOUNDATION_PLATFORM_ANDROID object_t test_thread = thread_create( test_runner, "test_runner", THREAD_PRIORITY_NORMAL, 0 ); thread_start( test_thread, tests ); log_debug( HASH_TEST, "Starting test runner thread" ); while( !thread_is_running( test_thread ) ) { system_process_events(); thread_sleep( 10 ); } while( thread_is_running( test_thread ) ) { system_process_events(); thread_sleep( 10 ); } process_result = (int)(intptr_t)thread_result( test_thread ); thread_destroy( test_thread ); while( thread_is_thread( test_thread ) ) { system_process_events(); thread_sleep( 10 ); } #else process_result = (int)(intptr_t)test_runner( 0, tests ); #endif if( process_result != 0 ) log_warnf( HASH_TEST, WARNING_SUSPICIOUS, "Tests failed with exit code %d", process_result ); #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID || FOUNDATION_PLATFORM_PNACL while( !_test_should_terminate && _test_have_focus && ( remain_counter < 50 ) ) { system_process_events(); thread_sleep( 100 ); ++remain_counter; } #endif log_debug( HASH_TEST, "Exiting main loop" ); #else // !BUILD_MONOLITHIC //Find all test executables in the current executable directory #if FOUNDATION_PLATFORM_WINDOWS pattern = "^test-.*\\.exe$"; #elif FOUNDATION_PLATFORM_MACOSX pattern = "^test-.*$"; #elif FOUNDATION_PLATFORM_POSIX pattern = "^test-.*$"; #else # error Not implemented #endif exe_paths = fs_matching_files( environment_executable_directory(), pattern, false ); array_resize( exe_flags, array_size( exe_paths ) ); memset( exe_flags, 0, sizeof( unsigned int ) * array_size( exe_flags ) ); #if FOUNDATION_PLATFORM_MACOSX //Also search for test applications const char* app_pattern = "^test-.*\\.app$"; regex_t* app_regex = regex_compile( app_pattern ); char** subdirs = fs_subdirs( environment_executable_directory() ); for( int idir = 0, dirsize = array_size( subdirs ); idir < dirsize; ++idir ) { if( regex_match( app_regex, subdirs[idir], string_length( subdirs[idir] ), 0, 0 ) ) { array_push( exe_paths, string_substr( subdirs[idir], 0, string_length( subdirs[idir] ) - 4 ) ); array_push( exe_flags, PROCESS_MACOSX_USE_OPENAPPLICATION ); } } string_array_deallocate( subdirs ); regex_deallocate( app_regex ); #endif for( iexe = 0, exesize = array_size( exe_paths ); iexe < exesize; ++iexe ) { bool is_self = false; char* exe_file_name = path_base_file_name( exe_paths[iexe] ); if( string_equal( exe_file_name, environment_executable_name() ) ) is_self = true; string_deallocate( exe_file_name ); if( is_self ) continue; //Don't run self process_path = path_merge( environment_executable_directory(), exe_paths[iexe] ); process = process_allocate(); process_set_executable_path( process, process_path ); process_set_working_directory( process, environment_executable_directory() ); process_set_flags( process, PROCESS_ATTACHED | exe_flags[iexe] ); log_infof( HASH_TEST, "Running test executable: %s", exe_paths[iexe] ); process_result = process_spawn( process ); while( process_result == PROCESS_WAIT_INTERRUPTED ) { thread_sleep( 10 ); process_result = process_wait( process ); } process_deallocate( process ); string_deallocate( process_path ); if( process_result != 0 ) { if( process_result >= PROCESS_INVALID_ARGS ) log_warnf( HASH_TEST, WARNING_SUSPICIOUS, "Tests failed, process terminated with error %x", process_result ); else log_warnf( HASH_TEST, WARNING_SUSPICIOUS, "Tests failed with exit code %d", process_result ); process_set_exit_code( -1 ); goto exit; } log_infof( HASH_TEST, "All tests from %s passed (%d)", exe_paths[iexe], process_result ); } log_info( HASH_TEST, "All tests passed" ); exit: if( exe_paths ) string_array_deallocate( exe_paths ); array_deallocate( exe_flags ); #endif thread_terminate( thread ); thread_destroy( thread ); while( thread_is_running( thread ) ) thread_sleep( 10 ); while( thread_is_thread( thread ) ) thread_sleep( 10 ); log_infof( HASH_TEST, "Tests exiting: %d", process_result ); return process_result; }
int process_wait( process_t* proc ) { #if FOUNDATION_PLATFORM_POSIX int cstatus; pid_t err; #endif if( !proc ) return PROCESS_INVALID_ARGS; #if FOUNDATION_PLATFORM_WINDOWS if( !proc->hp ) return proc->code; while( GetExitCodeProcess( proc->hp, (LPDWORD)&proc->code ) ) { if( ( proc->code != STILL_ACTIVE ) || ( proc->flags & PROCESS_DETACHED ) ) break; thread_sleep( 50 ); proc->code = -1; } if( ( proc->code == STILL_ACTIVE ) && ( proc->flags & PROCESS_DETACHED ) ) return PROCESS_STILL_ACTIVE; if( proc->ht ) CloseHandle( proc->ht ); if( proc->hp ) CloseHandle( proc->hp ); proc->hp = 0; proc->ht = 0; #elif FOUNDATION_PLATFORM_POSIX if( !proc->pid ) return proc->code; # if FOUNDATION_PLATFORM_MACOSX if( proc->flags & PROCESS_OSX_USE_OPENAPPLICATION ) { if( proc->kq ) { struct kevent event; int ret = kevent( proc->kq, 0, 0, &event, 1, 0 ); if( ret != 1 ) log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to wait on process, failed to read event from kqueue (%d)", ret ); close( proc->kq ); proc->kq = 0; } else { log_warnf( 0, WARNING_BAD_DATA, "Unable to wait on a process started with PROCESS_OSX_USE_OPENAPPLICATION and no kqueue" ); return PROCESS_WAIT_FAILED; } proc->pid = 0; proc->code = 0; return proc->code; } # endif cstatus = 0; err = waitpid( proc->pid, &cstatus, ( proc->flags & PROCESS_DETACHED ) ? WNOHANG : 0 ); if( err > 0 ) { if( WIFEXITED( cstatus ) ) proc->code = (int)((char)WEXITSTATUS( cstatus )); else if( WIFSIGNALED( cstatus ) ) { proc->code = PROCESS_TERMINATED_SIGNAL; #ifdef WCOREDUMP //if( WCOREDUMP( cstatus ) ) //... #endif //proc->signal = WTERMSIG( cstatus ); } proc->pid = 0; } else { int cur_errno = errno; if( ( err == 0 ) && ( proc->flags & PROCESS_DETACHED ) ) return PROCESS_STILL_ACTIVE; if( ( err < 0 ) && ( cur_errno == EINTR ) ) return PROCESS_WAIT_INTERRUPTED; log_warnf( 0, WARNING_BAD_DATA, "waitpid(%d) failed: %s (%d) (returned %d)", proc->pid, system_error_message( cur_errno ), cur_errno, err ); return PROCESS_WAIT_FAILED; } #else #error Not implemented #endif return proc->code; }
int process_spawn( process_t* proc ) { static char unescaped[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.:/\\"; int i, num_args; int size; #if FOUNDATION_PLATFORM_WINDOWS wchar_t* wcmdline; wchar_t* wwd; char* cmdline = 0; #endif if( !proc ) return PROCESS_INVALID_ARGS; proc->code = PROCESS_INVALID_ARGS; if( !string_length( proc->path ) ) return proc->code; //Always escape path on Windows platforms #if FOUNDATION_PLATFORM_POSIX if( string_find_first_not_of( proc->path, unescaped, 0 ) != STRING_NPOS ) #endif { if( proc->path[0] != '"' ) proc->path = string_prepend( proc->path, "\"" ); if( proc->path[ string_length( proc->path ) - 1 ] != '"' ) proc->path = string_append( proc->path, "\"" ); } size = array_size( proc->args ); for( i = 0, num_args = 0; i < size; ++i ) { char* arg = proc->args[i]; if( !string_length( arg ) ) continue; ++num_args; if( string_find_first_not_of( arg, unescaped, 0 ) != STRING_NPOS ) { if( arg[0] != '"' ) { //Check if we need to escape " characters unsigned int pos = string_find( arg, '"', 0 ); while( pos != STRING_NPOS ) { if( arg[ pos - 1 ] != '\\' ) { char* escarg = string_substr( arg, 0, pos ); char* left = string_substr( arg, pos, STRING_NPOS ); escarg = string_append( escarg, "\\" ); escarg = string_append( escarg, left ); string_deallocate( left ); string_deallocate( arg ); arg = escarg; } pos = string_find( arg, '"', pos + 2 ); } arg = string_prepend( arg, "\"" ); arg = string_append( arg, "\"" ); proc->args[i] = arg; } } } #if FOUNDATION_PLATFORM_WINDOWS # ifndef SEE_MASK_NOASYNC # define SEE_MASK_NOASYNC 0x00000100 # endif if( !( proc->flags & PROCESS_WINDOWS_USE_SHELLEXECUTE ) ) //Don't prepend exe path to parameters if using ShellExecute cmdline = string_clone( proc->path ); //Build command line string for( i = 0; i < size; ++i ) { char* arg = proc->args[i]; if( !string_length( arg ) ) continue; if( cmdline ) cmdline = string_append( cmdline, " " ); cmdline = string_append( cmdline, arg ); } if( !string_length( proc->wd ) ) proc->wd = string_clone( environment_current_working_directory() ); wcmdline = wstring_allocate_from_string( cmdline, 0 ); wwd = wstring_allocate_from_string( proc->wd, 0 ); if( proc->flags & PROCESS_WINDOWS_USE_SHELLEXECUTE ) { SHELLEXECUTEINFOW sei; wchar_t* wverb; wchar_t* wpath; wverb = ( proc->verb && string_length( proc->verb ) ) ? wstring_allocate_from_string( proc->verb, 0 ) : 0; wpath = wstring_allocate_from_string( proc->path, 0 ); ZeroMemory( &sei, sizeof( sei ) ); sei.cbSize = sizeof(SHELLEXECUTEINFOW); sei.hwnd = 0; sei.fMask = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; sei.lpVerb = wverb; sei.lpFile = wpath; sei.lpParameters = wcmdline; sei.lpDirectory = wwd; sei.nShow = SW_SHOWNORMAL; if( !( proc->flags & PROCESS_CONSOLE ) ) sei.fMask |= SEE_MASK_NO_CONSOLE; if( proc->flags & PROCESS_STDSTREAMS ) log_warnf( 0, WARNING_UNSUPPORTED, "Unable to redirect standard in/out through pipes when using ShellExecute for process spawning" ); log_debugf( 0, "Spawn process (ShellExecute): %s %s", proc->path, cmdline ); if( !ShellExecuteExW( &sei ) ) { log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to spawn process (ShellExecute) for executable '%s': %s", proc->path, system_error_message( GetLastError() ) ); } else { proc->hp = sei.hProcess; proc->ht = 0; proc->code = 0; } wstring_deallocate( wverb ); wstring_deallocate( wpath ); } else { STARTUPINFOW si; PROCESS_INFORMATION pi; BOOL inherit_handles = FALSE; memset( &si, 0, sizeof( si ) ); memset( &pi, 0, sizeof( pi ) ); si.cb = sizeof( si ); if( proc->flags & PROCESS_STDSTREAMS ) { proc->pipeout = pipe_allocate(); proc->pipein = pipe_allocate(); si.dwFlags |= STARTF_USESTDHANDLES; si.hStdOutput = pipe_write_handle( proc->pipeout ); si.hStdError = pipe_write_handle( proc->pipeout ); si.hStdInput = pipe_read_handle( proc->pipein ); //Don't inherit wrong ends of pipes SetHandleInformation( pipe_read_handle( proc->pipeout ), HANDLE_FLAG_INHERIT, 0 ); SetHandleInformation( pipe_write_handle( proc->pipein ), HANDLE_FLAG_INHERIT, 0 ); inherit_handles = TRUE; } log_debugf( 0, "Spawn process (CreateProcess): %s %s", proc->path, cmdline ); if( !CreateProcessW( 0/*wpath*/, wcmdline, 0, 0, inherit_handles, ( proc->flags & PROCESS_CONSOLE ) ? CREATE_NEW_CONSOLE : 0, 0, wwd, &si, &pi ) ) { log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to spawn process (CreateProcess) for executable '%s': %s", proc->path, system_error_message( GetLastError() ) ); stream_deallocate( proc->pipeout ); stream_deallocate( proc->pipein ); proc->pipeout = 0; proc->pipein = 0; } else { proc->hp = pi.hProcess; proc->ht = pi.hThread; proc->code = 0; } if( proc->pipeout ) pipe_close_write( proc->pipeout ); if( proc->pipein ) pipe_close_read( proc->pipein ); } wstring_deallocate( wcmdline ); wstring_deallocate( wwd ); string_deallocate( cmdline ); if( proc->code < 0 ) return proc->code; //Error #endif #if FOUNDATION_PLATFORM_MACOSX if( proc->flags & PROCESS_OSX_USE_OPENAPPLICATION ) { proc->pid = 0; LSApplicationParameters params; ProcessSerialNumber psn; FSRef* fsref = memory_allocate( 0, sizeof( FSRef ), 0, MEMORY_TEMPORARY | MEMORY_ZERO_INITIALIZED ); memset( ¶ms, 0, sizeof( LSApplicationParameters ) ); memset( &psn, 0, sizeof( ProcessSerialNumber ) ); char* pathstripped = string_strip( string_clone( proc->path ), "\"" ); OSStatus status = 0; status = FSPathMakeRef( (uint8_t*)pathstripped, fsref, 0 ); if( status < 0 ) { pathstripped = string_append( pathstripped, ".app" ); status = FSPathMakeRef( (uint8_t*)pathstripped, fsref, 0 ); } CFStringRef* args = 0; for( i = 0, size = array_size( proc->args ); i < size; ++i ) //App gets executable path automatically, don't include array_push( args, CFStringCreateWithCString( 0, proc->args[i], kCFStringEncodingUTF8 ) ); CFArrayRef argvref = CFArrayCreate( 0, (const void**)args, (CFIndex)array_size( args ), 0 ); params.flags = kLSLaunchDefaults; params.application = fsref; params.argv = argvref; log_debugf( 0, "Spawn process (LSOpenApplication): %s", pathstripped ); status = LSOpenApplication( ¶ms, &psn ); if( status != 0 ) { proc->code = status; log_warnf( 0, WARNING_BAD_DATA, "Unable to spawn process for executable '%s': %s", proc->path, system_error_message( status ) ); } CFRelease( argvref ); for( i = 0, size = array_size( args ); i < size; ++i ) CFRelease( args[i] ); memory_deallocate( fsref ); string_deallocate( pathstripped ); if( status == 0 ) { pid_t pid = 0; GetProcessPID( &psn, &pid ); proc->pid = pid; //Always "detached" with LSOpenApplication, not a child process at all //Setup a kqueue to watch when process terminates so we can emulate a wait proc->kq = kqueue(); if( proc->kq < 0 ) { log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to create kqueue for process watch: %s (%d)", proc->kq, system_error_message( proc->kq ) ); proc->kq = 0; } else { struct kevent changes; EV_SET( &changes, (pid_t)pid, EVFILT_PROC, EV_ADD | EV_RECEIPT, NOTE_EXIT, 0, 0 ); int ret = kevent( proc->kq, &changes, 1, &changes, 1, 0 ); if( ret != 1 ) { log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to setup kqueue for process watch, failed to add event to kqueue (%d)", ret ); close( proc->kq ); proc->kq = 0; } } } goto exit; } #endif #if FOUNDATION_PLATFORM_POSIX //Insert executable arg at start and null ptr at end int argc = array_size( proc->args ) + 1; array_grow( proc->args, 2 ); for( int arg = argc - 1; arg > 0; --arg ) proc->args[arg] = proc->args[arg-1]; proc->args[0] = string_clone( proc->path ); proc->args[argc] = 0; if( proc->flags & PROCESS_STDSTREAMS ) { proc->pipeout = pipe_allocate(); proc->pipein = pipe_allocate(); } proc->pid = 0; pid_t pid = fork(); if( pid == 0 ) { //Child if( string_length( proc->wd ) ) { log_debugf( 0, "Spawned child process, setting working directory to %s", proc->wd ); environment_set_current_working_directory( proc->wd ); } log_debugf( 0, "Child process executing: %s", proc->path ); if( proc->flags & PROCESS_STDSTREAMS ) { pipe_close_read( proc->pipeout ); dup2( pipe_write_fd( proc->pipeout ), STDOUT_FILENO ); pipe_close_write( proc->pipein ); dup2( pipe_read_fd( proc->pipein ), STDIN_FILENO ); } int code = execv( proc->path, proc->args ); if( code < 0 ) //Will always be true since this point will never be reached if execve() is successful log_warnf( 0, WARNING_BAD_DATA, "Child process failed execve() : %s : %s", proc->path, system_error_message( errno ) ); //Error process_exit( -1 ); } if( pid > 0 ) { log_debugf( 0, "Child process forked, pid %d", pid ); proc->pid = pid; if( proc->pipeout ) pipe_close_write( proc->pipeout ); if( proc->pipein ) pipe_close_read( proc->pipein ); if( proc->flags & PROCESS_DETACHED ) { int cstatus = 0; pid_t err = waitpid( pid, &cstatus, WNOHANG ); if( err == 0 ) { //TODO: Ugly wait to make sure process spawned correctly thread_sleep( 500 ); err = waitpid( pid, &cstatus, WNOHANG ); } if( err > 0 ) { //Process exited, check code proc->code = (int)((char)WEXITSTATUS( cstatus )); log_debugf( 0, "Child process returned: %d", proc->code ); return proc->code; } } } else { //Error proc->code = errno; log_warnf( 0, WARNING_BAD_DATA, "Unable to spawn process: %s : %s", proc->path, system_error_message( proc->code ) ); if( proc->pipeout ) stream_deallocate( proc->pipeout ); if( proc->pipein ) stream_deallocate( proc->pipein ); proc->pipeout = 0; proc->pipein = 0; return proc->code; } #endif #if !FOUNDATION_PLATFORM_WINDOWS && !FOUNDATION_PLATFORM_POSIX FOUNDATION_ASSERT_FAIL( "Process spawning not supported on platform" ); #endif #if FOUNDATION_PLATFORM_MACOSX exit: #endif if( proc->flags & PROCESS_DETACHED ) return PROCESS_STILL_ACTIVE; return process_wait( proc ); }
size_t network_poll(network_poll_t* pollobj, network_poll_event_t* events, size_t capacity, unsigned int timeoutms) { int avail = 0; size_t num_events = 0; #if FOUNDATION_PLATFORM_WINDOWS //TODO: Refactor to keep fd_set across loop and rebuild on change (add/remove) int num_fd = 0; int ret = 0; size_t islot; fd_set fdread, fdwrite, fderr; #endif if (!pollobj->num_sockets) return num_events; #if FOUNDATION_PLATFORM_APPLE int ret = poll(pollobj->pollfds, pollobj->num_sockets, timeoutms); #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID int ret = epoll_wait(pollobj->fd_poll, pollobj->events, pollobj->num_sockets + 1, timeoutms); int num_polled = ret; #elif FOUNDATION_PLATFORM_WINDOWS FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fderr); for (islot = 0; islot < pollobj->num_sockets; ++islot) { int fd = pollobj->slots[islot].fd; socket_base_t* sockbase = _socket_base + pollobj->slots[islot].base; FD_SET(fd, &fdread); if (sockbase->state == SOCKETSTATE_CONNECTING) FD_SET(fd, &fdwrite); FD_SET(fd, &fderr); if (fd >= num_fd) num_fd = fd + 1; } if (!num_fd) { return num_events; } else { struct timeval tv; tv.tv_sec = timeoutms / 1000; tv.tv_usec = (timeoutms % 1000) * 1000; ret = select(num_fd, &fdread, &fdwrite, &fderr, &tv); } #else # error Not implemented #endif if (ret < 0) { int err = NETWORK_SOCKET_ERROR; string_const_t errmsg = system_error_message(err); log_warnf(HASH_NETWORK, WARNING_SUSPICIOUS, STRING_CONST("Error in socket poll: %.*s (%d)"), STRING_FORMAT(errmsg), err); if (!avail) return num_events; ret = avail; } if (!avail && !ret) return num_events; #if FOUNDATION_PLATFORM_APPLE struct pollfd* pfd = pollobj->pollfds; network_poll_slot_t* slot = pollobj->slots; for (size_t i = 0; i < pollobj->num_sockets; ++i, ++pfd, ++slot) { socket_t* sock = slot->sock; socket_base_t* sockbase = _socket_base + slot->base; int fd = slot->fd; if (pfd->revents & POLLIN) { if (sockbase->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if ((sockbase->state == SOCKETSTATE_CONNECTING) && (pfd->revents & POLLOUT)) { sockbase->state = SOCKETSTATE_CONNECTED; pfd->events = POLLIN | POLLERR | POLLHUP; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } if (pfd->revents & POLLERR) { pfd->events = POLLOUT | POLLERR | POLLHUP; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } if (pfd->revents & POLLHUP) { pfd->events = POLLOUT | POLLERR | POLLHUP; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } } #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID struct epoll_event* event = pollobj->events; for (int i = 0; i < num_polled; ++i, ++event) { FOUNDATION_ASSERT(pollobj->slots[ event->data.fd ].base >= 0); socket_t* sock = pollobj->slots[ event->data.fd ].sock; socket_base_t* sockbase = _socket_base + pollobj->slots[ event->data.fd ].base; int fd = pollobj->slots[ event->data.fd ].fd; if (event->events & EPOLLIN) { if (sockbase->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if ((sockbase->state == SOCKETSTATE_CONNECTING) && (event->events & EPOLLOUT)) { sockbase->state = SOCKETSTATE_CONNECTED; struct epoll_event mod_event; mod_event.events = EPOLLIN | EPOLLERR | EPOLLHUP; mod_event.data.fd = event->data.fd; epoll_ctl(pollobj->fd_poll, EPOLL_CTL_MOD, fd, &mod_event); network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } if (event->events & EPOLLERR) { struct epoll_event del_event; epoll_ctl(pollobj->fd_poll, EPOLL_CTL_DEL, fd, &del_event); network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } if (event->events & EPOLLHUP) { struct epoll_event del_event; epoll_ctl(pollobj->fd_poll, EPOLL_CTL_DEL, fd, &del_event); network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } } #elif FOUNDATION_PLATFORM_WINDOWS for (islot = 0; islot < pollobj->num_sockets; ++islot) { int fd = pollobj->slots[islot].fd; socket_t* sock = pollobj->slots[islot].sock; socket_base_t* sockbase = _socket_base + pollobj->slots[islot].base; if (sockbase->fd != fd) continue; if (FD_ISSET(fd, &fdread)) { if (sockbase->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { //SOCKETSTATE_CONNECTED network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if ((sockbase->state == SOCKETSTATE_CONNECTING) && FD_ISSET(fd, &fdwrite)) { sockbase->state = SOCKETSTATE_CONNECTED; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } if (FD_ISSET(fd, &fderr)) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } } #else # error Not implemented #endif return num_events; }
bool mutex_wait( mutex_t* mutex, unsigned int timeout ) { #if FOUNDATION_PLATFORM_WINDOWS DWORD ret; #elif FOUNDATION_PLATFORM_POSIX struct timeval now; struct timespec then; #endif FOUNDATION_ASSERT( mutex ); #if FOUNDATION_PLATFORM_WINDOWS #if !BUILD_DEPLOY profile_wait( mutex->name ); #endif atomic_incr32( &mutex->waiting ); ret = WaitForSingleObject( mutex->event, ( timeout == 0 ) ? INFINITE : timeout ); if( ret == WAIT_OBJECT_0 ) mutex_lock( mutex ); if( atomic_decr32( &mutex->waiting ) == 0 ) ResetEvent( mutex->event ); return ret == WAIT_OBJECT_0; #elif FOUNDATION_PLATFORM_POSIX mutex_lock( mutex ); if( mutex->pending ) { mutex->pending = false; return true; } --mutex->lockcount; bool was_signal = false; if( !timeout ) { int ret = pthread_cond_wait( &mutex->cond, &mutex->mutex ); if( ret == 0 ) { was_signal = true; } else { log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to wait on mutex '%s': %s (%d)", mutex->name, system_error_message( ret ), ret ); } } else { int ret; gettimeofday( &now, 0 ); then.tv_sec = now.tv_sec + ( timeout / 1000 ); then.tv_nsec = ( now.tv_usec * 1000 ) + (long)( timeout % 1000 ) * 1000000L; while( then.tv_nsec > 999999999 ) { ++then.tv_sec; then.tv_nsec -= 1000000000L; } ret = pthread_cond_timedwait( &mutex->cond, &mutex->mutex, &then ); if( ret == 0 ) { was_signal = true; } else if( ret != ETIMEDOUT ) { log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to wait (timed) on mutex '%s': %s (%d)", mutex->name, system_error_message( ret ), ret ); } } ++mutex->lockcount; mutex->lockedthread = thread_id(); if( was_signal ) mutex->pending = false; else mutex_unlock( mutex ); return was_signal; #else # error mutex_wait not implemented #endif }
int main_run(void* main_arg) { #if !BUILD_MONOLITHIC string_const_t pattern; string_t* exe_paths = 0; size_t iexe, exesize; process_t* process = 0; string_t process_path = { 0, 0 }; unsigned int* exe_flags = 0; #else void* test_result; #endif #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID || FOUNDATION_PLATFORM_PNACL int remain_counter = 0; #endif #if BUILD_DEBUG const string_const_t build_name = string_const(STRING_CONST("debug")); #elif BUILD_RELEASE const string_const_t build_name = string_const(STRING_CONST("release")); #elif BUILD_PROFILE const string_const_t build_name = string_const(STRING_CONST("profile")); #elif BUILD_DEPLOY const string_const_t build_name = string_const(STRING_CONST("deploy")); #endif #if BUILD_MONOLITHIC const string_const_t build_type = string_const(STRING_CONST(" monolithic")); #else const string_const_t build_type = string_empty(); #endif char* pathbuf; int process_result = 0; thread_t event_thread; FOUNDATION_UNUSED(main_arg); FOUNDATION_UNUSED(build_name); log_set_suppress(HASH_TEST, ERRORLEVEL_DEBUG); log_infof(HASH_TEST, STRING_CONST("Task library v%s built for %s using %s (%.*s%.*s)"), string_from_version_static(task_module_version()).str, FOUNDATION_PLATFORM_DESCRIPTION, FOUNDATION_COMPILER_DESCRIPTION, STRING_FORMAT(build_name), STRING_FORMAT(build_type)); thread_initialize(&event_thread, event_loop, 0, STRING_CONST("event_thread"), THREAD_PRIORITY_NORMAL, 0); thread_start(&event_thread); pathbuf = memory_allocate(HASH_STRING, BUILD_MAX_PATHLEN, 0, MEMORY_PERSISTENT); while (!thread_is_running(&event_thread)) thread_sleep(10); #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID || FOUNDATION_PLATFORM_PNACL while (!_test_should_start) { #if FOUNDATION_PLATFORM_ANDROID system_process_events(); #endif thread_sleep(100); } #endif fs_remove_directory(STRING_ARGS(environment_temporary_directory())); #if BUILD_MONOLITHIC test_run_fn tests[] = { test_task_run, 0 }; #if FOUNDATION_PLATFORM_ANDROID thread_t test_thread; thread_initialize(&test_thread, test_runner, tests, STRING_CONST("test_runner"), THREAD_PRIORITY_NORMAL, 0); thread_start(&test_thread); log_debug(HASH_TEST, STRING_CONST("Starting test runner thread")); while (!thread_is_running(&test_thread)) { system_process_events(); thread_sleep(10); } while (thread_is_running(&test_thread)) { system_process_events(); thread_sleep(10); } test_result = thread_join(&test_thread); process_result = (int)(intptr_t)test_result; thread_finalize(&test_thread); #else test_result = test_runner(tests); process_result = (int)(intptr_t)test_result; #endif if (process_result != 0) log_warnf(HASH_TEST, WARNING_SUSPICIOUS, STRING_CONST("Tests failed with exit code %d"), process_result); #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID || FOUNDATION_PLATFORM_PNACL while (!_test_should_terminate && _test_have_focus && (remain_counter < 50)) { system_process_events(); thread_sleep(100); ++remain_counter; } #endif log_debug(HASH_TEST, STRING_CONST("Exiting main loop")); #else // !BUILD_MONOLITHIC //Find all test executables in the current executable directory #if FOUNDATION_PLATFORM_WINDOWS pattern = string_const(STRING_CONST("^test-.*\\.exe$")); #elif FOUNDATION_PLATFORM_MACOSX pattern = string_const(STRING_CONST("^test-.*$")); #elif FOUNDATION_PLATFORM_POSIX pattern = string_const(STRING_CONST("^test-.*$")); #else # error Not implemented #endif exe_paths = fs_matching_files(STRING_ARGS(environment_executable_directory()), STRING_ARGS(pattern), false); array_resize(exe_flags, array_size(exe_paths)); memset(exe_flags, 0, sizeof(unsigned int) * array_size(exe_flags)); #if FOUNDATION_PLATFORM_MACOSX //Also search for test applications string_const_t app_pattern = string_const(STRING_CONST("^test-.*\\.app$")); regex_t* app_regex = regex_compile(app_pattern.str, app_pattern.length); string_t* subdirs = fs_subdirs(STRING_ARGS(environment_executable_directory())); for (size_t idir = 0, dirsize = array_size(subdirs); idir < dirsize; ++idir) { if (regex_match(app_regex, subdirs[idir].str, subdirs[idir].length, 0, 0)) { string_t exe_path = { subdirs[idir].str, subdirs[idir].length - 4 }; array_push(exe_paths, exe_path); array_push(exe_flags, PROCESS_MACOSX_USE_OPENAPPLICATION); } } string_array_deallocate(subdirs); regex_deallocate(app_regex); #endif for (iexe = 0, exesize = array_size(exe_paths); iexe < exesize; ++iexe) { string_const_t* process_args = 0; string_const_t exe_file_name = path_base_file_name(STRING_ARGS(exe_paths[iexe])); if (string_equal(STRING_ARGS(exe_file_name), STRING_ARGS(environment_executable_name()))) continue; //Don't run self process_path = path_concat(pathbuf, BUILD_MAX_PATHLEN, STRING_ARGS(environment_executable_directory()), STRING_ARGS(exe_paths[iexe])); process = process_allocate(); process_set_executable_path(process, STRING_ARGS(process_path)); process_set_working_directory(process, STRING_ARGS(environment_executable_directory())); process_set_flags(process, PROCESS_ATTACHED | exe_flags[iexe]); if (!_test_memory_tracker) array_push(process_args, string_const(STRING_CONST("--no-memory-tracker"))); process_set_arguments(process, process_args, array_size(process_args)); log_infof(HASH_TEST, STRING_CONST("Running test executable: %.*s"), STRING_FORMAT(exe_paths[iexe])); process_result = process_spawn(process); while (process_result == PROCESS_WAIT_INTERRUPTED) { thread_sleep(10); process_result = process_wait(process); } process_deallocate(process); array_deallocate(process_args); if (process_result != 0) { if (process_result >= PROCESS_INVALID_ARGS) log_warnf(HASH_TEST, WARNING_SUSPICIOUS, STRING_CONST("Tests failed, process terminated with error %x"), process_result); else log_warnf(HASH_TEST, WARNING_SUSPICIOUS, STRING_CONST("Tests failed with exit code %d"), process_result); process_set_exit_code(-1); goto exit; } log_infof(HASH_TEST, STRING_CONST("All tests from %.*s passed (%d)"), STRING_FORMAT(exe_paths[iexe]), process_result); } log_info(HASH_TEST, STRING_CONST("All tests passed")); exit: if (exe_paths) string_array_deallocate(exe_paths); array_deallocate(exe_flags); #endif _test_should_terminate = true; thread_signal(&event_thread); thread_finalize(&event_thread); memory_deallocate(pathbuf); log_infof(HASH_TEST, STRING_CONST("Tests exiting: %s (%d)"), process_result ? "FAILED" : "PASSED", process_result); if (process_result) memory_set_tracker(memory_tracker_none()); return process_result; }
static int render_import_program(stream_t* stream, const uuid_t uuid) { char buffer[1024]; char pathbuf[BUILD_MAX_PATHLEN]; resource_source_t source; resource_platform_t platformdecl = {-1, -1, -1, -1, -1, -1}; uint64_t platform; tick_t timestamp; int ret = 0; resource_source_initialize(&source); resource_source_read(&source, uuid); platform = resource_platform(platformdecl); timestamp = time_system(); while (!stream_eos(stream)) { string_const_t type, ref; string_const_t fullpath; string_const_t uuidstr; uuid_t shaderuuid; hash_t typehash; string_t line = stream_read_line_buffer(stream, buffer, sizeof(buffer), '\n'); string_split(STRING_ARGS(line), STRING_CONST(" \t"), &type, &ref, false); type = string_strip(STRING_ARGS(type), STRING_CONST(STRING_WHITESPACE)); ref = string_strip(STRING_ARGS(ref), STRING_CONST(STRING_WHITESPACE)); if (!type.length || !ref.length) continue; typehash = hash(STRING_ARGS(type)); if ((typehash != HASH_VERTEXSHADER) && (typehash != HASH_PIXELSHADER)) { log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Ignore invalid line: %.*s"), STRING_FORMAT(line)); continue; } shaderuuid = string_to_uuid(STRING_ARGS(ref)); if (uuid_is_null(shaderuuid)) { if (path_is_absolute(STRING_ARGS(ref))) { fullpath = ref; } else { string_t full; string_const_t path = stream_path(stream); path = path_directory_name(STRING_ARGS(path)); full = path_concat(pathbuf, sizeof(pathbuf), STRING_ARGS(path), STRING_ARGS(ref)); full = path_absolute(STRING_ARGS(full), sizeof(pathbuf)); fullpath = string_const(STRING_ARGS(full)); } resource_signature_t sig = resource_import_lookup(STRING_ARGS(fullpath)); if (uuid_is_null(sig.uuid)) { if (!resource_import(STRING_ARGS(fullpath), uuid_null())) { log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Unable to import linked shader: %.*s"), STRING_FORMAT(fullpath)); ret = -1; goto finalize; } sig = resource_import_lookup(STRING_ARGS(fullpath)); if (uuid_is_null(sig.uuid)) { log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Import linked shader gave no UUID: %.*s"), STRING_FORMAT(fullpath)); ret = -1; goto finalize; } } shaderuuid = sig.uuid; } if (!uuid_is_null(shaderuuid)) { uuidstr = string_from_uuid_static(shaderuuid); resource_source_set(&source, timestamp, typehash, platform, STRING_ARGS(uuidstr)); } } resource_source_set(&source, timestamp, HASH_RESOURCE_TYPE, 0, STRING_CONST("program")); if (!resource_source_write(&source, uuid, false)) { string_const_t uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Failed writing imported program: %.*s"), STRING_FORMAT(uuidstr)); ret = -1; goto finalize; } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_infof(HASH_RESOURCE, STRING_CONST("Wrote imported program: %.*s"), STRING_FORMAT(uuidstr)); } finalize: resource_source_finalize(&source); return ret; }
static int render_import_shader(stream_t* stream, const uuid_t uuid) { char buffer[1024]; char pathbuf[BUILD_MAX_PATHLEN]; resource_source_t source; resource_platform_t platformdecl = {-1, -1, -1, -1, -1, -1}; uint64_t platform; tick_t timestamp; int ret = 0; resource_source_initialize(&source); resource_source_read(&source, uuid); platform = resource_platform(platformdecl); timestamp = time_system(); while (!stream_eos(stream)) { uuid_t shaderuuid; string_const_t target, ref, fullpath; string_t line = stream_read_line_buffer(stream, buffer, sizeof(buffer), '\n'); string_split(STRING_ARGS(line), STRING_CONST(" \t"), &target, &ref, false); ref = string_strip(STRING_ARGS(ref), STRING_CONST(STRING_WHITESPACE)); shaderuuid = string_to_uuid(STRING_ARGS(ref)); if (uuid_is_null(shaderuuid)) { if (path_is_absolute(STRING_ARGS(ref))) { fullpath = ref; } else { string_t full; string_const_t path = stream_path(stream); path = path_directory_name(STRING_ARGS(path)); full = path_concat(pathbuf, sizeof(pathbuf), STRING_ARGS(path), STRING_ARGS(ref)); full = path_absolute(STRING_ARGS(full), sizeof(pathbuf)); fullpath = string_const(STRING_ARGS(full)); } resource_signature_t sig = resource_import_lookup(STRING_ARGS(fullpath)); if (uuid_is_null(sig.uuid)) { if (!resource_import(STRING_ARGS(fullpath), uuid_null())) { log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Unable to import linked shader: %.*s"), STRING_FORMAT(fullpath)); ret = -1; goto finalize; } sig = resource_import_lookup(STRING_ARGS(fullpath)); if (uuid_is_null(sig.uuid)) { log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Import linked shader gave no UUID: %.*s"), STRING_FORMAT(fullpath)); ret = -1; goto finalize; } } shaderuuid = sig.uuid; } if (!uuid_is_null(shaderuuid)) { resource_platform_t targetplatformdecl = render_import_parse_target(STRING_ARGS(target), platformdecl); uint64_t targetplatform = resource_platform(targetplatformdecl); if (resource_autoimport_need_update(shaderuuid, targetplatform)) resource_autoimport(shaderuuid); const string_const_t uuidstr = string_from_uuid_static(shaderuuid); resource_source_set(&source, timestamp, HASH_SHADER, targetplatform, STRING_ARGS(uuidstr)); resource_source_set_dependencies(uuid, targetplatform, &shaderuuid, 1); } } resource_source_set(&source, timestamp, HASH_RESOURCE_TYPE, 0, STRING_CONST("shader")); if (!resource_source_write(&source, uuid, false)) { string_const_t uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Failed writing imported shader: %.*s"), STRING_FORMAT(uuidstr)); ret = -1; goto finalize; } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_infof(HASH_RESOURCE, STRING_CONST("Wrote imported shader: %.*s"), STRING_FORMAT(uuidstr)); } finalize: resource_source_finalize(&source); return 0; }
int process_spawn(process_t* proc) { static const string_const_t unescaped = { STRING_CONST("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.:/\\") }; size_t i, num_args; size_t size; #if FOUNDATION_PLATFORM_WINDOWS wchar_t* wcmdline; wchar_t* wwd; string_t cmdline; #endif #if !FOUNDATION_PLATFORM_POSIX size_t capacity; #endif proc->code = PROCESS_INVALID_ARGS; if (!proc->path.length) return proc->code; //Always escape path on Windows platforms #if FOUNDATION_PLATFORM_POSIX if (string_find_first_not_of(STRING_ARGS(proc->path), STRING_ARGS(unescaped), 0) != STRING_NPOS) #endif { bool preesc = (proc->path.str[0] != '"'); bool postesc = (proc->path.str[ proc->path.length - 1 ] != '"'); if (preesc || postesc) { char* buffer = memory_allocate(HASH_STRING, proc->path.length + 4, 0, MEMORY_PERSISTENT); string_t pathesc = string_concat_varg(buffer, proc->path.length + 4, "\"", (size_t)(preesc ? 1 : 0), STRING_ARGS(proc->path), "\"", (size_t)(postesc ? 1 : 0), nullptr); string_deallocate(proc->path.str); proc->path = pathesc; } } size = array_size(proc->args); for (i = 0, num_args = 0; i < size; ++i) { string_t arg = proc->args[i]; if (!arg.length) continue; ++num_args; #if !FOUNDATION_PLATFORM_POSIX if (string_find_first_not_of(arg.str, arg.length, unescaped.str, unescaped.length, 0) != STRING_NPOS) { if (arg.str[0] != '"') { //Check if we need to escape " characters string_t escarg; size_t pos = string_find(arg.str, arg.length, '"', 0); while (pos != STRING_NPOS) { if (arg.str[ pos - 1 ] != '\\') { string_const_t right = string_substr(STRING_ARGS(arg), 0, pos); string_const_t left = string_substr(STRING_ARGS(arg), pos, STRING_NPOS); capacity = arg.length + 2; escarg = string_allocate(0, capacity); escarg = string_concat(escarg.str, capacity, STRING_ARGS(right), STRING_CONST("\\")); escarg = string_append(STRING_ARGS(escarg), capacity, STRING_ARGS(left)); string_deallocate(arg.str); arg = escarg; } pos = string_find(STRING_ARGS(arg), '"', pos + 2); } escarg = string_allocate_concat_varg(STRING_CONST("\""), STRING_ARGS(arg), STRING_CONST("\""), nullptr); string_deallocate(arg.str); proc->args[i] = escarg; } } #endif } #if FOUNDATION_PLATFORM_WINDOWS # ifndef SEE_MASK_NOASYNC # define SEE_MASK_NOASYNC 0x00000100 # endif capacity = BUILD_MAX_PATHLEN; cmdline = string_allocate(0, capacity); //Don't prepend exe path to parameters if using ShellExecute if (!(proc->flags & PROCESS_WINDOWS_USE_SHELLEXECUTE)) cmdline = string_copy(cmdline.str, capacity, STRING_ARGS(proc->path)); //Build command line string for (i = 0; i < size; ++i) { string_t arg = proc->args[i]; if (!arg.length) continue; if (cmdline.length + arg.length + 2 >= capacity) { string_t newline; capacity *= 2; newline = string_allocate(0, capacity); newline = string_copy(newline.str, capacity, STRING_ARGS(cmdline)); } if (cmdline.length) cmdline = string_append(STRING_ARGS(cmdline), capacity, STRING_CONST(" ")); cmdline = string_append(STRING_ARGS(cmdline), capacity, STRING_ARGS(arg)); } if (!proc->wd.length) proc->wd = string_clone(STRING_ARGS(environment_current_working_directory())); wcmdline = wstring_allocate_from_string(STRING_ARGS(cmdline)); wwd = wstring_allocate_from_string(STRING_ARGS(proc->wd)); if (proc->flags & PROCESS_WINDOWS_USE_SHELLEXECUTE) { SHELLEXECUTEINFOW sei; wchar_t* wverb; wchar_t* wpath; wverb = (proc->verb.length ? wstring_allocate_from_string(STRING_ARGS(proc->verb)) : 0); wpath = wstring_allocate_from_string(STRING_ARGS(proc->path)); ZeroMemory(&sei, sizeof(sei)); sei.cbSize = sizeof(SHELLEXECUTEINFOW); sei.hwnd = 0; sei.fMask = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; sei.lpVerb = wverb; sei.lpFile = wpath; sei.lpParameters = wcmdline; sei.lpDirectory = wwd; sei.nShow = SW_SHOWNORMAL; if (!(proc->flags & PROCESS_CONSOLE)) sei.fMask |= SEE_MASK_NO_CONSOLE; if (proc->flags & PROCESS_STDSTREAMS) log_warn(0, WARNING_UNSUPPORTED, STRING_CONST("Unable to redirect standard in/out" " through pipes when using ShellExecute for process spawning")); log_debugf(0, STRING_CONST("Spawn process (ShellExecute): %.*s %.*s"), STRING_FORMAT(proc->path), STRING_FORMAT(cmdline)); if (!ShellExecuteExW(&sei)) { string_const_t errstr = system_error_message(0); log_warnf(0, WARNING_SYSTEM_CALL_FAIL, STRING_CONST("Unable to spawn process (ShellExecute) for executable '%.*s': %s"), STRING_FORMAT(proc->path), STRING_FORMAT(errstr)); } else { proc->hp = sei.hProcess; proc->ht = 0; proc->code = 0; } wstring_deallocate(wverb); wstring_deallocate(wpath); } else { STARTUPINFOW si; PROCESS_INFORMATION pi; BOOL inherit_handles = FALSE; memset(&si, 0, sizeof(si)); memset(&pi, 0, sizeof(pi)); si.cb = sizeof(si); if (proc->flags & PROCESS_STDSTREAMS) { proc->pipeout = pipe_allocate(); proc->pipein = pipe_allocate(); si.dwFlags |= STARTF_USESTDHANDLES; si.hStdOutput = pipe_write_handle(proc->pipeout); si.hStdInput = pipe_read_handle(proc->pipein); si.hStdError = GetStdHandle(STD_ERROR_HANDLE); //Don't inherit wrong ends of pipes SetHandleInformation(pipe_read_handle(proc->pipeout), HANDLE_FLAG_INHERIT, 0); SetHandleInformation(pipe_write_handle(proc->pipein), HANDLE_FLAG_INHERIT, 0); inherit_handles = TRUE; } log_debugf(0, STRING_CONST("Spawn process (CreateProcess): %.*s %.*s"), STRING_FORMAT(proc->path), STRING_FORMAT(cmdline)); if (!CreateProcessW(0, wcmdline, 0, 0, inherit_handles, (proc->flags & PROCESS_CONSOLE) ? CREATE_NEW_CONSOLE : 0, 0, wwd, &si, &pi)) { string_const_t errstr = system_error_message(0); log_warnf(0, WARNING_SYSTEM_CALL_FAIL, STRING_CONST("Unable to spawn process (CreateProcess) for executable '%.*s': %.*s"), STRING_FORMAT(proc->path), STRING_FORMAT(errstr)); stream_deallocate(proc->pipeout); stream_deallocate(proc->pipein); proc->pipeout = 0; proc->pipein = 0; } else { proc->hp = pi.hProcess; proc->ht = pi.hThread; proc->code = 0; } if (proc->pipeout) pipe_close_write(proc->pipeout); if (proc->pipein) pipe_close_read(proc->pipein); } wstring_deallocate(wcmdline); wstring_deallocate(wwd); string_deallocate(cmdline.str); if (proc->code < 0) return proc->code; //Error #endif #if FOUNDATION_PLATFORM_MACOSX if (proc->flags & PROCESS_MACOSX_USE_OPENAPPLICATION) { proc->pid = 0; LSApplicationParameters params; ProcessSerialNumber psn; FSRef* fsref = memory_allocate(0, sizeof(FSRef), 0, MEMORY_TEMPORARY | MEMORY_ZERO_INITIALIZED); memset(¶ms, 0, sizeof(LSApplicationParameters)); memset(&psn, 0, sizeof(ProcessSerialNumber)); string_const_t pathstripped = string_strip(proc->path.str, proc->path.length, STRING_CONST("\"")); size_t localcap = pathstripped.length + 5; string_t localpath = string_allocate(0, localcap - 1); localpath = string_copy(localpath.str, localcap, STRING_ARGS(pathstripped)); //Need it zero terminated OSStatus status = 0; status = FSPathMakeRef((uint8_t*)localpath.str, fsref, 0); if (status < 0) { localpath = string_append(localpath.str, localpath.length, localcap, STRING_CONST(".app")); status = FSPathMakeRef((uint8_t*)localpath.str, fsref, 0); } CFStringRef* args = 0; for (i = 0, size = array_size(proc->args); i < size; ++i) //App gets executable path automatically, don't include array_push(args, CFStringCreateWithCString(0, proc->args[i].str, kCFStringEncodingUTF8)); CFArrayRef argvref = CFArrayCreate(0, (const void**)args, (CFIndex)array_size(args), 0); params.flags = kLSLaunchDefaults; params.application = fsref; params.argv = argvref; log_debugf(0, STRING_CONST("Spawn process (LSOpenApplication): %.*s"), STRING_FORMAT(localpath)); status = LSOpenApplication(¶ms, &psn); if (status != 0) { int err = status; string_const_t errmsg = system_error_message(err); proc->code = status; log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to spawn process for executable '%.*s': %.*s (%d)"), STRING_FORMAT(localpath), STRING_FORMAT(errmsg), err); } CFRelease(argvref); for (i = 0, size = array_size(args); i < size; ++i) CFRelease(args[i]); array_deallocate(args); memory_deallocate(fsref); string_deallocate(localpath.str); if (status == 0) { pid_t pid = 0; GetProcessPID(&psn, &pid); proc->pid = pid; //Always "detached" with LSOpenApplication, not a child process at all //Setup a kqueue to watch when process terminates so we can emulate a wait proc->kq = kqueue(); if (proc->kq < 0) { string_const_t errmsg = system_error_message(proc->kq); log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to create kqueue for process watch: %.*s (%d)"), STRING_FORMAT(errmsg), proc->kq); proc->kq = 0; } else { struct kevent changes; EV_SET(&changes, (pid_t)pid, EVFILT_PROC, EV_ADD | EV_RECEIPT, NOTE_EXIT, 0, 0); int ret = kevent(proc->kq, &changes, 1, &changes, 1, 0); if (ret != 1) { int err = errno; string_const_t errmsg = system_error_message(err); log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to setup kqueue for process watch, failed to add event to kqueue: %.*s (%d)"), STRING_FORMAT(errmsg), err); close(proc->kq); proc->kq = 0; } } } goto exit; } #endif #if FOUNDATION_PLATFORM_POSIX //Insert executable arg at start and null ptr at end size_t arg; size_t argc = array_size(proc->args) + 1; array_grow(proc->args, 2); for (arg = argc - 1; arg > 0; --arg) proc->args[arg] = proc->args[arg - 1]; proc->args[0] = string_clone(STRING_ARGS(proc->path)); proc->args[argc] = (string_t) { 0, 0 }; char** argv = memory_allocate(0, sizeof(char*) * (argc + 1), 0, MEMORY_PERSISTENT); for (arg = 0; arg < argc; ++arg) argv[arg] = proc->args[arg].str; argv[argc] = 0; if (proc->flags & PROCESS_STDSTREAMS) { proc->pipeout = pipe_allocate(); proc->pipein = pipe_allocate(); } proc->pid = 0; pid_t pid = fork(); if (pid == 0) { //Child if (proc->wd.length) { log_debugf(0, STRING_CONST("Spawned child process, setting working directory to %.*s"), STRING_FORMAT(proc->wd)); environment_set_current_working_directory(STRING_ARGS(proc->wd)); } log_debugf(0, STRING_CONST("Child process executing: %.*s"), STRING_FORMAT(proc->path)); if (proc->flags & PROCESS_STDSTREAMS) { pipe_close_read(proc->pipeout); dup2(pipe_write_handle(proc->pipeout), STDOUT_FILENO); pipe_close_write(proc->pipein); dup2(pipe_read_handle(proc->pipein), STDIN_FILENO); } int code = execv(proc->path.str, (char* const*)argv); //Error int err = errno; string_const_t errmsg = system_error_message(err); log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Child process failed execve() '%.*s': %.*s (%d) (%d)"), STRING_FORMAT(proc->path), STRING_FORMAT(errmsg), err, code); process_exit(PROCESS_EXIT_FAILURE); FOUNDATION_UNUSED(code); } memory_deallocate(argv); if (pid > 0) { log_debugf(0, STRING_CONST("Child process forked, pid %d"), pid); proc->pid = pid; if (proc->pipeout) pipe_close_write(proc->pipeout); if (proc->pipein) pipe_close_read(proc->pipein); /*if (proc->flags & PROCESS_DETACHED) { int cstatus = 0; pid_t err = waitpid(pid, &cstatus, WNOHANG); if (err == 0) { //TODO: Ugly wait to make sure process spawned correctly thread_sleep(500); err = waitpid(pid, &cstatus, WNOHANG); } if (err > 0) { //Process exited, check code proc->pid = 0; proc->code = (int)((char)WEXITSTATUS(cstatus)); log_debugf(0, STRING_CONST("Child process returned: %d"), proc->code); return proc->code; } }*/ } else { //Error string_const_t errmsg; errmsg = system_error_message(proc->code); log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to spawn process '%.*s': %.*s (%d)"), STRING_FORMAT(proc->path), STRING_FORMAT(errmsg), proc->code); if (proc->pipeout) stream_deallocate(proc->pipeout); if (proc->pipein) stream_deallocate(proc->pipein); proc->pipeout = 0; proc->pipein = 0; proc->code = PROCESS_INVALID_ARGS; return proc->code; } #endif #if !FOUNDATION_PLATFORM_WINDOWS && !FOUNDATION_PLATFORM_POSIX FOUNDATION_ASSERT_FAIL("Process spawning not supported on platform"); #endif #if FOUNDATION_PLATFORM_MACOSX exit: #endif if (proc->flags & PROCESS_DETACHED) return PROCESS_STILL_ACTIVE; return process_wait(proc); }
static int render_import_glsl_shader(stream_t* stream, const uuid_t uuid, const char* type, size_t type_length) { resource_source_t source; void* blob = 0; size_t size; size_t read; size_t begin; size_t next; hash_t checksum; tick_t timestamp; uint64_t platform; size_t maxtokens; size_t parameter; string_const_t* token = 0; string_const_t valstr; char buffer[128]; int ret = 0; resource_platform_t platformdecl = { -1, -1, RENDERAPIGROUP_OPENGL, -1, -1, -1}; resource_source_initialize(&source); resource_source_read(&source, uuid); read = 0; size = stream_size(stream); blob = memory_allocate(HASH_RESOURCE, size, 0, MEMORY_PERSISTENT); size = stream_read(stream, blob, size); platform = resource_platform(platformdecl); timestamp = time_system(); checksum = hash(blob, size); if (resource_source_write_blob(uuid, timestamp, HASH_SOURCE, platform, checksum, blob, size)) { resource_source_set_blob(&source, timestamp, HASH_SOURCE, platform, checksum, size); } else { ret = -1; goto finalize; } //Parse source and set parameters maxtokens = 256; token = memory_allocate(HASH_RESOURCE, sizeof(string_const_t) * maxtokens, 0, MEMORY_PERSISTENT); begin = 0; parameter = 0; do { string_const_t tokens[64], line; size_t itok, ntokens; next = string_find_first_of(blob, size, STRING_CONST("\n\r"), begin); line = string_substr(blob, size, begin, next - begin); ntokens = string_explode(STRING_ARGS(line), STRING_CONST(GLSL_TOKEN_DELIM), tokens, maxtokens, false); for (itok = 0; itok < ntokens; ++itok) { if ((string_equal(STRING_ARGS(tokens[itok]), STRING_CONST("attribute")) || string_equal(STRING_ARGS(tokens[itok]), STRING_CONST("uniform"))) && (itok + 2 < ntokens)) { char typebuf[16], dimbuf[16]; string_const_t typestr = tokens[itok + 1]; string_const_t namestr = tokens[itok + 2]; string_const_t dimstr; int parameter_type = glsl_type_to_parameter_type(typestr); if (parameter_type < 0) continue; int parameter_dim = glsl_dim_from_token(typestr); if (parameter_dim == 1) parameter_dim = glsl_dim_from_token(namestr); namestr = glsl_name_from_token(namestr); typestr = string_to_const(string_from_uint(typebuf, sizeof(typebuf), (unsigned int)parameter_type, false, 0, 0)); dimstr = string_to_const(string_from_uint(dimbuf, sizeof(dimbuf), (unsigned int)parameter_dim, false, 0, 0)); log_debugf(HASH_RESOURCE, STRING_CONST("parameter: %.*s type %.*s dim %.*s"), STRING_FORMAT(namestr), STRING_FORMAT(typestr), STRING_FORMAT(dimstr)); string_t param = string_format(buffer, sizeof(buffer), STRING_CONST("parameter_type_%" PRIsize), parameter); resource_source_set(&source, timestamp, hash(STRING_ARGS(param)), platform, STRING_ARGS(typestr)); param = string_format(buffer, sizeof(buffer), STRING_CONST("parameter_name_%" PRIsize), parameter); resource_source_set(&source, timestamp, hash(STRING_ARGS(param)), platform, STRING_ARGS(namestr)); param = string_format(buffer, sizeof(buffer), STRING_CONST("parameter_dim_%" PRIsize), parameter); resource_source_set(&source, timestamp, hash(STRING_ARGS(param)), platform, STRING_ARGS(dimstr)); ++parameter; } } begin = string_find_first_not_of(blob, size, STRING_CONST(STRING_WHITESPACE), next); } while (next != STRING_NPOS); valstr = string_from_uint_static(parameter, false, 0, 0); resource_source_set(&source, timestamp, HASH_PARAMETER_COUNT, platform, STRING_ARGS(valstr)); resource_source_set(&source, timestamp, HASH_RESOURCE_TYPE, 0, type, type_length); if (!resource_source_write(&source, uuid, false)) { string_const_t uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Failed writing imported GLSL shader: %.*s"), STRING_FORMAT(uuidstr)); ret = -1; goto finalize; } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_infof(HASH_RESOURCE, STRING_CONST("Wrote imported GLSL shader: %.*s"), STRING_FORMAT(uuidstr)); } finalize: memory_deallocate(blob); memory_deallocate(token); resource_source_finalize(&source); return ret; }
int process_wait(process_t* proc) { #if FOUNDATION_PLATFORM_POSIX int cstatus; pid_t ret; #endif #if FOUNDATION_PLATFORM_WINDOWS if (!proc->hp) return proc->code; while (GetExitCodeProcess(proc->hp, (LPDWORD)&proc->code)) { if ((proc->code != (int)STILL_ACTIVE) || (proc->flags & PROCESS_DETACHED)) break; thread_sleep(50); proc->code = -1; } if ((proc->code == (int)STILL_ACTIVE) && (proc->flags & PROCESS_DETACHED)) return PROCESS_STILL_ACTIVE; if (proc->ht) CloseHandle(proc->ht); if (proc->hp) CloseHandle(proc->hp); proc->hp = 0; proc->ht = 0; #elif FOUNDATION_PLATFORM_POSIX if (!proc->pid) return proc->code; # if FOUNDATION_PLATFORM_MACOSX if (proc->flags & PROCESS_MACOSX_USE_OPENAPPLICATION) { if (proc->kq) { struct kevent event; ret = kevent(proc->kq, 0, 0, &event, 1, 0); if (ret != 1) { int err = errno; string_const_t errmsg = system_error_message(err); log_warnf(0, WARNING_SYSTEM_CALL_FAIL, STRING_CONST("Unable to wait on process, failed to read event from kqueue: %.*s (%d)"), STRING_FORMAT(errmsg), err); } close(proc->kq); proc->kq = 0; } else { log_warn(0, WARNING_INVALID_VALUE, STRING_CONST("Unable to wait on a process started with PROCESS_MACOSX_USE_OPENAPPLICATION and no kqueue")); return PROCESS_WAIT_FAILED; } proc->pid = 0; proc->code = 0; return proc->code; } # endif cstatus = 0; ret = waitpid(proc->pid, &cstatus, (proc->flags & PROCESS_DETACHED) ? WNOHANG : 0); if (ret > 0) { if (WIFEXITED(cstatus)) proc->code = (int)((char)WEXITSTATUS(cstatus)); else if (WIFSIGNALED(cstatus)) { proc->code = PROCESS_TERMINATED_SIGNAL; #ifdef WCOREDUMP //if( WCOREDUMP( cstatus ) ) //... #endif //proc->signal = WTERMSIG( cstatus ); } else { proc->code = PROCESS_WAIT_FAILED; } proc->pid = 0; } else { int err = errno; if ((ret == 0) && (proc->flags & PROCESS_DETACHED)) return PROCESS_STILL_ACTIVE; if ((ret < 0) && (err == EINTR)) return PROCESS_WAIT_INTERRUPTED; string_const_t errmsg = system_error_message(err); log_warnf(0, WARNING_INVALID_VALUE, STRING_CONST("waitpid(%d) failed: %.*s (%d) (returned %d)"), proc->pid, STRING_FORMAT(errmsg), err, ret); return PROCESS_WAIT_FAILED; } #elif FOUNDATION_PLATFORM_PNACL //Not supported #else #error Not implemented #endif return proc->code; }