• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 23877209284

01 Apr 2026 05:23PM UTC coverage: 72.343% (-0.06%) from 72.404%
23877209284

push

github

bluca
hwdb/keyboard: fix enter key for X+ piccolo

The main enter key gives a code for keypad one... Map it to
regular enter key.

318395 of 440116 relevant lines covered (72.34%)

1157750.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.54
/src/shared/socket-forward.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <sys/socket.h>
5
#include <unistd.h>
6

7
#include "sd-event.h"
8

9
#include "alloc-util.h"
10
#include "errno-util.h"
11
#include "fd-util.h"
12
#include "log.h"
13
#include "socket-forward.h"
14

15
#define SOCKET_FORWARD_BUFFER_SIZE (256 * 1024)
16

17
/* Unidirectional forwarder: splices data from read_fd to write_fd via a kernel pipe buffer.
18
 * Each direction of a full-duplex SocketForward is handled by one of these. */
19
typedef struct SimplexForward {
20
        sd_event *event;
21

22
        int read_fd, write_fd;
23

24
        int buffer[2]; /* a pipe */
25

26
        size_t buffer_full, buffer_size;
27

28
        sd_event_source *read_event_source, *write_event_source;
29

30
        int (*on_done)(struct SimplexForward *fwd, int error, void *userdata);
31
        void *userdata;
32
} SimplexForward;
33

34
static SimplexForward* simplex_forward_free(SimplexForward *fwd) {
6✔
35
        if (!fwd)
6✔
36
                return NULL;
37

38
        sd_event_source_unref(fwd->read_event_source);
6✔
39
        sd_event_source_unref(fwd->write_event_source);
6✔
40

41
        safe_close(fwd->read_fd);
6✔
42
        safe_close(fwd->write_fd);
6✔
43

44
        safe_close_pair(fwd->buffer);
6✔
45

46
        sd_event_unref(fwd->event);
6✔
47

48
        return mfree(fwd);
6✔
49
}
50

51
DEFINE_TRIVIAL_CLEANUP_FUNC(SimplexForward*, simplex_forward_free);
6✔
52

53
static int socket_forward_create_pipes(int buffer[static 2], size_t *ret_size) {
6✔
54
        int r;
6✔
55

56
        assert(buffer);
6✔
57
        assert(ret_size);
6✔
58

59
        if (buffer[0] >= 0)
6✔
60
                return 0;
61

62
        r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK);
6✔
63
        if (r < 0)
6✔
64
                return log_debug_errno(errno, "Failed to allocate pipe buffer: %m");
×
65

66
        (void) fcntl(buffer[0], F_SETPIPE_SZ, SOCKET_FORWARD_BUFFER_SIZE);
6✔
67

68
        r = fcntl(buffer[0], F_GETPIPE_SZ);
6✔
69
        if (r < 0)
6✔
70
                return log_debug_errno(errno, "Failed to get pipe buffer size: %m");
×
71

72
        assert(r > 0);
6✔
73
        *ret_size = r;
6✔
74

75
        return 0;
6✔
76
}
77

