Coverage Report

Created: 2025-07-18 06:07

/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
}