xref: /freebsd/contrib/sendmail/makemap/makemap.c (revision 11afcc8f9f96d657b8e6f7547c02c1957331fc96)
1 /*
2  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3  * Copyright (c) 1992 Eric P. Allman.  All rights reserved.
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  */
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)makemap.c	8.62 (Berkeley) 6/24/98";
15 #endif /* not lint */
16 
17 #include <sys/types.h>
18 #include <sys/errno.h>
19 #ifndef ISC_UNIX
20 # include <sys/file.h>
21 #endif
22 #include "sendmail.h"
23 #include "pathnames.h"
24 
25 #ifdef NDBM
26 # include <ndbm.h>
27 #endif
28 
29 #ifdef NEWDB
30 # include <db.h>
31 # ifndef DB_VERSION_MAJOR
32 #  define DB_VERSION_MAJOR 1
33 # endif
34 #endif
35 
36 enum type { T_DBM, T_BTREE, T_HASH, T_ERR, T_UNKNOWN };
37 
38 union dbent
39 {
40 #ifdef NDBM
41 	datum	dbm;
42 #endif
43 #ifdef NEWDB
44 	DBT	db;
45 #endif
46 	struct
47 	{
48 		char	*data;
49 		size_t	size;
50 	} xx;
51 };
52 
53 uid_t	RealUid;
54 gid_t	RealGid;
55 char	*RealUserName;
56 uid_t	RunAsUid;
57 uid_t	RunAsGid;
58 char	*RunAsUserName;
59 int	Verbose = 2;
60 bool	DontInitGroups = TRUE;
61 long	DontBlameSendmail = DBS_SAFE;
62 u_char	tTdvect[100];
63 uid_t	TrustedFileUid = 0;
64 
65 #define BUFSIZE		1024
66 
67 int
68 main(argc, argv)
69 	int argc;
70 	char **argv;
71 {
72 	char *progname;
73 	char *cfile;
74 	bool inclnull = FALSE;
75 	bool notrunc = FALSE;
76 	bool allowreplace = FALSE;
77 	bool allowdups = FALSE;
78 	bool verbose = FALSE;
79 	bool foldcase = TRUE;
80 	int exitstat;
81 	int opt;
82 	char *typename;
83 	char *mapname;
84 	char *ext;
85 	int lineno;
86 	int st;
87 	int mode;
88 	int putflags;
89 	long dbcachesize = 1024 * 1024;
90 	enum type type;
91 	int fd;
92 	int sff = SFF_ROOTOK|SFF_REGONLY;
93 	struct passwd *pw;
94 	union
95 	{
96 #ifdef NDBM
97 		DBM	*dbm;
98 #endif
99 #ifdef NEWDB
100 		DB	*db;
101 #endif
102 		void	*dbx;
103 	} dbp;
104 	union dbent key, val;
105 #ifdef NEWDB
106 # if DB_VERSION_MAJOR < 2
107 	BTREEINFO bti;
108 	HASHINFO hinfo;
109 # else
110 	DB_INFO dbinfo;
111 # endif
112 #endif
113 	char ibuf[BUFSIZE];
114 	char fbuf[MAXNAME];
115 	char dbuf[MAXNAME];
116 #ifdef NDBM
117 	char pbuf[MAXNAME];
118 #endif
119 #if _FFR_TRUSTED_FILE_OWNER
120 	FILE *cfp;
121 	char buf[MAXLINE];
122 #endif
123 	static char rnamebuf[MAXNAME];	/* holds RealUserName */
124 	struct stat std;
125 #ifdef NDBM
126 	struct stat stp;
127 #endif
128 	extern char *optarg;
129 	extern int optind;
130 
131 	progname = argv[0];
132 	cfile = _PATH_SENDMAILCF;
133 
134 	RunAsUid = RealUid = getuid();
135 	RunAsGid = RealGid = getgid();
136 	pw = getpwuid(RealUid);
137 	if (pw != NULL)
138 	{
139 		if (strlen(pw->pw_name) > MAXNAME - 1)
140 			pw->pw_name[MAXNAME] = 0;
141 		sprintf(rnamebuf, "%s", pw->pw_name);
142 	}
143 	else
144 		sprintf(rnamebuf, "Unknown UID %d", (int) RealUid);
145 	RunAsUserName = RealUserName = rnamebuf;
146 
147 #if _FFR_NEW_MAKEMAP_FLAGS
148 #define OPTIONS		"C:Nc:dforsv"
149 #else
150 #define OPTIONS		"C:Ndforsv"
151 #endif
152 	while ((opt = getopt(argc, argv, OPTIONS)) != -1)
153 	{
154 		switch (opt)
155 		{
156 		  case 'C':
157 			cfile = optarg;
158 			break;
159 
160 		  case 'N':
161 			inclnull = TRUE;
162 			break;
163 
164 #if _FFR_NEW_MAKEMAP_FLAGS
165 		  case 'c':
166 			dbcachesize = atol(optarg);
167 			break;
168 #endif
169 
170 		  case 'd':
171 			allowdups = TRUE;
172 			break;
173 
174 		  case 'f':
175 			foldcase = FALSE;
176 			break;
177 
178 		  case 'o':
179 			notrunc = TRUE;
180 			break;
181 
182 		  case 'r':
183 			allowreplace = TRUE;
184 			break;
185 
186 		  case 's':
187 			DontBlameSendmail |= DBS_MAPINUNSAFEDIRPATH|DBS_WRITEMAPTOHARDLINK|DBS_WRITEMAPTOSYMLINK|DBS_LINKEDMAPINWRITABLEDIR;
188 			break;
189 
190 		  case 'v':
191 			verbose = TRUE;
192 			break;
193 
194 		  default:
195 			type = T_ERR;
196 			break;
197 		}
198 	}
199 
200 	if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail))
201 		sff |= SFF_NOSLINK;
202         if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail))
203 		sff |= SFF_NOHLINK;
204 	if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail))
205 		sff |= SFF_NOWLINK;
206 
207 	argc -= optind;
208 	argv += optind;
209 	if (argc != 2)
210 		type = T_ERR;
211 	else
212 	{
213 		typename = argv[0];
214 		mapname = argv[1];
215 		ext = NULL;
216 
217 		if (strcmp(typename, "dbm") == 0)
218 		{
219 			type = T_DBM;
220 		}
221 		else if (strcmp(typename, "btree") == 0)
222 		{
223 			type = T_BTREE;
224 			ext = ".db";
225 		}
226 		else if (strcmp(typename, "hash") == 0)
227 		{
228 			type = T_HASH;
229 			ext = ".db";
230 		}
231 		else
232 			type = T_UNKNOWN;
233 	}
234 
235 #if _FFR_TRUSTED_FILE_OWNER
236 	if ((cfp = fopen(cfile, "r")) == NULL)
237 	{
238 		fprintf(stderr, "mailstats: ");
239 		perror(cfile);
240 		exit(EX_NOINPUT);
241 	}
242 	while (fgets(buf, sizeof(buf), cfp) != NULL)
243 	{
244 		register char *b;
245 
246 		if ((b = strchr(buf, '\n')) != NULL)
247 			*b = '\0';
248 
249 		b = buf;
250 		switch (*b++)
251 		{
252 		  case 'O':		/* option */
253 			if (strncasecmp(b, " TrustedFileOwner", 17) == 0 &&
254 			    !(isascii(b[17]) && isalnum(b[17])))
255 			{
256 				b = strchr(b, '=');
257 				if (b == NULL)
258 					continue;
259 				while (isascii(*++b) && isspace(*b))
260 					continue;
261 				if (isascii(*b) && isdigit(*b))
262 					TrustedFileUid = atoi(b);
263 				else
264 				{
265 					register struct passwd *pw;
266 
267 					TrustedFileUid = 0;
268 					pw = getpwnam(b);
269 					if (pw == NULL)
270 						fprintf(stderr,
271 							"TrustedFileOwner: unknown user %s", b);
272 					else
273 						TrustedFileUid = pw->pw_uid;
274 				}
275 
276 # ifdef UID_MAX
277 				if (TrustedFileUid > UID_MAX)
278 				{
279 					syserr("TrustedFileOwner: uid value (%ld) > UID_MAX (%ld)",
280 					       TrustedFileUid, UID_MAX);
281 					TrustedFileUid = 0;
282 				}
283 # endif
284 				break;
285 			}
286 
287 
288 		  default:
289 			continue;
290 		}
291 	}
292 	(void) fclose(cfp);
293 #endif
294 	switch (type)
295 	{
296 	  case T_ERR:
297 #if _FFR_NEW_MAKEMAP_FLAGS
298 		fprintf(stderr,
299 			"Usage: %s [-N] [-c cachesize] [-d] [-f] [-o] [-r] [-s] [-v] type mapname\n",
300 			progname);
301 #else
302 		fprintf(stderr, "Usage: %s [-N] [-d] [-f] [-o] [-r] [-s] [-v] type mapname\n", progname);
303 #endif
304 		exit(EX_USAGE);
305 
306 	  case T_UNKNOWN:
307 		fprintf(stderr, "%s: Unknown database type %s\n",
308 			progname, typename);
309 		exit(EX_USAGE);
310 
311 #ifndef NDBM
312 	  case T_DBM:
313 #endif
314 #ifndef NEWDB
315 	  case T_BTREE:
316 	  case T_HASH:
317 #endif
318 		fprintf(stderr, "%s: Type %s not supported in this version\n",
319 			progname, typename);
320 		exit(EX_UNAVAILABLE);
321 
322 #ifdef NEWDB
323 	  case T_BTREE:
324 # if DB_VERSION_MAJOR < 2
325 		bzero(&bti, sizeof bti);
326 # else
327 		bzero(&dbinfo, sizeof dbinfo);
328 # endif
329 		if (allowdups)
330 		{
331 # if DB_VERSION_MAJOR < 2
332 			bti.flags |= R_DUP;
333 # else
334 			dbinfo.flags |= DB_DUP;
335 # endif
336 		}
337 		if (allowdups || allowreplace)
338 			putflags = 0;
339 		else
340 		{
341 # if DB_VERSION_MAJOR < 2
342 			putflags = R_NOOVERWRITE;
343 # else
344 			putflags = DB_NOOVERWRITE;
345 # endif
346 		}
347 		break;
348 
349 	  case T_HASH:
350 # if DB_VERSION_MAJOR < 2
351 		bzero(&hinfo, sizeof hinfo);
352 # else
353 		bzero(&dbinfo, sizeof dbinfo);
354 # endif
355 		if (allowreplace)
356 			putflags = 0;
357 		else
358 		{
359 # if DB_VERSION_MAJOR < 2
360 			putflags = R_NOOVERWRITE;
361 # else
362 			putflags = DB_NOOVERWRITE;
363 # endif
364 		}
365 		break;
366 #endif
367 #ifdef NDBM
368 	  case T_DBM:
369 		if (allowdups)
370 		{
371 			fprintf(stderr, "%s: Type %s does not support -d (allow dups)\n",
372 				progname, typename);
373 			exit(EX_UNAVAILABLE);
374 		}
375 		if (allowreplace)
376 			putflags = DBM_REPLACE;
377 		else
378 			putflags = DBM_INSERT;
379 		break;
380 #endif
381 	}
382 
383 	/*
384 	**  Adjust file names.
385 	*/
386 
387 	if (ext != NULL)
388 	{
389 		int el, fl;
390 
391 		el = strlen(ext);
392 		fl = strlen(mapname);
393 		if (el + fl + 1 >= sizeof fbuf)
394 		{
395 			fprintf(stderr, "%s: file name too long", mapname);
396 			exit(EX_USAGE);
397 		}
398 		if (fl < el || strcmp(&mapname[fl - el], ext) != 0)
399 		{
400 			strcpy(fbuf, mapname);
401 			strcat(fbuf, ext);
402 			mapname = fbuf;
403 		}
404 	}
405 
406 	if (!notrunc)
407 		sff |= SFF_CREAT;
408 	switch (type)
409 	{
410 #ifdef NEWDB
411 	  case T_BTREE:
412 	  case T_HASH:
413 		if (strlen(mapname) >= sizeof dbuf)
414 		{
415 			fprintf(stderr,
416 				"%s: map name too long\n", mapname);
417 			exit(EX_USAGE);
418 		}
419 		strcpy(dbuf, mapname);
420 		if ((st = safefile(dbuf, RealUid, RealGid, RealUserName,
421 				   sff, S_IWUSR, &std)) != 0)
422 		{
423 			fprintf(stderr,
424 				"%s: could not create: %s\n",
425 				dbuf, errstring(st));
426 			exit(EX_CANTCREAT);
427 		}
428 		break;
429 #endif
430 #ifdef NDBM
431 	  case T_DBM:
432 		if (strlen(mapname) + 5 > sizeof dbuf)
433 		{
434 			fprintf(stderr,
435 				"%s: map name too long\n", mapname);
436 			exit(EX_USAGE);
437 		}
438 		sprintf(dbuf, "%s.dir", mapname);
439 		if ((st = safefile(dbuf, RealUid, RealGid, RealUserName,
440 			   sff, S_IWUSR, &std)) != 0)
441 		{
442 			fprintf(stderr,
443 				"%s: could not create: %s\n",
444 				dbuf, errstring(st));
445 			exit(EX_CANTCREAT);
446 		}
447 		sprintf(pbuf, "%s.pag", mapname);
448 		if ((st = safefile(pbuf, RealUid, RealGid, RealUserName,
449 			   sff, S_IWUSR, &stp)) != 0)
450 		{
451 			fprintf(stderr,
452 				"%s: could not create: %s\n",
453 				pbuf, errstring(st));
454 			exit(EX_CANTCREAT);
455 		}
456 		break;
457 #endif
458 	  default:
459 		fprintf(stderr,
460 			"%s: internal error: type %d\n",
461 			progname,
462 			type);
463 		exit(EX_SOFTWARE);
464 	}
465 
466 	/*
467 	**  Create the database.
468 	*/
469 
470 	mode = O_RDWR;
471 	if (!notrunc)
472 		mode |= O_CREAT|O_TRUNC;
473 #if O_EXLOCK
474 	mode |= O_EXLOCK;
475 #else
476 	/* pre-lock the database */
477 	fd = safeopen(dbuf, mode & ~O_TRUNC, 0644, sff);
478 	if (fd < 0)
479 	{
480 		fprintf(stderr, "%s: cannot create type %s map %s\n",
481 			progname, typename, mapname);
482 		exit(EX_CANTCREAT);
483 	}
484 #endif
485 	switch (type)
486 	{
487 #ifdef NDBM
488 	  case T_DBM:
489 		dbp.dbm = dbm_open(mapname, mode, 0644);
490 		if (dbp.dbm != NULL &&
491 		    dbm_dirfno(dbp.dbm) == dbm_pagfno(dbp.dbm))
492 		{
493 			fprintf(stderr, "dbm map %s: cannot run with GDBM\n",
494 				mapname);
495 			dbm_close(dbp.dbm);
496 			exit(EX_CONFIG);
497 		}
498 		if (dbp.dbm != NULL &&
499 		    (filechanged(dbuf, dbm_dirfno(dbp.dbm), &std) ||
500 		     filechanged(pbuf, dbm_pagfno(dbp.dbm), &stp)))
501 		{
502 			fprintf(stderr,
503 				"dbm map %s: file changed after open\n",
504 				mapname);
505 			dbm_close(dbp.dbm);
506 			exit(EX_CANTCREAT);
507 		}
508 		if (geteuid() == 0 && TrustedFileUid != 0)
509 		{
510 			if (fchown(dbm_dirfno(dbp.dbm), TrustedFileUid, -1) < 0 ||
511 			    fchown(dbm_pagfno(dbp.dbm), TrustedFileUid, -1) < 0)
512 			{
513 				fprintf(stderr,
514 					"WARNING: ownership change on %s failed: %s",
515 					mapname, errstring(errno));
516 			}
517 		}
518 
519 		break;
520 #endif
521 
522 #ifdef NEWDB
523 	  case T_HASH:
524 		/* tweak some parameters for performance */
525 # if DB_VERSION_MAJOR < 2
526 		hinfo.nelem = 4096;
527 		hinfo.cachesize = dbcachesize;
528 # else
529 		dbinfo.h_nelem = 4096;
530 		dbinfo.db_cachesize = dbcachesize;
531 # endif
532 
533 # if DB_VERSION_MAJOR < 2
534 		dbp.db = dbopen(mapname, mode, 0644, DB_HASH, &hinfo);
535 # else
536 		{
537 			int flags = 0;
538 
539 			if (bitset(O_CREAT, mode))
540 				flags |= DB_CREATE;
541 			if (bitset(O_TRUNC, mode))
542 				flags |= DB_TRUNCATE;
543 
544 			dbp.db = NULL;
545 			errno = db_open(mapname, DB_HASH, flags, 0644,
546 					NULL, &dbinfo, &dbp.db);
547 		}
548 # endif
549 		if (dbp.db != NULL)
550 		{
551 			int fd;
552 
553 # if DB_VERSION_MAJOR < 2
554 			fd = dbp.db->fd(dbp.db);
555 # else
556 			fd = -1;
557 			errno = dbp.db->fd(dbp.db, &fd);
558 # endif
559 			if (filechanged(dbuf, fd, &std))
560 			{
561 				fprintf(stderr,
562 					"db map %s: file changed after open\n",
563 					mapname);
564 # if DB_VERSION_MAJOR < 2
565 				dbp.db->close(dbp.db);
566 # else
567 				errno = dbp.db->close(dbp.db, 0);
568 # endif
569 				exit(EX_CANTCREAT);
570 			}
571 			(void) (*dbp.db->sync)(dbp.db, 0);
572 			if (geteuid() == 0 && TrustedFileUid != 0)
573 			{
574 				if (fchown(fd, TrustedFileUid, -1) < 0)
575 				{
576 					fprintf(stderr,
577 						"WARNING: ownership change on %s failed: %s",
578 						mapname, errstring(errno));
579 				}
580 			}
581 		}
582 		break;
583 
584 	  case T_BTREE:
585 		/* tweak some parameters for performance */
586 # if DB_VERSION_MAJOR < 2
587 		bti.cachesize = dbcachesize;
588 # else
589 		dbinfo.db_cachesize = dbcachesize;
590 # endif
591 
592 # if DB_VERSION_MAJOR < 2
593 		dbp.db = dbopen(mapname, mode, 0644, DB_BTREE, &bti);
594 # else
595 		{
596 			int flags = 0;
597 
598 			if (bitset(O_CREAT, mode))
599 				flags |= DB_CREATE;
600 			if (bitset(O_TRUNC, mode))
601 				flags |= DB_TRUNCATE;
602 
603 			dbp.db = NULL;
604 			errno = db_open(mapname, DB_BTREE, flags, 0644,
605 					NULL, &dbinfo, &dbp.db);
606 		}
607 # endif
608 		if (dbp.db != NULL)
609 		{
610 			int fd;
611 
612 # if DB_VERSION_MAJOR < 2
613 			fd = dbp.db->fd(dbp.db);
614 # else
615 			fd = -1;
616 			errno = dbp.db->fd(dbp.db, &fd);
617 # endif
618 			if (filechanged(dbuf, fd, &std))
619 			{
620 				fprintf(stderr,
621 					"db map %s: file changed after open\n",
622 					mapname);
623 # if DB_VERSION_MAJOR < 2
624 				dbp.db->close(dbp.db);
625 # else
626 				errno = dbp.db->close(dbp.db, 0);
627 # endif
628 				exit(EX_CANTCREAT);
629 			}
630 			(void) (*dbp.db->sync)(dbp.db, 0);
631 			if (geteuid() == 0 && TrustedFileUid != 0)
632 			{
633 				if (fchown(fd, TrustedFileUid, -1) < 0)
634 				{
635 					fprintf(stderr,
636 						"WARNING: ownership change on %s failed: %s",
637 						mapname, errstring(errno));
638 				}
639 			}
640 		}
641 		break;
642 #endif
643 
644 	  default:
645 		fprintf(stderr, "%s: internal error: type %d\n",
646 			progname, type);
647 		exit(EX_SOFTWARE);
648 	}
649 
650 	if (dbp.dbx == NULL)
651 	{
652 		fprintf(stderr, "%s: cannot open type %s map %s\n",
653 			progname, typename, mapname);
654 		exit(EX_CANTCREAT);
655 	}
656 
657 	/*
658 	**  Copy the data
659 	*/
660 
661 	lineno = 0;
662 	exitstat = EX_OK;
663 	while (fgets(ibuf, sizeof ibuf, stdin) != NULL)
664 	{
665 		register char *p;
666 
667 		lineno++;
668 
669 		/*
670 		**  Parse the line.
671 		*/
672 
673 		p = strchr(ibuf, '\n');
674 		if (p != NULL)
675 			*p = '\0';
676 		else if (!feof(stdin))
677 		{
678 			fprintf(stderr, "%s: %s: line %d: line too long (%ld bytes max)\n",
679 				progname, mapname, lineno, (long) sizeof ibuf);
680 			continue;
681 		}
682 
683 		if (ibuf[0] == '\0' || ibuf[0] == '#')
684 			continue;
685 		if (isascii(ibuf[0]) && isspace(ibuf[0]))
686 		{
687 			fprintf(stderr, "%s: %s: line %d: syntax error (leading space)\n",
688 				progname, mapname, lineno);
689 			continue;
690 		}
691 #ifdef NEWDB
692 		if (type == T_HASH || type == T_BTREE)
693 		{
694 			bzero(&key.db, sizeof key.db);
695 			bzero(&val.db, sizeof val.db);
696 		}
697 #endif
698 
699 		key.xx.data = ibuf;
700 		for (p = ibuf; *p != '\0' && !(isascii(*p) && isspace(*p)); p++)
701 		{
702 			if (foldcase && isascii(*p) && isupper(*p))
703 				*p = tolower(*p);
704 		}
705 		key.xx.size = p - key.xx.data;
706 		if (inclnull)
707 			key.xx.size++;
708 		if (*p != '\0')
709 			*p++ = '\0';
710 		while (isascii(*p) && isspace(*p))
711 			p++;
712 		if (*p == '\0')
713 		{
714 			fprintf(stderr, "%s: %s: line %d: no RHS for LHS %s\n",
715 				progname, mapname, lineno, key.xx.data);
716 			continue;
717 		}
718 		val.xx.data = p;
719 		val.xx.size = strlen(p);
720 		if (inclnull)
721 			val.xx.size++;
722 
723 		/*
724 		**  Do the database insert.
725 		*/
726 
727 		if (verbose)
728 		{
729 			printf("key=`%s', val=`%s'\n", key.xx.data, val.xx.data);
730 		}
731 
732 		switch (type)
733 		{
734 #ifdef NDBM
735 		  case T_DBM:
736 			st = dbm_store(dbp.dbm, key.dbm, val.dbm, putflags);
737 			break;
738 #endif
739 
740 #ifdef NEWDB
741 		  case T_BTREE:
742 		  case T_HASH:
743 # if DB_VERSION_MAJOR < 2
744 			st = (*dbp.db->put)(dbp.db, &key.db, &val.db, putflags);
745 # else
746 			errno = (*dbp.db->put)(dbp.db, NULL, &key.db,
747 					       &val.db, putflags);
748 			switch (errno)
749 			{
750 			  case DB_KEYEXIST:
751 				st = 1;
752 				break;
753 
754 			  case 0:
755 				st = 0;
756 				break;
757 
758 			  default:
759 				st = -1;
760 				break;
761 			}
762 # endif
763 			break;
764 #endif
765 		  default:
766 			break;
767 		}
768 
769 		if (st < 0)
770 		{
771 			fprintf(stderr, "%s: %s: line %d: key %s: put error\n",
772 				progname, mapname, lineno, key.xx.data);
773 			perror(mapname);
774 			exitstat = EX_IOERR;
775 		}
776 		else if (st > 0)
777 		{
778 			fprintf(stderr,
779 				"%s: %s: line %d: key %s: duplicate key\n",
780 				progname, mapname, lineno, key.xx.data);
781 		}
782 	}
783 
784 	/*
785 	**  Now close the database.
786 	*/
787 
788 	switch (type)
789 	{
790 #ifdef NDBM
791 	  case T_DBM:
792 		dbm_close(dbp.dbm);
793 		break;
794 #endif
795 
796 #ifdef NEWDB
797 	  case T_HASH:
798 	  case T_BTREE:
799 # if DB_VERSION_MAJOR < 2
800 		if ((*dbp.db->close)(dbp.db) < 0)
801 # else
802 		if ((errno = (*dbp.db->close)(dbp.db, 0)) != 0)
803 # endif
804 		{
805 			fprintf(stderr, "%s: %s: error on close\n",
806 				progname, mapname);
807 			perror(mapname);
808 			exitstat = EX_IOERR;
809 		}
810 #endif
811 	  default:
812 		break;
813 	}
814 
815 #if !O_EXLOCK
816 	/* release locks */
817 	close(fd);
818 #endif
819 
820 	exit (exitstat);
821 }
822 /*
823 **  LOCKFILE -- lock a file using flock or (shudder) fcntl locking
824 **
825 **	Parameters:
826 **		fd -- the file descriptor of the file.
827 **		filename -- the file name (for error messages).
828 **		ext -- the filename extension.
829 **		type -- type of the lock.  Bits can be:
830 **			LOCK_EX -- exclusive lock.
831 **			LOCK_NB -- non-blocking.
832 **
833 **	Returns:
834 **		TRUE if the lock was acquired.
835 **		FALSE otherwise.
836 */
837 
838 bool
839 lockfile(fd, filename, ext, type)
840 	int fd;
841 	char *filename;
842 	char *ext;
843 	int type;
844 {
845 # if !HASFLOCK
846 	int action;
847 	struct flock lfd;
848 	extern int errno;
849 
850 	bzero(&lfd, sizeof lfd);
851 	if (bitset(LOCK_UN, type))
852 		lfd.l_type = F_UNLCK;
853 	else if (bitset(LOCK_EX, type))
854 		lfd.l_type = F_WRLCK;
855 	else
856 		lfd.l_type = F_RDLCK;
857 	if (bitset(LOCK_NB, type))
858 		action = F_SETLK;
859 	else
860 		action = F_SETLKW;
861 
862 	if (fcntl(fd, action, &lfd) >= 0)
863 		return TRUE;
864 
865 	/*
866 	**  On SunOS, if you are testing using -oQ/tmp/mqueue or
867 	**  -oA/tmp/aliases or anything like that, and /tmp is mounted
868 	**  as type "tmp" (that is, served from swap space), the
869 	**  previous fcntl will fail with "Invalid argument" errors.
870 	**  Since this is fairly common during testing, we will assume
871 	**  that this indicates that the lock is successfully grabbed.
872 	*/
873 
874 	if (errno == EINVAL)
875 		return TRUE;
876 
877 # else	/* HASFLOCK */
878 
879 	if (flock(fd, type) >= 0)
880 		return TRUE;
881 
882 # endif
883 
884 	return FALSE;
885 }
886 
887 /*VARARGS1*/
888 void
889 #ifdef __STDC__
890 message(const char *msg, ...)
891 #else
892 message(msg, va_alist)
893 	const char *msg;
894 	va_dcl
895 #endif
896 {
897 	const char *m;
898 	VA_LOCAL_DECL
899 
900 	m = msg;
901 	if (isascii(m[0]) && isdigit(m[0]) &&
902 	    isascii(m[1]) && isdigit(m[1]) &&
903 	    isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
904 		m += 4;
905 	VA_START(msg);
906 	vfprintf(stderr, m, ap);
907 	VA_END;
908 	fprintf(stderr, "\n");
909 }
910 
911 /*VARARGS1*/
912 void
913 #ifdef __STDC__
914 syserr(const char *msg, ...)
915 #else
916 syserr(msg, va_alist)
917 	const char *msg;
918 	va_dcl
919 #endif
920 {
921 	const char *m;
922 	VA_LOCAL_DECL
923 
924 	m = msg;
925 	if (isascii(m[0]) && isdigit(m[0]) &&
926 	    isascii(m[1]) && isdigit(m[1]) &&
927 	    isascii(m[2]) && isdigit(m[2]) && m[3] == ' ')
928 		m += 4;
929 	VA_START(msg);
930 	vfprintf(stderr, m, ap);
931 	VA_END;
932 	fprintf(stderr, "\n");
933 }
934 
935 const char *
936 errstring(err)
937 	int err;
938 {
939 	static char errstr[64];
940 #if !HASSTRERROR && !defined(ERRLIST_PREDEFINED)
941 	extern char *sys_errlist[];
942 	extern int sys_nerr;
943 #endif
944 
945 	/* handle pseudo-errors internal to sendmail */
946 	switch (err)
947 	{
948 	  case E_SM_OPENTIMEOUT:
949 		return "Timeout on file open";
950 
951 	  case E_SM_NOSLINK:
952 		return "Symbolic links not allowed";
953 
954 	  case E_SM_NOHLINK:
955 		return "Hard links not allowed";
956 
957 	  case E_SM_REGONLY:
958 		return "Regular files only";
959 
960 	  case E_SM_ISEXEC:
961 		return "Executable files not allowed";
962 
963 	  case E_SM_WWDIR:
964 		return "World writable directory";
965 
966 	  case E_SM_GWDIR:
967 		return "Group writable directory";
968 
969 	  case E_SM_FILECHANGE:
970 		return "File changed after open";
971 
972 	  case E_SM_WWFILE:
973 		return "World writable file";
974 
975 	  case E_SM_GWFILE:
976 		return "Group writable file";
977 	}
978 
979 #if HASSTRERROR
980 	return strerror(err);
981 #else
982 	if (err < 0 || err >= sys_nerr)
983 	{
984 		sprintf(errstr, "Error %d", err);
985 		return errstr;
986 	}
987 	return sys_errlist[err];
988 #endif
989 }
990