/src/openvswitch/lib/ovsdb-error.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 2009, 2010, 2011, 2012, 2016, 2017 Nicira, Inc. |
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" |
23 | | #include "openvswitch/dynamic-string.h" |
24 | | #include "openvswitch/json.h" |
25 | | #include "util.h" |
26 | | #include "openvswitch/vlog.h" |
27 | | |
28 | | VLOG_DEFINE_THIS_MODULE(ovsdb_error); |
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 | 0 | { |
40 | 0 | struct ovsdb_error *error = xmalloc(sizeof *error); |
41 | 0 | error->tag = tag ? tag : "ovsdb error"; |
42 | 0 | error->details = details ? xvasprintf(details, args) : NULL; |
43 | 0 | error->syntax = NULL; |
44 | 0 | error->errno_ = 0; |
45 | 0 | return error; |
46 | 0 | } |
47 | | |
48 | | struct ovsdb_error * |
49 | | ovsdb_error(const char *tag, const char *details, ...) |
50 | 0 | { |
51 | 0 | struct ovsdb_error *error; |
52 | 0 | va_list args; |
53 | |
|
54 | 0 | va_start(args, details); |
55 | 0 | error = ovsdb_error_valist(tag, details, args); |
56 | 0 | va_end(args); |
57 | |
|
58 | 0 | return error; |
59 | 0 | } |
60 | | |
61 | | struct ovsdb_error * |
62 | | ovsdb_io_error(int errno_, const char *details, ...) |
63 | 0 | { |
64 | 0 | struct ovsdb_error *error; |
65 | 0 | va_list args; |
66 | |
|
67 | 0 | va_start(args, details); |
68 | 0 | error = ovsdb_error_valist("I/O error", details, args); |
69 | 0 | va_end(args); |
70 | |
|
71 | 0 | error->errno_ = errno_; |
72 | |
|
73 | 0 | return error; |
74 | 0 | } |
75 | | |
76 | | struct ovsdb_error * |
77 | | ovsdb_syntax_error(const struct json *json, const char *tag, |
78 | | const char *details, ...) |
79 | 0 | { |
80 | 0 | struct ovsdb_error *error; |
81 | 0 | va_list args; |
82 | |
|
83 | 0 | va_start(args, details); |
84 | 0 | error = ovsdb_error_valist(tag ? tag : "syntax error", details, args); |
85 | 0 | va_end(args); |
86 | |
|
87 | 0 | if (json) { |
88 | | /* XXX this is much too much information in some cases */ |
89 | 0 | error->syntax = json_to_string(json, JSSF_SORT); |
90 | 0 | } |
91 | |
|
92 | 0 | return error; |
93 | 0 | } |
94 | | |
95 | | struct ovsdb_error * |
96 | | ovsdb_wrap_error(struct ovsdb_error *error, const char *details, ...) |
97 | 0 | { |
98 | 0 | va_list args; |
99 | 0 | char *msg; |
100 | |
|
101 | 0 | va_start(args, details); |
102 | 0 | msg = xvasprintf(details, args); |
103 | 0 | va_end(args); |
104 | |
|
105 | 0 | if (error->details) { |
106 | 0 | char *new = xasprintf("%s: %s", msg, error->details); |
107 | 0 | free(error->details); |
108 | 0 | error->details = new; |
109 | 0 | free(msg); |
110 | 0 | } else { |
111 | 0 | error->details = msg; |
112 | 0 | } |
113 | |
|
114 | 0 | return error; |
115 | 0 | } |
116 | | |
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'. */ |
124 | | struct ovsdb_error * |
125 | | ovsdb_internal_error(struct ovsdb_error *inner_error, |
126 | | const char *file, int line, const char *details, ...) |
127 | 0 | { |
128 | 0 | struct ds ds = DS_EMPTY_INITIALIZER; |
129 | 0 | struct backtrace backtrace; |
130 | 0 | struct ovsdb_error *error; |
131 | 0 | va_list args; |
132 | |
|
133 | 0 | ds_put_format(&ds, "%s:%d:", file, line); |
134 | |
|
135 | 0 | if (details) { |
136 | 0 | ds_put_char(&ds, ' '); |
137 | 0 | va_start(args, details); |
138 | 0 | ds_put_format_valist(&ds, details, args); |
139 | 0 | va_end(args); |
140 | 0 | } |
141 | |
|
142 | 0 | backtrace_capture(&backtrace); |
143 | 0 | if (backtrace.n_frames) { |
144 | 0 | ds_put_cstr(&ds, " (backtrace:"); |
145 | 0 | backtrace_format(&ds, &backtrace, ", "); |
146 | 0 | ds_put_char(&ds, ')'); |
147 | 0 | } |
148 | |
|
149 | 0 | ds_put_format(&ds, " (%s)", ovs_get_program_version()); |
150 | |
|
151 | 0 | if (inner_error) { |
152 | 0 | char *s = ovsdb_error_to_string_free(inner_error); |
153 | 0 | ds_put_format(&ds, " (generated from: %s)", s); |
154 | 0 | free(s); |
155 | 0 | } |
156 | |
|
157 | 0 | error = ovsdb_error("internal error", "%s", ds_cstr(&ds)); |
158 | |
|
159 | 0 | ds_destroy(&ds); |
160 | |
|
161 | 0 | return error; |
162 | 0 | } |
163 | | |
164 | | struct ovsdb_error * |
165 | | ovsdb_perm_error(const char *details, ...) |
166 | 0 | { |
167 | 0 | struct ovsdb_error *error; |
168 | 0 | va_list args; |
169 | |
|
170 | 0 | va_start(args, details); |
171 | 0 | error = ovsdb_error_valist("permission error", details, args); |
172 | 0 | va_end(args); |
173 | |
|
174 | 0 | return error; |
175 | 0 | } |
176 | | |
177 | | void |
178 | | ovsdb_error_destroy(struct ovsdb_error *error) |
179 | 0 | { |
180 | 0 | if (error) { |
181 | 0 | free(error->details); |
182 | 0 | free(error->syntax); |
183 | 0 | free(error); |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | struct ovsdb_error * |
188 | | ovsdb_error_clone(const struct ovsdb_error *old) |
189 | 0 | { |
190 | 0 | if (old) { |
191 | 0 | struct ovsdb_error *new = xmalloc(sizeof *new); |
192 | 0 | new->tag = old->tag; |
193 | 0 | new->details = nullable_xstrdup(old->details); |
194 | 0 | new->syntax = nullable_xstrdup(old->syntax); |
195 | 0 | new->errno_ = old->errno_; |
196 | 0 | return new; |
197 | 0 | } else { |
198 | 0 | return NULL; |
199 | 0 | } |
200 | 0 | } |
201 | | |
202 | | /* Returns 'error' converted to the <error> JSON object format described in RFC |
203 | | * 7047. The caller must free the returned json (with json_destroy()). */ |
204 | | struct json * |
205 | | ovsdb_error_to_json(const struct ovsdb_error *error) |
206 | 0 | { |
207 | 0 | struct json *json = json_object_create(); |
208 | 0 | json_object_put_string(json, "error", error->tag); |
209 | 0 | if (error->details) { |
210 | 0 | json_object_put_string(json, "details", error->details); |
211 | 0 | } |
212 | | |
213 | | /* These are RFC 7047-compliant extensions. */ |
214 | 0 | if (error->syntax) { |
215 | 0 | json_object_put_string(json, "syntax", error->syntax); |
216 | 0 | } |
217 | 0 | if (error->errno_) { |
218 | 0 | json_object_put_string(json, "io-error", |
219 | 0 | ovs_retval_to_string(error->errno_)); |
220 | 0 | } |
221 | |
|
222 | 0 | return json; |
223 | 0 | } |
224 | | |
225 | | /* Returns 'error' converted to the <error> JSON object format described in RFC |
226 | | * 7047. The caller must free the returned json (with json_destroy()). |
227 | | * |
228 | | * Also, frees 'error'. */ |
229 | | struct json * |
230 | | ovsdb_error_to_json_free(struct ovsdb_error *error) |
231 | 0 | { |
232 | 0 | struct json *json = ovsdb_error_to_json(error); |
233 | 0 | ovsdb_error_destroy(error); |
234 | 0 | return json; |
235 | 0 | } |
236 | | |
237 | | /* Returns 'error' converted to a string suitable for use as an error message. |
238 | | * The caller must free the returned string (with free()). */ |
239 | | char * |
240 | | ovsdb_error_to_string(const struct ovsdb_error *error) |
241 | 0 | { |
242 | 0 | struct ds ds = DS_EMPTY_INITIALIZER; |
243 | 0 | if (error->syntax) { |
244 | 0 | ds_put_format(&ds, "syntax \"%s\": ", error->syntax); |
245 | 0 | } |
246 | 0 | ds_put_cstr(&ds, error->tag); |
247 | 0 | if (error->details) { |
248 | 0 | ds_put_format(&ds, ": %s", error->details); |
249 | 0 | } |
250 | 0 | if (error->errno_) { |
251 | 0 | ds_put_format(&ds, " (%s)", ovs_retval_to_string(error->errno_)); |
252 | 0 | } |
253 | 0 | return ds_steal_cstr(&ds); |
254 | 0 | } |
255 | | |
256 | | /* Returns 'error' converted to a string suitable for use as an error message. |
257 | | * The caller must free the returned string (with free()). |
258 | | * |
259 | | * If 'error' is NULL, returns NULL. |
260 | | * |
261 | | * Also, frees 'error'. */ |
262 | | char * |
263 | | ovsdb_error_to_string_free(struct ovsdb_error *error) |
264 | 0 | { |
265 | 0 | if (error) { |
266 | 0 | char *s = ovsdb_error_to_string(error); |
267 | 0 | ovsdb_error_destroy(error); |
268 | 0 | return s; |
269 | 0 | } else { |
270 | 0 | return NULL; |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | | const char * |
275 | | ovsdb_error_get_tag(const struct ovsdb_error *error) |
276 | 0 | { |
277 | 0 | return error->tag; |
278 | 0 | } |
279 | | |
280 | | /* If 'error' is nonnull, logs it as an error and frees it. To be used in |
281 | | * situations where an error should never occur, but an 'ovsdb_error *' gets |
282 | | * passed back anyhow. */ |
283 | | void |
284 | | ovsdb_error_assert(struct ovsdb_error *error) |
285 | 0 | { |
286 | 0 | if (error) { |
287 | 0 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); |
288 | 0 | char *s = ovsdb_error_to_string_free(error); |
289 | 0 | VLOG_ERR_RL(&rl, "unexpected ovsdb error: %s", s); |
290 | 0 | free(s); |
291 | 0 | } |
292 | 0 | } |