diff --git a/jni/daemon/bootstages.c b/jni/daemon/bootstages.c index 03f57026a..585e33779 100644 --- a/jni/daemon/bootstages.c +++ b/jni/daemon/bootstages.c @@ -28,10 +28,6 @@ static int seperate_vendor = 0; extern char **environ; -#ifdef MAGISK_DEBUG -static int debug_log_pid, debug_log_fd; -#endif - /****************** * Node structure * ******************/ @@ -599,15 +595,8 @@ void post_fs_data(int client) { if (!check_data()) goto unblock; - // Start log monitor - monitor_logs(); - -#ifdef MAGISK_DEBUG - // Log everything initially - debug_log_fd = xopen(DEBUG_LOG, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644); - xwrite(debug_log_fd, "Boot logs:\n", 11); - debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", "-v", "thread", NULL); -#endif + // Start the debug log + start_debug_full_log(); LOGI("** post-fs-data mode running\n"); @@ -804,12 +793,5 @@ core_only: buf = buf2 = NULL; vec_deep_destroy(&module_list); -#ifdef MAGISK_DEBUG - // Stop recording the boot logcat after every boot task is done - kill(debug_log_pid, SIGTERM); - waitpid(debug_log_pid, NULL, 0); - // Then start to log Magisk verbosely - xwrite(debug_log_fd, "\nVerbose logs:\n", 15); - debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", "-v", "thread", "-s", "Magisk", NULL); -#endif + stop_debug_full_log(); } diff --git a/jni/daemon/daemon.c b/jni/daemon/daemon.c index 7b10e580b..b05d92df8 100644 --- a/jni/daemon/daemon.c +++ b/jni/daemon/daemon.c @@ -123,6 +123,9 @@ void start_daemon() { xdup2(fd, STDERR_FILENO); close(fd); + // Start the log monitor + monitor_logs(); + // Patch selinux with medium patch before we do anything load_policydb(SELINUX_POLICY); sepol_med_rules(); diff --git a/jni/daemon/log_monitor.c b/jni/daemon/log_monitor.c index 9d3be1aab..f496ee4b1 100644 --- a/jni/daemon/log_monitor.c +++ b/jni/daemon/log_monitor.c @@ -1,8 +1,8 @@ /* log_monitor.c - New thread to monitor logcat * - * Open a new thread to call logcat and get logs with tag "Magisk" - * Also, write the logs to a log file for debugging purpose - * + * A universal logcat monitor for many usages. Add listeners to the list, + * and the pointer of the new log line will be sent through pipes to trigger + * asynchronous events without polling */ #include @@ -13,33 +13,150 @@ #include "magisk.h" #include "utils.h" -#include "daemon.h" + +int logcat_events[] = { -1, -1, -1 }; + +static int is_restart = 0; + +#ifdef MAGISK_DEBUG +static int debug_log_pid, debug_log_fd; +#endif static void *logger_thread(void *args) { // Setup error handler err_handler = exit_thread; - rename(LOGFILE, LASTLOG); - int log_fd, log_pid; - - log_fd = xopen(LOGFILE, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644); + int log_fd = -1, log_pid; + char line[4096]; while (1) { + // Clear buffer + exec_command_sync("logcat", "-b", "all", "-c", NULL); // Start logcat - log_pid = exec_command(0, &log_fd, NULL, "logcat", "-v", "thread", "Magisk:I", "*:S", NULL); - if (log_pid > 0) - waitpid(log_pid, NULL, 0); - // For some reason it went here, clear buffer and restart - exec_command_sync("logcat", "-c", NULL); + log_pid = exec_command(0, &log_fd, NULL, "logcat", "-b", "events", "-b", "default", "-s", "am_proc_start", "Magisk", NULL); + while (fdgets(line, sizeof(line), log_fd)) { + for (int i = 0; i < (sizeof(logcat_events) / sizeof(int)); ++i) { + if (logcat_events[i] > 0) { + char *s = strdup(line); + xwrite(logcat_events[i], &s, sizeof(s)); + } + } + if (kill(log_pid, 0)) + break; + } } // Should never be here, but well... return NULL; } -/* Start a new thread to monitor logcat and dump to logfile */ +static void *magisk_log_thread(void *args) { + // Setup error handler + err_handler = exit_thread; + + int have_data = 0; + + // Temp buffer for logs before we have data access + struct vector logs; + vec_init(&logs); + + FILE *log; + int pipefd[2]; + if (xpipe2(pipefd, O_CLOEXEC) == -1) + return NULL; + + // Register our listener + logcat_events[LOG_EVENT] = pipefd[1]; + + for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line)) { + char *ss; + if ((ss = strstr(line, " Magisk")) && (ss[-1] != 'D') && (ss[-1] != 'V')) { + if (!have_data) { + if ((have_data = check_data())) { + // Dump buffered logs to file + if (!is_restart) + rename(LOGFILE, LASTLOG); + log = xfopen(LOGFILE, "a"); + setbuf(log, NULL); + char *tmp; + vec_for_each(&logs, tmp) { + fprintf(log, "%s", tmp); + free(tmp); + } + vec_destroy(&logs); + } else { + vec_push_back(&logs, line); + } + } + if (have_data) + fprintf(log, "%s", line); + } + } + return NULL; +} + +static void *debug_magisk_log_thread(void *args) { + // Setup error handler + err_handler = exit_thread; + + FILE *log = xfopen(DEBUG_LOG, "a"); + setbuf(log, NULL); + int pipefd[2]; + if (xpipe2(pipefd, O_CLOEXEC) == -1) + return NULL; + + // Register our listener + logcat_events[DEBUG_EVENT] = pipefd[1]; + + for (char *line; xxread(pipefd[0], &line, sizeof(line)) > 0; free(line)) { + char *ss; + if ((ss = strstr(line, "Magisk"))) + fprintf(log, "%s", line); + } + return NULL; +} + +/* Start new threads to monitor logcat and dump to logfile */ void monitor_logs() { pthread_t thread; + + is_restart = check_data(); + + // Start log file dumper before monitor + xpthread_create(&thread, NULL, magisk_log_thread, NULL); + pthread_detach(thread); + +#ifdef MAGISK_DEBUG + if (is_restart) { + // Restart debug logs + xpthread_create(&thread, NULL, debug_magisk_log_thread, NULL); + pthread_detach(thread); + } +#endif + + // Start logcat monitor xpthread_create(&thread, NULL, logger_thread, NULL); pthread_detach(thread); + +} + +void start_debug_full_log() { +#ifdef MAGISK_DEBUG + // Log everything initially + debug_log_fd = xopen(DEBUG_LOG, O_WRONLY | O_CREAT | O_CLOEXEC | O_TRUNC, 0644); + debug_log_pid = exec_command(0, &debug_log_fd, NULL, "logcat", NULL); + close(debug_log_fd); +#endif +} + +void stop_debug_full_log() { +#ifdef MAGISK_DEBUG + // Stop recording the boot logcat after every boot task is done + kill(debug_log_pid, SIGTERM); + waitpid(debug_log_pid, NULL, 0); + pthread_t thread; + // Start debug thread + xpthread_create(&thread, NULL, debug_magisk_log_thread, NULL); + pthread_detach(thread); +#endif } diff --git a/jni/include/daemon.h b/jni/include/daemon.h index b3275d02f..abe994e4b 100644 --- a/jni/include/daemon.h +++ b/jni/include/daemon.h @@ -50,10 +50,6 @@ void write_int(int fd, int val); char* read_string(int fd); void write_string(int fd, const char* val); -// log_monitor.c - -void monitor_logs(); - /*************** * Boot Stages * ***************/ diff --git a/jni/include/logging.h b/jni/include/logging.h index 8727c982b..11673ebd5 100644 --- a/jni/include/logging.h +++ b/jni/include/logging.h @@ -34,6 +34,17 @@ static inline void do_nothing() {} #define PLOGE(fmt, args...) { LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)); err_handler(); } +enum { + HIDE_EVENT, + LOG_EVENT, + DEBUG_EVENT +}; +extern int logcat_events[]; + +void monitor_logs(); +void start_debug_full_log(); +void stop_debug_full_log(); + #else // IS_DAEMON #include diff --git a/jni/include/utils.h b/jni/include/utils.h index 308be8d1a..b7ccd2579 100644 --- a/jni/include/utils.h +++ b/jni/include/utils.h @@ -90,6 +90,7 @@ void fclone_attr(const int sourcefd, const int targetfd); void clone_attr(const char *source, const char *target); void get_client_cred(int fd, struct ucred *cred); int switch_mnt_ns(int pid); +int fork_dont_care(); // img.c diff --git a/jni/magiskhide/proc_monitor.c b/jni/magiskhide/proc_monitor.c index ca3f9833e..b357138a1 100644 --- a/jni/magiskhide/proc_monitor.c +++ b/jni/magiskhide/proc_monitor.c @@ -19,27 +19,20 @@ #include "utils.h" #include "magiskhide.h" -static int zygote_num; static char init_ns[32], zygote_ns[2][32], cache_block[256]; -static int log_pid, log_fd, target_pid, has_cache = 1; -static char *buffer; +static int zygote_num, has_cache = 1, pipefd[2] = { -1, -1 }; // Workaround for the lack of pthread_cancel static void quit_pthread(int sig) { err_handler = do_nothing; LOGD("proc_monitor: running cleanup\n"); destroy_list(); - free(buffer); hideEnabled = 0; - // Kill the logging if needed - if (log_pid > 0) { - kill(log_pid, SIGTERM); - waitpid(log_pid, NULL, 0); - close(log_fd); - } - // Resume process if possible - if (target_pid > 0) - kill(target_pid, SIGCONT); + // Unregister listener + logcat_events[HIDE_EVENT] = -1; + close(pipefd[0]); + close(pipefd[1]); + pipefd[0] = pipefd[1] = -1; pthread_mutex_destroy(&hide_lock); pthread_mutex_destroy(&file_lock); LOGD("proc_monitor: terminating...\n"); @@ -47,7 +40,7 @@ static void quit_pthread(int sig) { } static void proc_monitor_err() { - LOGD("proc_monitor: error occured, stopping magiskhide services\n"); + LOGE("proc_monitor: error occured, stopping magiskhide services\n"); quit_pthread(SIGUSR1); } @@ -77,25 +70,24 @@ static void lazy_unmount(const char* mountpoint) { } static void hide_daemon_err() { - LOGD("hide_daemon: error occured, stopping magiskhide services\n"); - _exit(-1); + LOGE("hide_daemon: error occured\n"); } static void hide_daemon(int pid) { LOGD("hide_daemon: start unmount for pid=[%d]\n", pid); - // When an error occurs, report its failure to main process + // When an error occurs, report its failure err_handler = hide_daemon_err; - char *line; + char *line, buffer[PATH_MAX]; struct vector mount_list; manage_selinux(); clean_magisk_props(); if (switch_mnt_ns(pid)) - return; + goto exit; - snprintf(buffer, PATH_MAX, "/proc/%d/mounts", pid); + snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid); vec_init(&mount_list); file_to_vector(buffer, &mount_list); @@ -132,7 +124,7 @@ static void hide_daemon(int pid) { vec_destroy(&mount_list); // Re-read mount infos - snprintf(buffer, PATH_MAX, "/proc/%d/mounts", pid); + snprintf(buffer, sizeof(buffer), "/proc/%d/mounts", pid); vec_init(&mount_list); file_to_vector(buffer, &mount_list); @@ -145,8 +137,12 @@ static void hide_daemon(int pid) { free(line); } - // Free uo memory +exit: + // Send resume signal + kill(pid, SIGCONT); + // Free up memory vec_destroy(&mount_list); + _exit(0); } void proc_monitor() { @@ -158,9 +154,7 @@ void proc_monitor() { // The error handler should stop magiskhide services err_handler = proc_monitor_err; - log_pid = target_pid = -1; - buffer = xmalloc(PATH_MAX); cache_block[0] = '\0'; // Get the mount namespace of init @@ -188,20 +182,15 @@ void proc_monitor() { break; } - while (1) { - // Clear previous logcat buffer - exec_command_sync("logcat", "-b", "events", "-c", NULL); + // Register our listener to logcat monitor + xpipe2(pipefd, O_CLOEXEC); + logcat_events[HIDE_EVENT] = pipefd[1]; - // Monitor am_proc_start - log_fd = -1; - log_pid = exec_command(0, &log_fd, NULL, "logcat", "-b", "events", "-v", "raw", "-s", "am_proc_start", NULL); - - if (log_pid < 0) continue; - if (kill(log_pid, 0)) continue; - - while(fdgets(buffer, PATH_MAX, log_fd)) { + for (char *log, *line; xxread(pipefd[0], &log, sizeof(log)) > 0; free(log)) { + char *ss; + if ((ss = strstr(log, "am_proc_start")) && (ss = strchr(ss, '['))) { int pid, ret, comma = 0; - char *pos = buffer, *line, processName[256]; + char *pos = ss, processName[256], ns[32]; while(1) { pos = strchr(pos, ','); @@ -212,9 +201,9 @@ void proc_monitor() { } if (comma == 6) - ret = sscanf(buffer, "[%*d %d %*d %*d %256s", &pid, processName); + ret = sscanf(ss, "[%*d %d %*d %*d %256s", &pid, processName); else - ret = sscanf(buffer, "[%*d %d %*d %256s", &pid, processName); + ret = sscanf(ss, "[%*d %d %*d %256s", &pid, processName); if(ret != 2) continue; @@ -225,12 +214,11 @@ void proc_monitor() { pthread_mutex_lock(&hide_lock); vec_for_each(hide_list, line) { if (strcmp(processName, line) == 0) { - target_pid = pid; while(1) { ret = 1; for (int i = 0; i < zygote_num; ++i) { - read_namespace(target_pid, buffer, 32); - if (strcmp(buffer, zygote_ns[i]) == 0) { + read_namespace(pid, ns, sizeof(ns)); + if (strcmp(ns, zygote_ns[i]) == 0) { usleep(50); ret = 0; break; @@ -240,43 +228,21 @@ void proc_monitor() { } // Send pause signal ASAP - if (kill(target_pid, SIGSTOP) == -1) continue; + if (kill(pid, SIGSTOP) == -1) continue; - LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, target_pid, buffer); + LOGI("proc_monitor: %s (PID=%d ns=%s)\n", processName, pid, ns); /* * The setns system call do not support multithread processes * We have to fork a new process, setns, then do the unmounts */ - int hide_pid = fork(); - switch(hide_pid) { - case -1: - PLOGE("fork"); - return; - case 0: - hide_daemon(target_pid); - _exit(0); - default: - break; - } + if (fork_dont_care() == 0) + hide_daemon(pid); - // Wait till the unmount process is done - waitpid(hide_pid, &ret, 0); - if (WEXITSTATUS(ret)) - quit_pthread(SIGUSR1); - - // All done, send resume signal - kill(target_pid, SIGCONT); - target_pid = -1; break; } } pthread_mutex_unlock(&hide_lock); } - - // For some reason it went here, restart logging - kill(log_pid, SIGTERM); - waitpid(log_pid, NULL, 0); - close(log_fd); } } diff --git a/jni/utils/misc.c b/jni/utils/misc.c index afe250b2a..3cc44eed3 100644 --- a/jni/utils/misc.c +++ b/jni/utils/misc.c @@ -408,3 +408,14 @@ int switch_mnt_ns(int pid) { close(fd); return ret; } + +int fork_dont_care() { + int pid = fork(); + if (pid) { + waitpid(pid, NULL, 0); + return pid; + } else if ((pid = fork())) { + exit(0); + } + return 0; +}