Line data Source code
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 12 : DEFINE_MTYPE_STATIC(LIB, ERRINFO, "error information");
28 :
29 : /*
30 : * Thread-specific key for temporary storage of allocated ferr.
31 : */
32 : static pthread_key_t errkey;
33 :
34 0 : static void ferr_free(void *arg)
35 : {
36 0 : XFREE(MTYPE_ERRINFO, arg);
37 0 : }
38 :
39 : static void err_key_init(void) __attribute__((_CONSTRUCTOR(500)));
40 4 : static void err_key_init(void)
41 : {
42 4 : pthread_key_create(&errkey, ferr_free);
43 4 : }
44 :
45 : static void err_key_fini(void) __attribute__((_DESTRUCTOR(500)));
46 8 : static void err_key_fini(void)
47 : {
48 8 : pthread_key_delete(errkey);
49 8 : }
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 2 : static bool ferr_hash_cmp(const void *a, const void *b)
58 : {
59 2 : const struct log_ref *f_a = a;
60 2 : const struct log_ref *f_b = b;
61 :
62 2 : return f_a->code == f_b->code;
63 : }
64 :
65 592 : static inline unsigned int ferr_hash_key(const void *a)
66 : {
67 592 : const struct log_ref *f = a;
68 :
69 592 : return f->code;
70 : }
71 :
72 14 : void log_ref_add(struct log_ref *ref)
73 : {
74 14 : uint32_t i = 0;
75 :
76 28 : frr_with_mutex (&refs_mtx) {
77 606 : while (ref[i].code != END_FERR) {
78 592 : (void)hash_get(refs, &ref[i], hash_alloc_intern);
79 592 : i++;
80 : }
81 : }
82 14 : }
83 :
84 0 : struct log_ref *log_ref_get(uint32_t code)
85 : {
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 : }
93 :
94 0 : return ref;
95 : }
96 :
97 0 : void log_ref_display(struct vty *vty, uint32_t code, bool json)
98 : {
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 : }
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 : }
119 0 : listnode_add(errlist, ref);
120 : }
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 : ref->description);
131 0 : json_object_string_add(obj, "suggestion",
132 : ref->suggestion);
133 0 : json_object_object_add(top, key, obj);
134 : } else {
135 0 : char pbuf[256];
136 0 : char ubuf[256];
137 :
138 0 : snprintf(pbuf, sizeof(pbuf), "\nError %u - %s",
139 : 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 : }
147 : }
148 :
149 0 : vty_json(vty, top);
150 0 : list_delete(&errlist);
151 : }
152 :
153 0 : 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 : {
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 : }
171 :
172 4 : void log_ref_init(void)
173 : {
174 4 : frr_with_mutex (&refs_mtx) {
175 4 : refs = hash_create(ferr_hash_key, ferr_hash_cmp,
176 : "Error Reference Texts");
177 : }
178 4 : }
179 :
180 4 : void log_ref_fini(void)
181 : {
182 8 : frr_with_mutex (&refs_mtx) {
183 4 : hash_clean_and_free(&refs, NULL);
184 : }
185 4 : }
186 :
187 4 : void log_ref_vty_init(void)
188 : {
189 4 : install_element(VIEW_NODE, &show_error_code_cmd);
190 4 : }
191 :
192 :
193 0 : const struct ferr *ferr_get_last(ferr_r errval)
194 : {
195 0 : struct ferr *last_error = pthread_getspecific(errkey);
196 0 : if (!last_error || last_error->kind == 0)
197 0 : return NULL;
198 : return last_error;
199 : }
200 :
201 0 : ferr_r ferr_clear(void)
202 : {
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 : }
208 :
209 : PRINTFRR(7, 0)
210 0 : 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 : {
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 : }
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 : pathname);
234 : else
235 0 : error->pathname[0] = '\0';
236 :
237 0 : vsnprintf(error->message, sizeof(error->message), text, va);
238 0 : return -1;
239 : }
240 :
241 0 : ferr_r ferr_set_internal(const char *file, int line, const char *func,
242 : enum ferr_kind kind, const char *text, ...)
243 : {
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 : }
251 :
252 0 : 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 : {
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 : }
263 :
264 : #define REPLACE "$ERR"
265 0 : void vty_print_error(struct vty *vty, ferr_r err, const char *msg, ...)
266 : {
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 : else {
279 0 : replacepos[0] = '\0';
280 0 : replacepos += sizeof(REPLACE) - 1;
281 0 : vty_out(vty, "%s%s%s\n", tmpmsg,
282 : last_error ? last_error->message : "(no error?)",
283 : replacepos);
284 : }
285 0 : }
|