78
static int simplex_forward_shovel(
11✔
79
                int *from, int buffer[2], int *to,
80
                size_t *full, size_t *sz,
81
                sd_event_source **from_source, sd_event_source **to_source) {
82

83
        bool shoveled;
11✔
84

85
        assert(from);
11✔
86
        assert(buffer);
11✔
87
        assert(buffer[0] >= 0);
11✔
88
        assert(buffer[1] >= 0);
11✔
89
        assert(to);
11✔
90
        assert(full);
11✔
91
        assert(sz);
11✔
92
        assert(from_source);
11✔
93
        assert(to_source);
11✔
94

95
        do {
19✔
96
                ssize_t z;
19✔
97

98
                shoveled = false;
19✔
99

100
                if (*full < *sz && *from >= 0 && *to >= 0) {
19✔
101
                        z = splice(*from, NULL, buffer[1], NULL, *sz - *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
19✔
102
                        if (z > 0) {
19✔
103
                                *full += z;
8✔
104
                                shoveled = true;
8✔
105
                        } else if (z == 0 || ERRNO_IS_DISCONNECT(errno)) {
11✔
106
                                *from_source = sd_event_source_unref(*from_source);
6✔
107
                                *from = safe_close(*from);
6✔
108
                        } else if (!ERRNO_IS_TRANSIENT(errno))
5✔
109
                                return log_debug_errno(errno, "Failed to splice: %m");
×
110
                }
111

112
                if (*full > 0 && *to >= 0) {
19✔
113
                        z = splice(buffer[0], NULL, *to, NULL, *full, SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
8✔
114
                        if (z > 0) {
8✔
115
                                *full -= z;
8✔
116
                                shoveled = true;
8✔
117
                        } else if (z == 0 || ERRNO_IS_DISCONNECT(errno)) {
×
118
                                *to_source = sd_event_source_unref(*to_source);
×
119
                                *to = safe_close(*to);
×
120
                        } else if (!ERRNO_IS_TRANSIENT(errno))
×
121
                                return log_debug_errno(errno, "Failed to splice: %m");
×
122
                }
123
        } while (shoveled);
19✔
124

125
        return 0;
126
}
127

128
static int simplex_forward_enable_event_sources(SimplexForward *fwd);
129

130
static int simplex_forward_traffic(SimplexForward *fwd) {
11✔
131
        int r;
11✔
132

133
        r = simplex_forward_shovel(
22✔
134
                        &fwd->read_fd, fwd->buffer, &fwd->write_fd,
11✔
135
                        &fwd->buffer_full, &fwd->buffer_size,
136
                        &fwd->read_event_source, &fwd->write_event_source);
137
        if (r < 0)
11✔
138
                goto quit;
×
139

140
        /* Read side closed and all buffered data written? */
141
        if (fwd->read_fd < 0 && fwd->buffer_full <= 0)
11✔
142
                goto quit;
6✔
143

144
        /* Write side closed? */
145
        if (fwd->write_fd < 0)
5✔
146
                goto quit;
×
147

148
        r = simplex_forward_enable_event_sources(fwd);
5✔
149
        if (r < 0)
5✔
150
                goto quit;
×
151

152
        return 1;
153

154
quit:
6✔
155
        fwd->read_event_source = sd_event_source_disable_unref(fwd->read_event_source);
6✔
156
        fwd->write_event_source = sd_event_source_disable_unref(fwd->write_event_source);
6✔
157
        return fwd->on_done(fwd, r, fwd->userdata);
6✔
158
}
159

160
static int simplex_forward_io_cb(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
10✔
161
        SimplexForward *fwd = ASSERT_PTR(userdata);
10✔
162

163
        return simplex_forward_traffic(fwd);
10✔
164
}
165

166
static int simplex_forward_defer_cb(sd_event_source *s, void *userdata) {
1✔
167
        SimplexForward *fwd = ASSERT_PTR(userdata);
1✔
168

169
        return simplex_forward_traffic(fwd);
1✔
170
}
171

172
static int simplex_forward_enable_event_sources(SimplexForward *fwd) {
11✔
173
        bool can_read, can_write;
11✔
174
        int r;
11✔
175

176
        assert(fwd);
11✔
177

178
        can_read = fwd->buffer_full < fwd->buffer_size;
11✔
179
        can_write = fwd->buffer_full > 0;
11✔
180

181
        /* Event sources may have been unref'd by the shovel on EOF/disconnect */
182
        if (fwd->read_event_source) {
11✔
183
                r = sd_event_source_set_enabled(fwd->read_event_source, can_read ? SD_EVENT_ONESHOT : SD_EVENT_OFF);
11✔
184
                if (r < 0)
11✔
185
                        return log_debug_errno(r, "Failed to update read event source: %m");
×
186
        }
187

188
        if (fwd->write_event_source) {
11✔
189
                r = sd_event_source_set_enabled(fwd->write_event_source, can_write ? SD_EVENT_ONESHOT : SD_EVENT_OFF);
22✔
190
                if (r < 0)
11✔
191
                        return log_debug_errno(r, "Failed to update write event source: %m");
×
192
        }
193

194
        return 0;
195
}
196

197
static int simplex_forward_create_event_source(
12✔
198
                SimplexForward *fwd,
199
                sd_event_source **ret,
200
                int fd,
201
                uint32_t events) {
202

203
        int r;
12✔
204

205
        r = sd_event_add_io(fwd->event, ret, fd, events, simplex_forward_io_cb, fwd);
12✔
206
        if (r == -EPERM)
12✔
207
                /* fd is not pollable (e.g. regular file). Fall back to a defer event source
208
                 * which fires on each event loop iteration. This works because regular
209
                 * file are always ready for I/O so we don't need to poll. */
210
                r = sd_event_add_defer(fwd->event, ret, simplex_forward_defer_cb, fwd);
1✔
211

212
        return r;
12✔
213
}
214

215
static int simplex_forward_new(
6✔
216
                sd_event *event,
217
                int read_fd,
218
                int write_fd,
219
                int (*on_done)(SimplexForward *fwd, int error, void *userdata),
220
                void *userdata,
221
                SimplexForward **ret) {
222

223
        _cleanup_(simplex_forward_freep) SimplexForward *fwd = NULL;
6✔
224
        int r;
6✔
225

226
        assert(event);
6✔
227
        assert(read_fd >= 0);
6✔
228
        assert(write_fd >= 0);
6✔
229
        assert(read_fd != write_fd);
6✔
230
        assert(on_done);
6✔
231
        assert(ret);
6✔
232

233
        fwd = new(SimplexForward, 1);
6✔
234
        if (!fwd) {
6✔
235
                safe_close(read_fd);
×
236
                safe_close(write_fd);
×
237
                return log_oom_debug();
×
238
        }
239

240
        *fwd = (SimplexForward) {
12✔
241
                .event = sd_event_ref(event),
6✔
242
                .read_fd = read_fd,
243
                .write_fd = write_fd,
244
                .buffer = EBADF_PAIR,
245
                .on_done = on_done,
246
                .userdata = userdata,
247
        };
248

249
        r = socket_forward_create_pipes(fwd->buffer, &fwd->buffer_size);
6✔
250
        if (r < 0)
6✔
251
                return r;
252

253
        r = simplex_forward_create_event_source(fwd, &fwd->read_event_source, fwd->read_fd, EPOLLIN);
6✔
254
        if (r < 0)
6✔
255
                return r;
256

257
        r = simplex_forward_create_event_source(fwd, &fwd->write_event_source, fwd->write_fd, EPOLLOUT);
6✔
258
        if (r < 0)
6✔
259
                return r;
260

261
        r = simplex_forward_enable_event_sources(fwd);
6✔
262
        if (r < 0)
6✔
263
                return r;
264

265
        *ret = TAKE_PTR(fwd);
6✔
266
        return 0;
6✔
267
}
268

269
/* Full-duplex forwarder from two SimplexForward instances */
270
struct SocketForward {
271
        SimplexForward *server_to_client;
272
        SimplexForward *client_to_server;
273

274
        socket_forward_done_t on_done;
275
        void *userdata;
276

277
        int first_error;
278
        unsigned directions_done;
279
};
280

281
SocketForward* socket_forward_free(SocketForward *sf) {
3✔
282
        if (!sf)
3✔
283
                return NULL;
284

285
        simplex_forward_free(sf->server_to_client);
3✔
286
        simplex_forward_free(sf->client_to_server);
3✔
287

288
        return mfree(sf);
3✔
289
}
290

291
static int socket_forward_direction_done(SimplexForward *fwd, int error, void *userdata) {
6✔
292
        SocketForward *sf = ASSERT_PTR(userdata);
6✔
293

294
        /* Half-close the write side so the remote end sees EOF. For sockets,
295
         * shutdown(SHUT_WR) sends FIN while keeping the fd open for the read side
296
         * (which belongs to the other direction's dup'd fd). For pipes/FIFOs,
297
         * shutdown() fails with ENOTSOCK - close the fd instead, which is the
298
         * only way to signal EOF on a pipe. */
299
        if (fwd->write_fd >= 0 && shutdown(fwd->write_fd, SHUT_WR) < 0) {
6✔
300
                if (errno == ENOTSOCK)
4✔
301
                        fwd->write_fd = safe_close(fwd->write_fd);
4✔
302
                else
303
                        log_debug_errno(errno, "Failed to shutdown write side of fd %d: %m, ignoring",
×
304
                                        fwd->write_fd);
305
        }
306

307
        if (error < 0 && sf->first_error >= 0)
6✔
308
                sf->first_error = error;
×
309

310
        sf->directions_done++;
6✔
311

312
        if (sf->directions_done >= 2)
6✔
313
                return sf->on_done(sf, sf->first_error, sf->userdata);
3✔
314

315
        return 0;
316
}
317

318
int socket_forward_new_pair(
3✔
319
                sd_event *event,
320
                int server_read_fd,
321
                int server_write_fd,
322
                int client_read_fd,
323
                int client_write_fd,
324
                socket_forward_done_t on_done,
325
                void *userdata,
326
                SocketForward **ret) {
327

328
        _cleanup_close_ int server_read_fd_close = server_read_fd,
3✔
329
                            server_write_fd_close = server_write_fd,
3✔
330
                            client_read_fd_close = client_read_fd,
3✔
331
                            client_write_fd_close = client_write_fd;
3✔
332
        _cleanup_(socket_forward_freep) SocketForward *sf = NULL;
3✔
333
        int r;
3✔
334

335
        assert(event);
3✔
336
        assert(server_read_fd >= 0);
3✔
337
        assert(server_write_fd >= 0);
3✔
338
        assert(client_read_fd >= 0);
3✔
339
        assert(client_write_fd >= 0);
3✔
340
        assert(server_read_fd != server_write_fd);
3✔
341
        assert(client_read_fd != client_write_fd);
3✔
342
        assert(server_read_fd != client_read_fd);
3✔
343
        assert(server_read_fd != client_write_fd);
3✔
344
        assert(server_write_fd != client_read_fd);
3✔
345
        assert(server_write_fd != client_write_fd);
3✔
346
        assert(on_done);
3✔
347
        assert(ret);
3✔
348

349
        sf = new(SocketForward, 1);
3✔
350
        if (!sf)
3✔
351
                return log_oom_debug();
×
352

353
        *sf = (SocketForward) {
3✔
354
                .on_done = on_done,
355
                .userdata = userdata,
356
        };
357

358
        r = simplex_forward_new(event,
6✔
359
                                TAKE_FD(server_read_fd_close), TAKE_FD(client_write_fd_close),
3✔
360
                                socket_forward_direction_done, sf,
361
                                &sf->server_to_client);
362
        if (r < 0)
3✔
363
                return r;
364

365
        r = simplex_forward_new(event,
6✔
366
                                TAKE_FD(client_read_fd_close), TAKE_FD(server_write_fd_close),
3✔
367
                                socket_forward_direction_done, sf,
368
                                &sf->client_to_server);
3✔
369
        if (r < 0)
3✔
370
                return r;
371

372
        *ret = TAKE_PTR(sf);
3✔
373
        return 0;
3✔
374
}
375

376
int socket_forward_new(
×
377
                sd_event *event,
378
                int server_fd,
379
                int client_fd,
380
                socket_forward_done_t on_done,
381
                void *userdata,
382
                SocketForward **ret) {
383

384
        _cleanup_close_ int server_fd_close = server_fd, client_fd_close = client_fd,
×
385
                            server_write_fd = -EBADF, client_write_fd = -EBADF;
×
386

387
        assert(event);
×
388
        assert(server_fd >= 0);
×
389
        assert(client_fd >= 0);
×
390
        assert(on_done);
×
391
        assert(ret);
×
392

393
        server_write_fd = fcntl(server_fd, F_DUPFD_CLOEXEC, 3);
×
394
        if (server_write_fd < 0)
×
395
                return -errno;
×
396

397
        client_write_fd = fcntl(client_fd, F_DUPFD_CLOEXEC, 3);
×
398
        if (client_write_fd < 0)
×
399
                return -errno;
×
400

401
        return socket_forward_new_pair(
×
402
                        event,
403
                        TAKE_FD(server_fd_close), TAKE_FD(server_write_fd),
×
404
                        TAKE_FD(client_fd_close), TAKE_FD(client_write_fd),
×
405
                        on_done, userdata, ret);
406
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc