source: trunk/third/sendmail/sendmail/util.c @ 22421

Revision 22421, 51.3 KB checked in by zacheiss, 18 years ago (diff)
Apply patches from 3-22-06 CERT advisory.
Line 
1/*
2 * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
3 *      All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *      The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#include <sendmail.h>
15
16SM_RCSID("@(#)$Id: util.c,v 1.2 2006-03-23 21:02:47 zacheiss Exp $")
17
18#include <sysexits.h>
19#include <sm/xtrap.h>
20
21/*
22**  ADDQUOTES -- Adds quotes & quote bits to a string.
23**
24**      Runs through a string and adds backslashes and quote bits.
25**
26**      Parameters:
27**              s -- the string to modify.
28**              rpool -- resource pool from which to allocate result
29**
30**      Returns:
31**              pointer to quoted string.
32*/
33
34char *
35addquotes(s, rpool)
36        char *s;
37        SM_RPOOL_T *rpool;
38{
39        int len = 0;
40        char c;
41        char *p = s, *q, *r;
42
43        if (s == NULL)
44                return NULL;
45
46        /* Find length of quoted string */
47        while ((c = *p++) != '\0')
48        {
49                len++;
50                if (c == '\\' || c == '"')
51                        len++;
52        }
53
54        q = r = sm_rpool_malloc_x(rpool, len + 3);
55        p = s;
56
57        /* add leading quote */
58        *q++ = '"';
59        while ((c = *p++) != '\0')
60        {
61                /* quote \ or " */
62                if (c == '\\' || c == '"')
63                        *q++ = '\\';
64                *q++ = c;
65        }
66        *q++ = '"';
67        *q = '\0';
68        return r;
69}
70
71#if _FFR_STRIPBACKSL
72/*
73**  STRIPBACKSLASH -- Strip leading backslash from a string.
74**
75**      This is done in place.
76**
77**      Parameters:
78**              s -- the string to strip.
79**
80**      Returns:
81**              none.
82*/
83
84void
85stripbackslash(s)
86        char *s;
87{
88        char *p, *q, c;
89
90        if (s == NULL || *s == '\0')
91                return;
92        p = q = s;
93        while (*p == '\\' && (p[1] == '\\' || (isascii(p[1]) && isalnum(p[1]))))
94                p++;
95        do
96        {
97                c = *q++ = *p++;
98        } while (c != '\0');
99}
100#endif /* _FFR_STRIPBACKSL */
101
102/*
103**  RFC822_STRING -- Checks string for proper RFC822 string quoting.
104**
105**      Runs through a string and verifies RFC822 special characters
106**      are only found inside comments, quoted strings, or backslash
107**      escaped.  Also verified balanced quotes and parenthesis.
108**
109**      Parameters:
110**              s -- the string to modify.
111**
112**      Returns:
113**              true iff the string is RFC822 compliant, false otherwise.
114*/
115
116bool
117rfc822_string(s)
118        char *s;
119{
120        bool quoted = false;
121        int commentlev = 0;
122        char *c = s;
123
124        if (s == NULL)
125                return false;
126
127        while (*c != '\0')
128        {
129                /* escaped character */
130                if (*c == '\\')
131                {
132                        c++;
133                        if (*c == '\0')
134                                return false;
135                }
136                else if (commentlev == 0 && *c == '"')
137                        quoted = !quoted;
138                else if (!quoted)
139                {
140                        if (*c == ')')
141                        {
142                                /* unbalanced ')' */
143                                if (commentlev == 0)
144                                        return false;
145                                else
146                                        commentlev--;
147                        }
148                        else if (*c == '(')
149                                commentlev++;
150                        else if (commentlev == 0 &&
151                                 strchr(MustQuoteChars, *c) != NULL)
152                                return false;
153                }
154                c++;
155        }
156
157        /* unbalanced '"' or '(' */
158        return !quoted && commentlev == 0;
159}
160/*
161**  SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
162**
163**      Arbitrarily shorten (in place) an RFC822 string and rebalance
164**      comments and quotes.
165**
166**      Parameters:
167**              string -- the string to shorten
168**              length -- the maximum size, 0 if no maximum
169**
170**      Returns:
171**              true if string is changed, false otherwise
172**
173**      Side Effects:
174**              Changes string in place, possibly resulting
175**              in a shorter string.
176*/
177
178bool
179shorten_rfc822_string(string, length)
180        char *string;
181        size_t length;
182{
183        bool backslash = false;
184        bool modified = false;
185        bool quoted = false;
186        size_t slen;
187        int parencount = 0;
188        char *ptr = string;
189
190        /*
191        **  If have to rebalance an already short enough string,
192        **  need to do it within allocated space.
193        */
194
195        slen = strlen(string);
196        if (length == 0 || slen < length)
197                length = slen;
198
199        while (*ptr != '\0')
200        {
201                if (backslash)
202                {
203                        backslash = false;
204                        goto increment;
205                }
206
207                if (*ptr == '\\')
208                        backslash = true;
209                else if (*ptr == '(')
210                {
211                        if (!quoted)
212                                parencount++;
213                }
214                else if (*ptr == ')')
215                {
216                        if (--parencount < 0)
217                                parencount = 0;
218                }
219
220                /* Inside a comment, quotes don't matter */
221                if (parencount <= 0 && *ptr == '"')
222                        quoted = !quoted;
223
224increment:
225                /* Check for sufficient space for next character */
226                if (length - (ptr - string) <= (size_t) ((backslash ? 1 : 0) +
227                                                parencount +
228                                                (quoted ? 1 : 0)))
229                {
230                        /* Not enough, backtrack */
231                        if (*ptr == '\\')
232                                backslash = false;
233                        else if (*ptr == '(' && !quoted)
234                                parencount--;
235                        else if (*ptr == '"' && parencount == 0)
236                                quoted = false;
237                        break;
238                }
239                ptr++;
240        }
241
242        /* Rebalance */
243        while (parencount-- > 0)
244        {
245                if (*ptr != ')')
246                {
247                        modified = true;
248                        *ptr = ')';
249                }
250                ptr++;
251        }
252        if (quoted)
253        {
254                if (*ptr != '"')
255                {
256                        modified = true;
257                        *ptr = '"';
258                }
259                ptr++;
260        }
261        if (*ptr != '\0')
262        {
263                modified = true;
264                *ptr = '\0';
265        }
266        return modified;
267}
268/*
269**  FIND_CHARACTER -- find an unquoted character in an RFC822 string
270**
271**      Find an unquoted, non-commented character in an RFC822
272**      string and return a pointer to its location in the
273**      string.
274**
275**      Parameters:
276**              string -- the string to search
277**              character -- the character to find
278**
279**      Returns:
280**              pointer to the character, or
281**              a pointer to the end of the line if character is not found
282*/
283
284char *
285find_character(string, character)
286        char *string;
287        int character;
288{
289        bool backslash = false;
290        bool quoted = false;
291        int parencount = 0;
292
293        while (string != NULL && *string != '\0')
294        {
295                if (backslash)
296                {
297                        backslash = false;
298                        if (!quoted && character == '\\' && *string == '\\')
299                                break;
300                        string++;
301                        continue;
302                }
303                switch (*string)
304                {
305                  case '\\':
306                        backslash = true;
307                        break;
308
309                  case '(':
310                        if (!quoted)
311                                parencount++;
312                        break;
313
314                  case ')':
315                        if (--parencount < 0)
316                                parencount = 0;
317                        break;
318                }
319
320                /* Inside a comment, nothing matters */
321                if (parencount > 0)
322                {
323                        string++;
324                        continue;
325                }
326
327                if (*string == '"')
328                        quoted = !quoted;
329                else if (*string == character && !quoted)
330                        break;
331                string++;
332        }
333
334        /* Return pointer to the character */
335        return string;
336}
337
338/*
339**  CHECK_BODYTYPE -- check bodytype parameter
340**
341**      Parameters:
342**              bodytype -- bodytype parameter
343**
344**      Returns:
345**              BODYTYPE_* according to parameter
346**
347*/
348
349int
350check_bodytype(bodytype)
351        char *bodytype;
352{
353        /* check body type for legality */
354        if (bodytype == NULL)
355                return BODYTYPE_NONE;
356        if (sm_strcasecmp(bodytype, "7BIT") == 0)
357                return BODYTYPE_7BIT;
358        if (sm_strcasecmp(bodytype, "8BITMIME") == 0)
359                return BODYTYPE_8BITMIME;
360        return BODYTYPE_ILLEGAL;
361}
362
363#if _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI
364/*
365**  TRUNCATE_AT_DELIM -- truncate string at a delimiter and append "..."
366**
367**      Parameters:
368**              str -- string to truncate
369**              len -- maximum length (including '\0') (0 for unlimited)
370**              delim -- delimiter character
371**
372**      Returns:
373**              None.
374*/
375
376void
377truncate_at_delim(str, len, delim)
378        char *str;
379        size_t len;
380        int delim;
381{
382        char *p;
383
384        if (str == NULL || len == 0 || strlen(str) < len)
385                return;
386
387        *(str + len - 1) = '\0';
388        while ((p = strrchr(str, delim)) != NULL)
389        {
390                *p = '\0';
391                if (p - str + 4 < len)
392                {
393                        *p++ = ':';
394                        *p = '\0';
395                        (void) sm_strlcat(str, "...", len);
396                        return;
397                }
398        }
399
400        /* Couldn't find a place to append "..." */
401        if (len > 3)
402                (void) sm_strlcpy(str, "...", len);
403        else
404                str[0] = '\0';
405}
406#endif /* _FFR_BESTMX_BETTER_TRUNCATION || _FFR_DNSMAP_MULTI */
407/*
408**  XALLOC -- Allocate memory, raise an exception on error
409**
410**      Parameters:
411**              sz -- size of area to allocate.
412**
413**      Returns:
414**              pointer to data region.
415**
416**      Exceptions:
417**              SmHeapOutOfMemory (F:sm.heap) -- cannot allocate memory
418**
419**      Side Effects:
420**              Memory is allocated.
421*/
422
423char *
424#if SM_HEAP_CHECK
425xalloc_tagged(sz, file, line)
426        register int sz;
427        char *file;
428        int line;
429#else /* SM_HEAP_CHECK */
430xalloc(sz)
431        register int sz;
432#endif /* SM_HEAP_CHECK */
433{
434        register char *p;
435
436        /* some systems can't handle size zero mallocs */
437        if (sz <= 0)
438                sz = 1;
439
440        /* scaffolding for testing error handling code */
441        sm_xtrap_raise_x(&SmHeapOutOfMemory);
442
443        p = sm_malloc_tagged((unsigned) sz, file, line, sm_heap_group());
444        if (p == NULL)
445        {
446                sm_exc_raise_x(&SmHeapOutOfMemory);
447        }
448        return p;
449}
450/*
451**  COPYPLIST -- copy list of pointers.
452**
453**      This routine is the equivalent of strdup for lists of
454**      pointers.
455**
456**      Parameters:
457**              list -- list of pointers to copy.
458**                      Must be NULL terminated.
459**              copycont -- if true, copy the contents of the vector
460**                      (which must be a string) also.
461**              rpool -- resource pool from which to allocate storage,
462**                      or NULL
463**
464**      Returns:
465**              a copy of 'list'.
466*/
467
468char **
469copyplist(list, copycont, rpool)
470        char **list;
471        bool copycont;
472        SM_RPOOL_T *rpool;
473{
474        register char **vp;
475        register char **newvp;
476
477        for (vp = list; *vp != NULL; vp++)
478                continue;
479
480        vp++;
481
482        newvp = (char **) sm_rpool_malloc_x(rpool, (vp - list) * sizeof *vp);
483        memmove((char *) newvp, (char *) list, (int) (vp - list) * sizeof *vp);
484
485        if (copycont)
486        {
487                for (vp = newvp; *vp != NULL; vp++)
488                        *vp = sm_rpool_strdup_x(rpool, *vp);
489        }
490
491        return newvp;
492}
493/*
494**  COPYQUEUE -- copy address queue.
495**
496**      This routine is the equivalent of strdup for address queues;
497**      addresses marked as QS_IS_DEAD() aren't copied
498**
499**      Parameters:
500**              addr -- list of address structures to copy.
501**              rpool -- resource pool from which to allocate storage
502**
503**      Returns:
504**              a copy of 'addr'.
505*/
506
507ADDRESS *
508copyqueue(addr, rpool)
509        ADDRESS *addr;
510        SM_RPOOL_T *rpool;
511{
512        register ADDRESS *newaddr;
513        ADDRESS *ret;
514        register ADDRESS **tail = &ret;
515
516        while (addr != NULL)
517        {
518                if (!QS_IS_DEAD(addr->q_state))
519                {
520                        newaddr = (ADDRESS *) sm_rpool_malloc_x(rpool,
521                                                        sizeof *newaddr);
522                        STRUCTCOPY(*addr, *newaddr);
523                        *tail = newaddr;
524                        tail = &newaddr->q_next;
525                }
526                addr = addr->q_next;
527        }
528        *tail = NULL;
529
530        return ret;
531}
532/*
533**  LOG_SENDMAIL_PID -- record sendmail pid and command line.
534**
535**      Parameters:
536**              e -- the current envelope.
537**
538**      Returns:
539**              none.
540**
541**      Side Effects:
542**              writes pidfile, logs command line.
543*/
544
545void
546log_sendmail_pid(e)
547        ENVELOPE *e;
548{
549        long sff;
550        SM_FILE_T *pidf;
551        char pidpath[MAXPATHLEN];
552        extern char *CommandLineArgs;
553
554        /* write the pid to the log file for posterity */
555        sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
556        if (TrustedUid != 0 && RealUid == TrustedUid)
557                sff |= SFF_OPENASROOT;
558        expand(PidFile, pidpath, sizeof pidpath, e);
559        pidf = safefopen(pidpath, O_WRONLY|O_TRUNC, FileMode, sff);
560        if (pidf == NULL)
561        {
562                sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
563                          pidpath, sm_errstring(errno));
564        }
565        else
566        {
567                pid_t pid;
568
569                pid = getpid();
570
571                /* write the process id on line 1 */
572                (void) sm_io_fprintf(pidf, SM_TIME_DEFAULT, "%ld\n",
573                                     (long) pid);
574
575                /* line 2 contains all command line flags */
576                (void) sm_io_fprintf(pidf, SM_TIME_DEFAULT, "%s\n",
577                                     CommandLineArgs);
578
579                /* flush and close */
580                (void) sm_io_close(pidf, SM_TIME_DEFAULT);
581        }
582        if (LogLevel > 9)
583                sm_syslog(LOG_INFO, NOQID, "started as: %s", CommandLineArgs);
584}
585/*
586**  SET_DELIVERY_MODE -- set and record the delivery mode
587**
588**      Parameters:
589**              mode -- delivery mode
590**              e -- the current envelope.
591**
592**      Returns:
593**              none.
594**
595**      Side Effects:
596**              sets {deliveryMode} macro
597*/
598
599void
600set_delivery_mode(mode, e)
601        int mode;
602        ENVELOPE *e;
603{
604        char buf[2];
605
606        e->e_sendmode = (char) mode;
607        buf[0] = (char) mode;
608        buf[1] = '\0';
609        macdefine(&e->e_macro, A_TEMP, macid("{deliveryMode}"), buf);
610}
611/*
612**  SET_OP_MODE -- set and record the op mode
613**
614**      Parameters:
615**              mode -- op mode
616**              e -- the current envelope.
617**
618**      Returns:
619**              none.
620**
621**      Side Effects:
622**              sets {opMode} macro
623*/
624
625void
626set_op_mode(mode)
627        int mode;
628{
629        char buf[2];
630        extern ENVELOPE BlankEnvelope;
631
632        OpMode = (char) mode;
633        buf[0] = (char) mode;
634        buf[1] = '\0';
635        macdefine(&BlankEnvelope.e_macro, A_TEMP, MID_OPMODE, buf);
636}
637/*
638**  PRINTAV -- print argument vector.
639**
640**      Parameters:
641**              av -- argument vector.
642**
643**      Returns:
644**              none.
645**
646**      Side Effects:
647**              prints av.
648*/
649
650void
651printav(av)
652        register char **av;
653{
654        while (*av != NULL)
655        {
656                if (tTd(0, 44))
657                        sm_dprintf("\n\t%08lx=", (unsigned long) *av);
658                else
659                        (void) sm_io_putc(smioout, SM_TIME_DEFAULT, ' ');
660                xputs(*av++);
661        }
662        (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '\n');
663}
664/*
665**  XPUTS -- put string doing control escapes.
666**
667**      Parameters:
668**              s -- string to put.
669**
670**      Returns:
671**              none.
672**
673**      Side Effects:
674**              output to stdout
675*/
676
677void
678xputs(s)
679        register const char *s;
680{
681        register int c;
682        register struct metamac *mp;
683        bool shiftout = false;
684        extern struct metamac MetaMacros[];
685        static SM_DEBUG_T DebugANSI = SM_DEBUG_INITIALIZER("ANSI",
686                "@(#)$Debug: ANSI - enable reverse video in debug output $");
687
688        /*
689        **  TermEscape is set here, rather than in main(),
690        **  because ANSI mode can be turned on or off at any time
691        **  if we are in -bt rule testing mode.
692        */
693
694        if (sm_debug_unknown(&DebugANSI))
695        {
696                if (sm_debug_active(&DebugANSI, 1))
697                {
698                        TermEscape.te_rv_on = "\033[7m";
699                        TermEscape.te_rv_off = "\033[0m";
700                }
701                else
702                {
703                        TermEscape.te_rv_on = "";
704                        TermEscape.te_rv_off = "";
705                }
706        }
707
708        if (s == NULL)
709        {
710                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s<null>%s",
711                                     TermEscape.te_rv_on, TermEscape.te_rv_off);
712                return;
713        }
714        while ((c = (*s++ & 0377)) != '\0')
715        {
716                if (shiftout)
717                {
718                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s",
719                                             TermEscape.te_rv_off);
720                        shiftout = false;
721                }
722                if (!isascii(c))
723                {
724                        if (c == MATCHREPL)
725                        {
726                                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
727                                                     "%s$",
728                                                     TermEscape.te_rv_on);
729                                shiftout = true;
730                                if (*s == '\0')
731                                        continue;
732                                c = *s++ & 0377;
733                                goto printchar;
734                        }
735                        if (c == MACROEXPAND || c == MACRODEXPAND)
736                        {
737                                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
738                                                     "%s$",
739                                                     TermEscape.te_rv_on);
740                                if (c == MACRODEXPAND)
741                                        (void) sm_io_putc(smioout,
742                                                          SM_TIME_DEFAULT, '&');
743                                shiftout = true;
744                                if (*s == '\0')
745                                        continue;
746                                if (strchr("=~&?", *s) != NULL)
747                                        (void) sm_io_putc(smioout,
748                                                          SM_TIME_DEFAULT,
749                                                          *s++);
750                                if (bitset(0200, *s))
751                                        (void) sm_io_fprintf(smioout,
752                                                             SM_TIME_DEFAULT,
753                                                             "{%s}",
754                                                             macname(bitidx(*s++)));
755                                else
756                                        (void) sm_io_fprintf(smioout,
757                                                             SM_TIME_DEFAULT,
758                                                             "%c",
759                                                             *s++);
760                                continue;
761                        }
762                        for (mp = MetaMacros; mp->metaname != '\0'; mp++)
763                        {
764                                if (bitidx(mp->metaval) == c)
765                                {
766                                        (void) sm_io_fprintf(smioout,
767                                                             SM_TIME_DEFAULT,
768                                                             "%s$%c",
769                                                             TermEscape.te_rv_on,
770                                                             mp->metaname);
771                                        shiftout = true;
772                                        break;
773                                }
774                        }
775                        if (c == MATCHCLASS || c == MATCHNCLASS)
776                        {
777                                if (bitset(0200, *s))
778                                        (void) sm_io_fprintf(smioout,
779                                                             SM_TIME_DEFAULT,
780                                                             "{%s}",
781                                                             macname(bitidx(*s++)));
782                                else if (*s != '\0')
783                                        (void) sm_io_fprintf(smioout,
784                                                             SM_TIME_DEFAULT,
785                                                             "%c",
786                                                             *s++);
787                        }
788                        if (mp->metaname != '\0')
789                                continue;
790
791                        /* unrecognized meta character */
792                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%sM-",
793                                             TermEscape.te_rv_on);
794                        shiftout = true;
795                        c &= 0177;
796                }
797  printchar:
798                if (isprint(c))
799                {
800                        (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
801                        continue;
802                }
803
804                /* wasn't a meta-macro -- find another way to print it */
805                switch (c)
806                {
807                  case '\n':
808                        c = 'n';
809                        break;
810
811                  case '\r':
812                        c = 'r';
813                        break;
814
815                  case '\t':
816                        c = 't';
817                        break;
818                }
819                if (!shiftout)
820                {
821                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s",
822                                             TermEscape.te_rv_on);
823                        shiftout = true;
824                }
825                if (isprint(c))
826                {
827                        (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '\\');
828                        (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
829                }
830                else
831                {
832                        (void) sm_io_putc(smioout, SM_TIME_DEFAULT, '^');
833                        (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c ^ 0100);
834                }
835        }
836        if (shiftout)
837                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s",
838                                     TermEscape.te_rv_off);
839        (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
840}
841/*
842**  MAKELOWER -- Translate a line into lower case
843**
844**      Parameters:
845**              p -- the string to translate.  If NULL, return is
846**                      immediate.
847**
848**      Returns:
849**              none.
850**
851**      Side Effects:
852**              String pointed to by p is translated to lower case.
853*/
854
855void
856makelower(p)
857        register char *p;
858{
859        register char c;
860
861        if (p == NULL)
862                return;
863        for (; (c = *p) != '\0'; p++)
864                if (isascii(c) && isupper(c))
865                        *p = tolower(c);
866}
867/*
868**  FIXCRLF -- fix <CR><LF> in line.
869**
870**      Looks for the <CR><LF> combination and turns it into the
871**      UNIX canonical <NL> character.  It only takes one line,
872**      i.e., it is assumed that the first <NL> found is the end
873**      of the line.
874**
875**      Parameters:
876**              line -- the line to fix.
877**              stripnl -- if true, strip the newline also.
878**
879**      Returns:
880**              none.
881**
882**      Side Effects:
883**              line is changed in place.
884*/
885
886void
887fixcrlf(line, stripnl)
888        char *line;
889        bool stripnl;
890{
891        register char *p;
892
893        p = strchr(line, '\n');
894        if (p == NULL)
895                return;
896        if (p > line && p[-1] == '\r')
897                p--;
898        if (!stripnl)
899                *p++ = '\n';
900        *p = '\0';
901}
902/*
903**  PUTLINE -- put a line like fputs obeying SMTP conventions
904**
905**      This routine always guarantees outputing a newline (or CRLF,
906**      as appropriate) at the end of the string.
907**
908**      Parameters:
909**              l -- line to put.
910**              mci -- the mailer connection information.
911**
912**      Returns:
913**              true iff line was written successfully
914**
915**      Side Effects:
916**              output of l to mci->mci_out.
917*/
918
919bool
920putline(l, mci)
921        register char *l;
922        register MCI *mci;
923{
924        return putxline(l, strlen(l), mci, PXLF_MAPFROM);
925}
926/*
927**  PUTXLINE -- putline with flags bits.
928**
929**      This routine always guarantees outputing a newline (or CRLF,
930**      as appropriate) at the end of the string.
931**
932**      Parameters:
933**              l -- line to put.
934**              len -- the length of the line.
935**              mci -- the mailer connection information.
936**              pxflags -- flag bits:
937**                  PXLF_MAPFROM -- map From_ to >From_.
938**                  PXLF_STRIP8BIT -- strip 8th bit.
939**                  PXLF_HEADER -- map bare newline in header to newline space.
940**                  PXLF_NOADDEOL -- don't add an EOL if one wasn't present.
941**
942**      Returns:
943**              true iff line was written successfully
944**
945**      Side Effects:
946**              output of l to mci->mci_out.
947*/
948
949bool
950putxline(l, len, mci, pxflags)
951        register char *l;
952        size_t len;
953        register MCI *mci;
954        int pxflags;
955{
956        bool dead = false;
957        register char *p, *end;
958        int slop = 0;
959
960        /* strip out 0200 bits -- these can look like TELNET protocol */
961        if (bitset(MCIF_7BIT, mci->mci_flags) ||
962            bitset(PXLF_STRIP8BIT, pxflags))
963        {
964                register char svchar;
965
966                for (p = l; (svchar = *p) != '\0'; ++p)
967                        if (bitset(0200, svchar))
968                                *p = svchar &~ 0200;
969        }
970
971        end = l + len;
972        do
973        {
974                bool noeol = false;
975
976                /* find the end of the line */
977                p = memchr(l, '\n', end - l);
978                if (p == NULL)
979                {
980                        p = end;
981                        noeol = true;
982                }
983
984                if (TrafficLogFile != NULL)
985                        (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
986                                             "%05d >>> ", (int) CurrentPid);
987
988                /* check for line overflow */
989                while (mci->mci_mailer->m_linelimit > 0 &&
990                       (p - l + slop) > mci->mci_mailer->m_linelimit)
991                {
992                        char *l_base = l;
993                        register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
994
995                        if (l[0] == '.' && slop == 0 &&
996                            bitnset(M_XDOT, mci->mci_mailer->m_flags))
997                        {
998                                if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
999                                               '.') == SM_IO_EOF)
1000                                        dead = true;
1001                                if (TrafficLogFile != NULL)
1002                                        (void) sm_io_putc(TrafficLogFile,
1003                                                          SM_TIME_DEFAULT, '.');
1004                        }
1005                        else if (l[0] == 'F' && slop == 0 &&
1006                                 bitset(PXLF_MAPFROM, pxflags) &&
1007                                 strncmp(l, "From ", 5) == 0 &&
1008                                 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1009                        {
1010                                if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1011                                               '>') == SM_IO_EOF)
1012                                        dead = true;
1013                                if (TrafficLogFile != NULL)
1014                                        (void) sm_io_putc(TrafficLogFile,
1015                                                          SM_TIME_DEFAULT,
1016                                                          '>');
1017                        }
1018                        if (dead)
1019                                break;
1020
1021                        while (l < q)
1022                        {
1023                                if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1024                                       (unsigned char) *l++) == SM_IO_EOF)
1025                                {
1026                                        dead = true;
1027                                        break;
1028                                }
1029                        }
1030                        if (dead)
1031                                break;
1032
1033                        if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '!') ==
1034                            SM_IO_EOF ||
1035                            sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1036                                        mci->mci_mailer->m_eol) ==
1037                            SM_IO_EOF ||
1038                            sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, ' ') ==
1039                            SM_IO_EOF)
1040                        {
1041                                dead = true;
1042                                break;
1043                        }
1044                        if (TrafficLogFile != NULL)
1045                        {
1046                                for (l = l_base; l < q; l++)
1047                                        (void) sm_io_putc(TrafficLogFile,
1048                                                          SM_TIME_DEFAULT,
1049                                                          (unsigned char)*l);
1050                                (void) sm_io_fprintf(TrafficLogFile,
1051                                                     SM_TIME_DEFAULT,
1052                                                     "!\n%05d >>>  ",
1053                                                     (int) CurrentPid);
1054                        }
1055                        slop = 1;
1056                }
1057
1058                if (dead)
1059                        break;
1060
1061                /* output last part */
1062                if (l[0] == '.' && slop == 0 &&
1063                    bitnset(M_XDOT, mci->mci_mailer->m_flags))
1064                {
1065                        if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '.') ==
1066                            SM_IO_EOF)
1067                        {
1068                                dead = true;
1069                                break;
1070                        }
1071                        if (TrafficLogFile != NULL)
1072                                (void) sm_io_putc(TrafficLogFile,
1073                                                  SM_TIME_DEFAULT, '.');
1074                }
1075                else if (l[0] == 'F' && slop == 0 &&
1076                         bitset(PXLF_MAPFROM, pxflags) &&
1077                         strncmp(l, "From ", 5) == 0 &&
1078                         bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
1079                {
1080                        if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT, '>') ==
1081                            SM_IO_EOF)
1082                        {
1083                                dead = true;
1084                                break;
1085                        }
1086                        if (TrafficLogFile != NULL)
1087                                (void) sm_io_putc(TrafficLogFile,
1088                                                  SM_TIME_DEFAULT, '>');
1089                }
1090                for ( ; l < p; ++l)
1091                {
1092                        if (TrafficLogFile != NULL)
1093                                (void) sm_io_putc(TrafficLogFile,
1094                                                  SM_TIME_DEFAULT,
1095                                                  (unsigned char)*l);
1096                        if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1097                                       (unsigned char) *l) == SM_IO_EOF)
1098                        {
1099                                dead = true;
1100                                break;
1101                        }
1102                }
1103                if (dead)
1104                        break;
1105
1106                if (TrafficLogFile != NULL)
1107                        (void) sm_io_putc(TrafficLogFile, SM_TIME_DEFAULT,
1108                                          '\n');
1109                if ((!bitset(PXLF_NOADDEOL, pxflags) || !noeol) &&
1110                    sm_io_fputs(mci->mci_out, SM_TIME_DEFAULT,
1111                                mci->mci_mailer->m_eol) == SM_IO_EOF)
1112                {
1113                        dead = true;
1114                        break;
1115                }
1116                if (l < end && *l == '\n')
1117                {
1118                        if (*++l != ' ' && *l != '\t' && *l != '\0' &&
1119                            bitset(PXLF_HEADER, pxflags))
1120                        {
1121                                if (sm_io_putc(mci->mci_out, SM_TIME_DEFAULT,
1122                                               ' ') == SM_IO_EOF)
1123                                {
1124                                        dead = true;
1125                                        break;
1126                                }
1127
1128                                if (TrafficLogFile != NULL)
1129                                        (void) sm_io_putc(TrafficLogFile,
1130                                                          SM_TIME_DEFAULT, ' ');
1131                        }
1132                }
1133
1134        } while (l < end);
1135        return !dead;
1136}
1137
1138/*
1139**  XUNLINK -- unlink a file, doing logging as appropriate.
1140**
1141**      Parameters:
1142**              f -- name of file to unlink.
1143**
1144**      Returns:
1145**              return value of unlink()
1146**
1147**      Side Effects:
1148**              f is unlinked.
1149*/
1150
1151int
1152xunlink(f)
1153        char *f;
1154{
1155        register int i;
1156        int save_errno;
1157
1158        if (LogLevel > 98)
1159                sm_syslog(LOG_DEBUG, CurEnv->e_id, "unlink %s", f);
1160
1161        i = unlink(f);
1162        save_errno = errno;
1163        if (i < 0 && LogLevel > 97)
1164                sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s: unlink-fail %d",
1165                          f, errno);
1166        if (i >= 0)
1167                SYNC_DIR(f, false);
1168        errno = save_errno;
1169        return i;
1170}
1171/*
1172**  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
1173**
1174**      Parameters:
1175**              buf -- place to put the input line.
1176**              siz -- size of buf.
1177**              fp -- file to read from.
1178**              timeout -- the timeout before error occurs.
1179**              during -- what we are trying to read (for error messages).
1180**
1181**      Returns:
1182**              NULL on error (including timeout).  This may also leave
1183**                      buf containing a null string.
1184**              buf otherwise.
1185*/
1186
1187
1188char *
1189sfgets(buf, siz, fp, timeout, during)
1190        char *buf;
1191        int siz;
1192        SM_FILE_T *fp;
1193        time_t timeout;
1194        char *during;
1195{
1196        register char *p;
1197        int save_errno;
1198        int io_timeout;
1199
1200        SM_REQUIRE(siz > 0);
1201        SM_REQUIRE(buf != NULL);
1202
1203        if (fp == NULL)
1204        {
1205                buf[0] = '\0';
1206                errno = EBADF;
1207                return NULL;
1208        }
1209
1210        /* try to read */
1211        p = NULL;
1212        errno = 0;
1213
1214        /* convert the timeout to sm_io notation */
1215        io_timeout = (timeout <= 0) ? SM_TIME_DEFAULT : timeout * 1000;
1216        while (!sm_io_eof(fp) && !sm_io_error(fp))
1217        {
1218                errno = 0;
1219                p = sm_io_fgets(fp, io_timeout, buf, siz);
1220                if (p == NULL && errno == EAGAIN)
1221                {
1222                        /* The sm_io_fgets() call timedout */
1223                        if (LogLevel > 1)
1224                                sm_syslog(LOG_NOTICE, CurEnv->e_id,
1225                                          "timeout waiting for input from %.100s during %s",
1226                                          CURHOSTNAME,
1227                                          during);
1228                        buf[0] = '\0';
1229#if XDEBUG
1230                        checkfd012(during);
1231#endif /* XDEBUG */
1232                        if (TrafficLogFile != NULL)
1233                                (void) sm_io_fprintf(TrafficLogFile,
1234                                                     SM_TIME_DEFAULT,
1235                                                     "%05d <<< [TIMEOUT]\n",
1236                                                     (int) CurrentPid);
1237                        errno = ETIMEDOUT;
1238                        return NULL;
1239                }
1240                if (p != NULL || errno != EINTR)
1241                        break;
1242                (void) sm_io_clearerr(fp);
1243        }
1244        save_errno = errno;
1245
1246        /* clean up the books and exit */
1247        LineNumber++;
1248        if (p == NULL)
1249        {
1250                buf[0] = '\0';
1251                if (TrafficLogFile != NULL)
1252                        (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1253                                             "%05d <<< [EOF]\n",
1254                                             (int) CurrentPid);
1255                errno = save_errno;
1256                return NULL;
1257        }
1258        if (TrafficLogFile != NULL)
1259                (void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
1260                                     "%05d <<< %s", (int) CurrentPid, buf);
1261        if (SevenBitInput)
1262        {
1263                for (p = buf; *p != '\0'; p++)
1264                        *p &= ~0200;
1265        }
1266        else if (!HasEightBits)
1267        {
1268                for (p = buf; *p != '\0'; p++)
1269                {
1270                        if (bitset(0200, *p))
1271                        {
1272                                HasEightBits = true;
1273                                break;
1274                        }
1275                }
1276        }
1277        return buf;
1278}
1279/*
1280**  FGETFOLDED -- like fgets, but knows about folded lines.
1281**
1282**      Parameters:
1283**              buf -- place to put result.
1284**              n -- bytes available.
1285**              f -- file to read from.
1286**
1287**      Returns:
1288**              input line(s) on success, NULL on error or SM_IO_EOF.
1289**              This will normally be buf -- unless the line is too
1290**                      long, when it will be sm_malloc_x()ed.
1291**
1292**      Side Effects:
1293**              buf gets lines from f, with continuation lines (lines
1294**              with leading white space) appended.  CRLF's are mapped
1295**              into single newlines.  Any trailing NL is stripped.
1296*/
1297
1298char *
1299fgetfolded(buf, n, f)
1300        char *buf;
1301        register int n;
1302        SM_FILE_T *f;
1303{
1304        register char *p = buf;
1305        char *bp = buf;
1306        register int i;
1307
1308        SM_REQUIRE(n > 0);
1309        SM_REQUIRE(buf != NULL);
1310        if (f == NULL)
1311        {
1312                buf[0] = '\0';
1313                errno = EBADF;
1314                return NULL;
1315        }
1316
1317        n--;
1318        while ((i = sm_io_getc(f, SM_TIME_DEFAULT)) != SM_IO_EOF)
1319        {
1320                if (i == '\r')
1321                {
1322                        i = sm_io_getc(f, SM_TIME_DEFAULT);
1323                        if (i != '\n')
1324                        {
1325                                if (i != SM_IO_EOF)
1326                                        (void) sm_io_ungetc(f, SM_TIME_DEFAULT,
1327                                                            i);
1328                                i = '\r';
1329                        }
1330                }
1331                if (--n <= 0)
1332                {
1333                        /* allocate new space */
1334                        char *nbp;
1335                        int nn;
1336
1337                        nn = (p - bp);
1338                        if (nn < MEMCHUNKSIZE)
1339                                nn *= 2;
1340                        else
1341                                nn += MEMCHUNKSIZE;
1342                        nbp = sm_malloc_x(nn);
1343                        memmove(nbp, bp, p - bp);
1344                        p = &nbp[p - bp];
1345                        if (bp != buf)
1346                                sm_free(bp);
1347                        bp = nbp;
1348                        n = nn - (p - bp);
1349                }
1350                *p++ = i;
1351                if (i == '\n')
1352                {
1353                        LineNumber++;
1354                        i = sm_io_getc(f, SM_TIME_DEFAULT);
1355                        if (i != SM_IO_EOF)
1356                                (void) sm_io_ungetc(f, SM_TIME_DEFAULT, i);
1357                        if (i != ' ' && i != '\t')
1358                                break;
1359                }
1360        }
1361        if (p == bp)
1362                return NULL;
1363        if (p[-1] == '\n')
1364                p--;
1365        *p = '\0';
1366        return bp;
1367}
1368/*
1369**  CURTIME -- return current time.
1370**
1371**      Parameters:
1372**              none.
1373**
1374**      Returns:
1375**              the current time.
1376*/
1377
1378time_t
1379curtime()
1380{
1381        auto time_t t;
1382
1383        (void) time(&t);
1384        return t;
1385}
1386/*
1387**  ATOBOOL -- convert a string representation to boolean.
1388**
1389**      Defaults to false
1390**
1391**      Parameters:
1392**              s -- string to convert.  Takes "tTyY", empty, and NULL as true,
1393**                      others as false.
1394**
1395**      Returns:
1396**              A boolean representation of the string.
1397*/
1398
1399bool
1400atobool(s)
1401        register char *s;
1402{
1403        if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1404                return true;
1405        return false;
1406}
1407/*
1408**  ATOOCT -- convert a string representation to octal.
1409**
1410**      Parameters:
1411**              s -- string to convert.
1412**
1413**      Returns:
1414**              An integer representing the string interpreted as an
1415**              octal number.
1416*/
1417
1418int
1419atooct(s)
1420        register char *s;
1421{
1422        register int i = 0;
1423
1424        while (*s >= '0' && *s <= '7')
1425                i = (i << 3) | (*s++ - '0');
1426        return i;
1427}
1428/*
1429**  BITINTERSECT -- tell if two bitmaps intersect
1430**
1431**      Parameters:
1432**              a, b -- the bitmaps in question
1433**
1434**      Returns:
1435**              true if they have a non-null intersection
1436**              false otherwise
1437*/
1438
1439bool
1440bitintersect(a, b)
1441        BITMAP256 a;
1442        BITMAP256 b;
1443{
1444        int i;
1445
1446        for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1447        {
1448                if ((a[i] & b[i]) != 0)
1449                        return true;
1450        }
1451        return false;
1452}
1453/*
1454**  BITZEROP -- tell if a bitmap is all zero
1455**
1456**      Parameters:
1457**              map -- the bit map to check
1458**
1459**      Returns:
1460**              true if map is all zero.
1461**              false if there are any bits set in map.
1462*/
1463
1464bool
1465bitzerop(map)
1466        BITMAP256 map;
1467{
1468        int i;
1469
1470        for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1471        {
1472                if (map[i] != 0)
1473                        return false;
1474        }
1475        return true;
1476}
1477/*
1478**  STRCONTAINEDIN -- tell if one string is contained in another
1479**
1480**      Parameters:
1481**              icase -- ignore case?
1482**              a -- possible substring.
1483**              b -- possible superstring.
1484**
1485**      Returns:
1486**              true if a is contained in b (case insensitive).
1487**              false otherwise.
1488*/
1489
1490bool
1491strcontainedin(icase, a, b)
1492        bool icase;
1493        register char *a;
1494        register char *b;
1495{
1496        int la;
1497        int lb;
1498        int c;
1499
1500        la = strlen(a);
1501        lb = strlen(b);
1502        c = *a;
1503        if (icase && isascii(c) && isupper(c))
1504                c = tolower(c);
1505        for (; lb-- >= la; b++)
1506        {
1507                if (icase)
1508                {
1509                        if (*b != c &&
1510                            isascii(*b) && isupper(*b) && tolower(*b) != c)
1511                                continue;
1512                        if (sm_strncasecmp(a, b, la) == 0)
1513                                return true;
1514                }
1515                else
1516                {
1517                        if (*b != c)
1518                                continue;
1519                        if (strncmp(a, b, la) == 0)
1520                                return true;
1521                }
1522        }
1523        return false;
1524}
1525/*
1526**  CHECKFD012 -- check low numbered file descriptors
1527**
1528**      File descriptors 0, 1, and 2 should be open at all times.
1529**      This routine verifies that, and fixes it if not true.
1530**
1531**      Parameters:
1532**              where -- a tag printed if the assertion failed
1533**
1534**      Returns:
1535**              none
1536*/
1537
1538void
1539checkfd012(where)
1540        char *where;
1541{
1542#if XDEBUG
1543        register int i;
1544
1545        for (i = 0; i < 3; i++)
1546                fill_fd(i, where);
1547#endif /* XDEBUG */
1548}
1549/*
1550**  CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1551**
1552**      Parameters:
1553**              fd -- file descriptor to check.
1554**              where -- tag to print on failure.
1555**
1556**      Returns:
1557**              none.
1558*/
1559
1560void
1561checkfdopen(fd, where)
1562        int fd;
1563        char *where;
1564{
1565#if XDEBUG
1566        struct stat st;
1567
1568        if (fstat(fd, &st) < 0 && errno == EBADF)
1569        {
1570                syserr("checkfdopen(%d): %s not open as expected!", fd, where);
1571                printopenfds(true);
1572        }
1573#endif /* XDEBUG */
1574}
1575/*
1576**  CHECKFDS -- check for new or missing file descriptors
1577**
1578**      Parameters:
1579**              where -- tag for printing.  If null, take a base line.
1580**
1581**      Returns:
1582**              none
1583**
1584**      Side Effects:
1585**              If where is set, shows changes since the last call.
1586*/
1587
1588void
1589checkfds(where)
1590        char *where;
1591{
1592        int maxfd;
1593        register int fd;
1594        bool printhdr = true;
1595        int save_errno = errno;
1596        static BITMAP256 baseline;
1597        extern int DtableSize;
1598
1599        if (DtableSize > BITMAPBITS)
1600                maxfd = BITMAPBITS;
1601        else
1602                maxfd = DtableSize;
1603        if (where == NULL)
1604                clrbitmap(baseline);
1605
1606        for (fd = 0; fd < maxfd; fd++)
1607        {
1608                struct stat stbuf;
1609
1610                if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
1611                {
1612                        if (!bitnset(fd, baseline))
1613                                continue;
1614                        clrbitn(fd, baseline);
1615                }
1616                else if (!bitnset(fd, baseline))
1617                        setbitn(fd, baseline);
1618                else
1619                        continue;
1620
1621                /* file state has changed */
1622                if (where == NULL)
1623                        continue;
1624                if (printhdr)
1625                {
1626                        sm_syslog(LOG_DEBUG, CurEnv->e_id,
1627                                  "%s: changed fds:",
1628                                  where);
1629                        printhdr = false;
1630                }
1631                dumpfd(fd, true, true);
1632        }
1633        errno = save_errno;
1634}
1635/*
1636**  PRINTOPENFDS -- print the open file descriptors (for debugging)
1637**
1638**      Parameters:
1639**              logit -- if set, send output to syslog; otherwise
1640**                      print for debugging.
1641**
1642**      Returns:
1643**              none.
1644*/
1645
1646#if NETINET || NETINET6
1647# include <arpa/inet.h>
1648#endif /* NETINET || NETINET6 */
1649
1650void
1651printopenfds(logit)
1652        bool logit;
1653{
1654        register int fd;
1655        extern int DtableSize;
1656
1657        for (fd = 0; fd < DtableSize; fd++)
1658                dumpfd(fd, false, logit);
1659}
1660/*
1661**  DUMPFD -- dump a file descriptor
1662**
1663**      Parameters:
1664**              fd -- the file descriptor to dump.
1665**              printclosed -- if set, print a notification even if
1666**                      it is closed; otherwise print nothing.
1667**              logit -- if set, send output to syslog instead of stdout.
1668**
1669**      Returns:
1670**              none.
1671*/
1672
1673void
1674dumpfd(fd, printclosed, logit)
1675        int fd;
1676        bool printclosed;
1677        bool logit;
1678{
1679        register char *p;
1680        char *hp;
1681#ifdef S_IFSOCK
1682        SOCKADDR sa;
1683#endif /* S_IFSOCK */
1684        auto SOCKADDR_LEN_T slen;
1685        int i;
1686#if STAT64 > 0
1687        struct stat64 st;
1688#else /* STAT64 > 0 */
1689        struct stat st;
1690#endif /* STAT64 > 0 */
1691        char buf[200];
1692
1693        p = buf;
1694        (void) sm_snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
1695        p += strlen(p);
1696
1697        if (
1698#if STAT64 > 0
1699            fstat64(fd, &st)
1700#else /* STAT64 > 0 */
1701            fstat(fd, &st)
1702#endif /* STAT64 > 0 */
1703            < 0)
1704        {
1705                if (errno != EBADF)
1706                {
1707                        (void) sm_snprintf(p, SPACELEFT(buf, p),
1708                                "CANNOT STAT (%s)",
1709                                sm_errstring(errno));
1710                        goto printit;
1711                }
1712                else if (printclosed)
1713                {
1714                        (void) sm_snprintf(p, SPACELEFT(buf, p), "CLOSED");
1715                        goto printit;
1716                }
1717                return;
1718        }
1719
1720        i = fcntl(fd, F_GETFL, 0);
1721        if (i != -1)
1722        {
1723                (void) sm_snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
1724                p += strlen(p);
1725        }
1726
1727        (void) sm_snprintf(p, SPACELEFT(buf, p), "mode=%o: ",
1728                        (int) st.st_mode);
1729        p += strlen(p);
1730        switch (st.st_mode & S_IFMT)
1731        {
1732#ifdef S_IFSOCK
1733          case S_IFSOCK:
1734                (void) sm_snprintf(p, SPACELEFT(buf, p), "SOCK ");
1735                p += strlen(p);
1736                memset(&sa, '\0', sizeof sa);
1737                slen = sizeof sa;
1738                if (getsockname(fd, &sa.sa, &slen) < 0)
1739                        (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
1740                                 sm_errstring(errno));
1741                else
1742                {
1743                        hp = hostnamebyanyaddr(&sa);
1744                        if (hp == NULL)
1745                        {
1746                                /* EMPTY */
1747                                /* do nothing */
1748                        }
1749# if NETINET
1750                        else if (sa.sa.sa_family == AF_INET)
1751                                (void) sm_snprintf(p, SPACELEFT(buf, p),
1752                                        "%s/%d", hp, ntohs(sa.sin.sin_port));
1753# endif /* NETINET */
1754# if NETINET6
1755                        else if (sa.sa.sa_family == AF_INET6)
1756                                (void) sm_snprintf(p, SPACELEFT(buf, p),
1757                                        "%s/%d", hp, ntohs(sa.sin6.sin6_port));
1758# endif /* NETINET6 */
1759                        else
1760                                (void) sm_snprintf(p, SPACELEFT(buf, p),
1761                                        "%s", hp);
1762                }
1763                p += strlen(p);
1764                (void) sm_snprintf(p, SPACELEFT(buf, p), "->");
1765                p += strlen(p);
1766                slen = sizeof sa;
1767                if (getpeername(fd, &sa.sa, &slen) < 0)
1768                        (void) sm_snprintf(p, SPACELEFT(buf, p), "(%s)",
1769                                        sm_errstring(errno));
1770                else
1771                {
1772                        hp = hostnamebyanyaddr(&sa);
1773                        if (hp == NULL)
1774                        {
1775                                /* EMPTY */
1776                                /* do nothing */
1777                        }
1778# if NETINET
1779                        else if (sa.sa.sa_family == AF_INET)
1780                                (void) sm_snprintf(p, SPACELEFT(buf, p),
1781                                        "%s/%d", hp, ntohs(sa.sin.sin_port));
1782# endif /* NETINET */
1783# if NETINET6
1784                        else if (sa.sa.sa_family == AF_INET6)
1785                                (void) sm_snprintf(p, SPACELEFT(buf, p),
1786                                        "%s/%d", hp, ntohs(sa.sin6.sin6_port));
1787# endif /* NETINET6 */
1788                        else
1789                                (void) sm_snprintf(p, SPACELEFT(buf, p),
1790                                        "%s", hp);
1791                }
1792                break;
1793#endif /* S_IFSOCK */
1794
1795          case S_IFCHR:
1796                (void) sm_snprintf(p, SPACELEFT(buf, p), "CHR: ");
1797                p += strlen(p);
1798                goto defprint;
1799
1800#ifdef S_IFBLK
1801          case S_IFBLK:
1802                (void) sm_snprintf(p, SPACELEFT(buf, p), "BLK: ");
1803                p += strlen(p);
1804                goto defprint;
1805#endif /* S_IFBLK */
1806
1807#if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1808          case S_IFIFO:
1809                (void) sm_snprintf(p, SPACELEFT(buf, p), "FIFO: ");
1810                p += strlen(p);
1811                goto defprint;
1812#endif /* defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) */
1813
1814#ifdef S_IFDIR
1815          case S_IFDIR:
1816                (void) sm_snprintf(p, SPACELEFT(buf, p), "DIR: ");
1817                p += strlen(p);
1818                goto defprint;
1819#endif /* S_IFDIR */
1820
1821#ifdef S_IFLNK
1822          case S_IFLNK:
1823                (void) sm_snprintf(p, SPACELEFT(buf, p), "LNK: ");
1824                p += strlen(p);
1825                goto defprint;
1826#endif /* S_IFLNK */
1827
1828          default:
1829defprint:
1830                (void) sm_snprintf(p, SPACELEFT(buf, p),
1831                         "dev=%d/%d, ino=%llu, nlink=%d, u/gid=%d/%d, ",
1832                         major(st.st_dev), minor(st.st_dev),
1833                         (ULONGLONG_T) st.st_ino,
1834                         (int) st.st_nlink, (int) st.st_uid,
1835                         (int) st.st_gid);
1836                p += strlen(p);
1837                (void) sm_snprintf(p, SPACELEFT(buf, p), "size=%llu",
1838                         (ULONGLONG_T) st.st_size);
1839                break;
1840        }
1841
1842printit:
1843        if (logit)
1844                sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
1845                          "%.800s", buf);
1846        else
1847                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", buf);
1848}
1849/*
1850**  SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1851**
1852**      Parameters:
1853**              host -- the host to shorten (stripped in place).
1854**
1855**      Returns:
1856**              place where string was truncated, NULL if not truncated.
1857*/
1858
1859char *
1860shorten_hostname(host)
1861        char host[];
1862{
1863        register char *p;
1864        char *mydom;
1865        int i;
1866        bool canon = false;
1867
1868        /* strip off final dot */
1869        i = strlen(host);
1870        p = &host[(i == 0) ? 0 : i - 1];
1871        if (*p == '.')
1872        {
1873                *p = '\0';
1874                canon = true;
1875        }
1876
1877        /* see if there is any domain at all -- if not, we are done */
1878        p = strchr(host, '.');
1879        if (p == NULL)
1880                return NULL;
1881
1882        /* yes, we have a domain -- see if it looks like us */
1883        mydom = macvalue('m', CurEnv);
1884        if (mydom == NULL)
1885                mydom = "";
1886        i = strlen(++p);
1887        if ((canon ? sm_strcasecmp(p, mydom)
1888                   : sm_strncasecmp(p, mydom, i)) == 0 &&
1889                        (mydom[i] == '.' || mydom[i] == '\0'))
1890        {
1891                *--p = '\0';
1892                return p;
1893        }
1894        return NULL;
1895}
1896/*
1897**  PROG_OPEN -- open a program for reading
1898**
1899**      Parameters:
1900**              argv -- the argument list.
1901**              pfd -- pointer to a place to store the file descriptor.
1902**              e -- the current envelope.
1903**
1904**      Returns:
1905**              pid of the process -- -1 if it failed.
1906*/
1907
1908pid_t
1909prog_open(argv, pfd, e)
1910        char **argv;
1911        int *pfd;
1912        ENVELOPE *e;
1913{
1914        pid_t pid;
1915        int i;
1916        int save_errno;
1917        int sff;
1918        int ret;
1919        int fdv[2];
1920        char *p, *q;
1921        char buf[MAXPATHLEN];
1922        extern int DtableSize;
1923
1924        if (pipe(fdv) < 0)
1925        {
1926                syserr("%s: cannot create pipe for stdout", argv[0]);
1927                return -1;
1928        }
1929        pid = fork();
1930        if (pid < 0)
1931        {
1932                syserr("%s: cannot fork", argv[0]);
1933                (void) close(fdv[0]);
1934                (void) close(fdv[1]);
1935                return -1;
1936        }
1937        if (pid > 0)
1938        {
1939                /* parent */
1940                (void) close(fdv[1]);
1941                *pfd = fdv[0];
1942                return pid;
1943        }
1944
1945        /* Reset global flags */
1946        RestartRequest = NULL;
1947        RestartWorkGroup = false;
1948        ShutdownRequest = NULL;
1949        PendingSignal = 0;
1950        CurrentPid = getpid();
1951
1952        /*
1953        **  Initialize exception stack and default exception
1954        **  handler for child process.
1955        */
1956
1957        sm_exc_newthread(fatal_error);
1958
1959        /* child -- close stdin */
1960        (void) close(0);
1961
1962        /* stdout goes back to parent */
1963        (void) close(fdv[0]);
1964        if (dup2(fdv[1], 1) < 0)
1965        {
1966                syserr("%s: cannot dup2 for stdout", argv[0]);
1967                _exit(EX_OSERR);
1968        }
1969        (void) close(fdv[1]);
1970
1971        /* stderr goes to transcript if available */
1972        if (e->e_xfp != NULL)
1973        {
1974                int xfd;
1975
1976                xfd = sm_io_getinfo(e->e_xfp, SM_IO_WHAT_FD, NULL);
1977                if (xfd >= 0 && dup2(xfd, 2) < 0)
1978                {
1979                        syserr("%s: cannot dup2 for stderr", argv[0]);
1980                        _exit(EX_OSERR);
1981                }
1982        }
1983
1984        /* this process has no right to the queue file */
1985        if (e->e_lockfp != NULL)
1986                (void) close(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL));
1987
1988        /* chroot to the program mailer directory, if defined */
1989        if (ProgMailer != NULL && ProgMailer->m_rootdir != NULL)
1990        {
1991                expand(ProgMailer->m_rootdir, buf, sizeof buf, e);
1992                if (chroot(buf) < 0)
1993                {
1994                        syserr("prog_open: cannot chroot(%s)", buf);
1995                        exit(EX_TEMPFAIL);
1996                }
1997                if (chdir("/") < 0)
1998                {
1999                        syserr("prog_open: cannot chdir(/)");
2000                        exit(EX_TEMPFAIL);
2001                }
2002        }
2003
2004        /* run as default user */
2005        endpwent();
2006        sm_mbdb_terminate();
2007        if (setgid(DefGid) < 0 && geteuid() == 0)
2008        {
2009                syserr("prog_open: setgid(%ld) failed", (long) DefGid);
2010                exit(EX_TEMPFAIL);
2011        }
2012        if (setuid(DefUid) < 0 && geteuid() == 0)
2013        {
2014                syserr("prog_open: setuid(%ld) failed", (long) DefUid);
2015                exit(EX_TEMPFAIL);
2016        }
2017
2018        /* run in some directory */
2019        if (ProgMailer != NULL)
2020                p = ProgMailer->m_execdir;
2021        else
2022                p = NULL;
2023        for (; p != NULL; p = q)
2024        {
2025                q = strchr(p, ':');
2026                if (q != NULL)
2027                        *q = '\0';
2028                expand(p, buf, sizeof buf, e);
2029                if (q != NULL)
2030                        *q++ = ':';
2031                if (buf[0] != '\0' && chdir(buf) >= 0)
2032                        break;
2033        }
2034        if (p == NULL)
2035        {
2036                /* backup directories */
2037                if (chdir("/tmp") < 0)
2038                        (void) chdir("/");
2039        }
2040
2041        /* Check safety of program to be run */
2042        sff = SFF_ROOTOK|SFF_EXECOK;
2043        if (!bitnset(DBS_RUNWRITABLEPROGRAM, DontBlameSendmail))
2044                sff |= SFF_NOGWFILES|SFF_NOWWFILES;
2045        if (bitnset(DBS_RUNPROGRAMINUNSAFEDIRPATH, DontBlameSendmail))
2046                sff |= SFF_NOPATHCHECK;
2047        else
2048                sff |= SFF_SAFEDIRPATH;
2049        ret = safefile(argv[0], DefUid, DefGid, DefUser, sff, 0, NULL);
2050        if (ret != 0)
2051                sm_syslog(LOG_INFO, e->e_id,
2052                          "Warning: prog_open: program %s unsafe: %s",
2053                          argv[0], sm_errstring(ret));
2054
2055        /* arrange for all the files to be closed */
2056        for (i = 3; i < DtableSize; i++)
2057        {
2058                register int j;
2059
2060                if ((j = fcntl(i, F_GETFD, 0)) != -1)
2061                        (void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
2062        }
2063
2064        /* now exec the process */
2065        (void) execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
2066
2067        /* woops!  failed */
2068        save_errno = errno;
2069        syserr("%s: cannot exec", argv[0]);
2070        if (transienterror(save_errno))
2071                _exit(EX_OSERR);
2072        _exit(EX_CONFIG);
2073        return -1;      /* avoid compiler warning on IRIX */
2074}
2075/*
2076**  GET_COLUMN -- look up a Column in a line buffer
2077**
2078**      Parameters:
2079**              line -- the raw text line to search.
2080**              col -- the column number to fetch.
2081**              delim -- the delimiter between columns.  If null,
2082**                      use white space.
2083**              buf -- the output buffer.
2084**              buflen -- the length of buf.
2085**
2086**      Returns:
2087**              buf if successful.
2088**              NULL otherwise.
2089*/
2090
2091char *
2092get_column(line, col, delim, buf, buflen)
2093        char line[];
2094        int col;
2095        int delim;
2096        char buf[];
2097        int buflen;
2098{
2099        char *p;
2100        char *begin, *end;
2101        int i;
2102        char delimbuf[4];
2103
2104        if ((char) delim == '\0')
2105                (void) sm_strlcpy(delimbuf, "\n\t ", sizeof delimbuf);
2106        else
2107        {
2108                delimbuf[0] = (char) delim;
2109                delimbuf[1] = '\0';
2110        }
2111
2112        p = line;
2113        if (*p == '\0')
2114                return NULL;                    /* line empty */
2115        if (*p == (char) delim && col == 0)
2116                return NULL;                    /* first column empty */
2117
2118        begin = line;
2119
2120        if (col == 0 && (char) delim == '\0')
2121        {
2122                while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2123                        begin++;
2124        }
2125
2126        for (i = 0; i < col; i++)
2127        {
2128                if ((begin = strpbrk(begin, delimbuf)) == NULL)
2129                        return NULL;            /* no such column */
2130                begin++;
2131                if ((char) delim == '\0')
2132                {
2133                        while (*begin != '\0' && isascii(*begin) && isspace(*begin))
2134                                begin++;
2135                }
2136        }
2137
2138        end = strpbrk(begin, delimbuf);
2139        if (end == NULL)
2140                i = strlen(begin);
2141        else
2142                i = end - begin;
2143        if (i >= buflen)
2144                i = buflen - 1;
2145        (void) sm_strlcpy(buf, begin, i + 1);
2146        return buf;
2147}
2148/*
2149**  CLEANSTRCPY -- copy string keeping out bogus characters
2150**
2151**      Parameters:
2152**              t -- "to" string.
2153**              f -- "from" string.
2154**              l -- length of space available in "to" string.
2155**
2156**      Returns:
2157**              none.
2158*/
2159
2160void
2161cleanstrcpy(t, f, l)
2162        register char *t;
2163        register char *f;
2164        int l;
2165{
2166        /* check for newlines and log if necessary */
2167        (void) denlstring(f, true, true);
2168
2169        if (l <= 0)
2170                syserr("!cleanstrcpy: length == 0");
2171
2172        l--;
2173        while (l > 0 && *f != '\0')
2174        {
2175                if (isascii(*f) &&
2176                    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
2177                {
2178                        l--;
2179                        *t++ = *f;
2180                }
2181                f++;
2182        }
2183        *t = '\0';
2184}
2185/*
2186**  DENLSTRING -- convert newlines in a string to spaces
2187**
2188**      Parameters:
2189**              s -- the input string
2190**              strict -- if set, don't permit continuation lines.
2191**              logattacks -- if set, log attempted attacks.
2192**
2193**      Returns:
2194**              A pointer to a version of the string with newlines
2195**              mapped to spaces.  This should be copied.
2196*/
2197
2198char *
2199denlstring(s, strict, logattacks)
2200        char *s;
2201        bool strict;
2202        bool logattacks;
2203{
2204        register char *p;
2205        int l;
2206        static char *bp = NULL;
2207        static int bl = 0;
2208
2209        p = s;
2210        while ((p = strchr(p, '\n')) != NULL)
2211                if (strict || (*++p != ' ' && *p != '\t'))
2212                        break;
2213        if (p == NULL)
2214                return s;
2215
2216        l = strlen(s) + 1;
2217        if (bl < l)
2218        {
2219                /* allocate more space */
2220                char *nbp = sm_pmalloc_x(l);
2221
2222                if (bp != NULL)
2223                        sm_free(bp);
2224                bp = nbp;
2225                bl = l;
2226        }
2227        (void) sm_strlcpy(bp, s, l);
2228        for (p = bp; (p = strchr(p, '\n')) != NULL; )
2229                *p++ = ' ';
2230
2231        if (logattacks)
2232        {
2233                sm_syslog(LOG_NOTICE, CurEnv->e_id,
2234                          "POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
2235                          RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
2236                          shortenstring(bp, MAXSHORTSTR));
2237        }
2238
2239        return bp;
2240}
2241
2242/*
2243**  STRREPLNONPRT -- replace "unprintable" characters in a string with subst
2244**
2245**      Parameters:
2246**              s -- string to manipulate (in place)
2247**              subst -- character to use as replacement
2248**
2249**      Returns:
2250**              true iff string did not contain "unprintable" characters
2251*/
2252
2253bool
2254strreplnonprt(s, c)
2255        char *s;
2256        int c;
2257{
2258        bool ok;
2259
2260        ok = true;
2261        if (s == NULL)
2262                return ok;
2263        while (*s != '\0')
2264        {
2265                if (!(isascii(*s) && isprint(*s)))
2266                {
2267                        *s = c;
2268                        ok = false;
2269                }
2270                ++s;
2271        }
2272        return ok;
2273}
2274
2275/*
2276**  STR2PRT -- convert "unprintable" characters in a string to \oct
2277**
2278**      Parameters:
2279**              s -- string to convert
2280**
2281**      Returns:
2282**              converted string.
2283**              This is a static local buffer, string must be copied
2284**              before this function is called again!
2285*/
2286
2287char *
2288str2prt(s)
2289        char *s;
2290{
2291        int l;
2292        char c, *h;
2293        bool ok;
2294        static int len = 0;
2295        static char *buf = NULL;
2296
2297        if (s == NULL)
2298                return NULL;
2299        ok = true;
2300        for (h = s, l = 1; *h != '\0'; h++, l++)
2301        {
2302                if (*h == '\\')
2303                {
2304                        ++l;
2305                        ok = false;
2306                }
2307                else if (!(isascii(*h) && isprint(*h)))
2308                {
2309                        l += 3;
2310                        ok = false;
2311                }
2312        }
2313        if (ok)
2314                return s;
2315        if (l > len)
2316        {
2317                char *nbuf = sm_pmalloc_x(l);
2318
2319                if (buf != NULL)
2320                        sm_free(buf);
2321                len = l;
2322                buf = nbuf;
2323        }
2324        for (h = buf; *s != '\0' && l > 0; s++, l--)
2325        {
2326                c = *s;
2327                if (isascii(c) && isprint(c) && c != '\\')
2328                {
2329                        *h++ = c;
2330                }
2331                else
2332                {
2333                        *h++ = '\\';
2334                        --l;
2335                        switch (c)
2336                        {
2337                          case '\\':
2338                                *h++ = '\\';
2339                                break;
2340                          case '\t':
2341                                *h++ = 't';
2342                                break;
2343                          case '\n':
2344                                *h++ = 'n';
2345                                break;
2346                          case '\r':
2347                                *h++ = 'r';
2348                                break;
2349                          default:
2350                                (void) sm_snprintf(h, l, "%03o", (int) c);
2351
2352                                /*
2353                                **  XXX since l is unsigned this may
2354                                **  wrap around if the calculation is screwed
2355                                **  up...
2356                                */
2357
2358                                l -= 2;
2359                                h += 3;
2360                                break;
2361                        }
2362                }
2363        }
2364        *h = '\0';
2365        buf[len - 1] = '\0';
2366        return buf;
2367}
2368/*
2369**  PATH_IS_DIR -- check to see if file exists and is a directory.
2370**
2371**      There are some additional checks for security violations in
2372**      here.  This routine is intended to be used for the host status
2373**      support.
2374**
2375**      Parameters:
2376**              pathname -- pathname to check for directory-ness.
2377**              createflag -- if set, create directory if needed.
2378**
2379**      Returns:
2380**              true -- if the indicated pathname is a directory
2381**              false -- otherwise
2382*/
2383
2384int
2385path_is_dir(pathname, createflag)
2386        char *pathname;
2387        bool createflag;
2388{
2389        struct stat statbuf;
2390
2391#if HASLSTAT
2392        if (lstat(pathname, &statbuf) < 0)
2393#else /* HASLSTAT */
2394        if (stat(pathname, &statbuf) < 0)
2395#endif /* HASLSTAT */
2396        {
2397                if (errno != ENOENT || !createflag)
2398                        return false;
2399                if (mkdir(pathname, 0755) < 0)
2400                        return false;
2401                return true;
2402        }
2403        if (!S_ISDIR(statbuf.st_mode))
2404        {
2405                errno = ENOTDIR;
2406                return false;
2407        }
2408
2409        /* security: don't allow writable directories */
2410        if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
2411        {
2412                errno = EACCES;
2413                return false;
2414        }
2415        return true;
2416}
2417/*
2418**  PROC_LIST_ADD -- add process id to list of our children
2419**
2420**      Parameters:
2421**              pid -- pid to add to list.
2422**              task -- task of pid.
2423**              type -- type of process.
2424**              count -- number of processes.
2425**              other -- other information for this type.
2426**
2427**      Returns:
2428**              none
2429**
2430**      Side Effects:
2431**              May increase CurChildren. May grow ProcList.
2432*/
2433
2434typedef struct procs    PROCS_T;
2435
2436struct procs
2437{
2438        pid_t   proc_pid;
2439        char    *proc_task;
2440        int     proc_type;
2441        int     proc_count;
2442        int     proc_other;
2443};
2444
2445static PROCS_T  *volatile ProcListVec = NULL;
2446static int      ProcListSize = 0;
2447
2448void
2449proc_list_add(pid, task, type, count, other)
2450        pid_t pid;
2451        char *task;
2452        int type;
2453        int count;
2454        int other;
2455{
2456        int i;
2457
2458        for (i = 0; i < ProcListSize; i++)
2459        {
2460                if (ProcListVec[i].proc_pid == NO_PID)
2461                        break;
2462        }
2463        if (i >= ProcListSize)
2464        {
2465                /* probe the existing vector to avoid growing infinitely */
2466                proc_list_probe();
2467
2468                /* now scan again */
2469                for (i = 0; i < ProcListSize; i++)
2470                {
2471                        if (ProcListVec[i].proc_pid == NO_PID)
2472                                break;
2473                }
2474        }
2475        if (i >= ProcListSize)
2476        {
2477                /* grow process list */
2478                PROCS_T *npv;
2479
2480                SM_ASSERT(ProcListSize < INT_MAX - PROC_LIST_SEG);
2481                npv = (PROCS_T *) sm_pmalloc_x((sizeof *npv) *
2482                                               (ProcListSize + PROC_LIST_SEG));
2483                if (ProcListSize > 0)
2484                {
2485                        memmove(npv, ProcListVec,
2486                                ProcListSize * sizeof (PROCS_T));
2487                        sm_free(ProcListVec);
2488                }
2489
2490                /* XXX just use memset() to initialize this part? */
2491                for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
2492                {
2493                        npv[i].proc_pid = NO_PID;
2494                        npv[i].proc_task = NULL;
2495                        npv[i].proc_type = PROC_NONE;
2496                }
2497                i = ProcListSize;
2498                ProcListSize += PROC_LIST_SEG;
2499                ProcListVec = npv;
2500        }
2501        ProcListVec[i].proc_pid = pid;
2502        PSTRSET(ProcListVec[i].proc_task, task);
2503        ProcListVec[i].proc_type = type;
2504        ProcListVec[i].proc_count = count;
2505        ProcListVec[i].proc_other = other;
2506
2507        /* if process adding itself, it's not a child */
2508        if (pid != CurrentPid)
2509        {
2510                SM_ASSERT(CurChildren < INT_MAX);
2511                CurChildren++;
2512        }
2513}
2514/*
2515**  PROC_LIST_SET -- set pid task in process list
2516**
2517**      Parameters:
2518**              pid -- pid to set
2519**              task -- task of pid
2520**
2521**      Returns:
2522**              none.
2523*/
2524
2525void
2526proc_list_set(pid, task)
2527        pid_t pid;
2528        char *task;
2529{
2530        int i;
2531
2532        for (i = 0; i < ProcListSize; i++)
2533        {
2534                if (ProcListVec[i].proc_pid == pid)
2535                {
2536                        PSTRSET(ProcListVec[i].proc_task, task);
2537                        break;
2538                }
2539        }
2540}
2541/*
2542**  PROC_LIST_DROP -- drop pid from process list
2543**
2544**      Parameters:
2545**              pid -- pid to drop
2546**              st -- process status
2547**              other -- storage for proc_other (return).
2548**
2549**      Returns:
2550**              none.
2551**
2552**      Side Effects:
2553**              May decrease CurChildren, CurRunners, or
2554**              set RestartRequest or ShutdownRequest.
2555**
2556**      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2557**              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2558**              DOING.
2559*/
2560
2561void
2562proc_list_drop(pid, st, other)
2563        pid_t pid;
2564        int st;
2565        int *other;
2566{
2567        int i;
2568        int type = PROC_NONE;
2569
2570        for (i = 0; i < ProcListSize; i++)
2571        {
2572                if (ProcListVec[i].proc_pid == pid)
2573                {
2574                        ProcListVec[i].proc_pid = NO_PID;
2575                        type = ProcListVec[i].proc_type;
2576                        if (other != NULL)
2577                                *other = ProcListVec[i].proc_other;
2578                        break;
2579                }
2580        }
2581        if (CurChildren > 0)
2582                CurChildren--;
2583
2584
2585        if (type == PROC_CONTROL && WIFEXITED(st))
2586        {
2587                /* if so, see if we need to restart or shutdown */
2588                if (WEXITSTATUS(st) == EX_RESTART)
2589                        RestartRequest = "control socket";
2590                else if (WEXITSTATUS(st) == EX_SHUTDOWN)
2591                        ShutdownRequest = "control socket";
2592        }
2593        else if (type == PROC_QUEUE_CHILD && !WIFSTOPPED(st) &&
2594                 ProcListVec[i].proc_other > -1)
2595        {
2596                /* restart this persistent runner */
2597                mark_work_group_restart(ProcListVec[i].proc_other, st);
2598        }
2599        else if (type == PROC_QUEUE)
2600                CurRunners -= ProcListVec[i].proc_count;
2601}
2602/*
2603**  PROC_LIST_CLEAR -- clear the process list
2604**
2605**      Parameters:
2606**              none.
2607**
2608**      Returns:
2609**              none.
2610**
2611**      Side Effects:
2612**              Sets CurChildren to zero.
2613*/
2614
2615void
2616proc_list_clear()
2617{
2618        int i;
2619
2620        /* start from 1 since 0 is the daemon itself */
2621        for (i = 1; i < ProcListSize; i++)
2622                ProcListVec[i].proc_pid = NO_PID;
2623        CurChildren = 0;
2624}
2625/*
2626**  PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2627**
2628**      Parameters:
2629**              none
2630**
2631**      Returns:
2632**              none
2633**
2634**      Side Effects:
2635**              May decrease CurChildren.
2636*/
2637
2638void
2639proc_list_probe()
2640{
2641        int i;
2642
2643        /* start from 1 since 0 is the daemon itself */
2644        for (i = 1; i < ProcListSize; i++)
2645        {
2646                if (ProcListVec[i].proc_pid == NO_PID)
2647                        continue;
2648                if (kill(ProcListVec[i].proc_pid, 0) < 0)
2649                {
2650                        if (LogLevel > 3)
2651                                sm_syslog(LOG_DEBUG, CurEnv->e_id,
2652                                          "proc_list_probe: lost pid %d",
2653                                          (int) ProcListVec[i].proc_pid);
2654                        ProcListVec[i].proc_pid = NO_PID;
2655                        SM_FREE_CLR(ProcListVec[i].proc_task);
2656                        CurChildren--;
2657                }
2658        }
2659        if (CurChildren < 0)
2660                CurChildren = 0;
2661}
2662
2663/*
2664**  PROC_LIST_DISPLAY -- display the process list
2665**
2666**      Parameters:
2667**              out -- output file pointer
2668**              prefix -- string to output in front of each line.
2669**
2670**      Returns:
2671**              none.
2672*/
2673
2674void
2675proc_list_display(out, prefix)
2676        SM_FILE_T *out;
2677        char *prefix;
2678{
2679        int i;
2680
2681        for (i = 0; i < ProcListSize; i++)
2682        {
2683                if (ProcListVec[i].proc_pid == NO_PID)
2684                        continue;
2685
2686                (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "%s%d %s%s\n",
2687                                     prefix,
2688                                     (int) ProcListVec[i].proc_pid,
2689                                     ProcListVec[i].proc_task != NULL ?
2690                                     ProcListVec[i].proc_task : "(unknown)",
2691                                     (OpMode == MD_SMTP ||
2692                                      OpMode == MD_DAEMON ||
2693                                      OpMode == MD_ARPAFTP) ? "\r" : "");
2694        }
2695}
2696
2697/*
2698**  PROC_LIST_SIGNAL -- send a signal to a type of process in the list
2699**
2700**      Parameters:
2701**              type -- type of process to signal
2702**              signal -- the type of signal to send
2703**
2704**      Results:
2705**              none.
2706**
2707**      NOTE:   THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2708**              ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2709**              DOING.
2710*/
2711
2712void
2713proc_list_signal(type, signal)
2714        int type;
2715        int signal;
2716{
2717        int chldwasblocked;
2718        int alrmwasblocked;
2719        int i;
2720        pid_t mypid = getpid();
2721
2722        /* block these signals so that we may signal cleanly */
2723        chldwasblocked = sm_blocksignal(SIGCHLD);
2724        alrmwasblocked = sm_blocksignal(SIGALRM);
2725
2726        /* Find all processes of type and send signal */
2727        for (i = 0; i < ProcListSize; i++)
2728        {
2729                if (ProcListVec[i].proc_pid == NO_PID ||
2730                    ProcListVec[i].proc_pid == mypid)
2731                        continue;
2732                if (ProcListVec[i].proc_type != type)
2733                        continue;
2734                (void) kill(ProcListVec[i].proc_pid, signal);
2735        }
2736
2737        /* restore the signals */
2738        if (alrmwasblocked == 0)
2739                (void) sm_releasesignal(SIGALRM);
2740        if (chldwasblocked == 0)
2741                (void) sm_releasesignal(SIGCHLD);
2742}
Note: See TracBrowser for help on using the repository browser.