From 330747aca1de7738b2033ce601514389996abdc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=97=E5=AE=AB=E9=9B=AA=E7=8F=8A?= Date: Fri, 29 Oct 2021 10:13:08 +0800 Subject: [PATCH] [core] Dump fd if log file deleted (#1337) --- core/src/main/cpp/daemon/logcat.cpp | 26 ++++---- core/src/main/cpp/daemon/logcat.h | 2 +- .../lspd/service/ConfigFileManager.java | 14 ++++- .../lspd/service/LSPManagerService.java | 1 + .../lsposed/lspd/service/LogcatService.java | 59 ++++++++++++++++--- 5 files changed, 78 insertions(+), 24 deletions(-) diff --git a/core/src/main/cpp/daemon/logcat.cpp b/core/src/main/cpp/daemon/logcat.cpp index 41d75c55..9ae9d8da 100644 --- a/core/src/main/cpp/daemon/logcat.cpp +++ b/core/src/main/cpp/daemon/logcat.cpp @@ -5,7 +5,6 @@ #include #include #include "logcat.h" -#include #include using namespace std::string_view_literals; @@ -88,14 +87,16 @@ size_t Logcat::PrintLogLine(const AndroidLogEntry &entry, FILE *out) { } localtime_r(&now, &tm); strftime(time_buff.data(), time_buff.size(), "%Y-%m-%dT%H:%M:%S", &tm); - // implicitly convert to size_t and intentionally trigger overflow when failed - // to generate a new fd - return fprintf(out, "[ %s.%03ld %8d:%6d:%6d %c/%-15.*s ] %.*s\n", - time_buff.data(), - nsec / MS_PER_NSEC, - entry.uid, entry.pid, entry.tid, - kLogChar[entry.priority], static_cast(entry.tagLen), - entry.tag, static_cast(message_len), message); + int len = fprintf(out, "[ %s.%03ld %8d:%6d:%6d %c/%-15.*s ] %.*s\n", + time_buff.data(), + nsec / MS_PER_NSEC, + entry.uid, entry.pid, entry.tid, + kLogChar[entry.priority], static_cast(entry.tagLen), + entry.tag, static_cast(message_len), message); + fflush(out); + // trigger overflow when failed to generate a new fd + if (len <= 0) len = kMaxLogSize; + return static_cast(len); } void Logcat::RefreshFd(bool is_verbose) { @@ -104,15 +105,19 @@ void Logcat::RefreshFd(bool is_verbose) { if (is_verbose) { verbose_print_count_ = 0; fprintf(verbose_file_.get(), end, verbose_file_part_); + fflush(verbose_file_.get()); verbose_file_ = UniqueFile(env_->CallIntMethod(thiz_, refresh_fd_method_, JNI_TRUE), "a"); verbose_file_part_++; fprintf(verbose_file_.get(), start, verbose_file_part_); + fflush(verbose_file_.get()); } else { modules_print_count_ = 0; fprintf(modules_file_.get(), end, modules_file_part_); + fflush(modules_file_.get()); modules_file_ = UniqueFile(env_->CallIntMethod(thiz_, refresh_fd_method_, JNI_FALSE), "a"); modules_file_part_++; fprintf(modules_file_.get(), start, modules_file_part_); + fflush(modules_file_.get()); } } @@ -199,9 +204,6 @@ void Logcat::Run() { ProcessBuffer(&msg); - fflush(verbose_file_.get()); - fflush(modules_file_.get()); - if (verbose_print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(true); if (modules_print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(false); } diff --git a/core/src/main/cpp/daemon/logcat.h b/core/src/main/cpp/daemon/logcat.h index 64186aae..cc39b58a 100644 --- a/core/src/main/cpp/daemon/logcat.h +++ b/core/src/main/cpp/daemon/logcat.h @@ -5,7 +5,7 @@ #include -#define NS_PER_SEC 1000000000ULL +#define NS_PER_SEC 1000000000L #define MS_PER_NSEC 1000000 #define LOGGER_ENTRY_MAX_LEN (5 * 1024) diff --git a/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java b/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java index bc2f5593..fad26f7f 100644 --- a/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java +++ b/core/src/main/java/org/lsposed/lspd/service/ConfigFileManager.java @@ -28,6 +28,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.file.FileVisitResult; import java.nio.file.Files; +import java.nio.file.LinkOption; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.Paths; @@ -65,12 +66,19 @@ public class ConfigFileManager { Files.createDirectories(basePath); SELinux.setFileContext(basePath.toString(), "u:object_r:system_file:s0"); Files.createDirectories(configDirPath); - Files.createDirectories(logDirPath); + createLogDirPath(); } catch (IOException e) { Log.e(TAG, Log.getStackTraceString(e)); } } + private static void createLogDirPath() throws IOException { + if (!Files.isDirectory(logDirPath, LinkOption.NOFOLLOW_LINKS)) { + Files.deleteIfExists(logDirPath); + } + Files.createDirectories(logDirPath); + } + public static Resources getResources() { loadRes(); return res; @@ -153,12 +161,12 @@ public class ConfigFileManager { } static File getNewVerboseLogPath() throws IOException { - Files.createDirectories(logDirPath); + createLogDirPath(); return logDirPath.resolve(getNewLogFileName("verbose")).toFile(); } static File getNewModulesLogPath() throws IOException { - Files.createDirectories(logDirPath); + createLogDirPath(); return logDirPath.resolve(getNewLogFileName("modules")).toFile(); } diff --git a/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java b/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java index 57438d15..465d5ead 100644 --- a/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java +++ b/core/src/main/java/org/lsposed/lspd/service/LSPManagerService.java @@ -591,6 +591,7 @@ public class LSPManagerService extends ILSPManagerService.Stub { @Override public ParcelFileDescriptor getModulesLog() { + workerHandler.post(() -> ServiceManager.getLogcatService().checkLogFile()); return ConfigManager.getInstance().getModulesLog(); } diff --git a/core/src/main/java/org/lsposed/lspd/service/LogcatService.java b/core/src/main/java/org/lsposed/lspd/service/LogcatService.java index 698e4ff7..c238c5d2 100644 --- a/core/src/main/java/org/lsposed/lspd/service/LogcatService.java +++ b/core/src/main/java/org/lsposed/lspd/service/LogcatService.java @@ -9,6 +9,10 @@ import org.lsposed.lspd.BuildConfig; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.Paths; public class LogcatService implements Runnable { private static final String TAG = "LSPosedLogcat"; @@ -16,8 +20,8 @@ public class LogcatService implements Runnable { ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE | ParcelFileDescriptor.MODE_APPEND; - private File modulesLog = null; - private File verboseLog = null; + private Path modulesLog = null; + private Path verboseLog = null; private Thread thread = null; @SuppressLint("UnsafeDynamicallyLoadedCode") @@ -43,21 +47,47 @@ public class LogcatService implements Runnable { @SuppressWarnings("unused") private int refreshFd(boolean isVerboseLog) { try { - File log = isVerboseLog ? ConfigFileManager.getNewVerboseLogPath() : ConfigFileManager.getNewModulesLogPath(); - Log.i(TAG, "New " + (isVerboseLog ? "verbose" : "modules") + " log file: " + log); + File log; + if (isVerboseLog) { + checkFdFile(verboseLog); + log = ConfigFileManager.getNewVerboseLogPath(); + } else { + checkFdFile(modulesLog); + log = ConfigFileManager.getNewModulesLogPath(); + } + Log.i(TAG, "New log file: " + log); int fd = ParcelFileDescriptor.open(log, mode).detachFd(); - var fdFile = new File("/proc/self/fd/" + fd); + var fdFile = Paths.get("/proc/self/fd", String.valueOf(fd)); if (isVerboseLog) verboseLog = fdFile; else modulesLog = fdFile; return fd; } catch (IOException e) { if (isVerboseLog) verboseLog = null; else modulesLog = null; - Log.w(TAG, "someone chattr +i ?", e); + Log.w(TAG, "refreshFd", e); return -1; } } + private static void checkFdFile(Path fdFile) { + if (fdFile == null) return; + try { + var file = Files.readSymbolicLink(fdFile); + if (!Files.exists(file)) { + var parent = file.getParent(); + if (!Files.isDirectory(parent, LinkOption.NOFOLLOW_LINKS)) { + Files.deleteIfExists(parent); + } + Files.createDirectories(parent); + var name = file.getFileName().toString(); + var originName = name.substring(0, name.lastIndexOf(' ')); + Files.copy(fdFile, parent.resolve(originName)); + } + } catch (IOException e) { + Log.w(TAG, "checkFd " + fdFile, e); + } + } + public boolean isRunning() { return thread != null && thread.isAlive(); } @@ -91,10 +121,23 @@ public class LogcatService implements Runnable { } public File getVerboseLog() { - return verboseLog; + return verboseLog.toFile(); } public File getModulesLog() { - return modulesLog; + return modulesLog.toFile(); + } + + public void checkLogFile() { + try { + modulesLog.toRealPath(); + } catch (IOException e) { + refresh(false); + } + try { + verboseLog.toRealPath(); + } catch (IOException e) { + refresh(true); + } } }