gnunet-android

GNUnet for Android
Log | Files | Refs | README

native-lib.cpp (19228B)


      1 #include <fstream>
      2 #include <iostream>
      3 #include <jni.h>
      4 #include <string>
      5 #include <vector>
      6 #include <android/log.h>
      7 #include <android/asset_manager.h>
      8 #include <android/asset_manager_jni.h>
      9 #include "gnunet_util_lib.h"
     10 #include <ltdl.h>
     11 #include <dlfcn.h>
     12 #include <sys/stat.h>
     13 #include <libgen.h>
     14 #include <fcntl.h>
     15 #include <unistd.h>
     16 
     17 #define TAG "GNUNET"
     18 
     19 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,    TAG, __VA_ARGS__)
     20 #define LOGW(...) __android_log_print(ANDROID_LOG_WARN,     TAG, __VA_ARGS__)
     21 #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,     TAG, __VA_ARGS__)
     22 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,    TAG, __VA_ARGS__)
     23 
     24 extern "C" {
     25     #include <unistd.h>
     26     #include <gnunet_util_lib.h>
     27 }
     28 
     29 static int pfd[2];
     30 static pthread_t thr;
     31 static const char *tag = "GNUNET";
     32 
     33 void *thread_func(void*)
     34 {
     35     static const size_t READ_CHUNK = 1024;
     36     static const size_t MAX_LINE   = 8192; // eigene Obergrenze pro „logische“ Zeile
     37     std::string acc; acc.reserve(MAX_LINE);
     38     std::vector<char> tmp(READ_CHUNK);
     39 
     40     ssize_t n;
     41     while ((n = read(pfd[0], tmp.data(), tmp.size())) > 0) {
     42         acc.append(tmp.data(), n);
     43 
     44         size_t pos;
     45         while ((pos = acc.find('\n')) != std::string::npos) {
     46             std::string line = acc.substr(0, pos);
     47             acc.erase(0, pos + 1);
     48 
     49             // Leer \r am Ende weg
     50             if (!line.empty() && line.back() == '\r') line.pop_back();
     51 
     52             // In handliche Teile splitten, um log-Limit/ANSI zu vermeiden
     53             const size_t CHUNK = 1000;
     54             for (size_t i = 0; i < line.size(); i += CHUNK) {
     55                 std::string_view part(line.c_str() + i, std::min(CHUNK, line.size() - i));
     56                 __android_log_write(ANDROID_LOG_DEBUG, tag, std::string(part).c_str());
     57             }
     58         }
     59 
     60         // Falls Zeile extrem lang wird, „notfall“ flushen:
     61         if (acc.size() > MAX_LINE) {
     62             __android_log_write(ANDROID_LOG_DEBUG, tag, "[truncated: line too long]");
     63             acc.clear();
     64         }
     65     }
     66 
     67     // Rest ohne Newline flushen
     68     if (!acc.empty()) {
     69         __android_log_write(ANDROID_LOG_DEBUG, tag, acc.c_str());
     70     }
     71     return nullptr;
     72 }
     73 
     74 int start_logger(const char *app_name)
     75 {
     76     tag = app_name;
     77 
     78     /* make stdout line-buffered and stderr unbuffered */
     79     setvbuf(stdout, 0, _IOLBF, 0);
     80     setvbuf(stderr, 0, _IONBF, 0);
     81 
     82     /* create the pipe and redirect stdout and stderr */
     83     pipe(pfd);
     84     dup2(pfd[1], 1);
     85     dup2(pfd[1], 2);
     86 
     87     /* spawn the logging thread */
     88     if(pthread_create(&thr, 0, thread_func, 0) == -1)
     89         return -1;
     90     pthread_detach(thr);
     91     return 0;
     92 }
     93 
     94 /** ------------------ Milestone 6-7 code. -------------------------------
     95 static void
     96 run (void *cls,
     97      char *const *args,
     98      const char *cfgfile,
     99      const struct GNUNET_CONFIGURATION_Handle *c)
    100 {
    101     int sockfd, newsockfd, portno;
    102     socklen_t clilen;
    103     char buffer[256];
    104     struct sockaddr_in serv_addr, cli_addr;
    105     int n;
    106 
    107     sockfd = socket(AF_INET, SOCK_STREAM, 0);
    108     if (sockfd < 0){
    109         LOGE ("ERROR opening socket");
    110         perror("ERROR opening socket");
    111         goto shutdown;
    112     }
    113     bzero((char *) &serv_addr, sizeof(serv_addr));
    114     portno = 8081;
    115     serv_addr.sin_family = AF_INET;
    116     serv_addr.sin_addr.s_addr = INADDR_ANY;
    117     serv_addr.sin_port = htons(portno);
    118     if (bind(sockfd, (struct sockaddr *) &serv_addr,
    119              sizeof(serv_addr)) < 0)
    120     {
    121         perror("ERROR on binding");
    122         goto shutdown;
    123     }
    124     listen(sockfd,5);
    125     clilen = sizeof(cli_addr);
    126     newsockfd = accept(sockfd,
    127                        (struct sockaddr *) &cli_addr,
    128                        &clilen);
    129     if (newsockfd < 0)
    130     {
    131         perror("ERROR on accept");
    132         goto shutdown;
    133     }
    134     bzero(buffer,256);
    135     n = read(newsockfd,buffer,255);
    136     if (n < 0)
    137     {
    138         perror("ERROR reading from socket");
    139         goto shutdown;
    140     }
    141     printf("Here is the message: %s\n",buffer);
    142     n = write(newsockfd,"I got your message",18);
    143     if (n < 0)
    144     {
    145         perror("ERROR writing to socket");
    146         goto shutdown;
    147     }
    148     close(newsockfd);
    149     close(sockfd);
    150 
    151 shutdown:
    152     GNUNET_SCHEDULER_shutdown();
    153 }*/
    154 
    155 // END ------------------------------ Milestone 6 - 7 Code ---------------------
    156 
    157 static jobject android_java_asset_manager = NULL;
    158 static struct GNUNET_ARM_Handle *h;
    159 static struct GNUNET_CONFIGURATION_Handle *cfg;
    160 static struct GNUNET_ARM_Operation *op;
    161 
    162 /* -------------- Multi library code -----------------------------
    163 /*static void
    164 start_callback (void *cls,
    165                 enum GNUNET_ARM_RequestStatus rs,
    166                 enum GNUNET_ARM_Result result)
    167 {
    168     (void) cls;
    169     op = NULL;
    170     if (GNUNET_ARM_REQUEST_SENT_OK != rs)
    171     {
    172         LOGE("Failed to start the ARM service.");
    173         GNUNET_SCHEDULER_shutdown ();
    174         return;
    175     }
    176     if ((GNUNET_ARM_RESULT_STARTING != result) &&
    177         (GNUNET_ARM_RESULT_IS_STARTED_ALREADY != result))
    178     {
    179         LOGE("Failed to start the ARM service.");
    180         GNUNET_SCHEDULER_shutdown ();
    181         return;
    182     }
    183     LOGD ("ARM service [re]start successful");
    184 }
    185 
    186 
    187 static void
    188 shutdown_task (void *cls)
    189 {
    190     (void) cls;
    191     if (NULL != op)
    192     {
    193         GNUNET_ARM_operation_cancel (op);
    194         op = NULL;
    195     }
    196     if (NULL != h)
    197     {
    198         GNUNET_ARM_disconnect (h);
    199         h = NULL;
    200     }
    201     GNUNET_CONFIGURATION_destroy (cfg);
    202     cfg = NULL;
    203 }*/
    204 
    205 
    206 /**
    207  * Function called whenever we connect to or disconnect from ARM.
    208  * Termiantes the process if we fail to connect to the service on
    209  * our first attempt.
    210  *
    211  * @param cls closure
    212  * @param connected #GNUNET_YES if connected, #GNUNET_NO if disconnected,
    213  *                  #GNUNET_SYSERR on error.
    214  */
    215 /*static void
    216 conn_status (void *cls,
    217              enum GNUNET_GenericReturnValue connected)
    218 {
    219     static int once;
    220 
    221     (void) cls;
    222     if ( (GNUNET_SYSERR == connected) &&
    223          (0 == once) )
    224     {
    225         LOGE("Fatal error initializing ARM API.");
    226         GNUNET_SCHEDULER_shutdown ();
    227         return;
    228     }
    229     once = 1;
    230 }
    231 
    232 
    233 
    234 static void
    235 run (void *cls,
    236      char *const *args,
    237      const char *cfgfile,
    238      const struct GNUNET_CONFIGURATION_Handle *c)
    239 {
    240     start_logger(tag);
    241     printf("Some message.\n");
    242     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    243                 "Simulate error message. We do not no what is the actual log level.\n");
    244     cfg =  GNUNET_CONFIGURATION_dup (c);
    245     AAssetManager *mgr = static_cast<AAssetManager *>(cls);
    246     AAsset *asset = AAssetManager_open(mgr, "gnunet.conf", AASSET_MODE_BUFFER);
    247     char buf[AAsset_getLength(asset)];
    248 
    249     AAsset_read(asset, buf, AAsset_getLength(asset));
    250 
    251     if (GNUNET_OK != GNUNET_CONFIGURATION_deserialize (cfg, buf, strlen(buf), NULL))
    252     {
    253         LOGE ("Deserialization of configuration failed!");
    254     }
    255 
    256     AAsset_close(asset);
    257 
    258 
    259     if (NULL == (h = GNUNET_ARM_connect (cfg,
    260                                          &conn_status,
    261                                          NULL)))
    262         return;
    263 
    264     op = GNUNET_ARM_request_service_start (h,
    265                                            "arm",
    266                                            GNUNET_OS_INHERIT_STD_NONE,
    267                                            &start_callback,
    268                                            NULL);
    269 
    270 
    271 }
    272  END -------------- Multi library code ----------------------------- */
    273 
    274 void ensure_directory_exists(const char* path) {
    275     struct stat st;
    276     if (stat(path, &st) != 0) {
    277         if (mkdir(path, 0700) != 0) {
    278             __android_log_print(ANDROID_LOG_ERROR, "APP", "Failed to create dir: %s (%s)", path, strerror(errno));
    279         } else {
    280             __android_log_print(ANDROID_LOG_DEBUG, "APP", "Created dir: %s", path);
    281         }
    282     } else {
    283         __android_log_print(ANDROID_LOG_DEBUG, "APP", "Dir already exists: %s", path);
    284     }
    285 }
    286 
    287 void ensure_file_exists(const char* path) {
    288     struct stat st;
    289     if (stat(path, &st) != 0) {
    290         // Datei existiert nicht → versuchen, anzulegen
    291         int fd = open(path, O_CREAT | O_WRONLY, 0600);
    292         if (fd < 0) {
    293             __android_log_print(ANDROID_LOG_ERROR, "APP", "Failed to create file: %s (%s)", path, strerror(errno));
    294         } else {
    295             __android_log_print(ANDROID_LOG_DEBUG, "APP", "Created file: %s", path);
    296             close(fd);
    297         }
    298     } else {
    299         __android_log_print(ANDROID_LOG_DEBUG, "APP", "File already exists: %s", path);
    300     }
    301 }
    302 
    303 void copy_plugins(AAssetManager* mgr, const char* dest_dir) {
    304     // Zielverzeichnis erstellen
    305     mkdir(dest_dir, 0700);  // Ignoriert Fehler, wenn schon existiert
    306 
    307     // Asset-Verzeichnis öffnen
    308     AAssetDir* assetDir = AAssetManager_openDir(mgr, "plugins");
    309     if (!assetDir) {
    310         LOGE("Could not open assets/plugins/");
    311         return;
    312     }
    313 
    314     const char* filename = nullptr;
    315     while ((filename = AAssetDir_getNextFileName(assetDir)) != nullptr) {
    316         LOGD("Copying %s...", filename);
    317 
    318         // Pfad innerhalb von Assets
    319         std::string asset_path = std::string("plugins/") + filename;
    320 
    321         AAsset* asset = AAssetManager_open(mgr, asset_path.c_str(), AASSET_MODE_STREAMING);
    322         if (!asset) {
    323             LOGE("Failed to open asset %s", asset_path.c_str());
    324             continue;
    325         }
    326 
    327         // Zielpfad vorbereiten
    328         std::string out_path = std::string(dest_dir) + "/" + filename;
    329         int out_fd = open(out_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0700);
    330         if (out_fd < 0) {
    331             LOGE("Could not create output file %s", out_path.c_str());
    332             AAsset_close(asset);
    333             continue;
    334         }
    335 
    336         // Kopieren
    337         char buffer[4096];
    338         int read_bytes;
    339         while ((read_bytes = AAsset_read(asset, buffer, sizeof(buffer))) > 0) {
    340             write(out_fd, buffer, read_bytes);
    341         }
    342 
    343         close(out_fd);
    344         AAsset_close(asset);
    345         LOGD("Copied to %s", out_path.c_str());
    346     }
    347 
    348     AAssetDir_close(assetDir);
    349 }
    350 
    351 void set_executable_permissions(const char* path) {
    352     if (chmod(path, 0700) != 0) {
    353         __android_log_print(ANDROID_LOG_ERROR, "GNUNET", "chmod failed for %s: %s", path, strerror(errno));
    354     } else {
    355         __android_log_print(ANDROID_LOG_DEBUG, "GNUNET", "chmod OK: %s", path);
    356     }
    357 }
    358 
    359 extern "C" JNIEXPORT jstring JNICALL
    360 Java_org_gnu_gnunet_MainActivity_stringFromJNI(
    361         JNIEnv* env,
    362         jobject /* this */,
    363         jobject assets,
    364         jstring path) {
    365 
    366     jboolean isCopy = JNI_FALSE;
    367     const char *c_install_path = env->GetStringUTFChars(path, &isCopy);
    368     const struct GNUNET_OS_ProjectData *default_pd = GNUNET_OS_project_data_gnunet();
    369     const struct GNUNET_OS_ProjectData my_pd = {
    370             .libname = "libgnunetutil",
    371             .project_dirname = "gnunet",
    372             .binary_name = "gnunet-arm",
    373             .version = default_pd->version,
    374             .env_varname = "GNUNET_PREFIX",
    375             .base_config_varname = "GNUNET_BASE_CONFIG",
    376             .bug_email = "gnunet-developers@gnu.org",
    377             .homepage = "http://www.gnu.org/s/gnunet/",
    378             .config_file = "gnunet.conf",
    379             .user_config_file = "~/.config/gnunet.conf",
    380             .is_gnu = 1,
    381             .gettext_domain = "gnunet",
    382             .gettext_path = NULL,
    383             .agpl_url = GNUNET_AGPL_URL,
    384             .install_path_override = c_install_path
    385     };
    386 
    387     void* handle_gnunet_util = dlopen("libgnunetutil.so", RTLD_NOW);
    388     if (!handle_gnunet_util) return env->NewStringUTF("libgnunetutil.so not loaded");
    389    /* void* handle_plugin_peerstore_sqlite = dlopen("libgnunet_plugin_peerstore_sqlite.so", RTLD_NOW);
    390     if (!handle_plugin_peerstore_sqlite) return env->NewStringUTF("libgnunet_plugin_peerstore_sqlite.so not loaded");*/
    391     android_java_asset_manager = (*env).NewGlobalRef(assets);
    392     AAssetManager *mgr = AAssetManager_fromJava(env, android_java_asset_manager);
    393 
    394     std::string install_path_str(c_install_path);
    395     ensure_directory_exists((install_path_str + "/gnunet").c_str());
    396     ensure_directory_exists((install_path_str + "/tmp").c_str());
    397     ensure_directory_exists((install_path_str + "/share").c_str());
    398     ensure_directory_exists((install_path_str + "/share/gnunet").c_str());
    399     ensure_directory_exists((install_path_str + "/share/gnunet/hellos").c_str());
    400     ensure_directory_exists((install_path_str + "/home").c_str());
    401     ensure_directory_exists((install_path_str + "/home/data").c_str());
    402     ensure_file_exists((install_path_str + "/home/data/statistics.dat").c_str());
    403     ensure_directory_exists((install_path_str + "/home/data/nse").c_str());
    404     set_executable_permissions((install_path_str + "/home/data/nse").c_str());
    405     ensure_file_exists((install_path_str + "/home/data/nse/proof.dat").c_str());
    406     set_executable_permissions((install_path_str + "/home/data/nse/proof.dat").c_str());
    407     ensure_directory_exists((install_path_str + "/lib").c_str());
    408     ensure_directory_exists((install_path_str + "/lib/gnunet").c_str());
    409     copy_plugins(mgr, "/data/user/0/org.gnu.gnunet/files/lib/gnunet");
    410     set_executable_permissions("/data/user/0/org.gnu.gnunet/files/lib/gnunet/libgnunet_plugin_datacache_heap.so");
    411 
    412     struct stat st;
    413     if (0 == stat("/data/user/0/org.gnu.gnunet/files/home/data/nse/proof.dat", &st)) {
    414         __android_log_print(ANDROID_LOG_DEBUG, "GNUNET", "proof.dat size = %lld", (long long)st.st_size);
    415     } else {
    416         __android_log_print(ANDROID_LOG_ERROR, "GNUNET", "proof.dat not found after creation");
    417     }
    418 
    419     const char* proof_path = "/data/user/0/org.gnu.gnunet/files/home/data/nse/proof.dat";
    420     FILE* f = fopen(proof_path, "a");
    421     if (!f) {
    422         __android_log_print(ANDROID_LOG_ERROR, "GNUNET", "fopen() failed on %s", proof_path);
    423         perror("fopen");
    424     } else {
    425         fprintf(f, "Write test at %ld\n", time(nullptr));
    426         fclose(f);
    427         __android_log_print(ANDROID_LOG_DEBUG, "GNUNET", "Write to %s successful", proof_path);
    428     }
    429 
    430     void* plugin = dlopen("/data/user/0/org.gnu.gnunet/files/lib/gnunet/libgnunet_plugin_datacache_heap.so", RTLD_NOW);
    431 
    432     if (!plugin)
    433         __android_log_print(ANDROID_LOG_ERROR, "DLTEST", "dlopen failed: %s", dlerror());
    434     else
    435         __android_log_print(ANDROID_LOG_DEBUG, "DLTEST", "dlopen SUCCESS for libgnunet_plugin_datacache_heap");
    436 
    437     set_executable_permissions("/data/user/0/org.gnu.gnunet/files/lib/gnunet/libgnunet_plugin_datastore.so");
    438 
    439     void* plugin2 = dlopen("/data/user/0/org.gnu.gnunet/files/lib/gnunet/libgnunet_plugin_datastore.so", RTLD_NOW);
    440 
    441     if (!plugin2)
    442         __android_log_print(ANDROID_LOG_ERROR, "DLTEST", "dlopen failed: %s", dlerror());
    443     else
    444         __android_log_print(ANDROID_LOG_DEBUG, "DLTEST", "dlopen SUCCESS for libgnunet_plugin_datastore");
    445 
    446     void* handle_util = dlopen("/data/user/0/org.gnu.gnunet/files/lib/gnunet/libgnunetutil.so", RTLD_NOW | RTLD_GLOBAL);
    447     if (!handle_util) {
    448         __android_log_print(ANDROID_LOG_ERROR, "DLTEST", "Failed to load libgnunetutil.so: %s", dlerror());
    449     } else {
    450         __android_log_print(ANDROID_LOG_DEBUG, "DLTEST", "Loaded libgnunetutil.so successfully");
    451     }
    452 
    453     const char* tmp = GNUNET_DISK_mktemp(&my_pd, "test");
    454     if (tmp == nullptr) {
    455         // Fehlerbehandlung
    456         __android_log_print(ANDROID_LOG_ERROR, "GNUNET", "mktemp failed!");
    457         return env->NewStringUTF("mktemp failed");
    458     }
    459     std::string tmp_file = tmp;
    460     LOGD ("Temp file is here: %s", tmp_file.c_str());
    461     int ok = lt_dlinit();
    462     LOGD("lt_dlinit return code: %d", ok);
    463     LOGD ("current ltdl search path: %s", lt_dlgetsearchpath());
    464 
    465     lt_dlsetsearchpath ("/data/user/0/org.gnu.gnunet/files/lib/gnunet");
    466 
    467     LOGD ("current ltdl search path afterwards: %s", lt_dlgetsearchpath());
    468     char *const non_const_ptr = const_cast<char*>(tmp_file.c_str());
    469 
    470     char *const argvx[] = {
    471             "BuggerAll",
    472             nullptr
    473     };
    474 
    475 
    476     static const struct GNUNET_GETOPT_CommandLineOption options[] = {
    477             GNUNET_GETOPT_OPTION_END
    478     };
    479 
    480     start_logger(tag);
    481     printf("Some message.\n");
    482     /*
    483     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    484                 "Simulate error message. We do not no what is the actual log level.\n");
    485                 */
    486 
    487     /*AAsset *plugin_asset = AAssetManager_open(mgr, "libgnunet_plugin_peerstore_sqlite.so", AASSET_MODE_BUFFER);
    488     const off_t &length = AAsset_getLength(plugin_asset);
    489     char plugin_buf[length];
    490     LOGD("libgnunet_plugin_peerstore_sqlite.so size is %ld bytes", length);
    491     AAsset_read(plugin_asset, plugin_buf, length);
    492     enum GNUNET_DISK_AccessPermissions permission = static_cast<GNUNET_DISK_AccessPermissions>(
    493             GNUNET_DISK_PERM_USER_READ
    494             | GNUNET_DISK_PERM_USER_WRITE
    495             | GNUNET_DISK_PERM_USER_EXEC);
    496 
    497     // This code fails to get a fileHandle. TODO work out why not. It means the write doesn't happen.
    498     struct GNUNET_DISK_FileHandle *fh = GNUNET_DISK_file_open ("/data/user/0/org.gnu.gnunet/files/libgnunet_plugin_peerstore_sqlite_gnunet.so",
    499                            GNUNET_DISK_OPEN_WRITE,
    500                            permission );
    501     GNUNET_DISK_file_write (fh,
    502                             plugin_buf,
    503                             length);
    504     GNUNET_DISK_file_close (fh);*/
    505 
    506     // The following code does work; so does the kotlin code that copies the .so file from the assets folder.
    507     std::vector<int> data = {1, 2, 3, 4, 5, 6};
    508     std::ofstream outfile("/data/user/0/org.gnu.gnunet/files/output.bin", std::ios::binary);
    509     if (!outfile) {
    510         LOGE("Could not open output.bin for writing");
    511     }
    512 
    513     outfile.write(reinterpret_cast<const char*>(data.data()), data.size() * sizeof(int ));
    514     if (!outfile) {
    515         LOGE("Write failed for output.bin");
    516     }
    517     outfile.close();
    518 
    519 
    520     cfg = GNUNET_CONFIGURATION_create (GNUNET_OS_project_data_gnunet());
    521     if (cfg == nullptr) {
    522         LOGE("Failed to create GNUNET configuration object");
    523         return env->NewStringUTF("Failed to create GNUNET configuration object");
    524     }
    525     AAsset *asset = AAssetManager_open(mgr, "gnunet.conf", AASSET_MODE_BUFFER);
    526     off_t config_file_size = AAsset_getLength(asset);
    527     LOGD("gnunet.conf is %ld bytes long", config_file_size);
    528 
    529 
    530     std::vector<char> buf(config_file_size);
    531 
    532     int actually_read = AAsset_read(asset, buf.data(), config_file_size);
    533     LOGD("actually read %d bytes", actually_read);
    534 
    535     // Sicherstellen, dass der Puffer nullterminiert ist
    536     buf.push_back('\0');
    537 
    538     if (GNUNET_OK != GNUNET_CONFIGURATION_deserialize(cfg, buf.data(), config_file_size, NULL))
    539     {
    540         LOGE ("Deserialization of configuration failed!");
    541     }
    542     AAsset_close(asset);
    543     LOGD("about to run GNUNET_PROGRAM_monolith_main");
    544 
    545     GNUNET_PROGRAM_monolith_main (&my_pd,
    546                                   1,
    547                         argvx,
    548                         cfg);
    549 
    550     /*GNUNET_PROGRAM_run (4,
    551                         argvx,
    552                         "native-lib",
    553                         "native-lib",
    554                         options,
    555                         &run,
    556                         mgr);*/
    557     std::string hello = "Hello from C++. Temp file is here: " + tmp_file;
    558 
    559     return env->NewStringUTF(hello.c_str());
    560 }