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 +}
