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