1//
2// Copyright (C) 2012 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 "shill/scope_logger.h"
18
19#include <vector>
20
21#include <base/strings/string_tokenizer.h>
22#include <base/strings/string_util.h>
23
24using base::StringTokenizer;
25using std::string;
26using std::vector;
27
28namespace shill {
29
30namespace {
31
32const int kDefaultVerboseLevel = 0;
33
34// Scope names corresponding to the scope defined by ScopeLogger::Scope.
35const char* const kScopeNames[] = {
36  "binder",
37  "cellular",
38  "connection",
39  "crypto",
40  "daemon",
41  "dbus",
42  "device",
43  "dhcp",
44  "dns",
45  "ethernet",
46  "http",
47  "httpproxy",
48  "inet",
49  "link",
50  "manager",
51  "metrics",
52  "modem",
53  "portal",
54  "power",
55  "ppp",
56  "pppoe",
57  "profile",
58  "property",
59  "resolver",
60  "route",
61  "rtnl",
62  "service",
63  "storage",
64  "task",
65  "vpn",
66  "wifi",
67  "wimax",
68};
69
70static_assert(arraysize(kScopeNames) == ScopeLogger::kNumScopes,
71              "Scope tags do not have expected number of strings");
72
73// ScopeLogger needs to be a 'leaky' singleton as it needs to survive to
74// handle logging till the very end of the shill process. Making ScopeLogger
75// leaky is fine as it does not need to clean up or release any resource at
76// destruction.
77base::LazyInstance<ScopeLogger>::Leaky g_scope_logger =
78    LAZY_INSTANCE_INITIALIZER;
79
80}  // namespace
81
82// static
83ScopeLogger* ScopeLogger::GetInstance() {
84  return g_scope_logger.Pointer();
85}
86
87ScopeLogger::ScopeLogger()
88    : verbose_level_(kDefaultVerboseLevel) {
89}
90
91ScopeLogger::~ScopeLogger() {
92}
93
94bool ScopeLogger::IsLogEnabled(Scope scope, int verbose_level) const {
95  return IsScopeEnabled(scope) && verbose_level <= verbose_level_;
96}
97
98bool ScopeLogger::IsScopeEnabled(Scope scope) const {
99  CHECK_GE(scope, 0);
100  CHECK_LT(scope, kNumScopes);
101
102  return scope_enabled_[scope];
103}
104
105string ScopeLogger::GetAllScopeNames() const {
106  vector<string> names(kScopeNames, kScopeNames + arraysize(kScopeNames));
107  return base::JoinString(names, "+");
108}
109
110string ScopeLogger::GetEnabledScopeNames() const {
111  vector<string> names;
112  for (size_t i = 0; i < arraysize(kScopeNames); ++i) {
113    if (scope_enabled_[i])
114      names.push_back(kScopeNames[i]);
115  }
116  return base::JoinString(names, "+");
117}
118
119void ScopeLogger::EnableScopesByName(const string& expression) {
120  if (expression.empty()) {
121    DisableAllScopes();
122    return;
123  }
124
125  // As described in the header file, if the first scope name in the
126  // sequence specified by |expression| is not prefixed by a plus or
127  // minus sign, it indicates that all scopes are first disabled before
128  // enabled by |expression|.
129  if (expression[0] != '+' && expression[0] != '-')
130    DisableAllScopes();
131
132  bool enable_scope = true;
133  StringTokenizer tokenizer(expression, "+-");
134  tokenizer.set_options(StringTokenizer::RETURN_DELIMS);
135  while (tokenizer.GetNext()) {
136    if (tokenizer.token_is_delim()) {
137      enable_scope = (tokenizer.token() == "+");
138      continue;
139    }
140
141    if (tokenizer.token().empty())
142      continue;
143
144    size_t i;
145    for (i = 0; i < arraysize(kScopeNames); ++i) {
146      if (tokenizer.token() == kScopeNames[i]) {
147        SetScopeEnabled(static_cast<Scope>(i), enable_scope);
148        break;
149      }
150    }
151    LOG_IF(WARNING, i == arraysize(kScopeNames))
152        << "Unknown scope '" << tokenizer.token() << "'";
153  }
154}
155
156void ScopeLogger::RegisterScopeEnableChangedCallback(
157    Scope scope, ScopeEnableChangedCallback callback) {
158  CHECK_GE(scope, 0);
159  CHECK_LT(scope, kNumScopes);
160  log_scope_callbacks_[scope].push_back(callback);
161}
162
163void ScopeLogger::DisableAllScopes() {
164  // Iterate over all scopes so the notification side-effect occurs.
165  for (size_t i = 0; i < arraysize(kScopeNames); ++i) {
166    SetScopeEnabled(static_cast<Scope>(i), false);
167  }
168}
169
170void ScopeLogger::SetScopeEnabled(Scope scope, bool enabled) {
171  CHECK_GE(scope, 0);
172  CHECK_LT(scope, kNumScopes);
173
174  if (scope_enabled_[scope] != enabled) {
175    for (const auto& callback : log_scope_callbacks_[scope]) {
176      callback.Run(enabled);
177    }
178  }
179
180  scope_enabled_[scope] = enabled;
181}
182
183}  // namespace shill
184