From 02687e75b0d17ef08ab834553ba2fa3699fba09e Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Wed, 3 Aug 2016 14:20:41 -0700 Subject: [PATCH] logcat: add --id= for -f option Clear file rotate content if id signature changes. This allows logcatd to detect change of property ro.build.id to reset the recorded data. The id is stored in the the -f filespec as .id. If the id file is missing or unreadable, do not clear. Allow an API where chmod 0 to the id file blocks this feature. Does not work for root. Add logcat.logrotate_id gTest. Bug: 30591615 Change-Id: I895749ca7c6d4af57ec57f058a29d8e3af2f8b27 --- logcat/logcat.cpp | 31 +++++++++++++--- logcat/tests/logcat_test.cpp | 70 ++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 4 deletions(-) diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index 04efd68e0..49943fe83 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -283,6 +283,8 @@ static void show_help(const char *cmd) " Rotate log every kbytes. Requires -f option\n" " -n , --rotate-count=\n" " Sets max number of rotated logs to , default 4\n" + " --id= If the signature id for logging to file changes, then clear\n" + " the fileset and continue\n" " -v , --format=\n" " Sets log print format verb and adverbs, where is:\n" " brief long process raw tag thread threadtime time\n" @@ -546,6 +548,7 @@ int main(int argc, char **argv) unsigned long setLogSize = 0; int getPruneList = 0; char *setPruneList = NULL; + char *setId = NULL; int printStatistics = 0; int mode = ANDROID_LOG_RDONLY; const char *forceFilters = NULL; @@ -573,6 +576,7 @@ int main(int argc, char **argv) int option_index = 0; // list of long-argument only strings for later comparison static const char pid_str[] = "pid"; + static const char id_str[] = "id"; static const char wrap_str[] = "wrap"; static const char print_str[] = "print"; static const struct option long_options[] = { @@ -587,6 +591,7 @@ int main(int argc, char **argv) { "grep", required_argument, NULL, 'e' }, // hidden and undocumented reserved alias for --max-count { "head", required_argument, NULL, 'm' }, + { id_str, required_argument, NULL, 0 }, { "last", no_argument, NULL, 'L' }, { "max-count", required_argument, NULL, 'm' }, { pid_str, required_argument, NULL, 0 }, @@ -612,7 +617,7 @@ int main(int argc, char **argv) switch (ret) { case 0: - // One of the long options + // only long options if (long_options[option_index].name == pid_str) { // ToDo: determine runtime PID_MAX? if (!getSizeTArg(optarg, &pid, 1)) { @@ -643,6 +648,10 @@ int main(int argc, char **argv) g_printItAnyways = true; break; } + if (long_options[option_index].name == id_str) { + setId = optarg && optarg[0] ? optarg : NULL; + break; + } break; case 's': @@ -967,7 +976,20 @@ int main(int argc, char **argv) logcat_panic(true, "-r requires -f as well\n"); } - setupOutput(); + if (setId != NULL) { + if (g_outputFileName == NULL) { + logcat_panic(true, "--id='%s' requires -f as well\n", setId); + } + + std::string file_name = android::base::StringPrintf("%s.id", g_outputFileName); + std::string file; + bool file_ok = android::base::ReadFileToString(file_name, &file); + android::base::WriteStringToFile(setId, file_name, + S_IRUSR | S_IWUSR, getuid(), getgid()); + if (!file_ok || (file.compare(setId) == 0)) { + setId = NULL; + } + } if (hasSetLogFormat == 0) { const char* logFormat = getenv("ANDROID_PRINTF_LOG"); @@ -1033,7 +1055,7 @@ int main(int argc, char **argv) continue; } - if (clearLog) { + if (clearLog || setId) { if (g_outputFileName) { int maxRotationCountDigits = (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0; @@ -1175,7 +1197,6 @@ int main(int argc, char **argv) return EXIT_SUCCESS; } - if (getLogSize) { return EXIT_SUCCESS; } @@ -1186,6 +1207,8 @@ int main(int argc, char **argv) return EXIT_SUCCESS; } + setupOutput(mode); + //LOG_EVENT_INT(10, 12345); //LOG_EVENT_LONG(11, 0x1122334455667788LL); //LOG_EVENT_STRING(0, "whassup, doc?"); diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp index 0043d1bc0..3daee134e 100644 --- a/logcat/tests/logcat_test.cpp +++ b/logcat/tests/logcat_test.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -848,6 +849,75 @@ TEST(logcat, logrotate_clear) { EXPECT_FALSE(system(command)); } +static int logrotate_count_id(const char *logcat_cmd, const char *tmp_out_dir) { + + static const char log_filename[] = "log.txt"; + char command[strlen(tmp_out_dir) + strlen(logcat_cmd) + strlen(log_filename) + 32]; + + snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename); + + int ret; + EXPECT_FALSE((ret = system(command))); + if (ret) { + return -1; + } + std::unique_ptr dir(opendir(tmp_out_dir), closedir); + EXPECT_NE(nullptr, dir); + if (!dir) { + return -1; + } + struct dirent *entry; + int count = 0; + while ((entry = readdir(dir.get()))) { + if (strncmp(entry->d_name, log_filename, sizeof(log_filename) - 1)) { + continue; + } + ++count; + } + return count; +} + +TEST(logcat, logrotate_id) { + static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test"; + static const char logcat_short_cmd[] = "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test"; + static const char tmp_out_dir_form[] = "/data/local/tmp/logcat.logrotate.XXXXXX"; + static const char log_filename[] = "log.txt"; + char tmp_out_dir[strlen(tmp_out_dir_form) + 1]; + ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form))); + + EXPECT_EQ(34, logrotate_count_id(logcat_cmd, tmp_out_dir)); + EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir)); + + char id_file[strlen(tmp_out_dir_form) + strlen(log_filename) + 5]; + snprintf(id_file, sizeof(id_file), "%s/%s.id", tmp_out_dir, log_filename); + if (getuid() != 0) { + chmod(id_file, 0); + EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir)); + } + unlink(id_file); + EXPECT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir)); + + FILE *fp = fopen(id_file, "w"); + if (fp) { + fprintf(fp, "not_a_test"); + fclose(fp); + } + if (getuid() != 0) { + chmod(id_file, 0); // API to preserve content even with signature change + ASSERT_EQ(34, logrotate_count_id(logcat_short_cmd, tmp_out_dir)); + chmod(id_file, 0600); + } + + int new_signature; + EXPECT_LE(2, (new_signature = logrotate_count_id(logcat_short_cmd, tmp_out_dir))); + EXPECT_GT(34, new_signature); + + static const char cleanup_cmd[] = "rm -rf %s"; + char command[strlen(cleanup_cmd) + strlen(tmp_out_dir_form)]; + snprintf(command, sizeof(command), cleanup_cmd, tmp_out_dir); + EXPECT_FALSE(system(command)); +} + TEST(logcat, logrotate_nodir) { // expect logcat to error out on writing content and exit(1) for nodir EXPECT_EQ(W_EXITCODE(1, 0),