int main(int argc, char **argv) { if (argc < 2) { printf("Need .beam file\n"); return EXIT_FAILURE; } MappedFile *mapped_file = mapped_file_open_beam(argv[1]); if (IS_NULL_PTR(mapped_file)) { return EXIT_FAILURE; } GlobalContext *glb = globalcontext_new(); const void *startup_beam; uint32_t startup_beam_size; const char *startup_module_name = argv[1]; if (avmpack_is_valid(mapped_file->mapped, mapped_file->size)) { glb->avmpack_data = mapped_file->mapped; glb->avmpack_platform_data = mapped_file; if (!avmpack_find_section_by_flag(mapped_file->mapped, 1, &startup_beam, &startup_beam_size, &startup_module_name)) { fprintf(stderr, "%s cannot be started.\n", argv[1]); mapped_file_close(mapped_file); return EXIT_FAILURE; } } else if (iff_is_valid_beam(mapped_file->mapped)) { glb->avmpack_data = NULL; glb->avmpack_platform_data = NULL; startup_beam = mapped_file->mapped; startup_beam_size = mapped_file->size; } else { fprintf(stderr, "%s is not a BEAM file.\n", argv[1]); mapped_file_close(mapped_file); return EXIT_FAILURE; } Module *mod = module_new_from_iff_binary(glb, startup_beam, startup_beam_size); if (IS_NULL_PTR(mod)) { fprintf(stderr, "Cannot load startup module: %s\n", startup_module_name); return EXIT_FAILURE; } globalcontext_insert_module_with_filename(glb, mod, startup_module_name); mod->module_platform_data = NULL; Context *ctx = context_new(glb); ctx->leader = 1; context_execute_loop(ctx, mod, "start", 0); printf("Return value: %lx\n", ctx->x[0]); context_destroy(ctx); globalcontext_destroy(glb); module_destroy(mod); mapped_file_close(mapped_file); return EXIT_SUCCESS; }
Module *sys_load_module(GlobalContext *global, const char *module_name) { TRACE("sys_load_module: Going to load: %s\n", module_name); const void *beam_module = NULL; uint32_t beam_module_size = 0; MappedFile *beam_file = NULL; if (!(global->avmpack_data && avmpack_find_section_by_name(global->avmpack_data, module_name, &beam_module, &beam_module_size))) { beam_file = mapped_file_open_beam(module_name); if (UNLIKELY(!iff_is_valid_beam(beam_file->mapped))) { fprintf(stderr, "%s is not a valid BEAM file.\n", module_name); } beam_module = beam_file->mapped; beam_module_size = beam_file->size; } Module *new_module = module_new_from_iff_binary(global, beam_module, beam_module_size); if (IS_NULL_PTR(new_module)) { return NULL; } new_module->module_platform_data = beam_file; return new_module; }
static void process_console_mailbox(Context *ctx) { Message *msg = mailbox_dequeue(ctx); term pid = term_get_tuple_element(msg->message, 0); term val = term_get_tuple_element(msg->message, 1); int local_process_id = term_to_local_process_id(pid); Context *target = globalcontext_get_process(ctx->global, local_process_id); char *str = interop_term_to_string(val); if (IS_NULL_PTR(str)) { free(msg); return; } printf("%s", str); free(str); //TODO: use globalcontext_get_atom_id(ctx->global, ok_atom); int ok_index = globalcontext_insert_atom(ctx->global, ok_atom); mailbox_send(target, term_from_atom_index(ok_index)); free(msg); }
//----------------------------------------------------------// // CFileManager::Instance //----------------------------------------------------------// //-- Description //----------------------------------------------------------// const CFileManager* CFileManager::Instance(void) { if (IS_NULL_PTR(m_pSingleton)) { m_pSingleton = new CFileManager(); } return m_pSingleton; }
//----------------------------------------------------------// // CPacket::GetMessages //----------------------------------------------------------// CPacket::Error::Enum CPacket::GetMessages(TMessageList& tempList) { //-- DataBuffer should have exactly m_HeaderV1.m_nMessages inside it. tempList.clear(); u16 nProcessedMessages = 0; CPacketSerializer messageDeserializer(ISerializer::Mode::Deserializing, m_DataBuffer.Buffer(), m_DataBuffer.UsedSize()); CPacketSerializer::Error::Enum eSerError = CPacketSerializer::Error::Ok; while ( (CPacketSerializer::Error::Ok == eSerError) && (messageDeserializer.GetOffset() < messageDeserializer.GetSize()) ) { CMessage::Type nMessageType = CMessage::kTypeUnknown; if (IS_ZERO(messageDeserializer.SerializeU32(nMessageType, 'type'))) { return Error::Serializer; } CMessage* pMessage = CMessageFactory::CreateType(nMessageType); if (IS_NULL_PTR(pMessage)) { return Error::ProtocolMismatch; } pMessage->Serialize(messageDeserializer); eSerError = messageDeserializer.GetError(); if (CPacketSerializer::Error::Ok != eSerError) { return Error::Serializer; } SysSmartPtr<CMessage> pSmart(pMessage); tempList.push_back(pSmart); ++nProcessedMessages; } //-- Sanity tests //-- messageSerializer should have consumed all its bytes. assert(messageDeserializer.GetOffset() == messageDeserializer.GetSize()); if (messageDeserializer.GetOffset() != messageDeserializer.GetSize()) { return Error::SanityFail; } //-- We should have processed exactly the right number of messages. assert(nProcessedMessages == GetMessageCount()); if (nProcessedMessages != GetMessageCount()) { return Error::SanityFail; } return Error::Ok; }
//----------------------------------------------------------// // CFileDirectTextReader::GetString //----------------------------------------------------------// //-- Description // Read a string from an open direct access file. // A string is determined as terminated by a newline // character or the end of file. Any trailing newline will be // stripped from the output string. //----------------------------------------------------------// IFixedString& CFileDirectTextReader::GetString(IFixedString& strString) { if (IS_TRUE(Validate()) && IS_TRUE(IsOpen()) ) { if (IS_NULL_PTR(SysFileIO::Fgets(m_pFile, strString.Buffer(), strString.Size()))) { //-- fgets got nothing. Mirror that in the FixedString. strString.Clear(); } } return strString; }
static term nif_erlang_open_port_2(Context *ctx, int argc, term argv[]) { if (UNLIKELY(argc != 2)) { fprintf(stderr, "wrong arity\n"); abort(); } term port_name = argv[0]; term opts = argv[1]; if (!(term_is_tuple(port_name) && term_get_tuple_arity(port_name) == 2) && !term_is_nonempty_list(opts)) { fprintf(stderr, "bad args\n"); abort(); } term t = term_get_tuple_element(port_name, 1); char *driver_name = interop_term_to_string(t); if (IS_NULL_PTR(driver_name)) { int error_index = globalcontext_insert_atom(ctx->global, error_atom); if (error_index < 0) { abort(); } return term_from_atom_index(error_index); } Context *new_ctx = NULL; if (!strcmp("echo", driver_name)) { new_ctx = context_new(ctx->global); new_ctx->native_handler = process_echo_mailbox; } else if (!strcmp("console", driver_name)) { new_ctx = context_new(ctx->global); new_ctx->native_handler = process_console_mailbox; } if (!new_ctx) { new_ctx = platform_open_port(ctx->global, driver_name, opts); } free(driver_name); scheduler_make_waiting(ctx->global, new_ctx); return term_from_local_process_id(new_ctx->process_id); }
static term binary_to_atom(Context *ctx, int argc, term argv[], int create_new) { if (argc != 2) { fprintf(stderr, "binary_to_atom: wrong args count\n"); abort(); } if (UNLIKELY(argv[1] != term_from_atom_string(ctx->global, latin1_atom))) { fprintf(stderr, "binary_to_atom: only latin1 is supported.\n"); abort(); } char *atom_string = interop_binary_to_string(argv[0]); if (IS_NULL_PTR(atom_string)) { fprintf(stderr, "Failed to alloc temporary string\n"); abort(); } int atom_string_len = strlen(atom_string); if (UNLIKELY(atom_string_len > 255)) { fprintf(stderr, "Too long atom.\n"); free(atom_string); abort(); } AtomString atom = malloc(atom_string_len + 1); ((uint8_t *) atom)[0] = atom_string_len; memcpy(((char *) atom) + 1, atom_string, atom_string_len); unsigned long global_atom_index = atomshashtable_get_value(ctx->global->atoms_table, atom, ULONG_MAX); int has_atom = (global_atom_index != ULONG_MAX); if (create_new || has_atom) { if (!has_atom) { global_atom_index = globalcontext_insert_atom(ctx->global, atom); } else { free((void *) atom); } return term_from_atom_index(global_atom_index); } else { free((void *) atom); //TODO: error here return term_nil(); } }
int test_modules_execution() { struct Test *test = tests; if (chdir("erlang_tests")) { return EXIT_FAILURE; } do { printf("-- EXECUTING TEST: %s\n", test->test_file); MappedFile *beam_file = mapped_file_open_beam(test->test_file); assert(beam_file != NULL); GlobalContext *glb = globalcontext_new(); glb->avmpack_data = NULL; glb->avmpack_platform_data = NULL; Module *mod = module_new_from_iff_binary(glb, beam_file->mapped, beam_file->size); if (IS_NULL_PTR(mod)) { fprintf(stderr, "Cannot load startup module: %s\n", test->test_file); test++; continue; } globalcontext_insert_module_with_filename(glb, mod, test->test_file); Context *ctx = context_new(glb); ctx->leader = 1; context_execute_loop(ctx, mod, "start", 0); int32_t value = term_to_int32(ctx->x[0]); if (value != test->expected_value) { fprintf(stderr, "\x1b[1;31mFailed test module %s, got value: %i\x1b[0m\n", test->test_file, value); } context_destroy(ctx); globalcontext_destroy(glb); module_destroy(mod); mapped_file_close(beam_file); test++; } while (test->test_file); return EXIT_SUCCESS; }
//----------------------------------------------------------// // CTCPNonblockingConnector::Update //----------------------------------------------------------// //-- Description // Update the connector, opening a socket, calling // connect(), etc. // Note that the result contains a Non-blocking // CTCPConnection. //----------------------------------------------------------// CTCPConnector::Result CTCPNonblockingConnector::Update(void) { Result result; Error::Enum eError = Error::InProgress; switch (m_eState) { default: { eError = Error::WrongState; } break; case State::GettingAddrInfo: { m_pCur = m_pAddrResults; m_eState = State::NextAddr; } break; case State::NextAddr: { if (IS_NULL_PTR(m_pCur)) { eError = Error::NoMoreInfo; break; } m_nSocket = SysSocket::OpenSocket(m_pCur->ai_family, m_pCur->ai_socktype, m_pCur->ai_protocol); if (SysSocket::INVALID_SOCK == m_nSocket) { eError = Error::OpenFail; break; } if (SYS_SOCKET_NO_ERROR != SysSocket::SetNonblocking(m_nSocket, true)) { eError = Error::SetSockOptFail; break; } //-- Success switch (SysSocket::Connect(m_nSocket, m_pCur->ai_addr, m_pCur->ai_addrlen)) { case SYS_SOCKET_IN_PROGRESS: { //-- Expected return value for a non-blocking connect m_pollFd.fd = m_nSocket; m_eState = State::Connecting; } break; case SYS_SOCKET_NO_ERROR: { //-- Connected immediately eError = Error::Ok; } break; default: { eError = Error::ConnectFail; } break; } } break; case State::Connecting: { //-- Wait for select/poll if (SysSocket::INVALID_SOCK == m_pollFd.fd) { break; } s32 nError = SysSocket::Poll(&m_pollFd, 1, 0); if (SYS_SOCKET_ERROR == nError) { //-- Error occurred in select. eError = Error::SelectFail; break; } if (TEST_FLAG(m_pollFd.revents, POLLOUT)) { s32 nVal; size_t nValSize = sizeof(nVal); s32 nErr = SysSocket::GetSockOpt(m_nSocket, SOL_SOCKET, SO_ERROR, &nVal, &nValSize); if (SYS_SOCKET_ERROR == nError) { //-- Error. eError = Error::GetSockOptFail; break; } if (SYS_SOCKET_NO_ERROR == nVal) { //-- Connected eError = Error::Ok; } else { //-- Some other kind of error on socket. eError = Error::ConnectFail; } } if (TEST_FLAG(m_pollFd.revents, POLLERR)) { //-- Error. eError = Error::ConnectFail; } } break; } switch (eError) { case Error::InProgress: { //-- Nothing to do result.m_eError = Error::InProgress; } break; case Error::Ok: { //-- Finished! if (SysSocket::INVALID_SOCK != m_nSocket) { result.m_pConnection = new CTCPConnection(m_nSocket); } result.m_eError = Error::Ok; CleanAddrResults(); m_eState = State::Connected; } break; case Error::WrongState: { result.m_eError = Error::WrongState; CleanAddrResults(); CleanSocket(); m_pollFd.fd = SysSocket::INVALID_SOCK; m_eState = State::NotConnected; } break; default: { //-- Any other kind of error. CleanSocket(); m_pollFd.fd = SysSocket::INVALID_SOCK; if (IS_PTR(m_pCur)) { m_pCur = m_pCur->ai_next; switch (m_eState) { case State::NextAddr: { //-- return error as still 'in progress' instead of actual error. result.m_eError = Error::InProgress; } break; case State::Connecting: { //-- return error as still 'in progress' instead of actual error. result.m_eError = Error::InProgress; //-- reset state to loop onto next next address m_eState = State::NextAddr; } break; default: { //-- Unexpected state! //-- This is a bad failure and will stop the connect process. result.m_eError = eError; CleanAddrResults(); m_eState = State::NotConnected; } break; } } else { //-- No more addresses to try. result.m_eError = eError; CleanAddrResults(); m_eState = State::NotConnected; } } } return result; }
extern void sys_waitevents(struct ListHead *listeners_list) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); EventListener *listeners = GET_LIST_ENTRY(listeners_list, EventListener, listeners_list_head); EventListener *last_listener = GET_LIST_ENTRY(listeners_list->prev, EventListener, listeners_list_head); int min_timeout = INT_MAX; int count = 0; //first: find maximum allowed sleep time, and count file descriptor listeners EventListener *listener = listeners; do { if (listener->expires) { int wait_ms = timespec_diff_to_ms(&listener->expiral_timestamp, &now); if (wait_ms <= 0) { min_timeout = 0; } else if (min_timeout > wait_ms) { min_timeout = wait_ms; } } if (listener->fd >= 0) { count++; } listener = GET_LIST_ENTRY(listener->listeners_list_head.next, EventListener, listeners_list_head); } while (listener != listeners); //second: use either poll or nanosleep if (count > 0) { struct pollfd *fds = calloc(count, sizeof(struct pollfd)); if (IS_NULL_PTR(fds)) { fprintf(stderr, "Cannot allocate memory for pollfd, aborting.\n"); abort(); } int poll_fd_index = 0; //build pollfd array EventListener *listener = listeners; do { if (listener->fd >= 0) { fds[poll_fd_index].fd = listener->fd; fds[poll_fd_index].events = POLLIN; fds[poll_fd_index].revents = 0; } poll_fd_index++; listener = GET_LIST_ENTRY(listener->listeners_list_head.next, EventListener, listeners_list_head); } while (listener != listeners); poll(fds, poll_fd_index, min_timeout); //check which event happened listener = listeners; do { EventListener *next_listener = GET_LIST_ENTRY(listener->listeners_list_head.next, EventListener, listeners_list_head); for (int i = 0; i < poll_fd_index; i++) { if ((fds[i].fd == listener->fd) && (fds[i].revents & fds[i].events)) { //it is completely safe to free a listener in the callback, we are going to not use it after this call listener->handler(listener); } } listener = next_listener; } while (listener != listeners); free(fds); //just need to wait for a certain timespan } else { struct timespec t; t.tv_sec = min_timeout / 1000; t.tv_nsec = (min_timeout % 1000) * 1000000; struct timespec rem; int nanosleep_result = nanosleep(&t, &rem); while (nanosleep_result == -1) { nanosleep_result = nanosleep(&rem, &rem); } } //third: execute handlers for expiered timers if (min_timeout != INT_MAX) { listener = listeners; clock_gettime(CLOCK_MONOTONIC, &now); do { EventListener *next_listener = GET_LIST_ENTRY(listener->listeners_list_head.next, EventListener, listeners_list_head); if (listener->expires) { int wait_ms = timespec_diff_to_ms(&listener->expiral_timestamp, &now); if (wait_ms <= 0) { //it is completely safe to free a listener in the callback, we are going to not use it after this call listener->handler(listener); //TODO check if one shot } } listener = next_listener; } while (listener != last_listener); } }
//----------------------------------------------------------// // CPacket::Serialize //----------------------------------------------------------// CPacket::Error::Enum CPacket::Serialize(CPacketSerializer& serializer) { if (ISerializer::Mode::Serializing == serializer.GetMode()) { //-- Serializing m_nVersion = Version::Current; if (IS_ZERO(serializer.SerializeU8(m_nVersion, 'pktV'))) { return Error::Serializer; } //-- Serializer must have at least the size of the header in bytes free to proceed size_t nUnusedSize = serializer.GetSize() - serializer.GetOffset(); if (nUnusedSize < GetHeaderSize()) { //-- Not enough bytes in serializer. return Error::Serializer; } //-- Version::V1 if ( IS_ZERO(serializer.SerializeU8(m_HeaderV1.m_nFlags, 'flgs')) || IS_ZERO(serializer.SerializeU16(m_HeaderV1.m_nMessages, 'mess')) ) { return Error::Serializer; } if (IS_TRUE(IsEncrypted())) { size_t nEncodedSize = SysString::Base64EncodedSize(m_DataBuffer.UsedSize()); if (IS_ZERO(nEncodedSize)) { //-- Failed. //-- Something went wrong with the Encode. return Error::EncryptionFailed; } m_HeaderV1.m_nDataSize = nEncodedSize; if (IS_ZERO(serializer.SerializeU16(m_HeaderV1.m_nDataSize, 'size'))) { return Error::Serializer; } //-- We need a spare byte for null terminating the encoded base64 string. //-- This is blegh! //-- Test for unused space in serializer. size_t nUnusedSize = serializer.GetSize() - serializer.GetOffset(); if (nUnusedSize < ((size_t)m_HeaderV1.m_nDataSize + 1)) { //-- Not enough bytes in serializer. return Error::Serializer; } //-- Notice we do not include the extra byte when we reserve space. u8* pReserved = serializer.SerializeReserve(m_HeaderV1.m_nDataSize); if (IS_NULL_PTR(pReserved)) { //-- Failed. return Error::Serializer; } //-- Valid encryption key? if (SysString::INVALID_KEY == m_Key) { return Error::EncryptionFailed; } //-- NOTE: this will actually put a null terminator beyond the end //-- of the pReserved block! But should be ok because we checked //-- if there was room enough in the unused size of the serializer //-- for it. //-- Whatever gets serialized next will overwrite the null terminator, //-- which is ok. nEncodedSize = SysString::KeyEncode( (s8*)pReserved, m_HeaderV1.m_nDataSize + 1, m_DataBuffer.Buffer(), m_DataBuffer.UsedSize(), m_Key); if (IS_ZERO(nEncodedSize)) { //-- Failed. //-- Something went wrong with the Encode. return Error::EncryptionFailed; } if (IS_NULL_PTR(m_DataBuffer.StripHead(NULL, m_DataBuffer.UsedSize()))) { //-- Failed. //-- DataBuffer wasn't full enough return Error::DataBufferEmpty; } } else { //-- Write directly into serializer. m_HeaderV1.m_nDataSize = m_DataBuffer.UsedSize(); if (IS_ZERO(serializer.SerializeU16(m_HeaderV1.m_nDataSize, 'size'))) { return Error::Serializer; } u8* pReserved = serializer.SerializeReserve(m_HeaderV1.m_nDataSize); if (IS_NULL_PTR(pReserved)) { //-- Failed. return Error::Serializer; } if (IS_NULL_PTR(SysMemory::Memcpy(pReserved, m_HeaderV1.m_nDataSize, m_DataBuffer.Buffer(), m_HeaderV1.m_nDataSize))) { //-- Failed. return Error::CopyFailed; } if (IS_NULL_PTR(m_DataBuffer.StripHead(NULL, m_HeaderV1.m_nDataSize))) { //-- Failed. //-- DataBuffer wasn't full enough return Error::DataBufferEmpty; } } //-- DataBuffer should now be empty. assert(IS_ZERO(m_DataBuffer.UsedSize())); assert(m_HeaderV1.m_nMessages > 0); assert(m_HeaderV1.m_nDataSize > 0); //-- End. } else { //-- Deserializing. SysMemory::Memclear(&m_HeaderV1, sizeof(m_HeaderV1)); m_DataBuffer.Clear(); //-- Read version number. if (IS_ZERO(serializer.SerializeU8(m_nVersion, 'pktV'))) { //-- Failed to read version number. return Error::Serializer; } //-- Serializer must be at least the size of the header in bytes to proceed size_t nUnreadSize = serializer.GetSize() - serializer.GetOffset(); if (nUnreadSize < GetHeaderSize()) { //-- Not enough bytes in serializer. return Error::Serializer; } //-- Read the header switch (m_nVersion) { case Version::V1: { if ( IS_ZERO(serializer.SerializeU8(m_HeaderV1.m_nFlags, 'flgs')) || IS_ZERO(serializer.SerializeU16(m_HeaderV1.m_nMessages, 'mess')) || IS_ZERO(serializer.SerializeU16(m_HeaderV1.m_nDataSize, 'size')) ) { return Error::Serializer; } if (Version::Unknown == Validate()) { return Error::ProtocolMismatch; } u8* pDataSrc = serializer.SerializeReserve(m_HeaderV1.m_nDataSize); if (IS_NULL_PTR(pDataSrc)) { //-- Failed. return Error::Serializer; } //-- Is packet encrypted? if (IS_TRUE(IsEncrypted())) { //-- Decrypt packet payload data to DataBuffer size_t nDecodedSize = SysString::Base64DecodedSize((const s8*)pDataSrc, m_HeaderV1.m_nDataSize); if (IS_ZERO(nDecodedSize)) { //-- Failed. //-- Something went wrong with the Decode. return Error::EncryptionFailed; } assert(m_DataBuffer.Size() >= nDecodedSize); u8* pDataDest = m_DataBuffer.InsTail(NULL, nDecodedSize); if (IS_NULL_PTR(pDataDest)) { //-- Failed. //-- DataBuffer is too full. return Error::DataBufferFull; } //-- Valid encryption key? if (SysString::INVALID_KEY == m_Key) { return Error::EncryptionFailed; } nDecodedSize = SysString::KeyDecode( pDataDest, nDecodedSize, (const s8*)pDataSrc, m_HeaderV1.m_nDataSize, m_Key); if (IS_ZERO(nDecodedSize)) { //-- Failed. //-- Something went wrong with the Decode. return Error::EncryptionFailed; } } else { //-- Read directly into buffer. assert(m_DataBuffer.Size() >= m_HeaderV1.m_nDataSize); if (IS_NULL_PTR(m_DataBuffer.InsTail(pDataSrc, m_HeaderV1.m_nDataSize))) { //-- Failed. //-- DataBuffer is too full. return Error::DataBufferFull; } } //-- DataBuffer should now have exactly m_HeaderV1.m_nMessages inside it. //-- End. } break; default: { //-- Unexpected version return Error::UnknownVersion; } break; } } return Error::Ok; }