delivermaildir

changeset 1:f2de397824b9

Copied from my local repository. Tailorized 476
author hopper
date Mon, 07 Jun 2004 14:33:58 +0000
parents 5e9ca1ca6f8b
children e85e1b9c07a9
files delivermaildir.c dmd.cmd
diffstat 2 files changed, 301 insertions(+), 0 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/delivermaildir.c	Mon Jun 07 14:33:58 2004 +0000
     1.3 @@ -0,0 +1,244 @@
     1.4 +/*
     1.5 + * Copyright (C) 2004 Eric M. Hopper <hopper@omnifarious.org>
     1.6 + * 
     1.7 + *     This program is free software; you can redistribute it and/or modify it
     1.8 + *     under the terms of the GNU Lesser General Public License as published
     1.9 + *     by the Free Software Foundation; either version 2 of the License, or
    1.10 + *     (at your option) any later version.
    1.11 + * 
    1.12 + *     This program is distributed in the hope that it will be useful, but
    1.13 + *     WITHOUT ANY WARRANTY; without even the implied warranty of
    1.14 + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.15 + *     Lesser General Public License for more details.
    1.16 + * 
    1.17 + *     You should have received a copy of the GNU Lesser General Public
    1.18 + *     License along with this program; if not, write to the Free Software
    1.19 + *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    1.20 + */
    1.21 +
    1.22 +#include <sys/stat.h>
    1.23 +#include <sys/types.h>
    1.24 +#include <fcntl.h>
    1.25 +#include <unistd.h>
    1.26 +#include <stdio.h>
    1.27 +#include <sysexits.h>
    1.28 +#include <errno.h>
    1.29 +#include <string.h>
    1.30 +#include <time.h>
    1.31 +#include <stddef.h>
    1.32 +#include <stdlib.h>
    1.33 +#include <signal.h>
    1.34 +
    1.35 +#define MAX_TRIES 5
    1.36 +#define FNAME_BUFSIZE 110
    1.37 +
    1.38 +static volatile sig_atomic_t should_deliver = 0;
    1.39 +
    1.40 +static int complete_write(int fd, const void *buf, size_t len)
    1.41 +{
    1.42 +   size_t num_written = 0;
    1.43 +   const char * const cbuf = (const char *)buf;
    1.44 +   while (num_written < len) {
    1.45 +      int result = write(fd, cbuf + num_written, len - num_written);
    1.46 +      if (result < 0) {
    1.47 +         return -1;
    1.48 +      } else {
    1.49 +         num_written += result;
    1.50 +      }
    1.51 +   }
    1.52 +   return 0;
    1.53 +}
    1.54 +
    1.55 +static void abort_delivery(int junk)
    1.56 +{
    1.57 +   static const char msg[] = "delivery aborted\n";
    1.58 +   should_deliver = 0;
    1.59 +   complete_write(1, msg, sizeof(msg) - 1);
    1.60 +}
    1.61 +
    1.62 +static void commit_delivery(int junk)
    1.63 +{
    1.64 +   static const char msg[] = "delivery confirmed\n";
    1.65 +   should_deliver = 1;
    1.66 +   complete_write(1, msg, sizeof(msg) - 1);
    1.67 +}
    1.68 +
    1.69 +static void usage(const char *name)
    1.70 +{
    1.71 +   fprintf(stderr, "Usage: %s <maildir>\n\n", name);
    1.72 +   fputs(
    1.73 +      "Notes: This program will write out newline terminated line with the\n"
    1.74 +      "word 'initialized' on stdout when it has set up signal handling.\n"
    1.75 +      "After this happens, something should send a SIGUSR1 signal, then wait\n"
    1.76 +      "for a newline terminated line saying 'delivery comfirmed' to be sent\n"
    1.77 +      "to stdout before stdin of this program is closed.  If this doesn't\n"
    1.78 +      "happen, this program will assume that the maildir delivery should be\n"
    1.79 +      "aborted.\n", stderr);
    1.80 +   exit(EX_USAGE);
    1.81 +}
    1.82 +
    1.83 +static void setup_signal_handlers()
    1.84 +{
    1.85 +   typedef struct sigaction sigaction_t;
    1.86 +   sigaction_t abort_act;
    1.87 +   sigaction_t commit_act;
    1.88 +   static const char msg[] = "initialized\n";
    1.89 +
    1.90 +   abort_act.sa_handler = abort_delivery;
    1.91 +   commit_act.sa_handler = commit_delivery;
    1.92 +   if ((sigemptyset(&(abort_act.sa_mask)) != 0) ||
    1.93 +       (sigemptyset(&(commit_act.sa_mask)) != 0))
    1.94 +   {
    1.95 +      const int thiserr = errno;
    1.96 +      fprintf(stderr, "sigemptyset: %s\n", strerror(thiserr));
    1.97 +      exit(EX_OSERR);
    1.98 +   }
    1.99 +   if ((sigaddset(&(abort_act.sa_mask), SIGUSR1) != 0) ||
   1.100 +       (sigaddset(&(abort_act.sa_mask), SIGUSR2) != 0) ||
   1.101 +       (sigaddset(&(commit_act.sa_mask), SIGUSR1) != 0) ||
   1.102 +       (sigaddset(&(commit_act.sa_mask), SIGUSR2) != 0))
   1.103 +   {
   1.104 +      const int thiserr = errno;
   1.105 +      fprintf(stderr, "sigaddset: %s\n", strerror(thiserr));
   1.106 +      exit(EX_OSERR);
   1.107 +   }
   1.108 +   abort_act.sa_flags = commit_act.sa_flags = SA_RESTART;
   1.109 +   if ((sigaction(SIGUSR1, &commit_act, NULL) != 0) ||
   1.110 +       (sigaction(SIGUSR2, &abort_act, NULL) != 0))
   1.111 +   {
   1.112 +      const int thiserr = errno;
   1.113 +      fprintf(stderr, "sigaction: %s\n", strerror(thiserr));
   1.114 +      exit(EX_OSERR);
   1.115 +   }
   1.116 +   if (complete_write(1, msg, sizeof(msg) - 1) != 0) {
   1.117 +      const int thiserr = errno;
   1.118 +      fprintf(stderr, "write: (stdout) %s\n", strerror(thiserr));
   1.119 +      exit(EX_OSERR);
   1.120 +   }
   1.121 +}
   1.122 +
   1.123 +static int create_dest(char tmpfname[], const char hostname[])
   1.124 +{
   1.125 +   const pid_t mypid = getpid();
   1.126 +   int i = 0;
   1.127 +   int ok = 0;
   1.128 +   int fd = -1;
   1.129 +
   1.130 +   for (i = 0; !ok && (i < MAX_TRIES); ++i) {
   1.131 +      const time_t now = time(NULL);
   1.132 +      struct stat sbuf;
   1.133 +      if (snprintf(tmpfname, FNAME_BUFSIZE - 1, "tmp/%lu.%u.%s",
   1.134 +                   now, mypid, hostname) <= 0) {
   1.135 +         fputs("Internal program error.\n", stderr);
   1.136 +         return EX_SOFTWARE;
   1.137 +      }
   1.138 +      tmpfname[FNAME_BUFSIZE - 1] = '\0';
   1.139 +      if (stat(tmpfname, &sbuf) != 0) {
   1.140 +         const int thiserr = errno;
   1.141 +         if (thiserr == ENOENT) {
   1.142 +            ok = 1;
   1.143 +         }
   1.144 +         if (!ok) {
   1.145 +            usleep(1100000);
   1.146 +         }
   1.147 +      }
   1.148 +   }
   1.149 +   fd = open(tmpfname, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
   1.150 +   if (fd < 0) {
   1.151 +      const int thiserr = errno;
   1.152 +      fprintf(stderr, "Unable to open: [%s] %s\n", tmpfname, strerror(thiserr));
   1.153 +      exit(EX_CANTCREAT);
   1.154 +   }
   1.155 +   return fd;
   1.156 +}
   1.157 +
   1.158 +static void copy_stdin(int mailfd, const char tmpfname[])
   1.159 +{
   1.160 +   char buf[64 * 1024];
   1.161 +   int result = 0;
   1.162 +   do {
   1.163 +      result = read(0, buf, sizeof(buf));
   1.164 +      if (result < 0) {
   1.165 +         const int thiserr = errno;
   1.166 +         fprintf(stderr, "read: (stdin) %s\n", strerror(thiserr));
   1.167 +         unlink(tmpfname);
   1.168 +         exit(EX_IOERR);
   1.169 +      }
   1.170 +      if (complete_write(mailfd, buf, result) < 0) {
   1.171 +         const int thiserr = errno;
   1.172 +         fprintf(stderr, "write: [%s] %s\n", tmpfname, strerror(thiserr));
   1.173 +         unlink(tmpfname);
   1.174 +         exit(EX_IOERR);
   1.175 +      }
   1.176 +   } while (result != 0);  /* Stop when EOF of stdin is found */
   1.177 +   if (fsync(mailfd) != 0) {
   1.178 +      const int thiserr = errno;
   1.179 +      fprintf(stderr, "fsync: [%s] %s\n", tmpfname, strerror(thiserr));
   1.180 +      unlink(tmpfname);
   1.181 +      exit(EX_IOERR);
   1.182 +   }
   1.183 +}
   1.184 +
   1.185 +static void link_good(const char tmpfname[])
   1.186 +{
   1.187 +   char newfname[FNAME_BUFSIZE] = "";
   1.188 +   strcpy(newfname, tmpfname);
   1.189 +   newfname[0] = 'n';
   1.190 +   newfname[1] = 'e';
   1.191 +   newfname[2] = 'w';
   1.192 +   if (link(tmpfname, newfname) < 0) {
   1.193 +      const int thiserr = errno;
   1.194 +      fprintf(stderr, "link: [%s] -> [%s] %s\n",
   1.195 +              tmpfname, newfname, strerror(thiserr));
   1.196 +      unlink(tmpfname);
   1.197 +      exit(EX_CANTCREAT);
   1.198 +   }
   1.199 +   {
   1.200 +      /* Attempt to sync the directory as well, just to be sure. */
   1.201 +      /* Ignore any errors, as there's nothing to be done about them anyway. */
   1.202 +      int dirfd = open("new", O_RDONLY);
   1.203 +      if (dirfd != -1) {
   1.204 +         /* Ignore error, since there's nothing to do about it anyway. */
   1.205 +         fsync(dirfd);
   1.206 +      }
   1.207 +   }
   1.208 +}
   1.209 +
   1.210 +int main(int argc, char *argv[])
   1.211 +{
   1.212 +   char hostname[70] = "";
   1.213 +   char tmpfname[FNAME_BUFSIZE] = "";
   1.214 +   int mailfd = -1;
   1.215 +
   1.216 +   setup_signal_handlers();
   1.217 +   if (gethostname(hostname, sizeof(hostname) - 1) != 0) {
   1.218 +      const int thiserr = errno;
   1.219 +      fprintf(stderr, "gethostname: %s\n", strerror(thiserr));
   1.220 +      return EX_OSERR;
   1.221 +   }
   1.222 +   hostname[sizeof(hostname) - 1] = '\0';
   1.223 +   if (argc != 2) {
   1.224 +      usage(argv[0]);
   1.225 +   }
   1.226 +   if (chdir(argv[1]) != 0) {
   1.227 +      const int thiserr = errno;
   1.228 +      fprintf(stderr, "chdir: [%s] %s\n", argv[1], strerror(thiserr));
   1.229 +      if ((thiserr == ENOMEM) || (thiserr == ELOOP) || (thiserr == EIO)) {
   1.230 +         return EX_OSERR;
   1.231 +      } else {
   1.232 +         return EX_CANTCREAT;
   1.233 +      }
   1.234 +   }
   1.235 +   mailfd = create_dest(tmpfname, hostname);
   1.236 +   copy_stdin(mailfd, tmpfname);
   1.237 +   close(mailfd);
   1.238 +   mailfd = -1;
   1.239 +   if (should_deliver) {
   1.240 +      link_good(tmpfname);
   1.241 +      unlink(tmpfname);
   1.242 +      return EX_OK;
   1.243 +   } else {
   1.244 +      unlink(tmpfname);
   1.245 +      return EX_NOPERM;
   1.246 +   }
   1.247 +}
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/dmd.cmd	Mon Jun 07 14:33:58 2004 +0000
     2.3 @@ -0,0 +1,57 @@
     2.4 +# Copyright (C)  2004 Eric M. Hopper <hopper@omnifarious.org>
     2.5 +#
     2.6 +#     This program is free software; you can redistribute it and/or modify it
     2.7 +#     under the terms of the GNU Lesser General Public License as published
     2.8 +#     by the Free Software Foundation; either version 2 of the License, or
     2.9 +#     (at your option) any later version.
    2.10 +#
    2.11 +#     This program is distributed in the hope that it will be useful, but
    2.12 +#     WITHOUT ANY WARRANTY; without even the implied warranty of
    2.13 +#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    2.14 +#     Lesser General Public License for more details.
    2.15 +#
    2.16 +#     You should have received a copy of the GNU Lesser General Public
    2.17 +#     License along with this program; if not, write to the Free Software
    2.18 +#     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    2.19 +
    2.20 +function dmd {
    2.21 +    if [ "$#" -ne 1 ]; then
    2.22 +        echo "Usage: $0 <maildir>" 1>&2
    2.23 +        return 64
    2.24 +    fi
    2.25 +    (
    2.26 +        cd "$1" || return 73
    2.27 +        local tmpdir="$(mktemp -d dmd-XXXXXXXXXX)" || return 73
    2.28 +        (
    2.29 +            mkfifo "$tmpdir/mail" || return 73
    2.30 +            mkfifo "$tmpdir/results" || return 73
    2.31 +            delivermaildir . <"$tmpdir/mail" >"$tmpdir/results" &
    2.32 +            local pid="$!"
    2.33 +            (
    2.34 +                local line
    2.35 +                read -rs -u 3 -t 30 line || return 70
    2.36 +                echo "line: $line" 1>&2
    2.37 +                if [ "$line" != "initialized" ]; then
    2.38 +                    return 70
    2.39 +                fi
    2.40 +                cat
    2.41 +                kill -USR2 "$pid"
    2.42 +                read -rs -u 3 -t 30 line
    2.43 +                echo "line: $line" 1>&2
    2.44 +            ) >"$tmpdir/mail" 3<"$tmpdir/results"
    2.45 +            local ret="$?"
    2.46 +            if [ "$ret" -ne 0 ]; then
    2.47 +                kill "$pid"
    2.48 +            else
    2.49 +                wait "$pid"
    2.50 +                ret="$?"
    2.51 +                echo "delivermaildir returned $ret" 1>&2
    2.52 +            fi
    2.53 +            return "$ret"
    2.54 +        )
    2.55 +        local ret="$?"
    2.56 +        rm -rf "$tmpdir"
    2.57 +        return "$ret"
    2.58 +    )
    2.59 +    return "$?"
    2.60 +}