1 |
/* |
2 |
* Ascend Panic |
3 |
* by Mark Thomas |
4 |
* Created: 1997.05.15 |
5 |
* Version: $Revision: 1.1 $ |
6 |
* Version control file: $RCSfile: ascPanic.c,v $ |
7 |
* Date last modified: $Date: 1997/07/18 11:43:21 $ |
8 |
* Last modified by: $Author: mthomas $ |
9 |
* |
10 |
* This file is part of the Ascend Language Interpreter. |
11 |
* |
12 |
* Copyright (C) 1997 Carnegie Mellon University |
13 |
* |
14 |
* The Ascend Language Interpreter is free software; you can redistribute |
15 |
* it and/or modify it under the terms of the GNU General Public License as |
16 |
* published by the Free Software Foundation; either version 2 of the |
17 |
* License, or (at your option) any later version. |
18 |
* |
19 |
* The Ascend Language Interpreter is distributed in hope that it will be |
20 |
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
22 |
* General Public License for more details. |
23 |
* |
24 |
* You should have received a copy of the GNU General Public License |
25 |
* along with the program; if not, write to the Free Software Foundation, |
26 |
* Inc., 675 Mass Ave, Cambridge, MA 02139 USA. Check the file named |
27 |
* COPYING. |
28 |
* |
29 |
*/ |
30 |
|
31 |
/* ChangeLog |
32 |
* |
33 |
* 10/13/2005 Added callback functionality & ability to cancel exit() |
34 |
* for use in unit test. Changed sprintf's to snprintf's |
35 |
* to avoid buffer overflow (J.D. St.Clair) |
36 |
*/ |
37 |
|
38 |
#include <stdarg.h> |
39 |
#include "utilities/ascConfig.h" |
40 |
#include "utilities/ascPanic.h" |
41 |
|
42 |
/* PANIC_MSG_MAXLEN |
43 |
* The maximum length of the panic message. Used to create a buffer |
44 |
* to hold the message. |
45 |
*/ |
46 |
#define PANIC_MSG_MAXLEN 2047 |
47 |
|
48 |
|
49 |
/* THIS IS SUPERCEDED BY EXTERNAL UNIT TEST FOR ascPanic.c */ |
50 |
/* PANIC_TEST |
51 |
* Define this cpp macro to build a standalone program to test the |
52 |
* panic functions---since they shouldn't be called during normal |
53 |
* operation: cc -DPANIC_TEST -I.. ascpanic.c -o panictest |
54 |
* |
55 |
* TEST_OUTPUT_GOOD is a writable file to write a panic message to |
56 |
* TEST_OUTPUT_BAD is an unwritable file to write a panic message to |
57 |
*/ |
58 |
#ifdef PANIC_TEST |
59 |
FILE *ASCERR = stderr; |
60 |
#define exit(x) FPRINTF(ASCERR, "<<<call exit(%d) if !test>>>\n", (x)); return |
61 |
#define TEST_OUTPUT_GOOD "/tmp/Asc_Panic.out" |
62 |
#define TEST_OUTPUT_BAD "/foo/bar/baz/cow/grumble/Asc_Panic.out" |
63 |
#endif /* PANIC_TEST */ |
64 |
|
65 |
/* |
66 |
* f_panic_callback_func |
67 |
* Holds a pointer to a callback function if registered using |
68 |
* Asc_PanicSetCallback(). If NULL (the default), nothing is called. |
69 |
*/ |
70 |
static PanicCallbackFunc f_panic_callback_func = NULL; |
71 |
|
72 |
#ifdef __WIN32__ |
73 |
/* |
74 |
* On Windows only, flag to enable/disable display of the MessageBox |
75 |
* in Asc_Panic(). |
76 |
*/ |
77 |
static int f_display_MessageBox = TRUE; |
78 |
#endif |
79 |
|
80 |
/* |
81 |
* g_panic_output |
82 |
* Holds the name of the file in which to write panic messages. |
83 |
* Use the Asc_PanicSetOutfile(filename) function to set it. |
84 |
*/ |
85 |
static char g_panic_outfile[PATH_MAX]; |
86 |
|
87 |
|
88 |
void Asc_Panic(CONST int status, CONST char *function, |
89 |
CONST char *format, ...) |
90 |
{ |
91 |
char msg[PANIC_MSG_MAXLEN]; /* The message that will be printed */ |
92 |
size_t p; /* The current length of the msg array */ |
93 |
FILE *outfile; /* The file to save the message into */ |
94 |
va_list args; /* The arguments to print */ |
95 |
int cancel = FALSE; /* If non-zero, do not call exit(). Default is to exit() */ |
96 |
|
97 |
assert(NULL != ASCERR); /* fail loudly so know can't write msg. Can't use asc_assert(). */ |
98 |
/* |
99 |
* Give the name of the function where the panic occurred |
100 |
*/ |
101 |
if( function != NULL ) { |
102 |
snprintf( msg, PANIC_MSG_MAXLEN-2, "ASCEND PANIC!! in function \"%s\"\n", function ); |
103 |
} else { |
104 |
snprintf( msg, PANIC_MSG_MAXLEN-2, "ASCEND PANIC!!\n" ); |
105 |
} |
106 |
p = strlen(msg); |
107 |
|
108 |
/* |
109 |
* Add the variable args to the panic message using the format "format" |
110 |
*/ |
111 |
va_start(args, format); |
112 |
vsnprintf( (msg+p), PANIC_MSG_MAXLEN-p-2, format, args ); |
113 |
va_end(args); |
114 |
|
115 |
p = strlen(msg); |
116 |
msg[p++] = '\n'; |
117 |
msg[p++] = '\0'; |
118 |
|
119 |
/* |
120 |
* Print the message to ASCERR |
121 |
*/ |
122 |
if (ASCERR != NULL) |
123 |
FPRINTF(ASCERR, msg); |
124 |
|
125 |
/* |
126 |
* Write the message to g_panic_outfile if it is not empty |
127 |
* and we can actually write to that location. Print a |
128 |
* message on ASCERR (if valid) saying that we did that. |
129 |
*/ |
130 |
if(( g_panic_outfile[0] != '\0' ) |
131 |
&& ( (outfile=fopen(g_panic_outfile,"w")) != NULL )) |
132 |
{ |
133 |
FPRINTF(outfile, msg); |
134 |
if( ASCERR != NULL ) { |
135 |
FPRINTF(ASCERR, "ASCEND PANIC: Error message written to %s\n", g_panic_outfile); |
136 |
} |
137 |
fclose(outfile); |
138 |
} |
139 |
|
140 |
/* |
141 |
* Call the registered callback function, if any. |
142 |
*/ |
143 |
if (NULL != f_panic_callback_func) { |
144 |
cancel = (*f_panic_callback_func)(status); |
145 |
} |
146 |
|
147 |
#ifdef __WIN32__ |
148 |
/* |
149 |
* Display msg in a MessageBox under Windows unless turned off |
150 |
*/ |
151 |
if (FALSE != f_display_MessageBox) { |
152 |
(void)MessageBeep(MB_ICONEXCLAMATION); |
153 |
MessageBox(NULL, msg, "Fatal Error in ASCEND", |
154 |
(UINT)(MB_ICONSTOP | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND)); |
155 |
} |
156 |
if (0 == cancel) |
157 |
ExitProcess((UINT)status); |
158 |
#endif |
159 |
|
160 |
if (0 == cancel) |
161 |
exit(status); |
162 |
} |
163 |
|
164 |
|
165 |
void Asc_PanicSetOutfile(CONST char *filename) |
166 |
{ |
167 |
if( filename != NULL ) { |
168 |
strncpy( g_panic_outfile, filename, PATH_MAX-1 ); |
169 |
g_panic_outfile[PATH_MAX-1] = '\0'; |
170 |
} else { |
171 |
g_panic_outfile[0] = '\0'; |
172 |
} |
173 |
} |
174 |
|
175 |
PanicCallbackFunc Asc_PanicSetCallback(PanicCallbackFunc func) |
176 |
{ |
177 |
PanicCallbackFunc old_func = f_panic_callback_func; |
178 |
f_panic_callback_func = func; |
179 |
return old_func; |
180 |
} |
181 |
|
182 |
|
183 |
void Asc_PanicDisplayMessageBox(int TRUE_or_FALSE) |
184 |
{ |
185 |
#ifdef __WIN32__ |
186 |
f_display_MessageBox = TRUE_or_FALSE; |
187 |
#else |
188 |
UNUSED_PARAMETER(TRUE_or_FALSE); |
189 |
#endif |
190 |
} |
191 |
|
192 |
/* THIS IS SUPERCEDED BY EXTERNAL UNIT TEST FOR ascPanic.c */ |
193 |
#ifdef PANIC_TEST |
194 |
/* |
195 |
* main |
196 |
* A test driver for Asc_Panic() and Asc_PanicSetOutfile(). Needed |
197 |
* since we should never actually call Asc_Panic under normal |
198 |
* operation. (HA HA!) |
199 |
*/ |
200 |
int main(void) |
201 |
{ |
202 |
PRINTF("==>Testing Asc_Panic with no output file and single arg\n"); |
203 |
Asc_Panic(1, NULL, "Message generate by Asc_Panic, single string arg\n"); |
204 |
PRINTF("<==\n"); |
205 |
|
206 |
PRINTF("==>Testing Asc_Panic with no output file and multiple args\n"); |
207 |
Asc_Panic(2, "main", |
208 |
"%s%s\n\t%d%s%d%s\n", "Message generated by ", "Asc_Panic,", |
209 |
5, " string args and ", 3, " integer args"); |
210 |
PRINTF("<==\n"); |
211 |
|
212 |
PRINTF("==>Testing Asc_Panic with mismatched format/args: too many args\n"); |
213 |
Asc_Panic(3, "main", |
214 |
"%s%s%s\n", "Message generated by ", "Asc_Panic, ", |
215 |
"too many args", " for format"); |
216 |
PRINTF("<==\n"); |
217 |
|
218 |
PRINTF("==>Testing Asc_Panic with mismatched format/args: too few args\n"); |
219 |
Asc_Panic(4, NULL, |
220 |
"%s%s%s%s\n", "Message generated by ", "Asc_Panic, ", |
221 |
"too few args"); |
222 |
PRINTF("<==\n"); |
223 |
|
224 |
PRINTF("==>Testing Asc_Panic with file %s\n", TEST_OUTPUT_GOOD); |
225 |
Asc_PanicSetOutfile(TEST_OUTPUT_GOOD); |
226 |
Asc_Panic(5, NULL, |
227 |
"%s%s\n\t%s%s\n", "Message generated by ", "Asc_Panic, ", |
228 |
"should be written into ", TEST_OUTPUT_GOOD); |
229 |
PRINTF("<==\n"); |
230 |
|
231 |
PRINTF("==>Testing Asc_Panic with file %s\n", TEST_OUTPUT_BAD); |
232 |
Asc_PanicSetOutfile(TEST_OUTPUT_BAD); |
233 |
Asc_Panic(6, "main", |
234 |
"Message generated by Asc_Panic\n" |
235 |
"\tWrite to unwritable file %s", TEST_OUTPUT_BAD); |
236 |
PRINTF("<==\n"); |
237 |
|
238 |
PRINTF(">>End of tests\n"); |
239 |
return 0; |
240 |
} |
241 |
#endif /* PANIC_TEST */ |