1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997, 1998, 1999
5  *	Sleepycat Software.  All rights reserved.
6  */
7 
8 
9 #include "db_config.h"
10 
11 #ifndef lint
12 static const char sccsid[] = "@(#)db_err.c	11.10 (Sleepycat) 11/8/99";
13 #endif /* not lint */
14 
15 #ifndef NO_SYSTEM_INCLUDES
16 #include <sys/types.h>
17 
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #ifndef _MSC_VER /* WIN32 */
24 #ifdef __STDC__
25 #include <stdarg.h>
26 #else
27 #include <varargs.h>
28 #endif
29 #endif
30 #endif
31 
32 #include "db_int.h"
33 #include "db_shash.h"
34 #include "lock.h"
35 #include "lock_ext.h"
36 #include "log.h"
37 #include "log_ext.h"
38 #include "mp.h"
39 #include "mp_ext.h"
40 #include "txn.h"
41 #include "txn_ext.h"
42 #include "common_ext.h"
43 #include "db_auto.h"
44 
45 static void CDB___db_errcall __P((const DB_ENV *, int, int, const char *, va_list));
46 static void CDB___db_errfile __P((const DB_ENV *, int, int, const char *, va_list));
47 
48 /*
49  * CDB___db_fchk --
50  *	General flags checking routine.
51  *
52  * PUBLIC: int CDB___db_fchk __P((DB_ENV *, const char *, u_int32_t, u_int32_t));
53  */
54 int
CDB___db_fchk(dbenv,name,flags,ok_flags)55 CDB___db_fchk(dbenv, name, flags, ok_flags)
56 	DB_ENV *dbenv;
57 	const char *name;
58 	u_int32_t flags, ok_flags;
59 {
60 	return (LF_ISSET(~ok_flags) ? CDB___db_ferr(dbenv, name, 0) : 0);
61 }
62 
63 /*
64  * CDB___db_fcchk --
65  *	General combination flags checking routine.
66  *
67  * PUBLIC: int CDB___db_fcchk
68  * PUBLIC:    __P((DB_ENV *, const char *, u_int32_t, u_int32_t, u_int32_t));
69  */
70 int
CDB___db_fcchk(dbenv,name,flags,flag1,flag2)71 CDB___db_fcchk(dbenv, name, flags, flag1, flag2)
72 	DB_ENV *dbenv;
73 	const char *name;
74 	u_int32_t flags, flag1, flag2;
75 {
76 	return (LF_ISSET(flag1) &&
77 	    LF_ISSET(flag2) ? CDB___db_ferr(dbenv, name, 1) : 0);
78 }
79 
80 /*
81  * CDB___db_ferr --
82  *	Common flag errors.
83  *
84  * PUBLIC: int CDB___db_ferr __P((const DB_ENV *, const char *, int));
85  */
86 int
CDB___db_ferr(dbenv,name,iscombo)87 CDB___db_ferr(dbenv, name, iscombo)
88 	const DB_ENV *dbenv;
89 	const char *name;
90 	int iscombo;
91 {
92 	CDB___db_err(dbenv, "illegal flag %sspecified to %s",
93 	    iscombo ? "combination " : "", name);
94 	return (EINVAL);
95 }
96 
97 /*
98  * CDB___db_pgerr --
99  *	Error when unable to retrieve a specified page.
100  *
101  * PUBLIC: int CDB___db_pgerr __P((DB *, db_pgno_t));
102  */
103 int
CDB___db_pgerr(dbp,pgno)104 CDB___db_pgerr(dbp, pgno)
105 	DB *dbp;
106 	db_pgno_t pgno;
107 {
108 	/*
109 	 * Three things are certain:
110 	 * Death, taxes, and lost data.
111 	 * Guess which has occurred.
112 	 */
113 	CDB___db_err(dbp->dbenv,
114 	    "unable to create/retrieve page %lu", (u_long)pgno);
115 	return (CDB___db_panic(dbp->dbenv, EIO));
116 }
117 
118 /*
119  * CDB___db_pgfmt --
120  *	Error when a page has the wrong format.
121  *
122  * PUBLIC: int CDB___db_pgfmt __P((DB *, db_pgno_t));
123  */
124 int
CDB___db_pgfmt(dbp,pgno)125 CDB___db_pgfmt(dbp, pgno)
126 	DB *dbp;
127 	db_pgno_t pgno;
128 {
129 	CDB___db_err(dbp->dbenv,
130 	    "page %lu: illegal page type or format", (u_long)pgno);
131 	return (CDB___db_panic(dbp->dbenv, EINVAL));
132 }
133 
134 #ifdef DIAGNOSTIC
135 /*
136  * __db_assert --
137  *	Error when an assertion fails.  Only checked if #DIAGNOSTIC defined.
138  *
139  * PUBLIC: #ifdef DIAGNOSTIC
140  * PUBLIC: void __db_assert __P((const char *, const char *, int));
141  * PUBLIC: #endif
142  */
143 void
__db_assert(failedexpr,file,line)144 __db_assert(failedexpr, file, line)
145 	const char *failedexpr, *file;
146 	int line;
147 {
148 	(void)fprintf(stderr,
149 	    "__db_assert: \"%s\" failed: file \"%s\", line %d\n",
150 	    failedexpr, file, line);
151 	fflush(stderr);
152 
153 	/* We want a stack trace of how this could possibly happen. */
154 	abort();
155 
156 	/* NOTREACHED */
157 }
158 #endif
159 
160 /*
161  * CDB___db_panic_msg --
162  *	Just report that someone else paniced.
163  *
164  * PUBLIC: int CDB___db_panic_msg __P((DB_ENV *));
165  */
166 int
CDB___db_panic_msg(dbenv)167 CDB___db_panic_msg(dbenv)
168 	DB_ENV *dbenv;
169 {
170 	CDB___db_err(dbenv, "region error detected; run recovery.");
171 	/* Hack to make fatal errors really fatal... */
172 	fprintf(stderr,"DB_RUNRECOVERY: Fatal error, run database recovery\n");
173 	exit(1);
174 	return (DB_RUNRECOVERY);
175 }
176 
177 /*
178  * CDB___db_panic --
179  *	Lock out the tree due to unrecoverable error.
180  *
181  * PUBLIC: int CDB___db_panic __P((DB_ENV *, int));
182  */
183 int
CDB___db_panic(dbenv,errval)184 CDB___db_panic(dbenv, errval)
185 	DB_ENV *dbenv;
186 	int errval;
187 {
188 	if (dbenv != NULL) {
189 		((REGENV *)((REGINFO *)dbenv->reginfo)->addr)->panic = 1;
190 
191 		dbenv->db_panic = errval;
192 
193 		CDB___db_err(dbenv, "PANIC: %s", CDB_db_strerror(errval));
194 
195 		if (dbenv->db_paniccall != NULL)
196 			dbenv->db_paniccall(dbenv, errval);
197 	}
198 
199 	/*
200 	 * Chaos reigns within.
201 	 * Reflect, repent, and reboot.
202 	 * Order shall return.
203 	 */
204 	/* Hack to make fatal errors really fatal... */
205 	fprintf(stderr,"DB_RUNRECOVERY: Fatal error, run database recovery\n");
206 	exit(1);
207 	return (DB_RUNRECOVERY);
208 }
209 
210 /*
211  * CDB_db_strerror --
212  *	ANSI C strerror(3) for DB.
213  */
214 char *
CDB_db_strerror(error)215 CDB_db_strerror(error)
216 	int error;
217 {
218 	if (error == 0)
219 		return ("Successful return: 0");
220 	if (error > 0)
221 		return (strerror(error));
222 
223 	/*
224 	 * !!!
225 	 * The Tcl API requires that some of these return strings be compared
226 	 * against strings stored in application scripts.  So, any of these
227 	 * errors that do not invariably result in a Tcl exception may not be
228 	 * altered.
229 	 */
230 	switch (error) {
231 	case DB_INCOMPLETE:
232 		return ("DB_INCOMPLETE: Cache flush was unable to complete");
233 	case DB_KEYEMPTY:
234 		return ("DB_KEYEMPTY: Non-existent key/data pair");
235 	case DB_KEYEXIST:
236 		return ("DB_KEYEXIST: Key/data pair already exists");
237 	case DB_LOCK_DEADLOCK:
238 		return
239 		    ("DB_LOCK_DEADLOCK: Locker killed to resolve a deadlock");
240 	case DB_LOCK_NOTGRANTED:
241 		return ("DB_LOCK_NOTGRANTED: Lock not granted");
242 	case DB_NOTFOUND:
243 		return ("DB_NOTFOUND: No matching key/data pair found");
244 	case DB_OLD_VERSION:
245 		return ("DB_OLDVERSION: Database requires a version upgrade");
246 	case DB_RUNRECOVERY:
247 		return ("DB_RUNRECOVERY: Fatal error, run database recovery");
248 	default: {
249 		/*
250 		 * !!!
251 		 * Room for a 64-bit number + slop.  This buffer is only used
252 		 * if we're given an unknown error, which should never happen.
253 		 * Note, however, we're no longer thread-safe if it does.
254 		 */
255 		static char ebuf[40];
256 
257 		(void)snprintf(ebuf, sizeof(ebuf), "Unknown error: %d", error);
258 		return(ebuf);
259 	}
260 	}
261 }
262 
263 /*
264  * CDB___db_err --
265  *	Standard DB error routine.  The same as db_errx, except that we
266  *	don't write to stderr if no output mechanism was specified.
267  *
268  * PUBLIC: #ifdef __STDC__
269  * PUBLIC: void CDB___db_err __P((const DB_ENV *, const char *, ...));
270  * PUBLIC: #else
271  * PUBLIC: void CDB___db_err();
272  * PUBLIC: #endif
273  */
274 void
275 #if defined( __STDC__) || defined(_MSC_VER) /* WIN32 */
CDB___db_err(const DB_ENV * dbenv,const char * fmt,...)276 CDB___db_err(const DB_ENV *dbenv, const char *fmt, ...)
277 #else
278 CDB___db_err(dbenv, fmt, va_alist)
279 	const DB_ENV *dbenv;
280 	const char *fmt;
281 	va_dcl
282 #endif
283 {
284 	va_list ap;
285 
286 #if defined(__STDC__) || defined(_MSC_VER) /* WIN32 */
287 	va_start(ap, fmt);
288 #else
289 	va_start(ap);
290 #endif
291 	CDB___db_real_err(dbenv, 0, 0, 0, fmt, ap);
292 
293 	va_end(ap);
294 }
295 
296 /*
297  * CDB___db_real_err --
298  *	All the DB error routines end up here.
299  *
300  * PUBLIC: void CDB___db_real_err
301  * PUBLIC:     __P((const DB_ENV *, int, int, int, const char *, va_list));
302  */
303 void
CDB___db_real_err(dbenv,error,error_set,stderr_default,fmt,ap)304 CDB___db_real_err(dbenv, error, error_set, stderr_default, fmt, ap)
305 	const DB_ENV *dbenv;
306 	int error, error_set, stderr_default;
307 	const char *fmt;
308 	va_list ap;
309 {
310 	if (dbenv != NULL && dbenv->db_errcall != NULL)
311 		CDB___db_errcall(dbenv, error, error_set, fmt, ap);
312 
313 	if (dbenv != NULL && dbenv->db_errfile != NULL)
314 		CDB___db_errfile(dbenv, error, error_set, fmt, ap);
315 
316 	if (stderr_default && (dbenv == NULL ||
317 	    (dbenv->db_errcall == NULL && dbenv->db_errfile == NULL)))
318 		CDB___db_errfile(NULL, error, error_set, fmt, ap);
319 }
320 
321 /*
322  * CDB___db_errcall --
323  *	Do the error message work for callback functions.
324  */
325 static void
CDB___db_errcall(dbenv,error,error_set,fmt,ap)326 CDB___db_errcall(dbenv, error, error_set, fmt, ap)
327 	const DB_ENV *dbenv;
328 	int error, error_set;
329 	const char *fmt;
330 	va_list ap;
331 {
332 	char *p;
333 	char __errbuf[2048];	/* XXX: END OF THE STACK DON'T TRUST SPRINTF. */
334 
335 	p = __errbuf;
336 	if (fmt != NULL) {
337 		p += vsnprintf(__errbuf, sizeof(__errbuf), fmt, ap);
338 		if (error_set) {
339 			*p++ = ':';
340 			*p++ = ' ';
341 		}
342 	}
343 	if (error_set)
344 		(void)strcpy(p, CDB_db_strerror(error));
345 
346 	dbenv->db_errcall(dbenv->db_errpfx, __errbuf);
347 }
348 
349 /*
350  * CDB___db_errfile --
351  *	Do the error message work for FILE *s.
352  */
353 static void
CDB___db_errfile(dbenv,error,error_set,fmt,ap)354 CDB___db_errfile(dbenv, error, error_set, fmt, ap)
355 	const DB_ENV *dbenv;
356 	int error, error_set;
357 	const char *fmt;
358 	va_list ap;
359 {
360 	FILE *fp;
361 
362 	fp = dbenv == NULL ||
363 	    dbenv->db_errfile == NULL ? stderr : dbenv->db_errfile;
364 
365 	if (dbenv != NULL && dbenv->db_errpfx != NULL)
366 		(void)fprintf(fp, "%s: ", dbenv->db_errpfx);
367 	if (fmt != NULL) {
368 		(void)vfprintf(fp, fmt, ap);
369 		if (error_set)
370 			(void)fprintf(fp, ": ");
371 	}
372 	if (error_set)
373 		(void)fprintf(fp, "%s", CDB_db_strerror(error));
374 	(void)fprintf(fp, "\n");
375 	(void)fflush(fp);
376 }
377 
378 /*
379  * CDB___db_logmsg --
380  *	Write information into the DB log.
381  *
382  * PUBLIC: #ifdef __STDC__
383  * PUBLIC: int CDB___db_logmsg __P((DB_ENV *,
384  * PUBLIC:     DB_TXN *, const char *, u_int32_t, const char *, ...));
385  * PUBLIC: #else
386  * PUBLIC: int CDB___db_logmsg();
387  * PUBLIC: #endif
388  */
389 int
390 #if defined(__STDC__) || defined(_MSC_VER) /* WIN32 */
CDB___db_logmsg(DB_ENV * dbenv,DB_TXN * txnid,const char * opname,u_int32_t flags,const char * fmt,...)391 CDB___db_logmsg(DB_ENV *dbenv,
392     DB_TXN *txnid, const char *opname, u_int32_t flags, const char *fmt, ...)
393 #else
394 CDB___db_logmsg(dbenv, txnid, opname, flags, fmt, va_alist)
395 	DB_ENV *dbenv;
396 	DB_TXN *txnid;
397 	const char *opname, *fmt;
398 	u_int32_t flags;
399 	va_dcl
400 #endif
401 {
402 	DBT opdbt, msgdbt;
403 	DB_LSN lsn;
404 	va_list ap;
405 	char __logbuf[2048];	/* XXX: END OF THE STACK DON'T TRUST SPRINTF. */
406 
407 	if (!F_ISSET(dbenv, DB_ENV_LOGGING))
408 		return (0);
409 
410 	memset(&opdbt, 0, sizeof(opdbt));
411 	opdbt.data = (void *)opname;
412 	opdbt.size = strlen(opname) + 1;
413 
414 	memset(&msgdbt, 0, sizeof(msgdbt));
415 	msgdbt.data = __logbuf;
416 #if defined(__STDC__) || defined(_MSC_VER) /* WIN32 */
417 	va_start(ap, fmt);
418 #else
419 	va_start(ap);
420 #endif
421 	msgdbt.size = vsnprintf(__logbuf, sizeof(__logbuf), fmt, ap);
422 	va_end(ap);
423 
424 	return (CDB___db_debug_log(dbenv,
425 	    txnid, &lsn, flags, &opdbt, -1, &msgdbt, NULL, 0));
426 }
427