summaryrefslogtreecommitdiffstats
path: root/lib/ferr.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ferr.h')
-rw-r--r--lib/ferr.h267
1 files changed, 267 insertions, 0 deletions
diff --git a/lib/ferr.h b/lib/ferr.h
new file mode 100644
index 0000000..c27601f
--- /dev/null
+++ b/lib/ferr.h
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _FRR_FERR_H
+#define _FRR_FERR_H
+
+/***********************************************************
+ * scroll down to the end of this file for a full example! *
+ ***********************************************************/
+
+#include <stdint.h>
+#include <limits.h>
+#include <errno.h>
+
+#include "vty.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* return type when this error indication stuff is used.
+ *
+ * guaranteed to have boolean evaluation to "false" when OK, "true" when error
+ * (i.e. can be changed to pointer in the future if necessary)
+ *
+ * For checking, always use "if (value)", nothing else.
+ * Do _NOT_ use any integer constant (!= 0), or sign check (< 0).
+ */
+typedef int ferr_r;
+
+/* rough category of error indication */
+enum ferr_kind {
+ /* no error */
+ FERR_OK = 0,
+
+ /* something isn't the way it's supposed to be.
+ * (things that might otherwise be asserts, really)
+ */
+ FERR_CODE_BUG,
+
+ /* user-supplied parameters don't make sense or is inconsistent
+ * if you can express a rule for it (e.g. "holdtime > 2 * keepalive"),
+ * it's this category.
+ */
+ FERR_CONFIG_INVALID,
+
+ /* user-supplied parameters don't line up with reality
+ * (IP address or interface not available, etc.)
+ * NB: these are really TODOs where the code needs to be fixed to
+ * respond to future changes!
+ */
+ FERR_CONFIG_REALITY,
+
+ /* out of some system resource (probably memory)
+ * aka "you didn't spend enough money error" */
+ FERR_RESOURCE,
+
+ /* system error (permission denied, etc.) */
+ FERR_SYSTEM,
+
+ /* error return from some external library
+ * (FERR_SYSTEM and FERR_LIBRARY are not strongly distinct) */
+ FERR_LIBRARY,
+};
+
+struct ferr {
+ /* code location */
+ const char *file;
+ const char *func;
+ int line;
+
+ enum ferr_kind kind;
+
+ /* unique_id is calculated as a checksum of source filename and error
+ * message format (*before* calling vsnprintf). Line number and
+ * function name are not used; this keeps the number reasonably static
+ * across changes.
+ */
+ uint32_t unique_id;
+
+ char message[384];
+
+ /* valid if != 0. note "errno" might be preprocessor foobar. */
+ int errno_val;
+ /* valid if pathname[0] != '\0' */
+ char pathname[PATH_MAX];
+};
+
+/* Numeric ranges assigned to daemons for use as error codes. */
+#define BABEL_FERR_START 0x01000001
+#define BABEL_FRRR_END 0x01FFFFFF
+#define BGP_FERR_START 0x02000001
+#define BGP_FERR_END 0x02FFFFFF
+#define EIGRP_FERR_START 0x03000001
+#define EIGRP_FERR_END 0x03FFFFFF
+#define ISIS_FERR_START 0x04000001
+#define ISIS_FERR_END 0x04FFFFFF
+#define LDP_FERR_START 0x05000001
+#define LDP_FERR_END 0x05FFFFFF
+#define LIB_FERR_START 0x06000001
+#define LIB_FERR_END 0x06FFFFFF
+#define NHRP_FERR_START 0x07000001
+#define NHRP_FERR_END 0x07FFFFFF
+#define OSPF_FERR_START 0x08000001
+#define OSPF_FERR_END 0x08FFFFFF
+#define OSPFV3_FERR_START 0x09000001
+#define OSPFV3_FERR_END 0x09FFFFFF
+#define PBR_FERR_START 0x0A000001
+#define PBR_FERR_END 0x0AFFFFFF
+#define PIM_FERR_START 0x0B000001
+#define PIM_FERR_STOP 0x0BFFFFFF
+#define RIP_FERR_START 0x0C000001
+#define RIP_FERR_STOP 0x0CFFFFFF
+#define RIPNG_FERR_START 0x0D000001
+#define RIPNG_FERR_STOP 0x0DFFFFFF
+#define SHARP_FERR_START 0x0E000001
+#define SHARP_FERR_END 0x0EFFFFFF
+#define VTYSH_FERR_START 0x0F000001
+#define VTYSH_FRR_END 0x0FFFFFFF
+#define WATCHFRR_FERR_START 0x10000001
+#define WATCHFRR_FERR_END 0x10FFFFFF
+#define PATH_FERR_START 0x11000001
+#define PATH_FERR_END 0x11FFFFFF
+#define ZEBRA_FERR_START 0xF1000001
+#define ZEBRA_FERR_END 0xF1FFFFFF
+#define END_FERR 0xFFFFFFFF
+
+struct log_ref {
+ /* Unique error code displayed to end user as a reference. -1 means
+ * this is an uncoded error that does not have reference material. */
+ uint32_t code;
+ /* Ultra brief title */
+ const char *title;
+ /* Brief description of error */
+ const char *description;
+ /* Remedial suggestion */
+ const char *suggestion;
+};
+
+void log_ref_add(struct log_ref *ref);
+struct log_ref *log_ref_get(uint32_t code);
+void log_ref_display(struct vty *vty, uint32_t code, bool json);
+
+/*
+ * This function should be called by the
+ * code in libfrr.c
+ */
+void log_ref_init(void);
+void log_ref_fini(void);
+void log_ref_vty_init(void);
+
+/* get error details.
+ *
+ * NB: errval/ferr_r does NOT carry the full error information. It's only
+ * passed around for future API flexibility. ferr_get_last always returns
+ * the last error set in the current thread.
+ */
+const struct ferr *ferr_get_last(ferr_r errval);
+
+/*
+ * Can optionally be called at strategic locations.
+ * Always returns 0.
+ */
+ferr_r ferr_clear(void);
+
+/* do NOT call these functions directly. only for macro use! */
+ferr_r ferr_set_internal(const char *file, int line, const char *func,
+ enum ferr_kind kind, const char *text, ...);
+ferr_r ferr_set_internal_ext(const char *file, int line, const char *func,
+ enum ferr_kind kind, const char *pathname,
+ int errno_val, const char *text, ...);
+
+#define ferr_ok() 0
+
+/* Report an error.
+ *
+ * If you need to do cleanup (free memory, etc.), save the return value in a
+ * variable of type ferr_r.
+ *
+ * Don't put a \n at the end of the error message.
+ */
+#define ferr_code_bug(...) \
+ ferr_set_internal(__FILE__, __LINE__, __func__, FERR_CODE_BUG, \
+ __VA_ARGS__)
+#define ferr_cfg_invalid(...) \
+ ferr_set_internal(__FILE__, __LINE__, __func__, FERR_CONFIG_INVALID, \
+ __VA_ARGS__)
+#define ferr_cfg_reality(...) \
+ ferr_set_internal(__FILE__, __LINE__, __func__, FERR_CONFIG_REALITY, \
+ __VA_ARGS__)
+#define ferr_cfg_resource(...) \
+ ferr_set_internal(__FILE__, __LINE__, __func__, FERR_RESOURCE, \
+ __VA_ARGS__)
+#define ferr_system(...) \
+ ferr_set_internal(__FILE__, __LINE__, __func__, FERR_SYSTEM, \
+ __VA_ARGS__)
+#define ferr_library(...) \
+ ferr_set_internal(__FILE__, __LINE__, __func__, FERR_LIBRARY, \
+ __VA_ARGS__)
+
+/* extended information variants */
+#define ferr_system_errno(...) \
+ ferr_set_internal_ext(__FILE__, __LINE__, __func__, FERR_SYSTEM, NULL, \
+ errno, __VA_ARGS__)
+#define ferr_system_path_errno(path, ...) \
+ ferr_set_internal_ext(__FILE__, __LINE__, __func__, FERR_SYSTEM, path, \
+ errno, __VA_ARGS__)
+
+#include "vty.h"
+/* print error message to vty; $ERR is replaced by the error's message */
+void vty_print_error(struct vty *vty, ferr_r err, const char *msg, ...);
+
+#define CMD_FERR_DO(func, action, ...) \
+ do { \
+ ferr_r cmd_retval = func; \
+ if (cmd_retval) { \
+ vty_print_error(vty, cmd_retval, __VA_ARGS__); \
+ action; \
+ } \
+ } while (0)
+
+#define CMD_FERR_RETURN(func, ...) \
+ CMD_FERR_DO(func, return CMD_WARNING_CONFIG_FAILED, __VA_ARGS__)
+#define CMD_FERR_GOTO(func, label, ...) \
+ CMD_FERR_DO(func, goto label, __VA_ARGS__)
+
+/* example: uses bogus #define to keep indent.py happy */
+#ifdef THIS_IS_AN_EXAMPLE
+ferr_r foo_bar_set(struct object *obj, int bar)
+{
+ if (bar < 1 || bar >= 100)
+ return ferr_config_invalid("bar setting (%d) must be 0<x<100",
+ bar);
+ obj->bar = bar;
+ if (ioctl(obj->fd, bar))
+ return ferr_system_errno("couldn't set bar to %d", bar);
+
+ return ferr_ok();
+}
+
+DEFUN("bla")
+{
+ CMD_FERR_RETURN(foo_bar_set(obj, atoi(argv[1])),
+ "command failed: $ERR\n");
+ return CMD_SUCCESS;
+}
+
+#endif /* THIS_IS_AN_EXAMPLE */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FERR_H */