1 /* 2 * Copyright (c) 1998-2002, 2004, 2008, 2020 Proofpoint, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1992 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14 #include <sm/gen.h> 15 16 SM_IDSTR(copyright, 17 "@(#) Copyright (c) 1998-2002, 2004 Proofpoint, Inc. and its suppliers.\n\ 18 All rights reserved.\n\ 19 Copyright (c) 1992 Eric P. Allman. All rights reserved.\n\ 20 Copyright (c) 1992, 1993\n\ 21 The Regents of the University of California. All rights reserved.\n") 22 23 SM_IDSTR(id, "@(#)$Id: makemap.c,v 8.183 2013-11-22 20:51:52 ca Exp $") 24 25 26 #include <sys/types.h> 27 #ifndef ISC_UNIX 28 # include <sys/file.h> 29 #endif 30 #include <ctype.h> 31 #include <stdlib.h> 32 #include <unistd.h> 33 #ifdef EX_OK 34 # undef EX_OK /* unistd.h may have another use for this */ 35 #endif 36 #include <sysexits.h> 37 #include <sendmail/sendmail.h> 38 #include <sm/path.h> 39 #include <sendmail/pathnames.h> 40 #include <libsmdb/smdb.h> 41 #if USE_EAI 42 # include <sm/ixlen.h> 43 #endif 44 45 uid_t RealUid; 46 gid_t RealGid; 47 char *RealUserName; 48 uid_t RunAsUid; 49 gid_t RunAsGid; 50 char *RunAsUserName; 51 int Verbose = 2; 52 bool DontInitGroups = false; 53 uid_t TrustedUid = 0; 54 BITMAP256 DontBlameSendmail; 55 56 static bool verbose = false; 57 static int exitstat; 58 59 #define BUFSIZE 1024 60 #define ISASCII(c) isascii((unsigned char)(c)) 61 #define ISSPACE(c) (ISASCII(c) && isspace(c)) 62 #define ISSEP(c) (sep == '\0' ? ISASCII(c) && isspace(c) : (c) == sep) 63 64 static void usage __P((const char *)); 65 static char *readcf __P((const char *, char *, bool)); 66 static void db_put __P((SMDB_DATABASE *, SMDB_DBENT, SMDB_DBENT, int, const char *, int, const char *)); 67 68 static void 69 usage(progname) 70 const char *progname; 71 { 72 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 73 "Usage: %s [-C cffile] [-N] [-c cachesize] [-D commentchar]\n", 74 progname); 75 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 76 " %*s [-d] [-e] [-f] [-i type] [-l] [-o] [-r] [-s] [-t delimiter]\n", 77 (int) strlen(progname), ""); 78 #if _FFR_TESTS 79 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 80 " %*s [-S n]\n", 81 (int) strlen(progname), ""); 82 #endif 83 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 84 " %*s [-u] [-v] type mapname\n", 85 (int) strlen(progname), ""); 86 exit(EX_USAGE); 87 } 88 89 /* 90 ** DB_PUT -- do the DB insert 91 ** 92 ** Parameters: 93 ** database -- DB to use 94 ** db_key -- key 95 ** db_val -- value 96 ** putflags -- flags for smdb_put() 97 ** mapname -- name of map (for error reporting) 98 ** lineno -- line number (for error reporting) 99 ** progname -- name of program (for error reporting) 100 ** 101 ** Returns: 102 ** none. 103 ** 104 ** Side effects: 105 ** Sets exitstat so makemap exits with error if put fails 106 */ 107 108 static void 109 db_put(database, db_key, db_val, putflags, mapname, lineno, progname) 110 SMDB_DATABASE *database; 111 SMDB_DBENT db_key, db_val; 112 int putflags; 113 const char *mapname; 114 int lineno; 115 const char *progname; 116 { 117 int errcode; 118 119 if (verbose) 120 { 121 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 122 "key=`%s', val=`%s'\n", 123 (char *) db_key.data, 124 (char *) db_val.data); 125 } 126 127 errcode = database->smdb_put(database, &db_key, &db_val, putflags); 128 if (0 == errcode) 129 return; 130 131 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s: %s: ", 132 progname, mapname); 133 if (lineno >= 0) 134 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "line %u: ", 135 lineno); 136 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "key %s: ", 137 (char *) db_key.data); 138 if (SMDBE_KEY_EXIST == errcode) 139 { 140 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 141 "duplicate key\n"); 142 exitstat = EX_DATAERR; 143 } 144 else 145 { 146 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 147 "put error: %s\n", sm_errstring(errcode)); 148 exitstat = EX_IOERR; 149 } 150 } 151 152 /* 153 ** READCF -- read some settings from configuration file. 154 ** 155 ** Parameters: 156 ** cfile -- configuration file name. 157 ** mapfile -- file name of map to look up (if not NULL/empty) 158 ** Note: this finds the first match, so in case someone 159 ** uses the same map file for different maps, they are 160 ** hopefully using the same map type. 161 ** fullpath -- compare the full paths or just the "basename"s? 162 ** (even excluding any .ext !) 163 ** 164 ** Returns: 165 ** pointer to map class name (static!) 166 */ 167 168 static char * 169 readcf(cfile, mapfile, fullpath) 170 const char *cfile; 171 char *mapfile; 172 bool fullpath; 173 { 174 SM_FILE_T *cfp; 175 char buf[MAXLINE]; 176 static char classbuf[MAXLINE]; 177 char *classname, *mapname; 178 char *p; 179 180 if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, 181 SM_IO_RDONLY, NULL)) == NULL) 182 { 183 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 184 "makemap: %s: %s\n", 185 cfile, sm_errstring(errno)); 186 exit(EX_NOINPUT); 187 } 188 classname = NULL; 189 classbuf[0] = '\0'; 190 191 mapname = mapfile; 192 if (!fullpath && mapfile != NULL) 193 { 194 p = strrchr(mapfile, '/'); 195 if (p != NULL) 196 mapfile = ++p; 197 mapname = strdup(mapfile); 198 if (NULL == mapname) 199 { 200 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 201 "makemap: strdup(%s) failed: %s\n", 202 mapfile, sm_errstring(errno)); 203 exit(EX_OSERR); 204 } 205 if ((p = strchr(mapname, '.')) != NULL) 206 *p = '\0'; 207 } 208 209 while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0) 210 { 211 char *b; 212 213 if ((b = strchr(buf, '\n')) != NULL) 214 *b = '\0'; 215 216 b = buf; 217 switch (*b++) 218 { 219 case 'O': /* option */ 220 #if HASFCHOWN 221 if (strncasecmp(b, " TrustedUser", 12) == 0 && 222 !(ISASCII(b[12]) && isalnum(b[12]))) 223 { 224 b = strchr(b, '='); 225 if (b == NULL) 226 continue; 227 while (ISASCII(*++b) && isspace(*b)) 228 continue; 229 if (ISASCII(*b) && isdigit(*b)) 230 TrustedUid = atoi(b); 231 else 232 { 233 struct passwd *pw; 234 235 TrustedUid = 0; 236 pw = getpwnam(b); 237 if (pw == NULL) 238 (void) sm_io_fprintf(smioerr, 239 SM_TIME_DEFAULT, 240 "TrustedUser: unknown user %s\n", b); 241 else 242 TrustedUid = pw->pw_uid; 243 } 244 245 # ifdef UID_MAX 246 if (TrustedUid > UID_MAX) 247 { 248 (void) sm_io_fprintf(smioerr, 249 SM_TIME_DEFAULT, 250 "TrustedUser: uid value (%ld) > UID_MAX (%ld)", 251 (long) TrustedUid, 252 (long) UID_MAX); 253 TrustedUid = 0; 254 } 255 # endif /* UID_MAX */ 256 } 257 #endif /* HASFCHOWN */ 258 break; 259 260 case 'K': /* Keyfile (map) */ 261 if (classname != NULL) /* found it already */ 262 continue; 263 if (mapname == NULL || *mapname == '\0') 264 continue; 265 266 /* cut off trailing spaces */ 267 for (p = buf + strlen(buf) - 1; 268 ISASCII(*p) && isspace(*p) && p > buf; p--) 269 *p = '\0'; 270 271 /* find the last argument */ 272 p = strrchr(buf, ' '); 273 if (p == NULL) 274 continue; 275 b = strstr(p, mapname); 276 if (b == NULL) 277 continue; 278 if (b <= buf) 279 continue; 280 if (!fullpath) 281 { 282 p = strrchr(b, '.'); 283 if (p != NULL) 284 *p = '\0'; 285 } 286 287 /* allow trailing white space? */ 288 if (strcmp(mapname, b) != 0) 289 continue; 290 /* SM_ASSERT(b > buf); */ 291 --b; 292 if (!ISASCII(*b)) 293 continue; 294 if (!isspace(*b) && fullpath) 295 continue; 296 if (!fullpath && !(SM_IS_DIR_DELIM(*b) || isspace(*b))) 297 continue; 298 299 /* basically from readcf.c */ 300 for (b = buf + 1; ISASCII(*b) && isspace(*b); b++) 301 ; 302 if (!(ISASCII(*b) && isalnum(*b))) 303 { 304 /* syserr("readcf: config K line: no map name"); */ 305 return NULL; 306 } 307 308 while ((ISASCII(*++b) && isalnum(*b)) || *b == '_' || *b == '.') 309 ; 310 if (*b != '\0') 311 *b++ = '\0'; 312 while (ISASCII(*b) && isspace(*b)) 313 b++; 314 if (!(ISASCII(*b) && isalnum(*b))) 315 { 316 /* syserr("readcf: config K line, map %s: no map class", b); */ 317 return NULL; 318 } 319 classname = b; 320 while (ISASCII(*++b) && isalnum(*b)) 321 ; 322 if (*b != '\0') 323 *b++ = '\0'; 324 (void) sm_strlcpy(classbuf, classname, sizeof classbuf); 325 break; 326 327 default: 328 continue; 329 } 330 } 331 (void) sm_io_close(cfp, SM_TIME_DEFAULT); 332 333 /* not really needed because it is just a "one time leak" */ 334 if (mapname != mapfile && mapname != NULL) 335 { 336 free(mapname); 337 mapname = NULL; 338 } 339 return classbuf; 340 } 341 342 int 343 main(argc, argv) 344 int argc; 345 char **argv; 346 { 347 char *progname; 348 char *cfile; 349 bool inclnull = false; 350 bool notrunc = false; 351 bool allowreplace = false; 352 bool allowempty = false; 353 bool foldcase = true; 354 bool unmake = false; 355 #if _FFR_MM_ALIASES 356 /* 357 ** NOTE: this does not work properly: 358 ** sendmail does address rewriting which is not done here. 359 */ 360 361 bool aliases = false; 362 #endif 363 bool didreadcf = false; 364 char sep = '\0'; 365 char comment = '#'; 366 int opt; 367 char *typename = NULL; 368 char *fallback = NULL; 369 char *mapname = NULL; 370 unsigned int lineno; 371 int mode; 372 int smode; 373 int putflags = 0; 374 long sff = SFF_ROOTOK|SFF_REGONLY; 375 struct passwd *pw; 376 SMDB_DATABASE *database; 377 SMDB_CURSOR *cursor; 378 SMDB_DBENT db_key, db_val; 379 SMDB_DBPARAMS params; 380 SMDB_USER_INFO user_info; 381 char ibuf[BUFSIZE]; 382 static char rnamebuf[MAXNAME]; /* holds RealUserName */ 383 extern char *optarg; 384 extern int optind; 385 #if USE_EAI 386 bool ascii = true; 387 #endif 388 #if _FFR_TESTS 389 int slp = 0; 390 #endif 391 392 memset(¶ms, '\0', sizeof params); 393 params.smdbp_cache_size = 1024 * 1024; 394 395 progname = strrchr(argv[0], '/'); 396 if (progname != NULL) 397 progname++; 398 else 399 progname = argv[0]; 400 cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL); 401 402 clrbitmap(DontBlameSendmail); 403 RunAsUid = RealUid = getuid(); 404 RunAsGid = RealGid = getgid(); 405 pw = getpwuid(RealUid); 406 if (pw != NULL) 407 (void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf); 408 else 409 (void) sm_snprintf(rnamebuf, sizeof rnamebuf, 410 "Unknown UID %d", (int) RealUid); 411 RunAsUserName = RealUserName = rnamebuf; 412 user_info.smdbu_id = RunAsUid; 413 user_info.smdbu_group_id = RunAsGid; 414 (void) sm_strlcpy(user_info.smdbu_name, RunAsUserName, 415 SMDB_MAX_USER_NAME_LEN); 416 417 #define OPTIONS "C:D:Nc:defi:Llorst:uvx" 418 #if _FFR_MM_ALIASES 419 # define A_OPTIONS "a" 420 #else 421 # define A_OPTIONS 422 #endif 423 #if _FFR_TESTS 424 # define X_OPTIONS "S:" 425 #else 426 # define X_OPTIONS 427 #endif 428 while ((opt = getopt(argc, argv, A_OPTIONS OPTIONS X_OPTIONS)) != -1) 429 { 430 switch (opt) 431 { 432 case 'C': 433 cfile = optarg; 434 break; 435 436 case 'N': 437 inclnull = true; 438 break; 439 440 #if _FFR_MM_ALIASES 441 case 'a': 442 /* Note: this doesn't verify e-mail addresses */ 443 sep = ':'; 444 aliases = true; 445 break; 446 #endif 447 448 case 'c': 449 params.smdbp_cache_size = atol(optarg); 450 break; 451 452 case 'd': 453 params.smdbp_allow_dup = true; 454 break; 455 456 case 'e': 457 allowempty = true; 458 break; 459 460 case 'f': 461 foldcase = false; 462 break; 463 464 case 'i': 465 fallback =optarg; 466 break; 467 468 case 'D': 469 comment = *optarg; 470 break; 471 472 case 'L': 473 smdb_print_available_types(false); 474 sm_io_fprintf(smioout, SM_TIME_DEFAULT, 475 "cf\nCF\n"); 476 exit(EX_OK); 477 break; 478 479 case 'l': 480 smdb_print_available_types(false); 481 exit(EX_OK); 482 break; 483 484 case 'o': 485 notrunc = true; 486 break; 487 488 case 'r': 489 allowreplace = true; 490 break; 491 492 #if _FFR_TESTS 493 case 'S': 494 slp = atoi(optarg); 495 break; 496 #endif 497 498 case 's': 499 setbitn(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail); 500 setbitn(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail); 501 setbitn(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail); 502 setbitn(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail); 503 break; 504 505 case 't': 506 if (optarg == NULL || *optarg == '\0') 507 { 508 sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 509 "Invalid separator\n"); 510 break; 511 } 512 sep = *optarg; 513 break; 514 515 case 'u': 516 unmake = true; 517 break; 518 519 case 'v': 520 verbose = true; 521 break; 522 523 case 'x': 524 smdb_print_available_types(true); 525 exit(EX_OK); 526 break; 527 528 default: 529 usage(progname); 530 /* NOTREACHED */ 531 } 532 } 533 534 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 535 sff |= SFF_NOSLINK; 536 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 537 sff |= SFF_NOHLINK; 538 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 539 sff |= SFF_NOWLINK; 540 541 argc -= optind; 542 argv += optind; 543 if (argc != 2) 544 { 545 usage(progname); 546 /* NOTREACHED */ 547 } 548 else 549 { 550 typename = argv[0]; 551 mapname = argv[1]; 552 } 553 554 #define TYPEFROMCF (strcasecmp(typename, "cf") == 0) 555 #define FULLPATHFROMCF (strcmp(typename, "cf") == 0) 556 557 #if HASFCHOWN 558 if (geteuid() == 0) 559 { 560 if (TYPEFROMCF) 561 typename = readcf(cfile, mapname, FULLPATHFROMCF); 562 else 563 (void) readcf(cfile, NULL, false); 564 didreadcf = true; 565 } 566 #endif /* HASFCHOWN */ 567 568 if (!params.smdbp_allow_dup && !allowreplace) 569 putflags = SMDBF_NO_OVERWRITE; 570 571 if (unmake) 572 { 573 mode = O_RDONLY; 574 smode = S_IRUSR; 575 } 576 else 577 { 578 mode = O_RDWR; 579 if (!notrunc) 580 { 581 mode |= O_CREAT|O_TRUNC; 582 sff |= SFF_CREAT; 583 } 584 smode = S_IWUSR; 585 } 586 587 params.smdbp_num_elements = 4096; 588 589 if (!didreadcf && TYPEFROMCF) 590 { 591 typename = readcf(cfile, mapname, FULLPATHFROMCF); 592 didreadcf = true; 593 } 594 if (didreadcf && (typename == NULL || *typename == '\0')) 595 { 596 if (fallback != NULL && *fallback != '\0') 597 { 598 typename = fallback; 599 if (verbose) 600 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 601 "%s: mapfile %s: not found in %s, using fallback %s\n", 602 progname, mapname, cfile, fallback); 603 } 604 else 605 { 606 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 607 "%s: mapfile %s: not found in %s\n", 608 progname, mapname, cfile); 609 exit(EX_DATAERR); 610 } 611 } 612 613 /* 614 ** Note: if "implicit" is selected it does not work like 615 ** sendmail: it will just use the first available DB type, 616 ** it won't try several (for -u) to find one that "works". 617 */ 618 619 errno = smdb_open_database(&database, mapname, mode, smode, sff, 620 typename, &user_info, ¶ms); 621 if (errno != SMDBE_OK) 622 { 623 char *hint; 624 625 if (errno == SMDBE_UNSUPPORTED_DB_TYPE && 626 (hint = smdb_db_definition(typename)) != NULL) 627 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 628 "%s: Need to recompile with -D%s for %s support\n", 629 progname, hint, typename); 630 else 631 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 632 "%s: error opening type %s map %s: %s\n", 633 progname, typename, mapname, 634 sm_errstring(errno)); 635 exit(EX_CANTCREAT); 636 } 637 638 (void) database->smdb_sync(database, 0); 639 640 if (!unmake && geteuid() == 0 && TrustedUid != 0) 641 { 642 errno = database->smdb_set_owner(database, TrustedUid, -1); 643 if (errno != SMDBE_OK) 644 { 645 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 646 "WARNING: ownership change on %s failed %s", 647 mapname, sm_errstring(errno)); 648 } 649 } 650 651 /* 652 ** Copy the data 653 */ 654 655 exitstat = EX_OK; 656 if (unmake) 657 { 658 errno = database->smdb_cursor(database, &cursor, 0); 659 if (errno != SMDBE_OK) 660 { 661 662 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 663 "%s: cannot make cursor for type %s map %s\n", 664 progname, typename, mapname); 665 exit(EX_SOFTWARE); 666 } 667 668 memset(&db_key, '\0', sizeof db_key); 669 memset(&db_val, '\0', sizeof db_val); 670 671 for (lineno = 0; ; lineno++) 672 { 673 errno = cursor->smdbc_get(cursor, &db_key, &db_val, 674 SMDB_CURSOR_GET_NEXT); 675 if (errno != SMDBE_OK) 676 break; 677 678 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 679 "%.*s%c%.*s\n", 680 (int) db_key.size, 681 (char *) db_key.data, 682 (sep != '\0') ? sep : '\t', 683 (int) db_val.size, 684 (char *)db_val.data); 685 686 } 687 (void) cursor->smdbc_close(cursor); 688 } 689 else 690 { 691 lineno = 0; 692 while (sm_io_fgets(smioin, SM_TIME_DEFAULT, ibuf, sizeof ibuf) 693 >= 0) 694 { 695 register char *p; 696 697 lineno++; 698 699 /* 700 ** Parse the line. 701 */ 702 703 p = strchr(ibuf, '\n'); 704 if (p != NULL) 705 *p = '\0'; 706 else if (!sm_io_eof(smioin)) 707 { 708 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 709 "%s: %s: line %u: line too long (%ld bytes max)\n", 710 progname, mapname, lineno, 711 (long) sizeof ibuf); 712 exitstat = EX_DATAERR; 713 continue; 714 } 715 716 if (ibuf[0] == '\0' || ibuf[0] == comment) 717 continue; 718 if (sep == '\0' && ISASCII(ibuf[0]) && isspace(ibuf[0])) 719 { 720 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 721 "%s: %s: line %u: syntax error (leading space)\n", 722 progname, mapname, lineno); 723 exitstat = EX_DATAERR; 724 continue; 725 } 726 727 memset(&db_key, '\0', sizeof db_key); 728 memset(&db_val, '\0', sizeof db_val); 729 db_key.data = ibuf; 730 731 #if USE_EAI 732 db_key.size = 0; 733 if (foldcase) 734 { 735 for (p = ibuf; *p != '\0' && !ISSEP(*p); p++) 736 { 737 if (!ISASCII(*p)) 738 ascii = false; 739 } 740 if (!ascii) 741 { 742 char sep; 743 char *lkey; 744 745 sep = *p; 746 *p = '\0'; 747 748 lkey = sm_lowercase(ibuf); 749 db_key.data = lkey; 750 db_key.size = strlen(lkey); 751 *p = sep; 752 } 753 } 754 if (ascii) 755 #endif /* USE_EAI */ 756 /* NOTE: see if () above! */ 757 for (p = ibuf; *p != '\0' && !ISSEP(*p); p++) 758 { 759 if (foldcase && ISASCII(*p) && isupper(*p)) 760 *p = tolower(*p); 761 } 762 #if USE_EAI 763 if (0 == db_key.size) 764 #endif 765 db_key.size = p - ibuf; 766 if (inclnull) 767 db_key.size++; 768 769 if (*p != '\0') 770 *p++ = '\0'; 771 while (*p != '\0' && ISSEP(*p)) 772 p++; 773 #if _FFR_MM_ALIASES 774 while (aliases && *p != '\0' && ISSPACE(*p)) 775 p++; 776 #endif 777 if (!allowempty && *p == '\0') 778 { 779 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 780 "%s: %s: line %u: no RHS for LHS %s\n", 781 progname, mapname, lineno, 782 (char *) db_key.data); 783 exitstat = EX_DATAERR; 784 continue; 785 } 786 787 db_val.data = p; 788 db_val.size = strlen(p); 789 if (inclnull) 790 db_val.size++; 791 792 /* 793 ** Do the database insert. 794 */ 795 796 db_put(database, db_key, db_val, putflags, mapname, 797 lineno, progname); 798 } 799 #if _FFR_MM_ALIASES 800 if (aliases) 801 { 802 char magic[2] = "@"; 803 804 db_key.data = magic; 805 db_val.data = magic; 806 db_key.size = 1; 807 db_val.size = 1; 808 db_put(database, db_key, db_val, putflags, mapname, -1, 809 progname); 810 } 811 #endif /* _FFR_MM_ALIASES */ 812 } 813 814 #if _FFR_TESTS 815 if (slp > 0) 816 sleep(slp); 817 #endif 818 819 /* 820 ** Now close the database. 821 */ 822 823 errno = database->smdb_close(database); 824 if (errno != SMDBE_OK) 825 { 826 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 827 "%s: close(%s): %s\n", 828 progname, mapname, sm_errstring(errno)); 829 exitstat = EX_IOERR; 830 } 831 smdb_free_database(database); 832 833 exit(exitstat); 834 835 /* NOTREACHED */ 836 return exitstat; 837 } 838