void _config_shutdown( void ) { int isb, is, ikb, ik, ssize, ksize; config_section_t* section; config_key_t* key; for( isb = 0; isb < CONFIG_SECTION_BUCKETS; ++isb ) { section = _config_section[isb]; for( is = 0, ssize = array_size( section ); is < ssize; ++is ) { for( ikb = 0; ikb < CONFIG_KEY_BUCKETS; ++ikb ) { /*lint -e{613} array_size( section ) in loop condition does the null pointer guard */ key = section[is].key[ikb]; for( ik = 0, ksize = array_size( key ); ik < ksize; ++ik ) { /*lint --e{613} array_size( key ) in loop condition does the null pointer guard */ if( key[ik].expanded != key[ik].sval ) string_deallocate( key[ik].expanded ); if( ( key[ik].type != CONFIGVALUE_STRING_CONST ) && ( key[ik].type != CONFIGVALUE_STRING_CONST_VAR ) ) string_deallocate( key[ik].sval ); } array_deallocate( key ); } } array_deallocate( section ); } }
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; }
static void blast_client_deallocate(blast_client_t* client) { int isock, ssize = 0; if (client->sock) socket_deallocate(client->sock); for (isock = 0, ssize = array_size(client->socks); isock < ssize; ++isock) socket_deallocate(client->socks[isock]); array_deallocate(client->socks); array_deallocate(client->pending); }
static void test_free( void ) { unsigned int ig, gsize, ic, csize; for( ig = 0, gsize = array_size( _test_groups ); ig < gsize; ++ig ) { for( ic = 0, csize = array_size( _test_groups[ig]->cases ); ic < csize; ++ic ) { memory_deallocate( _test_groups[ig]->cases[ic] ); } array_deallocate( _test_groups[ig]->cases ); memory_deallocate( _test_groups[ig] ); } array_deallocate( _test_groups ); _test_groups = 0; }
void network_address_array_deallocate(network_address_t** addresses) { unsigned int iaddr, asize; for (iaddr = 0, asize = array_size(addresses); iaddr < asize; ++iaddr) memory_deallocate(addresses[iaddr]); array_deallocate(addresses); }
void hashmap_deallocate( hashmap_t* map ) { unsigned int ibucket; for( ibucket = 0; ibucket < map->num_buckets; ++ibucket ) array_deallocate( map->bucket[ibucket] ); memory_deallocate( map ); }
void _random_shutdown( void ) { int i, size; if( _random_mutex ) mutex_lock( _random_mutex ); for( i = 0, size = array_size( _random_state ); i < size; ++i ) memory_deallocate( _random_state[i] ); array_deallocate( _random_available_state ); array_deallocate( _random_state ); set_thread_state( 0 ); if( _random_mutex ) { mutex_unlock( _random_mutex ); mutex_deallocate( _random_mutex ); } }
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; }
void lua_module_registry_finalize(lua_State* state) { lua_pushlstring(state, STRING_CONST(BUILD_REGISTRY_LOADED_MODULES)); lua_gettable(state, LUA_REGISTRYINDEX); lua_pushlstring(state, STRING_CONST(LOADED_MODULES_ENTRY_ARRAY)); lua_gettable(state, -2); lua_modulemap_entry_t** entryarr = lua_touserdata(state, -1); lua_pop(state, 2); if (entryarr) array_deallocate(entryarr); }
int hashify_process_files(string_t* files, bool check_only) { int result = HASHIFY_RESULT_OK; hashify_string_t* history = 0; size_t ifile, files_size; for (ifile = 0, files_size = array_size(files); (result == HASHIFY_RESULT_OK) && (ifile < files_size); ++ifile) { string_t input_filename; string_t output_filename; string_const_t base_filename; stream_t* input_file; stream_t* output_file; input_filename = string_allocate(0, BUILD_MAX_PATHLEN); input_filename = string_copy(input_filename.str, BUILD_MAX_PATHLEN, STRING_ARGS(files[ifile])); input_filename = path_clean(STRING_ARGS(input_filename), BUILD_MAX_PATHLEN); error_context_push(STRING_CONST("parsing file"), STRING_ARGS(input_filename)); base_filename = path_base_file_name_with_directory(STRING_ARGS(input_filename)); output_filename = string_allocate_format(STRING_CONST("%.*s.h"), STRING_FORMAT(base_filename)); log_infof(0, STRING_CONST("Hashifying %.*s -> %.*s"), STRING_FORMAT(input_filename), STRING_FORMAT(output_filename)); input_file = stream_open(STRING_ARGS(input_filename), STREAM_IN); //If only validating, open the final output file. If generating, make a memory buffer as intermediate storage if (check_only) output_file = stream_open(STRING_ARGS(output_filename), STREAM_IN); else output_file = buffer_stream_allocate(memory_allocate(0, 65536, 0, MEMORY_PERSISTENT), STREAM_IN | STREAM_OUT, 0, 65536, true, true); if (!input_file) { log_warnf(0, WARNING_INVALID_VALUE, STRING_CONST("Unable to open input file: %.*s"), STRING_FORMAT(input_filename)); result = HASHIFY_RESULT_MISSING_INPUT_FILE; } else if (!output_file) { log_warnf(0, WARNING_INVALID_VALUE, STRING_CONST("Unable to open output file: %.*s"), STRING_FORMAT(output_filename)); result = HASHIFY_RESULT_MISSING_OUTPUT_FILE; } if (input_file && output_file) { result = hashify_process_file(input_file, output_file, output_filename, check_only, &history); if ((result == HASHIFY_RESULT_OK) && !check_only) result = hashify_write_file(output_file, output_filename); } stream_deallocate(input_file); stream_deallocate(output_file); error_context_pop(); string_deallocate(input_filename.str); string_deallocate(output_filename.str); } if ((result == HASHIFY_RESULT_OK) && (files_size > 0)) { if (check_only) log_info(0, STRING_CONST("All hashes validated")); else log_info(0, STRING_CONST("All hashes generated")); } array_deallocate(history); return result; }
void hashmap_finalize(hashmap_t* map) { size_t ibucket; for (ibucket = 0; ibucket < map->num_buckets; ++ibucket) array_deallocate(map->bucket[ibucket]); }
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); }
unsigned int stacktrace_capture( void** trace, unsigned int max_depth, unsigned int skip_frames ) { unsigned int num_frames = 0; if( !trace ) return 0; if( !max_depth ) max_depth = BUILD_SIZE_STACKTRACE_DEPTH; if( max_depth > BUILD_SIZE_STACKTRACE_DEPTH ) max_depth = BUILD_SIZE_STACKTRACE_DEPTH; if( !_stackwalk_initialized ) { if( !_initialize_stackwalker() ) { memset( trace, 0, sizeof( void* ) * max_depth ); return num_frames; } } #if FOUNDATION_PLATFORM_WINDOWS && ( FOUNDATION_COMPILER_MSVC || FOUNDATION_COMPILER_INTEL ) // Add 1 skip frame for this function call ++skip_frames; # if USE_CAPTURESTACKBACKTRACE if( CallRtlCaptureStackBackTrace ) { void* local_trace[BUILD_SIZE_STACKTRACE_DEPTH]; if( max_depth + skip_frames > BUILD_SIZE_STACKTRACE_DEPTH ) max_depth = BUILD_SIZE_STACKTRACE_DEPTH - skip_frames; num_frames = (unsigned int)CallRtlCaptureStackBackTrace( skip_frames, max_depth, local_trace, 0 ); if( num_frames > max_depth ) num_frames = max_depth; memcpy( trace, local_trace, sizeof( void* ) * num_frames ); memset( trace + num_frames, 0, sizeof( void* ) * ( max_depth - num_frames ) ); } else { # else { # endif # if FOUNDATION_PLATFORM_ARCH_X86_64 // Raise an exception so helper has access to context record. __try { RaiseException( 0, // Application-defined exception code. 0, // Zero indicates continuable exception. 0, // Number of arguments in args array (ignored if args is null) 0 ); // Array of arguments } __except( _capture_stack_trace_helper( trace, max_depth, skip_frames, (GetExceptionInformation())->ContextRecord ) ) { } # else // Use a bit of inline assembly to capture the information relevant to stack walking which is // basically EIP and EBP. CONTEXT context; memset( &context, 0, sizeof( CONTEXT ) ); context.ContextFlags = CONTEXT_FULL; log_warnf( WARNING_DEPRECATED, "********** REIMPLEMENT FALLBACK STACKTRACE **********" ); /* Use a fake function call to pop the return address and retrieve EIP.*/ __asm { call FakeStackTraceCall FakeStackTraceCall: pop eax mov context.Eip, eax mov context.Ebp, ebp mov context.Esp, esp } // Capture the back trace. _capture_stack_trace_helper( trace, max_depth, skip_frames, &context ); # endif } #elif FOUNDATION_PLATFORM_APPLE //TODO: Implement #elif FOUNDATION_PLATFORM_POSIX // Add 1 skip frames for this function call skip_frames += 1; void* localframes[BUILD_SIZE_STACKTRACE_DEPTH]; num_frames = (unsigned int)backtrace( localframes, BUILD_SIZE_STACKTRACE_DEPTH ); if( num_frames > skip_frames ) { num_frames -= skip_frames; if( num_frames > max_depth ) num_frames = max_depth; memcpy( trace, localframes + skip_frames, sizeof( void* ) * num_frames ); } else trace[0] = 0; #endif return num_frames; } static bool _symbol_resolve_initialized = false; static bool _initialize_symbol_resolve() { if( _symbol_resolve_initialized ) return true; #if FOUNDATION_PLATFORM_WINDOWS { unsigned int options; void* dll = LoadLibraryA( "PSAPI.DLL" ); if( !dll ) return _symbol_resolve_initialized; CallEnumProcesses = (EnumProcessesFn)GetProcAddress( dll, "EnumProcesses" ); CallEnumProcessModules = (EnumProcessModulesFn)GetProcAddress( dll, "EnumProcessModules" ); CallGetModuleFileNameEx = (GetModuleFileNameExFn)GetProcAddress( dll, "GetModuleFileNameExA" ); CallGetModuleBaseName = (GetModuleBaseNameFn)GetProcAddress( dll, "GetModuleBaseNameA" ); CallGetModuleInformation = (GetModuleInformationFn)GetProcAddress( dll, "GetModuleInformation" ); if( !CallEnumProcesses || !CallEnumProcessModules || !CallGetModuleFileNameEx || !CallGetModuleBaseName || !CallGetModuleInformation ) return _symbol_resolve_initialized; dll = LoadLibraryA( "DBGHELP.DLL" ); if( !dll ) return _symbol_resolve_initialized; CallSymInitialize = (SymInitializeFn)GetProcAddress( dll, "SymInitialize" ); CallSymSetOptions = (SymSetOptionsFn)GetProcAddress( dll, "SymSetOptions" ); CallSymGetOptions = (SymGetOptionsFn)GetProcAddress( dll, "SymGetOptions" ); CallSymLoadModule64 = (SymLoadModule64Fn)GetProcAddress( dll, "SymLoadModule64" ); CallSymSetSearchPath = (SymSetSearchPathFn)GetProcAddress( dll, "SymSetSearchPath" ); CallSymGetModuleInfo64 = (SymGetModuleInfo64Fn)GetProcAddress( dll, "SymGetModuleInfo64" ); CallSymGetLineFromAddr64 = (SymGetLineFromAddr64Fn)GetProcAddress( dll, "SymGetLineFromAddr64" ); CallSymGetSymFromAddr64 = (SymGetSymFromAddr64Fn)GetProcAddress( dll, "SymGetSymFromAddr64" ); CallSymGetModuleBase64 = (SymGetModuleBase64Fn)GetProcAddress( dll, "SymGetModuleBase64" ); CallSymFunctionTableAccess64 = (SymFunctionTableAccess64Fn)GetProcAddress( dll, "SymFunctionTableAccess64" ); if( !CallSymInitialize || !CallSymSetOptions || !CallSymGetOptions || !CallSymLoadModule64 || !CallSymSetSearchPath || !CallSymGetModuleInfo64 || !CallSymGetLineFromAddr64 || !CallSymGetSymFromAddr64 || !CallSymGetModuleBase64 || !CallSymFunctionTableAccess64 ) return _symbol_resolve_initialized; options = CallSymGetOptions(); options |= SYMOPT_LOAD_LINES; options |= SYMOPT_DEBUG; options |= SYMOPT_UNDNAME; options |= SYMOPT_LOAD_LINES; options |= SYMOPT_FAIL_CRITICAL_ERRORS; options |= SYMOPT_DEFERRED_LOADS; options |= SYMOPT_ALLOW_ABSOLUTE_SYMBOLS; options |= SYMOPT_EXACT_SYMBOLS; options |= SYMOPT_CASE_INSENSITIVE; CallSymSetOptions( options ); CallSymInitialize( GetCurrentProcess(), 0, TRUE ); } _load_process_modules(); _symbol_resolve_initialized = true; #else _symbol_resolve_initialized = true; #endif return _symbol_resolve_initialized; } static NOINLINE char** _resolve_stack_frames( void** frames, unsigned int max_frames ) { #if FOUNDATION_PLATFORM_WINDOWS char** lines = 0; char symbol_buffer[ sizeof( IMAGEHLP_SYMBOL64 ) + 512 ]; PIMAGEHLP_SYMBOL64 symbol; DWORD displacement = 0; uint64_t displacement64 = 0; unsigned int iaddr = 0; unsigned int last_error; bool found = false; HANDLE process_handle = GetCurrentProcess(); int buffer_offset = 0; bool last_was_main = false; IMAGEHLP_LINE64 line64; IMAGEHLP_MODULE64 module64; for( iaddr = 0; ( iaddr < max_frames ) && !last_was_main; ++iaddr ) { char* resolved = 0; const char* function_name = "??"; const char* file_name = "??"; const char* module_name = "??"; unsigned int line_number = 0; //Allow first frame to be null in case of a function call to a null pointer if( iaddr && !frames[iaddr] ) break; // Initialize symbol. symbol = (PIMAGEHLP_SYMBOL64)symbol_buffer; memset( symbol, 0, sizeof( symbol_buffer ) ); symbol->SizeOfStruct = sizeof( symbol_buffer ); symbol->MaxNameLength = 512; // Get symbol from address. if( CallSymGetSymFromAddr64 && CallSymGetSymFromAddr64( process_handle, (uint64_t)((uintptr_t)frames[iaddr]), &displacement64, symbol ) ) { int offset = 0; while( symbol->Name[offset] < 32 ) ++offset; function_name = symbol->Name + offset; } else { // No symbol found for this address. last_error = GetLastError(); } memset( &line64, 0, sizeof( line64 ) ); line64.SizeOfStruct = sizeof( line64 ); if( CallSymGetLineFromAddr64 && CallSymGetLineFromAddr64( process_handle, (uint64_t)((uintptr_t)frames[iaddr]), &displacement, &line64 ) ) { file_name = line64.FileName; line_number = line64.LineNumber; } memset( &module64, 0, sizeof( module64 ) ); module64.SizeOfStruct = sizeof( module64 ); if( CallSymGetModuleInfo64 && CallSymGetModuleInfo64( process_handle, (uint64_t)((uintptr_t)frames[iaddr]), &module64 ) ) { int last_slash = STRING_NPOS; module_name = module64.ImageName; last_slash = string_rfind( module_name, '\\', STRING_NPOS ); if( last_slash != STRING_NPOS ) module_name += last_slash + 1; } resolved = string_format( "[" STRING_FORMAT_POINTER "] %s (%s:%d +%d bytes) [in %s]", frames[iaddr], function_name, file_name, line_number, displacement, module_name ); array_push( lines, resolved ); if( string_equal( function_name, "main" ) ) last_was_main = true; } return lines; #elif FOUNDATION_PLATFORM_LINUX char** addrs = 0; char** lines = 0; const char** args = 0; process_t* proc = process_allocate(); unsigned int num_frames = 0; unsigned int requested_frames = 0; bool last_was_main = false; if( !string_length( environment_executable_path() ) ) { for( unsigned int iaddr = 0; iaddr < max_frames; ++iaddr ) { //Allow first frame to be null in case of a function call to a null pointer if( iaddr && !frames[iaddr] ) break; array_push( lines, string_format( "[" STRING_FORMAT_POINTER "]", frames[iaddr] ) ); } return lines; } array_push( args, "-e" ); array_push( args, environment_executable_path() ); array_push( args, "-f" ); for( unsigned int iaddr = 0; iaddr < max_frames; ++iaddr ) { //Allow first frame to be null in case of a function call to a null pointer if( iaddr && !frames[iaddr] ) break; char* addr = string_format( STRING_FORMAT_POINTER, frames[iaddr] ); array_push( addrs, addr ); array_push( args, addr ); ++requested_frames; } process_set_working_directory( proc, environment_initial_working_directory() ); process_set_executable_path( proc, "/usr/bin/addr2line" ); process_set_arguments( proc, args, array_size( args ) ); process_set_flags( proc, PROCESS_ATTACHED | PROCESS_STDSTREAMS ); process_spawn( proc ); stream_t* procout = process_stdout( proc ); while( !stream_eos( procout ) && ( num_frames < requested_frames ) && !last_was_main ) { char* function = stream_read_line( procout, '\n' ); char* filename = stream_read_line( procout, '\n' ); array_push( lines, string_format( "[" STRING_FORMAT_POINTER "] %s (%s)", frames[num_frames], function && string_length( function ) ? function : "??", filename && string_length( filename ) ? filename : "??" ) ); if( string_equal( function, "main" ) ) last_was_main = true; string_deallocate( function ); string_deallocate( filename ); ++num_frames; } process_wait( proc ); process_deallocate( proc ); string_array_deallocate( addrs ); array_deallocate( args ); return lines; #else char** lines = 0; for( unsigned int iaddr = 0; iaddr < max_frames; ++iaddr ) { //Allow first frame to be null in case of a function call to a null pointer if( iaddr && !frames[iaddr] ) break; array_push( lines, string_format( "[" STRING_FORMAT_POINTER "]\n", frames[iaddr] ) ); } return lines; #endif }
static int blast_client_handshake(blast_client_t* client) { int isock, ssize; socket_t** socks; if (blast_time_elapsed_ms(client->last_send) > 10) blast_client_send_handshake(client, client->readers[client->current]); if (client->sock) { socks = &client->sock; ssize = 1; } else { socks = client->socks; ssize = array_size(client->socks); } for (isock = 0; isock < ssize; ++isock) { const network_address_t* address = 0; char packetbuffer[PACKET_DATABUF_SIZE]; size_t size = udp_socket_recvfrom(socks[isock], packetbuffer, sizeof(packetbuffer), &address); if (size > 0) { packet_t* packet = (packet_t*)packetbuffer; if (packet->type == PACKET_HANDSHAKE) { char buffer[NETWORK_ADDRESS_NUMERIC_MAX_LENGTH]; string_t addr = network_address_to_string(buffer, sizeof(buffer), address, true); packet_handshake_t* handshake = (packet_handshake_t*)packet; log_infof(HASH_BLAST, STRING_CONST("Got handshake packet from %.*s (seq %d, timestamp %lld, latency %lld ms)"), STRING_FORMAT(addr), (int)packet->seq, (tick_t)packet->timestamp, blast_timestamp_elapsed_ms(client->begin_send, packet->timestamp)); if (!client->sock) { client->target = address; client->sock = client->socks[isock]; client->socks[isock] = 0; } if (client->state == BLAST_STATE_HANDSHAKE) { log_infof(HASH_BLAST, STRING_CONST("Begin transfer of '%.*s' %lld bytes with token %d to %.*s"), STRING_FORMAT(client->readers[client->current]->name), client->readers[client->current]->size, handshake->token, STRING_FORMAT(addr)); client->token = (unsigned int)handshake->token; client->begin_send = time_current(); client->last_send = 0; client->seq = 0; array_clear(client->pending); client->state = BLAST_STATE_TRANSFER; return 1; } } else { log_warnf(HASH_BLAST, WARNING_SUSPICIOUS, STRING_CONST("Unknown datagram on socket in handshake state")); } } } if (client->state != BLAST_STATE_HANDSHAKE) { for (isock = 0, ssize = array_size(client->socks); isock < ssize; ++isock) { socket_t* sock = client->socks[isock]; socket_deallocate(sock); } array_deallocate(client->socks); client->socks = 0; } return 1; }
int hashify_process_files( const char* const* files, bool check_only ) { int result = HASHIFY_RESULT_OK; hashify_string_t* history = 0; unsigned int ifile, files_size; for( ifile = 0, files_size = array_size( files ); ( result == HASHIFY_RESULT_OK ) && ( ifile < files_size ); ++ifile ) { char* input_filename; char* output_filename; stream_t* input_file; stream_t* output_file; input_filename = path_clean( string_clone( files[ifile] ), path_is_absolute( files[ifile] ) ); error_context_push( "parsing file", input_filename ); output_filename = string_append( path_base_file_name_with_path( input_filename ), ".h" ); log_infof( "Hashifying %s -> %s", input_filename, output_filename ); input_file = stream_open( input_filename, STREAM_IN ); //If only validating, open the final output file. If generating, make a memory buffer as intermediate storage if( check_only ) output_file = stream_open( output_filename, STREAM_IN ); else output_file = buffer_stream_allocate( memory_allocate( 65536, 0, MEMORY_PERSISTENT ), STREAM_IN | STREAM_OUT, 0, 65536, true, true ); if( !input_file ) { log_warnf( WARNING_BAD_DATA, "Unable to open input file: %s", input_filename ); result = HASHIFY_RESULT_MISSING_INPUT_FILE; } else if( !output_file ) { log_warnf( WARNING_BAD_DATA, "Unable to open output file: %s", output_filename ); result = HASHIFY_RESULT_MISSING_OUTPUT_FILE; } if( input_file && output_file ) { result = hashify_process_file( input_file, output_file, check_only, &history ); if( ( result == HASHIFY_RESULT_OK ) && !check_only ) result = hashify_write_file( output_file, output_filename ); } stream_deallocate( input_file ); stream_deallocate( output_file ); error_context_pop(); string_deallocate( input_filename ); string_deallocate( output_filename ); } if( ( result == HASHIFY_RESULT_OK ) && ( files_size > 0 ) ) { if( check_only ) log_infof( "All hashes validated" ); else log_infof( "All hashes generated" ); } array_deallocate( history ); return result; }
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 hashify_process_file( stream_t* input_file, stream_t* output_file, bool check_only, hashify_string_t** history ) { int result = HASHIFY_RESULT_OK; char line_buffer[HASHIFY_LINEBUFFER_LENGTH]; hashify_string_t* local_hashes = 0; hashify_string_t* local_generated = 0; if( check_only ) result = hashify_read_hashes( output_file, &local_hashes ); else result = hashify_generate_preamble( output_file ); while( !stream_eos( input_file ) && ( result == HASHIFY_RESULT_OK ) ) { char* def_string = 0; char* value_string = 0; stream_read_line_buffer( input_file, line_buffer, HASHIFY_LINEBUFFER_LENGTH, '\n' ); string_split( line_buffer, " \t", &def_string, &value_string, false ); string_strip( def_string, STRING_WHITESPACE ); string_strip( value_string, STRING_WHITESPACE ); if( string_length( value_string ) && ( value_string[0] == '"' ) && ( value_string[ string_length( value_string ) - 1 ] == '"' ) ) { unsigned int len = string_length( value_string ); memmove( value_string, value_string + 1, len - 2 ); value_string[len-2] = 0; } if( string_length( def_string ) ) { hash_t hash_value = hash( value_string, string_length( value_string ) ); log_infof( " %s: %s -> 0x%llx", def_string, value_string, hash_value ); if( check_only ) { //Check local consistency result = hashify_check_local_consistency( value_string, hash_value, local_hashes ); } else { stream_write_format( output_file, "#define %s static_hash_string( \"%s\", 0x%llxULL )\n", def_string, value_string, hash_value ); } if( result == HASHIFY_RESULT_OK ) { hashify_string_t hash_string; //Check history result = hashify_check_collisions( value_string, hash_value, *history ); //Add to history string_copy( hash_string.string, value_string, HASHIFY_STRING_LENGTH ); hash_string.hash = hash_value; array_push_memcpy( *history, &hash_string ); array_push_memcpy( local_generated, &hash_string ); } } string_deallocate( def_string ); string_deallocate( value_string ); } if( check_only ) { //Check local consistency result = hashify_check_match( local_hashes, local_generated ); } array_deallocate( local_hashes ); array_deallocate( local_generated ); return result; }
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 !FOUNDATION_PLATFORM_POSIX 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; } } #endif } #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_warn( 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.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, "Spawn process (CreateProcess): %s %s", proc->path, cmdline ); if( !CreateProcessW( 0, 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_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 ) ); 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] ); array_deallocate( args ); 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->pid = 0; 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 ); }
int hashify_process_file(stream_t* input_file, stream_t* output_file, string_t output_filename, bool check_only, hashify_string_t** history) { int result = HASHIFY_RESULT_OK; char line_buffer[HASHIFY_LINEBUFFER_LENGTH]; hashify_string_t* local_hashes = 0; hashify_string_t* local_generated = 0; if (check_only) result = hashify_read_hashes(output_file, &local_hashes); else result = hashify_generate_preamble(output_file, output_filename); memset(line_buffer, 0, sizeof(line_buffer)); while (!stream_eos(input_file) && (result == HASHIFY_RESULT_OK)) { string_t line_string; string_const_t def_string; string_const_t value_string; line_string = stream_read_line_buffer(input_file, line_buffer, sizeof(line_buffer), '\n'); string_split(STRING_ARGS(line_string), STRING_CONST(" \t"), &def_string, &value_string, false); def_string = string_strip(STRING_ARGS(def_string), STRING_CONST(STRING_WHITESPACE)); value_string = string_strip(STRING_ARGS(value_string), STRING_CONST(STRING_WHITESPACE)); if (value_string.length && (value_string.str[0] == '"') && (value_string.str[ value_string.length - 1 ] == '"')) { ++value_string.str; value_string.length -= 2; } if (def_string.length) { hash_t hash_value = hash(STRING_ARGS(value_string)); log_infof(0, STRING_CONST(" %.*s: %.*s -> 0x%" PRIx64), STRING_FORMAT(def_string), STRING_FORMAT(value_string), hash_value); if (check_only) { //Check local consistency result = hashify_check_local_consistency(value_string, hash_value, local_hashes); } else { stream_write_format(output_file, STRING_CONST("#define %.*s static_hash_string(\"%.*s\", %" PRIsize ", 0x%" PRIx64 "ULL)\n"), STRING_FORMAT(def_string), STRING_FORMAT(value_string), value_string.length, hash_value); } if (result == HASHIFY_RESULT_OK) { hashify_string_t hash_string; //Check history result = hashify_check_collisions(value_string, hash_value, *history); //Add to history hash_string.string = string_copy(hash_string.buffer, HASHIFY_STRING_LENGTH, STRING_ARGS(value_string)); hash_string.hash = hash_value; array_push_memcpy(*history, &hash_string); array_push_memcpy(local_generated, &hash_string); } } } if (check_only) { //Check local consistency result = hashify_check_match(local_hashes, local_generated); } array_deallocate(local_hashes); array_deallocate(local_generated); return result; }
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; }
int main_run( void* main_arg ) { #if !FOUNDATION_PLATFORM_IOS && !FOUNDATION_PLATFORM_ANDROID 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 int process_result = 0; object_t thread = 0; log_set_suppress( HASH_TEST, ERRORLEVEL_DEBUG ); 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 ) thread_sleep( 10 ); 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_profile_run, test_radixsort_run, test_random_run, test_ringbuffer_run, test_semaphore_run, test_stacktrace_run, test_string_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 ); 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 ); while( !_test_should_terminate ) { system_process_events(); thread_sleep( 100 ); } log_debug( HASH_TEST, "Exiting main loop" ); #else //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-*.app const char* app_pattern = "test-*.app"; char** subdirs = fs_subdirs( environment_executable_directory() ); for( int idir = 0, dirsize = array_size( subdirs ); idir < dirsize; ++idir ) { if( string_match_pattern( subdirs[idir], app_pattern ) ) { array_push( exe_paths, string_substr( subdirs[idir], 0, string_length( subdirs[idir] ) - 4 ) ); array_push( exe_flags, PROCESS_OSX_USE_OPENAPPLICATION ); } } string_array_deallocate( subdirs ); #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 ); return process_result; }