Path: blob/v3_openjdk/app_pojavlauncher/src/main/jni/stdio_is.c
2128 views
#include <jni.h>1#include <sys/types.h>2#include <stdbool.h>3#include <unistd.h>4#include <pthread.h>5#include <stdio.h>6#include <fcntl.h>7#include <string.h>8#include <errno.h>9#include <stdlib.h>10#include <environ/environ.h>1112#include "stdio_is.h"1314//15// Created by maks on 17.02.21.16//1718static volatile jobject exitTrap_ctx;19static volatile jclass exitTrap_exitClass;20static volatile jmethodID exitTrap_staticMethod;21static JavaVM *exitTrap_jvm;2223static int pfd[2];24static pthread_t logger;25static jmethodID logger_onEventLogged;26static volatile jobject logListener = NULL;27static int latestlog_fd = -1;282930static bool recordBuffer(char* buf, ssize_t len) {31if(strstr(buf, "Session ID is")) return false;32if(latestlog_fd != -1) {33write(latestlog_fd, buf, len);34fdatasync(latestlog_fd);35}36return true;37}3839static void *logger_thread() {40JNIEnv *env;41jstring writeString;42JavaVM* dvm = pojav_environ->dalvikJavaVMPtr;43(*dvm)->AttachCurrentThread(dvm, &env, NULL);44ssize_t rsize;45char buf[2050];46while((rsize = read(pfd[0], buf, sizeof(buf)-1)) > 0) {47bool shouldRecordString = recordBuffer(buf, rsize); //record with newline int latestlog48if(buf[rsize-1]=='\n') {49rsize=rsize-1; //truncate50}51buf[rsize]=0x00;52if(shouldRecordString && logListener != NULL) {53writeString = (*env)->NewStringUTF(env, buf); //send to app without newline54(*env)->CallVoidMethod(env, logListener, logger_onEventLogged, writeString);55(*env)->DeleteLocalRef(env, writeString);56}57}58(*dvm)->DetachCurrentThread(dvm);59return NULL;60}61JNIEXPORT void JNICALL62Java_net_kdt_pojavlaunch_Logger_begin(JNIEnv *env, __attribute((unused)) jclass clazz, jstring logPath) {63if(latestlog_fd != -1) {64int localfd = latestlog_fd;65latestlog_fd = -1;66close(localfd);67}68if(logger_onEventLogged == NULL) {69jclass eventLogListener = (*env)->FindClass(env, "net/kdt/pojavlaunch/Logger$eventLogListener");70logger_onEventLogged = (*env)->GetMethodID(env, eventLogListener, "onEventLogged", "(Ljava/lang/String;)V");71}72jclass ioeClass = (*env)->FindClass(env, "java/io/IOException");737475setvbuf(stdout, 0, _IOLBF, 0); // make stdout line-buffered76setvbuf(stderr, 0, _IONBF, 0); // make stderr unbuffered7778/* create the pipe and redirect stdout and stderr */79pipe(pfd);80dup2(pfd[1], 1);81dup2(pfd[1], 2);8283/* open latestlog.txt for writing */84const char* logFilePath = (*env)->GetStringUTFChars(env, logPath, NULL);85latestlog_fd = open(logFilePath, O_WRONLY | O_TRUNC);86if(latestlog_fd == -1) {87latestlog_fd = 0;88(*env)->ThrowNew(env, ioeClass, strerror(errno));89return;90}91(*env)->ReleaseStringUTFChars(env, logPath, logFilePath);9293/* spawn the logging thread */94int result = pthread_create(&logger, 0, logger_thread, 0);95if(result != 0) {96close(latestlog_fd);97(*env)->ThrowNew(env, ioeClass, strerror(result));98}99pthread_detach(logger);100}101102_Noreturn void nominal_exit(int code, bool is_signal) {103JNIEnv *env;104jint errorCode = (*exitTrap_jvm)->GetEnv(exitTrap_jvm, (void**)&env, JNI_VERSION_1_6);105if(errorCode == JNI_EDETACHED) {106errorCode = (*exitTrap_jvm)->AttachCurrentThread(exitTrap_jvm, &env, NULL);107}108if(errorCode != JNI_OK) {109// Step on a landmine and die, since we can't invoke the Dalvik exit without attaching to110// Dalvik.111// I mean, if Zygote can do that, why can't I?112killpg(getpgrp(), SIGTERM);113}114if(code != 0) {115// Exit code 0 is pretty established as "eh it's fine"116// so only open the GUI if the code is != 0117(*env)->CallStaticVoidMethod(env, exitTrap_exitClass, exitTrap_staticMethod, exitTrap_ctx, code, is_signal);118}119// Delete the reference, not gonna need 'em later anyway120(*env)->DeleteGlobalRef(env, exitTrap_ctx);121(*env)->DeleteGlobalRef(env, exitTrap_exitClass);122123// A hat trick, if you will124// Call the Android System.exit() to perform Android's shutdown hooks and do a125// fully clean exit.126// After doing this, either of these will happen:127// 1. Runtime calls exit() for real and it will be handled by ByteHook's recurse handler128// and redirected back to the OS129// 2. Zygote sends SIGTERM (no handling necessary, the process perishes)130// 3. A different thread calls exit() and the hook will go through the exit_tripped path131jclass systemClass = (*env)->FindClass(env,"java/lang/System");132jmethodID exitMethod = (*env)->GetStaticMethodID(env, systemClass, "exit", "(I)V");133(*env)->CallStaticVoidMethod(env, systemClass, exitMethod, 0);134// System.exit() should not ever return, but the compiler doesn't know about that135// so put a while loop here136while(1) {}137}138139JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_Logger_appendToLog(JNIEnv *env, __attribute((unused)) jclass clazz, jstring text) {140jsize appendStringLength = (*env)->GetStringUTFLength(env, text);141char newChars[appendStringLength+2];142(*env)->GetStringUTFRegion(env, text, 0, (*env)->GetStringLength(env, text), newChars);143newChars[appendStringLength] = '\n';144newChars[appendStringLength+1] = 0;145if(recordBuffer(newChars, appendStringLength+1) && logListener != NULL) {146(*env)->CallVoidMethod(env, logListener, logger_onEventLogged, text);147}148}149150JNIEXPORT void JNICALL151Java_net_kdt_pojavlaunch_Logger_setLogListener(JNIEnv *env, __attribute((unused)) jclass clazz, jobject log_listener) {152jobject logListenerLocal = logListener;153if(log_listener == NULL) {154logListener = NULL;155}else{156logListener = (*env)->NewGlobalRef(env, log_listener);157}158if(logListenerLocal != NULL) (*env)->DeleteGlobalRef(env, logListenerLocal);159}160161162JNIEXPORT void JNICALL163Java_net_kdt_pojavlaunch_utils_JREUtils_setupExitMethod(JNIEnv *env, jclass clazz,164jobject context) {165exitTrap_ctx = (*env)->NewGlobalRef(env,context);166(*env)->GetJavaVM(env,&exitTrap_jvm);167exitTrap_exitClass = (*env)->NewGlobalRef(env,(*env)->FindClass(env,"net/kdt/pojavlaunch/ExitActivity"));168exitTrap_staticMethod = (*env)->GetStaticMethodID(env,exitTrap_exitClass,"showExitMessage","(Landroid/content/Context;IZ)V");169}170171