1 /*
2  * Copyright (c) 2000-2001 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 
11 #include <sm/gen.h>
12 SM_RCSID("@(#)$Id: assert.c,v 1.26 2003/12/05 22:45:24 ca Exp $")
13 
14 /*
15 **  Abnormal program termination and assertion checking.
16 **  For documentation, see assert.html.
17 */
18 
19 #include <signal.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 
23 #include <sm/assert.h>
24 #include <sm/exc.h>
25 #include <sm/io.h>
26 #include <sm/varargs.h>
27 
28 /*
29 **  Debug categories that are used to guard expensive assertion checks.
30 */
31 
32 SM_DEBUG_T SmExpensiveAssert = SM_DEBUG_INITIALIZER("sm_check_assert",
33 	"@(#)$Debug: sm_check_assert - check assertions $");
34 
35 SM_DEBUG_T SmExpensiveRequire = SM_DEBUG_INITIALIZER("sm_check_require",
36 	"@(#)$Debug: sm_check_require - check function preconditions $");
37 
38 SM_DEBUG_T SmExpensiveEnsure = SM_DEBUG_INITIALIZER("sm_check_ensure",
39 	"@(#)$Debug: sm_check_ensure - check function postconditions $");
40 
41 /*
42 **  Debug category: send self SIGSTOP on fatal error,
43 **  so that you can run a debugger on the stopped process.
44 */
45 
46 SM_DEBUG_T SmAbortStop = SM_DEBUG_INITIALIZER("sm_abort_stop",
47 	"@(#)$Debug: sm_abort_stop - stop process on fatal error $");
48 
49 /*
50 **  SM_ABORT_DEFAULTHANDLER -- Default procedure for abnormal program
51 **				termination.
52 **
53 **	The goal is to display an error message without disturbing the
54 **	process state too much, then dump core.
55 **
56 **	Parameters:
57 **		filename -- filename (can be NULL).
58 **		lineno -- line number.
59 **		msg -- message.
60 **
61 **	Returns:
62 **		doesn't return.
63 */
64 
65 static void
66 sm_abort_defaulthandler __P((
67 	const char *filename,
68 	int lineno,
69 	const char *msg));
70 
71 static void
sm_abort_defaulthandler(filename,lineno,msg)72 sm_abort_defaulthandler(filename, lineno, msg)
73 	const char *filename;
74 	int lineno;
75 	const char *msg;
76 {
77 	if (filename != NULL)
78 		sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s:%d: %s\n", filename,
79 			      lineno, msg);
80 	else
81 		sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s\n", msg);
82 	sm_io_flush(smioerr, SM_TIME_DEFAULT);
83 #ifdef SIGSTOP
84 	if (sm_debug_active(&SmAbortStop, 1))
85 		kill(getpid(), SIGSTOP);
86 #endif /* SIGSTOP */
87 	abort();
88 }
89 
90 /*
91 **  This is the action to be taken to cause abnormal program termination.
92 */
93 
94 static SM_ABORT_HANDLER_T SmAbortHandler = sm_abort_defaulthandler;
95 
96 /*
97 **  SM_ABORT_SETHANDLER -- Set handler for SM_ABORT()
98 **
99 **	This allows you to set a handler function for causing abnormal
100 **	program termination; it is called when a logic bug is detected.
101 **
102 **	Parameters:
103 **		f -- handler.
104 **
105 **	Returns:
106 **		none.
107 */
108 
109 void
sm_abort_sethandler(f)110 sm_abort_sethandler(f)
111 	SM_ABORT_HANDLER_T f;
112 {
113 	if (f == NULL)
114 		SmAbortHandler = sm_abort_defaulthandler;
115 	else
116 		SmAbortHandler = f;
117 }
118 
119 /*
120 **  SM_ABORT -- Call it when you have detected a logic bug.
121 **
122 **	Parameters:
123 **		fmt -- format string.
124 **		... -- arguments.
125 **
126 **	Returns:
127 **		doesn't.
128 */
129 
130 void SM_DEAD_D
131 #if SM_VA_STD
sm_abort(char * fmt,...)132 sm_abort(char *fmt, ...)
133 #else /* SM_VA_STD */
134 sm_abort(fmt, va_alist)
135 	char *fmt;
136 	va_dcl
137 #endif /* SM_VA_STD */
138 {
139 	char msg[128];
140 	SM_VA_LOCAL_DECL
141 
142 	SM_VA_START(ap, fmt);
143 	sm_vsnprintf(msg, sizeof msg, fmt, ap);
144 	SM_VA_END(ap);
145 	sm_abort_at(NULL, 0, msg);
146 }
147 
148 /*
149 **  SM_ABORT_AT -- Initiate abnormal program termination.
150 **
151 **	This is the low level function that is called to initiate abnormal
152 **	program termination.  It prints an error message and terminates the
153 **	program.  It is called by sm_abort and by the assertion macros.
154 **	If filename != NULL then filename and lineno specify the line of source
155 **	code at which the bug was detected.
156 **
157 **	Parameters:
158 **		filename -- filename (can be NULL).
159 **		lineno -- line number.
160 **		msg -- message.
161 **
162 **	Returns:
163 **		doesn't.
164 */
165 
166 void SM_DEAD_D
sm_abort_at(filename,lineno,msg)167 sm_abort_at(filename, lineno, msg)
168 	const char *filename;
169 	int lineno;
170 	const char *msg;
171 {
172 	SM_TRY
173 		(*SmAbortHandler)(filename, lineno, msg);
174 	SM_EXCEPT(exc, "*")
175 		sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
176 			      "exception raised by abort handler:\n");
177 		sm_exc_print(exc, smioerr);
178 		sm_io_flush(smioerr, SM_TIME_DEFAULT);
179 	SM_END_TRY
180 
181 	/*
182 	**  SmAbortHandler isn't supposed to return.
183 	**  Since it has, let's make sure that the program is terminated.
184 	*/
185 
186 	abort();
187 }
188