resolve merge conflicts of 5f6dbd9 to nyc-mr1-dev-plus-aosp

Change-Id: Ia85ca9ea4c38d270229af394c351b7b31ea2057e
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 5bac717..7e2bac7 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -26,6 +26,7 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
@@ -109,29 +110,27 @@
             (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
 
     for (int i = g_maxRotatedLogs ; i > 0 ; i--) {
-        char *file0, *file1;
+        std::string file1 = android::base::StringPrintf(
+            "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
 
-        asprintf(&file1, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
-
+        std::string file0;
         if (i - 1 == 0) {
-            asprintf(&file0, "%s", g_outputFileName);
+            file0 = android::base::StringPrintf("%s", g_outputFileName);
         } else {
-            asprintf(&file0, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
+            file0 = android::base::StringPrintf(
+                "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
         }
 
-        if (!file0 || !file1) {
+        if ((file0.length() == 0) || (file1.length() == 0)) {
             perror("while rotating log files");
             break;
         }
 
-        err = rename(file0, file1);
+        err = rename(file0.c_str(), file1.c_str());
 
         if (err < 0 && errno != ENOENT) {
             perror("while rotating log files");
         }
-
-        free(file1);
-        free(file0);
     }
 
     g_outFD = openLogFile(g_outputFileName);
@@ -231,13 +230,15 @@
     }
 }
 
-static void setupOutput()
-{
-
+static void setupOutputAndSchedulingPolicy(bool blocking) {
     if (g_outputFileName == NULL) {
         g_outFD = STDOUT_FILENO;
+        return;
+    }
 
-    } else {
+    if (blocking) {
+        // Lower priority and set to batch scheduling if we are saving
+        // the logs into files and taking continuous content.
         if (set_sched_policy(0, SP_BACKGROUND) < 0) {
             fprintf(stderr, "failed to set background scheduling policy\n");
         }
@@ -251,26 +252,26 @@
         if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
             fprintf(stderr, "failed set to priority\n");
         }
-
-        g_outFD = openLogFile (g_outputFileName);
-
-        if (g_outFD < 0) {
-            logcat_panic(false, "couldn't open output file");
-        }
-
-        struct stat statbuf;
-        if (fstat(g_outFD, &statbuf) == -1) {
-            close(g_outFD);
-            logcat_panic(false, "couldn't get output file stat\n");
-        }
-
-        if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
-            close(g_outFD);
-            logcat_panic(false, "invalid output file stat\n");
-        }
-
-        g_outByteCount = statbuf.st_size;
     }
+
+    g_outFD = openLogFile (g_outputFileName);
+
+    if (g_outFD < 0) {
+        logcat_panic(false, "couldn't open output file");
+    }
+
+    struct stat statbuf;
+    if (fstat(g_outFD, &statbuf) == -1) {
+        close(g_outFD);
+        logcat_panic(false, "couldn't get output file stat\n");
+    }
+
+    if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
+        close(g_outFD);
+        logcat_panic(false, "invalid output file stat\n");
+    }
+
+    g_outByteCount = statbuf.st_size;
 }
 
 static void show_help(const char *cmd)
@@ -284,6 +285,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"
@@ -443,7 +446,7 @@
     return t.strptime(cp, "%s.%q");
 }
 
-// Find last logged line in gestalt of all matching existing output files
+// Find last logged line in <outputFileName>, or <outputFileName>.1
 static log_time lastLogTime(char *outputFileName) {
     log_time retval(log_time::EPOCH);
     if (!outputFileName) {
@@ -468,24 +471,18 @@
         return retval;
     }
 
-    clockid_t clock_type = android_log_clockid();
-    log_time now(clock_type);
-    bool monotonic = clock_type == CLOCK_MONOTONIC;
+    log_time now(android_log_clockid());
 
     size_t len = strlen(file);
     log_time modulo(0, NS_PER_SEC);
     struct dirent *dp;
 
     while ((dp = readdir(dir.get())) != NULL) {
-        if ((dp->d_type != DT_REG)
-                // If we are using realtime, check all files that match the
-                // basename for latest time. If we are using monotonic time
-                // then only check the main file because time cycles on
-                // every reboot.
-                || strncmp(dp->d_name, file, len + monotonic)
-                || (dp->d_name[len]
-                    && ((dp->d_name[len] != '.')
-                        || !isdigit(dp->d_name[len+1])))) {
+        if ((dp->d_type != DT_REG) ||
+                (strncmp(dp->d_name, file, len) != 0) ||
+                (dp->d_name[len] &&
+                     ((dp->d_name[len] != '.') ||
+                         (strtoll(dp->d_name + 1, NULL, 10) != 1)))) {
             continue;
         }
 
@@ -547,6 +544,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;
@@ -574,6 +572,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[] = {
@@ -588,6 +587,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 },
@@ -613,7 +613,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)) {
@@ -644,6 +644,10 @@
                     g_printItAnyways = true;
                     break;
                 }
+                if (long_options[option_index].name == id_str) {
+                    setId = optarg && optarg[0] ? optarg : NULL;
+                    break;
+                }
             break;
 
             case 's':
@@ -657,7 +661,7 @@
             break;
 
             case 'L':
-                mode |= ANDROID_LOG_PSTORE;
+                mode |= ANDROID_LOG_RDONLY | ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK;
             break;
 
             case 'd':
@@ -968,7 +972,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");
@@ -1034,34 +1051,33 @@
             continue;
         }
 
-        if (clearLog) {
+        if (clearLog || setId) {
             if (g_outputFileName) {
                 int maxRotationCountDigits =
                     (g_maxRotatedLogs > 0) ? (int) (floor(log10(g_maxRotatedLogs) + 1)) : 0;
 
                 for (int i = g_maxRotatedLogs ; i >= 0 ; --i) {
-                    char *file;
+                    std::string file;
 
                     if (i == 0) {
-                        asprintf(&file, "%s", g_outputFileName);
+                        file = android::base::StringPrintf("%s", g_outputFileName);
                     } else {
-                        asprintf(&file, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i);
+                        file = android::base::StringPrintf("%s.%.*d",
+                            g_outputFileName, maxRotationCountDigits, i);
                     }
 
-                    if (!file) {
+                    if (file.length() == 0) {
                         perror("while clearing log files");
                         clearFail = clearFail ?: dev->device;
                         break;
                     }
 
-                    err = unlink(file);
+                    err = unlink(file.c_str());
 
                     if (err < 0 && errno != ENOENT && clearFail == NULL) {
                         perror("while clearing log files");
                         clearFail = dev->device;
                     }
-
-                    free(file);
                 }
             } else if (android_logger_clear(dev->logger)) {
                 clearFail = clearFail ?: dev->device;
@@ -1177,7 +1193,6 @@
         return EXIT_SUCCESS;
     }
 
-
     if (getLogSize) {
         return EXIT_SUCCESS;
     }
@@ -1188,6 +1203,8 @@
         return EXIT_SUCCESS;
     }
 
+    setupOutputAndSchedulingPolicy((mode & ANDROID_LOG_NONBLOCK) == 0);
+
     //LOG_EVENT_INT(10, 12345);
     //LOG_EVENT_LONG(11, 0x1122334455667788LL);
     //LOG_EVENT_STRING(0, "whassup, doc?");
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 4d5c80c..b082a64 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -36,7 +36,7 @@
     mkdir /data/misc/logd 0700 logd log
     # logd for write to /data/misc/logd, log group for read from pstore (-L)
     # b/28788401 b/30041146 b/30612424
-    # exec - logd log -- /system/bin/logcat -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256}
+    # exec - logd log -- /system/bin/logcat -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
     start logcatd
 
 # stop logcatd service and clear data
@@ -57,7 +57,7 @@
     stop logcatd
 
 # logcatd service
-service logcatd /system/bin/logcat -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256}
+service logcatd /system/bin/logcat -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
     class late_start
     disabled
     # logd for write to /data/misc/logd, log group for read from log daemon
diff --git a/logcat/logpersist b/logcat/logpersist
index 74d99fc..c09b6b2 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -17,8 +17,9 @@
    ;;
 esac
 
+log_uid=logd
 log_tag_property=persist.log.tag
-data=/data/misc/logd
+data=/data/misc/logd/logcat
 service=logcatd
 size_default=256
 buffer_default=all
@@ -74,11 +75,12 @@
   if [ -n "${size}${buffer}" -o "true" = "${clear}" ]; then
     echo WARNING: Can not use --clear, --size or --buffer with ${progname%.*}.cat >&2
   fi
-  su logd ls "${data}" |
+  su ${log_uid} ls "${data%/*}" |
   tr -d '\r' |
   sort -ru |
-  sed "s#^#${data}/#" |
-  su logd xargs cat
+  sed "s#^#${data%/*}/#" |
+  grep "${data}[.]*[0-9]*\$" |
+  su ${log_uid} xargs cat
   ;;
 *.start)
   current_buffer="`getprop ${property#persist.}.buffer`"
@@ -139,7 +141,7 @@
   sleep 1
   getprop ${property#persist.}
   # also generate an error return code if not found running
-  pgrep -u ${data##*/} ${service%d}
+  pgrep -u ${log_uid} ${service%d}
   ;;
 *.stop)
   if [ -n "${size}${buffer}" ]; then
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 0043d1b..3daee13 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 
@@ -848,6 +849,75 @@
     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, decltype(&closedir)> 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),