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
usage(progname)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
db_put(database,db_key,db_val,putflags,mapname,lineno,progname)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 *
readcf(cfile,mapfile,fullpath)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
main(argc,argv)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