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