ovsdb: Remove SPECS in favor of referring to RFC 7047.
[sliver-openvswitch.git] / lib / ovsdb-error.c
1 /* Copyright (c) 2009, 2010, 2011, 2012 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 "dynamic-string.h"
24 #include "json.h"
25 #include "util.h"
26 #include "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 {
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 */
89         error->syntax = json_to_string(json, JSSF_SORT);
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
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 {
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
153     ds_put_format(&ds, " (%s %s)", program_name, VERSION);
154
155     if (inner_error) {
156         char *s = ovsdb_error_to_string(inner_error);
157         ds_put_format(&ds, " (generated from: %s)", s);
158         free(s);
159
160         ovsdb_error_destroy(inner_error);
161     }
162
163     error = ovsdb_error("internal error", "%s", ds_cstr(&ds));
164
165     ds_destroy(&ds);
166
167     return error;
168 }
169
170 void
171 ovsdb_error_destroy(struct ovsdb_error *error)
172 {
173     if (error) {
174         free(error->details);
175         free(error->syntax);
176         free(error);
177     }
178 }
179
180 struct ovsdb_error *
181 ovsdb_error_clone(const struct ovsdb_error *old)
182 {
183     if (old) {
184         struct ovsdb_error *new = xmalloc(sizeof *new);
185         new->tag = old->tag;
186         new->details = old->details ? xstrdup(old->details) : NULL;
187         new->syntax = old->syntax ? xstrdup(old->syntax) : NULL;
188         new->errno_ = old->errno_;
189         return new;
190     } else {
191         return NULL;
192     }
193 }
194
195 struct json *
196 ovsdb_error_to_json(const struct ovsdb_error *error)
197 {
198     struct json *json = json_object_create();
199     json_object_put_string(json, "error", error->tag);
200     if (error->details) {
201         json_object_put_string(json, "details", error->details);
202     }
203     if (error->syntax) {
204         json_object_put_string(json, "syntax", error->syntax);
205     }
206     if (error->errno_) {
207         json_object_put_string(json, "io-error",
208                                ovs_retval_to_string(error->errno_));
209     }
210     return json;
211 }
212
213 char *
214 ovsdb_error_to_string(const struct ovsdb_error *error)
215 {
216     struct ds ds = DS_EMPTY_INITIALIZER;
217     if (error->syntax) {
218         ds_put_format(&ds, "syntax \"%s\": ", error->syntax);
219     }
220     ds_put_cstr(&ds, error->tag);
221     if (error->details) {
222         ds_put_format(&ds, ": %s", error->details);
223     }
224     if (error->errno_) {
225         ds_put_format(&ds, " (%s)", ovs_retval_to_string(error->errno_));
226     }
227     return ds_steal_cstr(&ds);
228 }
229
230 const char *
231 ovsdb_error_get_tag(const struct ovsdb_error *error)
232 {
233     return error->tag;
234 }
235
236 /* If 'error' is nonnull, logs it as an error and frees it.  To be used in
237  * situations where an error should never occur, but an 'ovsdb_error *' gets
238  * passed back anyhow. */
239 void
240 ovsdb_error_assert(struct ovsdb_error *error)
241 {
242     if (error) {
243         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
244         char *s = ovsdb_error_to_string(error);
245         VLOG_ERR_RL(&rl, "unexpected ovsdb error: %s", s);
246         free(s);
247         ovsdb_error_destroy(error);
248     }
249 }