GArray* rspamd_sqlite3_init_prstmt (sqlite3 *db, struct rspamd_sqlite3_prstmt *init_stmt, gint max_idx, GError **err) { gint i; GArray *res; struct rspamd_sqlite3_prstmt *nst; res = g_array_sized_new (FALSE, TRUE, sizeof (struct rspamd_sqlite3_prstmt), max_idx); g_array_set_size (res, max_idx); for (i = 0; i < max_idx; i ++) { nst = &g_array_index (res, struct rspamd_sqlite3_prstmt, i); memcpy (nst, &init_stmt[i], sizeof (*nst)); if (sqlite3_prepare_v2 (db, init_stmt[i].sql, -1, &nst->stmt, NULL) != SQLITE_OK) { g_set_error (err, rspamd_sqlite3_quark (), -1, "Cannot initialize prepared sql `%s`: %s", nst->sql, sqlite3_errmsg (db)); rspamd_sqlite3_close_prstmt (db, res); return NULL; } } return res; }
static gboolean rspamd_sqlite3_wait (const gchar *lock) { gint fd; struct timespec sleep_ts = { .tv_sec = 0, .tv_nsec = 1000000 }; fd = open (lock, O_RDONLY); if (fd == -1) { msg_err ("cannot open lock file %s: %s", lock, strerror (errno)); return FALSE; } while (!rspamd_file_lock (fd, TRUE)) { if (nanosleep (&sleep_ts, NULL) == -1 && errno != EINTR) { close (fd); msg_err ("cannot sleep open lock file %s: %s", lock, strerror (errno)); return FALSE; } } rspamd_file_unlock (fd, FALSE); close (fd); return TRUE; } sqlite3 * rspamd_sqlite3_open_or_create (const gchar *path, const gchar *create_sql, GError **err) { sqlite3 *sqlite; gint rc, flags, lock_fd; gchar lock_path[PATH_MAX], dbdir[PATH_MAX], *pdir; static const char sqlite_wal[] = "PRAGMA journal_mode=\"wal\";", exclusive_lock_sql[] = "PRAGMA locking_mode=\"exclusive\";"; gboolean create = FALSE; flags = SQLITE_OPEN_READWRITE; #ifdef SQLITE_OPEN_SHAREDCACHE flags |= SQLITE_OPEN_SHAREDCACHE; #endif #ifdef SQLITE_OPEN_WAL flags |= SQLITE_OPEN_WAL; #endif rspamd_strlcpy (dbdir, path, sizeof (dbdir)); pdir = dirname (dbdir); if (access (pdir, W_OK) == -1) { g_set_error (err, rspamd_sqlite3_quark (), errno, "cannot open sqlite directory %s: %s", pdir, strerror (errno)); return NULL; } rspamd_snprintf (lock_path, sizeof (lock_path), "%s.lock", path); if (access (path, R_OK) == -1 || access (lock_path, R_OK) != -1) { flags |= SQLITE_OPEN_CREATE; rspamd_snprintf (lock_path, sizeof (lock_path), "%s.lock", path); lock_fd = open (lock_path, O_WRONLY|O_CREAT|O_EXCL, 00600); if (lock_fd == -1 && (errno == EEXIST || errno == EBUSY)) { msg_debug ("checking %s to wait for db being created", lock_path); if (!rspamd_sqlite3_wait (lock_path)) { g_set_error (err, rspamd_sqlite3_quark (), errno, "cannot create sqlite file %s: %s", path, strerror (errno)); return NULL; } /* At this point we have database created */ create = FALSE; } else { msg_debug ("locking %s to block creating", lock_path); g_assert (rspamd_file_lock (lock_fd, FALSE)); create = TRUE; } } if ((rc = sqlite3_open_v2 (path, &sqlite, flags, NULL)) != SQLITE_OK) { #if SQLITE_VERSION_NUMBER >= 3008000 g_set_error (err, rspamd_sqlite3_quark (), rc, "cannot open sqlite db %s: %s", path, sqlite3_errstr (rc)); #else g_set_error (err, rspamd_sqlite3_quark (), rc, "cannot open sqlite db %s: %d", path, rc); #endif return NULL; } if (create) { if (sqlite3_exec (sqlite, sqlite_wal, NULL, NULL, NULL) != SQLITE_OK) { msg_warn ("WAL mode is not supported (%s), locking issues might occur", sqlite3_errmsg (sqlite)); } if (sqlite3_exec (sqlite, exclusive_lock_sql, NULL, NULL, NULL) != SQLITE_OK) { msg_warn ("cannot exclusively lock database to create schema: %s", sqlite3_errmsg (sqlite)); } if (sqlite3_exec (sqlite, create_sql, NULL, NULL, NULL) != SQLITE_OK) { g_set_error (err, rspamd_sqlite3_quark (), -1, "cannot execute create sql `%s`: %s", create_sql, sqlite3_errmsg (sqlite)); sqlite3_close (sqlite); rspamd_file_unlock (lock_fd, FALSE); unlink (lock_path); close (lock_fd); return NULL; } msg_debug ("removing lock from %s", lock_path); sqlite3_close (sqlite); rspamd_file_unlock (lock_fd, FALSE); unlink (lock_path); close (lock_fd); /* Reopen in normal mode */ msg_debug ("reopening %s in normal mode", path); flags &= ~SQLITE_OPEN_CREATE; if ((rc = sqlite3_open_v2 (path, &sqlite, flags, NULL)) != SQLITE_OK) { #if SQLITE_VERSION_NUMBER >= 3008000 g_set_error (err, rspamd_sqlite3_quark (), rc, "cannot open sqlite db after creation %s: %s", path, sqlite3_errstr (rc)); #else g_set_error (err, rspamd_sqlite3_quark (), rc, "cannot open sqlite db after creation %s: %d", path, rc); #endif return NULL; } } if (sqlite3_exec (sqlite, sqlite_wal, NULL, NULL, NULL) != SQLITE_OK) { msg_warn ("WAL mode is not supported (%s), locking issues might occur", sqlite3_errmsg (sqlite)); } return sqlite; }
static gboolean rspamd_sqlite3_wait (rspamd_mempool_t *pool, const gchar *lock) { gint fd; struct timespec sleep_ts = { .tv_sec = 0, .tv_nsec = 1000000 }; fd = open (lock, O_RDONLY); if (fd == -1) { if (errno == ENOENT) { /* Lock is already released, so we can continue */ return TRUE; } msg_err_pool_check ("cannot open lock file %s: %s", lock, strerror (errno)); return FALSE; } while (!rspamd_file_lock (fd, TRUE)) { if (nanosleep (&sleep_ts, NULL) == -1 && errno != EINTR) { close (fd); msg_err_pool_check ("cannot sleep open lock file %s: %s", lock, strerror (errno)); return FALSE; } } rspamd_file_unlock (fd, FALSE); unlink (lock); close (fd); return TRUE; } #define RSPAMD_SQLITE_MMAP_LIMIT 268435456 #define RSPAMD_SQLITE_CACHE_SIZE 262144 sqlite3 * rspamd_sqlite3_open_or_create (rspamd_mempool_t *pool, const gchar *path, const gchar *create_sql, GError **err) { sqlite3 *sqlite; gint rc, flags, lock_fd; gchar lock_path[PATH_MAX], dbdir[PATH_MAX], *pdir; static const char sqlite_wal[] = "PRAGMA journal_mode=\"wal\";" "PRAGMA wal_autocheckpoint = 16;" "PRAGMA journal_size_limit = 1536;", exclusive_lock_sql[] = "PRAGMA locking_mode=\"exclusive\";", fsync_sql[] = "PRAGMA synchronous=\"NORMAL\";", foreign_keys[] = "PRAGMA foreign_keys=\"ON\";", enable_mmap[] = "PRAGMA mmap_size=" G_STRINGIFY(RSPAMD_SQLITE_MMAP_LIMIT) ";", other_pragmas[] = "PRAGMA read_uncommitted=\"ON\";" "PRAGMA cache_size=" G_STRINGIFY(RSPAMD_SQLITE_CACHE_SIZE) ";"; gboolean create = FALSE, has_lock = FALSE; flags = SQLITE_OPEN_READWRITE; #ifdef SQLITE_OPEN_SHAREDCACHE flags |= SQLITE_OPEN_SHAREDCACHE; #endif #ifdef SQLITE_OPEN_WAL flags |= SQLITE_OPEN_WAL; #endif rspamd_strlcpy (dbdir, path, sizeof (dbdir)); pdir = dirname (dbdir); if (access (pdir, W_OK) == -1) { g_set_error (err, rspamd_sqlite3_quark (), errno, "cannot open sqlite directory %s: %s", pdir, strerror (errno)); return NULL; } rspamd_snprintf (lock_path, sizeof (lock_path), "%s.lock", path); if (access (path, R_OK) == -1) { flags |= SQLITE_OPEN_CREATE; create = TRUE; } rspamd_snprintf (lock_path, sizeof (lock_path), "%s.lock", path); lock_fd = open (lock_path, O_WRONLY|O_CREAT|O_EXCL, 00600); if (lock_fd == -1 && (errno == EEXIST || errno == EBUSY)) { msg_debug_pool_check ("checking %s to wait for db being initialized", lock_path); if (!rspamd_sqlite3_wait (pool, lock_path)) { g_set_error (err, rspamd_sqlite3_quark (), errno, "cannot create sqlite file %s: %s", path, strerror (errno)); return NULL; } /* At this point we have database created */ create = FALSE; has_lock = FALSE; } else { msg_debug_pool_check ("locking %s to block other processes", lock_path); g_assert (rspamd_file_lock (lock_fd, FALSE)); has_lock = TRUE; } sqlite3_enable_shared_cache (1); if ((rc = sqlite3_open_v2 (path, &sqlite, flags, NULL)) != SQLITE_OK) { #if SQLITE_VERSION_NUMBER >= 3008000 g_set_error (err, rspamd_sqlite3_quark (), rc, "cannot open sqlite db %s: %s", path, sqlite3_errstr (rc)); #else g_set_error (err, rspamd_sqlite3_quark (), rc, "cannot open sqlite db %s: %d", path, rc); #endif return NULL; } if (create && has_lock) { if (sqlite3_exec (sqlite, sqlite_wal, NULL, NULL, NULL) != SQLITE_OK) { msg_warn_pool_check ("WAL mode is not supported (%s), locking issues might occur", sqlite3_errmsg (sqlite)); } if (sqlite3_exec (sqlite, exclusive_lock_sql, NULL, NULL, NULL) != SQLITE_OK) { msg_warn_pool_check ("cannot exclusively lock database to create schema: %s", sqlite3_errmsg (sqlite)); } if (create_sql) { if (sqlite3_exec (sqlite, create_sql, NULL, NULL, NULL) != SQLITE_OK) { g_set_error (err, rspamd_sqlite3_quark (), -1, "cannot execute create sql `%s`: %s", create_sql, sqlite3_errmsg (sqlite)); sqlite3_close (sqlite); rspamd_file_unlock (lock_fd, FALSE); unlink (lock_path); close (lock_fd); return NULL; } } sqlite3_close (sqlite); /* Reopen in normal mode */ msg_debug_pool_check ("reopening %s in normal mode", path); flags &= ~SQLITE_OPEN_CREATE; if ((rc = sqlite3_open_v2 (path, &sqlite, flags, NULL)) != SQLITE_OK) { #if SQLITE_VERSION_NUMBER >= 3008000 g_set_error (err, rspamd_sqlite3_quark (), rc, "cannot open sqlite db after creation %s: %s", path, sqlite3_errstr (rc)); #else g_set_error (err, rspamd_sqlite3_quark (), rc, "cannot open sqlite db after creation %s: %d", path, rc); #endif rspamd_file_unlock (lock_fd, FALSE); unlink (lock_path); close (lock_fd); return NULL; } } if (sqlite3_exec (sqlite, sqlite_wal, NULL, NULL, NULL) != SQLITE_OK) { msg_warn_pool_check ("WAL mode is not supported (%s), locking issues might occur", sqlite3_errmsg (sqlite)); } if (sqlite3_exec (sqlite, fsync_sql, NULL, NULL, NULL) != SQLITE_OK) { msg_warn_pool_check ("cannot set synchronous: %s", sqlite3_errmsg (sqlite)); } if ((rc = sqlite3_exec (sqlite, foreign_keys, NULL, NULL, NULL)) != SQLITE_OK) { msg_warn_pool_check ("cannot enable foreign keys: %s", sqlite3_errmsg (sqlite)); } #if defined(__LP64__) || defined(_LP64) if ((rc = sqlite3_exec (sqlite, enable_mmap, NULL, NULL, NULL)) != SQLITE_OK) { msg_warn_pool_check ("cannot enable mmap: %s", sqlite3_errmsg (sqlite)); } #endif if ((rc = sqlite3_exec (sqlite, other_pragmas, NULL, NULL, NULL)) != SQLITE_OK) { msg_warn_pool_check ("cannot execute tuning pragmas: %s", sqlite3_errmsg (sqlite)); } if (has_lock) { msg_debug_pool_check ("removing lock from %s", lock_path); rspamd_file_unlock (lock_fd, FALSE); unlink (lock_path); close (lock_fd); } return sqlite; }