summaryrefslogtreecommitdiffstats
path: root/src/journal/journald-socket.c
blob: a079624f3c89d573498d6121f8f38641232056dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include <string.h>
#include <sys/socket.h>
#include <sys/uio.h>

#include "fd-util.h"
#include "iovec-util.h"
#include "journald-socket.h"
#include "log.h"
#include "macro.h"
#include "process-util.h"
#include "socket-util.h"
#include "sparse-endian.h"

static int server_open_forward_socket(Server *s) {
        _cleanup_close_ int socket_fd = -EBADF;
        const SocketAddress *addr;
        int family;

        assert(s);

        /* Noop if there is nothing to do. */
        if (s->forward_to_socket.sockaddr.sa.sa_family == AF_UNSPEC || s->namespace)
                return 0;
        /* All ready, nothing to do. */
        if (s->forward_socket_fd >= 0)
                return 1;

        addr = &s->forward_to_socket;

        family = socket_address_family(addr);

        if (!IN_SET(family, AF_UNIX, AF_INET, AF_INET6, AF_VSOCK))
                return log_debug_errno(SYNTHETIC_ERRNO(ESOCKTNOSUPPORT),
                                       "Unsupported socket type for forward socket: %d", family);

        socket_fd = socket(family, SOCK_STREAM|SOCK_CLOEXEC, 0);
        if (socket_fd < 0)
                return log_debug_errno(errno, "Failed to create forward socket, ignoring: %m");

        if (connect(socket_fd, &addr->sockaddr.sa, addr->size) < 0)
                return log_debug_errno(errno, "Failed to connect to remote address for forwarding, ignoring: %m");

        s->forward_socket_fd = TAKE_FD(socket_fd);
        log_debug("Successfully connected to remote address for forwarding.");
        return 1;
}

static inline bool must_serialize(struct iovec iov) {
        /* checks an iovec of the form FIELD=VALUE to see if VALUE needs binary safe serialisation:
         * See https://systemd.io/JOURNAL_EXPORT_FORMATS/#journal-export-format for more information
         * on binary safe serialisation for the journal export format */

        assert(iov.iov_len == 0 || iov.iov_base);

        const uint8_t *s = iov.iov_base;
        bool before_value = true;

        FOREACH_ARRAY(c, s, iov.iov_len)
                if (before_value)
                        before_value = *c != (uint8_t)'=';
                else if (*c < (uint8_t)' ' && *c != (uint8_t)'\t')
                        return true;

        return false;
}

int server_forward_socket(
                Server *s,
                const struct iovec *iovec,
                size_t n_iovec,
                const dual_timestamp *ts,
                int priority) {

        _cleanup_free_ struct iovec *iov_alloc = NULL;
        struct iovec *iov;
        _cleanup_free_ le64_t *len_alloc = NULL;
        le64_t *len;
        int r;

        assert(s);
        assert(iovec);
        assert(n_iovec > 0);
        assert(ts);

        if (LOG_PRI(priority) > s->max_level_socket)
                return 0;

        r = server_open_forward_socket(s);
        if (r <= 0)
                return r;

        /* We need a newline after each iovec + 4 for each we have to serialize in a binary safe way
         * + 2 for the final __REALTIME_TIMESTAMP and __MONOTONIC_TIMESTAMP metadata fields. */
        size_t n = n_iovec * 5 + 2;

        if (n < ALLOCA_MAX / (sizeof(struct iovec) + sizeof(le64_t)) / 2) {
                iov = newa(struct iovec, n);
                len = newa(le64_t, n_iovec);
        } else {
                iov_alloc = new(struct iovec, n);
                if (!iov_alloc)
                        return log_oom();

                iov = iov_alloc;

                len_alloc = new(le64_t, n_iovec);
                if (!len_alloc)
                        return log_oom();

                len = len_alloc;
        }

        struct iovec nl = IOVEC_MAKE_STRING("\n");
        size_t iov_idx = 0, len_idx = 0;
        FOREACH_ARRAY(i, iovec, n_iovec) {
                if (must_serialize(*i)) {
                        const uint8_t *c;
                        c = memchr(i->iov_base, '=', i->iov_len);

                        /* this should never happen */
                        if (_unlikely_(!c || c == i->iov_base))
                                return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                         "Found invalid journal field, refusing to forward.");

                        /* write the field name */
                        iov[iov_idx++] = IOVEC_MAKE(i->iov_base, c - (uint8_t*) i->iov_base);
                        iov[iov_idx++] = nl;

                        /* write the length of the value */
                        len[len_idx] = htole64(i->iov_len - (c - (uint8_t*) i->iov_base) - 1);
                        iov[iov_idx++] = IOVEC_MAKE(&len[len_idx++], sizeof(le64_t));

                        /* write the raw binary value */
                        iov[iov_idx++] = IOVEC_MAKE(c + 1, i->iov_len - (c - (uint8_t*) i->iov_base) - 1);
                } else
                        /* if it doesn't need special treatment just write the value out */
                        iov[iov_idx++] = *i;

                iov[iov_idx++] = nl;
        }

        /* Synthesise __REALTIME_TIMESTAMP and __MONOTONIC_TIMESTAMP as the last arguments so
         * systemd-journal-upload can receive these export messages. */
        char realtime_buf[STRLEN("__REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t) + 1];
        xsprintf(realtime_buf, "__REALTIME_TIMESTAMP="USEC_FMT"\n", ts->realtime);
        iov[iov_idx++] = IOVEC_MAKE_STRING(realtime_buf);

        char monotonic_buf[STRLEN("__MONOTONIC_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t) + 2];
        xsprintf(monotonic_buf, "__MONOTONIC_TIMESTAMP="USEC_FMT"\n\n", ts->monotonic);
        iov[iov_idx++] = IOVEC_MAKE_STRING(monotonic_buf);

        if (writev(s->forward_socket_fd, iov, iov_idx) < 0) {
                log_debug_errno(errno, "Failed to forward log message over socket: %m");

                /* If we failed to send once we will probably fail again so wait for a new connection to
                 * establish before attempting to forward again. */
                s->forward_socket_fd = safe_close(s->forward_socket_fd);
        }

        return 0;
}