makemap.c revision 38032
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.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
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 = TRUE;
61long	DontBlameSendmail = DBS_SAFE;
62u_char	tTdvect[100];
63uid_t	TrustedFileUid = 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;
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)) != EOF)
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
838bool
839lockfile(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*/
888void
889#ifdef __STDC__
890message(const char *msg, ...)
891#else
892message(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*/
912void
913#ifdef __STDC__
914syserr(const char *msg, ...)
915#else
916syserr(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
935const char *
936errstring(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