void pipe_initialize( stream_pipe_t* pipestream ) { stream_t* stream = (stream_t*)pipestream; memset( stream, 0, sizeof( stream_pipe_t ) ); stream_initialize( stream, system_byteorder() ); pipestream->type = STREAMTYPE_PIPE; pipestream->path = string_format( "pipe://0x" PRIfixPTR, pipestream ); pipestream->mode = STREAM_OUT | STREAM_IN | STREAM_BINARY; pipestream->sequential = true; #if FOUNDATION_PLATFORM_WINDOWS { //Inheritable by default so process can use for stdstreams SECURITY_ATTRIBUTES security_attribs = {0}; security_attribs.nLength = sizeof( SECURITY_ATTRIBUTES ); security_attribs.bInheritHandle = TRUE; security_attribs.lpSecurityDescriptor = 0; if( !CreatePipe( &pipestream->handle_read, &pipestream->handle_write, &security_attribs, 0 ) ) log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to create unnamed pipe: %s", system_error_message( GetLastError() ) ); } #elif FOUNDATION_PLATFORM_POSIX || FOUNDATION_PLATFORM_PNACL int fds[2] = { 0, 0 }; if( pipe( fds ) < 0 ) log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to create unnamed pipe: %s", system_error_message( 0 ) ); pipestream->fd_read = fds[0]; pipestream->fd_write = fds[1]; #endif pipestream->vtable = &_pipe_stream_vtable; }
void mutex_signal(mutex_t* mutex) { #if !BUILD_DEPLOY profile_signal(mutex->name.str, mutex->name.length); #endif #if FOUNDATION_PLATFORM_WINDOWS SetEvent(mutex->event); #elif FOUNDATION_PLATFORM_POSIX || FOUNDATION_PLATFORM_PNACL mutex_lock(mutex); mutex->pending = true; int ret = pthread_cond_broadcast(&mutex->cond); if (ret != 0) { string_const_t errmsg = system_error_message(ret); log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to signal mutex '%.*s': %.*s (%d)"), STRING_FORMAT(mutex->name), STRING_FORMAT(errmsg), ret); } mutex_unlock(mutex); #else # error mutex_signal not implemented #endif }
const char* environment_current_working_directory( void ) { if( _environment_current_working_dir[0] ) return _environment_current_working_dir; #if FOUNDATION_PLATFORM_WINDOWS { char* path; wchar_t* wd = memory_allocate_zero( sizeof( wchar_t ) * FOUNDATION_MAX_PATHLEN, 0, MEMORY_TEMPORARY ); GetCurrentDirectoryW( FOUNDATION_MAX_PATHLEN-1, wd ); path = path_clean( string_allocate_from_wstring( wd, 0 ), true ); string_copy( _environment_current_working_dir, path, FOUNDATION_MAX_PATHLEN ); string_copy( _environment_current_working_dir, path, FOUNDATION_MAX_PATHLEN ); string_deallocate( path ); memory_deallocate( wd ); } #elif FOUNDATION_PLATFORM_POSIX char* path = memory_allocate_zero( FOUNDATION_MAX_PATHLEN, 0, MEMORY_TEMPORARY ); if( !getcwd( path, FOUNDATION_MAX_PATHLEN ) ) { log_errorf( ERROR_SYSTEM_CALL_FAIL, "Unable to get cwd: %s", system_error_message( 0 ) ); return ""; } path = path_clean( path, true ); string_copy( _environment_current_working_dir, path, FOUNDATION_MAX_PATHLEN ); memory_deallocate( path ); #else # error Not implemented #endif return _environment_current_working_dir; }
static void* _memory_reallocate_malloc( void* p, uint64_t size, unsigned int align ) { align = _memory_get_align( align ); #if FOUNDATION_PLATFORM_WINDOWS return _aligned_realloc( p, (size_t)size, align ); #else if( align ) { //No realloc aligned available void* memory = aligned_alloc( align, (size_t)size ); if( !memory ) { log_panicf( ERROR_OUT_OF_MEMORY, "Unable to reallocate memory: %s", system_error_message( 0 ) ); return 0; } if( p ) { size_t prev_size = malloc_usable_size( p ); memcpy( memory, p, ( size < prev_size ) ? size : prev_size ); } return memory; } return realloc( p, (size_t)size ); #endif }
stream_t* pipe_allocate( void ) { stream_pipe_t* pipestream = memory_allocate_zero_context( MEMORYCONTEXT_STREAM, sizeof( stream_pipe_t ), 0, MEMORY_PERSISTENT ); stream_t* stream = (stream_t*)pipestream; _stream_initialize( stream, system_byteorder() ); pipestream->type = STREAMTYPE_PIPE; pipestream->path = string_format( "pipe://" STRING_FORMAT_POINTER, pipestream ); pipestream->mode = STREAM_OUT | STREAM_IN | STREAM_BINARY; pipestream->sequential = true; #if FOUNDATION_PLATFORM_WINDOWS { //Inheritable by default so process can use for stdstreams SECURITY_ATTRIBUTES security_attribs = {0}; security_attribs.nLength = sizeof( SECURITY_ATTRIBUTES ); security_attribs.bInheritHandle = TRUE; security_attribs.lpSecurityDescriptor = 0; if( !CreatePipe( &pipestream->handle_read, &pipestream->handle_write, &security_attribs, 0 ) ) log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to create unnamed pipe: %s", system_error_message( GetLastError() ) ); } #elif FOUNDATION_PLATFORM_POSIX int fds[2] = { 0, 0 }; if( pipe( fds ) < 0 ) log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to create unnamed pipe: %s", system_error_message( 0 ) ); else { pipestream->fd_read = fds[0]; pipestream->fd_write = fds[1]; } #endif pipestream->vtable = &_pipe_stream_vtable; return stream; }
static uint64_t _pipe_stream_read( stream_t* stream, void* dest, uint64_t num ) { stream_pipe_t* pipestream = (stream_pipe_t*)stream; FOUNDATION_ASSERT( stream->type == STREAMTYPE_PIPE ); #if FOUNDATION_PLATFORM_WINDOWS if( pipestream->handle_read && ( ( pipestream->mode & STREAM_IN ) != 0 ) ) { uint64_t total_read = 0; do { unsigned long num_read = 0; if( !ReadFile( pipestream->handle_read, pointer_offset( dest, total_read ), (unsigned int)( num - total_read ), &num_read, 0 ) ) { int err = GetLastError(); if( err == ERROR_BROKEN_PIPE ) { pipestream->eos = true; break; } log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to read from pipe: %s (%d)", system_error_message( err ), err ); } else { total_read += num_read; } } while( total_read < num ); return total_read; } #elif FOUNDATION_PLATFORM_POSIX || FOUNDATION_PLATFORM_PNACL if( pipestream->fd_read && ( ( pipestream->mode & STREAM_IN ) != 0 ) ) { uint64_t total_read = 0; do { ssize_t num_read = read( pipestream->fd_read, pointer_offset( dest, total_read ), (size_t)( num - total_read ) ); if( num_read <= 0 ) { pipestream->eos = true; break; } total_read += num_read; } while( total_read < num ); return total_read; } #endif return 0; }
/* * Wrapper around openvpn_execve */ bool openvpn_execve_check (const struct argv *a, const struct env_set *es, const unsigned int flags, const char *error_message) { struct gc_arena gc = gc_new (); const int stat = openvpn_execve (a, es, flags); int ret = false; if (platform_system_ok (stat)) ret = true; else { if (error_message) msg (((flags & S_FATAL) ? M_FATAL : M_WARN), "%s: %s", error_message, system_error_message (stat, &gc)); } gc_free (&gc); return ret; }
static void* _memory_allocate_malloc( uint16_t context, uint64_t size, unsigned int align, memory_hint_t hint ) { align = _memory_get_align( align ); #if FOUNDATION_PLATFORM_WINDOWS return _aligned_malloc( (size_t)size, align ); #elif FOUNDATION_PLATFORM_POSIX && !FOUNDATION_PLATFORM_ANDROID void* memory = 0; if( !align ) return malloc( (size_t)size ); /*int result = posix_memalign( &memory, align, (size_t)size ); if( result || !memory ) log_panicf( ERROR_OUT_OF_MEMORY, "Unable to allocate memory: %s", system_error_message( 0 ) ); return ( result == 0 ) ? memory : 0;*/ memory = aligned_alloc( align, (size_t)size ); if( !memory ) log_panicf( ERROR_OUT_OF_MEMORY, "Unable to allocate memory: %s", system_error_message( 0 ) ); return memory; #else void* memory = malloc( size + align ); memory = _memory_align_pointer( memory, align ); return memory; #endif }
size_t network_poll(network_poll_t* pollobj, network_poll_event_t* events, size_t capacity, unsigned int timeoutms) { int avail = 0; size_t num_events = 0; #if FOUNDATION_PLATFORM_WINDOWS //TODO: Refactor to keep fd_set across loop and rebuild on change (add/remove) int num_fd = 0; int ret = 0; size_t islot; fd_set fdread, fdwrite, fderr; #endif if (!pollobj->num_sockets) return num_events; #if FOUNDATION_PLATFORM_APPLE int ret = poll(pollobj->pollfds, pollobj->num_sockets, timeoutms); #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID int ret = epoll_wait(pollobj->fd_poll, pollobj->events, pollobj->num_sockets + 1, timeoutms); int num_polled = ret; #elif FOUNDATION_PLATFORM_WINDOWS FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fderr); for (islot = 0; islot < pollobj->num_sockets; ++islot) { int fd = pollobj->slots[islot].fd; socket_base_t* sockbase = _socket_base + pollobj->slots[islot].base; FD_SET(fd, &fdread); if (sockbase->state == SOCKETSTATE_CONNECTING) FD_SET(fd, &fdwrite); FD_SET(fd, &fderr); if (fd >= num_fd) num_fd = fd + 1; } if (!num_fd) { return num_events; } else { struct timeval tv; tv.tv_sec = timeoutms / 1000; tv.tv_usec = (timeoutms % 1000) * 1000; ret = select(num_fd, &fdread, &fdwrite, &fderr, &tv); } #else # error Not implemented #endif if (ret < 0) { int err = NETWORK_SOCKET_ERROR; string_const_t errmsg = system_error_message(err); log_warnf(HASH_NETWORK, WARNING_SUSPICIOUS, STRING_CONST("Error in socket poll: %.*s (%d)"), STRING_FORMAT(errmsg), err); if (!avail) return num_events; ret = avail; } if (!avail && !ret) return num_events; #if FOUNDATION_PLATFORM_APPLE struct pollfd* pfd = pollobj->pollfds; network_poll_slot_t* slot = pollobj->slots; for (size_t i = 0; i < pollobj->num_sockets; ++i, ++pfd, ++slot) { socket_t* sock = slot->sock; socket_base_t* sockbase = _socket_base + slot->base; int fd = slot->fd; if (pfd->revents & POLLIN) { if (sockbase->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if ((sockbase->state == SOCKETSTATE_CONNECTING) && (pfd->revents & POLLOUT)) { sockbase->state = SOCKETSTATE_CONNECTED; pfd->events = POLLIN | POLLERR | POLLHUP; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } if (pfd->revents & POLLERR) { pfd->events = POLLOUT | POLLERR | POLLHUP; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } if (pfd->revents & POLLHUP) { pfd->events = POLLOUT | POLLERR | POLLHUP; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } } #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID struct epoll_event* event = pollobj->events; for (int i = 0; i < num_polled; ++i, ++event) { FOUNDATION_ASSERT(pollobj->slots[ event->data.fd ].base >= 0); socket_t* sock = pollobj->slots[ event->data.fd ].sock; socket_base_t* sockbase = _socket_base + pollobj->slots[ event->data.fd ].base; int fd = pollobj->slots[ event->data.fd ].fd; if (event->events & EPOLLIN) { if (sockbase->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if ((sockbase->state == SOCKETSTATE_CONNECTING) && (event->events & EPOLLOUT)) { sockbase->state = SOCKETSTATE_CONNECTED; struct epoll_event mod_event; mod_event.events = EPOLLIN | EPOLLERR | EPOLLHUP; mod_event.data.fd = event->data.fd; epoll_ctl(pollobj->fd_poll, EPOLL_CTL_MOD, fd, &mod_event); network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } if (event->events & EPOLLERR) { struct epoll_event del_event; epoll_ctl(pollobj->fd_poll, EPOLL_CTL_DEL, fd, &del_event); network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } if (event->events & EPOLLHUP) { struct epoll_event del_event; epoll_ctl(pollobj->fd_poll, EPOLL_CTL_DEL, fd, &del_event); network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } } #elif FOUNDATION_PLATFORM_WINDOWS for (islot = 0; islot < pollobj->num_sockets; ++islot) { int fd = pollobj->slots[islot].fd; socket_t* sock = pollobj->slots[islot].sock; socket_base_t* sockbase = _socket_base + pollobj->slots[islot].base; if (sockbase->fd != fd) continue; if (FD_ISSET(fd, &fdread)) { if (sockbase->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { //SOCKETSTATE_CONNECTED network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if ((sockbase->state == SOCKETSTATE_CONNECTING) && FD_ISSET(fd, &fdwrite)) { sockbase->state = SOCKETSTATE_CONNECTED; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } if (FD_ISSET(fd, &fderr)) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } } #else # error Not implemented #endif return num_events; }
bool mutex_try_wait(mutex_t* mutex, unsigned int milliseconds) { #if FOUNDATION_PLATFORM_WINDOWS DWORD ret; #elif FOUNDATION_PLATFORM_POSIX || FOUNDATION_PLATFORM_PNACL struct timeval now; struct timespec then; #endif #if FOUNDATION_PLATFORM_WINDOWS #if !BUILD_DEPLOY profile_wait(STRING_ARGS(mutex->name)); #endif atomic_incr32(&mutex->waiting); ret = WaitForSingleObject(mutex->event, milliseconds); if (ret == WAIT_OBJECT_0) mutex_lock(mutex); if (atomic_decr32(&mutex->waiting) == 0) ResetEvent(mutex->event); return ret == WAIT_OBJECT_0; #elif FOUNDATION_PLATFORM_POSIX || FOUNDATION_PLATFORM_PNACL mutex_lock(mutex); if (mutex->pending) { mutex->pending = false; return true; } if (!milliseconds) { mutex_unlock(mutex); return false; } --mutex->lockcount; bool was_signal = false; if (milliseconds == 0xFFFFFFFF) { int ret = pthread_cond_wait(&mutex->cond, &mutex->mutex); if (ret == 0) { was_signal = true; } else { string_const_t errmsg = system_error_message(ret); log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to wait on mutex '%.*s': %.*s (%d)"), STRING_FORMAT(mutex->name), STRING_FORMAT(errmsg), ret); } } else { int ret; gettimeofday(&now, 0); then.tv_sec = now.tv_sec + (time_t)(milliseconds / 1000); then.tv_nsec = (now.tv_usec * 1000) + (long)(milliseconds % 1000) * 1000000L; while (then.tv_nsec >= 1000000000L) { ++then.tv_sec; then.tv_nsec -= 1000000000L; } ret = pthread_cond_timedwait(&mutex->cond, &mutex->mutex, &then); if (ret == 0) { was_signal = true; } else if (ret != ETIMEDOUT) { string_const_t errmsg = system_error_message(ret); log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to wait (timed) on mutex '%.*s': %.*s (%d)"), STRING_FORMAT(mutex->name), STRING_FORMAT(errmsg), ret); } } ++mutex->lockcount; mutex->lockedthread = thread_id(); if (was_signal) mutex->pending = false; else mutex_unlock(mutex); return was_signal; #else # error mutex_wait not implemented #endif }
static void* _memory_reallocate_malloc(void* p, size_t size, unsigned int align, size_t oldsize) { #if ( FOUNDATION_SIZE_POINTER == 4 ) && FOUNDATION_PLATFORM_WINDOWS FOUNDATION_UNUSED(oldsize); align = _memory_get_align(align); # if BUILD_ENABLE_MEMORY_GUARD if (p) { p = _memory_guard_verify(p); p = _aligned_realloc(p, (size_t)size + FOUNDATION_MAX_ALIGN * 3, align); } else { p = _aligned_malloc((size_t)size + FOUNDATION_MAX_ALIGN * 3, align); } if (p) p = _memory_guard_initialize(p, (size_t)size); return p; # else return _aligned_realloc(p, (size_t)size, align); # endif #else void* memory; void* raw_p; align = _memory_get_align(align); memory = p; # if BUILD_ENABLE_MEMORY_GUARD if (memory) memory = _memory_guard_verify(memory); # endif raw_p = memory ? *((void**)memory - 1) : nullptr; memory = nullptr; #if FOUNDATION_PLATFORM_WINDOWS if (raw_p && !((uintptr_t)raw_p & 1)) { size_t padding = (align > FOUNDATION_SIZE_POINTER ? align : FOUNDATION_SIZE_POINTER); # if BUILD_ENABLE_MEMORY_GUARD size_t extra_padding = FOUNDATION_MAX_ALIGN * 3; # else size_t extra_padding = 0; # endif void* raw_memory = _aligned_realloc(raw_p, size + padding + extra_padding, align ? align : 8); if (raw_memory) { memory = pointer_offset(raw_memory, padding); *((void**)memory - 1) = raw_memory; # if BUILD_ENABLE_MEMORY_GUARD memory = _memory_guard_initialize(memory, size); # endif } } else { # if FOUNDATION_SIZE_POINTER == 4 memory = _memory_allocate_malloc_raw(size, align, 0U); # else memory = _memory_allocate_malloc_raw(size, align, (raw_p && ((uintptr_t)raw_p < 0xFFFFFFFFULL)) ? MEMORY_32BIT_ADDRESS : 0U); # endif if (p && memory && oldsize) memcpy(memory, p, (size < oldsize) ? size : oldsize); _memory_deallocate_malloc(p); } #else //!FOUNDATION_PLATFORM_WINDOWS //If we're on ARM the realloc can return a 16-bit aligned address, causing raw pointer store to SIGILL //Realigning does not work since the realloc memory copy preserve cannot be done properly. Revert to normal alloc-and-copy //Same with alignment, since we cannot guarantee that the returned memory block offset from start of actual memory block //is the same in the reallocated block as the original block, we need to alloc-and-copy to get alignment //Memory guard introduces implicit alignments as well so alloc-and-copy for that #if !FOUNDATION_ARCH_ARM && !FOUNDATION_ARCH_ARM_64 && !BUILD_ENABLE_MEMORY_GUARD if (!align && raw_p && !((uintptr_t)raw_p & 1)) { void* raw_memory = realloc(raw_p, (size_t)size + FOUNDATION_SIZE_POINTER); if (raw_memory) { *(void**)raw_memory = raw_memory; memory = pointer_offset(raw_memory, FOUNDATION_SIZE_POINTER); } } else #endif { # if FOUNDATION_SIZE_POINTER == 4 # if !BUILD_ENABLE_LOG FOUNDATION_UNUSED(raw_p); # endif memory = _memory_allocate_malloc_raw(size, align, 0U); # else memory = _memory_allocate_malloc_raw(size, align, (raw_p && ((uintptr_t)raw_p < 0xFFFFFFFFULL)) ? MEMORY_32BIT_ADDRESS : 0U); # endif if (p && memory && oldsize) memcpy(memory, p, (size < oldsize) ? (size_t)size : (size_t)oldsize); _memory_deallocate_malloc(p); } #endif if (!memory) { string_const_t errmsg = system_error_message(0); log_panicf(HASH_MEMORY, ERROR_OUT_OF_MEMORY, STRING_CONST("Unable to reallocate memory (%" PRIsize " -> %" PRIsize " @ 0x%" PRIfixPTR ", raw 0x%" PRIfixPTR "): %.*s"), oldsize, size, (uintptr_t)p, (uintptr_t)raw_p, STRING_FORMAT(errmsg)); } return memory; #endif }
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); }
void mutex_signal( mutex_t* mutex ) { FOUNDATION_ASSERT( mutex ); #if !BUILD_DEPLOY profile_signal( mutex->name ); #endif #if FOUNDATION_PLATFORM_WINDOWS SetEvent( mutex->event ); #elif FOUNDATION_PLATFORM_POSIX mutex_lock( mutex ); mutex->pending = true; int ret = pthread_cond_broadcast( &mutex->cond ); if( ret != 0 ) log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to signal mutex '%s': %s (%d)", mutex->name, system_error_message( ret ), ret ); mutex_unlock( mutex ); #else # error mutex_signal not implemented #endif }
bool mutex_wait( mutex_t* mutex, unsigned int timeout ) { #if FOUNDATION_PLATFORM_WINDOWS DWORD ret; #elif FOUNDATION_PLATFORM_POSIX struct timeval now; struct timespec then; #endif FOUNDATION_ASSERT( mutex ); #if FOUNDATION_PLATFORM_WINDOWS #if !BUILD_DEPLOY profile_wait( mutex->name ); #endif atomic_incr32( &mutex->waiting ); ret = WaitForSingleObject( mutex->event, ( timeout == 0 ) ? INFINITE : timeout ); if( ret == WAIT_OBJECT_0 ) mutex_lock( mutex ); if( atomic_decr32( &mutex->waiting ) == 0 ) ResetEvent( mutex->event ); return ret == WAIT_OBJECT_0; #elif FOUNDATION_PLATFORM_POSIX mutex_lock( mutex ); if( mutex->pending ) { mutex->pending = false; return true; } --mutex->lockcount; bool was_signal = false; if( !timeout ) { int ret = pthread_cond_wait( &mutex->cond, &mutex->mutex ); if( ret == 0 ) { was_signal = true; } else { log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to wait on mutex '%s': %s (%d)", mutex->name, system_error_message( ret ), ret ); } } else { int ret; gettimeofday( &now, 0 ); then.tv_sec = now.tv_sec + ( timeout / 1000 ); then.tv_nsec = ( now.tv_usec * 1000 ) + (long)( timeout % 1000 ) * 1000000L; while( then.tv_nsec > 999999999 ) { ++then.tv_sec; then.tv_nsec -= 1000000000L; } ret = pthread_cond_timedwait( &mutex->cond, &mutex->mutex, &then ); if( ret == 0 ) { was_signal = true; } else if( ret != ETIMEDOUT ) { log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to wait (timed) on mutex '%s': %s (%d)", mutex->name, system_error_message( ret ), ret ); } } ++mutex->lockcount; mutex->lockedthread = thread_id(); if( was_signal ) mutex->pending = false; else mutex_unlock( mutex ); return was_signal; #else # error mutex_wait not implemented #endif }
size_t network_poll(network_poll_t* pollobj, network_poll_event_t* events, size_t capacity, unsigned int timeoutms) { int avail = 0; size_t num_events = 0; #if FOUNDATION_PLATFORM_WINDOWS //TODO: Refactor to keep fd_set across loop and rebuild on change (add/remove) int num_fd = 0; int ret = 0; size_t islot; fd_set fdread, fdwrite, fderr; #endif if (!pollobj->num_sockets) return num_events; #if FOUNDATION_PLATFORM_APPLE int ret = poll(pollobj->pollfds, (nfds_t)pollobj->num_sockets, (int)timeoutms); #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID int ret = epoll_wait(pollobj->fd_poll, pollobj->events, (int)pollobj->num_sockets + 1, (int)timeoutms); int num_polled = ret; #elif FOUNDATION_PLATFORM_WINDOWS FD_ZERO(&fdread); FD_ZERO(&fdwrite); FD_ZERO(&fderr); for (islot = 0; islot < pollobj->num_sockets; ++islot) { int fd = pollobj->slots[islot].fd; if (fd != NETWORK_SOCKET_INVALID) { socket_t* sock = pollobj->slots[islot].sock; FD_SET(fd, &fdread); if (sock->state == SOCKETSTATE_CONNECTING) FD_SET(fd, &fdwrite); FD_SET(fd, &fderr); if (fd >= num_fd) num_fd = fd + 1; } } if (!num_fd) { return num_events; } else { struct timeval tv; tv.tv_sec = timeoutms / 1000; tv.tv_usec = (timeoutms % 1000) * 1000; ret = select(num_fd, &fdread, &fdwrite, &fderr, (timeoutms != NETWORK_TIMEOUT_INFINITE) ? &tv : nullptr); } #else # error Not implemented #endif if (ret < 0) { int err = NETWORK_SOCKET_ERROR; string_const_t errmsg = system_error_message(err); log_warnf(HASH_NETWORK, WARNING_SUSPICIOUS, STRING_CONST("Error in socket poll: %.*s (%d)"), STRING_FORMAT(errmsg), err); if (!avail) return num_events; ret = avail; } if (!avail && !ret) return num_events; #if FOUNDATION_PLATFORM_APPLE struct pollfd* pfd = pollobj->pollfds; network_poll_slot_t* slot = pollobj->slots; for (size_t islot = 0; islot < pollobj->num_sockets; ++islot, ++pfd, ++slot) { socket_t* sock = slot->sock; bool update_slot = false; bool had_error = false; if (pfd->revents & POLLERR) { update_slot = true; had_error = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } if (pfd->revents & POLLHUP) { update_slot = true; had_error = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } if (!had_error && (pfd->revents & POLLIN)) { if (sock->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if (!had_error && (sock->state == SOCKETSTATE_CONNECTING) && (pfd->revents & POLLOUT)) { int serr = 0; socklen_t slen = sizeof(int); getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, (void*)&serr, &slen); if (!serr) { sock->state = SOCKETSTATE_CONNECTED; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } else { had_error = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } update_slot = true; } if (update_slot) network_poll_update_slot(pollobj, islot, sock); } #elif FOUNDATION_PLATFORM_LINUX || FOUNDATION_PLATFORM_ANDROID struct epoll_event* event = pollobj->events; for (int i = 0; i < num_polled; ++i, ++event) { socket_t* sock = pollobj->slots[ event->data.fd ].sock; bool update_slot = false; bool had_error = false; if (event->events & EPOLLERR) { update_slot = true; had_error = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } if (event->events & EPOLLHUP) { update_slot = true; had_error = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } if (!had_error && (event->events & EPOLLIN)) { if (sock->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if (!had_error && (sock->state == SOCKETSTATE_CONNECTING) && (event->events & EPOLLOUT)) { int serr = 0; socklen_t slen = sizeof(int); getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, (void*)&serr, &slen); if (!serr) { sock->state = SOCKETSTATE_CONNECTED; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } else { had_error = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_ERROR, sock); socket_close(sock); } update_slot = true; } if (update_slot) network_poll_update_slot(pollobj, (size_t)event->data.fd, sock); } #elif FOUNDATION_PLATFORM_WINDOWS for (islot = 0; islot < pollobj->num_sockets; ++islot) { int fd = pollobj->slots[islot].fd; socket_t* sock = pollobj->slots[islot].sock; bool update_slot = false; if (FD_ISSET(fd, &fdread)) { if (sock->state == SOCKETSTATE_LISTENING) { network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTION, sock); } else { //SOCKETSTATE_CONNECTED network_poll_push_event(events, capacity, num_events, NETWORKEVENT_DATAIN, sock); } } if ((sock->state == SOCKETSTATE_CONNECTING) && FD_ISSET(fd, &fdwrite)) { update_slot = true; sock->state = SOCKETSTATE_CONNECTED; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_CONNECTED, sock); } if (FD_ISSET(fd, &fderr)) { update_slot = true; network_poll_push_event(events, capacity, num_events, NETWORKEVENT_HANGUP, sock); socket_close(sock); } if (update_slot) network_poll_update_slot(pollobj, islot, sock); } #else # error Not implemented #endif return num_events; }
int process_wait(process_t* proc) { #if FOUNDATION_PLATFORM_POSIX int cstatus; pid_t ret; #endif #if FOUNDATION_PLATFORM_WINDOWS if (!proc->hp) return proc->code; while (GetExitCodeProcess(proc->hp, (LPDWORD)&proc->code)) { if ((proc->code != (int)STILL_ACTIVE) || (proc->flags & PROCESS_DETACHED)) break; thread_sleep(50); proc->code = -1; } if ((proc->code == (int)STILL_ACTIVE) && (proc->flags & PROCESS_DETACHED)) return PROCESS_STILL_ACTIVE; if (proc->ht) CloseHandle(proc->ht); if (proc->hp) CloseHandle(proc->hp); proc->hp = 0; proc->ht = 0; #elif FOUNDATION_PLATFORM_POSIX if (!proc->pid) return proc->code; # if FOUNDATION_PLATFORM_MACOSX if (proc->flags & PROCESS_MACOSX_USE_OPENAPPLICATION) { if (proc->kq) { struct kevent event; ret = kevent(proc->kq, 0, 0, &event, 1, 0); if (ret != 1) { int err = errno; string_const_t errmsg = system_error_message(err); log_warnf(0, WARNING_SYSTEM_CALL_FAIL, STRING_CONST("Unable to wait on process, failed to read event from kqueue: %.*s (%d)"), STRING_FORMAT(errmsg), err); } close(proc->kq); proc->kq = 0; } else { log_warn(0, WARNING_INVALID_VALUE, STRING_CONST("Unable to wait on a process started with PROCESS_MACOSX_USE_OPENAPPLICATION and no kqueue")); return PROCESS_WAIT_FAILED; } proc->pid = 0; proc->code = 0; return proc->code; } # endif cstatus = 0; ret = waitpid(proc->pid, &cstatus, (proc->flags & PROCESS_DETACHED) ? WNOHANG : 0); if (ret > 0) { if (WIFEXITED(cstatus)) proc->code = (int)((char)WEXITSTATUS(cstatus)); else if (WIFSIGNALED(cstatus)) { proc->code = PROCESS_TERMINATED_SIGNAL; #ifdef WCOREDUMP //if( WCOREDUMP( cstatus ) ) //... #endif //proc->signal = WTERMSIG( cstatus ); } else { proc->code = PROCESS_WAIT_FAILED; } proc->pid = 0; } else { int err = errno; if ((ret == 0) && (proc->flags & PROCESS_DETACHED)) return PROCESS_STILL_ACTIVE; if ((ret < 0) && (err == EINTR)) return PROCESS_WAIT_INTERRUPTED; string_const_t errmsg = system_error_message(err); log_warnf(0, WARNING_INVALID_VALUE, STRING_CONST("waitpid(%d) failed: %.*s (%d) (returned %d)"), proc->pid, STRING_FORMAT(errmsg), err, ret); return PROCESS_WAIT_FAILED; } #elif FOUNDATION_PLATFORM_PNACL //Not supported #else #error Not implemented #endif return proc->code; }
static void* _memory_allocate_malloc_raw(size_t size, unsigned int align, unsigned int hint) { FOUNDATION_UNUSED(hint); //If we align manually, we must be able to retrieve the original pointer for passing to free() //Thus all allocations need to go through that path #if FOUNDATION_PLATFORM_WINDOWS # if FOUNDATION_SIZE_POINTER == 4 # if BUILD_ENABLE_MEMORY_GUARD char* memory = _aligned_malloc((size_t)size + FOUNDATION_MAX_ALIGN * 3, align); if (memory) memory = _memory_guard_initialize(memory, (size_t)size); return memory; # else return _aligned_malloc((size_t)size, align); # endif # else unsigned int padding, extra_padding = 0; size_t allocate_size; char* raw_memory; void* memory; long vmres; if (!(hint & MEMORY_32BIT_ADDRESS)) { padding = (align > FOUNDATION_SIZE_POINTER ? align : FOUNDATION_SIZE_POINTER); #if BUILD_ENABLE_MEMORY_GUARD extra_padding = FOUNDATION_MAX_ALIGN * 3; #endif raw_memory = _aligned_malloc((size_t)size + padding + extra_padding, align); if (raw_memory) { memory = raw_memory + padding; //Will be aligned since padding is multiple of alignment (minimum align/pad is pointer size) *((void**)memory - 1) = raw_memory; FOUNDATION_ASSERT(!((uintptr_t)raw_memory & 1)); FOUNDATION_ASSERT(!((uintptr_t)memory & 1)); #if BUILD_ENABLE_MEMORY_GUARD memory = _memory_guard_initialize(memory, size); FOUNDATION_ASSERT(!((uintptr_t)memory & 1)); #endif return memory; } log_errorf(HASH_MEMORY, ERROR_OUT_OF_MEMORY, STRING_CONST("Unable to allocate %" PRIsize " bytes of memory"), size); return 0; } # if BUILD_ENABLE_MEMORY_GUARD extra_padding = FOUNDATION_MAX_ALIGN * 3; # endif allocate_size = size + FOUNDATION_SIZE_POINTER + extra_padding + align; raw_memory = 0; vmres = NtAllocateVirtualMemory(INVALID_HANDLE_VALUE, &raw_memory, 1, &allocate_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (vmres != 0) { log_errorf(HASH_MEMORY, ERROR_OUT_OF_MEMORY, STRING_CONST("Unable to allocate %" PRIsize " bytes of memory in low 32bit address space"), size); return 0; } memory = _memory_align_pointer(raw_memory + FOUNDATION_SIZE_POINTER, align); *((void**)memory - 1) = (void*)((uintptr_t)raw_memory | 1); # if BUILD_ENABLE_MEMORY_GUARD memory = _memory_guard_initialize(memory, size); # endif FOUNDATION_ASSERT(!((uintptr_t)raw_memory & 1)); FOUNDATION_ASSERT(!((uintptr_t)memory & 1)); return memory; # endif #else # if FOUNDATION_SIZE_POINTER > 4 if (!(hint & MEMORY_32BIT_ADDRESS)) # endif { #if BUILD_ENABLE_MEMORY_GUARD size_t extra_padding = FOUNDATION_MAX_ALIGN * 3; #else size_t extra_padding = 0; #endif size_t allocate_size = size + align + FOUNDATION_SIZE_POINTER + extra_padding; char* raw_memory = malloc(allocate_size); if (raw_memory) { void* memory = _memory_align_pointer(raw_memory + FOUNDATION_SIZE_POINTER, align); *((void**)memory - 1) = raw_memory; FOUNDATION_ASSERT(!((uintptr_t)raw_memory & 1)); FOUNDATION_ASSERT(!((uintptr_t)memory & 1)); #if BUILD_ENABLE_MEMORY_GUARD memory = _memory_guard_initialize(memory, size); FOUNDATION_ASSERT(!((uintptr_t)memory & 1)); #endif return memory; } log_errorf(HASH_MEMORY, ERROR_OUT_OF_MEMORY, STRING_CONST("Unable to allocate %" PRIsize " bytes of memory (%" PRIsize " requested)"), size, allocate_size); return 0; } # if FOUNDATION_SIZE_POINTER > 4 size_t allocate_size; char* raw_memory; void* memory; # if BUILD_ENABLE_MEMORY_GUARD unsigned int extra_padding = FOUNDATION_MAX_ALIGN * 3; #else unsigned int extra_padding = 0; # endif allocate_size = size + align + FOUNDATION_SIZE_POINTER * 2 + extra_padding; #ifndef MAP_UNINITIALIZED #define MAP_UNINITIALIZED 0 #endif #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif # ifndef MAP_32BIT //On MacOSX app needs to be linked with -pagezero_size 10000 -image_base 100000000 to // 1) Free up low 4Gb address range by reducing page zero size // 2) Move executable base address above 4Gb to free up more memory address space #define MMAP_REGION_START ((uintptr_t)0x10000) #define MMAP_REGION_END ((uintptr_t)0x80000000) static atomicptr_t baseaddr = { (void*)MMAP_REGION_START }; bool retried = false; do { raw_memory = mmap(atomic_loadptr(&baseaddr), allocate_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_UNINITIALIZED, -1, 0); if (((uintptr_t)raw_memory >= MMAP_REGION_START) && (uintptr_t)(raw_memory + allocate_size) < MMAP_REGION_END) { atomic_storeptr(&baseaddr, pointer_offset(raw_memory, allocate_size)); break; } if (raw_memory && (raw_memory != MAP_FAILED)) { if (munmap(raw_memory, allocate_size) < 0) log_warn(HASH_MEMORY, WARNING_SYSTEM_CALL_FAIL, STRING_CONST("Failed to munmap pages outside 32-bit range")); } raw_memory = 0; if (retried) break; retried = true; atomic_storeptr(&baseaddr, (void*)MMAP_REGION_START); } while (true); # else raw_memory = mmap(0, allocate_size, PROT_READ | PROT_WRITE, MAP_32BIT | MAP_PRIVATE | MAP_ANONYMOUS | MAP_UNINITIALIZED, -1, 0); if (raw_memory == MAP_FAILED) { raw_memory = mmap(0, allocate_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_UNINITIALIZED, -1, 0); if (raw_memory == MAP_FAILED) raw_memory = 0; if ((uintptr_t)raw_memory > 0xFFFFFFFFULL) { if (munmap(raw_memory, allocate_size) < 0) log_warn(HASH_MEMORY, WARNING_SYSTEM_CALL_FAIL, STRING_CONST("Failed to munmap pages outside 32-bit range")); raw_memory = 0; } } # endif if (!raw_memory) { string_const_t errmsg = system_error_message(0); log_errorf(HASH_MEMORY, ERROR_OUT_OF_MEMORY, STRING_CONST("Unable to allocate %" PRIsize " bytes of memory in low 32bit address space: %.*s"), size, STRING_FORMAT(errmsg)); return 0; } memory = _memory_align_pointer(raw_memory + FOUNDATION_SIZE_POINTER * 2, align); *((uintptr_t*)memory - 1) = ((uintptr_t)raw_memory | 1); *((uintptr_t*)memory - 2) = (uintptr_t)allocate_size; FOUNDATION_ASSERT(!((uintptr_t)raw_memory & 1)); FOUNDATION_ASSERT(!((uintptr_t)memory & 1)); # if BUILD_ENABLE_MEMORY_GUARD memory = _memory_guard_initialize(memory, size); FOUNDATION_ASSERT(!((uintptr_t)memory & 1)); # endif return memory; # endif #endif }
int process_wait( process_t* proc ) { #if FOUNDATION_PLATFORM_POSIX int cstatus; pid_t err; #endif if( !proc ) return PROCESS_INVALID_ARGS; #if FOUNDATION_PLATFORM_WINDOWS if( !proc->hp ) return proc->code; while( GetExitCodeProcess( proc->hp, (LPDWORD)&proc->code ) ) { if( ( proc->code != STILL_ACTIVE ) || ( proc->flags & PROCESS_DETACHED ) ) break; thread_sleep( 50 ); proc->code = -1; } if( ( proc->code == STILL_ACTIVE ) && ( proc->flags & PROCESS_DETACHED ) ) return PROCESS_STILL_ACTIVE; if( proc->ht ) CloseHandle( proc->ht ); if( proc->hp ) CloseHandle( proc->hp ); proc->hp = 0; proc->ht = 0; #elif FOUNDATION_PLATFORM_POSIX if( !proc->pid ) return proc->code; # if FOUNDATION_PLATFORM_MACOSX if( proc->flags & PROCESS_OSX_USE_OPENAPPLICATION ) { if( proc->kq ) { struct kevent event; int ret = kevent( proc->kq, 0, 0, &event, 1, 0 ); if( ret != 1 ) log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to wait on process, failed to read event from kqueue (%d)", ret ); close( proc->kq ); proc->kq = 0; } else { log_warnf( 0, WARNING_BAD_DATA, "Unable to wait on a process started with PROCESS_OSX_USE_OPENAPPLICATION and no kqueue" ); return PROCESS_WAIT_FAILED; } proc->pid = 0; proc->code = 0; return proc->code; } # endif cstatus = 0; err = waitpid( proc->pid, &cstatus, ( proc->flags & PROCESS_DETACHED ) ? WNOHANG : 0 ); if( err > 0 ) { if( WIFEXITED( cstatus ) ) proc->code = (int)((char)WEXITSTATUS( cstatus )); else if( WIFSIGNALED( cstatus ) ) { proc->code = PROCESS_TERMINATED_SIGNAL; #ifdef WCOREDUMP //if( WCOREDUMP( cstatus ) ) //... #endif //proc->signal = WTERMSIG( cstatus ); } proc->pid = 0; } else { int cur_errno = errno; if( ( err == 0 ) && ( proc->flags & PROCESS_DETACHED ) ) return PROCESS_STILL_ACTIVE; if( ( err < 0 ) && ( cur_errno == EINTR ) ) return PROCESS_WAIT_INTERRUPTED; log_warnf( 0, WARNING_BAD_DATA, "waitpid(%d) failed: %s (%d) (returned %d)", proc->pid, system_error_message( cur_errno ), cur_errno, err ); return PROCESS_WAIT_FAILED; } #else #error Not implemented #endif return proc->code; }
object_t library_load( const char* name ) { library_t* library; hash_t namehash; unsigned int i, size; uint64_t id; #if FOUNDATION_PLATFORM_WINDOWS char* dllname; HANDLE dll; #endif //Locate already loaded library library = 0; namehash = string_hash( name ); for( i = 0, size = objectmap_size( _library_map ); i < size; ++i ) { library = objectmap_raw_lookup( _library_map, i ); if( library && ( library->namehash == namehash ) ) { FOUNDATION_ASSERT( string_equal( library->name, name ) ); atomic_incr32( &library->ref ); return library->id; } } error_context_push( "loading library", name ); //Try loading library #if FOUNDATION_PLATFORM_WINDOWS dllname = string_format( "%s.dll", name ); dll = LoadLibraryA( dllname ); if( !dll ) { #if FOUNDATION_PLATFORM_ARCH_X86 string_deallocate( dllname ); dllname = string_format( "%s32.dll", name ); dll = LoadLibraryA( dllname ); #elif FOUNDATION_PLATFORM_ARCH_X86_64 string_deallocate( dllname ); dllname = string_format( "%s64.dll", name ); dll = LoadLibraryA( dllname ); #endif } string_deallocate( dllname ); if( !dll ) { log_warnf( 0, WARNING_SUSPICIOUS, "Unable to load DLL '%s': %s", name, system_error_message( 0 ) ); error_context_pop(); return 0; } #elif FOUNDATION_PLATFORM_POSIX # if FOUNDATION_PLATFORM_APPLE char* libname = string_format( "lib%s.dylib", name ); # else char* libname = string_format( "lib%s.so", name ); # endif void* lib = dlopen( libname, RTLD_LAZY ); string_deallocate( libname ); #if FOUNDATION_PLATFORM_ANDROID if( !lib ) { libname = string_format( "%s/lib%s.so", environment_executable_directory(), name ); lib = dlopen( libname, RTLD_LAZY ); string_deallocate( libname ); } #endif if( !lib ) { log_warnf( 0, WARNING_SUSPICIOUS, "Unable to load dynamic library '%s': %s", name, dlerror() ); error_context_pop(); return 0; } #else log_errorf( 0, ERROR_NOT_IMPLEMENTED, "Dynamic library loading not implemented for this platform: %s", name ); error_context_pop(); return 0; #endif id = objectmap_reserve( _library_map ); if( !id ) { #if FOUNDATION_PLATFORM_WINDOWS FreeLibrary( dll ); #elif FOUNDATION_PLATFORM_POSIX dlclose( lib ); #endif log_errorf( 0, ERROR_OUT_OF_MEMORY, "Unable to allocate new library '%s', map full", name ); error_context_pop(); return 0; } library = memory_allocate_zero( sizeof( library_t ), 0, MEMORY_PERSISTENT ); library->ref = 1; library->id = id; library->namehash = string_hash( name ); string_copy( library->name, name, 32 ); #if FOUNDATION_PLATFORM_WINDOWS library->dll = dll; #elif FOUNDATION_PLATFORM_POSIX library->lib = lib; #endif objectmap_set( _library_map, id, library ); error_context_pop(); return library->id; }