1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2014 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include "config.h"
19 
20 #include "ioctl.h"
21 
22 #include "alloc_hooks.h"
23 #include "connection.h"
24 #include "connections.h"
25 #include "cookie.h"
26 #include "tracing.h"
27 #include "utilities/string_utilities.h"
28 
29 #include <mcbp/mcbp.h>
30 
31 /*
32  * Implement ioctl-style memcached commands (ioctl_get / ioctl_set).
33  */
34 
35 /**
36  * Function interface for ioctl_get callbacks
37  */
38 using GetCallbackFunc = std::function<ENGINE_ERROR_CODE(
39         Cookie& cookie, const StrToStrMap& arguments, std::string& value)>;
40 
41 /**
42  * Function interface for ioctl_set callbacks
43  */
44 using SetCallbackFunc =
45         std::function<ENGINE_ERROR_CODE(Cookie& cookie,
46                                         const StrToStrMap& arguments,
47                                         const std::string& value)>;
48 
49 /**
50  * Callback for calling allocator specific memory release
51  */
setReleaseFreeMemory(Cookie& cookie, const StrToStrMap&, const std::string& value)52 static ENGINE_ERROR_CODE setReleaseFreeMemory(Cookie& cookie,
53                                               const StrToStrMap&,
54                                               const std::string& value) {
55     AllocHooks::release_free_memory();
56     auto& c = cookie.getConnection();
57     LOG_INFO("{}: IOCTL_SET: release_free_memory called", c.getId());
58     return ENGINE_SUCCESS;
59 }
60 
setJemallocProfActive(Cookie& cookie, const StrToStrMap&, const std::string& value)61 static ENGINE_ERROR_CODE setJemallocProfActive(Cookie& cookie,
62                                                const StrToStrMap&,
63                                                const std::string& value) {
64     bool enable;
65     if (value == "true") {
66         enable = true;
67     } else if (value == "false") {
68         enable = false;
69     } else {
70         return ENGINE_EINVAL;
71     }
72 
73     int res = AllocHooks::set_allocator_property(
74             "prof.active", &enable, sizeof(enable));
75     auto& c = cookie.getConnection();
76     LOG_INFO("{}: {} IOCTL_SET: setJemallocProfActive:{} called, result:{}",
77              c.getId(),
78              c.getDescription(),
79              value,
80              (res == 0) ? "success" : "failure");
81 
82     return (res == 0) ? ENGINE_SUCCESS : ENGINE_EINVAL;
83 }
84 
setJemallocProfDump(Cookie& cookie, const StrToStrMap&, const std::string&)85 static ENGINE_ERROR_CODE setJemallocProfDump(Cookie& cookie,
86                                              const StrToStrMap&,
87                                              const std::string&) {
88     int res = AllocHooks::set_allocator_property("prof.dump", nullptr, 0);
89     auto& c = cookie.getConnection();
90     LOG_INFO("{}: {} IOCTL_SET: setJemallocProfDump called, result:{}",
91              c.getId(),
92              c.getDescription(),
93              (res == 0) ? "success" : "failure");
94 
95     return (res == 0) ? ENGINE_SUCCESS : ENGINE_EINVAL;
96 }
97 
ioctlGetMcbpSla(Cookie& cookie, const StrToStrMap& arguments, std::string& value)98 ENGINE_ERROR_CODE ioctlGetMcbpSla(Cookie& cookie,
99                                   const StrToStrMap& arguments,
100                                   std::string& value) {
101     if (!arguments.empty() || !value.empty()) {
102         return ENGINE_EINVAL;
103     }
104 
105     value = to_string(cb::mcbp::sla::to_json(), false);
106     return ENGINE_SUCCESS;
107 }
108 
109 static const std::unordered_map<std::string, GetCallbackFunc> ioctl_get_map{
110         {"trace.config", ioctlGetTracingConfig},
111         {"trace.status", ioctlGetTracingStatus},
112         {"trace.dump.begin", ioctlGetTracingBeginDump},
113         {"trace.dump.chunk", ioctlGetTracingDumpChunk},
114         {"sla", ioctlGetMcbpSla}};
115 
ioctl_get_property(Cookie& cookie, const std::string& key, std::string& value)116 ENGINE_ERROR_CODE ioctl_get_property(Cookie& cookie,
117                                      const std::string& key,
118                                      std::string& value) {
119     std::pair<std::string, StrToStrMap> request;
120 
121     try {
122         request = decode_query(key);
123     } catch (const std::invalid_argument&) {
124         return ENGINE_EINVAL;
125     }
126 
127     auto entry = ioctl_get_map.find(request.first);
128     if (entry != ioctl_get_map.end()) {
129         return entry->second(cookie, request.second, value);
130     }
131     return ENGINE_EINVAL;
132 }
133 
ioctlSetMcbpSla(Cookie& cookie, const StrToStrMap&, const std::string& value)134 static ENGINE_ERROR_CODE ioctlSetMcbpSla(Cookie& cookie,
135                                          const StrToStrMap&,
136                                          const std::string& value) {
137     unique_cJSON_ptr doc(cJSON_Parse(value.c_str()));
138     if (!doc) {
139         return ENGINE_EINVAL;
140     }
141 
142     try {
143         cb::mcbp::sla::reconfigure(*doc);
144         LOG_INFO("SLA configuration changed to: {}",
145                  to_string(cb::mcbp::sla::to_json(), false));
146     } catch (const std::invalid_argument& e) {
147         cookie.getEventId();
148         auto& c = cookie.getConnection();
149         LOG_INFO("{}: Failed to set MCBP SLA. UUID:[{}]: {}",
150                  c.getId(),
151                  cookie.getEventId(),
152                  e.what());
153         return ENGINE_EINVAL;
154     }
155 
156     return ENGINE_SUCCESS;
157 }
158 
159 static const std::unordered_map<std::string, SetCallbackFunc> ioctl_set_map{
160         {"jemalloc.prof.active", setJemallocProfActive},
161         {"jemalloc.prof.dump", setJemallocProfDump},
162         {"release_free_memory", setReleaseFreeMemory},
163         {"trace.config", ioctlSetTracingConfig},
164         {"trace.start", ioctlSetTracingStart},
165         {"trace.stop", ioctlSetTracingStop},
166         {"trace.dump.clear", ioctlSetTracingClearDump},
167         {"sla", ioctlSetMcbpSla}};
168 
ioctl_set_property(Cookie& cookie, const std::string& key, const std::string& value)169 ENGINE_ERROR_CODE ioctl_set_property(Cookie& cookie,
170                                      const std::string& key,
171                                      const std::string& value) {
172     std::pair<std::string, StrToStrMap> request;
173 
174     try {
175         request = decode_query(key);
176     } catch (const std::invalid_argument&) {
177         return ENGINE_EINVAL;
178     }
179 
180     auto entry = ioctl_set_map.find(request.first);
181     if (entry != ioctl_set_map.end()) {
182         return entry->second(cookie, request.second, value);
183     }
184     return ENGINE_EINVAL;
185 }
186