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