Coverage Report

Created: 2025-07-01 06:50

/src/openvswitch/lib/ovs-replay.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2021, Red Hat, Inc.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at:
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include <config.h>
18
#include <ctype.h>
19
#include <errno.h>
20
#include <stdlib.h>
21
#include <string.h>
22
#include <sys/socket.h>
23
#include <sys/types.h>
24
#include <unistd.h>
25
#include "dirs.h"
26
#include "ovs-atomic.h"
27
#include "ovs-replay.h"
28
#include "util.h"
29
#include "openvswitch/vlog.h"
30
31
VLOG_DEFINE_THIS_MODULE(ovs_replay);
32
33
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25);
34
35
static struct ovs_mutex replay_mutex = OVS_MUTEX_INITIALIZER;
36
static int replay_seqno OVS_GUARDED_BY(replay_mutex) = 0;
37
static atomic_int replay_state = OVS_REPLAY_NONE;
38
39
static char *dirname = NULL;
40
41
void
42
ovs_replay_set_state(enum ovs_replay_state state)
43
0
{
44
0
    atomic_store_relaxed(&replay_state, state);
45
0
}
46
47
enum ovs_replay_state
48
ovs_replay_get_state(void)
49
0
{
50
0
    int state;
51
52
0
    atomic_read_relaxed(&replay_state, &state);
53
0
    return state;
54
0
}
55
56
void
57
ovs_replay_set_dirname(const char *new_dirname)
58
0
{
59
0
    if (new_dirname) {
60
0
        free(dirname);
61
0
        dirname = xstrdup(new_dirname);
62
0
    }
63
0
}
64
65
void
66
ovs_replay_lock(void)
67
    OVS_ACQUIRES(replay_mutex)
68
0
{
69
0
    ovs_mutex_lock(&replay_mutex);
70
0
}
71
72
void
73
ovs_replay_unlock(void)
74
    OVS_RELEASES(replay_mutex)
75
0
{
76
0
    ovs_mutex_unlock(&replay_mutex);
77
0
}
78
79
int
80
ovs_replay_seqno(void)
81
    OVS_REQUIRES(replay_mutex)
82
0
{
83
0
    return replay_seqno;
84
0
}
85
86
static char *
87
ovs_replay_file_name(const char *name, int seqno)
88
0
{
89
0
    char *local_name = xstrdup(name);
90
0
    char *filename, *p, *c;
91
0
    bool skip = false;
92
93
    /* Replace all the numbers and special symbols with single underscore.
94
     * Numbers might be PIDs or port numbers that could change between record
95
     * and replay phases, special symbols might be not good as a filename.
96
     * We have a unique seuqence number as part of the name, so we don't care
97
     * keeping too much information. */
98
0
    for (c = p = local_name; *p; p++) {
99
0
         if (!isalpha((unsigned char) *p)) {
100
0
             if (!skip) {
101
0
                *c++ = '_';
102
0
                skip = true;
103
0
             }
104
0
         } else {
105
0
             *c++ = *p;
106
0
             skip = false;
107
0
         }
108
0
    }
109
0
    if (skip) {
110
0
        c--;
111
0
    }
112
0
    *c = '\0';
113
0
    filename = xasprintf("%s/replay_%s_%d", dirname ? dirname : "",
114
0
                                            local_name, seqno);
115
0
    VLOG_DBG("Constructing replay filename: '%s' --> '%s' --> '%s'.",
116
0
             name, local_name, filename);
117
0
    free(local_name);
118
119
0
    return filename;
120
0
}
121
122
int
123
ovs_replay_file_open(const char *name, replay_file_t *f, int *seqno)
124
    OVS_REQUIRES(replay_mutex)
