1 Return-Path: <bremner@tesseract.cs.unb.ca>
\r
2 X-Original-To: notmuch@notmuchmail.org
\r
3 Delivered-To: notmuch@notmuchmail.org
\r
4 Received: from localhost (localhost [127.0.0.1])
\r
5 by olra.theworths.org (Postfix) with ESMTP id 2A08B431FCF
\r
6 for <notmuch@notmuchmail.org>; Sat, 5 Apr 2014 08:46:41 -0700 (PDT)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]
\r
13 Received: from olra.theworths.org ([127.0.0.1])
\r
14 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
15 with ESMTP id aNyATK6fzQ4O for <notmuch@notmuchmail.org>;
\r
16 Sat, 5 Apr 2014 08:46:32 -0700 (PDT)
\r
17 Received: from mx.xen14.node3324.gplhost.com (gitolite.debian.net
\r
18 [87.98.215.224]) (using TLSv1 with cipher AES256-SHA (256/256 bits))
\r
19 (No client certificate requested)
\r
20 by olra.theworths.org (Postfix) with ESMTPS id 50436431FB6
\r
21 for <notmuch@notmuchmail.org>; Sat, 5 Apr 2014 08:46:27 -0700 (PDT)
\r
22 Received: from remotemail by mx.xen14.node3324.gplhost.com with local (Exim
\r
23 4.72) (envelope-from <bremner@tesseract.cs.unb.ca>)
\r
24 id 1WWSmv-000833-VR; Sat, 05 Apr 2014 15:45:45 +0000
\r
25 Received: (nullmailer pid 10912 invoked by uid 1000); Sat, 05 Apr 2014
\r
27 From: David Bremner <david@tethera.net>
\r
28 To: notmuch@notmuchmail.org
\r
29 Subject: [Patch v7 1/6] dump: support gzipped and atomic output
\r
30 Date: Sat, 5 Apr 2014 12:43:51 -0300
\r
31 Message-Id: <1396712636-10640-2-git-send-email-david@tethera.net>
\r
32 X-Mailer: git-send-email 1.9.0
\r
33 In-Reply-To: <1396712636-10640-1-git-send-email-david@tethera.net>
\r
34 References: <1396712636-10640-1-git-send-email-david@tethera.net>
\r
35 X-BeenThere: notmuch@notmuchmail.org
\r
36 X-Mailman-Version: 2.1.13
\r
38 List-Id: "Use and development of the notmuch mail system."
\r
39 <notmuch.notmuchmail.org>
\r
40 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
41 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
42 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
43 List-Post: <mailto:notmuch@notmuchmail.org>
\r
44 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
45 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
46 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
47 X-List-Received-Date: Sat, 05 Apr 2014 15:46:41 -0000
\r
49 The main goal is to support gzipped output for future internal
\r
50 calls (e.g. from notmuch-new) to notmuch_database_dump.
\r
52 The additional dependency is not very heavy since xapian already pulls
\r
55 We want the dump to be "atomic", in the sense that after running the
\r
56 dump file is either present and complete, or not present. This avoids
\r
57 certain classes of mishaps involving overwriting a good backup with a
\r
60 INSTALL | 20 +++++++--
\r
61 Makefile.local | 2 +-
\r
62 configure | 28 +++++++++++--
\r
63 doc/man1/notmuch-dump.rst | 3 ++
\r
64 notmuch-client.h | 4 +-
\r
65 notmuch-dump.c | 101 +++++++++++++++++++++++++++++++++++++---------
\r
66 test/T240-dump-restore.sh | 12 ++++++
\r
67 7 files changed, 142 insertions(+), 28 deletions(-)
\r
69 diff --git a/INSTALL b/INSTALL
\r
70 index 690b0ef..b543c50 100644
\r
73 @@ -20,8 +20,8 @@ configure stage.
\r
77 -Notmuch depends on three libraries: Xapian, GMime 2.4 or 2.6, and
\r
78 -Talloc which are each described below:
\r
79 +Notmuch depends on four libraries: Xapian, GMime 2.4 or 2.6,
\r
80 +Talloc, and zlib which are each described below:
\r
84 @@ -60,6 +60,18 @@ Talloc which are each described below:
\r
86 Talloc is available from http://talloc.samba.org/
\r
91 + zlib is an extremely popular compression library. It is used
\r
92 + by Xapian, so if you installed that you will already have
\r
93 + zlib. You may need to install the zlib headers separately.
\r
95 + Notmuch needs the transparent write feature of zlib introduced
\r
96 + in version 1.2.5.2 (Dec. 2011).
\r
98 + zlib is available from http://zlib.net
\r
100 Building Documentation
\r
101 ----------------------
\r
103 @@ -79,11 +91,11 @@ dependencies with a simple simple command line. For example:
\r
105 For Debian and similar:
\r
107 - sudo apt-get install libxapian-dev libgmime-2.6-dev libtalloc-dev python-sphinx
\r
108 + sudo apt-get install libxapian-dev libgmime-2.6-dev libtalloc-dev zlib1g-dev python-sphinx
\r
110 For Fedora and similar:
\r
112 - sudo yum install xapian-core-devel gmime-devel libtalloc-devel python-sphinx
\r
113 + sudo yum install xapian-core-devel gmime-devel libtalloc-devel zlib-devel python-sphinx
\r
115 On other systems, a similar command can be used, but the details of
\r
116 the package names may be different.
\r
117 diff --git a/Makefile.local b/Makefile.local
\r
118 index cb7b106..e5a20a7 100644
\r
119 --- a/Makefile.local
\r
120 +++ b/Makefile.local
\r
121 @@ -41,7 +41,7 @@ PV_FILE=bindings/python/notmuch/version.py
\r
122 # Smash together user's values with our extra values
\r
123 FINAL_CFLAGS = -DNOTMUCH_VERSION=$(VERSION) $(CPPFLAGS) $(CFLAGS) $(WARN_CFLAGS) $(extra_cflags) $(CONFIGURE_CFLAGS)
\r
124 FINAL_CXXFLAGS = $(CPPFLAGS) $(CXXFLAGS) $(WARN_CXXFLAGS) $(extra_cflags) $(extra_cxxflags) $(CONFIGURE_CXXFLAGS)
\r
125 -FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Lutil -lutil -Llib -lnotmuch $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS)
\r
126 +FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Lutil -lutil -Llib -lnotmuch $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS) $(ZLIB_LDFLAGS)
\r
127 FINAL_NOTMUCH_LINKER = CC
\r
128 ifneq ($(LINKER_RESOLVES_LIBRARY_DEPENDENCIES),1)
\r
129 FINAL_NOTMUCH_LDFLAGS += $(CONFIGURE_LDFLAGS)
\r
130 diff --git a/configure b/configure
\r
131 index 1d430b9..83b4af7 100755
\r
134 @@ -340,6 +340,18 @@ else
\r
135 errors=$((errors + 1))
\r
138 +printf "Checking for zlib (>= 1.2.5.2)... "
\r
140 +if pkg-config --atleast-version=1.2.5.2 zlib; then
\r
143 + zlib_cflags=$(pkg-config --cflags zlib)
\r
144 + zlib_ldflags=$(pkg-config --libs zlib)
\r
147 + errors=$((errors + 1))
\r
150 printf "Checking for talloc development files... "
\r
151 if pkg-config --exists talloc; then
\r
153 @@ -496,6 +508,11 @@ EOF
\r
154 echo " Xapian library (including development files such as headers)"
\r
155 echo " http://xapian.org/"
\r
157 + if [ $have_zlib -eq 0 ]; then
\r
158 + echo " zlib library (>= version 1.2.5.2, including development files such as headers)"
\r
159 + echo " http://zlib.net/"
\r
162 if [ $have_gmime -eq 0 ]; then
\r
163 echo " Either GMime 2.4 library" $GMIME_24_VERSION_CTR "or GMime 2.6 library" $GMIME_26_VERSION_CTR
\r
164 echo " (including development files such as headers)"
\r
165 @@ -519,11 +536,11 @@ case a simple command will install everything you need. For example:
\r
167 On Debian and similar systems:
\r
169 - sudo apt-get install libxapian-dev libgmime-2.6-dev libtalloc-dev
\r
170 + sudo apt-get install libxapian-dev libgmime-2.6-dev libtalloc-dev zlib1g-dev
\r
172 Or on Fedora and similar systems:
\r
174 - sudo yum install xapian-core-devel gmime-devel libtalloc-devel
\r
175 + sudo yum install xapian-core-devel gmime-devel libtalloc-devel zlib-devel
\r
177 On other systems, similar commands can be used, but the details of the
\r
178 package names may be different.
\r
179 @@ -844,6 +861,10 @@ XAPIAN_LDFLAGS = ${xapian_ldflags}
\r
180 GMIME_CFLAGS = ${gmime_cflags}
\r
181 GMIME_LDFLAGS = ${gmime_ldflags}
\r
183 +# Flags needed to compile and link against zlib
\r
184 +ZLIB_CFLAGS = ${zlib_cflags}
\r
185 +ZLIB_LDFLAGS = ${zlib_ldflags}
\r
187 # Flags needed to compile and link against talloc
\r
188 TALLOC_CFLAGS = ${talloc_cflags}
\r
189 TALLOC_LDFLAGS = ${talloc_ldflags}
\r
190 @@ -882,6 +903,7 @@ CONFIGURE_CFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\
\r
191 -DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER)
\r
193 CONFIGURE_CXXFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\
\r
194 + \$(ZLIB_CFLAGS) \\
\r
195 \$(TALLOC_CFLAGS) -DHAVE_VALGRIND=\$(HAVE_VALGRIND) \\
\r
196 \$(VALGRIND_CFLAGS) \$(XAPIAN_CXXFLAGS) \\
\r
197 -DHAVE_STRCASESTR=\$(HAVE_STRCASESTR) \\
\r
198 @@ -892,5 +914,5 @@ CONFIGURE_CXXFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\
\r
199 -DHAVE_XAPIAN_COMPACT=\$(HAVE_XAPIAN_COMPACT) \\
\r
200 -DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER)
\r
202 -CONFIGURE_LDFLAGS = \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(XAPIAN_LDFLAGS)
\r
203 +CONFIGURE_LDFLAGS = \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(ZLIB_LDFLAGS) \$(XAPIAN_LDFLAGS)
\r
205 diff --git a/doc/man1/notmuch-dump.rst b/doc/man1/notmuch-dump.rst
\r
206 index 17d1da5..d94cb4f 100644
\r
207 --- a/doc/man1/notmuch-dump.rst
\r
208 +++ b/doc/man1/notmuch-dump.rst
\r
209 @@ -19,6 +19,9 @@ recreated from the messages themselves. The output of notmuch dump is
\r
210 therefore the only critical thing to backup (and much more friendly to
\r
211 incremental backup than the native database files.)
\r
214 + Compress the output in a format compatible with **gzip(1)**.
\r
216 ``--format=(sup|batch-tag)``
\r
217 Notmuch restore supports two plain text dump formats, both with one
\r
218 message-id per line, followed by a list of tags.
\r
219 diff --git a/notmuch-client.h b/notmuch-client.h
\r
220 index d110648..e1efbe0 100644
\r
221 --- a/notmuch-client.h
\r
222 +++ b/notmuch-client.h
\r
223 @@ -450,7 +450,9 @@ typedef enum dump_formats {
\r
225 notmuch_database_dump (notmuch_database_t *notmuch,
\r
226 const char *output_file_name,
\r
227 - const char *query_str, dump_format_t output_format);
\r
228 + const char *query_str,
\r
229 + dump_format_t output_format,
\r
230 + notmuch_bool_t gzip_output);
\r
232 #include "command-line-arguments.h"
\r
234 diff --git a/notmuch-dump.c b/notmuch-dump.c
\r
235 index 21702d7..2849eab 100644
\r
236 --- a/notmuch-dump.c
\r
237 +++ b/notmuch-dump.c
\r
239 #include "notmuch-client.h"
\r
240 #include "hex-escape.h"
\r
241 #include "string-util.h"
\r
246 -database_dump_file (notmuch_database_t *notmuch, FILE *output,
\r
247 +database_dump_file (notmuch_database_t *notmuch, gzFile output,
\r
248 const char *query_str, int output_format)
\r
250 notmuch_query_t *query;
\r
251 @@ -69,7 +71,7 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,
\r
254 if (output_format == DUMP_FORMAT_SUP) {
\r
255 - fprintf (output, "%s (", message_id);
\r
256 + gzprintf (output, "%s (", message_id);
\r
259 for (tags = notmuch_message_get_tags (message);
\r
260 @@ -78,12 +80,12 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,
\r
261 const char *tag_str = notmuch_tags_get (tags);
\r
264 - fputs (" ", output);
\r
265 + gzputs (output, " ");
\r
269 if (output_format == DUMP_FORMAT_SUP) {
\r
270 - fputs (tag_str, output);
\r
271 + gzputs (output, tag_str);
\r
273 if (hex_encode (notmuch, tag_str,
\r
274 &buffer, &buffer_size) != HEX_SUCCESS) {
\r
275 @@ -91,12 +93,12 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,
\r
277 return EXIT_FAILURE;
\r
279 - fprintf (output, "+%s", buffer);
\r
280 + gzprintf (output, "+%s", buffer);
\r
284 if (output_format == DUMP_FORMAT_SUP) {
\r
285 - fputs (")\n", output);
\r
286 + gzputs (output, ")\n");
\r
288 if (make_boolean_term (notmuch, "id", message_id,
\r
289 &buffer, &buffer_size)) {
\r
290 @@ -104,7 +106,7 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,
\r
291 message_id, strerror (errno));
\r
292 return EXIT_FAILURE;
\r
294 - fprintf (output, " -- %s\n", buffer);
\r
295 + gzprintf (output, " -- %s\n", buffer);
\r
298 notmuch_message_destroy (message);
\r
299 @@ -121,24 +123,83 @@ database_dump_file (notmuch_database_t *notmuch, FILE *output,
\r
301 notmuch_database_dump (notmuch_database_t *notmuch,
\r
302 const char *output_file_name,
\r
303 - const char *query_str, dump_format_t output_format)
\r
304 + const char *query_str,
\r
305 + dump_format_t output_format,
\r
306 + notmuch_bool_t gzip_output)
\r
308 - FILE *output = stdout;
\r
310 + gzFile output = NULL;
\r
311 + const char *mode = gzip_output ? "w9" : "wT";
\r
312 + const char *name_for_error = output_file_name ? output_file_name : "stdout";
\r
314 + char *tempname = NULL;
\r
319 if (output_file_name) {
\r
320 - output = fopen (output_file_name, "w");
\r
321 - if (output == NULL) {
\r
322 - fprintf (stderr, "Error opening %s for writing: %s\n",
\r
323 - output_file_name, strerror (errno));
\r
324 - return EXIT_FAILURE;
\r
326 + tempname = talloc_asprintf (notmuch, "%s.XXXXXX", output_file_name);
\r
327 + outfd = mkstemp (tempname);
\r
329 + outfd = dup (STDOUT_FILENO);
\r
333 + fprintf (stderr, "Bad output file %s\n", name_for_error);
\r
337 + output = gzdopen (outfd, mode);
\r
339 + if (output == NULL) {
\r
340 + fprintf (stderr, "Error opening %s for (gzip) writing: %s\n",
\r
341 + name_for_error, strerror (errno));
\r
342 + if (close (outfd))
\r
343 + fprintf (stderr, "Error closing %s during shutdown: %s\n",
\r
344 + name_for_error, strerror (errno));
\r
348 ret = database_dump_file (notmuch, output, query_str, output_format);
\r
349 + if (ret) goto DONE;
\r
351 + ret = gzflush (output, Z_FINISH);
\r
353 + fprintf (stderr, "Error flushing output: %s\n", gzerror (output, NULL));
\r
357 + if (output_file_name) {
\r
358 + ret = fdatasync (outfd);
\r
360 + fprintf (stderr, "Error syncing %s to disk: %s\n",
\r
361 + name_for_error, strerror (errno));
\r
366 + if (gzclose_w (output) != Z_OK) {
\r
367 + fprintf (stderr, "Error closing %s: %s\n", name_for_error,
\r
368 + gzerror (output, NULL));
\r
369 + ret = EXIT_FAILURE;
\r
374 + if (output_file_name) {
\r
375 + ret = rename (tempname, output_file_name);
\r
377 + fprintf (stderr, "Error renaming %s to %s: %s\n",
\r
378 + tempname, output_file_name, strerror (errno));
\r
384 + if (ret != EXIT_SUCCESS && output)
\r
385 + (void) gzclose_w (output);
\r
387 - if (output != stdout)
\r
389 + if (ret != EXIT_SUCCESS && output_file_name)
\r
390 + (void) unlink (tempname);
\r
394 @@ -158,6 +219,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
\r
397 int output_format = DUMP_FORMAT_BATCH_TAG;
\r
398 + notmuch_bool_t gzip_output = 0;
\r
400 notmuch_opt_desc_t options[] = {
\r
401 { NOTMUCH_OPT_KEYWORD, &output_format, "format", 'f',
\r
402 @@ -165,6 +227,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
\r
403 { "batch-tag", DUMP_FORMAT_BATCH_TAG },
\r
405 { NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0 },
\r
406 + { NOTMUCH_OPT_BOOLEAN, &gzip_output, "gzip", 'z', 0 },
\r
410 @@ -181,7 +244,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
\r
413 ret = notmuch_database_dump (notmuch, output_file_name, query_str,
\r
415 + output_format, gzip_output);
\r
417 notmuch_database_destroy (notmuch);
\r
419 diff --git a/test/T240-dump-restore.sh b/test/T240-dump-restore.sh
\r
420 index 0004438..d79aca8 100755
\r
421 --- a/test/T240-dump-restore.sh
\r
422 +++ b/test/T240-dump-restore.sh
\r
423 @@ -68,6 +68,18 @@ test_begin_subtest "dump --output=outfile --"
\r
424 notmuch dump --output=dump-1-arg-dash.actual --
\r
425 test_expect_equal_file dump.expected dump-1-arg-dash.actual
\r
429 +test_begin_subtest "dump --gzip"
\r
430 +notmuch dump --gzip > dump-gzip.gz
\r
431 +gunzip dump-gzip.gz
\r
432 +test_expect_equal_file dump.expected dump-gzip
\r
434 +test_begin_subtest "dump --gzip --output=outfile"
\r
435 +notmuch dump --gzip --output=dump-gzip-outfile.gz
\r
436 +gunzip dump-gzip-outfile.gz
\r
437 +test_expect_equal_file dump.expected dump-gzip-outfile
\r
439 # Note, we assume all messages from cworth have a message-id
\r
440 # containing cworth.org
\r