[core] Dump fd if log file deleted (#1337)
This commit is contained in:
parent
e58db90d72
commit
330747aca1
|
|
@ -5,7 +5,6 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include "logcat.h"
|
#include "logcat.h"
|
||||||
#include <string_view>
|
|
||||||
#include <sys/system_properties.h>
|
#include <sys/system_properties.h>
|
||||||
|
|
||||||
using namespace std::string_view_literals;
|
using namespace std::string_view_literals;
|
||||||
|
|
@ -88,14 +87,16 @@ size_t Logcat::PrintLogLine(const AndroidLogEntry &entry, FILE *out) {
|
||||||
}
|
}
|
||||||
localtime_r(&now, &tm);
|
localtime_r(&now, &tm);
|
||||||
strftime(time_buff.data(), time_buff.size(), "%Y-%m-%dT%H:%M:%S", &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
|
int len = fprintf(out, "[ %s.%03ld %8d:%6d:%6d %c/%-15.*s ] %.*s\n",
|
||||||
// to generate a new fd
|
|
||||||
return fprintf(out, "[ %s.%03ld %8d:%6d:%6d %c/%-15.*s ] %.*s\n",
|
|
||||||
time_buff.data(),
|
time_buff.data(),
|
||||||
nsec / MS_PER_NSEC,
|
nsec / MS_PER_NSEC,
|
||||||
entry.uid, entry.pid, entry.tid,
|
entry.uid, entry.pid, entry.tid,
|
||||||
kLogChar[entry.priority], static_cast<int>(entry.tagLen),
|
kLogChar[entry.priority], static_cast<int>(entry.tagLen),
|
||||||
entry.tag, static_cast<int>(message_len), message);
|
entry.tag, static_cast<int>(message_len), message);
|
||||||
|
fflush(out);
|
||||||
|
// trigger overflow when failed to generate a new fd
|
||||||
|
if (len <= 0) len = kMaxLogSize;
|
||||||
|
return static_cast<size_t>(len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logcat::RefreshFd(bool is_verbose) {
|
void Logcat::RefreshFd(bool is_verbose) {
|
||||||
|
|
@ -104,15 +105,19 @@ void Logcat::RefreshFd(bool is_verbose) {
|
||||||
if (is_verbose) {
|
if (is_verbose) {
|
||||||
verbose_print_count_ = 0;
|
verbose_print_count_ = 0;
|
||||||
fprintf(verbose_file_.get(), end, verbose_file_part_);
|
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_ = UniqueFile(env_->CallIntMethod(thiz_, refresh_fd_method_, JNI_TRUE), "a");
|
||||||
verbose_file_part_++;
|
verbose_file_part_++;
|
||||||
fprintf(verbose_file_.get(), start, verbose_file_part_);
|
fprintf(verbose_file_.get(), start, verbose_file_part_);
|
||||||
|
fflush(verbose_file_.get());
|
||||||
} else {
|
} else {
|
||||||
modules_print_count_ = 0;
|
modules_print_count_ = 0;
|
||||||
fprintf(modules_file_.get(), end, modules_file_part_);
|
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_ = UniqueFile(env_->CallIntMethod(thiz_, refresh_fd_method_, JNI_FALSE), "a");
|
||||||
modules_file_part_++;
|
modules_file_part_++;
|
||||||
fprintf(modules_file_.get(), start, modules_file_part_);
|
fprintf(modules_file_.get(), start, modules_file_part_);
|
||||||
|
fflush(modules_file_.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,9 +204,6 @@ void Logcat::Run() {
|
||||||
|
|
||||||
ProcessBuffer(&msg);
|
ProcessBuffer(&msg);
|
||||||
|
|
||||||
fflush(verbose_file_.get());
|
|
||||||
fflush(modules_file_.get());
|
|
||||||
|
|
||||||
if (verbose_print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(true);
|
if (verbose_print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(true);
|
||||||
if (modules_print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(false);
|
if (modules_print_count_ >= kMaxLogSize) [[unlikely]] RefreshFd(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
|
||||||
#define NS_PER_SEC 1000000000ULL
|
#define NS_PER_SEC 1000000000L
|
||||||
#define MS_PER_NSEC 1000000
|
#define MS_PER_NSEC 1000000
|
||||||
#define LOGGER_ENTRY_MAX_LEN (5 * 1024)
|
#define LOGGER_ENTRY_MAX_LEN (5 * 1024)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import java.nio.channels.FileChannel;
|
||||||
import java.nio.channels.FileLock;
|
import java.nio.channels.FileLock;
|
||||||
import java.nio.file.FileVisitResult;
|
import java.nio.file.FileVisitResult;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.LinkOption;
|
||||||
import java.nio.file.OpenOption;
|
import java.nio.file.OpenOption;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
@ -65,12 +66,19 @@ public class ConfigFileManager {
|
||||||
Files.createDirectories(basePath);
|
Files.createDirectories(basePath);
|
||||||
SELinux.setFileContext(basePath.toString(), "u:object_r:system_file:s0");
|
SELinux.setFileContext(basePath.toString(), "u:object_r:system_file:s0");
|
||||||
Files.createDirectories(configDirPath);
|
Files.createDirectories(configDirPath);
|
||||||
Files.createDirectories(logDirPath);
|
createLogDirPath();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, Log.getStackTraceString(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() {
|
public static Resources getResources() {
|
||||||
loadRes();
|
loadRes();
|
||||||
return res;
|
return res;
|
||||||
|
|
@ -153,12 +161,12 @@ public class ConfigFileManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
static File getNewVerboseLogPath() throws IOException {
|
static File getNewVerboseLogPath() throws IOException {
|
||||||
Files.createDirectories(logDirPath);
|
createLogDirPath();
|
||||||
return logDirPath.resolve(getNewLogFileName("verbose")).toFile();
|
return logDirPath.resolve(getNewLogFileName("verbose")).toFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
static File getNewModulesLogPath() throws IOException {
|
static File getNewModulesLogPath() throws IOException {
|
||||||
Files.createDirectories(logDirPath);
|
createLogDirPath();
|
||||||
return logDirPath.resolve(getNewLogFileName("modules")).toFile();
|
return logDirPath.resolve(getNewLogFileName("modules")).toFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -591,6 +591,7 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ParcelFileDescriptor getModulesLog() {
|
public ParcelFileDescriptor getModulesLog() {
|
||||||
|
workerHandler.post(() -> ServiceManager.getLogcatService().checkLogFile());
|
||||||
return ConfigManager.getInstance().getModulesLog();
|
return ConfigManager.getInstance().getModulesLog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,10 @@ import org.lsposed.lspd.BuildConfig;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
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 {
|
public class LogcatService implements Runnable {
|
||||||
private static final String TAG = "LSPosedLogcat";
|
private static final String TAG = "LSPosedLogcat";
|
||||||
|
|
@ -16,8 +20,8 @@ public class LogcatService implements Runnable {
|
||||||
ParcelFileDescriptor.MODE_CREATE |
|
ParcelFileDescriptor.MODE_CREATE |
|
||||||
ParcelFileDescriptor.MODE_TRUNCATE |
|
ParcelFileDescriptor.MODE_TRUNCATE |
|
||||||
ParcelFileDescriptor.MODE_APPEND;
|
ParcelFileDescriptor.MODE_APPEND;
|
||||||
private File modulesLog = null;
|
private Path modulesLog = null;
|
||||||
private File verboseLog = null;
|
private Path verboseLog = null;
|
||||||
private Thread thread = null;
|
private Thread thread = null;
|
||||||
|
|
||||||
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
@SuppressLint("UnsafeDynamicallyLoadedCode")
|
||||||
|
|
@ -43,21 +47,47 @@ public class LogcatService implements Runnable {
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private int refreshFd(boolean isVerboseLog) {
|
private int refreshFd(boolean isVerboseLog) {
|
||||||
try {
|
try {
|
||||||
File log = isVerboseLog ? ConfigFileManager.getNewVerboseLogPath() : ConfigFileManager.getNewModulesLogPath();
|
File log;
|
||||||
Log.i(TAG, "New " + (isVerboseLog ? "verbose" : "modules") + " 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();
|
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;
|
if (isVerboseLog) verboseLog = fdFile;
|
||||||
else modulesLog = fdFile;
|
else modulesLog = fdFile;
|
||||||
return fd;
|
return fd;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (isVerboseLog) verboseLog = null;
|
if (isVerboseLog) verboseLog = null;
|
||||||
else modulesLog = null;
|
else modulesLog = null;
|
||||||
Log.w(TAG, "someone chattr +i ?", e);
|
Log.w(TAG, "refreshFd", e);
|
||||||
return -1;
|
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() {
|
public boolean isRunning() {
|
||||||
return thread != null && thread.isAlive();
|
return thread != null && thread.isAlive();
|
||||||
}
|
}
|
||||||
|
|
@ -91,10 +121,23 @@ public class LogcatService implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getVerboseLog() {
|
public File getVerboseLog() {
|
||||||
return verboseLog;
|
return verboseLog.toFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getModulesLog() {
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue