mci.c revision 141887
1/* 2 * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 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 * $FreeBSD: head/contrib/sendmail/src/mci.c 141887 2005-02-14 08:04:08Z gshapiro $ 13 */ 14 15#include <sendmail.h> 16 17SM_RCSID("@(#)$Id: mci.c,v 8.212 2004/08/04 21:11:31 ca Exp $") 18 19#if NETINET || NETINET6 20# include <arpa/inet.h> 21#endif /* NETINET || NETINET6 */ 22 23#include <dirent.h> 24 25static int mci_generate_persistent_path __P((const char *, char *, 26 int, bool)); 27static bool mci_load_persistent __P((MCI *)); 28static void mci_uncache __P((MCI **, bool)); 29static int mci_lock_host_statfile __P((MCI *)); 30static int mci_read_persistent __P((SM_FILE_T *, MCI *)); 31 32/* 33** Mail Connection Information (MCI) Caching Module. 34** 35** There are actually two separate things cached. The first is 36** the set of all open connections -- these are stored in a 37** (small) list. The second is stored in the symbol table; it 38** has the overall status for all hosts, whether or not there 39** is a connection open currently. 40** 41** There should never be too many connections open (since this 42** could flood the socket table), nor should a connection be 43** allowed to sit idly for too long. 44** 45** MaxMciCache is the maximum number of open connections that 46** will be supported. 47** 48** MciCacheTimeout is the time (in seconds) that a connection 49** is permitted to survive without activity. 50** 51** We actually try any cached connections by sending a NOOP 52** before we use them; if the NOOP fails we close down the 53** connection and reopen it. Note that this means that a 54** server SMTP that doesn't support NOOP will hose the 55** algorithm -- but that doesn't seem too likely. 56** 57** The persistent MCI code is donated by Mark Lovell and Paul 58** Vixie. It is based on the long term host status code in KJS 59** written by Paul but has been adapted by Mark to fit into the 60** MCI structure. 61*/ 62 63static MCI **MciCache; /* the open connection cache */ 64 65/* 66** MCI_CACHE -- enter a connection structure into the open connection cache 67** 68** This may cause something else to be flushed. 69** 70** Parameters: 71** mci -- the connection to cache. 72** 73** Returns: 74** none. 75*/ 76 77void 78mci_cache(mci) 79 register MCI *mci; 80{ 81 register MCI **mcislot; 82 83 /* 84 ** Find the best slot. This may cause expired connections 85 ** to be closed. 86 */ 87 88 mcislot = mci_scan(mci); 89 if (mcislot == NULL) 90 { 91 /* we don't support caching */ 92 return; 93 } 94 95 if (mci->mci_host == NULL) 96 return; 97 98 /* if this is already cached, we are done */ 99 if (bitset(MCIF_CACHED, mci->mci_flags)) 100 return; 101 102 /* otherwise we may have to clear the slot */ 103 if (*mcislot != NULL) 104 mci_uncache(mcislot, true); 105 106 if (tTd(42, 5)) 107 sm_dprintf("mci_cache: caching %p (%s) in slot %d\n", 108 mci, mci->mci_host, (int) (mcislot - MciCache)); 109 if (tTd(91, 100)) 110 sm_syslog(LOG_DEBUG, CurEnv->e_id, 111 "mci_cache: caching %lx (%.100s) in slot %d", 112 (unsigned long) mci, mci->mci_host, 113 (int) (mcislot - MciCache)); 114 115 *mcislot = mci; 116 mci->mci_flags |= MCIF_CACHED; 117} 118/* 119** MCI_SCAN -- scan the cache, flush junk, and return best slot 120** 121** Parameters: 122** savemci -- never flush this one. Can be null. 123** 124** Returns: 125** The LRU (or empty) slot. 126*/ 127 128MCI ** 129mci_scan(savemci) 130 MCI *savemci; 131{ 132 time_t now; 133 register MCI **bestmci; 134 register MCI *mci; 135 register int i; 136 137 if (MaxMciCache <= 0) 138 { 139 /* we don't support caching */ 140 return NULL; 141 } 142 143 if (MciCache == NULL) 144 { 145 /* first call */ 146 MciCache = (MCI **) sm_pmalloc_x(MaxMciCache * sizeof *MciCache); 147 memset((char *) MciCache, '\0', MaxMciCache * sizeof *MciCache); 148 return &MciCache[0]; 149 } 150 151 now = curtime(); 152 bestmci = &MciCache[0]; 153 for (i = 0; i < MaxMciCache; i++) 154 { 155 mci = MciCache[i]; 156 if (mci == NULL || mci->mci_state == MCIS_CLOSED) 157 { 158 bestmci = &MciCache[i]; 159 continue; 160 } 161 if ((mci->mci_lastuse + MciCacheTimeout <= now || 162 (mci->mci_mailer != NULL && 163 mci->mci_mailer->m_maxdeliveries > 0 && 164 mci->mci_deliveries + 1 >= mci->mci_mailer->m_maxdeliveries))&& 165 mci != savemci) 166 { 167 /* connection idle too long or too many deliveries */ 168 bestmci = &MciCache[i]; 169 170 /* close it */ 171 mci_uncache(bestmci, true); 172 continue; 173 } 174 if (*bestmci == NULL) 175 continue; 176 if (mci->mci_lastuse < (*bestmci)->mci_lastuse) 177 bestmci = &MciCache[i]; 178 } 179 return bestmci; 180} 181/* 182** MCI_UNCACHE -- remove a connection from a slot. 183** 184** May close a connection. 185** 186** Parameters: 187** mcislot -- the slot to empty. 188** doquit -- if true, send QUIT protocol on this connection. 189** if false, we are assumed to be in a forked child; 190** all we want to do is close the file(s). 191** 192** Returns: 193** none. 194*/ 195 196static void 197mci_uncache(mcislot, doquit) 198 register MCI **mcislot; 199 bool doquit; 200{ 201 register MCI *mci; 202 extern ENVELOPE BlankEnvelope; 203 204 mci = *mcislot; 205 if (mci == NULL) 206 return; 207 *mcislot = NULL; 208 if (mci->mci_host == NULL) 209 return; 210 211 mci_unlock_host(mci); 212 213 if (tTd(42, 5)) 214 sm_dprintf("mci_uncache: uncaching %p (%s) from slot %d (%d)\n", 215 mci, mci->mci_host, (int) (mcislot - MciCache), 216 doquit); 217 if (tTd(91, 100)) 218 sm_syslog(LOG_DEBUG, CurEnv->e_id, 219 "mci_uncache: uncaching %lx (%.100s) from slot %d (%d)", 220 (unsigned long) mci, mci->mci_host, 221 (int) (mcislot - MciCache), doquit); 222 223 mci->mci_deliveries = 0; 224 if (doquit) 225 { 226 message("Closing connection to %s", mci->mci_host); 227 228 mci->mci_flags &= ~MCIF_CACHED; 229 230 /* only uses the envelope to flush the transcript file */ 231 if (mci->mci_state != MCIS_CLOSED) 232 smtpquit(mci->mci_mailer, mci, &BlankEnvelope); 233#if XLA 234 xla_host_end(mci->mci_host); 235#endif /* XLA */ 236 } 237 else 238 { 239 if (mci->mci_in != NULL) 240 (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 241 if (mci->mci_out != NULL) 242 (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 243 mci->mci_in = mci->mci_out = NULL; 244 mci->mci_state = MCIS_CLOSED; 245 mci->mci_exitstat = EX_OK; 246 mci->mci_errno = 0; 247 mci->mci_flags = 0; 248 249 mci->mci_retryrcpt = false; 250 mci->mci_tolist = NULL; 251#if PIPELINING 252 mci->mci_okrcpts = 0; 253#endif /* PIPELINING */ 254 } 255 256 SM_FREE_CLR(mci->mci_status); 257 SM_FREE_CLR(mci->mci_rstatus); 258 SM_FREE_CLR(mci->mci_heloname); 259 if (mci->mci_rpool != NULL) 260 { 261 sm_rpool_free(mci->mci_rpool); 262 mci->mci_macro.mac_rpool = NULL; 263 mci->mci_rpool = NULL; 264 } 265} 266/* 267** MCI_FLUSH -- flush the entire cache 268** 269** Parameters: 270** doquit -- if true, send QUIT protocol. 271** if false, just close the connection. 272** allbut -- but leave this one open. 273** 274** Returns: 275** none. 276*/ 277 278void 279mci_flush(doquit, allbut) 280 bool doquit; 281 MCI *allbut; 282{ 283 register int i; 284 285 if (MciCache == NULL) 286 return; 287 288 for (i = 0; i < MaxMciCache; i++) 289 { 290 if (allbut != MciCache[i]) 291 mci_uncache(&MciCache[i], doquit); 292 } 293} 294/* 295** MCI_GET -- get information about a particular host 296** 297** Parameters: 298** host -- host to look for. 299** m -- mailer. 300** 301** Returns: 302** mci for this host (might be new). 303*/ 304 305MCI * 306mci_get(host, m) 307 char *host; 308 MAILER *m; 309{ 310 register MCI *mci; 311 register STAB *s; 312 extern SOCKADDR CurHostAddr; 313 314 /* clear CurHostAddr so we don't get a bogus address with this name */ 315 memset(&CurHostAddr, '\0', sizeof CurHostAddr); 316 317 /* clear out any expired connections */ 318 (void) mci_scan(NULL); 319 320 if (m->m_mno < 0) 321 syserr("!negative mno %d (%s)", m->m_mno, m->m_name); 322 323 s = stab(host, ST_MCI + m->m_mno, ST_ENTER); 324 mci = &s->s_mci; 325 326 /* initialize per-message data */ 327 mci->mci_retryrcpt = false; 328 mci->mci_tolist = NULL; 329#if PIPELINING 330 mci->mci_okrcpts = 0; 331#endif /* PIPELINING */ 332 333 if (mci->mci_rpool == NULL) 334 mci->mci_rpool = sm_rpool_new_x(NULL); 335 336 if (mci->mci_macro.mac_rpool == NULL) 337 mci->mci_macro.mac_rpool = mci->mci_rpool; 338 339 /* 340 ** We don't need to load the persistent data if we have data 341 ** already loaded in the cache. 342 */ 343 344 if (mci->mci_host == NULL && 345 (mci->mci_host = s->s_name) != NULL && 346 !mci_load_persistent(mci)) 347 { 348 if (tTd(42, 2)) 349 sm_dprintf("mci_get(%s %s): lock failed\n", 350 host, m->m_name); 351 mci->mci_exitstat = EX_TEMPFAIL; 352 mci->mci_state = MCIS_CLOSED; 353 mci->mci_statfile = NULL; 354 return mci; 355 } 356 357 if (tTd(42, 2)) 358 { 359 sm_dprintf("mci_get(%s %s): mci_state=%d, _flags=%lx, _exitstat=%d, _errno=%d\n", 360 host, m->m_name, mci->mci_state, mci->mci_flags, 361 mci->mci_exitstat, mci->mci_errno); 362 } 363 364 if (mci->mci_state == MCIS_OPEN) 365 { 366 /* poke the connection to see if it's still alive */ 367 (void) smtpprobe(mci); 368 369 /* reset the stored state in the event of a timeout */ 370 if (mci->mci_state != MCIS_OPEN) 371 { 372 mci->mci_errno = 0; 373 mci->mci_exitstat = EX_OK; 374 mci->mci_state = MCIS_CLOSED; 375 } 376 else 377 { 378 /* get peer host address */ 379 /* (this should really be in the mci struct) */ 380 SOCKADDR_LEN_T socklen = sizeof CurHostAddr; 381 382 (void) getpeername(sm_io_getinfo(mci->mci_in, 383 SM_IO_WHAT_FD, NULL), 384 (struct sockaddr *) &CurHostAddr, &socklen); 385 } 386 } 387 if (mci->mci_state == MCIS_CLOSED) 388 { 389 time_t now = curtime(); 390 391 /* if this info is stale, ignore it */ 392 if (mci->mci_lastuse + MciInfoTimeout <= now) 393 { 394 mci->mci_lastuse = now; 395 mci->mci_errno = 0; 396 mci->mci_exitstat = EX_OK; 397 } 398 } 399 400 return mci; 401} 402 403/* 404** MCI_CLOSE -- (forcefully) close files used for a connection. 405** Note: this is a last resort, usually smtpquit() or endmailer() 406** should be used to close a connection. 407** 408** Parameters: 409** mci -- the connection to close. 410** where -- where has this been called? 411** 412** Returns: 413** none. 414*/ 415 416void 417mci_close(mci, where) 418 MCI *mci; 419 char *where; 420{ 421 bool dumped; 422 423 if (mci == NULL) 424 return; 425 dumped = false; 426 if (mci->mci_out != NULL) 427 { 428 if (tTd(56, 1)) 429 { 430 sm_dprintf("mci_close: mci_out!=NULL, where=%s\n", 431 where); 432 mci_dump(sm_debug_file(), mci, false); 433 dumped = true; 434 } 435 (void) sm_io_close(mci->mci_out, SM_TIME_DEFAULT); 436 mci->mci_out = NULL; 437 } 438 if (mci->mci_in != NULL) 439 { 440 if (tTd(56, 1)) 441 { 442 sm_dprintf("mci_close: mci_in!=NULL, where=%s\n", 443 where); 444 if (!dumped) 445 mci_dump(sm_debug_file(), mci, false); 446 } 447 (void) sm_io_close(mci->mci_in, SM_TIME_DEFAULT); 448 mci->mci_in = NULL; 449 } 450 mci->mci_state = MCIS_CLOSED; 451} 452 453/* 454** MCI_NEW -- allocate new MCI structure 455** 456** Parameters: 457** rpool -- if non-NULL: allocate from that rpool. 458** 459** Returns: 460** mci (new). 461*/ 462 463MCI * 464mci_new(rpool) 465 SM_RPOOL_T *rpool; 466{ 467 register MCI *mci; 468 469 if (rpool == NULL) 470 mci = (MCI *) sm_malloc_x(sizeof *mci); 471 else 472 mci = (MCI *) sm_rpool_malloc_x(rpool, sizeof *mci); 473 memset((char *) mci, '\0', sizeof *mci); 474 mci->mci_rpool = sm_rpool_new_x(NULL); 475 mci->mci_macro.mac_rpool = mci->mci_rpool; 476 return mci; 477} 478/* 479** MCI_MATCH -- check connection cache for a particular host 480** 481** Parameters: 482** host -- host to look for. 483** m -- mailer. 484** 485** Returns: 486** true iff open connection exists. 487*/ 488 489bool 490mci_match(host, m) 491 char *host; 492 MAILER *m; 493{ 494 register MCI *mci; 495 register STAB *s; 496 497 if (m->m_mno < 0 || m->m_mno > MAXMAILERS) 498 return false; 499 s = stab(host, ST_MCI + m->m_mno, ST_FIND); 500 if (s == NULL) 501 return false; 502 503 mci = &s->s_mci; 504 return mci->mci_state == MCIS_OPEN; 505} 506/* 507** MCI_SETSTAT -- set status codes in MCI structure. 508** 509** Parameters: 510** mci -- the MCI structure to set. 511** xstat -- the exit status code. 512** dstat -- the DSN status code. 513** rstat -- the SMTP status code. 514** 515** Returns: 516** none. 517*/ 518 519void 520mci_setstat(mci, xstat, dstat, rstat) 521 MCI *mci; 522 int xstat; 523 char *dstat; 524 char *rstat; 525{ 526 /* protocol errors should never be interpreted as sticky */ 527 if (xstat != EX_NOTSTICKY && xstat != EX_PROTOCOL) 528 mci->mci_exitstat = xstat; 529 530 SM_FREE_CLR(mci->mci_status); 531 if (dstat != NULL) 532 mci->mci_status = sm_strdup_x(dstat); 533 534 SM_FREE_CLR(mci->mci_rstatus); 535 if (rstat != NULL) 536 mci->mci_rstatus = sm_strdup_x(rstat); 537} 538/* 539** MCI_DUMP -- dump the contents of an MCI structure. 540** 541** Parameters: 542** fp -- output file pointer 543** mci -- the MCI structure to dump. 544** 545** Returns: 546** none. 547** 548** Side Effects: 549** none. 550*/ 551 552struct mcifbits 553{ 554 int mcif_bit; /* flag bit */ 555 char *mcif_name; /* flag name */ 556}; 557static struct mcifbits MciFlags[] = 558{ 559 { MCIF_VALID, "VALID" }, 560 { MCIF_CACHED, "CACHED" }, 561 { MCIF_ESMTP, "ESMTP" }, 562 { MCIF_EXPN, "EXPN" }, 563 { MCIF_SIZE, "SIZE" }, 564 { MCIF_8BITMIME, "8BITMIME" }, 565 { MCIF_7BIT, "7BIT" }, 566 { MCIF_INHEADER, "INHEADER" }, 567 { MCIF_CVT8TO7, "CVT8TO7" }, 568 { MCIF_DSN, "DSN" }, 569 { MCIF_8BITOK, "8BITOK" }, 570 { MCIF_CVT7TO8, "CVT7TO8" }, 571 { MCIF_INMIME, "INMIME" }, 572 { MCIF_AUTH, "AUTH" }, 573 { MCIF_AUTHACT, "AUTHACT" }, 574 { MCIF_ENHSTAT, "ENHSTAT" }, 575 { MCIF_PIPELINED, "PIPELINED" }, 576#if STARTTLS 577 { MCIF_TLS, "TLS" }, 578 { MCIF_TLSACT, "TLSACT" }, 579#endif /* STARTTLS */ 580 { MCIF_DLVR_BY, "DLVR_BY" }, 581 { 0, NULL } 582}; 583 584void 585mci_dump(fp, mci, logit) 586 SM_FILE_T *fp; 587 register MCI *mci; 588 bool logit; 589{ 590 register char *p; 591 char *sep; 592 char buf[4000]; 593 594 sep = logit ? " " : "\n\t"; 595 p = buf; 596 (void) sm_snprintf(p, SPACELEFT(buf, p), "MCI@%p: ", mci); 597 p += strlen(p); 598 if (mci == NULL) 599 { 600 (void) sm_snprintf(p, SPACELEFT(buf, p), "NULL"); 601 goto printit; 602 } 603 (void) sm_snprintf(p, SPACELEFT(buf, p), "flags=%lx", mci->mci_flags); 604 p += strlen(p); 605 606 /* 607 ** The following check is just for paranoia. It protects the 608 ** assignment in the if() clause. If there's not some minimum 609 ** amount of space we can stop right now. The check will not 610 ** trigger as long as sizeof(buf)=4000. 611 */ 612 613 if (p >= buf + sizeof(buf) - 4) 614 goto printit; 615 if (mci->mci_flags != 0) 616 { 617 struct mcifbits *f; 618 619 *p++ = '<'; /* protected above */ 620 for (f = MciFlags; f->mcif_bit != 0; f++) 621 { 622 if (!bitset(f->mcif_bit, mci->mci_flags)) 623 continue; 624 (void) sm_strlcpyn(p, SPACELEFT(buf, p), 2, 625 f->mcif_name, ","); 626 p += strlen(p); 627 } 628 p[-1] = '>'; 629 } 630 631 /* Note: sm_snprintf() takes care of NULL arguments for %s */ 632 (void) sm_snprintf(p, SPACELEFT(buf, p), 633 ",%serrno=%d, herrno=%d, exitstat=%d, state=%d, pid=%d,%s", 634 sep, mci->mci_errno, mci->mci_herrno, 635 mci->mci_exitstat, mci->mci_state, (int) mci->mci_pid, sep); 636 p += strlen(p); 637 (void) sm_snprintf(p, SPACELEFT(buf, p), 638 "maxsize=%ld, phase=%s, mailer=%s,%s", 639 mci->mci_maxsize, mci->mci_phase, 640 mci->mci_mailer == NULL ? "NULL" : mci->mci_mailer->m_name, 641 sep); 642 p += strlen(p); 643 (void) sm_snprintf(p, SPACELEFT(buf, p), 644 "status=%s, rstatus=%s,%s", 645 mci->mci_status, mci->mci_rstatus, sep); 646 p += strlen(p); 647 (void) sm_snprintf(p, SPACELEFT(buf, p), 648 "host=%s, lastuse=%s", 649 mci->mci_host, ctime(&mci->mci_lastuse)); 650printit: 651 if (logit) 652 sm_syslog(LOG_DEBUG, CurEnv->e_id, "%.1000s", buf); 653 else 654 (void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s\n", buf); 655} 656/* 657** MCI_DUMP_ALL -- print the entire MCI cache 658** 659** Parameters: 660** fp -- output file pointer 661** logit -- if set, log the result instead of printing 662** to stdout. 663** 664** Returns: 665** none. 666*/ 667 668void 669mci_dump_all(fp, logit) 670 SM_FILE_T *fp; 671 bool logit; 672{ 673 register int i; 674 675 if (MciCache == NULL) 676 return; 677 678 for (i = 0; i < MaxMciCache; i++) 679 mci_dump(fp, MciCache[i], logit); 680} 681/* 682** MCI_LOCK_HOST -- Lock host while sending. 683** 684** If we are contacting a host, we'll need to 685** update the status information in the host status 686** file, and if we want to do that, we ought to have 687** locked it. This has the (according to some) 688** desirable effect of serializing connectivity with 689** remote hosts -- i.e.: one connection to a given 690** host at a time. 691** 692** Parameters: 693** mci -- containing the host we want to lock. 694** 695** Returns: 696** EX_OK -- got the lock. 697** EX_TEMPFAIL -- didn't get the lock. 698*/ 699 700int 701mci_lock_host(mci) 702 MCI *mci; 703{ 704 if (mci == NULL) 705 { 706 if (tTd(56, 1)) 707 sm_dprintf("mci_lock_host: NULL mci\n"); 708 return EX_OK; 709 } 710 711 if (!SingleThreadDelivery) 712 return EX_OK; 713 714 return mci_lock_host_statfile(mci); 715} 716 717static int 718mci_lock_host_statfile(mci) 719 MCI *mci; 720{ 721 int save_errno = errno; 722 int retVal = EX_OK; 723 char fname[MAXPATHLEN]; 724 725 if (HostStatDir == NULL || mci->mci_host == NULL) 726 return EX_OK; 727 728 if (tTd(56, 2)) 729 sm_dprintf("mci_lock_host: attempting to lock %s\n", 730 mci->mci_host); 731 732 if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, 733 true) < 0) 734 { 735 /* of course this should never happen */ 736 if (tTd(56, 2)) 737 sm_dprintf("mci_lock_host: Failed to generate host path for %s\n", 738 mci->mci_host); 739 740 retVal = EX_TEMPFAIL; 741 goto cleanup; 742 } 743 744 mci->mci_statfile = safefopen(fname, O_RDWR, FileMode, 745 SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH|SFF_CREAT); 746 747 if (mci->mci_statfile == NULL) 748 { 749 syserr("mci_lock_host: cannot create host lock file %s", fname); 750 goto cleanup; 751 } 752 753 if (!lockfile(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 754 fname, "", LOCK_EX|LOCK_NB)) 755 { 756 if (tTd(56, 2)) 757 sm_dprintf("mci_lock_host: couldn't get lock on %s\n", 758 fname); 759 (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 760 mci->mci_statfile = NULL; 761 retVal = EX_TEMPFAIL; 762 goto cleanup; 763 } 764 765 if (tTd(56, 12) && mci->mci_statfile != NULL) 766 sm_dprintf("mci_lock_host: Sanity check -- lock is good\n"); 767 768cleanup: 769 errno = save_errno; 770 return retVal; 771} 772/* 773** MCI_UNLOCK_HOST -- unlock host 774** 775** Clean up the lock on a host, close the file, let 776** someone else use it. 777** 778** Parameters: 779** mci -- us. 780** 781** Returns: 782** nothing. 783*/ 784 785void 786mci_unlock_host(mci) 787 MCI *mci; 788{ 789 int save_errno = errno; 790 791 if (mci == NULL) 792 { 793 if (tTd(56, 1)) 794 sm_dprintf("mci_unlock_host: NULL mci\n"); 795 return; 796 } 797 798 if (HostStatDir == NULL || mci->mci_host == NULL) 799 return; 800 801 if (!SingleThreadDelivery && mci_lock_host_statfile(mci) == EX_TEMPFAIL) 802 { 803 if (tTd(56, 1)) 804 sm_dprintf("mci_unlock_host: stat file already locked\n"); 805 } 806 else 807 { 808 if (tTd(56, 2)) 809 sm_dprintf("mci_unlock_host: store prior to unlock\n"); 810 mci_store_persistent(mci); 811 } 812 813 if (mci->mci_statfile != NULL) 814 { 815 (void) sm_io_close(mci->mci_statfile, SM_TIME_DEFAULT); 816 mci->mci_statfile = NULL; 817 } 818 819 errno = save_errno; 820} 821/* 822** MCI_LOAD_PERSISTENT -- load persistent host info 823** 824** Load information about host that is kept 825** in common for all running sendmails. 826** 827** Parameters: 828** mci -- the host/connection to load persistent info for. 829** 830** Returns: 831** true -- lock was successful 832** false -- lock failed 833*/ 834 835static bool 836mci_load_persistent(mci) 837 MCI *mci; 838{ 839 int save_errno = errno; 840 bool locked = true; 841 SM_FILE_T *fp; 842 char fname[MAXPATHLEN]; 843 844 if (mci == NULL) 845 { 846 if (tTd(56, 1)) 847 sm_dprintf("mci_load_persistent: NULL mci\n"); 848 return true; 849 } 850 851 if (IgnoreHostStatus || HostStatDir == NULL || mci->mci_host == NULL) 852 return true; 853 854 /* Already have the persistent information in memory */ 855 if (SingleThreadDelivery && mci->mci_statfile != NULL) 856 return true; 857 858 if (tTd(56, 1)) 859 sm_dprintf("mci_load_persistent: Attempting to load persistent information for %s\n", 860 mci->mci_host); 861 862 if (mci_generate_persistent_path(mci->mci_host, fname, sizeof fname, 863 false) < 0) 864 { 865 /* Not much we can do if the file isn't there... */ 866 if (tTd(56, 1)) 867 sm_dprintf("mci_load_persistent: Couldn't generate host path\n"); 868 goto cleanup; 869 } 870 871 fp = safefopen(fname, O_RDONLY, FileMode, 872 SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 873 if (fp == NULL) 874 { 875 /* I can't think of any reason this should ever happen */ 876 if (tTd(56, 1)) 877 sm_dprintf("mci_load_persistent: open(%s): %s\n", 878 fname, sm_errstring(errno)); 879 goto cleanup; 880 } 881 882 FileName = fname; 883 locked = lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, "", 884 LOCK_SH|LOCK_NB); 885 if (locked) 886 { 887 (void) mci_read_persistent(fp, mci); 888 (void) lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), fname, 889 "", LOCK_UN); 890 } 891 FileName = NULL; 892 (void) sm_io_close(fp, SM_TIME_DEFAULT); 893 894cleanup: 895 errno = save_errno; 896 return locked; 897} 898/* 899** MCI_READ_PERSISTENT -- read persistent host status file 900** 901** Parameters: 902** fp -- the file pointer to read. 903** mci -- the pointer to fill in. 904** 905** Returns: 906** -1 -- if the file was corrupt. 907** 0 -- otherwise. 908** 909** Warning: 910** This code makes the assumption that this data 911** will be read in an atomic fashion, and that the data 912** was written in an atomic fashion. Any other functioning 913** may lead to some form of insanity. This should be 914** perfectly safe due to underlying stdio buffering. 915*/ 916 917static int 918mci_read_persistent(fp, mci) 919 SM_FILE_T *fp; 920 register MCI *mci; 921{ 922 int ver; 923 register char *p; 924 int saveLineNumber = LineNumber; 925 char buf[MAXLINE]; 926 927 if (fp == NULL) 928 syserr("mci_read_persistent: NULL fp"); 929 if (mci == NULL) 930 syserr("mci_read_persistent: NULL mci"); 931 if (tTd(56, 93)) 932 { 933 sm_dprintf("mci_read_persistent: fp=%lx, mci=", 934 (unsigned long) fp); 935 } 936 937 SM_FREE_CLR(mci->mci_status); 938 SM_FREE_CLR(mci->mci_rstatus); 939 940 sm_io_rewind(fp, SM_TIME_DEFAULT); 941 ver = -1; 942 LineNumber = 0; 943 while (sm_io_fgets(fp, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) 944 { 945 LineNumber++; 946 p = strchr(buf, '\n'); 947 if (p != NULL) 948 *p = '\0'; 949 switch (buf[0]) 950 { 951 case 'V': /* version stamp */ 952 ver = atoi(&buf[1]); 953 if (ver < 0 || ver > 0) 954 syserr("Unknown host status version %d: %d max", 955 ver, 0); 956 break; 957 958 case 'E': /* UNIX error number */ 959 mci->mci_errno = atoi(&buf[1]); 960 break; 961 962 case 'H': /* DNS error number */ 963 mci->mci_herrno = atoi(&buf[1]); 964 break; 965 966 case 'S': /* UNIX exit status */ 967 mci->mci_exitstat = atoi(&buf[1]); 968 break; 969 970 case 'D': /* DSN status */ 971 mci->mci_status = newstr(&buf[1]); 972 break; 973 974 case 'R': /* SMTP status */ 975 mci->mci_rstatus = newstr(&buf[1]); 976 break; 977 978 case 'U': /* last usage time */ 979 mci->mci_lastuse = atol(&buf[1]); 980 break; 981 982 case '.': /* end of file */ 983 if (tTd(56, 93)) 984 mci_dump(sm_debug_file(), mci, false); 985 return 0; 986 987 default: 988 sm_syslog(LOG_CRIT, NOQID, 989 "%s: line %d: Unknown host status line \"%s\"", 990 FileName == NULL ? mci->mci_host : FileName, 991 LineNumber, buf); 992 LineNumber = saveLineNumber; 993 return -1; 994 } 995 } 996 LineNumber = saveLineNumber; 997 if (tTd(56, 93)) 998 sm_dprintf("incomplete (missing dot for EOF)\n"); 999 if (ver < 0) 1000 return -1; 1001 return 0; 1002} 1003/* 1004** MCI_STORE_PERSISTENT -- Store persistent MCI information 1005** 1006** Store information about host that is kept 1007** in common for all running sendmails. 1008** 1009** Parameters: 1010** mci -- the host/connection to store persistent info for. 1011** 1012** Returns: 1013** none. 1014*/ 1015 1016void 1017mci_store_persistent(mci) 1018 MCI *mci; 1019{ 1020 int save_errno = errno; 1021 1022 if (mci == NULL) 1023 { 1024 if (tTd(56, 1)) 1025 sm_dprintf("mci_store_persistent: NULL mci\n"); 1026 return; 1027 } 1028 1029 if (HostStatDir == NULL || mci->mci_host == NULL) 1030 return; 1031 1032 if (tTd(56, 1)) 1033 sm_dprintf("mci_store_persistent: Storing information for %s\n", 1034 mci->mci_host); 1035 1036 if (mci->mci_statfile == NULL) 1037 { 1038 if (tTd(56, 1)) 1039 sm_dprintf("mci_store_persistent: no statfile\n"); 1040 return; 1041 } 1042 1043 sm_io_rewind(mci->mci_statfile, SM_TIME_DEFAULT); 1044#if !NOFTRUNCATE 1045 (void) ftruncate(sm_io_getinfo(mci->mci_statfile, SM_IO_WHAT_FD, NULL), 1046 (off_t) 0); 1047#endif /* !NOFTRUNCATE */ 1048 1049 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "V0\n"); 1050 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "E%d\n", 1051 mci->mci_errno); 1052 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "H%d\n", 1053 mci->mci_herrno); 1054 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "S%d\n", 1055 mci->mci_exitstat); 1056 if (mci->mci_status != NULL) 1057 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 1058 "D%.80s\n", 1059 denlstring(mci->mci_status, true, false)); 1060 if (mci->mci_rstatus != NULL) 1061 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, 1062 "R%.80s\n", 1063 denlstring(mci->mci_rstatus, true, false)); 1064 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, "U%ld\n", 1065 (long)(mci->mci_lastuse)); 1066 (void) sm_io_fprintf(mci->mci_statfile, SM_TIME_DEFAULT, ".\n"); 1067 1068 (void) sm_io_flush(mci->mci_statfile, SM_TIME_DEFAULT); 1069 1070 errno = save_errno; 1071 return; 1072} 1073/* 1074** MCI_TRAVERSE_PERSISTENT -- walk persistent status tree 1075** 1076** Recursively find all the mci host files in `pathname'. Default to 1077** main host status directory if no path is provided. 1078** Call (*action)(pathname, host) for each file found. 1079** 1080** Note: all information is collected in a list before it is processed. 1081** This may not be the best way to do it, but it seems safest, since 1082** the file system would be touched while we are attempting to traverse 1083** the directory tree otherwise (during purges). 1084** 1085** Parameters: 1086** action -- function to call on each node. If returns < 0, 1087** return immediately. 1088** pathname -- root of tree. If null, use main host status 1089** directory. 1090** 1091** Returns: 1092** < 0 -- if any action routine returns a negative value, that 1093** value is returned. 1094** 0 -- if we successfully went to completion. 1095** > 0 -- return status from action() 1096*/ 1097 1098int 1099mci_traverse_persistent(action, pathname) 1100 int (*action)__P((char *, char *)); 1101 char *pathname; 1102{ 1103 struct stat statbuf; 1104 DIR *d; 1105 int ret; 1106 1107 if (pathname == NULL) 1108 pathname = HostStatDir; 1109 if (pathname == NULL) 1110 return -1; 1111 1112 if (tTd(56, 1)) 1113 sm_dprintf("mci_traverse: pathname is %s\n", pathname); 1114 1115 ret = stat(pathname, &statbuf); 1116 if (ret < 0) 1117 { 1118 if (tTd(56, 2)) 1119 sm_dprintf("mci_traverse: Failed to stat %s: %s\n", 1120 pathname, sm_errstring(errno)); 1121 return ret; 1122 } 1123 if (S_ISDIR(statbuf.st_mode)) 1124 { 1125 bool leftone, removedone; 1126 size_t len; 1127 char *newptr; 1128 struct dirent *e; 1129 char newpath[MAXPATHLEN]; 1130 1131 if ((d = opendir(pathname)) == NULL) 1132 { 1133 if (tTd(56, 2)) 1134 sm_dprintf("mci_traverse: opendir %s: %s\n", 1135 pathname, sm_errstring(errno)); 1136 return -1; 1137 } 1138 len = sizeof(newpath) - MAXNAMLEN - 3; 1139 if (sm_strlcpy(newpath, pathname, len) >= len) 1140 { 1141 if (tTd(56, 2)) 1142 sm_dprintf("mci_traverse: path \"%s\" too long", 1143 pathname); 1144 return -1; 1145 } 1146 newptr = newpath + strlen(newpath); 1147 *newptr++ = '/'; 1148 1149 /* 1150 ** repeat until no file has been removed 1151 ** this may become ugly when several files "expire" 1152 ** during these loops, but it's better than doing 1153 ** a rewinddir() inside the inner loop 1154 */ 1155 1156 do 1157 { 1158 leftone = removedone = false; 1159 while ((e = readdir(d)) != NULL) 1160 { 1161 if (e->d_name[0] == '.') 1162 continue; 1163 1164 (void) sm_strlcpy(newptr, e->d_name, 1165 sizeof newpath - 1166 (newptr - newpath)); 1167 1168 if (StopRequest) 1169 stop_sendmail(); 1170 ret = mci_traverse_persistent(action, newpath); 1171 if (ret < 0) 1172 break; 1173 if (ret == 1) 1174 leftone = true; 1175 if (!removedone && ret == 0 && 1176 action == mci_purge_persistent) 1177 removedone = true; 1178 } 1179 if (ret < 0) 1180 break; 1181 1182 /* 1183 ** The following appears to be 1184 ** necessary during purges, since 1185 ** we modify the directory structure 1186 */ 1187 1188 if (removedone) 1189 rewinddir(d); 1190 if (tTd(56, 40)) 1191 sm_dprintf("mci_traverse: path %s: ret %d removed %d left %d\n", 1192 pathname, ret, removedone, leftone); 1193 } while (removedone); 1194 1195 /* purge (or whatever) the directory proper */ 1196 if (!leftone) 1197 { 1198 *--newptr = '\0'; 1199 ret = (*action)(newpath, NULL); 1200 } 1201 (void) closedir(d); 1202 } 1203 else if (S_ISREG(statbuf.st_mode)) 1204 { 1205 char *end = pathname + strlen(pathname) - 1; 1206 char *start; 1207 char *scan; 1208 char host[MAXHOSTNAMELEN]; 1209 char *hostptr = host; 1210 1211 /* 1212 ** Reconstruct the host name from the path to the 1213 ** persistent information. 1214 */ 1215 1216 do 1217 { 1218 if (hostptr != host) 1219 *(hostptr++) = '.'; 1220 start = end; 1221 while (start > pathname && *(start - 1) != '/') 1222 start--; 1223 1224 if (*end == '.') 1225 end--; 1226 1227 for (scan = start; scan <= end; scan++) 1228 *(hostptr++) = *scan; 1229 1230 end = start - 2; 1231 } while (end > pathname && *end == '.'); 1232 1233 *hostptr = '\0'; 1234 1235 /* 1236 ** Do something with the file containing the persistent 1237 ** information. 1238 */ 1239 1240 ret = (*action)(pathname, host); 1241 } 1242 1243 return ret; 1244} 1245/* 1246** MCI_PRINT_PERSISTENT -- print persistent info 1247** 1248** Dump the persistent information in the file 'pathname' 1249** 1250** Parameters: 1251** pathname -- the pathname to the status file. 1252** hostname -- the corresponding host name. 1253** 1254** Returns: 1255** 0 1256*/ 1257 1258int 1259mci_print_persistent(pathname, hostname) 1260 char *pathname; 1261 char *hostname; 1262{ 1263 static bool initflag = false; 1264 SM_FILE_T *fp; 1265 int width = Verbose ? 78 : 25; 1266 bool locked; 1267 MCI mcib; 1268 1269 /* skip directories */ 1270 if (hostname == NULL) 1271 return 0; 1272 1273 if (StopRequest) 1274 stop_sendmail(); 1275 1276 if (!initflag) 1277 { 1278 initflag = true; 1279 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 1280 " -------------- Hostname --------------- How long ago ---------Results---------\n"); 1281 } 1282 1283 fp = safefopen(pathname, O_RDONLY, FileMode, 1284 SFF_NOLOCK|SFF_NOLINK|SFF_OPENASROOT|SFF_REGONLY|SFF_SAFEDIRPATH); 1285 1286 if (fp == NULL) 1287 { 1288 if (tTd(56, 1)) 1289 sm_dprintf("mci_print_persistent: cannot open %s: %s\n", 1290 pathname, sm_errstring(errno)); 1291 return 0; 1292 } 1293 1294 FileName = pathname; 1295 memset(&mcib, '\0', sizeof mcib); 1296 if (mci_read_persistent(fp, &mcib) < 0) 1297 { 1298 syserr("%s: could not read status file", pathname); 1299 (void) sm_io_close(fp, SM_TIME_DEFAULT); 1300 FileName = NULL; 1301 return 0; 1302 } 1303 1304 locked = !lockfile(sm_io_getinfo(fp, SM_IO_WHAT_FD, NULL), pathname, 1305 "", LOCK_SH|LOCK_NB); 1306 (void) sm_io_close(fp, SM_TIME_DEFAULT); 1307 FileName = NULL; 1308 1309 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%c%-39s %12s ", 1310 locked ? '*' : ' ', hostname, 1311 pintvl(curtime() - mcib.mci_lastuse, true)); 1312 if (mcib.mci_rstatus != NULL) 1313 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", width, 1314 mcib.mci_rstatus); 1315 else if (mcib.mci_exitstat == EX_TEMPFAIL && mcib.mci_errno != 0) 1316 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 1317 "Deferred: %.*s\n", width - 10, 1318 sm_errstring(mcib.mci_errno)); 1319 else if (mcib.mci_exitstat != 0) 1320 { 1321 char *exmsg = sm_sysexmsg(mcib.mci_exitstat); 1322 1323 if (exmsg == NULL) 1324 { 1325 char buf[80]; 1326 1327 (void) sm_snprintf(buf, sizeof buf, 1328 "Unknown mailer error %d", 1329 mcib.mci_exitstat); 1330 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 1331 width, buf); 1332 } 1333 else 1334 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%.*s\n", 1335 width, &exmsg[5]); 1336 } 1337 else if (mcib.mci_errno == 0) 1338 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK\n"); 1339 else 1340 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "OK: %.*s\n", 1341 width - 4, sm_errstring(mcib.mci_errno)); 1342 1343 return 0; 1344} 1345/* 1346** MCI_PURGE_PERSISTENT -- Remove a persistence status file. 1347** 1348** Parameters: 1349** pathname -- path to the status file. 1350** hostname -- name of host corresponding to that file. 1351** NULL if this is a directory (domain). 1352** 1353** Returns: 1354** 0 -- ok 1355** 1 -- file not deleted (too young, incorrect format) 1356** < 0 -- some error occurred 1357*/ 1358 1359int 1360mci_purge_persistent(pathname, hostname) 1361 char *pathname; 1362 char *hostname; 1363{ 1364 struct stat statbuf; 1365 char *end = pathname + strlen(pathname) - 1; 1366 int ret; 1367 1368 if (tTd(56, 1)) 1369 sm_dprintf("mci_purge_persistent: purging %s\n", pathname); 1370 1371 ret = stat(pathname, &statbuf); 1372 if (ret < 0) 1373 { 1374 if (tTd(56, 2)) 1375 sm_dprintf("mci_purge_persistent: Failed to stat %s: %s\n", 1376 pathname, sm_errstring(errno)); 1377 return ret; 1378 } 1379 if (curtime() - statbuf.st_mtime <= MciInfoTimeout) 1380 return 1; 1381 if (hostname != NULL) 1382 { 1383 /* remove the file */ 1384 ret = unlink(pathname); 1385 if (ret < 0) 1386 { 1387 if (LogLevel > 8) 1388 sm_syslog(LOG_ERR, NOQID, 1389 "mci_purge_persistent: failed to unlink %s: %s", 1390 pathname, sm_errstring(errno)); 1391 if (tTd(56, 2)) 1392 sm_dprintf("mci_purge_persistent: failed to unlink %s: %s\n", 1393 pathname, sm_errstring(errno)); 1394 return ret; 1395 } 1396 } 1397 else 1398 { 1399 /* remove the directory */ 1400 if (*end != '.') 1401 return 1; 1402 1403 if (tTd(56, 1)) 1404 sm_dprintf("mci_purge_persistent: dpurge %s\n", pathname); 1405 1406 ret = rmdir(pathname); 1407 if (ret < 0) 1408 { 1409 if (tTd(56, 2)) 1410 sm_dprintf("mci_purge_persistent: rmdir %s: %s\n", 1411 pathname, sm_errstring(errno)); 1412 return ret; 1413 } 1414 } 1415 1416 return 0; 1417} 1418/* 1419** MCI_GENERATE_PERSISTENT_PATH -- generate path from hostname 1420** 1421** Given `host', convert from a.b.c to $HostStatDir/c./b./a, 1422** putting the result into `path'. if `createflag' is set, intervening 1423** directories will be created as needed. 1424** 1425** Parameters: 1426** host -- host name to convert from. 1427** path -- place to store result. 1428** pathlen -- length of path buffer. 1429** createflag -- if set, create intervening directories as 1430** needed. 1431** 1432** Returns: 1433** 0 -- success 1434** -1 -- failure 1435*/ 1436 1437static int 1438mci_generate_persistent_path(host, path, pathlen, createflag) 1439 const char *host; 1440 char *path; 1441 int pathlen; 1442 bool createflag; 1443{ 1444 char *elem, *p, *x, ch; 1445 int ret = 0; 1446 int len; 1447 char t_host[MAXHOSTNAMELEN]; 1448#if NETINET6 1449 struct in6_addr in6_addr; 1450#endif /* NETINET6 */ 1451 1452 /* 1453 ** Rationality check the arguments. 1454 */ 1455 1456 if (host == NULL) 1457 { 1458 syserr("mci_generate_persistent_path: null host"); 1459 return -1; 1460 } 1461 if (path == NULL) 1462 { 1463 syserr("mci_generate_persistent_path: null path"); 1464 return -1; 1465 } 1466 1467 if (tTd(56, 80)) 1468 sm_dprintf("mci_generate_persistent_path(%s): ", host); 1469 1470 if (*host == '\0' || *host == '.') 1471 return -1; 1472 1473 /* make certain this is not a bracketed host number */ 1474 if (strlen(host) > sizeof t_host - 1) 1475 return -1; 1476 if (host[0] == '[') 1477 (void) sm_strlcpy(t_host, host + 1, sizeof t_host); 1478 else 1479 (void) sm_strlcpy(t_host, host, sizeof t_host); 1480 1481 /* 1482 ** Delete any trailing dots from the hostname. 1483 ** Leave 'elem' pointing at the \0. 1484 */ 1485 1486 elem = t_host + strlen(t_host); 1487 while (elem > t_host && 1488 (elem[-1] == '.' || (host[0] == '[' && elem[-1] == ']'))) 1489 *--elem = '\0'; 1490 1491 /* check for bogus bracketed address */ 1492 if (host[0] == '[') 1493 { 1494 bool good = false; 1495# if NETINET6 1496 if (anynet_pton(AF_INET6, t_host, &in6_addr) == 1) 1497 good = true; 1498# endif /* NETINET6 */ 1499# if NETINET 1500 if (inet_addr(t_host) != INADDR_NONE) 1501 good = true; 1502# endif /* NETINET */ 1503 if (!good) 1504 return -1; 1505 } 1506 1507 /* check for what will be the final length of the path */ 1508 len = strlen(HostStatDir) + 2; 1509 for (p = (char *) t_host; *p != '\0'; p++) 1510 { 1511 if (*p == '.') 1512 len++; 1513 len++; 1514 if (p[0] == '.' && p[1] == '.') 1515 return -1; 1516 } 1517 if (len > pathlen || len < 1) 1518 return -1; 1519 (void) sm_strlcpy(path, HostStatDir, pathlen); 1520 p = path + strlen(path); 1521 while (elem > t_host) 1522 { 1523 if (!path_is_dir(path, createflag)) 1524 { 1525 ret = -1; 1526 break; 1527 } 1528 elem--; 1529 while (elem >= t_host && *elem != '.') 1530 elem--; 1531 *p++ = '/'; 1532 x = elem + 1; 1533 while ((ch = *x++) != '\0' && ch != '.') 1534 { 1535 if (isascii(ch) && isupper(ch)) 1536 ch = tolower(ch); 1537 if (ch == '/') 1538 ch = ':'; /* / -> : */ 1539 *p++ = ch; 1540 } 1541 if (elem >= t_host) 1542 *p++ = '.'; 1543 *p = '\0'; 1544 } 1545 if (tTd(56, 80)) 1546 { 1547 if (ret < 0) 1548 sm_dprintf("FAILURE %d\n", ret); 1549 else 1550 sm_dprintf("SUCCESS %s\n", path); 1551 } 1552 return ret; 1553} 1554