/* log_monitor.c - New thread to monitor logcat * * 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 #include #include #include #include #include "magisk.h" #include "utils.h" #include "daemon.h" int loggable = 1; static int sockfd; static pthread_t thread = -1; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; enum { HIDE_EVENT, LOG_EVENT }; struct log_listener { int fd; int (*filter) (const char*); }; static int am_proc_start_filter(const char *log) { return strstr(log, "am_proc_start") != NULL; } static int magisk_log_filter(const char *log) { return !am_proc_start_filter(log); } static struct log_listener log_events[] = { { /* HIDE_EVENT */ .fd = -1, .filter = am_proc_start_filter }, { /* LOG_EVENT */ .fd = -1, .filter = magisk_log_filter } }; #define EVENT_NUM (sizeof(log_events) / sizeof(struct log_listener)) static void test_logcat() { int log_fd = -1, log_pid; char buf[1]; log_pid = exec_command(0, &log_fd, NULL, "logcat", NULL); if (read(log_fd, buf, sizeof(buf)) != sizeof(buf)) { loggable = 0; LOGD("log_monitor: cannot read from logcat, disable logging"); } kill(log_pid, SIGTERM); waitpid(log_pid, NULL, 0); } static void sigpipe_handler(int sig) { close(log_events[HIDE_EVENT].fd); log_events[HIDE_EVENT].fd = -1; } static void *socket_thread(void *args) { /* This would block, so separate thread */ while(1) { int fd = accept4(sockfd, NULL, NULL, SOCK_CLOEXEC); switch(read_int(fd)) { case HIDE_CONNECT: pthread_mutex_lock(&lock); log_events[HIDE_EVENT].fd = fd; pthread_mutex_unlock(&lock); thread = -1; return NULL; default: close(fd); break; } } } static void *monitor_thread(void *args) { // Block SIGPIPE to prevent interruption sigset_t block_set; sigemptyset(&block_set); sigaddset(&block_set, SIGPIPE); pthread_sigmask(SIG_SETMASK, &block_set, NULL); // Give the main daemon some time before we monitor it sleep(5); int fd; char b[1]; do { fd = connect_daemon(); write_int(fd, MONITOR); // This should hold unless the daemon is killed read(fd, b, sizeof(b)); // The main daemon crashed, spawn a new one close(fd); } while (1); } void log_daemon() { setsid(); strcpy(argv0, "magisklogd"); struct sockaddr_un sun; sockfd = setup_socket(&sun, LOG_DAEMON); if (xbind(sockfd, (struct sockaddr*) &sun, sizeof(sun))) exit(1); xlisten(sockfd, 1); LOGI("Magisk v" xstr(MAGISK_VERSION) "(" xstr(MAGISK_VER_CODE) ") logger started\n"); // Start invincible mode monitor // TODO: Remove this when all crashes are fixed pthread_t t; pthread_create(&t, NULL, monitor_thread, NULL); pthread_detach(t); // Set SIGPIPE handler struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_handler = sigpipe_handler; sigaction(SIGPIPE, &act, NULL); // Setup log dumps rename(LOGFILE, LOGFILE ".bak"); log_events[LOG_EVENT].fd = xopen(LOGFILE, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0644); int log_fd = -1, log_pid; char line[PIPE_BUF]; while (1) { if (!loggable) { // Disable all services for (int i = 0; i < EVENT_NUM; ++i) { close(log_events[i].fd); log_events[i].fd = -1; } return; } // Start logcat log_pid = exec_command(0, &log_fd, NULL, "/system/bin/logcat", "-b", "events", "-b", "main", "-b", "crash", "-v", "threadtime", "-s", "am_proc_start", "Magisk", "*:F", NULL); FILE *logs = fdopen(log_fd, "r"); while (fgets(line, sizeof(line), logs)) { if (line[0] == '-') continue; size_t len = strlen(line); pthread_mutex_lock(&lock); for (int i = 0; i < EVENT_NUM; ++i) { if (log_events[i].fd > 0 && log_events[i].filter(line)) write(log_events[i].fd, line, len); } if (thread < 0 && log_events[HIDE_EVENT].fd < 0) { // New thread to handle connection to main daemon xpthread_create(&thread, NULL, socket_thread, NULL); pthread_detach(thread); } pthread_mutex_unlock(&lock); if (kill(log_pid, 0)) break; } // Cleanup fclose(logs); log_fd = -1; kill(log_pid, SIGTERM); waitpid(log_pid, NULL, 0); test_logcat(); } } /* Start new threads to monitor logcat and dump to logfile */ void monitor_logs() { test_logcat(); if (loggable) { int fd; connect_daemon2(LOG_DAEMON, &fd); write_int(fd, DO_NOTHING); close(fd); } }