summaryrefslogtreecommitdiffstats
path: root/lib/ferr.h
blob: f0191dffbaa1d579fb9705b22fdea1f11fa598f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
// SPDX-License-Identifier: ISC
/*
 * Copyright (c) 2015-16  David Lamparter, for NetDEF, Inc.
 */

#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, ...)
	PRINTFRR(5, 6);
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, ...)
	PRINTFRR(7, 8);

#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, ...)
	PRINTFRR(3, 4);

#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 */