[misc] Add vsc_error_reset().
[vsc-common.git] / misc / error.c
1 /*
2  * error.c: Library of miscellaneous stuff for other libvsc* libraries
3  *
4  * Copyright (C) 2009 Matthias Bolte <matthias.bolte@googlemail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
19  */
20
21 #include <stdarg.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25
26 #include "../include/libvscmisc/assert.h"
27 #include "../include/libvscmisc/error.h"
28 #include "../include/libvscmisc/list.h"
29 #include "../include/libvscmisc/memory.h"
30
31 #define _ERROR__MESSAGE_SIZE 4096
32
33 static const char *_error_default_message =
34    "Could not allocate memory for error message";
35
36 static struct VscError _error_default = {
37         .next = NULL,
38         .occured = TRUE,
39         .code = VSC__ERROR_CODE__OUT_OF_MEMORY,
40         .message = NULL,
41         .file = "None",
42         .line = -1,
43         .function = "None",
44         .timestamp = {0, 0},
45 };
46
47 static void
48 _error_fprint_list (struct VscError *error, FILE *stream, int indent)
49 {
50         int i;
51         struct VscError *current = NULL;
52         struct VscError *relevant = NULL;
53
54         VSC__ASSERT (stream != NULL);
55
56         if (indent == 0) {
57                 relevant = vsc_error_get_relevant (error);
58         }
59
60         for (current = error; current != NULL; current = current->next) {
61                 fprintf (stream, "             ");
62
63                 if (relevant == current) {
64                         fprintf (stream, "> ");
65                 } else {
66                         fprintf (stream, "  ");
67                 }
68
69                 for (i = 0; i < indent; ++i) {
70                         fprintf (stream, " ");
71                 }
72
73                 fprintf (stream, "%s:%d %s : Error [%s]",
74                          current->file, current->line, current->function,
75                          vsc_error_string (current->code));
76
77                 if (current->message != NULL) {
78                         fprintf (stream, " %s\n", current->message);
79                 } else {
80                         fprintf (stream, "\n");
81                 }
82
83                 if (current->branch != NULL) {
84                         _error_fprint_list (current->branch, stream, indent + 2);
85                 }
86         }
87 }
88
89 static void
90 _error_report_v (struct VscError *error, enum VscErrorCode code,
91                  const char *file, int line, const char *function,
92                  const char *message, va_list args)
93 {
94         VSC__ASSERT (error != NULL);
95         VSC__ASSERT (! error->occured);
96         VSC__ASSERT (error->message == NULL);
97         VSC__ASSERT (code != VSC__ERROR_CODE__UNDEFINED);
98         VSC__ASSERT (file != NULL);
99         VSC__ASSERT (line >= 0);
100         VSC__ASSERT (function != NULL);
101
102         error->occured = TRUE;
103         error->code = code;
104         error->file = file;
105         error->line = line;
106         error->function = function;
107
108         if (message == NULL) {
109                 error->message = NULL;
110         } else {
111                 error->message = calloc (1, _ERROR__MESSAGE_SIZE);
112
113                 if (error->message == NULL) {
114                         error->message = (char *) _error_default_message;
115                 } else {
116                         vsnprintf (error->message, _ERROR__MESSAGE_SIZE, message, args);
117                 }
118         }
119
120         gettimeofday (&error->timestamp, NULL);
121 }
122
123 void
124 vsc_error_init (struct VscError *error)
125 {
126         VSC__ASSERT (error != NULL);
127
128         error->next = NULL;
129         error->branch = NULL;
130         error->occured = FALSE;
131         error->code = VSC__ERROR_CODE__UNDEFINED;
132         error->file = NULL;
133         error->line = -1;
134         error->function = NULL;
135         error->message = NULL;
136
137         memset (&error->timestamp, 0, sizeof (struct timeval));
138 }
139
140 void
141 vsc_error_cleanup (struct VscError *error)
142 {
143         struct VscError *current;
144         struct VscError *next;
145
146         VSC__ASSERT (error != NULL);
147         VSC__ASSERT (error != &_error_default);
148
149         if (error == &_error_default) {
150                 /* Don't cleanup the default error */
151                 return;
152         }
153
154         if (error->message != _error_default_message) {
155                 vsc_free (&error->message);
156         } else {
157                 error->message = NULL;
158         }
159
160         /*
161          * Free the error list messages if they are allocated (not the default
162          * error message) and free the error structs too, if they are allocated
163          * (not the default error). But do not free the first element of the list
164          * because we do not "own" its memory.
165          *
166          * Cleanup and free branches, because they must always be allocated.
167          */
168         current = error->next;
169
170         while (current != NULL) {
171                 next = current->next;
172
173                 if (current->branch != NULL) {
174                         vsc_error_free (&current->branch);
175                 }
176
177                 if (current->message != _error_default_message) {
178                         vsc_free (&current->message);
179                 } else {
180                         current->message = NULL;
181                 }
182
183                 if (current != &_error_default) {
184                         vsc_free (&current);
185                 }
186
187                 current = next;
188         }
189 }
190
191 void
192 vsc_error_reset (struct VscError *error)
193 {
194         vsc_error_cleanup (error);
195         vsc_error_init (error);
196 }
197
198 struct VscError *
199 vsc_error_new (void)
200 {
201         struct VscError *error;
202
203         error = calloc (1, sizeof (struct VscError));
204
205         if (error == NULL) {
206                 return &_error_default;
207         } else {
208                 vsc_error_init (error);
209
210                 return error;
211         }
212 }
213
214 void
215 vsc_error_free (struct VscError **error)
216 {
217         if (error == NULL || *error == NULL) {
218                 return;
219         }
220
221         VSC__ASSERT (*error != &_error_default);
222
223         if (*error == &_error_default) {
224                 /* Don't cleanup the default error */
225                 return;
226         }
227
228         if ((*error)->branch != NULL) {
229                 vsc_error_cleanup ((*error)->branch);
230         }
231
232         vsc_free (&(*error)->branch);
233 }
234
235 struct VscError *
236 vsc_error_get_relevant (struct VscError *error)
237 {
238         struct VscError *current = NULL;
239         struct VscError *relevant = NULL;
240
241         for (current = error; current != NULL; current = current->next) {
242                 if (current->code != VSC__ERROR_CODE__TRACE) {
243                         relevant = current;
244                 }
245         }
246
247         return relevant;
248 }
249
250 const char *
251 vsc_error_string (enum VscErrorCode code)
252 {
253         switch (code) {
254         case VSC__ERROR_CODE__INTERNAL_ERROR:
255                 return "Internal error";
256
257         case VSC__ERROR_CODE__REMOTE_ERROR:
258                 return "Remote error";
259
260         case VSC__ERROR_CODE__OUT_OF_MEMORY:
261                 return "Out of memory";
262
263         case VSC__ERROR_CODE__OUT_OF_RESOURCES:
264                 return "Out of resources";
265
266         case VSC__ERROR_CODE__XMLRPC_ERROR:
267                 return "XML-RPC error";
268
269         case VSC__ERROR_CODE__PTHREAD_ERROR:
270                 return "Pthread error";
271
272         case VSC__ERROR_CODE__LIBVIRT_ERROR:
273                 return "Libvirt error";
274
275         case VSC__ERROR_CODE__INVALID_ARGUMENT:
276                 return "Invalid argument";
277
278         case VSC__ERROR_CODE__INVALID_CALL:
279                 return "Invalid call";
280
281         case VSC__ERROR_CODE__NOT_INITIALIZED:
282                 return "Not initialized";
283
284         case VSC__ERROR_CODE__NOT_IMPLEMENTED_YET:
285                 return "Not implemented yet";
286
287         case VSC__ERROR_CODE__ERRNO:
288                 return "errno";
289
290         case VSC__ERROR_CODE__TRACE:
291                 return "Inherited error";
292
293         default:
294                 return "Undefined";
295         }
296 }
297
298 void
299 vsc_error_fprint (struct VscError *error, FILE *stream)
300 {
301         struct tm time_info;
302
303         VSC__ASSERT (error != NULL);
304         VSC__ASSERT (error->occured);
305         VSC__ASSERT (stream != NULL);
306
307         if (! error->occured) {
308                 return;
309         }
310
311         localtime_r (&error->timestamp.tv_sec, &time_info);
312
313         fprintf (stream, "%02d:%02d:%02d.%03d   %s:%d %s : Error [%s]",
314                  time_info.tm_hour, time_info.tm_min, time_info.tm_sec,
315                  (int) error->timestamp.tv_usec / 1000, error->file,
316                  error->line, error->function,
317                  vsc_error_string (error->code));
318
319         if (error->message != NULL) {
320                 fprintf (stream, " %s\n", error->message);
321         } else {
322                 fprintf (stream, "\n");
323         }
324
325         if (error->branch != NULL) {
326                 _error_fprint_list (error->branch, stream, 2);
327         }
328
329         if (error->next != NULL) {
330                 _error_fprint_list (error->next, stream, 0);
331         }
332 }
333
334 void
335 vsc_error_report (struct VscError *error, enum VscErrorCode code,
336                   const char *file, int line, const char *function,
337                   const char *message, ...)
338 {
339         va_list args;
340
341         VSC__ASSERT (error != NULL);
342         VSC__ASSERT (error != &_error_default);
343         VSC__ASSERT (! error->occured);
344         VSC__ASSERT (code != VSC__ERROR_CODE__UNDEFINED);
345         VSC__ASSERT (file != NULL);
346         VSC__ASSERT (line >= 0);
347         VSC__ASSERT (function != NULL);
348
349         va_start (args, message);
350         _error_report_v (error, code, file, line, function, message, args);
351         va_end (args);
352 }
353
354 void
355 vsc_error_append_report (struct VscError *error, enum VscErrorCode code,
356                          const char *file, int line, const char *function,
357                          const char *message, ...)
358 {
359         va_list args;
360         struct VscError *error_last;
361         struct VscError *error_next;
362
363         VSC__ASSERT (error != NULL);
364         VSC__ASSERT (error->occured);
365         VSC__ASSERT (code != VSC__ERROR_CODE__UNDEFINED);
366         VSC__ASSERT (file != NULL);
367         VSC__ASSERT (line >= 0);
368         VSC__ASSERT (function != NULL);
369
370         error_last = (struct VscError *) vsc_list_get_last ((struct VscList *) error);
371
372         /*
373          * Do not append to the default error, because if it is present we are
374          * already in an out-of-memory situation.
375          */
376         if (error_last == &_error_default) {
377                 return;
378         }
379
380         error_next = calloc (1, sizeof (struct VscError));
381
382         if (error_next == NULL) {
383                 /* Append the default error in an out-of-memory situation */
384                 error_next = &_error_default;
385         } else {
386                 va_start (args, message);
387                 _error_report_v (error_next, code, file, line, function, message, args);
388                 va_end (args);
389         }
390
391         error_last->next = error_next;
392 }
393
394 void
395 vsc_error_append_branch (struct VscError *error, struct VscError *branch)
396 {
397         struct VscError *error_last;
398
399         VSC__ASSERT (error != NULL);
400         VSC__ASSERT (error->occured);
401         VSC__ASSERT (branch != NULL);
402         VSC__ASSERT (branch->occured);
403
404         /*
405          * Just set the error to the branch of the last error in the list. We
406          * assume that the given error to be appended
407          */
408         error_last = (struct VscError *) vsc_list_get_last ((struct VscList *) error);
409
410         VSC__ASSERT (error_last->branch == NULL);
411         VSC__ASSERT (error_last != &_error_default);
412
413         if (error == &_error_default) {
414                 /* Don't alter the default error */
415                 return;
416         }
417
418         error_last->branch = branch;
419 }