xref: /minix/external/bsd/kyua-testers/dist/error.c (revision 3260d16f)
1 // Copyright 2012 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 //   notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 //   notice, this list of conditions and the following disclaimer in the
12 //   documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 //   may be used to endorse or promote products derived from this software
15 //   without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include "error.h"
30 
31 #include <assert.h>
32 #include <err.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 
39 /// Generic hook to format an error that does not have a format callback.
40 ///
41 /// \param error Error for which to generate a message.
42 /// \param output_buffer Buffer to hold the generated message.
43 /// \param output_size Length of output_buffer.
44 static int
generic_format_callback(const kyua_error_t error,char * const output_buffer,size_t output_size)45 generic_format_callback(const kyua_error_t error, char* const output_buffer,
46                         size_t output_size)
47 {
48     assert(error != NULL);
49     return snprintf(output_buffer, output_size, "Error '%s'", error->type_name);
50 }
51 
52 
53 /// Initializes an error object.
54 ///
55 /// \param error Error for which to generate a message.
56 /// \param type_name Name of the error type.
57 /// \param data Opaque data that belongs to the error, for usage by
58 ///     error-specific methods like format_callback.
59 /// \param data_size Size of the opaque data object.
60 /// \param format_callback Type-specific method to generate a user
61 ///     representation of the error.
62 ///
63 /// \return True if the initialization succeeds; false otherwise.  If
64 /// false, the error object passed in has not been modified.
65 static bool
error_init(kyua_error_t const error,const char * const type_name,void * const data,const size_t data_size,const kyua_error_format_callback format_callback)66 error_init(kyua_error_t const error, const char* const type_name,
67            void* const data, const size_t data_size,
68            const kyua_error_format_callback format_callback)
69 {
70     assert(data != NULL || data_size == 0);
71     assert(data_size != 0 || data == NULL);
72 
73     bool ok;
74 
75     if (data == NULL) {
76         error->data = NULL;
77         error->needs_free = false;
78         ok = true;
79     } else {
80         void* new_data = malloc(data_size);
81         if (new_data == NULL) {
82             ok = false;
83         } else {
84             memcpy(new_data, data, data_size);
85             error->data = new_data;
86             ok = true;
87         }
88     }
89 
90     if (ok) {
91         error->type_name = type_name;
92         error->format_callback = (format_callback == NULL) ?
93             generic_format_callback : format_callback;
94     }
95 
96     return ok;
97 }
98 
99 
100 /// Allocates and initializes a new error.
101 ///
102 /// \param type_name Name of the error type.
103 /// \param data Opaque data that belongs to the error, for usage by
104 ///     error-specific methods like format_callback.
105 /// \param data_size Size of the opaque data object.
106 /// \param format_callback Type-specific method to generate a user
107 ///     representation of the error.
108 ///
109 /// \return The newly initialized error, or an out of memory error.
110 kyua_error_t
kyua_error_new(const char * const type_name,void * const data,const size_t data_size,const kyua_error_format_callback format_callback)111 kyua_error_new(const char* const type_name, void* const data,
112                const size_t data_size,
113                const kyua_error_format_callback format_callback)
114 {
115     assert(data != NULL || data_size == 0);
116     assert(data_size != 0 || data == NULL);
117 
118     kyua_error_t error = malloc(sizeof(struct kyua_error));
119     if (error == NULL)
120         error = kyua_oom_error_new();
121     else {
122         if (!error_init(error, type_name, data, data_size, format_callback)) {
123             free(error);
124             error = kyua_oom_error_new();
125         } else {
126             error->needs_free = true;
127         }
128     }
129 
130     assert(error != NULL);
131     return error;
132 }
133 
134 
135 /// Releases an error.
136 ///
137 /// \param error The error object to release.
138 void
kyua_error_free(kyua_error_t error)139 kyua_error_free(kyua_error_t error)
140 {
141     assert(error != NULL);
142 
143     const bool needs_free = error->needs_free;
144 
145     if (error->data != NULL)
146         free(error->data);
147     if (needs_free)
148         free(error);
149 }
150 
151 
152 /// Returns the "most important" of two errors.
153 ///
154 /// "Most important" is defined as: the primary error is returned if set,
155 /// otherwise the secondary error is returned.
156 ///
157 /// It is the responsibility of the caller to free the *resulting* error of this
158 /// call.  The original errors passed in should not be consulted any longer,
159 /// because it is impossible to know which one was chosen.
160 ///
161 /// \param primary The primary error to compare.
162 /// \param [in,out] secondary The secondary error to compare.  This is freed if
163 ///     the primary error is set.
164 ///
165 /// \return Either primary or secondary.
166 kyua_error_t
kyua_error_subsume(kyua_error_t primary,kyua_error_t secondary)167 kyua_error_subsume(kyua_error_t primary, kyua_error_t secondary)
168 {
169     if (kyua_error_is_set(primary)) {
170         if (kyua_error_is_set(secondary))
171             kyua_error_free(secondary);
172         return primary;
173     } else {
174         return secondary;
175     }
176 }
177 
178 
179 /// Constructor for a no-error condition.
180 ///
181 /// \return Opaque representation of a no-error condition.
182 kyua_error_t
kyua_error_ok(void)183 kyua_error_ok(void)
184 {
185     return NULL;
186 }
187 
188 
189 /// Checks if the given error object represents an error or not.
190 ///
191 /// \param error The error to check.
192 ///
193 /// \return True if the error is set.
194 bool
kyua_error_is_set(const kyua_error_t error)195 kyua_error_is_set(const kyua_error_t error)
196 {
197     return error != NULL;
198 }
199 
200 
201 /// Checks if the given error object is of a specific type.
202 ///
203 /// \pre The error must be set.
204 ///
205 /// \param error The error to check.
206 /// \param type_name The type of the expected error.
207 ///
208 /// \return True if the error is of type type_name.
209 bool
kyua_error_is_type(const kyua_error_t error,const char * type_name)210 kyua_error_is_type(const kyua_error_t error, const char* type_name)
211 {
212     assert(error != NULL);
213 
214     return strcmp(error->type_name, type_name) == 0;
215 }
216 
217 
218 /// Returns a pointer to the error-specific data.
219 ///
220 /// \pre The error must be set.
221 ///
222 /// \param error The error to query.
223 ///
224 /// \return An opaque pointer to the error data.  This should only be
225 /// dereferenced by the methods of the error class that created it.
226 const void*
kyua_error_data(const kyua_error_t error)227 kyua_error_data(const kyua_error_t error)
228 {
229     assert(error != NULL);
230 
231     return error->data;
232 }
233 
234 
235 /// Generates a user-friendly representation of the error.
236 ///
237 /// This cannot fail, but it is possible that the generated error does not
238 /// fit in the provided buffer.
239 ///
240 /// \pre The error must be set.
241 ///
242 /// \param error Error for which to generate a message.
243 /// \param output_buffer Buffer to hold the generated message.
244 /// \param output_size Length of output_buffer.
245 ///
246 /// \return The number of bytes written to output_buffer, or a negative value if
247 /// there was an error.
248 int
kyua_error_format(const kyua_error_t error,char * const output_buffer,const size_t output_size)249 kyua_error_format(const kyua_error_t error, char* const output_buffer,
250                   const size_t output_size)
251 {
252     assert(kyua_error_is_set(error));
253     return error->format_callback(error, output_buffer, output_size);
254 }
255 
256 
257 /// Formats a string and appends an error code to it.
258 ///
259 /// \param error Error to append to the formatted message.
260 /// \param format User-specified message, as a formatting string.
261 /// \param ap List of arguments to the format string.
262 /// \param [out] output_buffer Buffer into which to write the message.
263 /// \param output_size Length of the output_buffer.
264 ///
265 /// \return The number of bytes written to output_buffer, or a negative value if
266 /// there was an error.
267 static int
format_user_message(const kyua_error_t error,const char * format,va_list ap,char * const output_buffer,const size_t output_size)268 format_user_message(const kyua_error_t error, const char* format, va_list ap,
269                     char* const output_buffer, const size_t output_size)
270 {
271     assert(kyua_error_is_set(error));
272 
273     va_list ap2;
274     va_copy(ap2, ap);
275     size_t written = vsnprintf(output_buffer, output_size, format, ap2);
276     va_end(ap2);
277     if (written >= output_size)
278         return -1;
279 
280     written += snprintf(output_buffer + written, output_size - written, ": ");
281     if (written >= output_size)
282         return -1;
283 
284     return kyua_error_format(error, output_buffer + written,
285                              output_size - written);
286 }
287 
288 
289 /// Version of err(3) that works with kyua_error_t objects.
290 ///
291 /// \param exit_code Error code with which to terminate the execution.
292 /// \param error Error to append to the output.
293 /// \param format User-specified message, as a formatting string.
294 /// \param ... Positional arguments to the format string.
295 ///
296 /// \post Execution terminates with exit_code.
297 void
kyua_error_err(const int exit_code,const kyua_error_t error,const char * format,...)298 kyua_error_err(const int exit_code, const kyua_error_t error,
299                const char* format, ...)
300 {
301     char buffer[2048];
302 
303     va_list ap;
304     va_start(ap, format);
305     (void)format_user_message(error, format, ap, buffer, sizeof(buffer));
306     va_end(ap);
307     kyua_error_free(error);
308 
309     errx(exit_code, "%s", buffer);
310 }
311 
312 
313 /// Writes an error to a file stream.
314 ///
315 /// \param stream Stream to which to write the message.
316 /// \param error Error to append to the output.  This is not released.
317 /// \param format User-specified message, as a formatting string.
318 /// \param ... Positional arguments to the format string.
319 void
kyua_error_fprintf(FILE * stream,const kyua_error_t error,const char * format,...)320 kyua_error_fprintf(FILE* stream, const kyua_error_t error,
321                    const char* format, ...)
322 {
323     char buffer[2048];
324 
325     va_list ap;
326     va_start(ap, format);
327     (void)format_user_message(error, format, ap, buffer, sizeof(buffer));
328     va_end(ap);
329 
330     fprintf(stream, "%s", buffer);
331 }
332 
333 
334 /// Version of warn(3) that works with kyua_error_t objects.
335 ///
336 /// \param error Error to append to the output.  This is not released.
337 /// \param format User-specified message, as a formatting string.
338 /// \param ... Positional arguments to the format string.
339 void
kyua_error_warn(const kyua_error_t error,const char * format,...)340 kyua_error_warn(const kyua_error_t error, const char* format, ...)
341 {
342     char buffer[2048];
343 
344     va_list ap;
345     va_start(ap, format);
346     (void)format_user_message(error, format, ap, buffer, sizeof(buffer));
347     va_end(ap);
348 
349     warnx("%s", buffer);
350 }
351 
352 
353 /// Name of an generic error type.
354 const char* const kyua_generic_error_type = "generic";
355 
356 
357 /// Generates a user-friendly representation of the error.
358 ///
359 /// \pre The error must be set.
360 ///
361 /// \param error Error for which to generate a message.
362 /// \param output_buffer Buffer to hold the generated message.
363 /// \param output_size Length of output_buffer.
364 ///
365 /// \return The number of bytes written to output_buffer, or a negative value if
366 /// there was an error.
367 static int
generic_format(const kyua_error_t error,char * const output_buffer,const size_t output_size)368 generic_format(const kyua_error_t error, char* const output_buffer,
369              const size_t output_size)
370 {
371     assert(kyua_error_is_type(error, kyua_generic_error_type));
372 
373     const char* message = kyua_error_data(error);
374     return snprintf(output_buffer, output_size, "%s", message);
375 }
376 
377 
378 /// Constructs a new generic error.
379 ///
380 /// \param message Textual description of the problem.
381 /// \param ... Positional arguments for the description.
382 ///
383 /// \return The generated error.
384 kyua_error_t
kyua_generic_error_new(const char * message,...)385 kyua_generic_error_new(const char* message, ...)
386 {
387     char formatted[1024];
388     va_list ap;
389 
390     va_start(ap, message);
391     (void)vsnprintf(formatted, sizeof(formatted), message, ap);
392     va_end(ap);
393 
394     return kyua_error_new(kyua_generic_error_type, formatted, sizeof(formatted),
395                           generic_format);
396 }
397 
398 
399 /// Name of a libc type.
400 const char* const kyua_libc_error_type = "libc";
401 
402 
403 /// Representation of a libc error.
404 struct libc_error_data {
405     /// Value of the errno captured during the error creation.
406     int original_errno;
407 
408     /// Explanation of the problem that lead to the error.
409     char description[4096];
410 };
411 /// Shorthand for a libc_error_data structure.
412 typedef struct libc_error_data libc_error_data_t;
413 
414 
415 /// Generates a user-friendly representation of the error.
416 ///
417 /// \pre The error must be set.
418 ///
419 /// \param error Error for which to generate a message.
420 /// \param output_buffer Buffer to hold the generated message.
421 /// \param output_size Length of output_buffer.
422 ///
423 /// \return The number of bytes written to output_buffer, or a negative value if
424 /// there was an error.
425 static int
libc_format(const kyua_error_t error,char * const output_buffer,const size_t output_size)426 libc_format(const kyua_error_t error, char* const output_buffer,
427             const size_t output_size)
428 {
429     assert(kyua_error_is_type(error, kyua_libc_error_type));
430 
431     const libc_error_data_t* data = kyua_error_data(error);
432     return snprintf(output_buffer, output_size, "%s: %s", data->description,
433                     strerror(data->original_errno));
434 }
435 
436 
437 /// Constructs a new libc error.
438 ///
439 /// \param original_errno libc error code for this error.
440 /// \param description Textual description of the problem.
441 /// \param ... Positional arguments for the description.
442 ///
443 /// \return The generated error.
444 kyua_error_t
kyua_libc_error_new(const int original_errno,const char * description,...)445 kyua_libc_error_new(const int original_errno, const char* description, ...)
446 {
447     va_list ap;
448 
449     const size_t data_size = sizeof(libc_error_data_t);
450     libc_error_data_t* data = (libc_error_data_t*)malloc(data_size);
451     if (data == NULL)
452         return kyua_oom_error_new();
453 
454     data->original_errno = original_errno;
455     va_start(ap, description);
456     (void)vsnprintf(data->description, sizeof(data->description),
457                     description, ap);
458     va_end(ap);
459 
460     return kyua_error_new(kyua_libc_error_type, data, data_size, libc_format);
461 }
462 
463 
464 /// Extracts the original errno of a libc error.
465 ///
466 /// \pre error must have been constructed by kyua_libc_error_new.
467 ///
468 /// \param error The error object to access.
469 ///
470 /// \return The libc error code.
471 int
kyua_libc_error_errno(const kyua_error_t error)472 kyua_libc_error_errno(const kyua_error_t error)
473 {
474     assert(kyua_error_is_type(error, kyua_libc_error_type));
475 
476     const struct libc_error_data* data = kyua_error_data(error);
477     return data->original_errno;
478 }
479 
480 
481 /// Name of an OOM type.
482 const char* const kyua_oom_error_type = "oom";
483 
484 
485 /// Data of an out of memory error.
486 ///
487 /// All error types are allocated in dynamic memory.  However, doing so for
488 /// an out of memory error is not possible because, when we are out of
489 /// memory, we probably cannot allocate more memory to generate an error.
490 /// Therefore, we just keep a single static instance of the out of memory
491 /// error around all the time.
492 static struct kyua_error oom_error;
493 
494 
495 /// Generates a user-friendly representation of the error.
496 ///
497 /// \pre The error must be set.
498 ///
499 /// \param error Error for which to generate a message.
500 /// \param output_buffer Buffer to hold the generated message.
501 /// \param output_size Length of output_buffer.
502 ///
503 /// \return The number of bytes written to output_buffer, or a negative value if
504 /// there was an error.
505 static int
oom_format(const kyua_error_t error,char * const output_buffer,const size_t output_size)506 oom_format(const kyua_error_t error, char* const output_buffer,
507            const size_t output_size)
508 {
509     assert(kyua_error_is_type(error, kyua_oom_error_type));
510 
511     return snprintf(output_buffer, output_size, "Not enough memory");
512 }
513 
514 
515 /// Constructs a new out-of-memory error.
516 ///
517 /// This will always succeed because we just return a reference to the
518 /// statically-allocated oom_error.
519 ///
520 /// \return An error representing an out of memory condition.
521 kyua_error_t
kyua_oom_error_new(void)522 kyua_oom_error_new(void)
523 {
524     // This is idempotent; no need to ensure that we call it only once.
525 #if defined(__minix) && !defined(NDEBUG)
526     const bool ok =
527 #endif /* defined(__minix) && !defined(NDEBUG) */
528     error_init(&oom_error, kyua_oom_error_type, NULL, 0, oom_format);
529     assert(ok);
530 
531     return &oom_error;
532 }
533 
534 
535 /// Name of an usage error type.
536 const char* const kyua_usage_error_type = "usage";
537 
538 
539 /// Generates a user-friendly representation of the error.
540 ///
541 /// \pre The error must be set.
542 ///
543 /// \param error Error for which to generate a message.
544 /// \param output_buffer Buffer to hold the generated message.
545 /// \param output_size Length of output_buffer.
546 ///
547 /// \return The number of bytes written to output_buffer, or a negative value if
548 /// there was an error.
549 static int
usage_format(const kyua_error_t error,char * const output_buffer,const size_t output_size)550 usage_format(const kyua_error_t error, char* const output_buffer,
551              const size_t output_size)
552 {
553     assert(kyua_error_is_type(error, kyua_usage_error_type));
554 
555     const char* message = kyua_error_data(error);
556     return snprintf(output_buffer, output_size, "%s", message);
557 }
558 
559 
560 /// Constructs a new usage error.
561 ///
562 /// \param message Textual description of the problem.
563 /// \param ... Positional arguments for the description.
564 ///
565 /// \return The generated error.
566 kyua_error_t
kyua_usage_error_new(const char * message,...)567 kyua_usage_error_new(const char* message, ...)
568 {
569     char formatted[1024];
570     va_list ap;
571 
572     va_start(ap, message);
573     (void)vsnprintf(formatted, sizeof(formatted), message, ap);
574     va_end(ap);
575 
576     return kyua_error_new(kyua_usage_error_type, formatted, sizeof(formatted),
577                           usage_format);
578 }
579