C++ runtime: WIP: untested PLCObject implementation. Still missing tracing.
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Sat, 11 May 2024 19:27:28 +0200
changeset 3940 934bd46a7500
parent 3939 b73d6668eba3
child 3941 09aa8a10026c
C++ runtime: WIP: untested PLCObject implementation. Still missing tracing.
.gitignore
C_runtime/Makefile
C_runtime/PLCObject.cpp
C_runtime/PLCObject.hpp
C_runtime/README.md
C_runtime/blob.cpp
C_runtime/blob.hpp
C_runtime/md5.cpp
C_runtime/md5.hpp
C_runtime/posix_main.cpp
--- a/.gitignore	Fri Apr 26 09:45:02 2024 +0200
+++ b/.gitignore	Sat May 11 19:27:28 2024 +0200
@@ -7,3 +7,11 @@
 **/my_*.der
 **/my_*.pem
 tests/tools/Docker/requirements.txt
+**/management.json
+**/.secret
+
+doc/_build/**
+doc/locale/**
+
+C_runtime/**/*.d
+C_runtime/**/*.o
\ No newline at end of file
--- a/C_runtime/Makefile	Fri Apr 26 09:45:02 2024 +0200
+++ b/C_runtime/Makefile	Sat May 11 19:27:28 2024 +0200
@@ -7,13 +7,17 @@
 RUNTIME_ROOT = $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
 ERPC_ROOT ?= $(abspath $(RUNTIME_ROOT)/../../erpc)
 ERPC_C_ROOT = $(ERPC_ROOT)/erpc_c
+BEREMIZ_ROOT = $(abspath $(RUNTIME_ROOT)/..)
+MATIEC_ROOT ?= $(abspath $(RUNTIME_ROOT)/../../matiec)
 
 INCLUDES += $(ERPC_C_ROOT)/infra \
             $(ERPC_C_ROOT)/port \
             $(ERPC_C_ROOT)/setup \
             $(ERPC_C_ROOT)/transports \
             $(ERPC_ROOT)/test/common/config \
-            $(ERPC_ROOT)/erpcgen/src
+            $(ERPC_ROOT)/erpcgen/src \
+            $(BEREMIZ_ROOT)/targets \
+            $(MATIEC_ROOT)/lib/C
 
 INCLUDES := $(foreach includes, $(INCLUDES), -I $(includes))
 
@@ -42,6 +46,8 @@
             $(RUNTIME_ROOT)/erpc_PLCObject_client.cpp \
             $(RUNTIME_ROOT)/erpc_PLCObject_interface.cpp \
             $(RUNTIME_ROOT)/erpc_PLCObject_server.cpp \
+			$(RUNTIME_ROOT)/md5.cpp \
+			$(RUNTIME_ROOT)/blob.cpp \
 			$(RUNTIME_ROOT)/posix_main.cpp \
 			$(RUNTIME_ROOT)/PLCObject.cpp
 
--- a/C_runtime/PLCObject.cpp	Fri Apr 26 09:45:02 2024 +0200
+++ b/C_runtime/PLCObject.cpp	Sat May 11 19:27:28 2024 +0200
@@ -1,80 +1,341 @@
 
-#include <stdlib.h>
+#include <string.h>
+#include <filesystem>
+#include <dlfcn.h>
+#include <fstream>
+#include <iostream>
 
 #include "Logging.hpp"
 
 #include "PLCObject.hpp"
 
+#include "beremiz.h"
+
+
+// File name of the last transferred PLC md5 hex digest
+// with typo in the name, for compatibility with Python runtime
+#define LastTransferredPLC "lasttransferedPLC.md5"
+
+// File name of the extra files list
+#define ExtraFilesList "extra_files.txt"
+
+
+
+PLCObject::PLCObject(void)
+{
+}
+
 PLCObject::~PLCObject(void)
 {
 }
 
-uint32_t PLCObject::AppendChunkToBlob(const binary_t * data, const binary_t * blobID, binary_t * newBlobID)
-{
-    return 0;
-}
-
-uint32_t PLCObject::GetLogMessage(uint8_t level, uint32_t msgID, log_message * message)
-{
-    return 0;
-}
-
-uint32_t PLCObject::GetPLCID(PSKID * plcID)
-{
-    return 0;
-}
-
-uint32_t PLCObject::GetPLCstatus(PLCstatus * status)
-{
-    return 0;
-}
-
-uint32_t PLCObject::GetTraceVariables(uint32_t debugToken, TraceVariables * traces)
-{
-    return 0;
-}
-
-uint32_t PLCObject::MatchMD5(const char * MD5, bool * match)
-{
-    return 0;
-}
-
-uint32_t PLCObject::NewPLC(const char * md5sum, const binary_t * plcObjectBlobID, const list_extra_file_1_t * extrafiles, bool * success)
-{
+uint32_t PLCObject::AppendChunkToBlob(
+    const binary_t *data, const binary_t *blobID, binary_t *newBlobID)
+{
+    // Append data to blob with given blobID
+    // Output new blob's md5 into newBlobID
+    // Return 0 if success
+
+    auto nh = m_mapBlobIDToBlob.extract(std::vector<uint8_t>(
+        blobID->data, blobID->data + blobID->dataLength));
+    if (nh.empty())
+    {
+        return ENOENT;
+    }
+
+    Blob *blob = nh.mapped();
+
+    uint32_t res = blob->appendChunk(data->data, data->dataLength);
+    if (res != 0)
+    {
+        return res;
+    }
+
+    MD5::digest_t digest = blob->digest();
+
+    nh.key() = std::vector<uint8_t>(
+        (uint8_t)*digest.data, (uint8_t)*digest.data + MD5::digestsize);
+
+    m_mapBlobIDToBlob.insert(std::move(nh));
+
+    return 0;
+}
+
+#define LOG_READ_BUFFER_SIZE 1 << 10 // 1KB
+
+uint32_t PLCObject::GetLogMessage(
+    uint8_t level, uint32_t msgID, log_message *message)
+{
+    char buf[LOG_READ_BUFFER_SIZE];
+    uint32_t tick;
+    uint32_t tv_sec;
+    uint32_t tv_nsec;
+
+    uint32_t resultLen = m_PLCSyms.GetLogMessage(
+        level, msgID, buf, LOG_READ_BUFFER_SIZE - 1,
+        &tick, &tv_sec, &tv_nsec);
+
+    if (resultLen == 0)
+    {
+        return ENOENT;
+    }
+
+    // Get log message with given msgID
+    message->msg = (char *)malloc(resultLen);
+    if (message->msg == NULL)
+    {
+        return ENOMEM;
+    }
+    // Copy the log message into eRPC message
+    memcpy(message->msg, buf, resultLen);
+    message->msg[resultLen + 1] = '\0';
+
+    message->tick = tick;
+    message->sec = tv_sec;
+    message->nsec = tv_nsec;
+
+    return 0;
+}
+
+uint32_t PLCObject::GetPLCID(PSKID *plcID)
+{
+    // Get PLC ID
+    *plcID = m_plcID;
+    return 0;
+}
+
+uint32_t PLCObject::GetPLCstatus(PLCstatus *status)
+{
+    // Get PLC status
+    *status = m_status;
+    return 0;
+}
+
+uint32_t PLCObject::GetTraceVariables(
+    uint32_t debugToken, TraceVariables *traces)
+{
+    // XXX TODO
+    return 0;
+}
+
+uint32_t PLCObject::MatchMD5(const char *MD5, bool *match)
+{
+    // Load the last transferred PLC md5 hex digest
+    std::string md5sum;
+    std::ifstream(std::string(LastTransferredPLC), std::ios::binary) >> md5sum;
+
+    // Compare the given MD5 with the last transferred PLC md5
+    *match = (md5sum == MD5);
+
+    return 0;
+}
+
+#if defined(_WIN32) || defined(_WIN64)
+// For Windows platform
+#define SHARED_OBJECT_EXT ".dll"
+#elif defined(__APPLE__) || defined(__MACH__)
+// For MacOS platform
+#define SHARED_OBJECT_EXT ".dylib"
+#else
+// For Linux/Unix platform
+#define SHARED_OBJECT_EXT ".so"
+#endif
+
+uint32_t PLCObject::BlobAsFile(
+    const binary_t *BlobID, std::filesystem::path filename)
+{
+    // Extract the blob from the map
+    auto nh = m_mapBlobIDToBlob.extract(
+        std::vector<uint8_t>(BlobID->data, BlobID->data + BlobID->dataLength));
+    if (nh.empty())
+    {
+        return ENOENT;
+    }
+    Blob *blob = nh.mapped();
+
+    // Realize the blob into a file
+    uint32_t res = blob->asFile(filename);
+
+    delete blob;
+
+    if (res != 0)
+    {
+        return res;
+    }
+    return 0;
+}
+
+uint32_t PLCObject::NewPLC(
+    const char *md5sum, const binary_t *plcObjectBlobID,
+    const list_extra_file_1_t *extrafiles, bool *success)
+{
+    // Concatenate md5sum and shared object extension to obtain filename
+    std::filesystem::path filename =
+        std::filesystem::path(md5sum) += SHARED_OBJECT_EXT;
+
+    // Create the PLC object shared object file
+    BlobAsFile(plcObjectBlobID, filename);
+
+    // create "lasttransferedPLC.md5" file and Save md5sum in it
+    std::ofstream(std::string(LastTransferredPLC), std::ios::binary) << md5sum;
+
+    // create "extra_files.txt" file
+    std::ofstream extra_files_log(std::string(ExtraFilesList), std::ios::binary);
+
+    // Create extra files
+    for (int i = 0; i < extrafiles->elementsCount; i++)
+    {
+        extra_file *extrafile = extrafiles->elements + i;
+
+        BlobAsFile(plcObjectBlobID, extrafile->fname);
+
+        // Save the extra file name in "extra_files.txt"
+        extra_files_log << extrafile->fname << std::endl;
+    }
+
+    return 0;
+}
+
+#define DLSYM(sym)                                                      \
+    do                                                                  \
+    {                                                                   \
+        m_PLCSyms.sym = (decltype(m_PLCSyms.sym))dlsym(m_handle, #sym); \
+        if (m_PLCSyms.sym == NULL)                                      \
+        {                                                               \
+            return errno;                                               \
+        }                                                               \
+    } while (0);
+
+uint32_t PLCObject::LoadPLC(void)
+{
+    // Load the last transferred PLC md5 hex digest
+    std::string md5sum;
+    std::ifstream(std::string(LastTransferredPLC), std::ios::binary) >> md5sum;
+
+    // Concatenate md5sum and shared object extension to obtain filename
+    std::filesystem::path filename = std::filesystem::path(md5sum) += SHARED_OBJECT_EXT;
+
+    // Load the shared object file
+    m_handle = dlopen(filename.c_str(), RTLD_NOW);
+    if (m_handle == NULL)
+    {
+        return errno;
+    }
+
+    // Resolve shared object symbols
+    FOR_EACH_PLC_SYMBOLS_DO(DLSYM);
+
+    return 0;
+}
+
+#define ULSYM(sym)            \
+    do                        \
+    {                         \
+        m_PLCSyms.sym = NULL; \
+    } while (0);
+
+uint32_t PLCObject::UnLoadPLC(void)
+{
+    // Unload the shared object file
+    FOR_EACH_PLC_SYMBOLS_DO(ULSYM);
+    dlclose(m_handle);
+    
     return 0;
 }
 
 uint32_t PLCObject::PurgeBlobs(void)
 {
+    // Purge all blobs
+
+    for (auto &blob : m_mapBlobIDToBlob)
+    {
+        delete blob.second;
+    }
+    m_mapBlobIDToBlob.clear();
+
     return 0;
 }
 
 uint32_t PLCObject::RepairPLC(void)
 {
+    // XXX TODO
+    LogMessage(LOG_WARNING, "RepairPLC not implemented");
     return 0;
 }
 
 uint32_t PLCObject::ResetLogCount(void)
 {
-    return 0;
-}
-
-uint32_t PLCObject::SeedBlob(const binary_t * seed, binary_t * blobID)
-{
-    return 0;
-}
-
-uint32_t PLCObject::SetTraceVariablesList(const list_trace_order_1_t * orders, uint32_t * debugtoken)
-{
+    m_PLCSyms.ResetLogCount();
+    return 0;
+}
+
+uint32_t PLCObject::SeedBlob(const binary_t *seed, binary_t *blobID)
+{
+    // Create a blob with given seed
+    // Output new blob's md5 into blobID
+    // Return 0 if success
+
+    Blob *blob = NULL;
+    try
+    {
+        blob = new Blob(seed->data, seed->dataLength);
+    }
+    catch (int e)
+    {
+        return e;
+    }
+
+    MD5::digest_t digest = blob->digest();
+
+    m_mapBlobIDToBlob[std::vector<uint8_t>((uint8_t)*digest.data, (uint8_t)*digest.data + MD5::digestsize)] = blob;
+
+    blobID->data = (uint8_t *)malloc(MD5::digestsize);
+    if (blobID->data == NULL)
+    {
+        return ENOMEM;
+    }
+    memcpy(blobID->data, digest.data, MD5::digestsize);
+    blobID->dataLength = MD5::digestsize;
+
+    return 0;
+}
+
+uint32_t PLCObject::SetTraceVariablesList(
+    const list_trace_order_1_t *orders, uint32_t *debugtoken)
+{
+    // XXX TODO
+    LogMessage(LOG_WARNING, "SetTraceVariablesList not implemented");
     return 0;
 }
 
 uint32_t PLCObject::StartPLC(void)
 {
-    return 0;
-}
-
-uint32_t PLCObject::StopPLC(bool * success)
-{
-    return 0;
-}
+    LogMessage(LOG_INFO, "Starting PLC");
+    uint32_t res = m_PLCSyms.startPLC(m_argc, m_argv);
+    if(res != 0)
+    {
+        m_status.PLCstatus = Broken;
+        return res;
+    }
+    m_status.PLCstatus = Started;
+    return 0;
+}
+
+uint32_t PLCObject::StopPLC(bool *success)
+{
+    LogMessage(LOG_INFO, "Stopping PLC");
+    uint32_t res = m_PLCSyms.stopPLC();
+    if(res != 0)
+    {
+        m_status.PLCstatus = Broken;
+        return res;
+    }
+    m_status.PLCstatus = Stopped;
+    return 0;
+}
+
+uint32_t PLCObject::LogMessage(uint8_t level, std::string message)
+{
+    // Log std::string message with given level
+    return m_PLCSyms.LogMessage(level, (char *)message.c_str(), message.size());
+}
--- a/C_runtime/PLCObject.hpp	Fri Apr 26 09:45:02 2024 +0200
+++ b/C_runtime/PLCObject.hpp	Sat May 11 19:27:28 2024 +0200
@@ -6,30 +6,100 @@
 #if !defined(_PLCObject_hpp_)
 #define _PLCObject_hpp_
 
+#include <map>
+#include <vector>
+#include "blob.hpp"
+
 #include "erpc_PLCObject_interface.hpp"
 
 using namespace erpcShim;
 
+#define FOR_EACH_PLC_SYMBOLS_DO(ACTION) \
+    ACTION(PLC_ID)\
+    ACTION(startPLC)\
+    ACTION(stopPLC)\
+    ACTION(ResetDebugVariables)\
+    ACTION(RegisterDebugVariable)\
+    ACTION(FreeDebugData)\
+    ACTION(GetDebugData)\
+    ACTION(suspendDebug)\
+    ACTION(ResumeDebug)\
+    ACTION(ResetLogCount)\
+    ACTION(GetLogCount)\
+    ACTION(LogMessage)\
+    ACTION(GetLogMessage)
+
+typedef struct s_PLCSyms{
+    uint8_t *PLC_ID;
+    int (*startPLC)(int argc,char **argv);
+    int (*stopPLC)(void);
+    void (*ResetDebugVariables)(void);
+    int (*RegisterDebugVariable)(unsigned int idx, void* force, size_t force_size);
+    void (*FreeDebugData)(void);
+    int (*GetDebugData)(unsigned long *tick, unsigned long *size, void **buffer);
+    int (*suspendDebug)(int disable);
+    void (*ResumeDebug)(void);
+    void (*ResetLogCount)(void);
+    uint32_t (*GetLogCount)(uint8_t level);
+    int (*LogMessage)(uint8_t level, char* buf, uint32_t size);
+    uint32_t (*GetLogMessage)(uint8_t level, uint32_t msgidx, char* buf, uint32_t max_size, uint32_t* tick, uint32_t* tv_sec, uint32_t* tv_nsec);
+} PLCSyms;
+
 class PLCObject : public BeremizPLCObjectService_interface
 {
     public:
 
+        PLCObject(void);
         ~PLCObject(void);
 
-        virtual uint32_t AppendChunkToBlob(const binary_t * data, const binary_t * blobID, binary_t * newBlobID);
-        virtual uint32_t GetLogMessage(uint8_t level, uint32_t msgID, log_message * message);
-        virtual uint32_t GetPLCID(PSKID * plcID);
-        virtual uint32_t GetPLCstatus(PLCstatus * status);
-        virtual uint32_t GetTraceVariables(uint32_t debugToken, TraceVariables * traces);
-        virtual uint32_t MatchMD5(const char * MD5, bool * match);
-        virtual uint32_t NewPLC(const char * md5sum, const binary_t * plcObjectBlobID, const list_extra_file_1_t * extrafiles, bool * success);
-        virtual uint32_t PurgeBlobs(void);
-        virtual uint32_t RepairPLC(void);
-        virtual uint32_t ResetLogCount(void);
-        virtual uint32_t SeedBlob(const binary_t * seed, binary_t * blobID);
-        virtual uint32_t SetTraceVariablesList(const list_trace_order_1_t * orders, uint32_t * debugtoken);
-        virtual uint32_t StartPLC(void);
-        virtual uint32_t StopPLC(bool * success);
+        // ERPC interface
+        uint32_t AppendChunkToBlob(const binary_t * data, const binary_t * blobID, binary_t * newBlobID);
+        uint32_t GetLogMessage(uint8_t level, uint32_t msgID, log_message * message);
+        uint32_t GetPLCID(PSKID * plcID);
+        uint32_t GetPLCstatus(PLCstatus * status);
+        uint32_t GetTraceVariables(uint32_t debugToken, TraceVariables * traces);
+        uint32_t MatchMD5(const char * MD5, bool * match);
+        uint32_t NewPLC(const char * md5sum, const binary_t * plcObjectBlobID, const list_extra_file_1_t * extrafiles, bool * success);
+        uint32_t PurgeBlobs(void);
+        uint32_t RepairPLC(void);
+        uint32_t ResetLogCount(void);
+        uint32_t SeedBlob(const binary_t * seed, binary_t * blobID);
+        uint32_t SetTraceVariablesList(const list_trace_order_1_t * orders, uint32_t * debugtoken);
+        uint32_t StartPLC(void);
+        uint32_t StopPLC(bool * success);
+
+        // 
+
+    private:
+        // A map of all the blobs
+        std::map<std::vector<uint8_t>, Blob*> m_mapBlobIDToBlob;
+
+        // PLC object library handle
+        void * m_handle;
+
+        // Symbols resolved from the PLC object
+        PLCSyms m_PLCSyms;
+
+        // argc and argv for the PLC object
+        int m_argc;
+        char ** m_argv;
+
+        // PLC status
+        PLCstatus m_status;
+
+        // PLC ID
+        PSKID m_plcID;
+
+        uint32_t BlobAsFile(const binary_t * BlobID, std::filesystem::path filename);
+        uint32_t LoadPLC(void);
+        uint32_t UnLoadPLC(void);
+        uint32_t LogMessage(uint8_t level, std::string message);
+ 
+
+
+
+
+    
 };
 
 #endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C_runtime/README.md	Sat May 11 19:27:28 2024 +0200
@@ -0,0 +1,11 @@
+# Beremiz C++ runtime
+
+This project is licensed under the [LPGLv2](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html) license.
+
+## Warning: Work in Progress
+
+**Please note that this project is currently a work in progress.**
+
+## Description
+
+Beremiz C++ runtime. Loads shared object produced by Beremiz but doesn't use python.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C_runtime/blob.cpp	Sat May 11 19:27:28 2024 +0200
@@ -0,0 +1,82 @@
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "blob.hpp"
+#include <unistd.h>
+
+Blob::Blob(uint8_t *seedData, size_t seedLength)
+{
+    // Create a temporary file to store blob data
+    // not using tmpfile() because we need to know the filename
+    // for later renaming and avoid deletion on close
+    
+    // Assume that a tmp directory exists in the current directory
+    uint8_t template_name[] = "tmp/blobXXXXXX";
+    int fd = mkstemp((char *)template_name);
+    if (fd == -1) {
+        throw errno;
+    }
+
+    // Open file for stdlib I/O
+    m_file = fdopen(fd, "w+");
+    if (m_file == NULL) {
+        throw errno;
+    }
+
+    // Save a copy of the filename
+    m_filename = (char *)template_name;
+
+    // Seed the MD5 hash with the seed data
+    md5.update(seedData, seedLength);
+}
+
+Blob::~Blob() {
+    if (m_file != NULL) {
+        std::fclose(m_file);
+        std::remove(m_filename.c_str());
+    }
+}
+
+MD5::digest_t Blob::digest() {
+    return md5.digest();
+}
+
+uint32_t Blob::appendChunk(uint8_t *data, size_t length) {
+    // Write data to file
+    if (std::fwrite(data, 1, length, m_file) != length) {
+        return errno;
+    }
+
+    // Update MD5 hash
+    md5.update(data, length);
+
+    return 0;
+}
+
+uint32_t Blob::asFile(std::filesystem::path &filename)
+{
+    // Flush file
+    if (std::fflush(m_file) != 0) {
+        return errno;
+    }
+
+    // Sync file to disk
+    if (fsync(fileno(m_file)) != 0) {
+        return errno;
+    }
+
+    // Close file
+    if (std::fclose(m_file) != 0) {
+        return errno;
+    }
+
+    m_file = NULL;
+
+    // Rename temp file to final file
+    if (std::rename(m_filename.c_str(), filename.c_str()) != 0) {
+        return errno;
+    }
+
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C_runtime/blob.hpp	Sat May 11 19:27:28 2024 +0200
@@ -0,0 +1,24 @@
+#ifndef BLOB_HPP
+#define BLOB_HPP
+
+#include <string>
+#include <filesystem>
+
+#include "md5.hpp"
+
+class Blob
+{
+public:
+    Blob(uint8_t *seedData, size_t seedLength);
+    ~Blob();
+    MD5::digest_t digest();
+    uint32_t appendChunk(uint8_t *data, size_t length);
+    uint32_t asFile(std::filesystem::path &filename);
+
+private:
+    MD5 md5;
+    std::FILE * m_file;
+    std::filesystem::path m_filename;
+};
+
+#endif // BLOB_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C_runtime/md5.cpp	Sat May 11 19:27:28 2024 +0200
@@ -0,0 +1,325 @@
+/* MD5
+ converted to C++ class by Frank Thilo (thilo@unix-ag.org)
+ for bzflag (http://www.bzflag.org)
+
+   based on:
+
+   md5.h and md5.c
+   reference implemantion of RFC 1321
+
+   Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+
+*/
+
+#include <stdlib.h>
+#include <string.h>
+
+/* interface header */
+#include "md5.hpp"
+
+// Constants for MD5Transform routine.
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+///////////////////////////////////////////////
+
+// F, G, H and I are basic MD5 functions.
+inline uint32_t MD5::F(uint32_t x, uint32_t y, uint32_t z)
+{
+    return x & y | ~x & z;
+}
+
+inline uint32_t MD5::G(uint32_t x, uint32_t y, uint32_t z)
+{
+    return x & z | y & ~z;
+}
+
+inline uint32_t MD5::H(uint32_t x, uint32_t y, uint32_t z)
+{
+    return x ^ y ^ z;
+}
+
+inline uint32_t MD5::I(uint32_t x, uint32_t y, uint32_t z)
+{
+    return y ^ (x | ~z);
+}
+
+// rotate_left rotates x left n bits.
+inline uint32_t MD5::rotate_left(uint32_t x, int n)
+{
+    return (x << n) | (x >> (32 - n));
+}
+
+// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+// Rotation is separate from addition to prevent recomputation.
+inline void MD5::FF(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
+{
+    a = rotate_left(a + F(b, c, d) + x + ac, s) + b;
+}
+
+inline void MD5::GG(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
+{
+    a = rotate_left(a + G(b, c, d) + x + ac, s) + b;
+}
+
+inline void MD5::HH(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
+{
+    a = rotate_left(a + H(b, c, d) + x + ac, s) + b;
+}
+
+inline void MD5::II(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
+{
+    a = rotate_left(a + I(b, c, d) + x + ac, s) + b;
+}
+
+//////////////////////////////////////////////
+
+// default ctor, just initailize
+MD5::MD5()
+{
+    init();
+}
+
+//////////////////////////////
+
+void MD5::init()
+{ 
+    count[0] = 0;
+    count[1] = 0;
+
+    // load magic initialization constants.
+    state[0] = 0x67452301;
+    state[1] = 0xefcdab89;
+    state[2] = 0x98badcfe;
+    state[3] = 0x10325476;
+}
+
+//////////////////////////////
+
+// decodes input (unsigned char) into output (uint32_t). Assumes len is a multiple of 4.
+void MD5::decode(uint32_t output[], const uint8_t input[], size_type len)
+{
+    for (unsigned int i = 0, j = 0; j < len; i++, j += 4)
+        output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j + 1]) << 8) |
+                    (((uint32_t)input[j + 2]) << 16) | (((uint32_t)input[j + 3]) << 24);
+}
+
+//////////////////////////////
+
+// encodes input (uint32_t) into output (unsigned char). Assumes len is
+// a multiple of 4.
+void MD5::encode(uint8_t output[], const uint32_t input[], size_type len)
+{
+    for (size_type i = 0, j = 0; j < len; i++, j += 4)
+    {
+        output[j] = input[i] & 0xff;
+        output[j + 1] = (input[i] >> 8) & 0xff;
+        output[j + 2] = (input[i] >> 16) & 0xff;
+        output[j + 3] = (input[i] >> 24) & 0xff;
+    }
+}
+
+//////////////////////////////
+
+// apply MD5 algo on a block
+void MD5::transform(const uint8_t block[blocksize])
+{
+    uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+    decode(x, block, blocksize);
+
+    /* Round 1 */
+    FF(a, b, c, d, x[0], S11, 0xd76aa478);  /* 1 */
+    FF(d, a, b, c, x[1], S12, 0xe8c7b756);  /* 2 */
+    FF(c, d, a, b, x[2], S13, 0x242070db);  /* 3 */
+    FF(b, c, d, a, x[3], S14, 0xc1bdceee);  /* 4 */
+    FF(a, b, c, d, x[4], S11, 0xf57c0faf);  /* 5 */
+    FF(d, a, b, c, x[5], S12, 0x4787c62a);  /* 6 */
+    FF(c, d, a, b, x[6], S13, 0xa8304613);  /* 7 */
+    FF(b, c, d, a, x[7], S14, 0xfd469501);  /* 8 */
+    FF(a, b, c, d, x[8], S11, 0x698098d8);  /* 9 */
+    FF(d, a, b, c, x[9], S12, 0x8b44f7af);  /* 10 */
+    FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+    FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+    FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+    FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+    FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+    FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+    /* Round 2 */
+    GG(a, b, c, d, x[1], S21, 0xf61e2562);  /* 17 */
+    GG(d, a, b, c, x[6], S22, 0xc040b340);  /* 18 */
+    GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+    GG(b, c, d, a, x[0], S24, 0xe9b6c7aa);  /* 20 */
+    GG(a, b, c, d, x[5], S21, 0xd62f105d);  /* 21 */
+    GG(d, a, b, c, x[10], S22, 0x2441453);  /* 22 */
+    GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+    GG(b, c, d, a, x[4], S24, 0xe7d3fbc8);  /* 24 */
+    GG(a, b, c, d, x[9], S21, 0x21e1cde6);  /* 25 */
+    GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+    GG(c, d, a, b, x[3], S23, 0xf4d50d87);  /* 27 */
+    GG(b, c, d, a, x[8], S24, 0x455a14ed);  /* 28 */
+    GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+    GG(d, a, b, c, x[2], S22, 0xfcefa3f8);  /* 30 */
+    GG(c, d, a, b, x[7], S23, 0x676f02d9);  /* 31 */
+    GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+    /* Round 3 */
+    HH(a, b, c, d, x[5], S31, 0xfffa3942);  /* 33 */
+    HH(d, a, b, c, x[8], S32, 0x8771f681);  /* 34 */
+    HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+    HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+    HH(a, b, c, d, x[1], S31, 0xa4beea44);  /* 37 */
+    HH(d, a, b, c, x[4], S32, 0x4bdecfa9);  /* 38 */
+    HH(c, d, a, b, x[7], S33, 0xf6bb4b60);  /* 39 */
+    HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+    HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+    HH(d, a, b, c, x[0], S32, 0xeaa127fa);  /* 42 */
+    HH(c, d, a, b, x[3], S33, 0xd4ef3085);  /* 43 */
+    HH(b, c, d, a, x[6], S34, 0x4881d05);   /* 44 */
+    HH(a, b, c, d, x[9], S31, 0xd9d4d039);  /* 45 */
+    HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+    HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+    HH(b, c, d, a, x[2], S34, 0xc4ac5665);  /* 48 */
+
+    /* Round 4 */
+    II(a, b, c, d, x[0], S41, 0xf4292244);  /* 49 */
+    II(d, a, b, c, x[7], S42, 0x432aff97);  /* 50 */
+    II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+    II(b, c, d, a, x[5], S44, 0xfc93a039);  /* 52 */
+    II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+    II(d, a, b, c, x[3], S42, 0x8f0ccc92);  /* 54 */
+    II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+    II(b, c, d, a, x[1], S44, 0x85845dd1);  /* 56 */
+    II(a, b, c, d, x[8], S41, 0x6fa87e4f);  /* 57 */
+    II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+    II(c, d, a, b, x[6], S43, 0xa3014314);  /* 59 */
+    II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+    II(a, b, c, d, x[4], S41, 0xf7537e82);  /* 61 */
+    II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+    II(c, d, a, b, x[2], S43, 0x2ad7d2bb);  /* 63 */
+    II(b, c, d, a, x[9], S44, 0xeb86d391);  /* 64 */
+
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+
+    // Zeroize sensitive information.
+    memset(x, 0, sizeof x);
+}
+
+//////////////////////////////
+
+// MD5 block update operation. Continues an MD5 message-digest
+// operation, processing another message block
+void MD5::update(const unsigned char input[], size_type length)
+{
+    // compute number of bytes mod 64
+    size_type index = count[0] / 8 % blocksize;
+
+    // Update number of bits
+    if ((count[0] += (length << 3)) < (length << 3))
+        count[1]++;
+    count[1] += (length >> 29);
+
+    // number of bytes we need to fill in buffer
+    size_type firstpart = 64 - index;
+
+    size_type i;
+
+    // transform as many times as possible.
+    if (length >= firstpart)
+    {
+        // fill buffer first, transform
+        memcpy(&buffer[index], input, firstpart);
+        transform(buffer);
+
+        // transform chunks of blocksize (64 bytes)
+        for (i = firstpart; i + blocksize <= length; i += blocksize)
+            transform(&input[i]);
+
+        index = 0;
+    }
+    else
+        i = 0;
+
+    // buffer remaining input
+    memcpy(&buffer[index], &input[i], length - i);
+}
+
+//////////////////////////////
+
+
+MD5::digest_t MD5::digest()
+{
+    static const unsigned char padding[64] = {
+        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+    // Copies of hash state
+    uint8_t _buffer[blocksize];
+    uint32_t _count[2];
+    uint32_t _state[4];
+
+    // Backup hash state at previous block boundary
+    memcpy(_buffer, buffer, blocksize);
+    memcpy(_count, count, 8);
+    memcpy(_state, state, 16);
+
+    // Save number of bits
+    unsigned char bits[8];
+    encode(bits, count, 8);
+
+    // pad out to 56 mod 64.
+    size_type index = count[0] / 8 % 64;
+    size_type padLen = (index < 56) ? (56 - index) : (120 - index);
+    update(padding, padLen);
+
+    // Append length (before padding)
+    update(bits, 8);
+
+    // Store state in digest
+    digest_t result;
+    encode(result.data, state, 16);
+
+    // revert hash state to previous hash boundary
+    memcpy(buffer, _buffer, blocksize);
+    memcpy(count, _count, 8);
+    memcpy(state, _state, 16);
+
+    return result;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C_runtime/md5.hpp	Sat May 11 19:27:28 2024 +0200
@@ -0,0 +1,88 @@
+/* MD5
+
+    Modified in 2024 for use in Beremiz by Edouard Tisserant
+
+    converted to C++ class by Frank Thilo (thilo@unix-ag.org)
+    for bzflag (http://www.bzflag.org)
+
+    based on:
+
+    md5.h and md5.c
+    reference implementation of RFC 1321
+
+    Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+
+*/
+
+#ifndef BZF_MD5_H
+#define BZF_MD5_H
+
+#include <cstdint>
+
+// a small class for calculating MD5 hashes of strings or byte arrays
+// it is not meant to be fast or secure
+//
+// usage: 1) feed it blocks of uchars with update()
+//        2) get digest() data
+//
+// assumes that char is 8 bit and int is 32 bit
+class MD5
+{
+public:
+    typedef unsigned int size_type; // must be 32bit
+
+    MD5();
+    void update(const unsigned char *buf, size_type length);
+    typedef struct {
+        uint8_t data[16];
+    } digest_t;
+    digest_t digest();
+    enum
+    {
+        blocksize = 64,
+        digestsize = 16
+    };    
+
+private:
+    void init();
+
+    void transform(const uint8_t block[blocksize]);
+    static void decode(uint32_t output[], const uint8_t input[], size_type len);
+    static void encode(uint8_t output[], const uint32_t input[], size_type len);
+    
+    uint8_t buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk
+    uint32_t count[2];          // 64bit counter for number of bits (lo, hi)
+    uint32_t state[4];          // digest so far
+
+    // low level logic operations
+    static inline uint32_t F(uint32_t x, uint32_t y, uint32_t z);
+    static inline uint32_t G(uint32_t x, uint32_t y, uint32_t z);
+    static inline uint32_t H(uint32_t x, uint32_t y, uint32_t z);
+    static inline uint32_t I(uint32_t x, uint32_t y, uint32_t z);
+    static inline uint32_t rotate_left(uint32_t x, int n);
+    static inline void FF(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac);
+    static inline void GG(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac);
+    static inline void HH(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac);
+    static inline void II(uint32_t &a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac);
+};
+
+#endif
\ No newline at end of file
--- a/C_runtime/posix_main.cpp	Fri Apr 26 09:45:02 2024 +0200
+++ b/C_runtime/posix_main.cpp	Sat May 11 19:27:28 2024 +0200
@@ -1,3 +1,14 @@
+/*
+ * Beremiz C++ runtime
+ *
+ * This file implements Beremiz C++ runtime Command Line Interface for POSIX
+ *
+ * Based on erpcsniffer.cpp, BSD-3-Clause, Copyright 2017 NXP
+ *
+ * Copyright 2024 Beremiz SAS
+ * 
+ * See COPYING for licensing details
+ */
 
 #include <stdlib.h>
 #include <vector>
@@ -294,7 +305,17 @@
                 m_workingDir = std::filesystem::current_path().c_str();
             } else {
                 m_workingDir = m_positionalArgs[0].c_str();
-            }
+                std::filesystem::current_path(m_workingDir);
+            }
+
+            // remove temporary directory if it already exists
+            if (std::filesystem::exists("tmp"))
+            {
+                std::filesystem::remove_all("tmp");
+            }
+
+            // Create temporary directory in working directory
+            std::filesystem::create_directory("tmp");
 
             Transport *_transport;
             switch (m_transport)