]> git.notmuchmail.org Git - notmuch/commitdiff
notmuch-deliver: Use splice() if it's available
authorAli Polatel <alip@exherbo.org>
Wed, 26 May 2010 11:21:15 +0000 (14:21 +0300)
committerAli Polatel <alip@exherbo.org>
Fri, 4 Nov 2011 23:12:35 +0000 (01:12 +0200)
NOTMUCH_DELIVER_NO_SPLICE environment variable may be set to fallback to
the read/write method.

contrib/notmuch-deliver/configure.ac
contrib/notmuch-deliver/src/main.c

index 9d16af16fd398c9999d683c8d15e47b3aac4a3e4..b6142bcec30a28a459b307c1c4018735e4886b3f 100644 (file)
@@ -67,7 +67,7 @@ AC_STRUCT_TM
 dnl }}}
 
 dnl {{{ Check for library functions
-AC_CHECK_FUNCS([setgroups initgroups symlink readlink strcasecmp utime utimes])
+AC_CHECK_FUNCS([setgroups initgroups symlink readlink strcasecmp utime utimes splice])
 dnl }}}
 
 dnl {{{ gethostname()
index 49919ff10591631f4a06f7c808fe5f2fe035d0da..7c314911e7afa574f4c1b64b19cc910ea2eea0b9 100644 (file)
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
+#ifdef HAVE_SPLICE
+#include <fcntl.h>
+#endif
 
 #ifdef HAVE_SYSEXITS_H
 #include <sysexits.h>
@@ -136,32 +141,61 @@ load_keyfile(const gchar *path, gchar **db_path, gchar ***tags)
        return TRUE;
 }
 
+#ifdef HAVE_SPLICE
 static int
-save_maildir(int fdin, const char *dir, int auto_create, char **path)
+save_splice(int fdin, int fdout)
 {
-       int fd, ret, written;
-       char buf[4096], *p;
-       struct maildir_tmpcreate_info info;
+       int ret, written, pfd[2];
 
-       maildir_tmpcreate_init(&info);
-       info.openmode = 0666;
-       info.maildir = dir;
-       info.doordie = 1;
+       if (pipe(pfd) < 0) {
+               g_critical("Failed to create pipe: %s", g_strerror(errno));
+               return EX_IOERR;
+       }
 
-       while ((fd = maildir_tmpcreate_fd(&info)) < 0)
-       {
-               if (errno == ENOENT && auto_create && maildir_mkdir(dir) == 0)
-               {
-                       auto_create = 0;
-                       continue;
+       for (;;) {
+               ret = splice(fdin, NULL, pfd[1], NULL, 4096, 0);
+               if (!ret)
+                       break;
+               if (ret < 0) {
+                       g_critical("Splicing data from standard input failed: %s",
+                               g_strerror(errno));
+                       close(pfd[0]);
+                       close(pfd[1]);
+                       return EX_IOERR;
                }
 
-               g_critical("Failed to create temporary file `%s': %s",
-                       info.tmpname, g_strerror(errno));
-               return EX_TEMPFAIL;
+               do {
+                       written = splice(pfd[0], NULL, fdout, NULL, ret, 0);
+                       if (!written) {
+                               g_critical("Splicing data to temporary file failed: %s",
+                                       g_strerror(errno));
+                               close(pfd[0]);
+                               close(pfd[1]);
+                               return EX_IOERR;
+                       }
+                       if (written < 0) {
+                               g_critical("Splicing data to temporary file failed: %s",
+                                       g_strerror(errno));
+                               close(pfd[0]);
+                               close(pfd[1]);
+                               return EX_IOERR;
+                       }
+                       ret -= written;
+               } while (ret);
        }
 
-       g_debug("Reading from standard input and writing to `%s'", info.tmpname);
+       close(pfd[0]);
+       close(pfd[1]);
+       return 0;
+}
+#endif /* HAVE_SPLICE */
+
+static int
+save_readwrite(int fdin, int fdout)
+{
+       int ret, written;
+       char buf[4096], p;
+
        for (;;) {
                ret = read(fdin, buf, 4096);
                if (!ret)
@@ -169,27 +203,66 @@ save_maildir(int fdin, const char *dir, int auto_create, char **path)
                if (ret < 0) {
                        if (errno == EINTR)
                                continue;
-                       g_critical("Reading from standard input failed: %s", g_strerror(errno));
-                       goto fail;
+                       g_critical("Reading from standard input failed: %s",
+                               g_strerror(errno));
+                       return EX_IOERR;
                }
                p = buf;
                do {
-                       written = write(fd, p, ret);
+                       written = write(fdout, p, ret);
                        if (!written)
-                               goto fail;
+                               return EX_IOERR;
                        if (written < 0) {
                                if (errno == EINTR)
                                        continue;
-                               g_critical("Writing to temporary file `%s' failed: %s",
-                                       info.tmpname, g_strerror(errno));
-                               goto fail;
+                               g_critical("Writing to temporary file failed: %s",
+                                       g_strerror(errno));
+                               return EX_IOERR;
                        }
                        p += written;
                        ret -= written;
                } while (ret);
        }
 
-       close(fd);
+       return 0;
+}
+
+static int
+save_maildir(int fdin, const char *dir, int auto_create, char **path)
+{
+       int fdout, ret;
+       struct maildir_tmpcreate_info info;
+
+       maildir_tmpcreate_init(&info);
+       info.openmode = 0666;
+       info.maildir = dir;
+       info.doordie = 1;
+
+       while ((fdout = maildir_tmpcreate_fd(&info)) < 0)
+       {
+               if (errno == ENOENT && auto_create && maildir_mkdir(dir) == 0)
+               {
+                       auto_create = 0;
+                       continue;
+               }
+
+               g_critical("Failed to create temporary file `%s': %s",
+                       info.tmpname, g_strerror(errno));
+               return EX_TEMPFAIL;
+       }
+
+       g_debug("Reading from standard input and writing to `%s'", info.tmpname);
+#ifdef HAVE_SPLICE
+       ret = g_getenv("NOTMUCH_DELIVER_NO_SPLICE")
+               ? save_readwrite(fdin, fdout)
+               : save_splice(fdin, fdout);
+#else
+       ret = save_readwrite(fdin, fdout);
+#endif /* HAVE_SPLICE */
+       if (ret)
+               goto fail;
+
+       close(fdout);
        g_debug("Moving `%s' to `%s'", info.tmpname, info.newname);
        if (maildir_movetmpnew(info.tmpname, info.newname)) {
                g_critical("Moving `%s' to `%s' failed: %s",
@@ -295,6 +368,9 @@ main(int argc, char **argv)
                "  "PACKAGE" uses notmuch's configuration file to determine database path and\n"
                "  initial tags to add to new messages. You may set NOTMUCH_CONFIG environment\n"
                "  variable to specify an alternative configuration file.\n"
+               "\nEnvironment:\n"
+               "  NOTMUCH_CONFIG: Path to notmuch configuration file\n"
+               "  NOTMUCH_DELIVER_NO_SPLICE: Don't use splice() even if it's available\n"
                "\nExit codes:\n"
                "  0   => Successful run\n"
                "  64  => Usage error\n"