source: trunk/third/sendmail/src/err.c @ 12554

Revision 12554, 15.0 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12553, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1988, 1993
5 *      The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13#ifndef lint
14static char sccsid[] = "@(#)err.c       8.74 (Berkeley) 6/4/1998";
15#endif /* not lint */
16
17# include "sendmail.h"
18# include <errno.h>
19
20/*
21**  SYSERR -- Print error message.
22**
23**      Prints an error message via printf to the diagnostic output.
24**
25**      If the first character of the syserr message is `!' it will
26**      log this as an ALERT message and exit immediately.  This can
27**      leave queue files in an indeterminate state, so it should not
28**      be used lightly.
29**
30**      Parameters:
31**              fmt -- the format string.  If it does not begin with
32**                      a three-digit SMTP reply code, either 554 or
33**                      451 is assumed depending on whether errno
34**                      is set.
35**              (others) -- parameters
36**
37**      Returns:
38**              none
39**              Through TopFrame if QuickAbort is set.
40**
41**      Side Effects:
42**              increments Errors.
43**              sets ExitStat.
44*/
45
46char    MsgBuf[BUFSIZ*2];               /* text of most recent message */
47char    HeldMessageBuf[sizeof MsgBuf];  /* for held messages */
48
49extern void     putoutmsg __P((char *, bool, bool));
50extern void     puterrmsg __P((char *));
51static void     fmtmsg __P((char *, const char *, const char *, int, const char *, va_list));
52
53#if NAMED_BIND && !defined(NO_DATA)
54# define NO_DATA        NO_ADDRESS
55#endif
56
57void
58/*VARARGS1*/
59#ifdef __STDC__
60syserr(const char *fmt, ...)
61#else
62syserr(fmt, va_alist)
63        const char *fmt;
64        va_dcl
65#endif
66{
67        register char *p;
68        int olderrno = errno;
69        bool panic;
70        char *uname;
71        struct passwd *pw;
72        char ubuf[80];
73        VA_LOCAL_DECL
74
75        panic = *fmt == '!';
76        if (panic)
77        {
78                fmt++;
79                HoldErrs = FALSE;
80        }
81
82        /* format and output the error message */
83        if (olderrno == 0)
84                p = "554";
85        else
86                p = "451";
87        VA_START(fmt);
88        fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, ap);
89        VA_END;
90        puterrmsg(MsgBuf);
91
92        /* save this message for mailq printing */
93        if (!panic && CurEnv != NULL)
94        {
95                if (CurEnv->e_message != NULL)
96                        free(CurEnv->e_message);
97                CurEnv->e_message = newstr(MsgBuf + 4);
98        }
99
100        /* determine exit status if not already set */
101        if (ExitStat == EX_OK)
102        {
103                if (olderrno == 0)
104                        ExitStat = EX_SOFTWARE;
105                else
106                        ExitStat = EX_OSERR;
107                if (tTd(54, 1))
108                        printf("syserr: ExitStat = %d\n", ExitStat);
109        }
110
111        pw = sm_getpwuid(getuid());
112        if (pw != NULL)
113                uname = pw->pw_name;
114        else
115        {
116                uname = ubuf;
117                snprintf(ubuf, sizeof ubuf, "UID%d", getuid());
118        }
119
120        if (LogLevel > 0)
121                sm_syslog(panic ? LOG_ALERT : LOG_CRIT,
122                          CurEnv == NULL ? NOQID : CurEnv->e_id,
123                          "SYSERR(%s): %.900s",
124                          uname, &MsgBuf[4]);
125        switch (olderrno)
126        {
127          case EBADF:
128          case ENFILE:
129          case EMFILE:
130          case ENOTTY:
131#ifdef EFBIG
132          case EFBIG:
133#endif
134#ifdef ESPIPE
135          case ESPIPE:
136#endif
137#ifdef EPIPE
138          case EPIPE:
139#endif
140#ifdef ENOBUFS
141          case ENOBUFS:
142#endif
143#ifdef ESTALE
144          case ESTALE:
145#endif
146                printopenfds(TRUE);
147                mci_dump_all(TRUE);
148                break;
149        }
150        if (panic)
151        {
152#ifdef XLA
153                xla_all_end();
154#endif
155                if (tTd(0, 1))
156                        abort();
157                exit(EX_OSERR);
158        }
159        errno = 0;
160        if (QuickAbort)
161                longjmp(TopFrame, 2);
162}
163/*
164**  USRERR -- Signal user error.
165**
166**      This is much like syserr except it is for user errors.
167**
168**      Parameters:
169**              fmt -- the format string.  If it does not begin with
170**                      a three-digit SMTP reply code, 501 is assumed.
171**              (others) -- printf strings
172**
173**      Returns:
174**              none
175**              Through TopFrame if QuickAbort is set.
176**
177**      Side Effects:
178**              increments Errors.
179*/
180
181/*VARARGS1*/
182void
183#ifdef __STDC__
184usrerr(const char *fmt, ...)
185#else
186usrerr(fmt, va_alist)
187        const char *fmt;
188        va_dcl
189#endif
190{
191        VA_LOCAL_DECL
192
193        if (SuprErrs)
194                return;
195
196        VA_START(fmt);
197        fmtmsg(MsgBuf, CurEnv->e_to, "501", 0, fmt, ap);
198        VA_END;
199
200        /* save this message for mailq printing */
201        switch (MsgBuf[0])
202        {
203          case '4':
204          case '8':
205                if (CurEnv->e_message != NULL)
206                        break;
207
208                /* fall through.... */
209
210          case '5':
211          case '6':
212                if (CurEnv->e_message != NULL)
213                        free(CurEnv->e_message);
214                if (MsgBuf[0] == '6')
215                {
216                        char buf[MAXLINE];
217
218                        snprintf(buf, sizeof buf, "Postmaster warning: %.*s",
219                                sizeof buf - 22, MsgBuf + 4);
220                        CurEnv->e_message = newstr(buf);
221                }
222                else
223                {
224                        CurEnv->e_message = newstr(MsgBuf + 4);
225                }
226                break;
227        }
228
229        puterrmsg(MsgBuf);
230
231        if (LogLevel > 3 && LogUsrErrs)
232                sm_syslog(LOG_NOTICE, CurEnv->e_id,
233                        "%.900s",
234                        &MsgBuf[4]);
235
236        if (QuickAbort)
237                longjmp(TopFrame, 1);
238}
239/*
240**  MESSAGE -- print message (not necessarily an error)
241**
242**      Parameters:
243**              msg -- the message (printf fmt) -- it can begin with
244**                      an SMTP reply code.  If not, 050 is assumed.
245**              (others) -- printf arguments
246**
247**      Returns:
248**              none
249**
250**      Side Effects:
251**              none.
252*/
253
254/*VARARGS1*/
255void
256#ifdef __STDC__
257message(const char *msg, ...)
258#else
259message(msg, va_alist)
260        const char *msg;
261        va_dcl
262#endif
263{
264        VA_LOCAL_DECL
265
266        errno = 0;
267        VA_START(msg);
268        fmtmsg(MsgBuf, CurEnv->e_to, "050", 0, msg, ap);
269        VA_END;
270        putoutmsg(MsgBuf, FALSE, FALSE);
271
272        /* save this message for mailq printing */
273        switch (MsgBuf[0])
274        {
275          case '4':
276          case '8':
277                if (CurEnv->e_message != NULL)
278                        break;
279                /* fall through.... */
280
281          case '5':
282                if (CurEnv->e_message != NULL)
283                        free(CurEnv->e_message);
284                CurEnv->e_message = newstr(MsgBuf + 4);
285                break;
286        }
287}
288/*
289**  NMESSAGE -- print message (not necessarily an error)
290**
291**      Just like "message" except it never puts the to... tag on.
292**
293**      Parameters:
294**              msg -- the message (printf fmt) -- if it begins
295**                      with a three digit SMTP reply code, that is used,
296**                      otherwise 050 is assumed.
297**              (others) -- printf arguments
298**
299**      Returns:
300**              none
301**
302**      Side Effects:
303**              none.
304*/
305
306/*VARARGS1*/
307void
308#ifdef __STDC__
309nmessage(const char *msg, ...)
310#else
311nmessage(msg, va_alist)
312        const char *msg;
313        va_dcl
314#endif
315{
316        VA_LOCAL_DECL
317
318        errno = 0;
319        VA_START(msg);
320        fmtmsg(MsgBuf, (char *) NULL, "050", 0, msg, ap);
321        VA_END;
322        putoutmsg(MsgBuf, FALSE, FALSE);
323
324        /* save this message for mailq printing */
325        switch (MsgBuf[0])
326        {
327          case '4':
328          case '8':
329                if (CurEnv->e_message != NULL)
330                        break;
331                /* fall through.... */
332
333          case '5':
334                if (CurEnv->e_message != NULL)
335                        free(CurEnv->e_message);
336                CurEnv->e_message = newstr(MsgBuf + 4);
337                break;
338        }
339}
340/*
341**  PUTOUTMSG -- output error message to transcript and channel
342**
343**      Parameters:
344**              msg -- message to output (in SMTP format).
345**              holdmsg -- if TRUE, don't output a copy of the message to
346**                      our output channel.
347**              heldmsg -- if TRUE, this is a previously held message;
348**                      don't log it to the transcript file.
349**
350**      Returns:
351**              none.
352**
353**      Side Effects:
354**              Outputs msg to the transcript.
355**              If appropriate, outputs it to the channel.
356**              Deletes SMTP reply code number as appropriate.
357*/
358
359void
360putoutmsg(msg, holdmsg, heldmsg)
361        char *msg;
362        bool holdmsg;
363        bool heldmsg;
364{
365        char msgcode = msg[0];
366
367        /* display for debugging */
368        if (tTd(54, 8))
369                printf("--- %s%s%s\n", msg, holdmsg ? " (hold)" : "",
370                        heldmsg ? " (held)" : "");
371
372        /* map warnings to something SMTP can handle */
373        if (msgcode == '6')
374                msg[0] = '5';
375        else if (msgcode == '8')
376                msg[0] = '4';
377
378        /* output to transcript if serious */
379        if (!heldmsg && CurEnv != NULL && CurEnv->e_xfp != NULL &&
380            strchr("45", msg[0]) != NULL)
381                fprintf(CurEnv->e_xfp, "%s\n", msg);
382
383        if (LogLevel >= 15 && (OpMode == MD_SMTP || OpMode == MD_DAEMON))
384                sm_syslog(LOG_INFO, CurEnv->e_id,
385                        "--> %s%s",
386                        msg, holdmsg ? " (held)" : "");
387
388        if (msgcode == '8')
389                msg[0] = '0';
390
391        /* output to channel if appropriate */
392        if (!Verbose && msg[0] == '0')
393                return;
394        if (holdmsg)
395        {
396                /* save for possible future display */
397                msg[0] = msgcode;
398                snprintf(HeldMessageBuf, sizeof HeldMessageBuf, "%s", msg);
399                return;
400        }
401
402        (void) fflush(stdout);
403
404        if (OutChannel == NULL)
405                return;
406       
407        /* if DisConnected, OutChannel now points to the transcript */
408        if (!DisConnected &&
409            (OpMode == MD_SMTP || OpMode == MD_DAEMON || OpMode == MD_ARPAFTP))
410                fprintf(OutChannel, "%s\r\n", msg);
411        else
412                fprintf(OutChannel, "%s\n", &msg[4]);
413        if (TrafficLogFile != NULL)
414                fprintf(TrafficLogFile, "%05d >>> %s\n", (int) getpid(),
415                        (OpMode == MD_SMTP || OpMode == MD_DAEMON) ? msg : &msg[4]);
416        if (msg[3] == ' ')
417                (void) fflush(OutChannel);
418        if (!ferror(OutChannel) || DisConnected)
419                return;
420
421        /*
422        **  Error on output -- if reporting lost channel, just ignore it.
423        **  Also, ignore errors from QUIT response (221 message) -- some
424        **      rude servers don't read result.
425        */
426
427        if (InChannel == NULL || feof(InChannel) || ferror(InChannel) ||
428            strncmp(msg, "221", 3) == 0)
429                return;
430
431        /* can't call syserr, 'cause we are using MsgBuf */
432        HoldErrs = TRUE;
433        if (LogLevel > 0)
434                sm_syslog(LOG_CRIT, CurEnv->e_id,
435                        "SYSERR: putoutmsg (%s): error on output channel sending \"%s\": %s",
436                        CurHostName == NULL ? "NO-HOST" : CurHostName,
437                        shortenstring(msg, MAXSHORTSTR), errstring(errno));
438}
439/*
440**  PUTERRMSG -- like putoutmsg, but does special processing for error messages
441**
442**      Parameters:
443**              msg -- the message to output.
444**
445**      Returns:
446**              none.
447**
448**      Side Effects:
449**              Sets the fatal error bit in the envelope as appropriate.
450*/
451
452void
453puterrmsg(msg)
454        char *msg;
455{
456        char msgcode = msg[0];
457
458        /* output the message as usual */
459        putoutmsg(msg, HoldErrs, FALSE);
460
461        /* be careful about multiple error messages */
462        if (OnlyOneError)
463                HoldErrs = TRUE;
464
465        /* signal the error */
466        Errors++;
467
468        if (CurEnv == NULL)
469                return;
470       
471        if (msgcode == '6')
472        {
473                /* notify the postmaster */
474                CurEnv->e_flags |= EF_PM_NOTIFY;
475        }
476        else if (msgcode == '5' && bitset(EF_GLOBALERRS, CurEnv->e_flags))
477        {
478                /* mark long-term fatal errors */
479                CurEnv->e_flags |= EF_FATALERRS;
480        }
481}
482/*
483**  FMTMSG -- format a message into buffer.
484**
485**      Parameters:
486**              eb -- error buffer to get result.
487**              to -- the recipient tag for this message.
488**              num -- arpanet error number.
489**              en -- the error number to display.
490**              fmt -- format of string.
491**              a, b, c, d, e -- arguments.
492**
493**      Returns:
494**              none.
495**
496**      Side Effects:
497**              none.
498*/
499
500static void
501fmtmsg(eb, to, num, eno, fmt, ap)
502        register char *eb;
503        const char *to;
504        const char *num;
505        int eno;
506        const char *fmt;
507        va_list ap;
508{
509        char del;
510        int l;
511        int spaceleft = sizeof MsgBuf;
512
513        /* output the reply code */
514        if (isascii(fmt[0]) && isdigit(fmt[0]) &&
515            isascii(fmt[1]) && isdigit(fmt[1]) &&
516            isascii(fmt[2]) && isdigit(fmt[2]))
517        {
518                num = fmt;
519                fmt += 4;
520        }
521        if (num[3] == '-')
522                del = '-';
523        else
524                del = ' ';
525        (void) snprintf(eb, spaceleft, "%3.3s%c", num, del);
526        eb += 4;
527        spaceleft -= 4;
528
529        /* output the file name and line number */
530        if (FileName != NULL)
531        {
532                (void) snprintf(eb, spaceleft, "%s: line %d: ",
533                        shortenstring(FileName, 83), LineNumber);
534                eb += (l = strlen(eb));
535                spaceleft -= l;
536        }
537
538        /* output the "to" person */
539        if (to != NULL && to[0] != '\0' &&
540            strncmp(num, "551", 3) != 0 &&
541            strncmp(num, "251", 3) != 0)
542        {
543                (void) snprintf(eb, spaceleft, "%s... ",
544                        shortenstring(to, MAXSHORTSTR));
545                spaceleft -= strlen(eb);
546                while (*eb != '\0')
547                        *eb++ &= 0177;
548        }
549
550        /* output the message */
551        (void) vsnprintf(eb, spaceleft, fmt, ap);
552        spaceleft -= strlen(eb);
553        while (*eb != '\0')
554                *eb++ &= 0177;
555
556        /* output the error code, if any */
557        if (eno != 0)
558                (void) snprintf(eb, spaceleft, ": %s", errstring(eno));
559}
560/*
561**  BUFFER_ERRORS -- arrange to buffer future error messages
562**
563**      Parameters:
564**              none
565**
566**      Returns:
567**              none.
568*/
569
570void
571buffer_errors()
572{
573        HeldMessageBuf[0] = '\0';
574        HoldErrs = TRUE;
575}
576/*
577**  FLUSH_ERRORS -- flush the held error message buffer
578**
579**      Parameters:
580**              print -- if set, print the message, otherwise just
581**                      delete it.
582**
583**      Returns:
584**              none.
585*/
586
587void
588flush_errors(print)
589        bool print;
590{
591        if (print && HeldMessageBuf[0] != '\0')
592                putoutmsg(HeldMessageBuf, FALSE, TRUE);
593        HeldMessageBuf[0] = '\0';
594        HoldErrs = FALSE;
595}
596/*
597**  ERRSTRING -- return string description of error code
598**
599**      Parameters:
600**              errnum -- the error number to translate
601**
602**      Returns:
603**              A string description of errnum.
604**
605**      Side Effects:
606**              none.
607*/
608
609const char *
610errstring(errnum)
611        int errnum;
612{
613        char *dnsmsg;
614        char *bp;
615        static char buf[MAXLINE];
616# if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
617        extern char *sys_errlist[];
618        extern int sys_nerr;
619# endif
620# if SMTP
621        extern char *SmtpPhase;
622# endif /* SMTP */
623
624        /*
625        **  Handle special network error codes.
626        **
627        **      These are 4.2/4.3bsd specific; they should be in daemon.c.
628        */
629
630        dnsmsg = NULL;
631        switch (errnum)
632        {
633# if defined(DAEMON) && defined(ETIMEDOUT)
634          case ETIMEDOUT:
635          case ECONNRESET:
636                bp = buf;
637#if HASSTRERROR
638                snprintf(bp, SPACELEFT(buf, bp), "%s", strerror(errnum));
639#else
640                if (errnum >= 0 && errnum < sys_nerr)
641                        snprintf(bp, SPACELEFT(buf, bp), "%s", sys_errlist[errnum]);
642                else
643                        snprintf(bp, SPACELEFT(buf, bp), "Error %d", errnum);
644#endif
645                bp += strlen(bp);
646                if (CurHostName != NULL)
647                {
648                        if (errnum == ETIMEDOUT)
649                        {
650                                snprintf(bp, SPACELEFT(buf, bp), " with ");
651                                bp += strlen(bp);
652                        }
653                        else
654                        {
655                                bp = buf;
656                                snprintf(bp, SPACELEFT(buf, bp),
657                                        "Connection reset by ");
658                                bp += strlen(bp);
659                        }
660                        snprintf(bp, SPACELEFT(buf, bp), "%s",
661                                shortenstring(CurHostName, MAXSHORTSTR));
662                        bp += strlen(buf);
663                }
664                if (SmtpPhase != NULL)
665                {
666                        snprintf(bp, SPACELEFT(buf, bp), " during %s",
667                                SmtpPhase);
668                }
669                return (buf);
670
671          case EHOSTDOWN:
672                if (CurHostName == NULL)
673                        break;
674                (void) snprintf(buf, sizeof buf, "Host %s is down",
675                        shortenstring(CurHostName, MAXSHORTSTR));
676                return (buf);
677
678          case ECONNREFUSED:
679                if (CurHostName == NULL)
680                        break;
681                (void) snprintf(buf, sizeof buf, "Connection refused by %s",
682                        shortenstring(CurHostName, MAXSHORTSTR));
683                return (buf);
684# endif
685
686# if NAMED_BIND
687          case HOST_NOT_FOUND + E_DNSBASE:
688                dnsmsg = "host not found";
689                break;
690
691          case TRY_AGAIN + E_DNSBASE:
692                dnsmsg = "host name lookup failure";
693                break;
694
695          case NO_RECOVERY + E_DNSBASE:
696                dnsmsg = "non-recoverable error";
697                break;
698
699          case NO_DATA + E_DNSBASE:
700                dnsmsg = "no data known";
701                break;
702# endif
703
704          case EPERM:
705                /* SunOS gives "Not owner" -- this is the POSIX message */
706                return "Operation not permitted";
707
708        /*
709        **  Error messages used internally in sendmail.
710        */
711
712          case E_SM_OPENTIMEOUT:
713                return "Timeout on file open";
714
715          case E_SM_NOSLINK:
716                return "Symbolic links not allowed";
717
718          case E_SM_NOHLINK:
719                return "Hard links not allowed";
720
721          case E_SM_REGONLY:
722                return "Regular files only";
723
724          case E_SM_ISEXEC:
725                return "Executable files not allowed";
726
727          case E_SM_WWDIR:
728                return "World writable directory";
729
730          case E_SM_GWDIR:
731                return "Group writable directory";
732
733          case E_SM_FILECHANGE:
734                return "File changed after open";
735
736          case E_SM_WWFILE:
737                return "World writable file";
738
739          case E_SM_GWFILE:
740                return "Group writable file";
741        }
742
743        if (dnsmsg != NULL)
744        {
745                bp = buf;
746                strcpy(bp, "Name server: ");
747                bp += strlen(bp);
748                if (CurHostName != NULL)
749                {
750                        snprintf(bp, SPACELEFT(buf, bp), "%s: ",
751                                shortenstring(CurHostName, MAXSHORTSTR));
752                        bp += strlen(bp);
753                }
754                snprintf(bp, SPACELEFT(buf, bp), "%s", dnsmsg);
755                return buf;
756        }
757
758#if HASSTRERROR
759        return strerror(errnum);
760#else
761        if (errnum > 0 && errnum < sys_nerr)
762                return (sys_errlist[errnum]);
763
764        (void) snprintf(buf, sizeof buf, "Error %d", errnum);
765        return (buf);
766#endif
767}
Note: See TracBrowser for help on using the repository browser.