xref: /freebsd/contrib/sendmail/libsmdb/smdb.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
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
11 static 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 
33 SMDB_DATABASE *
34 smdb_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 
57 void
58 smdb_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 
79 static bool
80 smdb_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 
198 int
199 smdb_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 
277 int
278 smdb_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 
323 int
324 smdb_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 
358 int
359 smdb_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 
384 int
385 smdb_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 
409 int
410 smdb_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 
442 int
443 smdb_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 
485 int
486 smdb_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 
514 void
515 smdb_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 
539 typedef struct
540 {
541 	SMDB_DBTYPE type;
542 	char *dbdef;
543 } dbtype;
544 
545 static dbtype DatabaseDefs[] =
546 {
547 	{ SMDB_TYPE_HASH,	"NEWDB" },
548 	{ SMDB_TYPE_BTREE,	"NEWDB" },
549 	{ SMDB_TYPE_NDBM,	"NDBM"	},
550 	{ NULL,			"OOPS"	}
551 };
552 
553 char *
554 smdb_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