1/* 2 * Copyright (c) 1995-1999 Hannah Schroeter <hannah@mamba.pond.sub.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer, 9 * either in a separate file included with the distribution, or 10 * copied into the source files. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND HIS CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR HIS CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/types.h> 29 30#include <sys/file.h> 31#include <sys/time.h> 32#include <sys/resource.h> 33#include <sys/stat.h> 34#include <sys/wait.h> 35 36#include <errno.h> 37#include <fcntl.h> 38#include <signal.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42#include <time.h> 43#include <unistd.h> 44 45#ifdef RLIMIT_NOFILE 46struct rlimit fdlimit; 47#define getdtablesize() (getrlimit(RLIMIT_NOFILE,&fdlimit)?\ 48 (perror("getrlimit RLIMIT_NOFILE"),exit(EX_OSERR),0):\ 49 (int)fdlimit.rlim_cur) 50#endif 51 52#define MAXHOSTNAME 255 53#define TRANSFERBUFSIZE 2048 54/* safety */ 55 56/* From .../sendmail/src/sysexits.h */ 57#define EX_OK 0 58#define EX_USAGE 64 59#define EX_NOINPUT 66 60#define EX_NOHOST 68 61#define EX_OSERR 71 62#define EX_IOERR 74 63#define EX_TEMPFAIL 75 64 65#ifdef USE_FLOCK 66#undef USE_FLOCK 67#define USE_FLOCK 1 68#undef USE_LOCKFILES 69#else 70#undef USE_LOCKFILES 71#define USE_LOCKFILES 1 72#endif 73 74int maxfd; 75extern int errno; 76 77#if USE_LOCKFILES 78int lock (char *filename) 79{ 80 int fd; 81 fd = open (filename, O_CREAT | O_EXCL, 0000); 82 if (fd >= 0) { 83 close (fd); 84 return 0; 85 } 86 if (errno != EEXIST) { 87 perror("open (lockfile)"); 88 exit(EX_OSERR); 89 } 90 return -1; 91} 92 93void lockwait (char *filename) 94{ 95 while (lock (filename)) sleep (2); 96} 97 98void unlock (char *filename) 99{ 100 unlink (filename); 101} 102#endif 103 104void usage (char *av0) 105{ 106 fprintf (stderr, "usage: %s method hostname\n", av0); 107 exit (EX_USAGE); 108} 109 110int main (int argc, char **argv) 111{ 112 char *method, *host; 113#if USE_LOCKFILES 114 char lockfilename [MAXHOSTNAME+1+5]; 115#endif 116 char hostbuf[MAXHOSTNAME+6]; 117 int pid; 118 int pipefd[2]; 119 char transmitter_path[FILENAME_MAX+1]; 120 char transmitter_argv0 [FILENAME_MAX+1]; 121 int i; 122 char transferbuf[TRANSFERBUFSIZE]; 123 int hostfd; 124 int childstat; 125 126 maxfd = getdtablesize(); 127 128 argv[argc]=0; /* XXX what for? */ 129 130 if (argc != 3) 131 usage (argv[0]); 132 133 umask (007); 134 if (chdir ("@QUEUEDIR@")) { 135 perror ("chdir @QUEUEDIR@"); 136 exit (EX_IOERR); 137 } 138 method = argv[1]; 139 host = argv[2]; 140 if (strlen (host) > MAXHOSTNAME) { 141 fprintf (stderr, "%s: Hostname too long\n", host); 142 exit (EX_NOHOST); 143 } 144 { 145 struct stat s; 146 if (stat (host, &s)) { 147 if (errno == ENOENT) { 148 fprintf (stderr, "%s: Host unknown (or no batch present)\n", host); 149 exit (EX_NOHOST); 150 } else { 151 fprintf (stderr, "stat @QUEUEDIR@/%s", host); perror (""); 152 exit (EX_IOERR); 153 } 154 } 155 if (!s.st_size) { 156 /* nothing to batch */ 157 exit (0); 158 } 159 } 160 hostfd = open (host, O_RDWR, 0); 161 if (hostfd < 0) { 162 perror (host); exit (EX_IOERR); 163 } 164#if USE_LOCKFILES 165 sprintf (lockfilename, "%s.lock", host); 166 lockwait (lockfilename); 167#endif 168#if USE_FLOCK 169 lockredo: 170 if (flock(hostfd, LOCK_EX) < 0) { 171 switch (errno) { 172 case EBADF: 173 fprintf (stderr, "Panic: flock returns EBADF\n"); 174 exit (EX_OSERR); 175 /* NOTREACHED */ 176 case EINVAL: 177 fprintf (stderr, "Panic: flock returns EINVAL\n"); 178 fprintf (stderr, "Was @QUEUEDIR@/%s not a plain file?\n", host); 179 exit (EX_OSERR); 180 /* NOTREACHED */ 181 case EINTR: 182 goto lockredo; 183 default: 184 perror("Panic: flock"); 185 exit(EX_OSERR); 186 } 187 } 188#endif 189 if (! strncmp("@LOCALHOSTNAME@", "`hostname", 9)) { 190 if (gethostname (hostbuf, MAXHOSTNAME)) { 191#if USE_LOCKFILES 192 unlock (lockfilename); 193#endif 194 perror ("gethostname"); exit (EX_OSERR); 195 /* flock() lock implicitly cleared */ 196 } 197 } else { 198 strcpy(hostbuf, "@LOCALHOSTNAME@"); 199 } 200 hostbuf[sizeof(hostbuf)-1] = 0; 201 if (! strchr (hostbuf, '.') && strcmp("@DOMAINSUFFIX@", "none")) 202 strcat (hostbuf, "@DOMAINSUFFIX@"); 203 if (pipe (pipefd)) { 204#if USE_LOCKFILES 205 unlock (lockfilename); 206#endif 207 perror ("pipe"); exit (EX_OSERR); 208 /* flock() locks are implicitly cleared */ 209 } 210 strcpy (transmitter_path, "@PRIVBINDIR@/transmitter."); 211 if (strlen (transmitter_path) + strlen (method) > FILENAME_MAX) { 212 fprintf (stderr, "%s: Method name too long\n", method); 213#if USE_LOCKFILES 214 unlock (lockfilename); 215#endif 216 exit (EX_USAGE); 217 /* flock() locks are implicitly cleared */ 218 } 219 strcat (transmitter_path, method); 220 strcpy (transmitter_argv0, "transmitter."); 221 strcat (transmitter_argv0, method); 222 pid = fork (); 223 if (pid < 0) { 224 perror ("fork"); 225#if USE_LOCKFILES 226 unlock (lockfilename); 227#endif 228 exit (EX_OSERR); 229 /* flock() lock is implicitly cleared */ 230 } 231 if (pid == 0) { 232 /* child */ 233 close (pipefd[1]); 234 dup2 (pipefd[0], 0); /* fd 0 -> pipe */ 235 /* keep fd 1 and fd 2 */ 236 for (i = 3; i < maxfd; i++) close (i); 237 /* note: this will also close hostfd. Thus, the flock() variant 238 * of the batcher relies on the fact, that flock() has 239 * the BSD last close semantics (and renders broken flock() 240 * emulations basing on POSIX locks useless in such situations!) 241 */ 242 if (setpgid (0, getpid())) { 243 perror ("setpgid"); 244 exit (EX_OSERR); 245 } 246 execl (transmitter_path, transmitter_argv0, host, NULL); 247 perror ("execl transmitter"); 248 exit (EX_OSERR); 249 } 250 signal (SIGPIPE, SIG_IGN); 251 /* Now write the pipe */ 252 if (write (pipefd[1], "HELO ", 5) != 5) 253 goto bad; 254 { int l = strlen (hostbuf); 255 if (write (pipefd[1], hostbuf, l) != l) 256 goto bad; 257 } 258 if (write (pipefd[1], "\n", 1) != 1) 259 goto bad; 260 261 while (1) { 262 int rd; 263 rd = read (hostfd, transferbuf, TRANSFERBUFSIZE); 264 if (rd < 0) goto bad; 265 if (rd == 0) break; 266 if (write (pipefd[1], transferbuf, rd) != rd) goto bad; 267 } 268 if (write (pipefd[1], "QUIT\n", 5) != 5) goto bad; 269 close (pipefd[1]); 270 /* send EOF */ 271 272 while (wait (&childstat) != pid) 273 ; 274 if (WIFSIGNALED(childstat) || WEXITSTATUS(childstat)) goto bad2; 275 /* child exited ok */ 276 if (ftruncate (hostfd, 0)) { 277#if USE_LOCKFILES 278 unlock (lockfilename); 279#endif 280 perror ("ftruncate"); exit (EX_OSERR); 281 } 282#if USE_LOCKFILES 283 unlock (lockfilename); 284#endif 285 exit (EX_OK); 286 /* flock() locks are cleared implicitly */ 287bad: 288 /* parent error */ 289 killpg (pid, SIGTERM); 290 sleep (2); 291 killpg (pid, SIGKILL); 292 while (wait (&childstat) != pid) 293 ; 294bad2: 295#if USE_LOCKFILES 296 unlock (lockfilename); 297#endif 298 if (WIFSIGNALED(childstat)) { 299 signal (WTERMSIG(childstat), SIG_DFL); 300 kill (getpid(), WTERMSIG(childstat)); 301 sleep (1); 302 exit (WTERMSIG(childstat)); 303 } 304 return (WEXITSTATUS(childstat)); 305} 306