]>
Commit | Line | Data |
---|---|---|
3865965d | 1 | /* Copyright (c) 2009, 2010, 2011, 2012, 2016, 2017 Nicira, Inc. |
f85f8ebb BP |
2 | * |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at: | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | #include <config.h> | |
17 | ||
18 | #include "ovsdb-error.h" | |
19 | ||
20 | #include <inttypes.h> | |
21 | ||
22 | #include "backtrace.h" | |
3e8a2ad1 | 23 | #include "openvswitch/dynamic-string.h" |
ee89ea7b | 24 | #include "openvswitch/json.h" |
f85f8ebb | 25 | #include "util.h" |
e6211adc | 26 | #include "openvswitch/vlog.h" |
80af01ed | 27 | |
d98e6007 | 28 | VLOG_DEFINE_THIS_MODULE(ovsdb_error); |
f85f8ebb BP |
29 | |
30 | struct ovsdb_error { | |
31 | const char *tag; /* String for "error" member. */ | |
32 | char *details; /* String for "details" member. */ | |
33 | char *syntax; /* String for "syntax" member. */ | |
34 | int errno_; /* Unix errno value, 0 if none. */ | |
35 | }; | |
36 | ||
37 | static struct ovsdb_error * | |
38 | ovsdb_error_valist(const char *tag, const char *details, va_list args) | |
39 | { | |
40 | struct ovsdb_error *error = xmalloc(sizeof *error); | |
41 | error->tag = tag ? tag : "ovsdb error"; | |
42 | error->details = details ? xvasprintf(details, args) : NULL; | |
43 | error->syntax = NULL; | |
44 | error->errno_ = 0; | |
45 | return error; | |
46 | } | |
47 | ||
48 | struct ovsdb_error * | |
49 | ovsdb_error(const char *tag, const char *details, ...) | |
50 | { | |
51 | struct ovsdb_error *error; | |
52 | va_list args; | |
53 | ||
54 | va_start(args, details); | |
55 | error = ovsdb_error_valist(tag, details, args); | |
56 | va_end(args); | |
57 | ||
58 | return error; | |
59 | } | |
60 | ||
61 | struct ovsdb_error * | |
62 | ovsdb_io_error(int errno_, const char *details, ...) | |
63 | { | |
64 | struct ovsdb_error *error; | |
65 | va_list args; | |
66 | ||
67 | va_start(args, details); | |
68 | error = ovsdb_error_valist("I/O error", details, args); | |
69 | va_end(args); | |
70 | ||
71 | error->errno_ = errno_; | |
72 | ||
73 | return error; | |
74 | } | |
75 | ||
76 | struct ovsdb_error * | |
77 | ovsdb_syntax_error(const struct json *json, const char *tag, | |
78 | const char *details, ...) | |
79 | { | |
80 | struct ovsdb_error *error; | |
81 | va_list args; | |
82 | ||
83 | va_start(args, details); | |
84 | error = ovsdb_error_valist(tag ? tag : "syntax error", details, args); | |
85 | va_end(args); | |
86 | ||
87 | if (json) { | |
88 | /* XXX this is much too much information in some cases */ | |
62c87d4a | 89 | error->syntax = json_to_string(json, JSSF_SORT); |
f85f8ebb BP |
90 | } |
91 | ||
92 | return error; | |
93 | } | |
94 | ||
95 | struct ovsdb_error * | |
96 | ovsdb_wrap_error(struct ovsdb_error *error, const char *details, ...) | |
97 | { | |
98 | va_list args; | |
99 | char *msg; | |
100 | ||
101 | va_start(args, details); | |
102 | msg = xvasprintf(details, args); | |
103 | va_end(args); | |
104 | ||
105 | if (error->details) { | |
106 | char *new = xasprintf("%s: %s", msg, error->details); | |
107 | free(error->details); | |
108 | error->details = new; | |
109 | free(msg); | |
110 | } else { | |
111 | error->details = msg; | |
112 | } | |
113 | ||
114 | return error; | |
115 | } | |
116 | ||
1dd5b71d BP |
117 | /* Returns an ovsdb_error that represents an internal error for file name |
118 | * 'file' and line number 'line', with 'details' (formatted as with printf()) | |
119 | * as the associated message. The caller is responsible for freeing the | |
120 | * returned error. | |
121 | * | |
122 | * If 'inner_error' is nonnull then the returned error is wrapped around | |
123 | * 'inner_error'. Takes ownership of 'inner_error'. */ | |
f85f8ebb | 124 | struct ovsdb_error * |
1dd5b71d BP |
125 | ovsdb_internal_error(struct ovsdb_error *inner_error, |
126 | const char *file, int line, const char *details, ...) | |
f85f8ebb BP |
127 | { |
128 | struct ds ds = DS_EMPTY_INITIALIZER; | |
129 | struct backtrace backtrace; | |
130 | struct ovsdb_error *error; | |
131 | va_list args; | |
132 | ||
133 | ds_put_format(&ds, "%s:%d:", file, line); | |
134 | ||
135 | if (details) { | |
136 | ds_put_char(&ds, ' '); | |
137 | va_start(args, details); | |
138 | ds_put_format_valist(&ds, details, args); | |
139 | va_end(args); | |
140 | } | |
141 | ||
142 | backtrace_capture(&backtrace); | |
143 | if (backtrace.n_frames) { | |
144 | int i; | |
145 | ||
146 | ds_put_cstr(&ds, " (backtrace:"); | |
147 | for (i = 0; i < backtrace.n_frames; i++) { | |
148 | ds_put_format(&ds, " 0x%08"PRIxPTR, backtrace.frames[i]); | |
149 | } | |
150 | ds_put_char(&ds, ')'); | |
151 | } | |
152 | ||
8a07709c | 153 | ds_put_format(&ds, " (%s %s)", program_name, VERSION); |
f85f8ebb | 154 | |
1dd5b71d | 155 | if (inner_error) { |
3865965d | 156 | char *s = ovsdb_error_to_string_free(inner_error); |
1dd5b71d BP |
157 | ds_put_format(&ds, " (generated from: %s)", s); |
158 | free(s); | |
1dd5b71d BP |
159 | } |
160 | ||
f85f8ebb BP |
161 | error = ovsdb_error("internal error", "%s", ds_cstr(&ds)); |
162 | ||
163 | ds_destroy(&ds); | |
164 | ||
165 | return error; | |
166 | } | |
167 | ||
d6db7b3c LR |
168 | struct ovsdb_error * |
169 | ovsdb_perm_error(const char *details, ...) | |
170 | { | |
171 | struct ovsdb_error *error; | |
172 | va_list args; | |
173 | ||
174 | va_start(args, details); | |
175 | error = ovsdb_error_valist("permission error", details, args); | |
176 | va_end(args); | |
177 | ||
178 | return error; | |
179 | } | |
180 | ||
f85f8ebb BP |
181 | void |
182 | ovsdb_error_destroy(struct ovsdb_error *error) | |
183 | { | |
184 | if (error) { | |
185 | free(error->details); | |
186 | free(error->syntax); | |
187 | free(error); | |
188 | } | |
189 | } | |
190 | ||
191 | struct ovsdb_error * | |
192 | ovsdb_error_clone(const struct ovsdb_error *old) | |
193 | { | |
194 | if (old) { | |
195 | struct ovsdb_error *new = xmalloc(sizeof *new); | |
196 | new->tag = old->tag; | |
2225c0b9 BP |
197 | new->details = nullable_xstrdup(old->details); |
198 | new->syntax = nullable_xstrdup(old->syntax); | |
f85f8ebb BP |
199 | new->errno_ = old->errno_; |
200 | return new; | |
201 | } else { | |
202 | return NULL; | |
203 | } | |
204 | } | |
205 | ||
201891c3 BP |
206 | /* Returns 'error' converted to the <error> JSON object format described in RFC |
207 | * 7047. The caller must free the returned json (with json_destroy()). */ | |
f85f8ebb BP |
208 | struct json * |
209 | ovsdb_error_to_json(const struct ovsdb_error *error) | |
210 | { | |
211 | struct json *json = json_object_create(); | |
212 | json_object_put_string(json, "error", error->tag); | |
213 | if (error->details) { | |
214 | json_object_put_string(json, "details", error->details); | |
215 | } | |
201891c3 BP |
216 | |
217 | /* These are RFC 7047-compliant extensions. */ | |
f85f8ebb BP |
218 | if (error->syntax) { |
219 | json_object_put_string(json, "syntax", error->syntax); | |
220 | } | |
221 | if (error->errno_) { | |
222 | json_object_put_string(json, "io-error", | |
fe1e967e | 223 | ovs_retval_to_string(error->errno_)); |
f85f8ebb | 224 | } |
201891c3 BP |
225 | |
226 | return json; | |
227 | } | |
228 | ||
229 | /* Returns 'error' converted to the <error> JSON object format described in RFC | |
230 | * 7047. The caller must free the returned json (with json_destroy()). | |
231 | * | |
232 | * Also, frees 'error'. */ | |
233 | struct json * | |
234 | ovsdb_error_to_json_free(struct ovsdb_error *error) | |
235 | { | |
236 | struct json *json = ovsdb_error_to_json(error); | |
237 | ovsdb_error_destroy(error); | |
f85f8ebb BP |
238 | return json; |
239 | } | |
240 | ||
3865965d BP |
241 | /* Returns 'error' converted to a string suitable for use as an error message. |
242 | * The caller must free the returned string (with free()). */ | |
f85f8ebb BP |
243 | char * |
244 | ovsdb_error_to_string(const struct ovsdb_error *error) | |
245 | { | |
246 | struct ds ds = DS_EMPTY_INITIALIZER; | |
247 | if (error->syntax) { | |
248 | ds_put_format(&ds, "syntax \"%s\": ", error->syntax); | |
249 | } | |
250 | ds_put_cstr(&ds, error->tag); | |
251 | if (error->details) { | |
252 | ds_put_format(&ds, ": %s", error->details); | |
253 | } | |
254 | if (error->errno_) { | |
fe1e967e | 255 | ds_put_format(&ds, " (%s)", ovs_retval_to_string(error->errno_)); |
f85f8ebb BP |
256 | } |
257 | return ds_steal_cstr(&ds); | |
258 | } | |
259 | ||
3865965d BP |
260 | /* Returns 'error' converted to a string suitable for use as an error message. |
261 | * The caller must free the returned string (with free()). | |
262 | * | |
263 | * If 'error' is NULL, returns NULL. | |
264 | * | |
265 | * Also, frees 'error'. */ | |
266 | char * | |
267 | ovsdb_error_to_string_free(struct ovsdb_error *error) | |
268 | { | |
269 | if (error) { | |
270 | char *s = ovsdb_error_to_string(error); | |
271 | ovsdb_error_destroy(error); | |
272 | return s; | |
273 | } else { | |
274 | return NULL; | |
275 | } | |
276 | } | |
277 | ||
f85f8ebb BP |
278 | const char * |
279 | ovsdb_error_get_tag(const struct ovsdb_error *error) | |
280 | { | |
281 | return error->tag; | |
282 | } | |
80af01ed BP |
283 | |
284 | /* If 'error' is nonnull, logs it as an error and frees it. To be used in | |
285 | * situations where an error should never occur, but an 'ovsdb_error *' gets | |
286 | * passed back anyhow. */ | |
287 | void | |
288 | ovsdb_error_assert(struct ovsdb_error *error) | |
289 | { | |
290 | if (error) { | |
291 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); | |
3865965d | 292 | char *s = ovsdb_error_to_string_free(error); |
80af01ed BP |
293 | VLOG_ERR_RL(&rl, "unexpected ovsdb error: %s", s); |
294 | free(s); | |
80af01ed BP |
295 | } |
296 | } |