125
0
{
126
0
    char *file_path, *filename;
127
0
    int state = ovs_replay_get_state();
128
129
0
    ovs_assert(state != OVS_REPLAY_NONE);
130
131
0
    filename = ovs_replay_file_name(name, replay_seqno);
132
0
    if (filename[0] != '/') {
133
0
        file_path = abs_file_name(ovs_rundir(), filename);
134
0
        free(filename);
135
0
    } else {
136
0
        file_path = filename;
137
0
    }
138
139
0
    *f = fopen(file_path, state == OVS_REPLAY_WRITE ? "wb" : "rb");
140
0
    if (!*f) {
141
0
        VLOG_ERR_RL(&rl, "%s: fopen failed: %s",
142
0
                    file_path, ovs_strerror(errno));
143
0
        free(file_path);
144
0
        return errno;
145
0
    }
146
0
    free(file_path);
147
148
0
    if (state == OVS_REPLAY_READ
149
0
        && fread(seqno, sizeof *seqno, 1, *f) != 1) {
150
0
        VLOG_INFO("%s: failed to read seqno: replay might be empty.", name);
151
0
        *seqno = INT_MAX;
152
0
    }
153
0
    replay_seqno++;  /* New file opened. */
154
0
    return 0;
155
0
}
156
157
void
158
ovs_replay_file_close(replay_file_t f)
159
0
{
160
0
    fclose(f);
161
0
}
162
163
int
164
ovs_replay_write(replay_file_t f, const void *buffer, int n, bool is_read)
165
    OVS_EXCLUDED(replay_mutex)
166
0
{
167
0
    int state = ovs_replay_get_state();
168
0
    int seqno_to_write;
169
0
    int retval = 0;
170
171
0
    if (OVS_LIKELY(state != OVS_REPLAY_WRITE)) {
172
0
        return 0;
173
0
    }
174
175
0
    ovs_replay_lock();
176
177
0
    seqno_to_write = is_read ? replay_seqno : -replay_seqno;
178
0
    if (fwrite(&seqno_to_write, sizeof seqno_to_write, 1, f) != 1) {
179
0
        VLOG_ERR_RL(&rl, "Failed to write seqno.");
180
0
        retval = -1;
181
0
        goto out;
182
0
    }
183
0
    if (fwrite(&n, sizeof n, 1, f) != 1) {
184
0
        VLOG_ERR_RL(&rl, "Failed to write length.");
185
0
        retval = -1;
186
0
        goto out;
187
0
    }
188
0
    if (n > 0 && is_read && fwrite(buffer, 1, n, f) != n) {
189
0
        VLOG_ERR_RL(&rl, "Failed to write data.");
190
0
        retval = -1;
191
0
    }
192
0
out:
193
0
    replay_seqno++; /* Write completed. */
194
0
    ovs_replay_unlock();
195
0
    fflush(f);
196
0
    return retval;
197
0
}
198
199
int
200
ovs_replay_read(replay_file_t f, void *buffer, int buffer_size,
201
                int *len, int *seqno, bool is_read)
202
    OVS_REQUIRES(replay_mutex)
203
0
{
204
0
    int retval = EINVAL;
205
206
0
    if (fread(len, sizeof *len, 1, f) != 1) {
207
0
        VLOG_ERR_RL(&rl, "Failed to read replay length.");
208
0
        goto out;
209
0
    }
210
211
0
    if (is_read && *len > buffer_size) {
212
0
        VLOG_ERR_RL(&rl, "Failed to read replay buffer: "
213
0
                    "insufficient buffer size: provided %d, needed %d.",
214
0
                    buffer_size, *len);
215
0
        goto out;
216
0
    }
217
218
0
    if (*len > 0 && is_read && fread(buffer, 1, *len, f) != *len) {
219
0
        VLOG_ERR_RL(&rl, "Failed to read replay buffer.");
220
0
        goto out;
221
0
    }
222
223
0
    if (fread(seqno, sizeof *seqno, 1, f) != 1) {
224
0
        *seqno = INT_MAX;  /* Most likely EOF. */
225
0
        if (ferror(f)) {
226
0
            VLOG_INFO("Failed to read replay seqno.");
227
0
            goto out;
228
0
        }
229
0
    }
230
231
0
    retval = 0;
232
0
out:
233
0
    replay_seqno++;  /* Read completed. */
234
0
    return retval;
235
0
}
236
237
void
238
ovs_replay_usage(void)
239
0
{
240
0
    printf("\nReplay options:\n"
241
0
           "  --record[=DIR]            turn on writing replay files\n"
242
0
           "  --replay[=DIR]            run from replay files\n");
243
0
}