void profile_initialize( const char* identifier, void* buffer, uint64_t size ) { profile_block_t* root = buffer; profile_block_t* block = root; uint64_t num_blocks = size / sizeof( profile_block_t ); uint32_t i; if( num_blocks > 65535 ) num_blocks = 65535; for( i = 0; i < ( num_blocks - 1 ); ++i, ++block ) { block->child = ( i + 1 ); block->sibling = 0; } block->child = 0; block->sibling = 0; root->child = 0; atomic_store32( &_profile_root, 0 ); _profile_num_blocks = num_blocks; _profile_identifier = identifier; _profile_blocks = root; atomic_store32( &_profile_free, 1 ); //TODO: Currently 0 is a no-block identifier, so we waste the first block atomic_store32( &_profile_counter, 128 ); _profile_ground_time = time_current(); set_thread_profile_block( 0 ); log_debugf( 0, "Initialize profiling system with %llu blocks (%lluKiB)", num_blocks, size / 1024 ); }
static void* stream_blocking_thread(void* arg) { int iloop; socket_t* sock = (socket_t*)arg; char buffer_out[317] = {0}; char buffer_in[317] = {0}; stream_t* stream = socket_stream(sock); for (iloop = 0; !thread_try_wait(0) && iloop < 512; ++iloop) { log_infof(HASH_NETWORK, STRING_CONST("UDP write pass %d"), iloop); EXPECT_SIZEEQ(stream_write(stream, buffer_out, 127), 127); EXPECT_SIZEEQ(stream_write(stream, buffer_out + 127, 180), 180); stream_flush(stream); EXPECT_SIZEEQ(stream_write(stream, buffer_out + 307, 10), 10); stream_flush(stream); log_infof(HASH_NETWORK, STRING_CONST("UDP read pass %d"), iloop); EXPECT_SIZEEQ(stream_read(stream, buffer_in, 235), 235); EXPECT_SIZEEQ(stream_read(stream, buffer_in + 235, 82), 82); thread_yield(); } log_debugf(HASH_NETWORK, STRING_CONST("IO complete on socket 0x%llx"), sock); stream_deallocate(stream); return 0; }
bool network_poll_add_socket(network_poll_t* pollobj, socket_t* sock) { size_t num_sockets = pollobj->num_sockets; if ((sock->base >= 0) && (num_sockets < pollobj->max_sockets)) { socket_base_t* sockbase = _socket_base + sock->base; log_debugf(HASH_NETWORK, STRING_CONST("Network poll: Adding socket (0x%" PRIfixPTR " : %d)"), sock, sockbase->fd); pollobj->slots[ num_sockets ].sock = sock; pollobj->slots[ num_sockets ].base = sock->base; pollobj->slots[ num_sockets ].fd = sockbase->fd; if (sockbase->state == SOCKETSTATE_CONNECTING) _socket_poll_state(sockbase); #if FOUNDATION_PLATFORM_APPLE pollobj->pollfds[ num_sockets ].fd = sockbase->fd; pollobj->pollfds[ num_sockets ].events = ((sockbase->state == SOCKETSTATE_CONNECTING) ? POLLOUT : POLLIN) | POLLERR | POLLHUP; #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID struct epoll_event event; event.events = ((sockbase->state == SOCKETSTATE_CONNECTING) ? EPOLLOUT : EPOLLIN) | EPOLLERR | EPOLLHUP; event.data.fd = (int)pollobj->num_sockets; epoll_ctl(pollobj->fd_poll, EPOLL_CTL_ADD, sockbase->fd, &event); #endif ++pollobj->num_sockets; return true; } return false; }
static void* datagram_client_blocking_thread(void* arg) { int iloop; test_datagram_arg_t* darg = arg; socket_t* sock = darg->sock; network_address_t* target = darg->target; const network_address_t* address; char buffer[1024] = {0}; size_t send = 973; size_t recv; log_debugf(HASH_NETWORK, STRING_CONST("IO start on socket 0x%llx"), sock); for (iloop = 0; iloop < 512; ++iloop) { log_infof(HASH_NETWORK, STRING_CONST("UDP read/write pass %d"), iloop); EXPECT_EQ(udp_socket_sendto(sock, buffer, send, target), send); recv = udp_socket_recvfrom(sock, buffer, send, &address); EXPECT_EQ(recv, send); EXPECT_TRUE(network_address_equal(target, address)); thread_yield(); } log_infof(HASH_NETWORK, STRING_CONST("IO complete on socket 0x%llx"), sock); return 0; }
static NOINLINE char* _expand_string( hash_t section_current, char* str ) { char* expanded; char* variable; unsigned int var_pos, var_end_pos, variable_length, separator, var_offset; hash_t section, key; expanded = str; var_pos = string_find_string( expanded, "$(", 0 ); while( var_pos != STRING_NPOS ) { var_end_pos = string_find( expanded, ')', var_pos + 2 ); FOUNDATION_ASSERT_MSG( var_end_pos != STRING_NPOS, "Malformed config variable statement" ); variable = string_substr( expanded, var_pos, ( var_end_pos != STRING_NPOS ) ? ( 1 + var_end_pos - var_pos ) : STRING_NPOS ); section = section_current; key = 0; variable_length = string_length( variable ); separator = string_find( variable, ':', 0 ); if( separator != STRING_NPOS ) { if( separator != 2 ) section = hash( variable + 2, separator - 2 ); var_offset = separator + 1; } else { var_offset = 2; } key = hash( variable + var_offset, variable_length - ( var_offset + ( variable[ variable_length - 1 ] == ')' ? 1 : 0 ) ) ); if( expanded == str ) expanded = string_clone( str ); if( section != HASH_ENVIRONMENT ) expanded = string_replace( expanded, variable, config_string( section, key ), false ); else expanded = string_replace( expanded, variable, _expand_environment( key, variable + var_offset ), false ); string_deallocate( variable ); var_pos = string_find_string( expanded, "$(", 0 ); } #if BUILD_ENABLE_DEBUG_CONFIG if( str != expanded ) log_debugf( HASH_CONFIG, "Expanded config value \"%s\" to \"%s\"", str, expanded ); #endif return expanded; }
bool network_poll_add_socket(network_poll_t* pollobj, socket_t* sock) { size_t slot = pollobj->num_sockets; if (slot < pollobj->max_sockets) { log_debugf(HASH_NETWORK, STRING_CONST("Network poll: Adding socket (0x%" PRIfixPTR " : %d)"), (uintptr_t)sock, sock->fd); pollobj->slots[slot].sock = sock; pollobj->slots[slot].fd = sock->fd; ++pollobj->num_sockets; network_poll_update_slot(pollobj, slot, sock); return true; } return false; }
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; }
void network_poll_remove_socket(network_poll_t* pollobj, socket_t* sock) { size_t islot, num_sockets = pollobj->num_sockets; for (islot = 0; islot < num_sockets; ++islot) { if (pollobj->slots[islot].sock == sock) { #if FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID int fd_remove = pollobj->slots[islot].fd; #endif log_debugf(HASH_NETWORK, STRING_CONST("Network poll: Removing socket (0x%" PRIfixPTR " : %d)"), pollobj->slots[islot].sock, pollobj->slots[islot].fd); //Swap with last slot and erase if (islot < pollobj->num_sockets - 1) { memcpy(pollobj->slots + islot, pollobj->slots + (num_sockets - 1), sizeof(network_poll_slot_t)); #if FOUNDATION_PLATFORM_APPLE memcpy(pollobj->pollfds + islot, pollobj->pollfds + (num_sockets - 1), sizeof(struct pollfd)); #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID //Mod the moved socket FOUNDATION_ASSERT(pollobj->slots[islot].base >= 0); struct epoll_event event; event.events = ((_socket_base[ pollobj->slots[islot].base ].state == SOCKETSTATE_CONNECTING) ? EPOLLOUT : EPOLLIN) | EPOLLERR | EPOLLHUP; event.data.fd = (int)islot; epoll_ctl(pollobj->fd_poll, EPOLL_CTL_MOD, pollobj->slots[islot].fd, &event); #endif } memset(pollobj->slots + (num_sockets - 1), 0, sizeof(network_poll_slot_t)); #if FOUNDATION_PLATFORM_APPLE memset(pollobj->pollfds + (num_sockets - 1), 0, sizeof(struct pollfd)); #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID struct epoll_event event; epoll_ctl(pollobj->fd_poll, EPOLL_CTL_DEL, fd_remove, &event); #endif num_sockets = --pollobj->num_sockets; } } }
lua_result_t lua_do_bind(lua_t* env, const char* property, size_t length, lua_command_t cmd, lua_value_t val) { lua_State* state; int stacksize; size_t start, next; string_const_t part; if (!env || !length) return LUA_ERROR; state = env->state; stacksize = lua_gettop(state); next = string_find(property, length, '.', 0); if (next != STRING_NPOS) { int tables; unsigned int numtables = 0; part = string_const(property, next); lua_getlglobal(state, part.str, part.length); if (lua_isnil(state, -1)) { //Create global table lua_pop(state, 1); lua_newtable(state); lua_pushvalue(state, -1); lua_setlglobal(state, part.str, part.length); log_debugf(HASH_LUA, STRING_CONST("Created global table: %.*s"), STRING_FORMAT(part)); } else if (!lua_istable(state, -1)) { log_errorf(HASH_LUA, ERROR_INVALID_VALUE, STRING_CONST("Invalid script bind call, existing data '%.*s' in '%.*s' is not a table"), STRING_FORMAT(part), (int)length, property); lua_pop(state, lua_gettop(state) - stacksize); return LUA_ERROR; } //Top of stack is now table FOUNDATION_ASSERT(lua_istable(state, -1)); ++next; start = next; ++numtables; next = string_find(property, length, '.', next); while (next != STRING_NPOS) { part = string_const(property + start, next - start); lua_pushlstring(state, part.str, part.length); lua_gettable(state, -2); if (lua_isnil(state, -1)) { //Create sub-table lua_pop(state, 1); lua_newtable(state); lua_pushlstring(state, part.str, part.length); lua_pushvalue(state, -2); lua_settable(state, -4); log_debugf(HASH_LUA, STRING_CONST("Created table: %.*s"), next, property); } else if (!lua_istable(state, -1)) { log_errorf(HASH_LUA, ERROR_INVALID_VALUE, STRING_CONST("Invalid script bind call, existing data '%.*s' in '%.*s' is not a table"), STRING_FORMAT(part), (int)next, property); lua_pop(state, lua_gettop(state) - stacksize); return LUA_ERROR; } //Top of stack is now table FOUNDATION_ASSERT(lua_istable(state, -1)); ++next; start = next; next = string_find(property, length, '.', next); ++numtables; } part = string_const(property + start, length - start); switch (cmd) { case LUACMD_BIND: lua_push_method(state, STRING_ARGS(part), val.fn); break; case LUACMD_BIND_INT: lua_push_integer(state, STRING_ARGS(part), val.ival); break; case LUACMD_BIND_VAL: lua_push_number(state, STRING_ARGS(part), val.val); break; default: break; } tables = lua_gettop(state) - stacksize; lua_pop(state, tables); FOUNDATION_ASSERT(tables == (int)numtables); } else { part = string_const(property, length); switch (cmd) { case LUACMD_BIND: lua_push_method_global(state, STRING_ARGS(part), val.fn); break; case LUACMD_BIND_INT: lua_push_integer_global(state, STRING_ARGS(part), val.ival); break; case LUACMD_BIND_VAL: lua_push_number_global(state, STRING_ARGS(part), val.val); break; default: break; } } return LUA_OK; }
window_t* window_create(unsigned int adapter, const char* title, size_t length, unsigned int width, unsigned int height, bool show) { FOUNDATION_UNUSED(length); Display* display = XOpenDisplay(0); if (!display) { log_error(HASH_WINDOW, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to open X display")); goto fail; } unsigned int screen = adapter; if (!screen) screen = DefaultScreen(display); XVisualInfo* visual = _get_xvisual(display, screen, 24, 16, 0); if (!visual) { log_errorf(HASH_WINDOW, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to get X visual")); goto fail; } Colormap colormap = XCreateColormap(display, XRootWindow(display, screen), visual->visual, AllocNone); log_debugf(HASH_WINDOW, STRING_CONST("Creating window on screen %d with dimensions %dx%d"), screen, width, height); XSetWindowAttributes attrib; attrib.colormap = colormap; attrib.background_pixel = 0; attrib.border_pixel = 0; attrib.event_mask = ExposureMask | StructureNotifyMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | Button1MotionMask | Button2MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask | ButtonMotionMask | KeyPressMask | KeyReleaseMask | KeymapStateMask | VisibilityChangeMask | FocusChangeMask; Window drawable = XCreateWindow(display, XRootWindow(display, screen), 0, 0, width, height, 0, visual->depth, InputOutput, visual->visual, CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &attrib); XSizeHints sizehints; sizehints.x = 0; sizehints.y = 0; sizehints.width = width; sizehints.height = height; sizehints.flags = USSize | USPosition; XSetNormalHints(display, drawable, &sizehints); XSetStandardProperties(display, drawable, title, title, None, 0, 0, &sizehints); if (show) { XMapWindow(display, drawable); XFlush(display); } Atom atom_delete = XInternAtom(display, "WM_DELETE_WINDOW", False); XSetWMProtocols(display, drawable, &atom_delete, 1); XFlush(display); XSync(display, False); XIC xic = 0; XIM xim = XOpenIM(display, 0, 0, 0); if(xim) { xic = XCreateIC(xim, XNInputStyle, XIMPreeditNone | XIMStatusNone, XNClientWindow, drawable, nullptr); if(xic) { /*XGetICValues(ic, XNFilterEvents, &fevent, NULL); mask = ExposureMask | KeyPressMask | FocusChangeMask; XSelectInput(display, window, mask|fevent);*/ } else { log_warn(HASH_WINDOW, WARNING_SUSPICIOUS, STRING_CONST("Unable to create X input context")); } } else { log_warn(HASH_WINDOW, WARNING_SUSPICIOUS, STRING_CONST("Unable to open X input method")); } window_t* window = memory_allocate(HASH_WINDOW, sizeof(window_t), 0, MEMORY_PERSISTENT | MEMORY_ZERO_INITIALIZED); window->display = display; window->visual = visual; window->screen = screen; window->drawable = drawable; window->atom = atom_delete; window->xim = xim; window->xic = xic; return window; fail: return 0; }
DECLARE_TEST(profile, stream) { thread_t thread[32]; int ith; uint64_t frame; string_t filename; error(); //Clear error filename = path_allocate_concat(STRING_ARGS(environment_temporary_directory()), STRING_CONST("test.profile")); //log_infof(HASH_TEST, STRING_CONST("Output to profile file: %.*s"), STRING_FORMAT(filename)); fs_make_directory(STRING_ARGS(environment_temporary_directory())); _profile_stream = fs_open_file(STRING_ARGS(filename), STREAM_OUT | STREAM_BINARY); string_deallocate(filename.str); profile_initialize(STRING_CONST("test_profile"), _test_profile_buffer, TEST_PROFILE_BUFFER_SIZE); profile_set_output(_profile_file_writer); profile_set_output_wait(10); profile_enable(true); for (ith = 0; ith < 32; ++ith) thread_initialize(&thread[ith], _profile_stream_thread, 0, STRING_CONST("profile_thread"), THREAD_PRIORITY_NORMAL, 0); for (ith = 0; ith < 32; ++ith) thread_start(&thread[ith]); test_wait_for_threads_startup(thread, 32); for (frame = 0; frame < 1000; ++frame) { thread_sleep(16); profile_log( STRING_CONST("This is a really long profile log line that should break into multiple profile blocks automatically without causing any issues whatsoever if everything works as expected which it should or the code needs to be fixed")); profile_end_frame(frame++); if ((frame % 30) == 0) { profile_enable(false); thread_sleep(10); profile_enable(true); } } for (ith = 0; ith < 32; ++ith) thread_signal(&thread[ith]); test_wait_for_threads_finish(thread, 32); for (ith = 0; ith < 32; ++ith) thread_finalize(&thread[ith]); profile_end_frame(frame++); profile_set_output_wait(10000); thread_sleep(1000); profile_begin_block(STRING_CONST("Should be cleaned up")); profile_end_block(); profile_enable(false); profile_finalize(); error(); stream_deallocate(_profile_stream); //TODO: Validate that output is sane log_debugf(HASH_TEST, STRING_CONST("Generated %" PRId64 " blocks"), atomic_load64(&_profile_generated_blocks)); return 0; }
static int lua_module_upload(lua_State* state, const void* bytecode, size_t size) { string_const_t errmsg = {0, 0}; lua_readbuffer_t read_buffer = { .buffer = bytecode, .size = size, .offset = 0 }; log_debugf(HASH_LUA, STRING_CONST("Loading %u bytes of module bytecode"), read_buffer.size); if (lua_load(state, lua_read_buffer, &read_buffer, "module") != 0) { errmsg.str = lua_tolstring(state, -1, &errmsg.length); log_errorf(HASH_LUA, ERROR_INTERNAL_FAILURE, STRING_CONST("Lua load failed (module): %.*s"), STRING_FORMAT(errmsg)); lua_pop(state, 1); return -1; } if (lua_pcall(state, 0, 1, 0) != 0) { errmsg.str = lua_tolstring(state, -1, &errmsg.length); log_errorf(HASH_LUA, ERROR_INTERNAL_FAILURE, STRING_CONST("Lua pcall failed (module): %.*s"), STRING_FORMAT(errmsg)); return -1; } return 0; } int lua_module_loader(lua_State* state) { lua_modulemap_entry_t* entry = lua_touserdata(state, lua_upvalueindex(2)); if (!entry) return 0; size_t name_length = 0; const char* name = luaL_checklstring(state, lua_upvalueindex(1), &name_length); int stacksize = lua_gettop(state); if (entry->preload) entry->preload(); lua_module_t module = lua_module_load_resource(entry->uuid); if (module.size) { if (lua_module_upload(state, module.bytecode, module.size) == 0) { lua_module_registry_add(state, entry); if (lua_istable(state, -1)) { /* Modules are never garbage collected anyway, since lib_package keeps a global registry reference in _LOADED table, and we keep a reference in loaded-modules table for reload functionality lua_pushlstring(state, STRING_CONST("__gcproxy")); //Proxy table index lua_newuserdata(state, 8); //New proxy object lua_newtable(state); //New metatable for proxy lua_pushlstring(state, STRING_CONST("__gc")); lua_pushlightuserdata(state, entry); lua_pushcclosure(state, lua_module_gc, 1); lua_rawset(state, -3); //Set __gc metatable method lua_setmetatable(state, -2); //Set metatable on proxy lua_rawset(state, -3); //Set proxy in table index __gcproxy */ lua_pushlstring(state, STRING_CONST(BUILD_REGISTRY_LOADED_MODULES)); lua_gettable(state, LUA_REGISTRYINDEX); lua_pushlightuserdata(state, entry); //entry is key lua_pushvalue(state, -3); //copy the loaded module table as value lua_pushlstring(state, STRING_CONST("__modulename")); lua_pushlstring(state, name, name_length); lua_settable(state, -3); //set moduletable["__modulename"] = name lua_settable(state, -3); //set table loaded-modules[entry] = moduletable lua_pop(state, 1); } } memory_deallocate(module.bytecode); } else { string_const_t uuidstr = string_from_uuid_static(entry->uuid); lua_pushfstring(state, "unable to load module '%s'", uuidstr.str); lua_error(state); } return lua_gettop(state) - stacksize; }
DECLARE_TEST( profile, stream ) { object_t thread[32]; int ith; int frame; char* filename; error(); filename = path_merge( environment_temporary_directory(), "test.profile" ); log_infof( HASH_TEST, "Output to profile file: %s", filename ); fs_make_directory( environment_temporary_directory() ); _profile_stream = fs_open_file( filename, STREAM_OUT | STREAM_BINARY ); string_deallocate( filename ); profile_initialize( "test_profile", _test_profile_buffer, _test_profile_buffer_size ); profile_set_output( _profile_file_writer ); profile_set_output_wait( 10 ); profile_enable( true ); for( ith = 0; ith < 32; ++ith ) { thread[ith] = thread_create( _profile_stream_thread, "profile_thread", THREAD_PRIORITY_NORMAL, 0 ); thread_start( thread[ith], 0 ); } test_wait_for_threads_startup( thread, 32 ); for( frame = 0; frame < 1000; ++frame ) { thread_sleep( 16 ); profile_log( "This is a really long profile log line that should break into multiple profile blocks automatically without causing any issues whatsoever if everything works as expected which it should or the code needs to be fixed" ); profile_end_frame( frame++ ); if( ( frame % 30 ) == 0 ) { profile_enable( false ); thread_sleep( 10 ); profile_enable( true ); } } for( ith = 0; ith < 32; ++ith ) { thread_terminate( thread[ith] ); thread_destroy( thread[ith] ); thread_yield(); } test_wait_for_threads_exit( thread, 32 ); profile_end_frame( frame++ ); profile_set_output_wait( 100 ); thread_sleep( 1000 ); profile_enable( false ); profile_shutdown(); error(); stream_deallocate( _profile_stream ); //TODO: Validate that output is sane log_debugf( HASH_TEST, "Generated %lld blocks", atomic_load64( &_profile_generated_blocks ) ); return 0; }
void config_parse( stream_t* stream, hash_t filter_section, bool overwrite ) { char* buffer; hash_t section = 0; hash_t key = 0; unsigned int line = 0; #if BUILD_ENABLE_DEBUG_CONFIG log_debugf( HASH_CONFIG, "Parsing config stream: %s", stream_path( stream ) ); #endif buffer = memory_allocate_zero( 1024ULL, 0, MEMORY_TEMPORARY ); while( !stream_eos( stream ) ) { ++line; stream_read_line_buffer( stream, buffer, 1024, '\n' ); string_strip( buffer, " \t\n\r" ); if( !string_length( buffer ) || ( buffer[0] == ';' ) || ( buffer[0] == '#' ) ) continue; if( buffer[0] == '[' ) { //Section declaration unsigned int endpos = string_rfind( buffer, ']', string_length( buffer ) - 1 ); if( ( endpos == STRING_NPOS ) || ( endpos < 1 ) ) { log_warnf( HASH_CONFIG, WARNING_BAD_DATA, "Invalid section declaration on line %d in config stream '%s'", line, stream_path( stream ) ); continue; } buffer[endpos] = 0; section = hash( buffer + 1, endpos - 1 ); #if BUILD_ENABLE_DEBUG_CONFIG log_debugf( HASH_CONFIG, " config: section set to '%s' (0x%llx)", buffer + 1, section ); #endif } else if( !filter_section || ( filter_section == section ) ) { //name=value declaration char* name; char* value; unsigned int separator = string_find( buffer, '=', 0 ); if( separator == STRING_NPOS ) { log_warnf( HASH_CONFIG, WARNING_BAD_DATA, "Invalid value declaration on line %d in config stream '%s', missing assignment operator '=': %s", line, stream_path( stream ), buffer ); continue; } name = string_strip_substr( buffer, " \t", separator ); value = string_strip( buffer + separator + 1, " \t" ); if( !string_length( name ) ) { log_warnf( HASH_CONFIG, WARNING_BAD_DATA, "Invalid value declaration on line %d in config stream '%s', empty name string", line, stream_path( stream ) ); continue; } key = hash( name, string_length( name ) ); if( overwrite || !config_key( section, key, false ) ) { #if BUILD_ENABLE_DEBUG_CONFIG log_debugf( HASH_CONFIG, " config: %s (0x%llx) = %s", name, key, value ); #endif if( !string_length( value ) ) config_set_string( section, key, "" ); else if( string_equal( value, "false" ) ) config_set_bool( section, key, false ); else if( string_equal( value, "true" ) ) config_set_bool( section, key, true ); else if( ( string_find( value, '.', 0 ) != STRING_NPOS ) && ( string_find_first_not_of( value, "0123456789.", 0 ) == STRING_NPOS ) && ( string_find( value, '.', string_find( value, '.', 0 ) + 1 ) == STRING_NPOS ) ) //Exactly one "." config_set_real( section, key, string_to_real( value ) ); else if( string_find_first_not_of( value, "0123456789", 0 ) == STRING_NPOS ) config_set_int( section, key, string_to_int64( value ) ); else config_set_string( section, key, value ); } } } memory_deallocate( buffer ); }
int lua_module_reload(lua_t* lua, const uuid_t uuid) { int ret = -1; lua_State* state = lua_state(lua); lua_modulemap_entry_t* entry = lua_module_registry_lookup(state, uuid); if (entry) { int stacksize = lua_gettop(state); lua_module_t module = lua_module_load_resource(uuid); if (module.size) { string_const_t uuidstr = string_from_uuid_static(uuid); log_debugf(HASH_LUA, STRING_CONST("Reloading module: %.*s"), STRING_FORMAT(uuidstr)); if (lua_module_upload(state, module.bytecode, module.size) == 0) { //Check if module loaded as a table if (lua_istable(state, -1)) { lua_pushlstring(state, STRING_CONST(BUILD_REGISTRY_LOADED_MODULES)); lua_gettable(state, LUA_REGISTRYINDEX); //Get and replace the old loaded module table lua_pushlightuserdata(state, entry); lua_gettable(state, -2); lua_replace(state, -2); //Get rid of loaded-modules registry table from stack if (lua_istable(state, -1)) { lua_pushlstring(state, STRING_CONST("__modulename")); lua_gettable(state, -2); size_t name_length = 0; const char* modulename = lua_isnil(state, -1) ? nullptr : luaL_checklstring(state, -1, &name_length); log_debugf(HASH_LUA, STRING_CONST("Replacing module table: %.*s (%.*s)"), STRING_FORMAT(uuidstr), (int)name_length, modulename); lua_pop(state, 1); //Get rid of name from stack //Clear previous loaded-modules table lua_pushnil(state); while (lua_next(state, -2) != 0) { lua_pop(state, 1); //Old value lua_pushnil(state); //Replace with nil lua_settable(state, -3); //Erase in previous loaded-modules table lua_pushnil(state); //Restart lua_next } //Copy new module table to previous loaded-modules table lua_pushnil(state); while (lua_next(state, -3) != 0) { //Lookup in new module table lua_pushvalue(state, -2); //Copy key lua_pushvalue(state, -2); //Copy value lua_settable(state, -5); //Set in previous loaded-modules table lua_pop(state, 1); //Pop value, leaving key for lua_next iteration } lua_pushlstring(state, STRING_CONST("__modulename")); lua_pushlstring(state, modulename, name_length); lua_settable(state, -3); } } ret = 0; } else { uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_LUA, WARNING_RESOURCE, STRING_CONST("Unable to reload module '%.*s'"), STRING_FORMAT(uuidstr)); } } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_LUA, WARNING_RESOURCE, STRING_CONST("Unable to load module '%.*s'"), STRING_FORMAT(uuidstr)); } lua_settop(state, stacksize); } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_debugf(HASH_LUA, STRING_CONST("Reloading module ignored, not loaded: %.*s"), STRING_FORMAT(uuidstr)); } return ret; }
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 ); }
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; }
static size_t _socket_stream_read(stream_t* stream, void* buffer, size_t size) { socket_stream_t* sockstream; socket_t* sock; size_t was_read = 0; size_t copy; bool try_again; size_t want_read; sockstream = (socket_stream_t*)stream; sock = sockstream->socket; if ((sock->fd == NETWORK_SOCKET_INVALID) || ((sock->state != SOCKETSTATE_CONNECTED) && (sock->state != SOCKETSTATE_DISCONNECTED)) || !size) goto exit; do { try_again = false; copy = (sockstream->write_in - sockstream->read_in); want_read = size - was_read; if (copy > want_read) copy = want_read; if (copy > 0) { if (buffer) memcpy(buffer, sockstream->buffer_in + sockstream->read_in, copy); #if BUILD_ENABLE_NETWORK_DUMP_TRAFFIC > 0 log_debugf(HASH_NETWORK, STRING_CONST("Socket stream (0x%" PRIfixPTR " : %d) read %" PRIsize" of %" PRIsize " bytes from buffer position %" PRIsize), (uintptr_t)sock, sock->fd, copy, want_read, sockstream->read_in); #endif was_read += copy; sockstream->read_in += copy; if (sockstream->read_in == sockstream->write_in) { sockstream->read_in = 0; sockstream->write_in = 0; } } if (was_read < size) { FOUNDATION_ASSERT(sockstream->read_in == 0); FOUNDATION_ASSERT(sockstream->write_in == 0); sockstream->read_in = 0; sockstream->write_in = socket_read(sock, sockstream->buffer_in, sockstream->buffer_in_size); if (sockstream->write_in > 0) try_again = true; } } while ((was_read < size) && try_again); if (was_read < size) { if (was_read) log_warnf(HASH_NETWORK, WARNING_SUSPICIOUS, STRING_CONST("Socket stream (0x%" PRIfixPTR " : %d): partial read %" PRIsize " of %" PRIsize " bytes"), (uintptr_t)sock, sock->fd, was_read, size); socket_poll_state(sock); } exit: return was_read; }