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