Line | Count | Source |
1 | | // SPDX-License-Identifier: ISC |
2 | | /* |
3 | | * Copyright (c) 2015-16 David Lamparter, for NetDEF, Inc. |
4 | | */ |
5 | | |
6 | | #ifdef HAVE_CONFIG_H |
7 | | #include "config.h" |
8 | | #endif |
9 | | |
10 | | #include <stdlib.h> |
11 | | #include <stdarg.h> |
12 | | #include <string.h> |
13 | | #include <pthread.h> |
14 | | #include <signal.h> |
15 | | #include <inttypes.h> |
16 | | |
17 | | #include "ferr.h" |
18 | | #include "vty.h" |
19 | | #include "jhash.h" |
20 | | #include "memory.h" |
21 | | #include "hash.h" |
22 | | #include "command.h" |
23 | | #include "json.h" |
24 | | #include "linklist.h" |
25 | | #include "frr_pthread.h" |
26 | | |
27 | 8 | DEFINE_MTYPE_STATIC(LIB, ERRINFO, "error information"); |
28 | 8 | |
29 | 8 | /* |
30 | 8 | * Thread-specific key for temporary storage of allocated ferr. |
31 | 8 | */ |
32 | 8 | static pthread_key_t errkey; |
33 | 8 | |
34 | 8 | static void ferr_free(void *arg) |
35 | 8 | { |
36 | 0 | XFREE(MTYPE_ERRINFO, arg); |
37 | 0 | } |
38 | | |
39 | | static void err_key_init(void) __attribute__((_CONSTRUCTOR(500))); |
40 | | static void err_key_init(void) |
41 | 8 | { |
42 | 8 | pthread_key_create(&errkey, ferr_free); |
43 | 8 | } |
44 | | |
45 | | static void err_key_fini(void) __attribute__((_DESTRUCTOR(500))); |
46 | | static void err_key_fini(void) |
47 | 0 | { |
48 | 0 | pthread_key_delete(errkey); |
49 | 0 | } |
50 | | |
51 | | /* |
52 | | * Global shared hash table holding reference text for all defined errors. |
53 | | */ |
54 | | static pthread_mutex_t refs_mtx = PTHREAD_MUTEX_INITIALIZER; |
55 | | struct hash *refs; |
56 | | |
57 | | static bool ferr_hash_cmp(const void *a, const void *b) |
58 | 1 | { |
59 | 1 | const struct log_ref *f_a = a; |
60 | 1 | const struct log_ref *f_b = b; |
61 | | |
62 | 1 | return f_a->code == f_b->code; |
63 | 1 | } |
64 | | |
65 | | static inline unsigned int ferr_hash_key(const void *a) |
66 | 439 | { |
67 | 439 | const struct log_ref *f = a; |
68 | | |
69 | 439 | return f->code; |
70 | 439 | } |
71 | | |
72 | | void log_ref_add(struct log_ref *ref) |
73 | 14 | { |
74 | 14 | uint32_t i = 0; |
75 | | |
76 | 14 | frr_with_mutex (&refs_mtx) { |
77 | 453 | while (ref[i].code != END_FERR) { |
78 | 439 | (void)hash_get(refs, &ref[i], hash_alloc_intern); |
79 | 439 | i++; |
80 | 439 | } |
81 | 14 | } |
82 | 14 | } |
83 | | |
84 | | struct log_ref *log_ref_get(uint32_t code) |
85 | 0 | { |
86 | 0 | struct log_ref holder; |
87 | 0 | struct log_ref *ref; |
88 | |
|
89 | 0 | holder.code = code; |
90 | 0 | frr_with_mutex (&refs_mtx) { |
91 | 0 | ref = hash_lookup(refs, &holder); |
92 | 0 | } |
93 | |
|
94 | 0 | return ref; |
95 | 0 | } |
96 | | |
97 | | void log_ref_display(struct vty *vty, uint32_t code, bool json) |
98 | 0 | { |
99 | 0 | struct log_ref *ref; |
100 | 0 | struct json_object *top = NULL, *obj = NULL; |
101 | 0 | struct list *errlist; |
102 | 0 | struct listnode *ln; |
103 | |
|
104 | 0 | if (json) |
105 | 0 | top = json_object_new_object(); |
106 | |
|
107 | 0 | frr_with_mutex (&refs_mtx) { |
108 | 0 | errlist = code ? list_new() : hash_to_list(refs); |
109 | 0 | } |
110 | |
|
111 | 0 | if (code) { |
112 | 0 | ref = log_ref_get(code); |
113 | 0 | if (!ref) { |
114 | 0 | if (top) |
115 | 0 | json_object_free(top); |
116 | 0 | list_delete(&errlist); |
117 | 0 | return; |
118 | 0 | } |
119 | 0 | listnode_add(errlist, ref); |
120 | 0 | } |
121 | | |
122 | 0 | for (ALL_LIST_ELEMENTS_RO(errlist, ln, ref)) { |
123 | 0 | if (json) { |
124 | 0 | char key[11]; |
125 | |
|
126 | 0 | snprintf(key, sizeof(key), "%u", ref->code); |
127 | 0 | obj = json_object_new_object(); |
128 | 0 | json_object_string_add(obj, "title", ref->title); |
129 | 0 | json_object_string_add(obj, "description", |
130 | 0 | ref->description); |
131 | 0 | json_object_string_add(obj, "suggestion", |
132 | 0 | ref->suggestion); |
133 | 0 | json_object_object_add(top, key, obj); |
134 | 0 | } else { |
135 | 0 | char pbuf[256]; |
136 | 0 | char ubuf[256]; |
137 | |
|
138 | 0 | snprintf(pbuf, sizeof(pbuf), "\nError %u - %s", |
139 | 0 | ref->code, ref->title); |
140 | 0 | memset(ubuf, '=', strlen(pbuf)); |
141 | 0 | ubuf[strlen(pbuf)] = '\0'; |
142 | |
|
143 | 0 | vty_out(vty, "%s\n%s\n", pbuf, ubuf); |
144 | 0 | vty_out(vty, "Description:\n%s\n\n", ref->description); |
145 | 0 | vty_out(vty, "Recommendation:\n%s\n", ref->suggestion); |
146 | 0 | } |
147 | 0 | } |
148 | |
|
149 | 0 | vty_json(vty, top); |
150 | 0 | list_delete(&errlist); |
151 | 0 | } |
152 | | |
153 | | DEFUN_NOSH(show_error_code, |
154 | | show_error_code_cmd, |
155 | | "show error <(1-4294967295)|all> [json]", |
156 | | SHOW_STR |
157 | | "Information on errors\n" |
158 | | "Error code to get info about\n" |
159 | | "Information on all errors\n" |
160 | | JSON_STR) |
161 | 0 | { |
162 | 0 | bool json = strmatch(argv[argc-1]->text, "json"); |
163 | 0 | uint32_t arg = 0; |
164 | |
|
165 | 0 | if (!strmatch(argv[2]->text, "all")) |
166 | 0 | arg = strtoul(argv[2]->arg, NULL, 10); |
167 | |
|
168 | 0 | log_ref_display(vty, arg, json); |
169 | 0 | return CMD_SUCCESS; |
170 | 0 | } |
171 | | |
172 | | void log_ref_init(void) |
173 | 4 | { |
174 | 4 | frr_with_mutex (&refs_mtx) { |
175 | 4 | refs = hash_create(ferr_hash_key, ferr_hash_cmp, |
176 | 4 | "Error Reference Texts"); |
177 | 4 | } |
178 | 4 | } |
179 | | |
180 | | void log_ref_fini(void) |
181 | 0 | { |
182 | 0 | frr_with_mutex (&refs_mtx) { |
183 | 0 | hash_clean_and_free(&refs, NULL); |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | void log_ref_vty_init(void) |
188 | 0 | { |
189 | 0 | install_element(VIEW_NODE, &show_error_code_cmd); |
190 | 0 | } |
191 | | |
192 | | |
193 | | const struct ferr *ferr_get_last(ferr_r errval) |
194 | 0 | { |
195 | 0 | struct ferr *last_error = pthread_getspecific(errkey); |
196 | 0 | if (!last_error || last_error->kind == 0) |
197 | 0 | return NULL; |
198 | 0 | return last_error; |
199 | 0 | } |
200 | | |
201 | | ferr_r ferr_clear(void) |
202 | 0 | { |
203 | 0 | struct ferr *last_error = pthread_getspecific(errkey); |
204 | 0 | if (last_error) |
205 | 0 | last_error->kind = 0; |
206 | 0 | return ferr_ok(); |
207 | 0 | } |
208 | | |
209 | | PRINTFRR(7, 0) |
210 | | static ferr_r ferr_set_va(const char *file, int line, const char *func, |
211 | | enum ferr_kind kind, const char *pathname, |
212 | | int errno_val, const char *text, va_list va) |
213 | 0 | { |
214 | 0 | struct ferr *error = pthread_getspecific(errkey); |
215 | |
|
216 | 0 | if (!error) { |
217 | 0 | error = XCALLOC(MTYPE_ERRINFO, sizeof(*error)); |
218 | |
|
219 | 0 | pthread_setspecific(errkey, error); |
220 | 0 | } |
221 | |
|
222 | 0 | error->file = file; |
223 | 0 | error->line = line; |
224 | 0 | error->func = func; |
225 | 0 | error->kind = kind; |
226 | |
|
227 | 0 | error->unique_id = jhash(text, strlen(text), |
228 | 0 | jhash(file, strlen(file), 0xd4ed0298)); |
229 | |
|
230 | 0 | error->errno_val = errno_val; |
231 | 0 | if (pathname) |
232 | 0 | snprintf(error->pathname, sizeof(error->pathname), "%s", |
233 | 0 | pathname); |
234 | 0 | else |
235 | 0 | error->pathname[0] = '\0'; |
236 | |
|
237 | 0 | vsnprintf(error->message, sizeof(error->message), text, va); |
238 | 0 | return -1; |
239 | 0 | } |
240 | | |
241 | | ferr_r ferr_set_internal(const char *file, int line, const char *func, |
242 | | enum ferr_kind kind, const char *text, ...) |
243 | 0 | { |
244 | 0 | ferr_r rv; |
245 | 0 | va_list va; |
246 | 0 | va_start(va, text); |
247 | 0 | rv = ferr_set_va(file, line, func, kind, NULL, 0, text, va); |
248 | 0 | va_end(va); |
249 | 0 | return rv; |
250 | 0 | } |
251 | | |
252 | | ferr_r ferr_set_internal_ext(const char *file, int line, const char *func, |
253 | | enum ferr_kind kind, const char *pathname, |
254 | | int errno_val, const char *text, ...) |
255 | 0 | { |
256 | 0 | ferr_r rv; |
257 | 0 | va_list va; |
258 | 0 | va_start(va, text); |
259 | 0 | rv = ferr_set_va(file, line, func, kind, pathname, errno_val, text, va); |
260 | 0 | va_end(va); |
261 | 0 | return rv; |
262 | 0 | } |
263 | | |
264 | 0 | #define REPLACE "$ERR" |
265 | | void vty_print_error(struct vty *vty, ferr_r err, const char *msg, ...) |
266 | 0 | { |
267 | 0 | char tmpmsg[512], *replacepos; |
268 | 0 | const struct ferr *last_error = ferr_get_last(err); |
269 | |
|
270 | 0 | va_list va; |
271 | 0 | va_start(va, msg); |
272 | 0 | vsnprintf(tmpmsg, sizeof(tmpmsg), msg, va); |
273 | 0 | va_end(va); |
274 | |
|
275 | 0 | replacepos = strstr(tmpmsg, REPLACE); |
276 | 0 | if (!replacepos) |
277 | 0 | vty_out(vty, "%s\n", tmpmsg); |
278 | 0 | else { |
279 | 0 | replacepos[0] = '\0'; |
280 | 0 | replacepos += sizeof(REPLACE) - 1; |
281 | 0 | vty_out(vty, "%s%s%s\n", tmpmsg, |
282 | 0 | last_error ? last_error->message : "(no error?)", |
283 | 0 | replacepos); |
284 | 0 | } |
285 | 0 | } |