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