Merge "Use proper NO_ERROR checking KS return value"
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index be96fc4..5db539f 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -12,6 +12,7 @@
#include <signal.h>
#include <time.h>
#include <unistd.h>
+#include <sys/cdefs.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
@@ -24,7 +25,6 @@
#include <log/logprint.h>
#include <log/event_tag_map.h>
-#define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16
#define DEFAULT_MAX_ROTATED_LOGS 4
static AndroidLogFormat * g_logformat;
@@ -46,6 +46,8 @@
binary = b;
next = NULL;
printed = false;
+ logger = NULL;
+ logger_list = NULL;
}
};
@@ -54,13 +56,17 @@
/* Global Variables */
static const char * g_outputFileName = NULL;
-static int g_logRotateSizeKBytes = 0; // 0 means "no log rotation"
-static int g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded"
+// 0 means "no log rotation"
+static size_t g_logRotateSizeKBytes = 0;
+// 0 means "unbounded"
+static size_t g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
static int g_outFD = -1;
-static off_t g_outByteCount = 0;
+static size_t g_outByteCount = 0;
static int g_printBinary = 0;
static int g_devCount = 0; // >1 means multiple
+__noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
+
static int openLogFile (const char *pathname)
{
return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
@@ -93,7 +99,12 @@
asprintf(&file0, "%s.%.*d", g_outputFileName, maxRotationCountDigits, i - 1);
}
- err = rename (file0, file1);
+ if (!file0 || !file1) {
+ perror("while rotating log files");
+ break;
+ }
+
+ err = rename(file0, file1);
if (err < 0 && errno != ENOENT) {
perror("while rotating log files");
@@ -103,11 +114,10 @@
free(file0);
}
- g_outFD = openLogFile (g_outputFileName);
+ g_outFD = openLogFile(g_outputFileName);
if (g_outFD < 0) {
- perror ("couldn't open output file");
- exit(-1);
+ logcat_panic(false, "couldn't open output file");
}
g_outByteCount = 0;
@@ -153,8 +163,7 @@
bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
if (bytesWritten < 0) {
- perror("output error");
- exit(-1);
+ logcat_panic(false, "output error");
}
}
@@ -179,8 +188,7 @@
dev->printed ? "switch to" : "beginning of",
dev->device);
if (write(g_outFD, buf, strlen(buf)) < 0) {
- perror("output error");
- exit(-1);
+ logcat_panic(false, "output error");
}
}
dev->printed = true;
@@ -199,11 +207,18 @@
g_outFD = openLogFile (g_outputFileName);
if (g_outFD < 0) {
- perror ("couldn't open output file");
- exit(-1);
+ logcat_panic(false, "couldn't open output file");
}
- fstat(g_outFD, &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;
}
@@ -217,7 +232,7 @@
" -s Set default filter to silent.\n"
" Like specifying filterspec '*:S'\n"
" -f <filename> Log to file. Default to stdout\n"
- " -r [<kbytes>] Rotate log every kbytes. (16 if unspecified). Requires -f\n"
+ " -r <kbytes> Rotate log every kbytes. Requires -f\n"
" -n <count> Sets max number of rotated logs to <count>, default 4\n"
" -v <format> Sets the log print format, where <format> is:\n\n"
" brief color long process raw tag thread threadtime time\n\n"
@@ -265,9 +280,6 @@
"or defaults to \"threadtime\"\n\n");
}
-
-} /* namespace android */
-
static int setLogFormat(const char * formatString)
{
static AndroidLogPrintFormat format;
@@ -308,8 +320,48 @@
return multipliers[i];
}
+/*String to unsigned int, returns -1 if it fails*/
+static bool getSizeTArg(char *ptr, size_t *val, size_t min = 0,
+ size_t max = SIZE_MAX)
+{
+ char *endp;
+ errno = 0;
+ size_t ret = (size_t) strtoll(ptr, &endp, 0);
+
+ if (endp[0] != '\0' || errno != 0 ) {
+ return false;
+ }
+
+ if (ret > max || ret < min) {
+ return false;
+ }
+
+ *val = ret;
+ return true;
+}
+
+static void logcat_panic(bool showHelp, const char *fmt, ...)
+{
+ if (fmt) {
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ }
+
+ if (showHelp) {
+ show_help(getprogname());
+ }
+
+ exit(EXIT_FAILURE);
+}
+
+} /* namespace android */
+
+
int main(int argc, char **argv)
{
+ using namespace android;
int err;
int hasSetLogFormat = 0;
int clearLog = 0;
@@ -324,7 +376,7 @@
log_device_t* dev;
bool printDividers = false;
struct logger_list *logger_list;
- unsigned int tail_lines = 0;
+ size_t tail_lines = 0;
log_time tail_time(log_time::EPOCH);
signal(SIGPIPE, exit);
@@ -332,14 +384,14 @@
g_logformat = android_log_format_new();
if (argc == 2 && 0 == strcmp(argv[1], "--help")) {
- android::show_help(argv[0]);
- exit(0);
+ show_help(argv[0]);
+ return EXIT_SUCCESS;
}
for (;;) {
int ret;
- ret = getopt(argc, argv, "cdDLt:T:gG:sQf:r:n:v:b:BSpP:");
+ ret = getopt(argc, argv, ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:");
if (ret < 0) {
break;
@@ -372,10 +424,9 @@
char *cp = tail_time.strptime(optarg,
log_time::default_format);
if (!cp) {
- fprintf(stderr,
- "ERROR: -%c \"%s\" not in \"%s\" time format\n",
- ret, optarg, log_time::default_format);
- exit(1);
+ logcat_panic(false,
+ "-%c \"%s\" not in \"%s\" time format\n",
+ ret, optarg, log_time::default_format);
}
if (*cp) {
char c = *cp;
@@ -386,8 +437,7 @@
*cp = c;
}
} else {
- tail_lines = atoi(optarg);
- if (!tail_lines) {
+ if (!getSizeTArg(optarg, &tail_lines, 1)) {
fprintf(stderr,
"WARNING: -%c %s invalid, setting to 1\n",
ret, optarg);
@@ -405,13 +455,11 @@
break;
case 'G': {
- // would use atol if not for the multiplier
- char *cp = optarg;
- setLogSize = 0;
- while (('0' <= *cp) && (*cp <= '9')) {
- setLogSize *= 10;
- setLogSize += *cp - '0';
- ++cp;
+ char *cp;
+ if (strtoll(optarg, &cp, 0) > 0) {
+ setLogSize = strtoll(optarg, &cp, 0);
+ } else {
+ setLogSize = 0;
}
switch(*cp) {
@@ -436,7 +484,7 @@
if (!setLogSize) {
fprintf(stderr, "ERROR: -G <num><multiplier>\n");
- exit(1);
+ return EXIT_FAILURE;
}
}
break;
@@ -458,7 +506,7 @@
}
devices = dev = NULL;
- android::g_devCount = 0;
+ g_devCount = 0;
for(int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
const char *name = android_log_id_to_name((log_id_t)i);
log_id_t log_id = android_name_to_log_id(name);
@@ -476,7 +524,7 @@
} else {
devices = dev = d;
}
- android::g_devCount++;
+ g_devCount++;
}
break;
}
@@ -492,51 +540,36 @@
} else {
devices = new log_device_t(optarg, binary);
}
- android::g_devCount++;
+ g_devCount++;
}
break;
case 'B':
- android::g_printBinary = 1;
+ g_printBinary = 1;
break;
case 'f':
// redirect output to a file
-
- android::g_outputFileName = optarg;
+ g_outputFileName = optarg;
break;
case 'r':
- if (optarg == NULL) {
- android::g_logRotateSizeKBytes
- = DEFAULT_LOG_ROTATE_SIZE_KBYTES;
- } else {
- if (!isdigit(optarg[0])) {
- fprintf(stderr,"Invalid parameter to -r\n");
- android::show_help(argv[0]);
- exit(-1);
- }
- android::g_logRotateSizeKBytes = atoi(optarg);
+ if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
+ logcat_panic(true, "Invalid parameter %s to -r\n", optarg);
}
break;
case 'n':
- if (!isdigit(optarg[0])) {
- fprintf(stderr,"Invalid parameter to -r\n");
- android::show_help(argv[0]);
- exit(-1);
+ if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
+ logcat_panic(true, "Invalid parameter %s to -n\n", optarg);
}
-
- android::g_maxRotatedLogs = atoi(optarg);
break;
case 'v':
err = setLogFormat (optarg);
if (err < 0) {
- fprintf(stderr,"Invalid parameter to -v\n");
- android::show_help(argv[0]);
- exit(-1);
+ logcat_panic(true, "Invalid parameter %s to -v\n", optarg);
}
if (strcmp("color", optarg)) { // exception for modifiers
@@ -582,8 +615,9 @@
force_exit = 0;
}
/* if nothing found or invalid filters, exit quietly */
- if (force_exit)
- exit(0);
+ if (force_exit) {
+ return EXIT_SUCCESS;
+ }
/* redirect our output to the emulator console */
if (console) {
@@ -615,36 +649,34 @@
printStatistics = 1;
break;
+ case ':':
+ logcat_panic(true, "Option -%c needs an argument\n", optopt);
+ break;
+
default:
- fprintf(stderr,"Unrecognized Option\n");
- android::show_help(argv[0]);
- exit(-1);
- break;
+ logcat_panic(true, "Unrecognized Option %c\n", optopt);
+ break;
}
}
if (!devices) {
dev = devices = new log_device_t("main", false);
- android::g_devCount = 1;
+ g_devCount = 1;
if (android_name_to_log_id("system") == LOG_ID_SYSTEM) {
dev = dev->next = new log_device_t("system", false);
- android::g_devCount++;
+ g_devCount++;
}
if (android_name_to_log_id("crash") == LOG_ID_CRASH) {
dev = dev->next = new log_device_t("crash", false);
- android::g_devCount++;
+ g_devCount++;
}
}
- if (android::g_logRotateSizeKBytes != 0
- && android::g_outputFileName == NULL
- ) {
- fprintf(stderr,"-r requires -f as well\n");
- android::show_help(argv[0]);
- exit(-1);
+ if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) {
+ logcat_panic(true, "-r requires -f as well\n");
}
- android::setupOutput();
+ setupOutput();
if (hasSetLogFormat == 0) {
const char* logFormat = getenv("ANDROID_PRINTF_LOG");
@@ -663,8 +695,7 @@
if (forceFilters) {
err = android_log_addFilterString(g_logformat, forceFilters);
if (err < 0) {
- fprintf (stderr, "Invalid filter expression in -logcat option\n");
- exit(0);
+ logcat_panic(false, "Invalid filter expression in logcat args\n");
}
} else if (argc == optind) {
// Add from environment variable
@@ -674,10 +705,8 @@
err = android_log_addFilterString(g_logformat, env_tags_orig);
if (err < 0) {
- fprintf(stderr, "Invalid filter expression in"
- " ANDROID_LOG_TAGS\n");
- android::show_help(argv[0]);
- exit(-1);
+ logcat_panic(true,
+ "Invalid filter expression in ANDROID_LOG_TAGS\n");
}
}
} else {
@@ -686,9 +715,7 @@
err = android_log_addFilterString(g_logformat, argv[i]);
if (err < 0) {
- fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]);
- android::show_help(argv[0]);
- exit(-1);
+ logcat_panic(true, "Invalid filter expression '%s'\n", argv[i]);
}
}
}
@@ -704,22 +731,20 @@
dev->logger = android_logger_open(logger_list,
android_name_to_log_id(dev->device));
if (!dev->logger) {
- fprintf(stderr, "Unable to open log device '%s'\n", dev->device);
- exit(EXIT_FAILURE);
+ logcat_panic(false, "Unable to open log device '%s'\n",
+ dev->device);
}
if (clearLog) {
int ret;
ret = android_logger_clear(dev->logger);
if (ret) {
- perror("failed to clear the log");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "failed to clear the log");
}
}
if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
- perror("failed to set the log size");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "failed to set the log size");
}
if (getLogSize) {
@@ -727,14 +752,12 @@
size = android_logger_get_log_size(dev->logger);
if (size < 0) {
- perror("failed to get the log size");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "failed to get the log size");
}
readable = android_logger_get_log_readable_size(dev->logger);
if (readable < 0) {
- perror("failed to get the readable log size");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "failed to get the readable log size");
}
printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
@@ -748,16 +771,18 @@
}
if (setPruneList) {
- size_t len = strlen(setPruneList) + 32; // margin to allow rc
- char *buf = (char *) malloc(len);
-
- strcpy(buf, setPruneList);
- int ret = android_logger_set_prune_list(logger_list, buf, len);
- free(buf);
-
- if (ret) {
- perror("failed to set the prune list");
- exit(EXIT_FAILURE);
+ size_t len = strlen(setPruneList);
+ /*extra 32 bytes are needed by android_logger_set_prune_list */
+ size_t bLen = len + 32;
+ char *buf = NULL;
+ if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
+ buf[len] = '\0';
+ if (android_logger_set_prune_list(logger_list, buf, bLen)) {
+ logcat_panic(false, "failed to set the prune list");
+ }
+ free(buf);
+ } else {
+ logcat_panic(false, "failed to set the prune list (alloc)");
}
}
@@ -767,29 +792,28 @@
for(int retry = 32;
(retry >= 0) && ((buf = new char [len]));
- delete [] buf, --retry) {
+ delete [] buf, buf = NULL, --retry) {
if (getPruneList) {
android_logger_get_prune_list(logger_list, buf, len);
} else {
android_logger_get_statistics(logger_list, buf, len);
}
buf[len-1] = '\0';
- size_t ret = atol(buf) + 1;
- if (ret < 4) {
+ if (atol(buf) < 3) {
delete [] buf;
buf = NULL;
break;
}
- bool check = ret <= len;
- len = ret;
- if (check) {
+ size_t ret = atol(buf) + 1;
+ if (ret <= len) {
+ len = ret;
break;
}
+ len = ret;
}
if (!buf) {
- perror("failed to read data");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "failed to read data");
}
// remove trailing FF
@@ -813,18 +837,18 @@
printf("%s", cp);
delete [] buf;
- exit(0);
+ return EXIT_SUCCESS;
}
if (getLogSize) {
- exit(0);
+ return EXIT_SUCCESS;
}
if (setLogSize || setPruneList) {
- exit(0);
+ return EXIT_SUCCESS;
}
if (clearLog) {
- exit(0);
+ return EXIT_SUCCESS;
}
//LOG_EVENT_INT(10, 12345);
@@ -839,8 +863,7 @@
int ret = android_logger_list_read(logger_list, &log_msg);
if (ret == 0) {
- fprintf(stderr, "read: unexpected EOF!\n");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "read: unexpected EOF!\n");
}
if (ret < 0) {
@@ -849,15 +872,12 @@
}
if (ret == -EIO) {
- fprintf(stderr, "read: unexpected EOF!\n");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "read: unexpected EOF!\n");
}
if (ret == -EINVAL) {
- fprintf(stderr, "read: unexpected length.\n");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "read: unexpected length.\n");
}
- perror("logcat read failure");
- exit(EXIT_FAILURE);
+ logcat_panic(false, "logcat read failure");
}
for(d = devices; d; d = d->next) {
@@ -866,23 +886,23 @@
}
}
if (!d) {
- android::g_devCount = 2; // set to Multiple
+ g_devCount = 2; // set to Multiple
d = &unexpected;
d->binary = log_msg.id() == LOG_ID_EVENTS;
}
if (dev != d) {
dev = d;
- android::maybePrintStart(dev, printDividers);
+ maybePrintStart(dev, printDividers);
}
- if (android::g_printBinary) {
- android::printBinary(&log_msg);
+ if (g_printBinary) {
+ printBinary(&log_msg);
} else {
- android::processBuffer(dev, &log_msg);
+ processBuffer(dev, &log_msg);
}
}
android_logger_list_free(logger_list);
- return 0;
+ return EXIT_SUCCESS;
}
diff --git a/logd/Android.mk b/logd/Android.mk
index 127a66b..e65e9ff 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -32,8 +32,9 @@
# "s/^\([0-9]*\)[ \t]*$1[ \t].*/-D`echo $1 | tr a-z A-Z`_LOG_TAG=\1/p" \
# $(LOCAL_PATH)/$2/event.logtags)
# event_flag := $(call event_logtags,auditd)
+# event_flag += $(call event_logtags,logd)
# so make sure we do not regret hard-coding it as follows:
-event_flag := -DAUDITD_LOG_TAG=1003
+event_flag := -DAUDITD_LOG_TAG=1003 -DLOGD_LOG_TAG=1004
LOCAL_CFLAGS := -Werror $(event_flag)
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index a5844a3..260e237 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -279,7 +279,7 @@
size_t worst_sizes = 0;
size_t second_worst_sizes = 0;
- if ((id != LOG_ID_CRASH) && mPrune.worstUidEnabled()) {
+ if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
std::unique_ptr<const UidEntry *[]> sorted = stats.sort(2, id);
if (sorted.get()) {
@@ -297,6 +297,8 @@
}
bool kick = false;
+ bool leading = true;
+ LogBufferElement *last = NULL;
for(it = mLogElements.begin(); it != mLogElements.end();) {
LogBufferElement *e = *it;
@@ -309,26 +311,87 @@
continue;
}
- uid_t uid = e->getUid();
+ unsigned short dropped = e->getDropped();
- // !Worst and !BlackListed?
- if ((uid != worst) && (!hasBlacklist || !mPrune.naughty(e))) {
+ // remove any leading drops
+ if (leading && dropped) {
+ it = erase(it);
+ continue;
+ }
+
+ pid_t pid = e->getPid();
+
+ // merge any drops
+ if (last && dropped
+ && ((dropped + last->getDropped()) < USHRT_MAX)
+ && (last->getPid() == pid)
+ && (last->getTid() == e->getTid())) {
+ it = mLogElements.erase(it);
+ stats.erase(e);
+ delete e;
+ last->setDropped(dropped + last->getDropped());
+ continue;
+ }
+
+ leading = false;
+
+ if (hasBlacklist && mPrune.naughty(e)) {
+ last = NULL;
+ it = erase(it);
+ if (dropped) {
+ continue;
+ }
+
+ pruneRows--;
+ if (pruneRows == 0) {
+ break;
+ }
+
+ if (e->getUid() == worst) {
+ kick = true;
+ if (worst_sizes < second_worst_sizes) {
+ break;
+ }
+ worst_sizes -= e->getMsgLen();
+ }
+ continue;
+ }
+
+ if (dropped) {
+ last = e;
++it;
continue;
}
- unsigned short len = e->getMsgLen();
- it = erase(it);
+ if (e->getUid() != worst) {
+ last = NULL;
+ ++it;
+ continue;
+ }
+
pruneRows--;
if (pruneRows == 0) {
break;
}
- if (uid != worst) {
- continue;
- }
-
kick = true;
+
+ unsigned short len = e->getMsgLen();
+ stats.drop(e);
+ e->setDropped(1);
+ // merge any drops
+ if (last
+ && (last->getDropped() < (USHRT_MAX - 1))
+ && (last->getPid() == pid)
+ && (last->getTid() == e->getTid())) {
+ it = mLogElements.erase(it);
+ stats.erase(e);
+ delete e;
+ last->setDropped(last->getDropped() + 1);
+ } else {
+ last = e;
+ ++it;
+ }
if (worst_sizes < second_worst_sizes) {
break;
}
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 5e780b5..a173e63 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -14,14 +14,17 @@
* limitations under the License.
*/
+#include <endian.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <log/logger.h>
+#include <private/android_logger.h>
#include "LogBufferElement.h"
+#include "LogCommand.h"
#include "LogReader.h"
const uint64_t LogBufferElement::FLUSH_ERROR(0);
@@ -45,11 +48,59 @@
delete [] mMsg;
}
+// assumption: mMsg == NULL
+size_t LogBufferElement::populateDroppedMessage(char *&buffer, bool privileged) {
+ static const char format_uid[] = "uid=%u dropped=%u";
+ static const size_t unprivileged_offset = 7;
+ static const char tag[] = "logd";
+
+ size_t len;
+ if (privileged) {
+ len = snprintf(NULL, 0, format_uid, mUid, mDropped);
+ } else {
+ len = snprintf(NULL, 0, format_uid + unprivileged_offset, mDropped);
+ }
+
+ size_t hdrLen;
+ if (mLogId == LOG_ID_EVENTS) {
+ hdrLen = sizeof(android_log_event_string_t);
+ } else {
+ hdrLen = 1 + sizeof(tag);
+ }
+
+ buffer = static_cast<char *>(calloc(1, hdrLen + len + 1));
+ if (!buffer) {
+ return 0;
+ }
+
+ size_t retval = hdrLen + len;
+ if (mLogId == LOG_ID_EVENTS) {
+ android_log_event_string_t *e = reinterpret_cast<android_log_event_string_t *>(buffer);
+
+ e->header.tag = htole32(LOGD_LOG_TAG);
+ e->type = EVENT_TYPE_STRING;
+ e->length = htole32(len);
+ } else {
+ ++retval;
+ buffer[0] = ANDROID_LOG_INFO;
+ strcpy(buffer + 1, tag);
+ }
+
+ if (privileged) {
+ snprintf(buffer + hdrLen, len + 1, format_uid, mUid, mDropped);
+ } else {
+ snprintf(buffer + hdrLen, len + 1, format_uid + unprivileged_offset, mDropped);
+ }
+
+ return retval;
+}
+
uint64_t LogBufferElement::flushTo(SocketClient *reader) {
struct logger_entry_v3 entry;
+
memset(&entry, 0, sizeof(struct logger_entry_v3));
+
entry.hdr_size = sizeof(struct logger_entry_v3);
- entry.len = mMsgLen;
entry.lid = mLogId;
entry.pid = mPid;
entry.tid = mTid;
@@ -59,11 +110,26 @@
struct iovec iovec[2];
iovec[0].iov_base = &entry;
iovec[0].iov_len = sizeof(struct logger_entry_v3);
- iovec[1].iov_base = mMsg;
- iovec[1].iov_len = mMsgLen;
- if (reader->sendDatav(iovec, 2)) {
- return FLUSH_ERROR;
+
+ char *buffer = NULL;
+
+ if (!mMsg) {
+ entry.len = populateDroppedMessage(buffer, clientHasLogCredentials(reader));
+ if (!entry.len) {
+ return mSequence;
+ }
+ iovec[1].iov_base = buffer;
+ } else {
+ entry.len = mMsgLen;
+ iovec[1].iov_base = mMsg;
+ }
+ iovec[1].iov_len = entry.len;
+
+ uint64_t retval = reader->sendDatav(iovec, 2) ? FLUSH_ERROR : mSequence;
+
+ if (buffer) {
+ free(buffer);
}
- return mSequence;
+ return retval;
}
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 93671da..7b6456d 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -18,6 +18,7 @@
#define _LOGD_LOG_BUFFER_ELEMENT_H__
#include <stdatomic.h>
+#include <stdlib.h>
#include <sys/types.h>
#include <sysutils/SocketClient.h>
@@ -32,17 +33,27 @@
}
+static inline bool worstUidEnabledForLogid(log_id_t id) {
+ return (id != LOG_ID_CRASH) && (id != LOG_ID_EVENTS);
+}
+
class LogBufferElement {
const log_id_t mLogId;
const uid_t mUid;
const pid_t mPid;
const pid_t mTid;
char *mMsg;
- const unsigned short mMsgLen;
+ union {
+ const unsigned short mMsgLen; // mMSg != NULL
+ unsigned short mDropped; // mMsg == NULL
+ };
const uint64_t mSequence;
const log_time mRealTime;
static atomic_int_fast64_t sequence;
+ // assumption: mMsg == NULL
+ size_t populateDroppedMessage(char *&buffer, bool privileged);
+
public:
LogBufferElement(log_id_t log_id, log_time realtime,
uid_t uid, pid_t pid, pid_t tid,
@@ -53,7 +64,15 @@
uid_t getUid(void) const { return mUid; }
pid_t getPid(void) const { return mPid; }
pid_t getTid(void) const { return mTid; }
- unsigned short getMsgLen() const { return mMsgLen; }
+ unsigned short getDropped(void) const { return mMsg ? 0 : mDropped; }
+ unsigned short setDropped(unsigned short value) {
+ if (mMsg) {
+ free(mMsg);
+ mMsg = NULL;
+ }
+ return mDropped = value;
+ }
+ unsigned short getMsgLen() const { return mMsg ? mMsgLen : 0; }
uint64_t getSequence(void) const { return mSequence; }
static uint64_t getCurrentSequence(void) { return sequence.load(memory_order_relaxed); }
log_time getRealTime(void) const { return mRealTime; }
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index 3fbcfd6..40fbfae 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -71,16 +71,19 @@
++mElements[log_id];
uid_t uid = e->getUid();
+ unsigned short dropped = e->getDropped();
android::hash_t hash = android::hash_type(uid);
uidTable_t &table = uidTable[log_id];
ssize_t index = table.find(-1, hash, uid);
if (index == -1) {
UidEntry initEntry(uid);
initEntry.add(size);
+ initEntry.add_dropped(dropped);
table.add(hash, initEntry);
} else {
UidEntry &entry = table.editEntryAt(index);
entry.add(size);
+ entry.add_dropped(dropped);
}
mSizesTotal[log_id] += size;
@@ -96,6 +99,7 @@
if (index == -1) {
PidEntry initEntry(pid, uid, android::pidToName(pid));
initEntry.add(size);
+ initEntry.add_dropped(dropped);
pidTable.add(hash, initEntry);
} else {
PidEntry &entry = pidTable.editEntryAt(index);
@@ -109,6 +113,7 @@
}
}
entry.add(size);
+ entry.add_dropped(dropped);
}
}
@@ -119,12 +124,13 @@
--mElements[log_id];
uid_t uid = e->getUid();
+ unsigned short dropped = e->getDropped();
android::hash_t hash = android::hash_type(uid);
uidTable_t &table = uidTable[log_id];
ssize_t index = table.find(-1, hash, uid);
if (index != -1) {
UidEntry &entry = table.editEntryAt(index);
- if (entry.subtract(size)) {
+ if (entry.subtract(size) || entry.subtract_dropped(dropped)) {
table.removeAt(index);
}
}
@@ -138,12 +144,43 @@
index = pidTable.find(-1, hash, pid);
if (index != -1) {
PidEntry &entry = pidTable.editEntryAt(index);
- if (entry.subtract(size)) {
+ if (entry.subtract(size) || entry.subtract_dropped(dropped)) {
pidTable.removeAt(index);
}
}
}
+// Atomically set an entry to drop
+// entry->setDropped(1) must follow this call, caller should do this explicitly.
+void LogStatistics::drop(LogBufferElement *e) {
+ log_id_t log_id = e->getLogId();
+ unsigned short size = e->getMsgLen();
+ mSizes[log_id] -= size;
+
+ uid_t uid = e->getUid();
+ android::hash_t hash = android::hash_type(uid);
+ typeof uidTable[0] &table = uidTable[log_id];
+ ssize_t index = table.find(-1, hash, uid);
+ if (index != -1) {
+ UidEntry &entry = table.editEntryAt(index);
+ entry.subtract(size);
+ entry.add_dropped(1);
+ }
+
+ if (!enable) {
+ return;
+ }
+
+ pid_t pid = e->getPid();
+ hash = android::hash_type(pid);
+ index = pidTable.find(-1, hash, pid);
+ if (index != -1) {
+ PidEntry &entry = pidTable.editEntryAt(index);
+ entry.subtract(size);
+ entry.add_dropped(1);
+ }
+}
+
// caller must own and free character string
char *LogStatistics::uidToName(uid_t uid) {
// Local hard coded favourites
@@ -191,12 +228,22 @@
}
static void format_line(android::String8 &output,
- android::String8 &name, android::String8 &size) {
- static const size_t total_len = 70;
+ android::String8 &name, android::String8 &size, android::String8 &pruned) {
+ static const size_t pruned_len = 6;
+ static const size_t total_len = 70 + pruned_len;
- output.appendFormat("%s%*s\n", name.string(),
- (int)std::max(total_len - name.length() - 1, size.length() + 1),
- size.string());
+ ssize_t drop_len = std::max(pruned.length() + 1, pruned_len);
+ ssize_t size_len = std::max(size.length() + 1,
+ total_len - name.length() - drop_len - 1);
+
+ if (pruned.length()) {
+ output.appendFormat("%s%*s%*s\n", name.string(),
+ (int)size_len, size.string(),
+ (int)drop_len, pruned.string());
+ } else {
+ output.appendFormat("%s%*s\n", name.string(),
+ (int)size_len, size.string());
+ }
}
void LogStatistics::format(char **buf, uid_t uid, unsigned int logMask) {
@@ -285,14 +332,18 @@
output.appendFormat(
"\n\nChattiest UIDs in %s:\n",
android_log_id_to_name(id));
- android::String8 name("UID");
- android::String8 size("Size");
- format_line(output, name, size);
} else {
output.appendFormat(
"\n\nLogging for your UID in %s:\n",
android_log_id_to_name(id));
}
+ android::String8 name("UID");
+ android::String8 size("Size");
+ android::String8 pruned("Pruned");
+ if (!worstUidEnabledForLogid(id)) {
+ pruned.setTo("");
+ }
+ format_line(output, name, size, pruned);
headerPrinted = true;
}
@@ -307,7 +358,13 @@
android::String8 size("");
size.appendFormat("%zu", entry->getSizes());
- format_line(output, name, size);
+ android::String8 pruned("");
+ size_t dropped = entry->getDropped();
+ if (dropped) {
+ pruned.appendFormat("%zu", dropped);
+ }
+
+ format_line(output, name, size, pruned);
}
}
@@ -330,7 +387,8 @@
}
android::String8 name(" PID/UID");
android::String8 size("Size");
- format_line(output, name, size);
+ android::String8 pruned("Pruned");
+ format_line(output, name, size, pruned);
headerPrinted = true;
}
@@ -350,7 +408,13 @@
android::String8 size("");
size.appendFormat("%zu", entry->getSizes());
- format_line(output, name, size);
+ android::String8 pruned("");
+ size_t dropped = entry->getDropped();
+ if (dropped) {
+ pruned.appendFormat("%zu", dropped);
+ }
+
+ format_line(output, name, size, pruned);
}
}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index a65ffe0..f3110d7 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -78,13 +78,18 @@
struct UidEntry {
const uid_t uid;
size_t size;
+ size_t dropped;
- UidEntry(uid_t uid):uid(uid),size(0) { }
+ UidEntry(uid_t uid):uid(uid),size(0),dropped(0) { }
inline const uid_t&getKey() const { return uid; }
size_t getSizes() const { return size; }
+ size_t getDropped() const { return dropped; }
+
inline void add(size_t s) { size += s; }
- inline bool subtract(size_t s) { size -= s; return !size; }
+ inline void add_dropped(size_t d) { dropped += d; }
+ inline bool subtract(size_t s) { size -= s; return !dropped && !size; }
+ inline bool subtract_dropped(size_t d) { dropped -= d; return !dropped && !size; }
};
struct PidEntry {
@@ -92,13 +97,15 @@
uid_t uid;
char *name;
size_t size;
+ size_t dropped;
- PidEntry(pid_t p, uid_t u, char *n):pid(p),uid(u),name(n),size(0) { }
+ PidEntry(pid_t p, uid_t u, char *n):pid(p),uid(u),name(n),size(0),dropped(0) { }
PidEntry(const PidEntry &c):
pid(c.pid),
uid(c.uid),
name(c.name ? strdup(c.name) : NULL),
- size(c.size) { }
+ size(c.size),
+ dropped(c.dropped) { }
~PidEntry() { free(name); }
const pid_t&getKey() const { return pid; }
@@ -107,8 +114,11 @@
const char*getName() const { return name; }
char *setName(char *n) { free(name); return name = n; }
size_t getSizes() const { return size; }
+ size_t getDropped() const { return dropped; }
inline void add(size_t s) { size += s; }
- inline bool subtract(size_t s) { size -= s; return !size; }
+ inline void add_dropped(size_t d) { dropped += d; }
+ inline bool subtract(size_t s) { size -= s; return !dropped && !size; }
+ inline bool subtract_dropped(size_t d) { dropped -= d; return !dropped && !size; }
};
// Log Statistics
@@ -134,6 +144,10 @@
void add(LogBufferElement *entry);
void subtract(LogBufferElement *entry);
+ // entry->setDropped(1) must follow this call
+ void drop(LogBufferElement *entry);
+ // Correct for merging two entries referencing dropped content
+ void erase(LogBufferElement *e) { --mElements[e->getLogId()]; }
std::unique_ptr<const UidEntry *[]> sort(size_t n, log_id i) { return uidTable[i].sort(n); }
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index 6910854..bee940d 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -15,7 +15,6 @@
*/
#include <ctype.h>
-#include <malloc.h>
#include <utils/String8.h>
@@ -53,7 +52,7 @@
}
PruneList::PruneList()
- : mWorstUidEnabled(false) {
+ : mWorstUidEnabled(true) {
mNaughty.clear();
mNice.clear();
}
@@ -71,7 +70,7 @@
}
int PruneList::init(char *str) {
- mWorstUidEnabled = false;
+ mWorstUidEnabled = true;
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end();) {
delete (*it);
diff --git a/logd/event.logtags b/logd/event.logtags
index a63f034..db8c19b 100644
--- a/logd/event.logtags
+++ b/logd/event.logtags
@@ -34,3 +34,4 @@
# TODO: generate ".java" and ".h" files with integer constants from this file.
1003 auditd (avc|3)
+1004 logd (dropped|3)
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c4f9f5c..86b769f 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -328,6 +328,9 @@
# Set SELinux security contexts on upgrade or policy update.
restorecon_recursive /data
+ # Check any timezone data in /data is newer than the copy in /system, delete if not.
+ exec u:r:tzdatacheck:s0 system system -- /system/bin/tzdatacheck /system/usr/share/zoneinfo /data/misc/zoneinfo
+
# If there is no fs-post-data action in the init.<device>.rc file, you
# must uncomment this line, otherwise encrypted filesystems
# won't work.
diff --git a/tzdatacheck/Android.mk b/tzdatacheck/Android.mk
new file mode 100644
index 0000000..0e25f7d
--- /dev/null
+++ b/tzdatacheck/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# ========================================================
+# Executable
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= tzdatacheck.cpp
+LOCAL_MODULE := tzdatacheck
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_CFLAGS := -Werror
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= tzdatacheck.cpp
+LOCAL_MODULE := tzdatacheck
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_CFLAGS := -Werror
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
new file mode 100644
index 0000000..31f7b55
--- /dev/null
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <ftw.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+
+static const char* TZDATA_FILENAME = "/tzdata";
+// tzdata file header (as much as we need for the version):
+// byte[11] tzdata_version -- e.g. "tzdata2012f"
+static const int TZ_HEADER_LENGTH = 11;
+
+static void usage() {
+ std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
+ "\n"
+ "Compares the headers of two tzdata files. If the one in SYSTEM_TZ_DIR is the\n"
+ "same or a higher version than the one in DATA_TZ_DIR the DATA_TZ_DIR is renamed\n"
+ "and then deleted.\n";
+ exit(1);
+}
+
+/*
+ * Opens a file and fills headerBytes with the first byteCount bytes from the file. It is a fatal
+ * error if the file is too small or cannot be opened. If the file does not exist false is returned.
+ * If the bytes were read successfully then true is returned.
+ */
+static bool readHeader(const std::string& tzDataFileName, char* headerBytes, size_t byteCount) {
+ FILE* tzDataFile = fopen(tzDataFileName.c_str(), "r");
+ if (tzDataFile == nullptr) {
+ if (errno == ENOENT) {
+ return false;
+ } else {
+ PLOG(FATAL) << "Error opening tzdata file " << tzDataFileName;
+ }
+ }
+ size_t bytesRead = fread(headerBytes, 1, byteCount, tzDataFile);
+ if (bytesRead != byteCount) {
+ LOG(FATAL) << tzDataFileName << " is too small. " << byteCount << " bytes required";
+ }
+ fclose(tzDataFile);
+ return true;
+}
+
+/* Checks the contents of headerBytes. It is a fatal error if it not a tzdata header. */
+static void checkValidHeader(const std::string& fileName, char* headerBytes) {
+ if (strncmp("tzdata", headerBytes, 6) != 0) {
+ LOG(FATAL) << fileName << " does not start with the expected bytes (tzdata)";
+ }
+}
+
+/* Return the parent directory of dirName. */
+static std::string getParentDir(const std::string& dirName) {
+ std::unique_ptr<char> mutable_dirname(strdup(dirName.c_str()));
+ return dirname(mutable_dirname.get());
+}
+
+/* Deletes a single file, symlink or directory. Called from nftw(). */
+static int deleteFn(const char* fpath, const struct stat*, int typeflag, struct FTW*) {
+ LOG(DEBUG) << "Inspecting " << fpath;
+ switch (typeflag) {
+ case FTW_F:
+ case FTW_SL:
+ LOG(DEBUG) << "Unlinking " << fpath;
+ if (unlink(fpath)) {
+ PLOG(WARNING) << "Failed to unlink file/symlink " << fpath;
+ }
+ break;
+ case FTW_D:
+ case FTW_DP:
+ LOG(DEBUG) << "Removing dir " << fpath;
+ if (rmdir(fpath)) {
+ PLOG(WARNING) << "Failed to remove dir " << fpath;
+ }
+ break;
+ default:
+ LOG(WARNING) << "Unsupported file type " << fpath << ": " << typeflag;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Deletes dirToDelete and returns true if it is successful in removing or moving the directory out
+ * of the way. If dirToDelete does not exist this function does nothing and returns true.
+ *
+ * During deletion, this function first renames the directory to a temporary name. If the temporary
+ * directory cannot be created, or the directory cannot be renamed, false is returned. After the
+ * rename, deletion of files and subdirs beneath the directory is performed on a "best effort"
+ * basis. Symlinks beneath the directory are not followed.
+ */
+static bool deleteDir(const std::string& dirToDelete) {
+ // Check whether the dir exists.
+ struct stat buf;
+ if (stat(dirToDelete.c_str(), &buf) == 0) {
+ if (!S_ISDIR(buf.st_mode)) {
+ LOG(WARNING) << dirToDelete << " is not a directory";
+ return false;
+ }
+ } else {
+ if (errno == ENOENT) {
+ PLOG(INFO) << "Directory does not exist: " << dirToDelete;
+ return true;
+ } else {
+ PLOG(WARNING) << "Unable to stat " << dirToDelete;
+ return false;
+ }
+ }
+
+ // First, rename dirToDelete.
+ std::string tempDirNameTemplate = getParentDir(dirToDelete);
+ tempDirNameTemplate += "/tempXXXXXX";
+
+ // Create an empty directory with the temporary name. For this we need a non-const char*.
+ std::vector<char> tempDirName(tempDirNameTemplate.length() + 1);
+ strcpy(&tempDirName[0], tempDirNameTemplate.c_str());
+ if (mkdtemp(&tempDirName[0]) == nullptr) {
+ PLOG(WARNING) << "Unable to create a temporary directory: " << tempDirNameTemplate;
+ return false;
+ }
+
+ // Rename dirToDelete to tempDirName.
+ int rc = rename(dirToDelete.c_str(), &tempDirName[0]);
+ if (rc == -1) {
+ PLOG(WARNING) << "Unable to rename directory from " << dirToDelete << " to "
+ << &tempDirName[0];
+ return false;
+ }
+
+ // Recursively delete contents of tempDirName.
+ rc = nftw(&tempDirName[0], deleteFn, 10 /* openFiles */,
+ FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+ if (rc == -1) {
+ LOG(INFO) << "Could not delete directory: " << &tempDirName[0];
+ }
+ return true;
+}
+
+/*
+ * After a platform update it is likely that timezone data found on the system partition will be
+ * newer than the version found in the data partition. This tool detects this case and removes the
+ * version in /data along with any update metadata.
+ *
+ * Note: This code is related to code in com.android.server.updates.TzDataInstallReceiver. The
+ * paths for the metadata and current timezone data must match.
+ *
+ * Typically on device the two args will be:
+ * /system/usr/share/zoneinfo /data/misc/zoneinfo
+ *
+ * See usage() for usage notes.
+ */
+int main(int argc, char* argv[]) {
+ if (argc != 3) {
+ usage();
+ }
+
+ const char* systemZoneInfoDir = argv[1];
+ const char* dataZoneInfoDir = argv[2];
+
+ std::string dataCurrentDirName(dataZoneInfoDir);
+ dataCurrentDirName += "/current";
+ std::string dataTzDataFileName(dataCurrentDirName);
+ dataTzDataFileName += TZDATA_FILENAME;
+
+ std::vector<char> dataTzDataHeader;
+ dataTzDataHeader.reserve(TZ_HEADER_LENGTH);
+
+ bool dataFileExists = readHeader(dataTzDataFileName, dataTzDataHeader.data(), TZ_HEADER_LENGTH);
+ if (!dataFileExists) {
+ LOG(INFO) << "tzdata file " << dataTzDataFileName << " does not exist. No action required.";
+ return 0;
+ }
+ checkValidHeader(dataTzDataFileName, dataTzDataHeader.data());
+
+ std::string systemTzDataFileName(systemZoneInfoDir);
+ systemTzDataFileName += TZDATA_FILENAME;
+ std::vector<char> systemTzDataHeader;
+ systemTzDataHeader.reserve(TZ_HEADER_LENGTH);
+ bool systemFileExists =
+ readHeader(systemTzDataFileName, systemTzDataHeader.data(), TZ_HEADER_LENGTH);
+ if (!systemFileExists) {
+ LOG(FATAL) << systemTzDataFileName << " does not exist or could not be opened";
+ }
+ checkValidHeader(systemTzDataFileName, systemTzDataHeader.data());
+
+ if (strncmp(&systemTzDataHeader[0], &dataTzDataHeader[0], TZ_HEADER_LENGTH) < 0) {
+ LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the newer than "
+ << systemTzDataFileName << ". No action required.";
+ } else {
+ // We have detected the case this tool is intended to prevent. Go fix it.
+ LOG(INFO) << "tzdata file " << dataTzDataFileName << " is the same as or older than "
+ << systemTzDataFileName << "; fixing...";
+
+ // Delete the update metadata
+ std::string dataUpdatesDirName(dataZoneInfoDir);
+ dataUpdatesDirName += "/updates";
+ LOG(INFO) << "Removing: " << dataUpdatesDirName;
+ bool deleted = deleteDir(dataUpdatesDirName);
+ if (!deleted) {
+ LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
+ << " was not successful";
+ }
+
+ // Delete the TZ data
+ LOG(INFO) << "Removing: " << dataCurrentDirName;
+ deleted = deleteDir(dataCurrentDirName);
+ if (!deleted) {
+ LOG(WARNING) << "Deletion of tzdata " << dataCurrentDirName << " was not successful";
+ }
+ }
+
+ return 0;
+}