/*
* Copyright (C) 2014 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 <ctype.h>
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include "LogWhiteBlackList.h"
// White and Black list
Prune::Prune(uid_t uid, pid_t pid) : mUid(uid), mPid(pid) {
}
int Prune::cmp(uid_t uid, pid_t pid) const {
if ((mUid == uid_all) || (mUid == uid)) {
if (mPid == pid_all) {
return 0;
}
return pid - mPid;
}
return uid - mUid;
}
std::string Prune::format() {
if (mUid != uid_all) {
if (mPid != pid_all) {
return android::base::StringPrintf("%u/%u", mUid, mPid);
}
return android::base::StringPrintf("%u", mUid);
}
if (mPid != pid_all) {
return android::base::StringPrintf("/%u", mPid);
}
// NB: mPid == pid_all can not happen if mUid == uid_all
return std::string("/");
}
PruneList::PruneList() {
init(NULL);
}
PruneList::~PruneList() {
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end();) {
it = mNice.erase(it);
}
for (it = mNaughty.begin(); it != mNaughty.end();) {
it = mNaughty.erase(it);
}
}
int PruneList::init(const char *str) {
mWorstUidEnabled = true;
mWorstPidOfSystemEnabled = true;
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end();) {
it = mNice.erase(it);
}
for (it = mNaughty.begin(); it != mNaughty.end();) {
it = mNaughty.erase(it);
}
static const char _default[] = "default";
// default here means take ro.logd.filter, persist.logd.filter then
// internal default in that order.
if (str && !strcmp(str, _default)) {
str = NULL;
}
static const char _disable[] = "disable";
if (str && !strcmp(str, _disable)) {
str = "";
}
std::string filter;
if (str) {
filter = str;
} else {
char property[PROPERTY_VALUE_MAX];
property_get("ro.logd.filter", property, _default);
filter = property;
property_get("persist.logd.filter", property, filter.c_str());
// default here means take ro.logd.filter
if (strcmp(property, _default)) {
filter = property;
}
}
// default here means take internal default.
if (filter == _default) {
// See README.property for description of filter format
filter = "~! ~1000/!";
}
if (filter == _disable) {
filter = "";
}
mWorstUidEnabled = false;
mWorstPidOfSystemEnabled = false;
for(str = filter.c_str(); *str; ++str) {
if (isspace(*str)) {
continue;
}
PruneCollection *list;
if ((*str == '~') || (*str == '!')) { // ~ supported, ! undocumented
++str;
// special case, translates to worst UID at priority in blacklist
if (*str == '!') {
mWorstUidEnabled = true;
++str;
if (!*str) {
break;
}
if (!isspace(*str)) {
return 1;
}
continue;
}
// special case, translated to worst PID of System at priority
static const char worstPid[] = "1000/!";
if (!strncmp(str, worstPid, sizeof(worstPid) - 1)) {
mWorstPidOfSystemEnabled = true;
str += sizeof(worstPid) - 1;
if (!*str) {
break;
}
if (!isspace(*str)) {
return 1;
}
continue;
}
if (!*str) {
return 1;
}
list = &mNaughty;
} else {
list = &mNice;
}
uid_t uid = Prune::uid_all;
if (isdigit(*str)) {
uid = 0;
do {
uid = uid * 10 + *str++ - '0';
} while (isdigit(*str));
}
pid_t pid = Prune::pid_all;
if (*str == '/') {
++str;
if (isdigit(*str)) {
pid = 0;
do {
pid = pid * 10 + *str++ - '0';
} while (isdigit(*str));
}
}
if ((uid == Prune::uid_all) && (pid == Prune::pid_all)) {
return 1;
}
if (*str && !isspace(*str)) {
return 1;
}
// insert sequentially into list
PruneCollection::iterator it = list->begin();
while (it != list->end()) {
Prune &p = *it;
int m = uid - p.mUid;
if (m == 0) {
if (p.mPid == p.pid_all) {
break;
}
if ((pid == p.pid_all) && (p.mPid != p.pid_all)) {
it = list->erase(it);
continue;
}
m = pid - p.mPid;
}
if (m <= 0) {
if (m < 0) {
list->insert(it, Prune(uid,pid));
}
break;
}
++it;
}
if (it == list->end()) {
list->push_back(Prune(uid,pid));
}
if (!*str) {
break;
}
}
return 0;
}
std::string PruneList::format() {
static const char nice_format[] = " %s";
const char *fmt = nice_format + 1;
std::string string;
if (mWorstUidEnabled) {
string = "~!";
fmt = nice_format;
if (mWorstPidOfSystemEnabled) {
string += " ~1000/!";
}
}
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end(); ++it) {
string += android::base::StringPrintf(fmt, (*it).format().c_str());
fmt = nice_format;
}
static const char naughty_format[] = " ~%s";
fmt = naughty_format + (*fmt != ' ');
for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
string += android::base::StringPrintf(fmt, (*it).format().c_str());
fmt = naughty_format;
}
return string;
}
// ToDo: Lists are in sorted order, Prune->cmp() returns + or -
// If there is scaling issues, resort to a better algorithm than linear
// based on these assumptions.
bool PruneList::naughty(LogBufferElement *element) {
PruneCollection::iterator it;
for (it = mNaughty.begin(); it != mNaughty.end(); ++it) {
if (!(*it).cmp(element)) {
return true;
}
}
return false;
}
bool PruneList::nice(LogBufferElement *element) {
PruneCollection::iterator it;
for (it = mNice.begin(); it != mNice.end(); ++it) {
if (!(*it).cmp(element)) {
return true;
}
}
return false;
}