logcat: add --id=<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
<filespec>.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
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 04efd68..49943fe 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -283,6 +283,8 @@
                     "                  Rotate log every kbytes. Requires -f option\n"
                     "  -n <count>, --rotate-count=<count>\n"
                     "                  Sets max number of rotated logs to <count>, default 4\n"
+                    "  --id=<id>       If the signature id for logging to file changes, then clear\n"
+                    "                  the fileset and continue\n"
                     "  -v <format>, --format=<format>\n"
                     "                  Sets log print format verb and adverbs, where <format> is:\n"
                     "                    brief long process raw tag thread threadtime time\n"
@@ -546,6 +548,7 @@
     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 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 @@
           { "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 @@
 
         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 @@
                     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 @@
         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 @@
             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 @@
         return EXIT_SUCCESS;
     }
 
-
     if (getLogSize) {
         return EXIT_SUCCESS;
     }
@@ -1186,6 +1207,8 @@
         return EXIT_SUCCESS;
     }
 
+    setupOutput(mode);
+
     //LOG_EVENT_INT(10, 12345);
     //LOG_EVENT_LONG(11, 0x1122334455667788LL);
     //LOG_EVENT_STRING(0, "whassup, doc?");