1 /*
2 ** Copyright 1998 - 2005 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5
6 #include "courier.h"
7 #include "comctlfile.h"
8 #include "comqueuename.h"
9 #include "comstrtimestamp.h"
10 #include "comtrack.h"
11 #include "maxlongsize.h"
12 #include <sys/types.h>
13 #include <sys/uio.h>
14 #if HAVE_SYS_STAT_H
15 #include <sys/stat.h>
16 #endif
17 #if HAVE_UNISTD_H
18 #include <unistd.h>
19 #endif
20 #if HAVE_FCNTL_H
21 #include <fcntl.h>
22 #endif
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26
27 static const char rcsid[]="$Id: comctlfile.c,v 1.11 2005/02/04 02:44:59 mrsam Exp $";
28
ctlfile_openi(ino_t i,struct ctlfile * p,int ro)29 int ctlfile_openi(ino_t i, struct ctlfile *p, int ro)
30 {
31 return (ctlfile_openfn(qmsgsctlname(i), p, ro, 1));
32 }
33
ctlfile_openit(ino_t i,time_t t,struct ctlfile * p,int ro)34 int ctlfile_openit(ino_t i, time_t t, struct ctlfile *p, int ro)
35 {
36 return (ctlfile_openfn(qmsgqname(i, t), p, ro, 1));
37 }
38
ctlfile_openfn(const char * n,struct ctlfile * p,int ro,int chk)39 int ctlfile_openfn(const char *n, struct ctlfile *p, int ro, int chk)
40 {
41 struct stat stat_buf;
42 unsigned nlines=2, nreceipients=1;
43 char *cp;
44 unsigned i, j, rcnt, orcnt, dsncnt, nrcnt;
45 char *q;
46 static char deferred_status[2]={COMCTLFILE_DELDEFERRED, 0};
47
48 p->cancelled=0;
49 if ((p->fd=open(n, ro ? O_RDONLY:O_RDWR | O_APPEND)) < 0)
50 return (-1);
51
52 p->msgsize=0;
53
54 #ifdef FD_CLOEXEC
55 fcntl(p->fd, F_SETFD, FD_CLOEXEC);
56 #endif
57 if (fstat(p->fd, &stat_buf) < 0)
58 {
59 close (p->fd);
60 return (-1);
61 }
62
63 if (chk && stat_buf.st_nlink != 2)
64 {
65 if (ro)
66 {
67 close(p->fd);
68 errno=0;
69 return (-1);
70 }
71
72 if (stat_buf.st_nlink == 1)
73 /*
74 ** Happens all the time - delivery complete, hanging
75 ** link
76 */
77 {
78 unlink(n);
79 close(p->fd);
80 errno=0;
81 return (-1);
82 }
83 clog_msg_start_err();
84 clog_msg_str("Invalid number of links for ");
85 clog_msg_str(n);
86 clog_msg_send();
87 close(p->fd);
88 errno=EINVAL;
89 return (-1);
90 }
91 p->contents=q=courier_malloc(stat_buf.st_size+1);
92
93 i=stat_buf.st_size;
94 while (i)
95 {
96 j=read(p->fd, q, i);
97 if (j <= 0)
98 {
99 close(p->fd);
100 free(p->contents);
101 return (-1);
102 }
103 q += j;
104 i -= j;
105 }
106 *q=0;
107
108 p->n=stat_buf.st_ino;
109 p->mtime=stat_buf.st_mtime;
110 p->starttime=0;
111 for (cp=p->contents; *cp; cp++)
112 {
113 if (*cp == '\r')
114 *cp=' ';
115 if (*cp == '\n') ++nlines;
116 }
117 p->lines=(char **)courier_malloc(nlines * sizeof(char *));
118
119 for (nlines=0, cp=p->contents; (cp=strtok(cp, "\n")) != 0; cp=0)
120 {
121 p->lines[nlines++]=cp;
122 if (*cp == COMCTLFILE_RECEIPIENT) ++nreceipients;
123 if (*cp == COMCTLFILE_CANCEL_MSG) p->cancelled=1;
124 }
125 p->lines[nlines]=0;
126
127 p->sender="";
128 p->receipients=(char **)courier_malloc(nreceipients*sizeof(char *));
129 p->oreceipients=(char **)courier_malloc(nreceipients*sizeof(char *));
130 p->dsnreceipients=(char **)courier_malloc(nreceipients*sizeof(char *));
131 p->delstatus=(char **)courier_malloc(nreceipients*sizeof(char *));
132
133 for (i=0; i<nreceipients; i++)
134 {
135 p->receipients[i]=p->oreceipients[i]=p->dsnreceipients[i]=0;
136 p->delstatus[i]=deferred_status;
137 }
138
139 for (i=0, nrcnt=0, rcnt=0, orcnt=0, dsncnt=0; p->lines[i]; i++)
140 {
141 switch (p->lines[i][0]) {
142 case COMCTLFILE_SENDER:
143 p->sender=p->lines[i]+1;
144 break;
145 case COMCTLFILE_RECEIPIENT:
146 p->receipients[rcnt++]=p->lines[i]+1;
147 break;
148 case COMCTLFILE_ORECEIPIENT:
149 if (orcnt < nreceipients)
150 p->oreceipients[orcnt++]=p->lines[i]+1;
151 break;
152 case COMCTLFILE_DSN:
153 if (dsncnt < nreceipients)
154 p->dsnreceipients[dsncnt++]=p->lines[i]+1;
155 break;
156 case COMCTLFILE_DELSUCCESS:
157 case COMCTLFILE_DELFAIL:
158 j=atoi(p->lines[i]+1);
159 if (j < nreceipients)
160 p->delstatus[j]=p->lines[i];
161 break;
162 }
163 }
164 p->nreceipients=rcnt;
165
166 return (0);
167 }
168
ctlfile_close(struct ctlfile * p)169 void ctlfile_close(struct ctlfile *p)
170 {
171 free( p->oreceipients );
172 free( p->dsnreceipients );
173 free( p->receipients );
174 free( p->lines );
175 free( p->contents );
176 free( p->delstatus );
177
178 #if EXPLICITSYNC
179 fsync(p->fd);
180 #endif
181 close(p->fd);
182 }
183
ctlfile_searchfirst(struct ctlfile * p,char c)184 int ctlfile_searchfirst(struct ctlfile *p, char c)
185 {
186 int i;
187
188 for (i=0; p->lines[i]; i++)
189 if (p->lines[i][0] == c) return (i);
190 return (-1);
191 }
192
notatomic()193 static void notatomic()
194 {
195 clog_msg_start_err();
196 clog_msg_str("Write to control file FAILED - not atomic.");
197 clog_msg_send();
198 exit(1);
199 }
200
ctlfile_append(struct ctlfile * p,const char * q)201 void ctlfile_append(struct ctlfile *p, const char *q)
202 {
203 int l=strlen(q);
204
205 if (write(p->fd, q, l) != l) notatomic();
206 }
207
ctlfile_appendv(struct ctlfile * p,const struct iovec * iov,int iovcnt)208 void ctlfile_appendv(struct ctlfile *p, const struct iovec *iov, int iovcnt)
209 {
210 ctlfile_appendvfd(p->fd, iov, iovcnt);
211 }
212
ctlfile_appendvfd(int fd,const struct iovec * iov,int iovcnt)213 void ctlfile_appendvfd(int fd, const struct iovec *iov, int iovcnt)
214 {
215 int l=0;
216 int i;
217
218 for (i=0; i<iovcnt; i++)
219 l += iov[i].iov_len;
220
221 if (writev(fd, iov, iovcnt) != l) notatomic();
222 }
223
ctlfile_append_info(struct ctlfile * cf,unsigned rcptnum,const char * info)224 void ctlfile_append_info(struct ctlfile *cf, unsigned rcptnum, const char *info)
225 {
226 ctlfile_append_connectioninfo(cf, rcptnum, COMCTLFILE_DELINFO_REPLY,
227 info);
228 }
229
ctlfile_append_connectioninfo(struct ctlfile * cf,unsigned rcptnum,char infotype,const char * info)230 void ctlfile_append_connectioninfo(struct ctlfile *cf,
231 unsigned rcptnum, char infotype, const char *info)
232 {
233 if (infotype == COMCTLFILE_DELINFO_CONNECTIONERROR
234 || infotype == COMCTLFILE_DELINFO_REPLY)
235 {
236 clog_msg_start_info();
237 clog_msg_str("id=");
238 clog_msg_msgid(cf);
239 clog_msg_str(",from=<");
240 clog_msg_str(cf->sender);
241 clog_msg_str(">,addr=<");
242 clog_msg_str(cf->receipients[rcptnum]);
243 clog_msg_str(">: ");
244 clog_msg_str(info);
245 clog_msg_send();
246 }
247 ctlfile_append_connectioninfofd(cf->fd, rcptnum, infotype, info);
248 }
249
ctlfile_append_infofd(int fd,unsigned rcptnum,const char * info)250 void ctlfile_append_infofd(int fd, unsigned rcptnum, const char *info)
251 {
252 ctlfile_append_connectioninfofd(fd, rcptnum, COMCTLFILE_DELINFO_REPLY,
253 info);
254 }
255
ctlfile_append_connectioninfofd(int fd,unsigned rcptnum,char infotype,const char * info)256 void ctlfile_append_connectioninfofd(int fd, unsigned rcptnum,
257 char infotype, const char *info)
258 {
259 char fmtbuf[MAXLONGSIZE+10];
260 struct iovec iov[4];
261 char buf2[2];
262
263 sprintf(fmtbuf, "%c%u ", COMCTLFILE_DELINFO, rcptnum);
264 iov[0].iov_base=(caddr_t)fmtbuf;
265 iov[0].iov_len=strlen(fmtbuf);
266
267 buf2[0]=infotype;
268 buf2[1]=' ';
269 iov[1].iov_base=(caddr_t)buf2;
270 iov[1].iov_len=2;
271 iov[3].iov_base=(caddr_t)"\n";
272 iov[3].iov_len=1;
273
274 while (info && *info)
275 {
276 unsigned i;
277
278 for (i=0; info[i] && info[i] != '\n'; i++)
279 ;
280
281 iov[2].iov_base=(caddr_t)info;
282 iov[2].iov_len=i;
283
284 if (i && info[i-1] == '\r') --iov[2].iov_len;
285 ctlfile_appendvfd(fd, iov, 4);
286 info += i;
287 if (*info) ++info;
288 }
289 }
290
291 /* Record delivery completion status into the log file */
292
293 static void ctlfile_append_msgsize(struct ctlfile *);
294
ctlfile_append_reply(struct ctlfile * cf,unsigned rcptnum,const char * errmsg,char type,const char * delextra)295 void ctlfile_append_reply(struct ctlfile *cf, unsigned rcptnum,
296 const char *errmsg, char type, const char *delextra)
297 {
298 char type2;
299
300 if (errmsg && *errmsg)
301 {
302 clog_msg_start_info();
303 clog_msg_str("id=");
304 clog_msg_msgid(cf);
305 clog_msg_str(",from=<");
306 clog_msg_str(cf->sender);
307 clog_msg_str(">,addr=<");
308 clog_msg_str(cf->receipients[rcptnum]);
309 clog_msg_str(">");
310 if (type == COMCTLFILE_DELSUCCESS
311 || type == COMCTLFILE_DELSUCCESS_NOLOG)
312 {
313 if (cf->msgsize != 0)
314 ctlfile_append_msgsize(cf);
315 clog_msg_str(",success");
316 }
317 clog_msg_str(": ");
318 clog_msg_str(errmsg);
319 clog_msg_send();
320 if (type == COMCTLFILE_DELSUCCESS)
321 goto ugly_hack;
322 }
323 clog_msg_start_info();
324 clog_msg_str("id=");
325 clog_msg_msgid(cf);
326 clog_msg_str(",from=<");
327 clog_msg_str(cf->sender);
328 clog_msg_str(">,addr=<");
329 clog_msg_str(cf->receipients[rcptnum]);
330 clog_msg_str(">");
331
332 if ((type == COMCTLFILE_DELSUCCESS
333 || type == COMCTLFILE_DELSUCCESS_NOLOG)
334 && cf->msgsize != 0)
335 ctlfile_append_msgsize(cf);
336
337 clog_msg_str(type == COMCTLFILE_DELSUCCESS ||
338 type == COMCTLFILE_DELSUCCESS_NOLOG
339 ? ",status: success":
340 type == COMCTLFILE_DELFAIL ||
341 type == COMCTLFILE_DELFAIL_NOTRACK ? ",status: failure":
342 ",status: deferred");
343 clog_msg_send();
344
345 ugly_hack:
346
347 if (type == COMCTLFILE_DELSUCCESS_NOLOG)
348 {
349 type=COMCTLFILE_DELSUCCESS;
350 errmsg=0;
351 }
352
353 type2=type;
354
355 if (type2 == COMCTLFILE_DELFAIL_NOTRACK)
356 type2=COMCTLFILE_DELFAIL;
357
358 ctlfile_append_replyfd(cf->fd, rcptnum, errmsg, type2, delextra);
359
360 if (rcptnum < cf->nreceipients && /* Sanity check */
361 ctlfile_searchfirst(cf, COMCTLFILE_TRACK) >= 0)
362 {
363 time_t timestamp;
364 int results;
365
366 results=track_find(cf->receipients[rcptnum], ×tamp);
367
368 switch (type) {
369 case COMCTLFILE_DELFAIL_NOTRACK:
370 break;
371
372 case COMCTLFILE_DELSUCCESS:
373 if (results == TRACK_ADDRDEFERRED ||
374 results == TRACK_ADDRFAILED)
375 track_save(cf->receipients[rcptnum],
376 TRACK_ADDRACCEPTED);
377 break;
378
379 case COMCTLFILE_DELFAIL:
380 case COMCTLFILE_DELDEFERRED:
381
382 if (results == TRACK_ADDRDEFERRED ||
383 results == TRACK_ADDRFAILED)
384 {
385 if (timestamp >= time(NULL) -
386 (TRACK_NHOURS * 60 * 60)/2)
387 break;
388 /* If already had a failed attempt within
389 ** half the tracking window, no need to
390 ** add another record.
391 */
392 }
393 track_save(cf->receipients[rcptnum],
394 type == COMCTLFILE_DELFAIL ?
395 TRACK_ADDRFAILED:
396 TRACK_ADDRDEFERRED);
397 break;
398 default:
399 break;
400 }
401 }
402 }
403
ctlfile_append_msgsize(struct ctlfile * cf)404 static void ctlfile_append_msgsize(struct ctlfile *cf)
405 {
406 clog_msg_str(",size=");
407 clog_msg_ulong(cf->msgsize);
408 }
409
ctlfile_append_replyfd(int fd,unsigned rcptnum,const char * errmsg,char type,const char * delextra)410 void ctlfile_append_replyfd(int fd, unsigned rcptnum,
411 const char *errmsg, char type, const char *delextra)
412 {
413 const char *cp;
414 time_t timestamp;
415 char numbuf[1+MAXLONGSIZE];
416 int numbuflen;
417 struct iovec iov[12];
418 int n;
419 static const char delinfo=COMCTLFILE_DELINFO;
420 char replybuf[2];
421
422 time(×tamp);
423
424 sprintf(numbuf, "%u ", rcptnum);
425 numbuflen=strlen(numbuf);
426
427 replybuf[0]=COMCTLFILE_DELINFO_REPLY;
428 replybuf[1]=' ';
429
430 /*
431 ** errmsg can a multiline response. Each line is logged. The
432 ** last line of the response is logged together with the final
433 ** success/fail/deferred entry, so in most cases where there's
434 ** a one-line response, this results in only one write call.
435 */
436
437 n=0;
438
439 while (errmsg && *errmsg)
440 {
441 for (cp=errmsg; *cp && *cp != '\n'; cp++)
442 ;
443
444 iov[0].iov_base=(caddr_t)&delinfo;
445 iov[0].iov_len=1;
446 iov[1].iov_base=(caddr_t)numbuf;
447 iov[1].iov_len=numbuflen;
448
449 iov[2].iov_base=(caddr_t)replybuf;
450 iov[2].iov_len=2;
451
452 iov[3].iov_base=(caddr_t)errmsg;
453
454 /* Fix cruft like CRLF, or messages without trailing LFs */
455
456 if (cp > errmsg && cp[-1] == '\r')
457 {
458 iov[3].iov_len= cp - errmsg - 1;
459 iov[4].iov_base=(caddr_t)"\n";
460 iov[4].iov_len=1;
461 n=5;
462 }
463 else if (*cp == '\n')
464 {
465 iov[3].iov_len= cp - errmsg + 1;
466 n=4;
467 }
468 else
469 {
470 iov[3].iov_len= cp - errmsg;
471 iov[4].iov_base=(caddr_t)"\n";
472 iov[4].iov_len=1;
473 n=5;
474 }
475 if (*cp == '\n') ++cp;
476 errmsg=cp;
477 if (*errmsg == '\0')
478 break; /* Last line, attach it to status record */
479
480 ctlfile_appendvfd(fd, iov, n);
481 n=0;
482 }
483
484 iov[n].iov_base= (caddr_t)&type;
485 iov[n].iov_len=1;
486 ++n;
487 iov[n].iov_base= (caddr_t)numbuf;
488 iov[n].iov_len= numbuflen;
489 ++n;
490 cp=strtimestamp(timestamp);
491 iov[n].iov_base=(caddr_t)cp;
492 iov[n].iov_len=strlen(cp);
493 ++n;
494
495 if (delextra)
496 {
497 iov[n].iov_base=(caddr_t)delextra;
498 iov[n].iov_len=strlen(delextra);
499 ++n;
500 }
501
502 iov[n].iov_base=(caddr_t)"\n";
503 iov[n].iov_len=1;
504 ++n;
505 ctlfile_appendvfd(fd, iov, n);
506 }
507
ctlfile_nextattempt(struct ctlfile * ctf,time_t t)508 void ctlfile_nextattempt(struct ctlfile *ctf, time_t t)
509 {
510 ctlfile_nextattemptfd(ctf->fd, t);
511 }
512
513
ctlfile_nextattemptfd(int fd,time_t t)514 void ctlfile_nextattemptfd(int fd, time_t t)
515 {
516 const char *cp=strtimestamp(t);
517 struct iovec iov[3];
518 static const char a=COMCTLFILE_NEXTATTEMPT;
519
520 iov[0].iov_base=(caddr_t)&a;
521 iov[0].iov_len=1;
522 iov[1].iov_base=(caddr_t)cp;
523 iov[1].iov_len=strlen(cp);
524 iov[2].iov_base="\n";
525 iov[2].iov_len=1;
526
527 ctlfile_appendvfd(fd, iov, 3);
528 #if EXPLICITSYNC
529 fsync(fd);
530 #endif
531 }
532
ctlfile_getnextattempt(struct ctlfile * c)533 time_t ctlfile_getnextattempt(struct ctlfile *c)
534 {
535 unsigned i;
536 time_t t;
537 const char *cp;
538 struct stat stat_buf;
539
540 for (i=0; c->lines[i]; i++)
541 ;
542
543 while (i)
544 {
545 if (c->lines[--i][0] != COMCTLFILE_NEXTATTEMPT)
546 continue;
547 for (t=0, cp=c->lines[i]+1; *cp >= '0' && *cp <= '9'; ++cp)
548 t=t*10 + (*cp-'0');
549 if (stat(qmsgqname(c->n, t), &stat_buf) == 0)
550 return (t);
551 }
552 return (0);
553 }
554