1/*
2** Copyright (c) 1999-2002 Sendmail, Inc. and its suppliers.
3**	All rights reserved.
4**
5** By using this file, you agree to the terms and conditions set
6** forth in the LICENSE file which can be found at the top level of
7** the sendmail distribution.
8*/
9
10#pragma ident	"%Z%%M%	%I%	%E% SMI"
11
12#include <sm/gen.h>
13SM_RCSID("@(#)$Id: smdb.c,v 8.58 2004/08/03 20:58:38 ca Exp $")
14
15#include <fcntl.h>
16#include <stdlib.h>
17#include <unistd.h>
18
19
20#include <sendmail/sendmail.h>
21#include <libsmdb/smdb.h>
22
23static bool	smdb_lockfile __P((int, int));
24
25/*
26** SMDB_MALLOC_DATABASE -- Allocates a database structure.
27**
28**	Parameters:
29**		None
30**
31**	Returns:
32**		An pointer to an allocated SMDB_DATABASE structure or
33**		NULL if it couldn't allocate the memory.
34*/
35
36SMDB_DATABASE *
37smdb_malloc_database()
38{
39	SMDB_DATABASE *db;
40
41	db = (SMDB_DATABASE *) malloc(sizeof(SMDB_DATABASE));
42
43	if (db != NULL)
44		(void) memset(db, '\0', sizeof(SMDB_DATABASE));
45
46	return db;
47}
48
49
50/*
51** SMDB_FREE_DATABASE -- Unallocates a database structure.
52**
53**	Parameters:
54**		database -- a SMDB_DATABASE pointer to deallocate.
55**
56**	Returns:
57**		None
58*/
59
60void
61smdb_free_database(database)
62	SMDB_DATABASE *database;
63{
64	if (database != NULL)
65		free(database);
66}
67/*
68**  SMDB_LOCKFILE -- lock a file using flock or (shudder) fcntl locking
69**
70**	Parameters:
71**		fd -- the file descriptor of the file.
72**		type -- type of the lock.  Bits can be:
73**			LOCK_EX -- exclusive lock.
74**			LOCK_NB -- non-blocking.
75**
76**	Returns:
77**		true if the lock was acquired.
78**		false otherwise.
79*/
80
81static bool
82smdb_lockfile(fd, type)
83	int fd;
84	int type;
85{
86	int i;
87	int save_errno;
88#if !HASFLOCK
89	int action;
90	struct flock lfd;
91
92	(void) memset(&lfd, '\0', sizeof lfd);
93	if (bitset(LOCK_UN, type))
94		lfd.l_type = F_UNLCK;
95	else if (bitset(LOCK_EX, type))
96		lfd.l_type = F_WRLCK;
97	else
98		lfd.l_type = F_RDLCK;
99
100	if (bitset(LOCK_NB, type))
101		action = F_SETLK;
102	else
103		action = F_SETLKW;
104
105	while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
106		continue;
107	if (i >= 0)
108		return true;
109	save_errno = errno;
110
111	/*
112	**  On SunOS, if you are testing using -oQ/tmp/mqueue or
113	**  -oA/tmp/aliases or anything like that, and /tmp is mounted
114	**  as type "tmp" (that is, served from swap space), the
115	**  previous fcntl will fail with "Invalid argument" errors.
116	**  Since this is fairly common during testing, we will assume
117	**  that this indicates that the lock is successfully grabbed.
118	*/
119
120	if (save_errno == EINVAL)
121		return true;
122
123	if (!bitset(LOCK_NB, type) ||
124	    (save_errno != EACCES && save_errno != EAGAIN))
125	{
126# if 0
127		int omode = fcntl(fd, F_GETFL, NULL);
128		int euid = (int) geteuid();
129
130		syslog(LOG_ERR, "cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
131		       filename, ext, fd, type, omode, euid);
132# endif /* 0 */
133		errno = save_errno;
134		return false;
135	}
136#else /* !HASFLOCK */
137
138	while ((i = flock(fd, type)) < 0 && errno == EINTR)
139		continue;
140	if (i >= 0)
141		return true;
142	save_errno = errno;
143
144	if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK)
145	{
146# if 0
147		int omode = fcntl(fd, F_GETFL, NULL);
148		int euid = (int) geteuid();
149
150		syslog(LOG_ERR, "cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
151		       filename, ext, fd, type, omode, euid);
152# endif /* 0 */
153		errno = save_errno;
154		return false;
155	}
156#endif /* !HASFLOCK */
157	errno = save_errno;
158	return false;
159}
160/*
161** SMDB_OPEN_DATABASE -- Opens a database.
162**
163**	This opens a database. If type is SMDB_DEFAULT it tries to
164**	use a DB1 or DB2 hash. If that isn't available, it will try
165**	to use NDBM. If a specific type is given it will try to open
166**	a database of that type.
167**
168**	Parameters:
169**		database -- An pointer to a SMDB_DATABASE pointer where the
170**			   opened database will be stored. This should
171**			   be unallocated.
172**		db_name -- The name of the database to open. Do not include
173**			  the file name extension.
174**		mode -- The mode to set on the database file or files.
175**		mode_mask -- Mode bits that must match on an opened database.
176**		sff -- Flags to safefile.
177**		type -- The type of database to open. Supported types
178**		       vary depending on what was compiled in.
179**		user_info -- Information on the user to use for file
180**			    permissions.
181**		params -- Params specific to the database being opened.
182**			 Only supports some DB hash options right now
183**			 (see smdb_db_open() for details).
184**
185**	Returns:
186**		SMDBE_OK -- Success.
187**		Anything else is an error. Look up more info about the
188**		error in the comments for the specific open() used.
189*/
190
191int
192smdb_open_database(database, db_name, mode, mode_mask, sff, type, user_info,
193		   params)
194	SMDB_DATABASE **database;
195	char *db_name;
196	int mode;
197	int mode_mask;
198	long sff;
199	SMDB_DBTYPE type;
200	SMDB_USER_INFO *user_info;
201	SMDB_DBPARAMS *params;
202{
203	bool type_was_default = false;
204
205	if (type == SMDB_TYPE_DEFAULT)
206	{
207		type_was_default = true;
208#ifdef NEWDB
209		type = SMDB_TYPE_HASH;
210#else /* NEWDB */
211# ifdef NDBM
212		type = SMDB_TYPE_NDBM;
213# endif /* NDBM */
214#endif /* NEWDB */
215	}
216
217	if (type == SMDB_TYPE_DEFAULT)
218		return SMDBE_UNKNOWN_DB_TYPE;
219
220	if ((strncmp(type, SMDB_TYPE_HASH, SMDB_TYPE_HASH_LEN) == 0) ||
221	    (strncmp(type, SMDB_TYPE_BTREE, SMDB_TYPE_BTREE_LEN) == 0))
222	{
223#ifdef NEWDB
224		int result;
225
226		result = smdb_db_open(database, db_name, mode, mode_mask, sff,
227				      type, user_info, params);
228# ifdef NDBM
229		if (result == ENOENT && type_was_default)
230			type = SMDB_TYPE_NDBM;
231		else
232# endif /* NDBM */
233			return result;
234#else /* NEWDB */
235		return SMDBE_UNSUPPORTED_DB_TYPE;
236#endif /* NEWDB */
237	}
238
239	if (strncmp(type, SMDB_TYPE_NDBM, SMDB_TYPE_NDBM_LEN) == 0)
240	{
241#ifdef NDBM
242		int result;
243
244		result = smdb_ndbm_open(database, db_name, mode, mode_mask,
245					sff, type, user_info, params);
246		return result;
247#else /* NDBM */
248		return SMDBE_UNSUPPORTED_DB_TYPE;
249#endif /* NDBM */
250	}
251
252	return SMDBE_UNKNOWN_DB_TYPE;
253}
254/*
255** SMDB_ADD_EXTENSION -- Adds an extension to a file name.
256**
257**	Just adds a . followed by a string to a db_name if there
258**	is room and the db_name does not already have that extension.
259**
260**	Parameters:
261**		full_name -- The final file name.
262**		max_full_name_len -- The max length for full_name.
263**		db_name -- The name of the db.
264**		extension -- The extension to add.
265**
266**	Returns:
267**		SMDBE_OK -- Success.
268**		Anything else is an error. Look up more info about the
269**		error in the comments for the specific open() used.
270*/
271
272int
273smdb_add_extension(full_name, max_full_name_len, db_name, extension)
274	char *full_name;
275	int max_full_name_len;
276	char *db_name;
277	char *extension;
278{
279	int extension_len;
280	int db_name_len;
281
282	if (full_name == NULL || db_name == NULL || extension == NULL)
283		return SMDBE_INVALID_PARAMETER;
284
285	extension_len = strlen(extension);
286	db_name_len = strlen(db_name);
287
288	if (extension_len + db_name_len + 2 > max_full_name_len)
289		return SMDBE_DB_NAME_TOO_LONG;
290
291	if (db_name_len < extension_len + 1 ||
292	    db_name[db_name_len - extension_len - 1] != '.' ||
293	    strcmp(&db_name[db_name_len - extension_len], extension) != 0)
294		(void) sm_snprintf(full_name, max_full_name_len, "%s.%s",
295				   db_name, extension);
296	else
297		(void) sm_strlcpy(full_name, db_name, max_full_name_len);
298
299	return SMDBE_OK;
300}
301/*
302**  SMDB_LOCK_FILE -- Locks the database file.
303**
304**	Locks the actual database file.
305**
306**	Parameters:
307**		lock_fd -- The resulting descriptor for the locked file.
308**		db_name -- The name of the database without extension.
309**		mode -- The open mode.
310**		sff -- Flags to safefile.
311**		extension -- The extension for the file.
312**
313**	Returns:
314**		SMDBE_OK -- Success, otherwise errno.
315*/
316
317int
318smdb_lock_file(lock_fd, db_name, mode, sff, extension)
319	int *lock_fd;
320	char *db_name;
321	int mode;
322	long sff;
323	char *extension;
324{
325	int result;
326	char file_name[MAXPATHLEN];
327
328	result = smdb_add_extension(file_name, sizeof file_name, db_name,
329				    extension);
330	if (result != SMDBE_OK)
331		return result;
332
333	*lock_fd = safeopen(file_name, mode & ~O_TRUNC, DBMMODE, sff);
334	if (*lock_fd < 0)
335		return errno;
336
337	return SMDBE_OK;
338}
339/*
340**  SMDB_UNLOCK_FILE -- Unlocks a file
341**
342**	Unlocks a file.
343**
344**	Parameters:
345**		lock_fd -- The descriptor for the locked file.
346**
347**	Returns:
348**		SMDBE_OK -- Success, otherwise errno.
349*/
350
351int
352smdb_unlock_file(lock_fd)
353	int lock_fd;
354{
355	int result;
356
357	result = close(lock_fd);
358	if (result != 0)
359		return errno;
360
361	return SMDBE_OK;
362}
363/*
364**  SMDB_LOCK_MAP -- Locks a database.
365**
366**	Parameters:
367**		database -- database description.
368**		type -- type of the lock.  Bits can be:
369**			LOCK_EX -- exclusive lock.
370**			LOCK_NB -- non-blocking.
371**
372**	Returns:
373**		SMDBE_OK -- Success, otherwise errno.
374*/
375
376int
377smdb_lock_map(database, type)
378	SMDB_DATABASE *database;
379	int type;
380{
381	int fd;
382
383	fd = database->smdb_lockfd(database);
384	if (fd < 0)
385		return SMDBE_NOT_FOUND;
386	if (!smdb_lockfile(fd, type))
387		return SMDBE_LOCK_NOT_GRANTED;
388	return SMDBE_OK;
389}
390/*
391**  SMDB_UNLOCK_MAP -- Unlocks a database
392**
393**	Parameters:
394**		database -- database description.
395**
396**	Returns:
397**		SMDBE_OK -- Success, otherwise errno.
398*/
399
400int
401smdb_unlock_map(database)
402	SMDB_DATABASE *database;
403{
404	int fd;
405
406	fd = database->smdb_lockfd(database);
407	if (fd < 0)
408		return SMDBE_NOT_FOUND;
409	if (!smdb_lockfile(fd, LOCK_UN))
410		return SMDBE_LOCK_NOT_HELD;
411	return SMDBE_OK;
412}
413/*
414**  SMDB_SETUP_FILE -- Gets db file ready for use.
415**
416**	Makes sure permissions on file are safe and creates it if it
417**	doesn't exist.
418**
419**	Parameters:
420**		db_name -- The name of the database without extension.
421**		extension -- The extension.
422**		sff -- Flags to safefile.
423**		mode_mask -- Mode bits that must match.
424**		user_info -- Information on the user to use for file
425**			    permissions.
426**		stat_info -- A place to put the stat info for the file.
427**	Returns:
428**		SMDBE_OK -- Success, otherwise errno.
429*/
430
431int
432smdb_setup_file(db_name, extension, mode_mask, sff, user_info, stat_info)
433	char *db_name;
434	char *extension;
435	int mode_mask;
436	long sff;
437	SMDB_USER_INFO *user_info;
438	struct stat *stat_info;
439{
440	int st;
441	int result;
442	char db_file_name[MAXPATHLEN];
443
444	result = smdb_add_extension(db_file_name, sizeof db_file_name, db_name,
445				    extension);
446	if (result != SMDBE_OK)
447		return result;
448
449	st = safefile(db_file_name, user_info->smdbu_id,
450		      user_info->smdbu_group_id, user_info->smdbu_name,
451		      sff, mode_mask, stat_info);
452	if (st != 0)
453		return st;
454
455	return SMDBE_OK;
456}
457/*
458**  SMDB_FILECHANGED -- Checks to see if a file changed.
459**
460**	Compares the passed in stat_info with a current stat on
461**	the passed in file descriptor. Check filechanged for
462**	return values.
463**
464**	Parameters:
465**		db_name -- The name of the database without extension.
466**		extension -- The extension.
467**		db_fd -- A file descriptor for the database file.
468**		stat_info -- An old stat_info.
469**	Returns:
470**		SMDBE_OK -- Success, otherwise errno.
471*/
472
473int
474smdb_filechanged(db_name, extension, db_fd, stat_info)
475	char *db_name;
476	char *extension;
477	int db_fd;
478	struct stat *stat_info;
479{
480	int result;
481	char db_file_name[MAXPATHLEN];
482
483	result = smdb_add_extension(db_file_name, sizeof db_file_name, db_name,
484				    extension);
485	if (result != SMDBE_OK)
486		return result;
487	return filechanged(db_file_name, db_fd, stat_info);
488}
489/*
490** SMDB_PRINT_AVAILABLE_TYPES -- Prints the names of the available types.
491**
492**	Parameters:
493**		None
494**
495**	Returns:
496**		None
497*/
498
499void
500smdb_print_available_types()
501{
502#ifdef NDBM
503	printf("dbm\n");
504#endif /* NDBM */
505#ifdef NEWDB
506	printf("hash\n");
507	printf("btree\n");
508#endif /* NEWDB */
509}
510/*
511** SMDB_DB_DEFINITION -- Given a database type, return database definition
512**
513**	Reads though a structure making an association with the database
514**	type and the required cpp define from sendmail/README.
515**	List size is dynamic and must be NULL terminated.
516**
517**	Parameters:
518**		type -- The name of the database type.
519**
520**	Returns:
521**		definition for type, otherwise NULL.
522*/
523
524typedef struct
525{
526	SMDB_DBTYPE type;
527	char *dbdef;
528} dbtype;
529
530static dbtype DatabaseDefs[] =
531{
532	{ SMDB_TYPE_HASH,	"NEWDB" },
533	{ SMDB_TYPE_BTREE,	"NEWDB" },
534	{ SMDB_TYPE_NDBM,	"NDBM"	},
535	{ NULL,			"OOPS"	}
536};
537
538char *
539smdb_db_definition(type)
540	SMDB_DBTYPE type;
541{
542	dbtype *ptr = DatabaseDefs;
543
544	while (ptr != NULL && ptr->type != NULL)
545	{
546		if (strcmp(type, ptr->type) == 0)
547			return ptr->dbdef;
548		ptr++;
549	}
550	return NULL;
551}
552