makemap.c revision 42580
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
14static 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
36enum type { T_DBM, T_BTREE, T_HASH, T_ERR, T_UNKNOWN };
37
38union 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
53uid_t	RealUid;
54gid_t	RealGid;
55char	*RealUserName;
56uid_t	RunAsUid;
57uid_t	RunAsGid;
58char	*RunAsUserName;
59int	Verbose = 2;
60bool	DontInitGroups = FALSE;
61long	DontBlameSendmail = DBS_SAFE;
62u_char	tTdvect[100];
63uid_t	TrustedUid = 0;
64
65#define BUFSIZE		1024
66
67int
68main(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
863bool
864lockfile(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*/
913void
914#ifdef __STDC__
915message(const char *msg, ...)
916#else
917message(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*/
937void
938#ifdef __STDC__
939syserr(const char *msg, ...)
940#else
941syserr(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
960const char *
961errstring(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