Add accept() wrapper
[xapian.git] / xapian-core / backends / dbfactory.cc
blob7d8e1ffbd551b367508cda54fd8ebe395a078cd9
1 /** @file
2 * @brief Database factories for non-remote databases.
3 */
4 /* Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2011,2012,2013,2014,2015,2016,2017,2019 Olly Betts
5 * Copyright 2008 Lemur Consulting Ltd
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
23 #include <config.h>
25 #include "xapian/dbfactory.h"
27 #include "xapian/constants.h"
28 #include "xapian/database.h"
29 #include "xapian/error.h"
30 #include "xapian/version.h" // For XAPIAN_HAS_XXX_BACKEND.
32 #include "backends.h"
33 #include "databasehelpers.h"
34 #include "debuglog.h"
35 #include "filetests.h"
36 #include "fileutils.h"
37 #include "posixy_wrapper.h"
38 #include "str.h"
40 #include <cerrno>
42 #ifdef XAPIAN_HAS_GLASS_BACKEND
43 # include "glass/glass_database.h"
44 #endif
45 #ifdef XAPIAN_HAS_CHERT_BACKEND
46 # include "chert/chert_database.h"
47 #endif
48 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
49 # include "inmemory/inmemory_database.h"
50 #endif
51 // Even if none of the above get included, we still need a definition of
52 // Database::Internal.
53 #include "backends/database.h"
55 #include <string>
57 using namespace std;
59 namespace Xapian {
61 static void
62 open_stub(Database& db, const string& file)
64 read_stub_file(file,
65 [&db](const string& path) {
66 db.add_database(Database(path));
68 [&db](const string& path) {
69 #ifdef XAPIAN_HAS_CHERT_BACKEND
70 db.add_database(Database(new ChertDatabase(path)));
71 #else
72 (void)path;
73 #endif
75 [&db](const string& path) {
76 #ifdef XAPIAN_HAS_GLASS_BACKEND
77 db.add_database(Database(new GlassDatabase(path)));
78 #else
79 (void)path;
80 #endif
82 [&db](const string& prog, const string& args) {
83 #ifdef XAPIAN_HAS_REMOTE_BACKEND
84 db.add_database(Remote::open(prog, args));
85 #else
86 (void)prog;
87 (void)args;
88 #endif
90 [&db](const string& host, unsigned port) {
91 #ifdef XAPIAN_HAS_REMOTE_BACKEND
92 db.add_database(Remote::open(host, port));
93 #else
94 (void)host;
95 (void)port;
96 #endif
98 [&db]() {
99 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
100 db.add_database(Database(string(), DB_BACKEND_INMEMORY));
101 #endif
104 // Allowing a stub database with no databases listed allows things like
105 // a "search all databases" feature to be implemented by generating a
106 // stub database file without having to special case there not being any
107 // databases yet.
109 // 1.0.x threw DatabaseOpeningError here, but with a "Bad line" message
110 // with the line number just past the end of the file, which was a bit odd.
113 static void
114 open_stub(WritableDatabase& db, const string& file, int flags)
116 read_stub_file(file,
117 [&db, flags](const string& path) {
118 db.add_database(WritableDatabase(path, flags));
120 [&db, &flags](const string& path) {
121 flags |= DB_BACKEND_CHERT;
122 db.add_database(WritableDatabase(path, flags));
124 [&db, &flags](const string& path) {
125 flags |= DB_BACKEND_GLASS;
126 db.add_database(WritableDatabase(path, flags));
128 [&db, flags](const string& prog, const string& args) {
129 #ifdef XAPIAN_HAS_REMOTE_BACKEND
130 db.add_database(Remote::open_writable(prog, args,
131 0, flags));
132 #else
133 (void)prog;
134 (void)args;
135 #endif
137 [&db, flags](const string& host, unsigned port) {
138 #ifdef XAPIAN_HAS_REMOTE_BACKEND
139 db.add_database(Remote::open_writable(host, port,
140 0, 10000, flags));
141 #else
142 (void)host;
143 (void)port;
144 #endif
146 [&db]() {
147 db.add_database(WritableDatabase(string(),
148 DB_BACKEND_INMEMORY));
151 if (db.internal.empty()) {
152 throw DatabaseOpeningError(file + ": No databases listed");
156 Database::Database(const string &path, int flags)
158 LOGCALL_CTOR(API, "Database", path|flags);
160 int type = flags & DB_BACKEND_MASK_;
161 switch (type) {
162 case DB_BACKEND_CHERT:
163 #ifdef XAPIAN_HAS_CHERT_BACKEND
164 internal.push_back(new ChertDatabase(path));
165 return;
166 #else
167 throw FeatureUnavailableError("Chert backend disabled");
168 #endif
169 case DB_BACKEND_GLASS:
170 #ifdef XAPIAN_HAS_GLASS_BACKEND
171 internal.push_back(new GlassDatabase(path));
172 return;
173 #else
174 throw FeatureUnavailableError("Glass backend disabled");
175 #endif
176 case DB_BACKEND_STUB:
177 open_stub(*this, path);
178 return;
179 case DB_BACKEND_INMEMORY:
180 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
181 internal.push_back(new InMemoryDatabase());
182 return;
183 #else
184 throw FeatureUnavailableError("Inmemory backend disabled");
185 #endif
188 struct stat statbuf;
189 if (stat(path.c_str(), &statbuf) == -1) {
190 if (errno == ENOENT) {
191 throw DatabaseNotFoundError("Couldn't stat '" + path + "'", errno);
192 } else {
193 throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
197 if (S_ISREG(statbuf.st_mode)) {
198 // Could be a stub database file, or a single file glass database.
200 // Initialise to avoid bogus warning from GCC 4.9.2 with -Os.
201 int fd = -1;
202 switch (test_if_single_file_db(statbuf, path, &fd)) {
203 case BACKEND_GLASS:
204 #ifdef XAPIAN_HAS_GLASS_BACKEND
205 // Single file glass format.
206 internal.push_back(new GlassDatabase(fd));
207 return;
208 #else
209 throw FeatureUnavailableError("Glass backend disabled");
210 #endif
213 open_stub(*this, path);
214 return;
217 if (rare(!S_ISDIR(statbuf.st_mode))) {
218 throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
221 #ifdef XAPIAN_HAS_CHERT_BACKEND
222 if (file_exists(path + "/iamchert")) {
223 internal.push_back(new ChertDatabase(path));
224 return;
226 #endif
228 #ifdef XAPIAN_HAS_GLASS_BACKEND
229 if (file_exists(path + "/iamglass")) {
230 internal.push_back(new GlassDatabase(path));
231 return;
233 #endif
235 // Check for "stub directories".
236 string stub_file = path;
237 stub_file += "/XAPIANDB";
238 if (usual(file_exists(stub_file))) {
239 open_stub(*this, stub_file);
240 return;
243 #ifndef XAPIAN_HAS_CHERT_BACKEND
244 if (file_exists(path + "/iamchert")) {
245 throw FeatureUnavailableError("Chert backend disabled");
247 #endif
248 #ifndef XAPIAN_HAS_GLASS_BACKEND
249 if (file_exists(path + "/iamglass")) {
250 throw FeatureUnavailableError("Glass backend disabled");
252 #endif
253 if (file_exists(path + "/iamflint")) {
254 throw FeatureUnavailableError("Flint backend no longer supported");
257 throw DatabaseNotFoundError("Couldn't detect type of database");
260 Database::Database(int fd, int flags)
262 LOGCALL_CTOR(API, "Database", fd|flags);
263 if (rare(fd < 0))
264 throw InvalidArgumentError("fd < 0");
266 #ifdef XAPIAN_HAS_GLASS_BACKEND
267 int type = flags & DB_BACKEND_MASK_;
268 switch (type) {
269 case 0: case DB_BACKEND_GLASS:
270 internal.push_back(new GlassDatabase(fd));
271 return;
273 #else
274 (void)flags;
275 #endif
277 (void)::close(fd);
278 throw DatabaseOpeningError("Couldn't detect type of database");
281 #if defined XAPIAN_HAS_CHERT_BACKEND || \
282 defined XAPIAN_HAS_GLASS_BACKEND
283 #define HAVE_DISK_BACKEND
284 #endif
286 WritableDatabase::WritableDatabase(const std::string &path, int flags, int block_size)
287 : Database()
289 LOGCALL_CTOR(API, "WritableDatabase", path|flags|block_size);
290 // Avoid warning if both chert and glass are disabled.
291 (void)block_size;
292 int type = flags & DB_BACKEND_MASK_;
293 // Clear the backend bits, so we just pass on other flags to open_stub, etc.
294 flags &= ~DB_BACKEND_MASK_;
295 if (type == 0) {
296 struct stat statbuf;
297 if (stat(path.c_str(), &statbuf) == -1) {
298 // ENOENT probably just means that we need to create the directory.
299 if (errno != ENOENT)
300 throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
301 } else {
302 // File or directory already exists.
304 if (S_ISREG(statbuf.st_mode)) {
305 // The path is a file, so assume it is a stub database file.
306 open_stub(*this, path, flags);
307 return;
310 if (rare(!S_ISDIR(statbuf.st_mode))) {
311 throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
314 if (file_exists(path + "/iamchert")) {
315 // Existing chert DB.
316 #ifdef XAPIAN_HAS_CHERT_BACKEND
317 type = DB_BACKEND_CHERT;
318 #else
319 throw FeatureUnavailableError("Chert backend disabled");
320 #endif
321 } else if (file_exists(path + "/iamglass")) {
322 // Existing glass DB.
323 #ifdef XAPIAN_HAS_GLASS_BACKEND
324 type = DB_BACKEND_GLASS;
325 #else
326 throw FeatureUnavailableError("Glass backend disabled");
327 #endif
328 } else if (file_exists(path + "/iamflint")) {
329 // Existing flint DB.
330 throw FeatureUnavailableError("Flint backend no longer supported");
331 } else {
332 // Check for "stub directories".
333 string stub_file = path;
334 stub_file += "/XAPIANDB";
335 if (usual(file_exists(stub_file))) {
336 open_stub(*this, stub_file, flags);
337 return;
343 switch (type) {
344 case DB_BACKEND_STUB:
345 open_stub(*this, path, flags);
346 return;
347 case 0:
348 // Fall through to first enabled case, so order the remaining cases
349 // by preference.
350 #ifdef XAPIAN_HAS_GLASS_BACKEND
351 case DB_BACKEND_GLASS:
352 internal.push_back(new GlassWritableDatabase(path, flags, block_size));
353 return;
354 #endif
355 #ifdef XAPIAN_HAS_CHERT_BACKEND
356 case DB_BACKEND_CHERT:
357 internal.push_back(new ChertWritableDatabase(path, flags, block_size));
358 return;
359 #endif
360 case DB_BACKEND_INMEMORY:
361 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
362 internal.push_back(new InMemoryDatabase());
363 return;
364 #else
365 throw FeatureUnavailableError("Inmemory backend disabled");
366 #endif
368 #ifndef HAVE_DISK_BACKEND
369 throw FeatureUnavailableError("No disk-based writable backend is enabled");
370 #endif