1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2015 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/* The "crash" bucket is a bucket which simply crashes when it is initialized.
19 * It is intended to be used to test crash catching using Google Breakpad.
20 */
21
22#include "config.h"
23
24#include <stdlib.h>
25#include <gsl/gsl>
26#include <stdexcept>
27#include <string>
28
29#include <memcached/engine.h>
30#include <memcached/visibility.h>
31#include <memcached/util.h>
32#include <memcached/config_parser.h>
33#include <platform/cb_malloc.h>
34
35extern "C" {
36MEMCACHED_PUBLIC_API
37ENGINE_ERROR_CODE create_instance(uint64_t interface,
38                                  GET_SERVER_API gsa,
39                                  ENGINE_HANDLE **handle);
40
41MEMCACHED_PUBLIC_API
42void destroy_engine(void);
43} // extern "C"
44
45struct CrashEngine {
46    ENGINE_HANDLE_V1 engine;
47};
48
49// How do I crash thee? Let me count the ways.
50enum class CrashMode {
51    SegFault,
52    UncaughtStdException,
53    UncaughtUnknownException
54};
55
56static char dummy;
57
58/* Recursive functions which will crash using the given method after
59 * 'depth' calls.
60 * Note: mutates a dummy global variable to prevent optimization
61 * removing the recursion.
62 */
63MEMCACHED_PUBLIC_API
64char recursive_crash_function(char depth, CrashMode mode) {
65    if (depth == 0) {
66        switch (mode) {
67        case CrashMode::SegFault: {
68            char* death = (char*)(uintptr_t)0xdeadcbdb;
69            return *death + dummy;
70        }
71        case CrashMode::UncaughtStdException:
72            throw std::runtime_error(
73                    "crash_engine: This exception wasn't handled");
74        case CrashMode::UncaughtUnknownException:
75            // Crash via exception not derived from std::exception
76            class UnknownException {};
77            throw UnknownException();
78        }
79    }
80    recursive_crash_function(depth - char(1), mode);
81    return dummy++;
82}
83
84/* 'initializes' this engine - given this is the crash_engine that
85 * means crashing it.
86 */
87static ENGINE_ERROR_CODE initialize(gsl::not_null<ENGINE_HANDLE*> handle,
88                                    const char* config_str) {
89    (void)handle;
90    (void)config_str;
91    std::string mode_string(getenv("MEMCACHED_CRASH_TEST"));
92    CrashMode mode;
93    if (mode_string == "segfault") {
94        mode = CrashMode::SegFault;
95    } else if (mode_string == "std_exception") {
96        mode = CrashMode::UncaughtStdException;
97    } else if (mode_string == "unknown_exception") {
98        mode = CrashMode::UncaughtUnknownException;
99    } else {
100        fprintf(stderr, "crash_engine::initialize: could not find a valid "
101                "CrashMode from MEMCACHED_CRASH_TEST env var ('%s')\n",
102                mode_string.c_str());
103        exit(1);
104    }
105    return ENGINE_ERROR_CODE(recursive_crash_function(25, mode));
106}
107
108static void destroy(gsl::not_null<ENGINE_HANDLE*> handle, const bool force) {
109    (void)force;
110    cb_free(handle);
111}
112
113static cb::EngineErrorItemPair item_allocate(
114        gsl::not_null<ENGINE_HANDLE*> handle,
115        gsl::not_null<const void*> cookie,
116        const DocKey& key,
117        const size_t nbytes,
118        const int flags,
119        const rel_time_t exptime,
120        uint8_t datatype,
121        uint16_t vbucket) {
122    return cb::makeEngineErrorItemPair(cb::engine_errc::failed);
123}
124
125static std::pair<cb::unique_item_ptr, item_info> item_allocate_ex(
126        gsl::not_null<ENGINE_HANDLE*> handle,
127        gsl::not_null<const void*> cookie,
128        const DocKey& key,
129        size_t nbytes,
130        size_t priv_nbytes,
131        int flags,
132        rel_time_t exptime,
133        uint8_t datatype,
134        uint16_t vbucket) {
135    throw cb::engine_error{cb::engine_errc::failed, "crash_engine"};
136}
137
138static ENGINE_ERROR_CODE item_delete(gsl::not_null<ENGINE_HANDLE*> handle,
139                                     gsl::not_null<const void*> cookie,
140                                     const DocKey& key,
141                                     uint64_t& cas,
142                                     uint16_t vbucket,
143                                     mutation_descr_t& mut_info) {
144    return ENGINE_FAILED;
145}
146
147static void item_release(gsl::not_null<ENGINE_HANDLE*> handle,
148                         gsl::not_null<item*> item) {
149}
150
151static cb::EngineErrorItemPair get(gsl::not_null<ENGINE_HANDLE*> handle,
152                                   gsl::not_null<const void*> cookie,
153                                   const DocKey& key,
154                                   uint16_t vbucket,
155                                   DocStateFilter) {
156    return cb::makeEngineErrorItemPair(cb::engine_errc::failed);
157}
158
159static cb::EngineErrorItemPair get_if(gsl::not_null<ENGINE_HANDLE*> handle,
160                                      gsl::not_null<const void*>,
161                                      const DocKey&,
162                                      uint16_t,
163                                      std::function<bool(const item_info&)>) {
164    return cb::makeEngineErrorItemPair(cb::engine_errc::failed);
165}
166
167static cb::EngineErrorItemPair get_and_touch(
168        gsl::not_null<ENGINE_HANDLE*> handle,
169        gsl::not_null<const void*> cookie,
170        const DocKey&,
171        uint16_t,
172        uint32_t) {
173    return cb::makeEngineErrorItemPair(cb::engine_errc::failed);
174}
175
176static cb::EngineErrorItemPair get_locked(gsl::not_null<ENGINE_HANDLE*> handle,
177                                          gsl::not_null<const void*> cookie,
178                                          const DocKey& key,
179                                          uint16_t vbucket,
180                                          uint32_t lock_timeout) {
181    return cb::makeEngineErrorItemPair(cb::engine_errc::failed);
182}
183
184static ENGINE_ERROR_CODE unlock(gsl::not_null<ENGINE_HANDLE*> handle,
185                                gsl::not_null<const void*> cookie,
186                                const DocKey& key,
187                                uint16_t vbucket,
188                                uint64_t cas) {
189    return ENGINE_FAILED;
190}
191
192static ENGINE_ERROR_CODE get_stats(gsl::not_null<ENGINE_HANDLE*> handle,
193                                   gsl::not_null<const void*> cookie,
194                                   cb::const_char_buffer key,
195                                   ADD_STAT add_stat) {
196    return ENGINE_FAILED;
197}
198
199static ENGINE_ERROR_CODE store(gsl::not_null<ENGINE_HANDLE*> handle,
200                               gsl::not_null<const void*> cookie,
201                               gsl::not_null<item*> item,
202                               uint64_t& cas,
203                               ENGINE_STORE_OPERATION operation,
204                               DocumentState) {
205    return ENGINE_FAILED;
206}
207
208static cb::EngineErrorCasPair store_if(gsl::not_null<ENGINE_HANDLE*> handle,
209                                       gsl::not_null<const void*> cookie,
210                                       gsl::not_null<item*> item,
211                                       uint64_t cas,
212                                       ENGINE_STORE_OPERATION operation,
213                                       cb::StoreIfPredicate,
214                                       DocumentState) {
215    return {cb::engine_errc::failed, 0};
216}
217
218static ENGINE_ERROR_CODE flush(gsl::not_null<ENGINE_HANDLE*> handle,
219                               gsl::not_null<const void*> cookie) {
220    return ENGINE_FAILED;
221}
222
223static void reset_stats(gsl::not_null<ENGINE_HANDLE*> handle,
224                        gsl::not_null<const void*> cookie) {
225}
226
227static void item_set_cas(gsl::not_null<ENGINE_HANDLE*> handle,
228                         gsl::not_null<item*> item,
229                         uint64_t val) {
230}
231
232static void item_set_datatype(gsl::not_null<ENGINE_HANDLE*> handle,
233                              gsl::not_null<item*> item,
234                              protocol_binary_datatype_t val) {
235}
236
237static bool get_item_info(gsl::not_null<ENGINE_HANDLE*> handle,
238                          gsl::not_null<const item*> item,
239                          gsl::not_null<item_info*> item_info) {
240    return false;
241}
242
243static bool set_item_info(gsl::not_null<ENGINE_HANDLE*> handle,
244                          gsl::not_null<item*> item,
245                          gsl::not_null<const item_info*> itm_info) {
246    return false;
247}
248
249static bool is_xattr_enabled(gsl::not_null<ENGINE_HANDLE*> handle) {
250    return true;
251}
252
253static size_t get_max_item_size(gsl::not_null<ENGINE_HANDLE*> handle) {
254    return default_max_item_size;
255}
256
257ENGINE_ERROR_CODE create_instance(uint64_t interface,
258                                  GET_SERVER_API gsa,
259                                  ENGINE_HANDLE **handle)
260{
261    CrashEngine* engine;
262    (void)gsa;
263
264    if (interface != 1) {
265        return ENGINE_ENOTSUP;
266    }
267
268    if ((engine = reinterpret_cast<CrashEngine*>(cb_calloc(1, sizeof(*engine)))) == NULL) {
269        return ENGINE_ENOMEM;
270    }
271
272    engine->engine.interface.interface = 1;
273    engine->engine.initialize = initialize;
274    engine->engine.destroy = destroy;
275    engine->engine.allocate = item_allocate;
276    engine->engine.allocate_ex = item_allocate_ex;
277    engine->engine.remove = item_delete;
278    engine->engine.release = item_release;
279    engine->engine.get = get;
280    engine->engine.get_if = get_if;
281    engine->engine.get_and_touch = get_and_touch;
282    engine->engine.get_locked = get_locked;
283    engine->engine.unlock = unlock;
284    engine->engine.get_stats = get_stats;
285    engine->engine.reset_stats = reset_stats;
286    engine->engine.store = store;
287    engine->engine.store_if = store_if;
288    engine->engine.flush = flush;
289    engine->engine.item_set_cas = item_set_cas;
290    engine->engine.item_set_datatype = item_set_datatype;
291    engine->engine.get_item_info = get_item_info;
292    engine->engine.set_item_info = set_item_info;
293    engine->engine.isXattrEnabled = is_xattr_enabled;
294    engine->engine.getMaxItemSize = get_max_item_size;
295    *handle = reinterpret_cast<ENGINE_HANDLE*>(&engine->engine);
296    return ENGINE_SUCCESS;
297}
298
299void destroy_engine(){
300
301}
302