Coverage Report

Created: 2025-06-13 06:41

/src/osquery/osquery/database/database.cpp
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * Copyright (c) 2014-present, The osquery authors
3
 *
4
 * This source code is licensed as defined by the LICENSE file found in the
5
 * root directory of this source tree.
6
 *
7
 * SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only)
8
 */
9
10
#include <boost/algorithm/string/predicate.hpp>
11
#include <boost/io/quoted.hpp>
12
#include <boost/property_tree/json_parser.hpp>
13
14
#include <osquery/core/flagalias.h>
15
#include <osquery/core/flags.h>
16
#include <osquery/database/database.h>
17
#include <osquery/logger/logger.h>
18
#include <osquery/process/process.h>
19
#include <osquery/registry/registry.h>
20
#include <osquery/utils/config/default_paths.h>
21
#include <osquery/utils/conversions/tryto.h>
22
#include <osquery/utils/json/json.h>
23
24
namespace pt = boost::property_tree;
25
namespace rj = rapidjson;
26
27
namespace osquery {
28
/// Generate a specific-use registry for database access abstraction.
29
CREATE_REGISTRY(DatabasePlugin, "database");
30
31
CLI_FLAG(bool, database_dump, false, "Dump the contents of the backing store");
32
33
CLI_FLAG(string,
34
         database_path,
35
         OSQUERY_DB_HOME "osquery.db",
36
         "If using a disk-based backing store, specify a path");
37
38
FLAG_ALIAS(std::string, db_path, database_path);
39
40
FLAG(bool, disable_database, false, "Disable the persistent RocksDB storage");
41
42
const std::string kInternalDatabase = "rocksdb";
43
const std::string kPersistentSettings = "configurations";
44
const std::string kQueries = "queries";
45
const std::string kEvents = "events";
46
const std::string kCarves = "carves";
47
const std::string kLogs = "logs";
48
const std::string kDistributedQueries = "distributed";
49
const std::string kDistributedRunningQueries = "distributed_running";
50
const std::string kQueryPerformance = "query_performance";
51
52
const std::string kDbEpochSuffix = "epoch";
53
const std::string kDbCounterSuffix = "counter";
54
55
const std::string kDbVersionKey = "results_version";
56
57
const std::vector<std::string> kDomains = {kPersistentSettings,
58
                                           kQueries,
59
                                           kEvents,
60
                                           kLogs,
61
                                           kCarves,
62
                                           kDistributedQueries,
63
                                           kDistributedRunningQueries,
64
                                           kQueryPerformance};
65
66
std::atomic<bool> kDBAllowOpen(false);
67
std::atomic<bool> kDBInitialized(false);
68
std::atomic<bool> kDBChecking(false);
69
70
/**
71
 * @brief A reader/writer mutex protecting database resets.
72
 *
73
 * A write is locked while using reset flows. A read is locked when calling
74
 * database plugin APIs.
75
 */
76
Mutex kDatabaseReset;
77
78
/**
79
 * @brief Try multiple times to initialize persistent storage.
80
 *
81
 * It might be the case that other processes are stopping and have not released
82
 * their whole-process lock on the database.
83
 */
84
const size_t kDatabaseMaxRetryCount{25};
85
86
/// Number of milliseconds to pause between database initialize retries.
87
const size_t kDatabaseRetryDelay{200};
88
89
1
Status DatabasePlugin::reset() {
90
  // Keep this simple, scope the critical section to the broader methods.
91
1
  tearDown();
92
1
  return setUp();
93
1
}
94
95
0
bool DatabasePlugin::checkDB() {
96
0
  kDBChecking = true;
97
0
  bool result = true;
98
0
  try {
99
0
    auto status = setUp();
100
0
    tearDown();
101
0
    result = status.ok();
102
0
  } catch (const std::exception& e) {
103
0
    VLOG(1) << "Database plugin check failed: " << e.what();
104
0
    result = false;
105
0
  }
106
0
  kDBChecking = false;
107
0
  return result;
108
0
}
109
110
0
bool DatabasePlugin::allowOpen() const {
111
0
  return kDBAllowOpen;
112
0
}
113
114
0
bool DatabasePlugin::checkingDB() const {
115
0
  return kDBChecking;
116
0
}
117
118
Status DatabasePlugin::scan(const std::string& domain,
119
                            std::vector<std::string>& results,
120
                            const std::string& prefix,
121
0
                            uint64_t max) const {
122
0
  return Status::success();
123
0
}
124
125
Status DatabasePlugin::call(const PluginRequest& request,
126
1
                            PluginResponse& response) {
127
1
  if (request.count("action") == 0) {
128
0
    return Status(1, "Database plugin must include a request action");
129
0
  }
130
131
  // Get a domain/key, which are used for most database plugin actions.
132
1
  auto domain = (request.count("domain") > 0) ? request.at("domain") : "";
133
1
  auto key = (request.count("key") > 0) ? request.at("key") : "";
134
135
1
  if (request.at("action") == "reset") {
136
1
    WriteLock lock(kDatabaseReset);
137
1
    kDBInitialized = false;
138
    // Prevent RocksDB reentrancy by logger plugins during plugin setup.
139
1
    VLOG(1) << "Resetting the database plugin: " << getName();
140
1
    auto status = this->reset();
141
1
    if (!status.ok()) {
142
      // The active database could not be reset, fallback to an ephemeral.
143
0
      Registry::get().setActive("database", "ephemeral");
144
0
      LOG(WARNING) << "Unable to reset database plugin: " << getName();
145
0
    }
146
1
    kDBInitialized = true;
147
1
    return status;
148
1
  }
149
150
  // Switch over the possible database plugin actions.
151
0
  ReadLock lock(kDatabaseReset);
152
0
  if (request.at("action") == "get") {
153
0
    std::string value;
154
0
    auto status = this->get(domain, key, value);
155
0
    response.push_back({{"v", value}});
156
0
    return status;
157
0
  } else if (request.at("action") == "put") {
158
0
    if (request.count("value") == 0) {
159
0
      return Status(1, "Database plugin put action requires a value");
160
0
    }
161
0
    return this->put(domain, key, request.at("value"));
162
0
  } else if (request.at("action") == "putBatch") {
163
0
    if (request.count("json") == 0) {
164
0
      return Status(
165
0
          1,
166
0
          "Database plugin putBatch action requires a json-encoded value list");
167
0
    }
168
169
0
    auto json_object = JSON::newObject();
170
171
0
    auto status = json_object.fromString(request.at("json"));
172
0
    if (!status.ok()) {
173
0
      VLOG(1) << status.getMessage();
174
0
      return status;
175
0
    }
176
177
0
    const auto& json_object_list = json_object.doc().GetObject();
178
179
0
    DatabaseStringValueList data;
180
0
    data.reserve(json_object_list.MemberCount());
181
182
0
    for (auto& item : json_object_list) {
183
0
      if (!item.value.IsString()) {
184
0
        return Status(1,
185
0
                      "Database plugin putBatch action with an invalid json "
186
0
                      "received. Only string values are supported");
187
0
      }
188
189
0
      data.push_back(
190
0
          std::make_pair(item.name.GetString(), item.value.GetString()));
191
0
    }
192
193
0
    return this->putBatch(domain, data);
194
0
  } else if (request.at("action") == "remove") {
195
0
    return this->remove(domain, key);
196
0
  } else if (request.at("action") == "remove_range") {
197
0
    auto key_high =
198
0
        (request.count("key_high") > 0) ? request.at("key_high") : "";
199
0
    if (!key_high.empty() && !key.empty()) {
200
0
      return this->removeRange(domain, key, key_high);
201
0
    }
202
0
    return Status(1, "Missing range");
203
0
  } else if (request.at("action") == "scan") {
204
    // Accumulate scanned keys into a vector.
205
0
    std::vector<std::string> keys;
206
    // Optionally allow the caller to request a max number of keys.
207
0
    size_t max = 0;
208
0
    if (request.count("max") > 0) {
209
0
      max = std::stoul(request.at("max"));
210
0
    }
211
0
    auto status = this->scan(domain, keys, request.at("prefix"), max);
212
0
    for (const auto& k : keys) {
213
0
      response.push_back({{"k", k}});
214
0
    }
215
0
    return status;
216
0
  }
217
218
0
  return Status(1, "Unknown database plugin action");
219
0
}
220
221
0
static inline std::shared_ptr<DatabasePlugin> getDatabasePlugin() {
222
0
  auto& rf = RegistryFactory::get();
223
0
  if (!rf.exists("database", rf.getActive("database"), true)) {
224
0
    return nullptr;
225
0
  }
226
227
0
  auto plugin = rf.plugin("database", rf.getActive("database"));
228
0
  return std::dynamic_pointer_cast<DatabasePlugin>(plugin);
229
0
}
230
231
namespace {
232
Status sendPutBatchDatabaseRequest(const std::string& domain,
233
0
                                   const DatabaseStringValueList& data) {
234
0
  auto json_object = JSON::newObject();
235
0
  for (const auto& p : data) {
236
0
    const auto& key = p.first;
237
0
    const auto& value = p.second;
238
239
0
    json_object.addRef(key, value);
240
0
  }
241
242
0
  std::string serialized_data;
243
0
  auto status = json_object.toString(serialized_data);
244
0
  if (!status.ok()) {
245
0
    VLOG(1) << status.getMessage();
246
0
    return status;
247
0
  }
248
249
0
  PluginRequest request = {{"action", "putBatch"},
250
0
                           {"domain", domain},
251
0
                           {"json", std::move(serialized_data)}};
252
253
0
  status = Registry::call("database", request);
254
0
  if (!status.ok()) {
255
0
    VLOG(1) << status.getMessage();
256
0
  }
257
258
0
  return status;
259
0
}
260
261
Status sendPutDatabaseRequest(const std::string& domain,
262
0
                              const DatabaseStringValueList& data) {
263
0
  const auto& key = data[0].first;
264
0
  const auto& value = data[0].second;
265
266
0
  PluginRequest request = {
267
0
      {"action", "put"}, {"domain", domain}, {"key", key}, {"value", value}};
268
269
0
  auto status = Registry::call("database", request);
270
0
  if (!status.ok()) {
271
0
    VLOG(1) << status.getMessage();
272
0
  }
273
274
0
  return status;
275
0
}
276
} // namespace
277
278
Status getDatabaseValue(const std::string& domain,
279
                        const std::string& key,
280
0
                        std::string& value) {
281
0
  if (domain.empty()) {
282
0
    return Status(1, "Missing domain");
283
0
  }
284
285
0
  if (RegistryFactory::get().external()) {
286
    // External registries (extensions) do not have databases active.
287
    // It is not possible to use an extension-based database.
288
0
    PluginRequest request = {
289
0
        {"action", "get"}, {"domain", domain}, {"key", key}};
290
0
    PluginResponse response;
291
0
    auto status = Registry::call("database", request, response);
292
0
    if (status.ok()) {
293
      // Set value from the internally-known "v" key.
294
0
      if (response.size() > 0 && response[0].count("v") > 0) {
295
0
        value = response[0].at("v");
296
0
      }
297
0
    }
298
0
    return status;
299
0
  }
300
301
0
  ReadLock lock(kDatabaseReset);
302
0
  if (!kDBInitialized) {
303
0
    throw std::runtime_error("Cannot get database value: " + key);
304
0
  } else {
305
0
    auto plugin = getDatabasePlugin();
306
0
    return plugin->get(domain, key, value);
307
0
  }
308
0
}
309
310
Status getDatabaseValue(const std::string& domain,
311
                        const std::string& key,
312
0
                        int& value) {
313
0
  std::string result;
314
0
  auto s = getDatabaseValue(domain, key, result);
315
0
  if (s.ok()) {
316
0
    value = std::stoi(result);
317
0
  }
318
0
  return s;
319
0
}
320
321
Status setDatabaseValue(const std::string& domain,
322
                        const std::string& key,
323
0
                        const std::string& value) {
324
0
  return setDatabaseBatch(domain, {std::make_pair(key, value)});
325
0
}
326
327
Status setDatabaseBatch(const std::string& domain,
328
0
                        const DatabaseStringValueList& data) {
329
0
  if (domain.empty()) {
330
0
    return Status(1, "Missing domain");
331
0
  }
332
333
  // External registries (extensions) do not have databases active.
334
  // It is not possible to use an extension-based database.
335
0
  if (RegistryFactory::get().external()) {
336
0
    if (data.size() > 1) {
337
0
      return sendPutBatchDatabaseRequest(domain, data);
338
0
    } else {
339
0
      return sendPutDatabaseRequest(domain, data);
340
0
    }
341
0
  }
342
343
0
  ReadLock lock(kDatabaseReset);
344
0
  if (!kDBInitialized) {
345
0
    throw std::runtime_error("Cannot set database values");
346
0
  }
347
348
0
  auto plugin = getDatabasePlugin();
349
0
  return plugin->putBatch(domain, data);
350
0
}
351
352
Status setDatabaseValue(const std::string& domain,
353
                        const std::string& key,
354
0
                        int value) {
355
0
  return setDatabaseBatch(domain, {std::make_pair(key, std::to_string(value))});
356
0
}
357
358
0
Status deleteDatabaseValue(const std::string& domain, const std::string& key) {
359
0
  if (domain.empty()) {
360
0
    return Status(1, "Missing domain");
361
0
  }
362
363
0
  if (RegistryFactory::get().external()) {
364
    // External registries (extensions) do not have databases active.
365
    // It is not possible to use an extension-based database.
366
0
    PluginRequest request = {
367
0
        {"action", "remove"}, {"domain", domain}, {"key", key}};
368
0
    return Registry::call("database", request);
369
0
  }
370
371
0
  ReadLock lock(kDatabaseReset);
372
0
  if (!kDBInitialized) {
373
0
    throw std::runtime_error("Cannot delete database value: " + key);
374
0
  } else {
375
0
    auto plugin = getDatabasePlugin();
376
0
    return plugin->remove(domain, key);
377
0
  }
378
0
}
379
380
Status deleteDatabaseRange(const std::string& domain,
381
                           const std::string& low,
382
0
                           const std::string& high) {
383
0
  if (domain.empty()) {
384
0
    return Status(1, "Missing domain");
385
0
  }
386
387
0
  if (RegistryFactory::get().external()) {
388
    // External registries (extensions) do not have databases active.
389
    // It is not possible to use an extension-based database.
390
0
    PluginRequest request = {{"action", "remove_range"},
391
0
                             {"domain", domain},
392
0
                             {"key", low},
393
0
                             {"key_high", high}};
394
0
    return Registry::call("database", request);
395
0
  }
396
397
0
  ReadLock lock(kDatabaseReset);
398
0
  if (!kDBInitialized) {
399
0
    throw std::runtime_error("Cannot delete database values: " + low + " - " +
400
0
                             high);
401
0
  } else {
402
0
    auto plugin = getDatabasePlugin();
403
0
    return plugin->removeRange(domain, low, high);
404
0
  }
405
0
}
406
407
Status scanDatabaseKeys(const std::string& domain,
408
                        std::vector<std::string>& keys,
409
0
                        size_t max) {
410
0
  return scanDatabaseKeys(domain, keys, "", max);
411
0
}
412
413
/// Get a list of keys for a given domain.
414
Status scanDatabaseKeys(const std::string& domain,
415
                        std::vector<std::string>& keys,
416
                        const std::string& prefix,
417
0
                        uint64_t max) {
418
0
  if (domain.empty()) {
419
0
    return Status(1, "Missing domain");
420
0
  }
421
422
0
  if (RegistryFactory::get().external()) {
423
    // External registries (extensions) do not have databases active.
424
    // It is not possible to use an extension-based database.
425
0
    PluginRequest request = {{"action", "scan"},
426
0
                             {"domain", domain},
427
0
                             {"prefix", prefix},
428
0
                             {"max", std::to_string(max)}};
429
0
    PluginResponse response;
430
0
    auto status = Registry::call("database", request, response);
431
432
0
    for (const auto& item : response) {
433
0
      if (item.count("k") > 0) {
434
0
        keys.push_back(item.at("k"));
435
0
      }
436
0
    }
437
0
    return status;
438
0
  }
439
440
0
  ReadLock lock(kDatabaseReset);
441
0
  if (!kDBInitialized) {
442
0
    throw std::runtime_error("Cannot scan database values: " + prefix);
443
0
  } else {
444
0
    auto plugin = getDatabasePlugin();
445
0
    return plugin->scan(domain, keys, prefix, max);
446
0
  }
447
0
}
448
449
1
void resetDatabase() {
450
1
  PluginRequest request = {{"action", "reset"}};
451
1
  Registry::call("database", request);
452
1
}
453
454
0
void dumpDatabase() {
455
0
  for (const auto& domain : kDomains) {
456
0
    std::vector<std::string> keys;
457
0
    if (!scanDatabaseKeys(domain, keys)) {
458
0
      continue;
459
0
    }
460
0
    for (const auto& key : keys) {
461
0
      std::string value;
462
0
      if (!getDatabaseValue(domain, key, value)) {
463
0
        continue;
464
0
      }
465
0
      fprintf(
466
0
          stdout, "%s[%s]: %s\n", domain.c_str(), key.c_str(), value.c_str());
467
0
    }
468
0
  }
469
0
  fflush(stdout);
470
0
}
471
472
1
void setDatabaseAllowOpen(bool allow_open) {
473
1
  kDBAllowOpen = allow_open;
474
1
}
475
476
1
Status initDatabasePlugin() {
477
1
  if (kDBInitialized) {
478
0
    return Status::success();
479
0
  }
480
481
  // Initialize the database plugin using the flag.
482
1
  auto plugin = (FLAGS_disable_database) ? "ephemeral" : kInternalDatabase;
483
1
  Status status;
484
1
  for (size_t i = 0; i < kDatabaseMaxRetryCount; i++) {
485
1
    status = RegistryFactory::get().setActive("database", plugin);
486
1
    if (status.ok()) {
487
1
      break;
488
1
    }
489
490
0
    if (FLAGS_disable_database) {
491
      // Do not try multiple times to initialize the ephemeral plugin.
492
0
      break;
493
0
    }
494
0
    sleepFor(kDatabaseRetryDelay);
495
0
  }
496
497
1
  kDBInitialized = status.ok();
498
1
  return status;
499
1
}
500
501
1
Status initDatabasePluginForTesting() {
502
  // Use the built-in ephemeral database.
503
1
  FLAGS_disable_database = true;
504
1
  setDatabaseAllowOpen();
505
1
  initDatabasePlugin();
506
1
  resetDatabase();
507
1
  return Status::success();
508
1
}
509
510
0
bool databaseInitialized() {
511
0
  return kDBInitialized;
512
0
}
513
514
0
void shutdownDatabase() {
515
0
  auto database_registry = RegistryFactory::get().registry("database");
516
0
  for (auto& plugin : RegistryFactory::get().names("database")) {
517
0
    database_registry->remove(plugin);
518
0
  }
519
0
}
520
521
0
Status ptreeToRapidJSON(const std::string& in, std::string& out) {
522
0
  pt::ptree tree;
523
0
  try {
524
0
    std::stringstream ss;
525
0
    ss << in;
526
0
    pt::read_json(ss, tree);
527
0
  } catch (const pt::json_parser::json_parser_error& /* e */) {
528
0
    return Status(1, "Failed to parse JSON");
529
0
  }
530
531
0
  auto json = JSON::newArray();
532
0
  for (const auto& t : tree) {
533
0
    std::stringstream ss;
534
0
    pt::write_json(ss, t.second);
535
536
0
    rj::Document row;
537
0
    if (row.Parse(ss.str()).HasParseError()) {
538
0
      return Status(1, "Failed to serialize JSON");
539
0
    }
540
0
    json.push(row);
541
0
  }
542
543
0
  json.toString(out);
544
545
0
  return Status::success();
546
0
}
547
548
0
static Status migrateV0V1(void) {
549
0
  std::vector<std::string> keys;
550
0
  auto s = scanDatabaseKeys(kQueries, keys);
551
0
  if (!s.ok()) {
552
0
    return Status(1, "Failed to lookup legacy query data from database");
553
0
  }
554
555
0
  for (const auto& key : keys) {
556
    // Skip over epoch and counter entries, as 0 is parsed by ptree
557
0
    if (boost::algorithm::ends_with(key, kDbEpochSuffix) ||
558
0
        boost::algorithm::ends_with(key, kDbCounterSuffix) ||
559
0
        boost::algorithm::starts_with(key, "query.")) {
560
0
      continue;
561
0
    }
562
563
0
    std::string value{""};
564
0
    if (!getDatabaseValue(kQueries, key, value)) {
565
0
      LOG(WARNING) << "Failed to get value from database " << key;
566
0
      continue;
567
0
    }
568
569
0
    std::string out;
570
0
    s = ptreeToRapidJSON(value, out);
571
0
    if (!s.ok()) {
572
0
      LOG(WARNING) << "Conversion from ptree to RapidJSON failed for '" << key
573
0
                   << ": " << value << "': " << s.what() << ". Dropping key!";
574
0
      continue;
575
0
    }
576
577
0
    if (!setDatabaseValue(kQueries, key, out)) {
578
0
      LOG(WARNING) << "Failed to update value in database " << key << ": "
579
0
                   << value;
580
0
    }
581
0
  }
582
583
0
  return Status::success();
584
0
}
585
586
0
static Status migrateV1V2(void) {
587
0
  std::vector<std::string> keys;
588
0
  const std::string audit_str(".audit.");
589
590
0
  Status s = scanDatabaseKeys(kEvents, keys);
591
0
  if (!s.ok()) {
592
0
    return Status::failure(
593
0
        1, "Failed to scan event keys from database: " + s.what());
594
0
  }
595
596
0
  for (const auto& key : keys) {
597
0
    const auto pos = key.find(audit_str);
598
0
    if (pos != std::string::npos) {
599
0
      std::string value;
600
0
      std::string new_key = key;
601
0
      new_key.replace(pos, audit_str.length(), ".auditeventpublisher.");
602
603
0
      s = getDatabaseValue(kEvents, key, value);
604
0
      if (!s.ok()) {
605
0
        LOG(ERROR) << "Failed to read value for key '" << key
606
0
                   << "'. Key will be kept but won't be migrated!";
607
0
        continue;
608
0
      }
609
610
0
      s = setDatabaseValue(kEvents, new_key, value);
611
0
      if (!s.ok()) {
612
0
        LOG(ERROR) << "Failed to set value for key '" << new_key
613
0
                   << "' migrated from '" << key
614
0
                   << "'. Original key will be kept but won't be migrated!";
615
0
        continue;
616
0
      }
617
618
0
      s = deleteDatabaseValue(kEvents, key);
619
0
      if (!s.ok()) {
620
0
        LOG(WARNING) << "Failed to delete key '" << key
621
0
                     << "' after migration to new key '" << new_key
622
0
                     << "'. Original key will be kept but data was migrated!";
623
0
      }
624
0
    }
625
0
  }
626
627
0
  return Status::success();
628
0
}
629
630
0
Status upgradeDatabase(int to_version) {
631
0
  std::string value;
632
0
  Status st = getDatabaseValue(kPersistentSettings, kDbVersionKey, value);
633
634
0
  int db_version = 0;
635
  /* Since there isn't a reliable way to determined what happen when the read
636
   * fails we just assume the key doesn't exist which indicates database
637
   * version 0.
638
   */
639
0
  if (st.ok()) {
640
0
    auto ret = tryTo<int>(value);
641
0
    if (ret.isError()) {
642
0
      LOG(ERROR) << "Invalid value '" << value << "'for " << kDbVersionKey
643
0
                 << " key. Database is corrupted.";
644
0
      return Status::failure("Invalid value for database version.");
645
0
    } else {
646
0
      db_version = ret.get();
647
0
    }
648
0
  }
649
650
0
  while (db_version != to_version) {
651
0
    Status migrate_status;
652
0
    switch (db_version) {
653
0
    case 0:
654
0
      migrate_status = migrateV0V1();
655
0
      break;
656
657
0
    case 1:
658
0
      migrate_status = migrateV1V2();
659
0
      break;
660
661
0
    default:
662
0
      LOG(ERROR) << "Logic error: the migration code is broken!";
663
0
      migrate_status = Status::failure("Migration code broken.");
664
0
      break;
665
0
    }
666
667
0
    if (!migrate_status.ok()) {
668
0
      LOG(ERROR) << "Failed to migrate the database to version '" << db_version
669
0
                 << "': " << migrate_status.getMessage();
670
0
      return Status::failure("Database migration failed.");
671
0
    }
672
673
0
    st = setDatabaseValue(
674
0
        kPersistentSettings, kDbVersionKey, std::to_string(db_version + 1));
675
0
    if (!st.ok()) {
676
0
      LOG(ERROR) << "Failed to set new database version after migration. "
677
0
                 << "The DB was correctly migrated from version " << db_version
678
0
                 << " to version " << (db_version + 1)
679
0
                 << " but persisting the new version failed.";
680
0
      return Status::failure("Database version commit failed.");
681
0
    }
682
683
0
    db_version++;
684
0
  }
685
686
0
  return Status::success();
687
0
}
688
689
class OsqueryDatabase final : public IDatabaseInterface {
690
 public:
691
  OsqueryDatabase() = default;
692
  virtual ~OsqueryDatabase() = default;
693
694
  virtual Status getDatabaseValue(const std::string& domain,
695
                                  const std::string& key,
696
0
                                  std::string& value) const override {
697
0
    return osquery::getDatabaseValue(domain, key, value);
698
0
  }
699
700
  virtual Status getDatabaseValue(const std::string& domain,
701
                                  const std::string& key,
702
0
                                  int& value) const override {
703
0
    return osquery::getDatabaseValue(domain, key, value);
704
0
  }
705
706
  virtual Status setDatabaseValue(const std::string& domain,
707
                                  const std::string& key,
708
0
                                  const std::string& value) const override {
709
0
    return osquery::setDatabaseValue(domain, key, value);
710
0
  }
711
712
  virtual Status setDatabaseValue(const std::string& domain,
713
                                  const std::string& key,
714
0
                                  int value) const override {
715
0
    return osquery::setDatabaseValue(domain, key, value);
716
0
  }
717
718
  virtual Status setDatabaseBatch(
719
      const std::string& domain,
720
0
      const DatabaseStringValueList& data) const override {
721
0
    return osquery::setDatabaseBatch(domain, data);
722
0
  }
723
724
  virtual Status deleteDatabaseValue(const std::string& domain,
725
0
                                     const std::string& key) const override {
726
0
    return osquery::deleteDatabaseValue(domain, key);
727
0
  }
728
729
  virtual Status deleteDatabaseRange(const std::string& domain,
730
                                     const std::string& low,
731
0
                                     const std::string& high) const override {
732
0
    return osquery::deleteDatabaseRange(domain, low, high);
733
0
  }
734
735
  virtual Status scanDatabaseKeys(const std::string& domain,
736
                                  std::vector<std::string>& keys,
737
0
                                  size_t max) const override {
738
0
    return osquery::scanDatabaseKeys(domain, keys, max);
739
0
  }
740
741
  virtual Status scanDatabaseKeys(const std::string& domain,
742
                                  std::vector<std::string>& keys,
743
                                  const std::string& prefix,
744
0
                                  size_t max) const override {
745
0
    return osquery::scanDatabaseKeys(domain, keys, prefix, max);
746
0
  }
747
};
748
749
0
IDatabaseInterface& getOsqueryDatabase() {
750
0
  static OsqueryDatabase osquery_database;
751
0
  return osquery_database;
752
0
}
753
} // namespace osquery