Coverage Report

Created: 2025-06-13 06:25

/src/systemd/src/journal/journald-socket.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include <string.h>
4
#include <sys/socket.h>
5
#include <sys/uio.h>
6
7
#include "alloc-util.h"
8
#include "fd-util.h"
9
#include "iovec-util.h"
10
#include "journald-manager.h"
11
#include "journald-socket.h"
12
#include "log.h"
13
#include "stdio-util.h"
14
#include "socket-util.h"
15
#include "sparse-endian.h"
16
#include "time-util.h"
17
18
0
static int manager_open_forward_socket(Manager *m) {
19
0
        _cleanup_close_ int socket_fd = -EBADF;
20
0
        const SocketAddress *addr;
21
0
        int family;
22
23
0
        assert(m);
24
25
        /* Noop if there is nothing to do. */
26
0
        if (m->forward_to_socket.sockaddr.sa.sa_family == AF_UNSPEC || m->namespace)
27
0
                return 0;
28
        /* All ready, nothing to do. */
29
0
        if (m->forward_socket_fd >= 0)
30
0
                return 1;
31
32
0
        addr = &m->forward_to_socket;
33
34
0
        family = socket_address_family(addr);
35
36
0
        if (!IN_SET(family, AF_UNIX, AF_INET, AF_INET6, AF_VSOCK))
37
0
                return log_debug_errno(SYNTHETIC_ERRNO(ESOCKTNOSUPPORT),
38
0
                                       "Unsupported socket type for forward socket: %d", family);
39
40
0
        socket_fd = socket(family, SOCK_STREAM|SOCK_CLOEXEC, 0);
41
0
        if (socket_fd < 0)
42
0
                return log_debug_errno(errno, "Failed to create forward socket, ignoring: %m");
43
44
0
        if (connect(socket_fd, &addr->sockaddr.sa, addr->size) < 0)
45
0
                return log_debug_errno(errno, "Failed to connect to remote address for forwarding, ignoring: %m");
46
47
0
        m->forward_socket_fd = TAKE_FD(socket_fd);
48
0
        log_debug("Successfully connected to remote address for forwarding.");
49
0
        return 1;
50
0
}
51
52
0
static inline bool must_serialize(struct iovec iov) {
53
        /* checks an iovec of the form FIELD=VALUE to see if VALUE needs binary safe serialisation:
54
         * See https://systemd.io/JOURNAL_EXPORT_FORMATS/#journal-export-format for more information
55
         * on binary safe serialisation for the journal export format */
56
57
0
        assert(iov.iov_len == 0 || iov.iov_base);
58
59
0
        const uint8_t *s = iov.iov_base;
60
0
        bool before_value = true;
61
62
0
        FOREACH_ARRAY(c, s, iov.iov_len)
63
0
                if (before_value)
64
0
                        before_value = *c != (uint8_t)'=';
65
0
                else if (*c < (uint8_t)' ' && *c != (uint8_t)'\t')
66
0
                        return true;
67
68
0
        return false;
69
0
}
70
71
int manager_forward_socket(
72
                Manager *m,
73
                const struct iovec *iovec,
74
                size_t n_iovec,
75
                const dual_timestamp *ts,
76
0
                int priority) {
77
78
0
        _cleanup_free_ struct iovec *iov_alloc = NULL;
79
0
        struct iovec *iov;
80
0
        _cleanup_free_ le64_t *len_alloc = NULL;
81
0
        le64_t *len;
82
0
        int r;
83
84
0
        assert(m);
85
0
        assert(iovec);
86
0
        assert(n_iovec > 0);
87
0
        assert(ts);
88
89
0
        if (LOG_PRI(priority) > m->max_level_socket)
90
0
                return 0;
91
92
0
        r = manager_open_forward_socket(m);
93
0
        if (r <= 0)
94
0
                return r;
95
96
        /* We need a newline after each iovec + 4 for each we have to serialize in a binary safe way
97
         * + 2 for the final __REALTIME_TIMESTAMP and __MONOTONIC_TIMESTAMP metadata fields. */
98
0
        size_t n = n_iovec * 5 + 2;
99
100
0
        if (n < ALLOCA_MAX / (sizeof(struct iovec) + sizeof(le64_t)) / 2) {
101
0
                iov = newa(struct iovec, n);
102
0
                len = newa(le64_t, n_iovec);
103
0
        } else {
104
0
                iov_alloc = new(struct iovec, n);
105
0
                if (!iov_alloc)
106
0
                        return log_oom();
107
108
0
                iov = iov_alloc;
109
110
0
                len_alloc = new(le64_t, n_iovec);
111
0
                if (!len_alloc)
112
0
                        return log_oom();
113
114
0
                len = len_alloc;
115
0
        }
116
117
0
        struct iovec nl = IOVEC_MAKE_STRING("\n");
118
0
        size_t iov_idx = 0, len_idx = 0;
119
0
        FOREACH_ARRAY(i, iovec, n_iovec) {
120
0
                if (must_serialize(*i)) {
121
0
                        const uint8_t *c;
122
0
                        c = memchr(i->iov_base, '=', i->iov_len);
123
124
                        /* this should never happen */
125
0
                        if (_unlikely_(!c || c == i->iov_base))
126
0
                                return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG),
127
0
                                                         "Found invalid journal field, refusing to forward.");
128
129
                        /* write the field name */
130
0
                        iov[iov_idx++] = IOVEC_MAKE(i->iov_base, c - (uint8_t*) i->iov_base);
131
0
                        iov[iov_idx++] = nl;
132
133
                        /* write the length of the value */
134
0
                        len[len_idx] = htole64(i->iov_len - (c - (uint8_t*) i->iov_base) - 1);
135
0
                        iov[iov_idx++] = IOVEC_MAKE(&len[len_idx++], sizeof(le64_t));
136
137
                        /* write the raw binary value */
138
0
                        iov[iov_idx++] = IOVEC_MAKE(c + 1, i->iov_len - (c - (uint8_t*) i->iov_base) - 1);
139
0
                } else
140
                        /* if it doesn't need special treatment just write the value out */
141
0
                        iov[iov_idx++] = *i;
142
143
0
                iov[iov_idx++] = nl;
144
0
        }
145
146
        /* Synthesise __REALTIME_TIMESTAMP and __MONOTONIC_TIMESTAMP as the last arguments so
147
         * systemd-journal-upload can receive these export messages. */
148
0
        char realtime_buf[STRLEN("__REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t) + 1];
149
0
        xsprintf(realtime_buf, "__REALTIME_TIMESTAMP="USEC_FMT"\n", ts->realtime);
150
0
        iov[iov_idx++] = IOVEC_MAKE_STRING(realtime_buf);
151
152
0
        char monotonic_buf[STRLEN("__MONOTONIC_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t) + 2];
153
0
        xsprintf(monotonic_buf, "__MONOTONIC_TIMESTAMP="USEC_FMT"\n\n", ts->monotonic);
154
0
        iov[iov_idx++] = IOVEC_MAKE_STRING(monotonic_buf);
155
156
0
        if (writev(m->forward_socket_fd, iov, iov_idx) < 0) {
157
0
                log_debug_errno(errno, "Failed to forward log message over socket: %m");
158
159
                /* If we failed to send once we will probably fail again so wait for a new connection to
160
                 * establish before attempting to forward again. */
161
0
                m->forward_socket_fd = safe_close(m->forward_socket_fd);
162
0
        }
163
164
0
        return 0;
165
0
}