NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
db.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <errno.h>
31 #include <limits.h>
32 #include <notmuch.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include <time.h>
38 #include "private.h"
39 #include "mutt/lib.h"
40 #include "config/lib.h"
41 #include "email/lib.h"
42 #include "core/lib.h"
43 #include "lib.h"
44 #include "adata.h"
45 #include "mdata.h"
46 #include "mutt_logging.h"
47 
57 const char *nm_db_get_filename(struct Mailbox *m)
58 {
59  struct NmMboxData *mdata = nm_mdata_get(m);
60  const char *db_filename = NULL;
61 
62  const char *const c_nm_default_url =
63  cs_subset_string(NeoMutt->sub, "nm_default_url");
64  if (mdata && mdata->db_url && mdata->db_url->path)
65  db_filename = mdata->db_url->path;
66  else
67  db_filename = c_nm_default_url;
68 
69  const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
70  if (!db_filename && !c_folder)
71  return NULL;
72 
73  if (!db_filename)
74  db_filename = c_folder;
75 
76  if (nm_path_probe(db_filename, NULL) == MUTT_NOTMUCH)
77  db_filename += NmUrlProtocolLen;
78 
79  mutt_debug(LL_DEBUG2, "nm: db filename '%s'\n", db_filename);
80  return db_filename;
81 }
82 
90 notmuch_database_t *nm_db_do_open(const char *filename, bool writable, bool verbose)
91 {
92  struct stat st1;
93  char buf[PATH_MAX];
94  if (stat(mutt_path_concat(buf, filename, ".notmuch", sizeof(buf)), &st1) != 0)
95  {
96  mutt_error(_("Can't stat %s: %s"), buf, strerror(errno));
97  return NULL;
98  }
99  else if (!S_ISDIR(st1.st_mode))
100  {
101  mutt_error(_("%s is not a directory"), buf);
102  return NULL;
103  }
104 
105  notmuch_database_t *db = NULL;
106  int ct = 0;
107  notmuch_status_t st = NOTMUCH_STATUS_SUCCESS;
108 #if LIBNOTMUCH_CHECK_VERSION(4, 2, 0)
109  char *msg = NULL;
110 #endif
111 
112  const short c_nm_open_timeout =
113  cs_subset_number(NeoMutt->sub, "nm_open_timeout");
114  mutt_debug(LL_DEBUG1, "nm: db open '%s' %s (timeout %d)\n", filename,
115  writable ? "[WRITE]" : "[READ]", c_nm_open_timeout);
116 
117  const notmuch_database_mode_t mode =
118  writable ? NOTMUCH_DATABASE_MODE_READ_WRITE : NOTMUCH_DATABASE_MODE_READ_ONLY;
119 
120  do
121  {
122 #if LIBNOTMUCH_CHECK_VERSION(4, 2, 0)
123  st = notmuch_database_open_verbose(filename, mode, &db, &msg);
124 #elif defined(NOTMUCH_API_3)
125  st = notmuch_database_open(filename, mode, &db);
126 #else
127  db = notmuch_database_open(filename, mode);
128 #endif
129  if ((st == NOTMUCH_STATUS_FILE_ERROR) || db || !c_nm_open_timeout ||
130  ((ct / 2) > c_nm_open_timeout))
131  {
132  break;
133  }
134 
135  if (verbose && ct && ((ct % 2) == 0))
136  mutt_error(_("Waiting for notmuch DB... (%d sec)"), ct / 2);
137  mutt_date_sleep_ms(500); /* Half a second */
138  ct++;
139  } while (true);
140 
141  if (verbose)
142  {
143  if (!db)
144  {
145 #if LIBNOTMUCH_CHECK_VERSION(4, 2, 0)
146  if (msg)
147  {
148  mutt_error(msg);
149  FREE(&msg);
150  }
151  else
152 #endif
153  {
154  mutt_error(_("Can't open notmuch database: %s: %s"), filename,
155  st ? notmuch_status_to_string(st) : _("unknown reason"));
156  }
157  }
158  else if (ct > 1)
159  {
161  }
162  }
163  return db;
164 }
165 
172 notmuch_database_t *nm_db_get(struct Mailbox *m, bool writable)
173 {
174  struct NmAccountData *adata = nm_adata_get(m);
175 
176  if (!adata)
177  return NULL;
178 
179  // Use an existing open db if we have one.
180  if (adata->db)
181  return adata->db;
182 
183  const char *db_filename = nm_db_get_filename(m);
184  if (db_filename)
185  adata->db = nm_db_do_open(db_filename, writable, true);
186 
187  return adata->db;
188 }
189 
196 int nm_db_release(struct Mailbox *m)
197 {
198  struct NmAccountData *adata = nm_adata_get(m);
199  if (!adata || !adata->db || nm_db_is_longrun(m))
200  return -1;
201 
202  mutt_debug(LL_DEBUG1, "nm: db close\n");
203  nm_db_free(adata->db);
204  adata->db = NULL;
205  adata->longrun = false;
206  return 0;
207 }
208 
213 void nm_db_free(notmuch_database_t *db)
214 {
215 #ifdef NOTMUCH_API_3
216  notmuch_database_destroy(db);
217 #else
218  notmuch_database_close(db);
219 #endif
220 }
221 
230 {
231  struct NmAccountData *adata = nm_adata_get(m);
232  if (!adata || !adata->db)
233  return -1;
234 
235  if (adata->trans)
236  return 0;
237 
238  mutt_debug(LL_DEBUG2, "nm: db trans start\n");
239  if (notmuch_database_begin_atomic(adata->db))
240  return -1;
241  adata->trans = true;
242  return 1;
243 }
244 
251 int nm_db_trans_end(struct Mailbox *m)
252 {
253  struct NmAccountData *adata = nm_adata_get(m);
254  if (!adata || !adata->db)
255  return -1;
256 
257  if (!adata->trans)
258  return 0;
259 
260  mutt_debug(LL_DEBUG2, "nm: db trans end\n");
261  adata->trans = false;
262  if (notmuch_database_end_atomic(adata->db))
263  return -1;
264 
265  return 0;
266 }
267 
278 int nm_db_get_mtime(struct Mailbox *m, time_t *mtime)
279 {
280  if (!m || !mtime)
281  return -1;
282 
283  char path[PATH_MAX];
284  snprintf(path, sizeof(path), "%s/.notmuch/xapian", nm_db_get_filename(m));
285  mutt_debug(LL_DEBUG2, "nm: checking '%s' mtime\n", path);
286 
287  struct stat st;
288  if (stat(path, &st) != 0)
289  return -1;
290 
291  *mtime = st.st_mtime;
292  return 0;
293 }
294 
300 bool nm_db_is_longrun(struct Mailbox *m)
301 {
302  struct NmAccountData *adata = nm_adata_get(m);
303  if (!adata)
304  return false;
305 
306  return adata->longrun;
307 }
308 
314 void nm_db_longrun_init(struct Mailbox *m, bool writable)
315 {
316  struct NmAccountData *adata = nm_adata_get(m);
317 
318  if (!(adata && nm_db_get(m, writable)))
319  return;
320 
321  adata->longrun = true;
322  mutt_debug(LL_DEBUG2, "nm: long run initialized\n");
323 }
324 
330 {
331  struct NmAccountData *adata = nm_adata_get(m);
332 
333  if (adata)
334  {
335  adata->longrun = false; /* to force nm_db_release() released DB */
336  if (nm_db_release(m) == 0)
337  mutt_debug(LL_DEBUG2, "nm: long run deinitialized\n");
338  else
339  adata->longrun = true;
340  }
341 }
342 
347 void nm_db_debug_check(struct Mailbox *m)
348 {
349  struct NmAccountData *adata = nm_adata_get(m);
350  if (!adata || !adata->db)
351  return;
352 
353  mutt_debug(LL_DEBUG1, "nm: ERROR: db is open, closing\n");
354  nm_db_release(m);
355 }
notmuch_database_t * nm_db_do_open(const char *filename, bool writable, bool verbose)
Open a Notmuch database.
Definition: db.c:90
int nm_db_get_mtime(struct Mailbox *m, time_t *mtime)
Get the database modification time.
Definition: db.c:278
Structs that make up an email.
#define mutt_error(...)
Definition: logging.h:88
Notmuch-specific Account data.
NeoMutt Logging.
#define _(a)
Definition: message.h:28
void nm_db_longrun_init(struct Mailbox *m, bool writable)
Start a long transaction.
Definition: db.c:314
bool longrun
A long-lived action is in progress.
Definition: adata.h:37
int nm_db_trans_begin(struct Mailbox *m)
Start a Notmuch database transaction.
Definition: db.c:229
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
Container for Accounts, Notifications.
Definition: neomutt.h:36
notmuch_database_t * nm_db_get(struct Mailbox *m, bool writable)
Get the Notmuch database.
Definition: db.c:172
Convenience wrapper for the config headers.
notmuch_database_t * db
Definition: adata.h:36
bool nm_db_is_longrun(struct Mailbox *m)
Is Notmuch in the middle of a long-running transaction.
Definition: db.c:300
Log at debug level 2.
Definition: logging.h:41
Notmuch-specific Mailbox data -.
Definition: mdata.h:33
Convenience wrapper for the core headers.
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:112
void * mdata
Driver specific data.
Definition: mailbox.h:136
void nm_db_debug_check(struct Mailbox *m)
Check if the database is open.
Definition: db.c:347
struct NmMboxData * nm_mdata_get(struct Mailbox *m)
Get the Notmuch Mailbox data.
Definition: mdata.c:98
int nm_db_trans_end(struct Mailbox *m)
End a database transaction.
Definition: db.c:251
A mailbox.
Definition: mailbox.h:81
#define PATH_MAX
Definition: mutt.h:40
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
void nm_db_free(notmuch_database_t *db)
decoupled way to close a Notmuch database
Definition: db.c:213
void nm_db_longrun_done(struct Mailbox *m)
Finish a long transaction.
Definition: db.c:329
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool verbose
Display status messages?
Definition: mailbox.h:118
bool trans
Atomic transaction in progress.
Definition: adata.h:38
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
struct Url * db_url
Parsed view url of the Notmuch database.
Definition: mdata.h:35
char * path
Path.
Definition: url.h:75
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:54
enum MailboxType nm_path_probe(const char *path, const struct stat *st)
Is this a Notmuch Mailbox? - Implements MxOps::path_probe() -.
Definition: notmuch.c:2458
Log at debug level 1.
Definition: logging.h:40
struct NmAccountData * nm_adata_get(struct Mailbox *m)
Get the Notmuch Account data.
Definition: adata.c:68
int nm_db_release(struct Mailbox *m)
Close the Notmuch database.
Definition: db.c:196
#define FREE(x)
Definition: memory.h:40
char * mutt_path_concat(char *d, const char *dir, const char *fname, size_t l)
Join a directory name and a filename.
Definition: path.c:351
const int NmUrlProtocolLen
Definition: notmuch.c:94
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Convenience wrapper for the library headers.
Notmuch private types.
void mutt_date_sleep_ms(size_t ms)
Sleep for milliseconds.
Definition: date.c:704
Notmuch-specific Mailbox data.
Notmuch-specific Account data -.
Definition: adata.h:34
const char * nm_db_get_filename(struct Mailbox *m)
Get the filename of the Notmuch database.
Definition: db.c:57