tests: Fix bug in ovsdb-server test suite.
[sliver-openvswitch.git] / lib / ovsdb-error.c
1 /* Copyright (c) 2009, 2010 Nicira Networks
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 #define THIS_MODULE VLM_ovsdb_error
29 #include "vlog.h"
30
31 struct ovsdb_error {
32     const char *tag;            /* String for "error" member. */
33     char *details;              /* String for "details" member. */
34     char *syntax;               /* String for "syntax" member. */
35     int errno_;                 /* Unix errno value, 0 if none. */
36 };
37
38 static struct ovsdb_error *
39 ovsdb_error_valist(const char *tag, const char *details, va_list args)
40 {
41     struct ovsdb_error *error = xmalloc(sizeof *error);
42     error->tag = tag ? tag : "ovsdb error";
43     error->details = details ? xvasprintf(details, args) : NULL;
44     error->syntax = NULL;
45     error->errno_ = 0;
46     return error;
47 }
48
49 struct ovsdb_error *
50 ovsdb_error(const char *tag, const char *details, ...)
51 {
52     struct ovsdb_error *error;
53     va_list args;
54
55     va_start(args, details);
56     error = ovsdb_error_valist(tag, details, args);
57     va_end(args);
58
59     return error;
60 }
61
62 struct ovsdb_error *
63 ovsdb_io_error(int errno_, const char *details, ...)
64 {
65     struct ovsdb_error *error;
66     va_list args;
67
68     va_start(args, details);
69     error = ovsdb_error_valist("I/O error", details, args);
70     va_end(args);
71
72     error->errno_ = errno_;
73
74     return error;
75 }
76
77 struct ovsdb_error *
78 ovsdb_syntax_error(const struct json *json, const char *tag,
79                    const char *details, ...)
80 {
81     struct ovsdb_error *error;
82     va_list args;
83
84     va_start(args, details);
85     error = ovsdb_error_valist(tag ? tag : "syntax error", details, args);
86     va_end(args);
87
88     if (json) {
89         /* XXX this is much too much information in some cases */
90         error->syntax = json_to_string(json, JSSF_SORT);
91     }
92
93     return error;
94 }
95
96 struct ovsdb_error *
97 ovsdb_wrap_error(struct ovsdb_error *error, const char *details, ...)
98 {
99     va_list args;
100     char *msg;
101
102     va_start(args, details);
103     msg = xvasprintf(details, args);
104     va_end(args);
105
106     if (error->details) {
107         char *new = xasprintf("%s: %s", msg, error->details);
108         free(error->details);
109         error->details = new;
110         free(msg);
111     } else {
112         error->details = msg;
113     }
114
115     return error;
116 }
117
118 struct ovsdb_error *
119 ovsdb_internal_error(const char *file, int line, const char *details, ...)
120 {
121     struct ds ds = DS_EMPTY_INITIALIZER;
122     struct backtrace backtrace;
123     struct ovsdb_error *error;
124     va_list args;
125
126     ds_put_format(&ds, "%s:%d:", file, line);
127
128     if (details) {
129         ds_put_char(&ds, ' ');
130         va_start(args, details);
131         ds_put_format_valist(&ds, details, args);
132         va_end(args);
133     }
134
135     backtrace_capture(&backtrace);
136     if (backtrace.n_frames) {
137         int i;
138
139         ds_put_cstr(&ds, " (backtrace:");
140         for (i = 0; i < backtrace.n_frames; i++) {
141             ds_put_format(&ds, " 0x%08"PRIxPTR, backtrace.frames[i]);
142         }
143         ds_put_char(&ds, ')');
144     }
145
146     ds_put_format(&ds, " (%s %s%s)", program_name, VERSION, BUILDNR);
147
148     error = ovsdb_error("internal error", "%s", ds_cstr(&ds));
149
150     ds_destroy(&ds);
151
152     return error;
153 }
154
155 void
156 ovsdb_error_destroy(struct ovsdb_error *error)
157 {
158     if (error) {
159         free(error->details);
160         free(error->syntax);
161         free(error);
162     }
163 }
164
165 struct ovsdb_error *
166 ovsdb_error_clone(const struct ovsdb_error *old)
167 {
168     if (old) {
169         struct ovsdb_error *new = xmalloc(sizeof *new);
170         new->tag = old->tag;
171         new->details = old->details ? xstrdup(old->details) : NULL;
172         new->syntax = old->syntax ? xstrdup(old->syntax) : NULL;
173         new->errno_ = old->errno_;
174         return new;
175     } else {
176         return NULL;
177     }
178 }
179
180 static const char *
181 ovsdb_errno_string(int error)
182 {
183     return error == EOF ? "unexpected end of file" : strerror(error);
184 }
185
186 struct json *
187 ovsdb_error_to_json(const struct ovsdb_error *error)
188 {
189     struct json *json = json_object_create();
190     json_object_put_string(json, "error", error->tag);
191     if (error->details) {
192         json_object_put_string(json, "details", error->details);
193     }
194     if (error->syntax) {
195         json_object_put_string(json, "syntax", error->syntax);
196     }
197     if (error->errno_) {
198         json_object_put_string(json, "io-error",
199                                ovsdb_errno_string(error->errno_));
200     }
201     return json;
202 }
203
204 char *
205 ovsdb_error_to_string(const struct ovsdb_error *error)
206 {
207     struct ds ds = DS_EMPTY_INITIALIZER;
208     if (error->syntax) {
209         ds_put_format(&ds, "syntax \"%s\": ", error->syntax);
210     }
211     ds_put_cstr(&ds, error->tag);
212     if (error->details) {
213         ds_put_format(&ds, ": %s", error->details);
214     }
215     if (error->errno_) {
216         ds_put_format(&ds, " (%s)", ovsdb_errno_string(error->errno_));
217     }
218     return ds_steal_cstr(&ds);
219 }
220
221 const char *
222 ovsdb_error_get_tag(const struct ovsdb_error *error)
223 {
224     return error->tag;
225 }
226
227 /* If 'error' is nonnull, logs it as an error and frees it.  To be used in
228  * situations where an error should never occur, but an 'ovsdb_error *' gets
229  * passed back anyhow. */
230 void
231 ovsdb_error_assert(struct ovsdb_error *error)
232 {
233     if (error) {
234         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
235         char *s = ovsdb_error_to_string(error);
236         VLOG_ERR_RL(&rl, "unexpected ovsdb error: %s", s);
237         free(s);
238         ovsdb_error_destroy(error);
239     }
240 }
241