1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "PruneList.h"
18 
19 #include <ctype.h>
20 
21 #include <android-base/logging.h>
22 #include <android-base/properties.h>
23 #include <android-base/stringprintf.h>
24 #include <android-base/strings.h>
25 
Matches(LogBufferElement * element) const26 bool Prune::Matches(LogBufferElement* element) const {
27     return (uid_ == UID_ALL || uid_ == element->uid()) &&
28            (pid_ == PID_ALL || pid_ == element->pid());
29 }
30 
Format() const31 std::string Prune::Format() const {
32     if (uid_ != UID_ALL) {
33         if (pid_ != PID_ALL) {
34             return android::base::StringPrintf("%u/%u", uid_, pid_);
35         }
36         return android::base::StringPrintf("%u", uid_);
37     }
38     if (pid_ != PID_ALL) {
39         return android::base::StringPrintf("/%u", pid_);
40     }
41     // NB: pid_ == PID_ALL can not happen if uid_ == UID_ALL
42     return std::string("/");
43 }
44 
PruneList()45 PruneList::PruneList() {
46     Init(nullptr);
47 }
48 
Init(const char * str)49 bool PruneList::Init(const char* str) {
50     high_priority_prune_.clear();
51     low_priority_prune_.clear();
52 
53     // default here means take ro.logd.filter, persist.logd.filter then internal default in order.
54     if (str && !strcmp(str, "default")) {
55         str = nullptr;
56     }
57     if (str && !strcmp(str, "disable")) {
58         str = "";
59     }
60 
61     std::string filter;
62 
63     if (str) {
64         filter = str;
65     } else {
66         filter = android::base::GetProperty("ro.logd.filter", "default");
67         auto persist_filter = android::base::GetProperty("persist.logd.filter", "default");
68         // default here means take ro.logd.filter
69         if (persist_filter != "default") {
70             filter = persist_filter;
71         }
72     }
73 
74     // default here means take internal default.
75     if (filter == "default") {
76         filter = "~! ~1000/!";
77     }
78     if (filter == "disable") {
79         filter = "";
80     }
81 
82     worst_uid_enabled_ = false;
83     worst_pid_of_system_enabled_ = false;
84 
85     for (str = filter.c_str(); *str; ++str) {
86         if (isspace(*str)) {
87             continue;
88         }
89 
90         std::list<Prune>* list;
91         if (*str == '~' || *str == '!') {  // ~ supported, ! undocumented
92             ++str;
93             // special case, prune the worst UID of those using at least 1/8th of the buffer.
94             if (*str == '!') {
95                 worst_uid_enabled_ = true;
96                 ++str;
97                 if (!*str) {
98                     break;
99                 }
100                 if (!isspace(*str)) {
101                     LOG(ERROR) << "Nothing expected after '~!', but found '" << str << "'";
102                     return false;
103                 }
104                 continue;
105             }
106             // special case, translated to worst PID of System at priority
107             static const char WORST_SYSTEM_PID[] = "1000/!";
108             if (!strncmp(str, WORST_SYSTEM_PID, sizeof(WORST_SYSTEM_PID) - 1)) {
109                 worst_pid_of_system_enabled_ = true;
110                 str += sizeof(WORST_SYSTEM_PID) - 1;
111                 if (!*str) {
112                     break;
113                 }
114                 if (!isspace(*str)) {
115                     LOG(ERROR) << "Nothing expected after '~1000/!', but found '" << str << "'";
116                     return false;
117                 }
118                 continue;
119             }
120             if (!*str) {
121                 LOG(ERROR) << "Expected UID or PID after '~', but found nothing";
122                 return false;
123             }
124             list = &high_priority_prune_;
125         } else {
126             list = &low_priority_prune_;
127         }
128 
129         uid_t uid = Prune::UID_ALL;
130         if (isdigit(*str)) {
131             uid = 0;
132             do {
133                 uid = uid * 10 + *str++ - '0';
134             } while (isdigit(*str));
135         }
136 
137         pid_t pid = Prune::PID_ALL;
138         if (*str == '/') {
139             ++str;
140             if (isdigit(*str)) {
141                 pid = 0;
142                 do {
143                     pid = pid * 10 + *str++ - '0';
144                 } while (isdigit(*str));
145             }
146         }
147 
148         if (uid == Prune::UID_ALL && pid == Prune::PID_ALL) {
149             LOG(ERROR) << "Expected UID/PID combination, but found none";
150             return false;
151         }
152 
153         if (*str && !isspace(*str)) {
154             LOG(ERROR) << "Nothing expected after UID/PID combination, but found '" << str << "'";
155             return false;
156         }
157 
158         list->emplace_back(uid, pid);
159         if (!*str) {
160             break;
161         }
162     }
163 
164     return true;
165 }
166 
Format() const167 std::string PruneList::Format() const {
168     std::vector<std::string> prune_rules;
169 
170     if (worst_uid_enabled_) {
171         prune_rules.emplace_back("~!");
172     }
173     if (worst_pid_of_system_enabled_) {
174         prune_rules.emplace_back("~1000/!");
175     }
176     for (const auto& rule : low_priority_prune_) {
177         prune_rules.emplace_back(rule.Format());
178     }
179     for (const auto& rule : high_priority_prune_) {
180         prune_rules.emplace_back("~" + rule.Format());
181     }
182     return android::base::Join(prune_rules, " ");
183 }
184 
IsHighPriority(LogBufferElement * element) const185 bool PruneList::IsHighPriority(LogBufferElement* element) const {
186     for (const auto& rule : high_priority_prune_) {
187         if (rule.Matches(element)) {
188             return true;
189         }
190     }
191     return false;
192 }
193 
IsLowPriority(LogBufferElement * element) const194 bool PruneList::IsLowPriority(LogBufferElement* element) const {
195     for (const auto& rule : low_priority_prune_) {
196         if (rule.Matches(element)) {
197             return true;
198         }
199     }
200     return false;
201 }
202