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