]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
fdfccdbc ZJS |
2 | |
3 | #include <errno.h> | |
4 | #include <fcntl.h> | |
fdfccdbc ZJS |
5 | #include <stdio.h> |
6 | #include <stdlib.h> | |
7 | #include <string.h> | |
8 | #include <sys/prctl.h> | |
9 | #include <sys/socket.h> | |
5e38eb93 | 10 | #include <stdint.h> |
3f6fd1ba | 11 | |
fdfccdbc | 12 | #include "sd-daemon.h" |
3f6fd1ba | 13 | |
b5efdb8a | 14 | #include "alloc-util.h" |
a0f29c76 | 15 | #include "def.h" |
4f5dd394 | 16 | #include "escape.h" |
3ffd4af2 | 17 | #include "fd-util.h" |
fdfccdbc | 18 | #include "journal-file.h" |
4f5dd394 | 19 | #include "journal-remote-write.h" |
3ffd4af2 | 20 | #include "journal-remote.h" |
fdfccdbc | 21 | #include "journald-native.h" |
fdfccdbc | 22 | #include "macro.h" |
6bedfcbb | 23 | #include "parse-util.h" |
dccca82b | 24 | #include "process-util.h" |
3f6fd1ba | 25 | #include "socket-util.h" |
15a5e950 | 26 | #include "stdio-util.h" |
07630cea | 27 | #include "string-util.h" |
fdfccdbc | 28 | #include "strv.h" |
fdfccdbc | 29 | |
8201af08 | 30 | #define REMOTE_JOURNAL_PATH "/var/log/journal/remote" |
fdfccdbc | 31 | |
9ff48d09 | 32 | #define filename_escape(s) xescape((s), "/ ") |
8201af08 | 33 | |
c064d8db ZJS |
34 | static int open_output(RemoteServer *s, Writer *w, const char* host) { |
35 | _cleanup_free_ char *_filename = NULL; | |
36 | const char *filename; | |
fdfccdbc ZJS |
37 | int r; |
38 | ||
c064d8db | 39 | switch (s->split_mode) { |
8201af08 | 40 | case JOURNAL_WRITE_SPLIT_NONE: |
c064d8db | 41 | filename = s->output; |
8201af08 | 42 | break; |
fdfccdbc | 43 | |
8201af08 ZJS |
44 | case JOURNAL_WRITE_SPLIT_HOST: { |
45 | _cleanup_free_ char *name; | |
fdfccdbc | 46 | |
8201af08 | 47 | assert(host); |
fdfccdbc | 48 | |
8201af08 ZJS |
49 | name = filename_escape(host); |
50 | if (!name) | |
51 | return log_oom(); | |
52 | ||
c064d8db | 53 | r = asprintf(&_filename, "%s/remote-%s.journal", s->output, name); |
fdfccdbc ZJS |
54 | if (r < 0) |
55 | return log_oom(); | |
8201af08 | 56 | |
c064d8db | 57 | filename = _filename; |
8201af08 ZJS |
58 | break; |
59 | } | |
60 | ||
61 | default: | |
62 | assert_not_reached("what?"); | |
fdfccdbc ZJS |
63 | } |
64 | ||
c064d8db | 65 | r = journal_file_open_reliably(filename, |
fdfccdbc | 66 | O_RDWR|O_CREAT, 0640, |
c064d8db | 67 | s->compress, (uint64_t) -1, s->seal, |
8201af08 | 68 | &w->metrics, |
b58c888f | 69 | w->mmap, NULL, |
8201af08 | 70 | NULL, &w->journal); |
fdfccdbc | 71 | if (r < 0) |
c064d8db ZJS |
72 | return log_error_errno(r, "Failed to open output journal %s: %m", filename); |
73 | ||
74 | log_debug("Opened output file %s", w->journal->path); | |
75 | return 0; | |
fdfccdbc ZJS |
76 | } |
77 | ||
cc64d017 ZJS |
78 | /********************************************************************** |
79 | ********************************************************************** | |
80 | **********************************************************************/ | |
81 | ||
9ff48d09 | 82 | static int init_writer_hashmap(RemoteServer *s) { |
c064d8db | 83 | static const struct hash_ops* const hash_ops[] = { |
d5099efc MS |
84 | [JOURNAL_WRITE_SPLIT_NONE] = NULL, |
85 | [JOURNAL_WRITE_SPLIT_HOST] = &string_hash_ops, | |
9ff48d09 ZJS |
86 | }; |
87 | ||
c064d8db ZJS |
88 | assert(s); |
89 | assert(s->split_mode >= 0 && s->split_mode < (int) ELEMENTSOF(hash_ops)); | |
cc64d017 | 90 | |
c064d8db | 91 | s->writers = hashmap_new(hash_ops[s->split_mode]); |
9ff48d09 ZJS |
92 | if (!s->writers) |
93 | return log_oom(); | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
c064d8db | 98 | int journal_remote_get_writer(RemoteServer *s, const char *host, Writer **writer) { |
8e766630 | 99 | _cleanup_(writer_unrefp) Writer *w = NULL; |
9ff48d09 | 100 | const void *key; |
9ff48d09 ZJS |
101 | int r; |
102 | ||
c064d8db | 103 | switch(s->split_mode) { |
9ff48d09 ZJS |
104 | case JOURNAL_WRITE_SPLIT_NONE: |
105 | key = "one and only"; | |
106 | break; | |
107 | ||
108 | case JOURNAL_WRITE_SPLIT_HOST: | |
109 | assert(host); | |
110 | key = host; | |
111 | break; | |
112 | ||
113 | default: | |
114 | assert_not_reached("what split mode?"); | |
115 | } | |
116 | ||
117 | w = hashmap_get(s->writers, key); | |
118 | if (w) | |
119 | writer_ref(w); | |
120 | else { | |
121 | w = writer_new(s); | |
122 | if (!w) | |
123 | return log_oom(); | |
124 | ||
c064d8db | 125 | if (s->split_mode == JOURNAL_WRITE_SPLIT_HOST) { |
9ff48d09 ZJS |
126 | w->hashmap_key = strdup(key); |
127 | if (!w->hashmap_key) | |
128 | return log_oom(); | |
129 | } | |
cc64d017 | 130 | |
c064d8db | 131 | r = open_output(s, w, host); |
9ff48d09 ZJS |
132 | if (r < 0) |
133 | return r; | |
fdfccdbc | 134 | |
9ff48d09 ZJS |
135 | r = hashmap_put(s->writers, w->hashmap_key ?: key, w); |
136 | if (r < 0) | |
137 | return r; | |
138 | } | |
fdfccdbc | 139 | |
1cc6c93a YW |
140 | *writer = TAKE_PTR(w); |
141 | ||
9ff48d09 ZJS |
142 | return 0; |
143 | } | |
cc64d017 | 144 | |
9ff48d09 ZJS |
145 | /********************************************************************** |
146 | ********************************************************************** | |
147 | **********************************************************************/ | |
fdfccdbc | 148 | |
cc64d017 | 149 | /* This should go away as soon as µhttpd allows state to be passed around. */ |
c064d8db | 150 | RemoteServer *journal_remote_server_global; |
cc64d017 | 151 | |
fdfccdbc ZJS |
152 | static int dispatch_raw_source_event(sd_event_source *event, |
153 | int fd, | |
154 | uint32_t revents, | |
155 | void *userdata); | |
043945b9 ZJS |
156 | static int dispatch_raw_source_until_block(sd_event_source *event, |
157 | void *userdata); | |
70f1b2dd ZJS |
158 | static int dispatch_blocking_source_event(sd_event_source *event, |
159 | void *userdata); | |
fdfccdbc ZJS |
160 | static int dispatch_raw_connection_event(sd_event_source *event, |
161 | int fd, | |
162 | uint32_t revents, | |
163 | void *userdata); | |
164 | ||
9ff48d09 ZJS |
165 | static int get_source_for_fd(RemoteServer *s, |
166 | int fd, char *name, RemoteSource **source) { | |
167 | Writer *writer; | |
168 | int r; | |
169 | ||
1f8af042 ZJS |
170 | /* This takes ownership of name, but only on success. */ |
171 | ||
fdfccdbc ZJS |
172 | assert(fd >= 0); |
173 | assert(source); | |
174 | ||
ca2d3784 | 175 | if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1)) |
fdfccdbc ZJS |
176 | return log_oom(); |
177 | ||
c064d8db | 178 | r = journal_remote_get_writer(s, name, &writer); |
eb56eb9b MS |
179 | if (r < 0) |
180 | return log_warning_errno(r, "Failed to get writer for source %s: %m", | |
181 | name); | |
9ff48d09 | 182 | |
fdfccdbc | 183 | if (s->sources[fd] == NULL) { |
9ff48d09 ZJS |
184 | s->sources[fd] = source_new(fd, false, name, writer); |
185 | if (!s->sources[fd]) { | |
186 | writer_unref(writer); | |
fdfccdbc | 187 | return log_oom(); |
9ff48d09 ZJS |
188 | } |
189 | ||
fdfccdbc ZJS |
190 | s->active++; |
191 | } | |
192 | ||
193 | *source = s->sources[fd]; | |
194 | return 0; | |
195 | } | |
196 | ||
197 | static int remove_source(RemoteServer *s, int fd) { | |
198 | RemoteSource *source; | |
199 | ||
200 | assert(s); | |
ca2d3784 | 201 | assert(fd >= 0 && fd < (ssize_t) s->sources_size); |
fdfccdbc ZJS |
202 | |
203 | source = s->sources[fd]; | |
204 | if (source) { | |
8201af08 | 205 | /* this closes fd too */ |
fdfccdbc ZJS |
206 | source_free(source); |
207 | s->sources[fd] = NULL; | |
208 | s->active--; | |
209 | } | |
210 | ||
fdfccdbc ZJS |
211 | return 0; |
212 | } | |
213 | ||
c064d8db | 214 | int journal_remote_add_source(RemoteServer *s, int fd, char* name, bool own_name) { |
a7f7d1bd | 215 | RemoteSource *source = NULL; |
fdfccdbc ZJS |
216 | int r; |
217 | ||
1f8af042 ZJS |
218 | /* This takes ownership of name, even on failure, if own_name is true. */ |
219 | ||
fdfccdbc ZJS |
220 | assert(s); |
221 | assert(fd >= 0); | |
9ff48d09 | 222 | assert(name); |
fdfccdbc | 223 | |
9ff48d09 ZJS |
224 | if (!own_name) { |
225 | name = strdup(name); | |
226 | if (!name) | |
227 | return log_oom(); | |
228 | } | |
fdfccdbc | 229 | |
9ff48d09 | 230 | r = get_source_for_fd(s, fd, name, &source); |
fdfccdbc | 231 | if (r < 0) { |
c33b3297 MS |
232 | log_error_errno(r, "Failed to create source for fd:%d (%s): %m", |
233 | fd, name); | |
1f8af042 | 234 | free(name); |
fdfccdbc ZJS |
235 | return r; |
236 | } | |
8201af08 | 237 | |
fdfccdbc | 238 | r = sd_event_add_io(s->events, &source->event, |
8201af08 | 239 | fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI, |
043945b9 ZJS |
240 | dispatch_raw_source_event, source); |
241 | if (r == 0) { | |
242 | /* Add additional source for buffer processing. It will be | |
243 | * enabled later. */ | |
244 | r = sd_event_add_defer(s->events, &source->buffer_event, | |
245 | dispatch_raw_source_until_block, source); | |
246 | if (r == 0) | |
247 | sd_event_source_set_enabled(source->buffer_event, SD_EVENT_OFF); | |
248 | } else if (r == -EPERM) { | |
70f1b2dd ZJS |
249 | log_debug("Falling back to sd_event_add_defer for fd:%d (%s)", fd, name); |
250 | r = sd_event_add_defer(s->events, &source->event, | |
251 | dispatch_blocking_source_event, source); | |
252 | if (r == 0) | |
253 | sd_event_source_set_enabled(source->event, SD_EVENT_ON); | |
254 | } | |
fdfccdbc | 255 | if (r < 0) { |
c33b3297 MS |
256 | log_error_errno(r, "Failed to register event source for fd:%d: %m", |
257 | fd); | |
fdfccdbc ZJS |
258 | goto error; |
259 | } | |
260 | ||
356779df | 261 | r = sd_event_source_set_description(source->event, name); |
43300d9d | 262 | if (r < 0) { |
da927ba9 | 263 | log_error_errno(r, "Failed to set source name for fd:%d: %m", fd); |
43300d9d ZJS |
264 | goto error; |
265 | } | |
266 | ||
fdfccdbc ZJS |
267 | return 1; /* work to do */ |
268 | ||
269 | error: | |
270 | remove_source(s, fd); | |
271 | return r; | |
272 | } | |
273 | ||
c064d8db | 274 | int journal_remote_add_raw_socket(RemoteServer *s, int fd) { |
8a8d55f2 | 275 | int r; |
43300d9d | 276 | _cleanup_close_ int fd_ = fd; |
fbd0b64f | 277 | char name[STRLEN("raw-socket-") + DECIMAL_STR_MAX(int) + 1]; |
43300d9d ZJS |
278 | |
279 | assert(fd >= 0); | |
fdfccdbc | 280 | |
8201af08 ZJS |
281 | r = sd_event_add_io(s->events, &s->listen_event, |
282 | fd, EPOLLIN, | |
fdfccdbc | 283 | dispatch_raw_connection_event, s); |
43300d9d | 284 | if (r < 0) |
fdfccdbc | 285 | return r; |
fdfccdbc | 286 | |
5ffa8c81 | 287 | xsprintf(name, "raw-socket-%d", fd); |
43300d9d | 288 | |
356779df | 289 | r = sd_event_source_set_description(s->listen_event, name); |
43300d9d ZJS |
290 | if (r < 0) |
291 | return r; | |
292 | ||
293 | fd_ = -1; | |
313cefa1 | 294 | s->active++; |
fdfccdbc ZJS |
295 | return 0; |
296 | } | |
297 | ||
8201af08 ZJS |
298 | /********************************************************************** |
299 | ********************************************************************** | |
300 | **********************************************************************/ | |
301 | ||
c064d8db ZJS |
302 | int journal_remote_server_init( |
303 | RemoteServer *s, | |
304 | const char *output, | |
305 | JournalWriteSplitMode split_mode, | |
306 | bool compress, | |
307 | bool seal) { | |
cc64d017 | 308 | |
fdfccdbc ZJS |
309 | int r; |
310 | ||
311 | assert(s); | |
312 | ||
c064d8db ZJS |
313 | assert(journal_remote_server_global == NULL); |
314 | journal_remote_server_global = s; | |
fdfccdbc | 315 | |
c064d8db ZJS |
316 | s->split_mode = split_mode; |
317 | s->compress = compress; | |
318 | s->seal = seal; | |
43300d9d | 319 | |
c064d8db ZJS |
320 | if (output) |
321 | s->output = output; | |
322 | else if (split_mode == JOURNAL_WRITE_SPLIT_NONE) | |
323 | s->output = REMOTE_JOURNAL_PATH "/remote.journal"; | |
324 | else if (split_mode == JOURNAL_WRITE_SPLIT_HOST) | |
325 | s->output = REMOTE_JOURNAL_PATH; | |
42b6bf75 | 326 | else |
c064d8db | 327 | assert_not_reached("bad split mode"); |
ad95fd1d | 328 | |
b1604b34 | 329 | r = sd_event_default(&s->events); |
23bbb0de MS |
330 | if (r < 0) |
331 | return log_error_errno(r, "Failed to allocate event loop: %m"); | |
fdfccdbc | 332 | |
22259a00 JL |
333 | r = init_writer_hashmap(s); |
334 | if (r < 0) | |
335 | return r; | |
336 | ||
8201af08 | 337 | return 0; |
fdfccdbc ZJS |
338 | } |
339 | ||
63e2ebcd | 340 | #if HAVE_MICROHTTPD |
1599f593 ZJS |
341 | static void MHDDaemonWrapper_free(MHDDaemonWrapper *d) { |
342 | MHD_stop_daemon(d->daemon); | |
343 | sd_event_source_unref(d->io_event); | |
344 | sd_event_source_unref(d->timer_event); | |
345 | free(d); | |
346 | } | |
63e2ebcd | 347 | #endif |
1599f593 | 348 | |
94952201 | 349 | void journal_remote_server_destroy(RemoteServer *s) { |
ca2d3784 | 350 | size_t i; |
cc64d017 | 351 | |
63e2ebcd | 352 | #if HAVE_MICROHTTPD |
1599f593 | 353 | hashmap_free_with_destructor(s->daemons, MHDDaemonWrapper_free); |
63e2ebcd | 354 | #endif |
cc64d017 | 355 | |
fdfccdbc | 356 | assert(s->sources_size == 0 || s->sources); |
cc64d017 | 357 | for (i = 0; i < s->sources_size; i++) |
fdfccdbc | 358 | remove_source(s, i); |
fdfccdbc ZJS |
359 | free(s->sources); |
360 | ||
9ff48d09 ZJS |
361 | writer_unref(s->_single_writer); |
362 | hashmap_free(s->writers); | |
363 | ||
fdfccdbc ZJS |
364 | sd_event_source_unref(s->sigterm_event); |
365 | sd_event_source_unref(s->sigint_event); | |
366 | sd_event_source_unref(s->listen_event); | |
367 | sd_event_unref(s->events); | |
368 | ||
c064d8db ZJS |
369 | if (s == journal_remote_server_global) |
370 | journal_remote_server_global = NULL; | |
371 | ||
fdfccdbc | 372 | /* fds that we're listening on remain open... */ |
fdfccdbc ZJS |
373 | } |
374 | ||
375 | /********************************************************************** | |
376 | ********************************************************************** | |
377 | **********************************************************************/ | |
378 | ||
864876ec ZJS |
379 | int journal_remote_handle_raw_source( |
380 | sd_event_source *event, | |
381 | int fd, | |
382 | uint32_t revents, | |
383 | RemoteServer *s) { | |
fdfccdbc | 384 | |
fdfccdbc ZJS |
385 | RemoteSource *source; |
386 | int r; | |
387 | ||
043945b9 ZJS |
388 | /* Returns 1 if there might be more data pending, |
389 | * 0 if data is currently exhausted, negative on error. | |
390 | */ | |
391 | ||
ca2d3784 | 392 | assert(fd >= 0 && fd < (ssize_t) s->sources_size); |
fdfccdbc | 393 | source = s->sources[fd]; |
b18453ed | 394 | assert(source->importer.fd == fd); |
fdfccdbc | 395 | |
c064d8db | 396 | r = process_source(source, s->compress, s->seal); |
b18453ed | 397 | if (journal_importer_eof(&source->importer)) { |
4a0a6ac0 ZJS |
398 | size_t remaining; |
399 | ||
b18453ed ZJS |
400 | log_debug("EOF reached with source %s (fd=%d)", |
401 | source->importer.name, source->importer.fd); | |
4a0a6ac0 | 402 | |
b18453ed | 403 | remaining = journal_importer_bytes_remaining(&source->importer); |
4a0a6ac0 | 404 | if (remaining > 0) |
0e72da6f | 405 | log_notice("Premature EOF. %zu bytes lost.", remaining); |
b18453ed | 406 | remove_source(s, source->importer.fd); |
0e72da6f | 407 | log_debug("%zu active sources remaining", s->active); |
8201af08 | 408 | return 0; |
fdfccdbc | 409 | } else if (r == -E2BIG) { |
ef4d6abe ZJS |
410 | log_notice("Entry with too many fields, skipped"); |
411 | return 1; | |
412 | } else if (r == -ENOBUFS) { | |
d4e98880 | 413 | log_notice("Entry too big, skipped"); |
8201af08 | 414 | return 1; |
ff55c3c7 | 415 | } else if (r == -EAGAIN) { |
8201af08 ZJS |
416 | return 0; |
417 | } else if (r < 0) { | |
0e72da6f | 418 | log_debug_errno(r, "Closing connection: %m"); |
c064d8db | 419 | remove_source(s, fd); |
8201af08 ZJS |
420 | return 0; |
421 | } else | |
422 | return 1; | |
fdfccdbc ZJS |
423 | } |
424 | ||
043945b9 ZJS |
425 | static int dispatch_raw_source_until_block(sd_event_source *event, |
426 | void *userdata) { | |
427 | RemoteSource *source = userdata; | |
428 | int r; | |
429 | ||
430 | /* Make sure event stays around even if source is destroyed */ | |
431 | sd_event_source_ref(event); | |
432 | ||
864876ec | 433 | r = journal_remote_handle_raw_source(event, source->importer.fd, EPOLLIN, journal_remote_server_global); |
043945b9 ZJS |
434 | if (r != 1) |
435 | /* No more data for now */ | |
436 | sd_event_source_set_enabled(event, SD_EVENT_OFF); | |
437 | ||
438 | sd_event_source_unref(event); | |
439 | ||
440 | return r; | |
441 | } | |
442 | ||
443 | static int dispatch_raw_source_event(sd_event_source *event, | |
444 | int fd, | |
445 | uint32_t revents, | |
446 | void *userdata) { | |
447 | RemoteSource *source = userdata; | |
448 | int r; | |
449 | ||
450 | assert(source->event); | |
451 | assert(source->buffer_event); | |
452 | ||
864876ec | 453 | r = journal_remote_handle_raw_source(event, fd, EPOLLIN, journal_remote_server_global); |
043945b9 ZJS |
454 | if (r == 1) |
455 | /* Might have more data. We need to rerun the handler | |
456 | * until we are sure the buffer is exhausted. */ | |
457 | sd_event_source_set_enabled(source->buffer_event, SD_EVENT_ON); | |
458 | ||
459 | return r; | |
460 | } | |
461 | ||
70f1b2dd ZJS |
462 | static int dispatch_blocking_source_event(sd_event_source *event, |
463 | void *userdata) { | |
464 | RemoteSource *source = userdata; | |
465 | ||
864876ec | 466 | return journal_remote_handle_raw_source(event, source->importer.fd, EPOLLIN, journal_remote_server_global); |
70f1b2dd ZJS |
467 | } |
468 | ||
8201af08 ZJS |
469 | static int accept_connection(const char* type, int fd, |
470 | SocketAddress *addr, char **hostname) { | |
fdfccdbc ZJS |
471 | int fd2, r; |
472 | ||
cc64d017 ZJS |
473 | log_debug("Accepting new %s connection on fd:%d", type, fd); |
474 | fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC); | |
4a62c710 MS |
475 | if (fd2 < 0) |
476 | return log_error_errno(errno, "accept() on fd:%d failed: %m", fd); | |
fdfccdbc | 477 | |
cc64d017 | 478 | switch(socket_address_family(addr)) { |
fdfccdbc ZJS |
479 | case AF_INET: |
480 | case AF_INET6: { | |
8201af08 ZJS |
481 | _cleanup_free_ char *a = NULL; |
482 | char *b; | |
fdfccdbc | 483 | |
cc64d017 | 484 | r = socket_address_print(addr, &a); |
fdfccdbc | 485 | if (r < 0) { |
da927ba9 | 486 | log_error_errno(r, "socket_address_print(): %m"); |
fdfccdbc ZJS |
487 | close(fd2); |
488 | return r; | |
489 | } | |
490 | ||
8201af08 ZJS |
491 | r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b); |
492 | if (r < 0) { | |
1938ac51 | 493 | log_error_errno(r, "Resolving hostname failed: %m"); |
8201af08 ZJS |
494 | close(fd2); |
495 | return r; | |
496 | } | |
497 | ||
0e72da6f ZJS |
498 | log_debug("Accepted %s %s connection from %s", |
499 | type, | |
500 | socket_address_family(addr) == AF_INET ? "IP" : "IPv6", | |
501 | a); | |
cc64d017 | 502 | |
8201af08 ZJS |
503 | *hostname = b; |
504 | ||
cc64d017 | 505 | return fd2; |
fdfccdbc ZJS |
506 | }; |
507 | default: | |
cc64d017 ZJS |
508 | log_error("Rejected %s connection with unsupported family %d", |
509 | type, socket_address_family(addr)); | |
fdfccdbc | 510 | close(fd2); |
cc64d017 | 511 | |
fdfccdbc ZJS |
512 | return -EINVAL; |
513 | } | |
cc64d017 | 514 | } |
fdfccdbc | 515 | |
cc64d017 ZJS |
516 | static int dispatch_raw_connection_event(sd_event_source *event, |
517 | int fd, | |
518 | uint32_t revents, | |
519 | void *userdata) { | |
520 | RemoteServer *s = userdata; | |
1f8af042 | 521 | int fd2; |
cc64d017 ZJS |
522 | SocketAddress addr = { |
523 | .size = sizeof(union sockaddr_union), | |
524 | .type = SOCK_STREAM, | |
525 | }; | |
a7f7d1bd | 526 | char *hostname = NULL; |
fdfccdbc | 527 | |
8201af08 | 528 | fd2 = accept_connection("raw", fd, &addr, &hostname); |
cc64d017 ZJS |
529 | if (fd2 < 0) |
530 | return fd2; | |
fdfccdbc | 531 | |
c064d8db | 532 | return journal_remote_add_source(s, fd2, hostname, true); |
fdfccdbc | 533 | } |