chiark / gitweb /
build: colorize gcc only if on tty
[elogind.git] / src / journal-remote / journal-remote.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2012 Zbigniew JÄ™drzejewski-Szmek
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/prctl.h>
28 #include <sys/socket.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <getopt.h>
33
34 #include "sd-daemon.h"
35 #include "journal-file.h"
36 #include "journald-native.h"
37 #include "socket-util.h"
38 #include "mkdir.h"
39 #include "build.h"
40 #include "macro.h"
41 #include "strv.h"
42 #include "fileio.h"
43 #include "conf-parser.h"
44 #include "siphash24.h"
45
46 #ifdef HAVE_GNUTLS
47 #include <gnutls/gnutls.h>
48 #endif
49
50 #include "journal-remote.h"
51 #include "journal-remote-write.h"
52
53 #define REMOTE_JOURNAL_PATH "/var/log/journal/remote"
54
55 #define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-remote.pem"
56 #define CERT_FILE     CERTIFICATE_ROOT "/certs/journal-remote.pem"
57 #define TRUST_FILE    CERTIFICATE_ROOT "/ca/trusted.pem"
58
59 static char* arg_url = NULL;
60 static char* arg_getter = NULL;
61 static char* arg_listen_raw = NULL;
62 static char* arg_listen_http = NULL;
63 static char* arg_listen_https = NULL;
64 static char** arg_files = NULL;
65 static int arg_compress = true;
66 static int arg_seal = false;
67 static int http_socket = -1, https_socket = -1;
68 static char** arg_gnutls_log = NULL;
69
70 static JournalWriteSplitMode arg_split_mode = JOURNAL_WRITE_SPLIT_HOST;
71 static char* arg_output = NULL;
72
73 static char *arg_key = NULL;
74 static char *arg_cert = NULL;
75 static char *arg_trust = NULL;
76 static bool arg_trust_all = false;
77
78 /**********************************************************************
79  **********************************************************************
80  **********************************************************************/
81
82 static int spawn_child(const char* child, char** argv) {
83         int fd[2];
84         pid_t parent_pid, child_pid;
85         int r;
86
87         if (pipe(fd) < 0) {
88                 log_error("Failed to create pager pipe: %m");
89                 return -errno;
90         }
91
92         parent_pid = getpid();
93
94         child_pid = fork();
95         if (child_pid < 0) {
96                 r = -errno;
97                 log_error("Failed to fork: %m");
98                 safe_close_pair(fd);
99                 return r;
100         }
101
102         /* In the child */
103         if (child_pid == 0) {
104                 r = dup2(fd[1], STDOUT_FILENO);
105                 if (r < 0) {
106                         log_error("Failed to dup pipe to stdout: %m");
107                         _exit(EXIT_FAILURE);
108                 }
109
110                 safe_close_pair(fd);
111
112                 /* Make sure the child goes away when the parent dies */
113                 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
114                         _exit(EXIT_FAILURE);
115
116                 /* Check whether our parent died before we were able
117                  * to set the death signal */
118                 if (getppid() != parent_pid)
119                         _exit(EXIT_SUCCESS);
120
121                 execvp(child, argv);
122                 log_error("Failed to exec child %s: %m", child);
123                 _exit(EXIT_FAILURE);
124         }
125
126         r = close(fd[1]);
127         if (r < 0)
128                 log_warning("Failed to close write end of pipe: %m");
129
130         return fd[0];
131 }
132
133 static int spawn_curl(const char* url) {
134         char **argv = STRV_MAKE("curl",
135                                 "-HAccept: application/vnd.fdo.journal",
136                                 "--silent",
137                                 "--show-error",
138                                 url);
139         int r;
140
141         r = spawn_child("curl", argv);
142         if (r < 0)
143                 log_error("Failed to spawn curl: %m");
144         return r;
145 }
146
147 static int spawn_getter(const char *getter, const char *url) {
148         int r;
149         _cleanup_strv_free_ char **words = NULL;
150
151         assert(getter);
152         r = strv_split_quoted(&words, getter);
153         if (r < 0) {
154                 log_error("Failed to split getter option: %s", strerror(-r));
155                 return r;
156         }
157
158         r = strv_extend(&words, url);
159         if (r < 0) {
160                 log_error("Failed to create command line: %s", strerror(-r));
161                 return r;
162         }
163
164         r = spawn_child(words[0], words);
165         if (r < 0)
166                 log_error("Failed to spawn getter %s: %m", getter);
167
168         return r;
169 }
170
171 #define filename_escape(s) xescape((s), "/ ")
172
173 static int open_output(Writer *w, const char* host) {
174         _cleanup_free_ char *_output = NULL;
175         const char *output;
176         int r;
177
178         switch (arg_split_mode) {
179         case JOURNAL_WRITE_SPLIT_NONE:
180                 output = arg_output ?: REMOTE_JOURNAL_PATH "/remote.journal";
181                 break;
182
183         case JOURNAL_WRITE_SPLIT_HOST: {
184                 _cleanup_free_ char *name;
185
186                 assert(host);
187
188                 name = filename_escape(host);
189                 if (!name)
190                         return log_oom();
191
192                 r = asprintf(&_output, "%s/remote-%s.journal",
193                              arg_output ?: REMOTE_JOURNAL_PATH,
194                              name);
195                 if (r < 0)
196                         return log_oom();
197
198                 output = _output;
199                 break;
200         }
201
202         default:
203                 assert_not_reached("what?");
204         }
205
206         r = journal_file_open_reliably(output,
207                                        O_RDWR|O_CREAT, 0640,
208                                        arg_compress, arg_seal,
209                                        &w->metrics,
210                                        w->mmap,
211                                        NULL, &w->journal);
212         if (r < 0)
213                 log_error("Failed to open output journal %s: %s",
214                           output, strerror(-r));
215         else
216                 log_info("Opened output file %s", w->journal->path);
217         return r;
218 }
219
220 /**********************************************************************
221  **********************************************************************
222  **********************************************************************/
223
224 static int init_writer_hashmap(RemoteServer *s) {
225         static const struct {
226                 hash_func_t hash_func;
227                 compare_func_t compare_func;
228         } functions[] = {
229                 [JOURNAL_WRITE_SPLIT_NONE] = {trivial_hash_func,
230                                               trivial_compare_func},
231                 [JOURNAL_WRITE_SPLIT_HOST] = {string_hash_func,
232                                               string_compare_func},
233         };
234
235         assert(arg_split_mode >= 0 && arg_split_mode < (int) ELEMENTSOF(functions));
236
237         s->writers = hashmap_new(functions[arg_split_mode].hash_func,
238                                  functions[arg_split_mode].compare_func);
239         if (!s->writers)
240                 return log_oom();
241
242         return 0;
243 }
244
245 static int get_writer(RemoteServer *s, const char *host,
246                       Writer **writer) {
247         const void *key;
248         _cleanup_writer_unref_ Writer *w = NULL;
249         int r;
250
251         switch(arg_split_mode) {
252         case JOURNAL_WRITE_SPLIT_NONE:
253                 key = "one and only";
254                 break;
255
256         case JOURNAL_WRITE_SPLIT_HOST:
257                 assert(host);
258                 key = host;
259                 break;
260
261         default:
262                 assert_not_reached("what split mode?");
263         }
264
265         w = hashmap_get(s->writers, key);
266         if (w)
267                 writer_ref(w);
268         else {
269                 w = writer_new(s);
270                 if (!w)
271                         return log_oom();
272
273                 if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST) {
274                         w->hashmap_key = strdup(key);
275                         if (!w->hashmap_key)
276                                 return log_oom();
277                 }
278
279                 r = open_output(w, host);
280                 if (r < 0)
281                         return r;
282
283                 r = hashmap_put(s->writers, w->hashmap_key ?: key, w);
284                 if (r < 0)
285                         return r;
286         }
287
288         *writer = w;
289         w = NULL;
290         return 0;
291 }
292
293 /**********************************************************************
294  **********************************************************************
295  **********************************************************************/
296
297 /* This should go away as soon as Âµhttpd allows state to be passed around. */
298 static RemoteServer *server;
299
300 static int dispatch_raw_source_event(sd_event_source *event,
301                                      int fd,
302                                      uint32_t revents,
303                                      void *userdata);
304 static int dispatch_raw_connection_event(sd_event_source *event,
305                                          int fd,
306                                          uint32_t revents,
307                                          void *userdata);
308 static int dispatch_http_event(sd_event_source *event,
309                                int fd,
310                                uint32_t revents,
311                                void *userdata);
312
313 static int get_source_for_fd(RemoteServer *s,
314                              int fd, char *name, RemoteSource **source) {
315         Writer *writer;
316         int r;
317
318         assert(fd >= 0);
319         assert(source);
320
321         if (!GREEDY_REALLOC0(s->sources, s->sources_size, fd + 1))
322                 return log_oom();
323
324         r = get_writer(s, name, &writer);
325         if (r < 0) {
326                 log_warning("Failed to get writer for source %s: %s",
327                             name, strerror(-r));
328                 return r;
329         }
330
331         if (s->sources[fd] == NULL) {
332                 s->sources[fd] = source_new(fd, false, name, writer);
333                 if (!s->sources[fd]) {
334                         writer_unref(writer);
335                         return log_oom();
336                 }
337
338                 s->active++;
339         }
340
341         *source = s->sources[fd];
342         return 0;
343 }
344
345 static int remove_source(RemoteServer *s, int fd) {
346         RemoteSource *source;
347
348         assert(s);
349         assert(fd >= 0 && fd < (ssize_t) s->sources_size);
350
351         source = s->sources[fd];
352         if (source) {
353                 /* this closes fd too */
354                 source_free(source);
355                 s->sources[fd] = NULL;
356                 s->active--;
357         }
358
359         return 0;
360 }
361
362 static int add_source(RemoteServer *s, int fd, char* name, bool own_name) {
363
364         RemoteSource *source;
365         int r;
366
367         assert(s);
368         assert(fd >= 0);
369         assert(name);
370
371         if (!own_name) {
372                 name = strdup(name);
373                 if (!name)
374                         return log_oom();
375         }
376
377         r = get_source_for_fd(s, fd, name, &source);
378         if (r < 0) {
379                 log_error("Failed to create source for fd:%d (%s): %s",
380                           fd, name, strerror(-r));
381                 return r;
382         }
383
384         r = sd_event_add_io(s->events, &source->event,
385                             fd, EPOLLIN|EPOLLRDHUP|EPOLLPRI,
386                             dispatch_raw_source_event, s);
387         if (r < 0) {
388                 log_error("Failed to register event source for fd:%d: %s",
389                           fd, strerror(-r));
390                 goto error;
391         }
392
393         return 1; /* work to do */
394
395  error:
396         remove_source(s, fd);
397         return r;
398 }
399
400 static int add_raw_socket(RemoteServer *s, int fd) {
401         int r;
402
403         r = sd_event_add_io(s->events, &s->listen_event,
404                             fd, EPOLLIN,
405                             dispatch_raw_connection_event, s);
406         if (r < 0) {
407                 close(fd);
408                 return r;
409         }
410
411         s->active ++;
412         return 0;
413 }
414
415 static int setup_raw_socket(RemoteServer *s, const char *address) {
416         int fd;
417
418         fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
419         if (fd < 0)
420                 return fd;
421
422         return add_raw_socket(s, fd);
423 }
424
425 /**********************************************************************
426  **********************************************************************
427  **********************************************************************/
428
429 static RemoteSource *request_meta(void **connection_cls, int fd, char *hostname) {
430         RemoteSource *source;
431         Writer *writer;
432         int r;
433
434         assert(connection_cls);
435         if (*connection_cls)
436                 return *connection_cls;
437
438         r = get_writer(server, hostname, &writer);
439         if (r < 0) {
440                 log_warning("Failed to get writer for source %s: %s",
441                             hostname, strerror(-r));
442                 return NULL;
443         }
444
445         source = source_new(fd, true, hostname, writer);
446         if (!source) {
447                 log_oom();
448                 writer_unref(writer);
449                 return NULL;
450         }
451
452         log_debug("Added RemoteSource as connection metadata %p", source);
453
454         *connection_cls = source;
455         return source;
456 }
457
458 static void request_meta_free(void *cls,
459                               struct MHD_Connection *connection,
460                               void **connection_cls,
461                               enum MHD_RequestTerminationCode toe) {
462         RemoteSource *s;
463
464         assert(connection_cls);
465         s = *connection_cls;
466
467         log_debug("Cleaning up connection metadata %p", s);
468         source_free(s);
469         *connection_cls = NULL;
470 }
471
472 static int process_http_upload(
473                 struct MHD_Connection *connection,
474                 const char *upload_data,
475                 size_t *upload_data_size,
476                 RemoteSource *source) {
477
478         bool finished = false;
479         size_t remaining;
480         int r;
481
482         assert(source);
483
484         log_debug("request_handler_upload: connection %p, %zu bytes",
485                   connection, *upload_data_size);
486
487         if (*upload_data_size) {
488                 log_debug("Received %zu bytes", *upload_data_size);
489
490                 r = push_data(source, upload_data, *upload_data_size);
491                 if (r < 0)
492                         return mhd_respond_oom(connection);
493
494                 *upload_data_size = 0;
495         } else
496                 finished = true;
497
498         while (true) {
499                 r = process_source(source, arg_compress, arg_seal);
500                 if (r == -EAGAIN || r == -EWOULDBLOCK)
501                         break;
502                 else if (r < 0) {
503                         log_warning("Failed to process data for connection %p", connection);
504                         if (r == -E2BIG)
505                                 return mhd_respondf(connection,
506                                                     MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
507                                                     "Entry is too large, maximum is %u bytes.\n",
508                                                     DATA_SIZE_MAX);
509                         else
510                                 return mhd_respondf(connection,
511                                                     MHD_HTTP_UNPROCESSABLE_ENTITY,
512                                                     "Processing failed: %s.", strerror(-r));
513                 }
514         }
515
516         if (!finished)
517                 return MHD_YES;
518
519         /* The upload is finished */
520
521         remaining = source_non_empty(source);
522         if (remaining > 0) {
523                 log_warning("Premature EOFbyte. %zu bytes lost.", remaining);
524                 return mhd_respondf(connection, MHD_HTTP_EXPECTATION_FAILED,
525                                     "Premature EOF. %zu bytes of trailing data not processed.",
526                                     remaining);
527         }
528
529         return mhd_respond(connection, MHD_HTTP_ACCEPTED, "OK.\n");
530 };
531
532 static int request_handler(
533                 void *cls,
534                 struct MHD_Connection *connection,
535                 const char *url,
536                 const char *method,
537                 const char *version,
538                 const char *upload_data,
539                 size_t *upload_data_size,
540                 void **connection_cls) {
541
542         const char *header;
543         int r, code, fd;
544         _cleanup_free_ char *hostname = NULL;
545
546         assert(connection);
547         assert(connection_cls);
548         assert(url);
549         assert(method);
550
551         log_debug("Handling a connection %s %s %s", method, url, version);
552
553         if (*connection_cls)
554                 return process_http_upload(connection,
555                                            upload_data, upload_data_size,
556                                            *connection_cls);
557
558         if (!streq(method, "POST"))
559                 return mhd_respond(connection, MHD_HTTP_METHOD_NOT_ACCEPTABLE,
560                                    "Unsupported method.\n");
561
562         if (!streq(url, "/upload"))
563                 return mhd_respond(connection, MHD_HTTP_NOT_FOUND,
564                                    "Not found.\n");
565
566         header = MHD_lookup_connection_value(connection,
567                                              MHD_HEADER_KIND, "Content-Type");
568         if (!header || !streq(header, "application/vnd.fdo.journal"))
569                 return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
570                                    "Content-Type: application/vnd.fdo.journal"
571                                    " is required.\n");
572
573         {
574                 const union MHD_ConnectionInfo *ci;
575
576                 ci = MHD_get_connection_info(connection,
577                                              MHD_CONNECTION_INFO_CONNECTION_FD);
578                 if (!ci) {
579                         log_error("MHD_get_connection_info failed: cannot get remote fd");
580                         return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
581                                            "Cannot check remote address");
582                 }
583
584                 fd = ci->connect_fd;
585                 assert(fd >= 0);
586         }
587
588         if (server->check_trust) {
589                 r = check_permissions(connection, &code, &hostname);
590                 if (r < 0)
591                         return code;
592         } else {
593                 r = getnameinfo_pretty(fd, &hostname);
594                 if (r < 0) {
595                         return mhd_respond(connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
596                                            "Cannot check remote hostname");
597                 }
598         }
599
600         assert(hostname);
601
602         if (!request_meta(connection_cls, fd, hostname))
603                 return respond_oom(connection);
604         hostname = NULL;
605         return MHD_YES;
606 }
607
608 static int setup_microhttpd_server(RemoteServer *s,
609                                    int fd,
610                                    const char *key,
611                                    const char *cert,
612                                    const char *trust) {
613         struct MHD_OptionItem opts[] = {
614                 { MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) request_meta_free},
615                 { MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) microhttpd_logger},
616                 { MHD_OPTION_LISTEN_SOCKET, fd},
617                 { MHD_OPTION_END},
618                 { MHD_OPTION_END},
619                 { MHD_OPTION_END},
620                 { MHD_OPTION_END}};
621         int opts_pos = 3;
622         int flags =
623                 MHD_USE_DEBUG |
624                 MHD_USE_PEDANTIC_CHECKS |
625                 MHD_USE_EPOLL_LINUX_ONLY |
626                 MHD_USE_DUAL_STACK;
627
628         const union MHD_DaemonInfo *info;
629         int r, epoll_fd;
630         MHDDaemonWrapper *d;
631
632         assert(fd >= 0);
633
634         r = fd_nonblock(fd, true);
635         if (r < 0) {
636                 log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r));
637                 return r;
638         }
639
640         if (key) {
641                 assert(cert);
642
643                 opts[opts_pos++] = (struct MHD_OptionItem)
644                         {MHD_OPTION_HTTPS_MEM_KEY, 0, (char*) key};
645                 opts[opts_pos++] = (struct MHD_OptionItem)
646                         {MHD_OPTION_HTTPS_MEM_CERT, 0, (char*) cert};
647
648                 flags |= MHD_USE_SSL;
649
650                 if (trust)
651                         opts[opts_pos++] = (struct MHD_OptionItem)
652                                 {MHD_OPTION_HTTPS_MEM_TRUST, 0, (char*) trust};
653         }
654
655         d = new(MHDDaemonWrapper, 1);
656         if (!d)
657                 return log_oom();
658
659         d->fd = (uint64_t) fd;
660
661         d->daemon = MHD_start_daemon(flags, 0,
662                                      NULL, NULL,
663                                      request_handler, NULL,
664                                      MHD_OPTION_ARRAY, opts,
665                                      MHD_OPTION_END);
666         if (!d->daemon) {
667                 log_error("Failed to start Âµhttp daemon");
668                 r = -EINVAL;
669                 goto error;
670         }
671
672         log_debug("Started MHD %s daemon on fd:%d (wrapper @ %p)",
673                   key ? "HTTPS" : "HTTP", fd, d);
674
675
676         info = MHD_get_daemon_info(d->daemon, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
677         if (!info) {
678                 log_error("µhttp returned NULL daemon info");
679                 r = -ENOTSUP;
680                 goto error;
681         }
682
683         epoll_fd = info->listen_fd;
684         if (epoll_fd < 0) {
685                 log_error("µhttp epoll fd is invalid");
686                 r = -EUCLEAN;
687                 goto error;
688         }
689
690         r = sd_event_add_io(s->events, &d->event,
691                             epoll_fd, EPOLLIN,
692                             dispatch_http_event, d);
693         if (r < 0) {
694                 log_error("Failed to add event callback: %s", strerror(-r));
695                 goto error;
696         }
697
698         r = hashmap_ensure_allocated(&s->daemons, uint64_hash_func, uint64_compare_func);
699         if (r < 0) {
700                 log_oom();
701                 goto error;
702         }
703
704         r = hashmap_put(s->daemons, &d->fd, d);
705         if (r < 0) {
706                 log_error("Failed to add daemon to hashmap: %s", strerror(-r));
707                 goto error;
708         }
709
710         s->active ++;
711         return 0;
712
713 error:
714         MHD_stop_daemon(d->daemon);
715         free(d->daemon);
716         free(d);
717         return r;
718 }
719
720 static int setup_microhttpd_socket(RemoteServer *s,
721                                    const char *address,
722                                    const char *key,
723                                    const char *cert,
724                                    const char *trust) {
725         int fd;
726
727         fd = make_socket_fd(LOG_INFO, address, SOCK_STREAM | SOCK_CLOEXEC);
728         if (fd < 0)
729                 return fd;
730
731         return setup_microhttpd_server(s, fd, key, cert, trust);
732 }
733
734 static int dispatch_http_event(sd_event_source *event,
735                                int fd,
736                                uint32_t revents,
737                                void *userdata) {
738         MHDDaemonWrapper *d = userdata;
739         int r;
740
741         assert(d);
742
743         r = MHD_run(d->daemon);
744         if (r == MHD_NO) {
745                 log_error("MHD_run failed!");
746                 // XXX: unregister daemon
747                 return -EINVAL;
748         }
749
750         return 1; /* work to do */
751 }
752
753 /**********************************************************************
754  **********************************************************************
755  **********************************************************************/
756
757 static int dispatch_sigterm(sd_event_source *event,
758                             const struct signalfd_siginfo *si,
759                             void *userdata) {
760         RemoteServer *s = userdata;
761
762         assert(s);
763
764         log_received_signal(LOG_INFO, si);
765
766         sd_event_exit(s->events, 0);
767         return 0;
768 }
769
770 static int setup_signals(RemoteServer *s) {
771         sigset_t mask;
772         int r;
773
774         assert(s);
775
776         assert_se(sigemptyset(&mask) == 0);
777         sigset_add_many(&mask, SIGINT, SIGTERM, -1);
778         assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
779
780         r = sd_event_add_signal(s->events, &s->sigterm_event, SIGTERM, dispatch_sigterm, s);
781         if (r < 0)
782                 return r;
783
784         r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, dispatch_sigterm, s);
785         if (r < 0)
786                 return r;
787
788         return 0;
789 }
790
791 static int fd_fd(const char *spec) {
792         int fd, r;
793
794         r = safe_atoi(spec, &fd);
795         if (r < 0)
796                 return r;
797         if (fd < 0)
798                 return -EINVAL;
799
800         return fd;
801 }
802
803 static int remoteserver_init(RemoteServer *s,
804                              const char* key,
805                              const char* cert,
806                              const char* trust) {
807         int r, n, fd;
808         char **file;
809
810         assert(s);
811
812         if ((arg_listen_raw || arg_listen_http) && trust) {
813                 log_error("Option --trust makes all non-HTTPS connections untrusted.");
814                 return -EINVAL;
815         }
816
817         sd_event_default(&s->events);
818
819         setup_signals(s);
820
821         assert(server == NULL);
822         server = s;
823
824         n = sd_listen_fds(true);
825         if (n < 0) {
826                 log_error("Failed to read listening file descriptors from environment: %s",
827                           strerror(-n));
828                 return n;
829         } else
830                 log_info("Received %d descriptors", n);
831
832         if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) {
833                 log_error("Received fewer sockets than expected");
834                 return -EBADFD;
835         }
836
837         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
838                 if (sd_is_socket(fd, AF_UNSPEC, 0, false)) {
839                         log_info("Received a listening socket (fd:%d)", fd);
840
841                         if (fd == http_socket)
842                                 r = setup_microhttpd_server(s, fd, NULL, NULL, NULL);
843                         else if (fd == https_socket)
844                                 r = setup_microhttpd_server(s, fd, key, cert, trust);
845                         else
846                                 r = add_raw_socket(s, fd);
847                 } else if (sd_is_socket(fd, AF_UNSPEC, 0, true)) {
848                         char *hostname;
849
850                         r = getnameinfo_pretty(fd, &hostname);
851                         if (r < 0) {
852                                 log_error("Failed to retrieve remote name: %s", strerror(-r));
853                                 return r;
854                         }
855
856                         log_info("Received a connection socket (fd:%d) from %s", fd, hostname);
857
858                         r = add_source(s, fd, hostname, true);
859                         if (r < 0)
860                                 free(hostname);
861                 } else {
862                         log_error("Unknown socket passed on fd:%d", fd);
863
864                         return -EINVAL;
865                 }
866
867                 if(r < 0) {
868                         log_error("Failed to register socket (fd:%d): %s",
869                                   fd, strerror(-r));
870                         return r;
871                 }
872         }
873
874         if (arg_url) {
875                 const char *url, *hostname;
876
877                 url = strappenda(arg_url, "/entries");
878
879                 if (arg_getter) {
880                         log_info("Spawning getter %s...", url);
881                         fd = spawn_getter(arg_getter, url);
882                 } else {
883                         log_info("Spawning curl %s...", url);
884                         fd = spawn_curl(url);
885                 }
886                 if (fd < 0)
887                         return fd;
888
889                 hostname =
890                         startswith(arg_url, "https://") ?:
891                         startswith(arg_url, "http://") ?:
892                         arg_url;
893
894                 r = add_source(s, fd, (char*) hostname, false);
895                 if (r < 0)
896                         return r;
897         }
898
899         if (arg_listen_raw) {
900                 log_info("Listening on a socket...");
901                 r = setup_raw_socket(s, arg_listen_raw);
902                 if (r < 0)
903                         return r;
904         }
905
906         if (arg_listen_http) {
907                 r = setup_microhttpd_socket(s, arg_listen_http, NULL, NULL, NULL);
908                 if (r < 0)
909                         return r;
910         }
911
912         if (arg_listen_https) {
913                 r = setup_microhttpd_socket(s, arg_listen_https, key, cert, trust);
914                 if (r < 0)
915                         return r;
916         }
917
918         STRV_FOREACH(file, arg_files) {
919                 const char *output_name;
920
921                 if (streq(*file, "-")) {
922                         log_info("Using standard input as source.");
923
924                         fd = STDIN_FILENO;
925                         output_name = "stdin";
926                 } else {
927                         log_info("Reading file %s...", *file);
928
929                         fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
930                         if (fd < 0) {
931                                 log_error("Failed to open %s: %m", *file);
932                                 return -errno;
933                         }
934                         output_name = *file;
935                 }
936
937                 r = add_source(s, fd, (char*) output_name, false);
938                 if (r < 0)
939                         return r;
940         }
941
942         if (s->active == 0) {
943                 log_error("Zarro sources specified");
944                 return -EINVAL;
945         }
946
947         r = init_writer_hashmap(s);
948         if (r < 0)
949                 return r;
950
951         if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE) {
952                 /* In this case we know what the writer will be
953                    called, so we can create it and verify that we can
954                    create output as expected. */
955                 r = get_writer(s, NULL, &s->_single_writer);
956                 if (r < 0)
957                         return r;
958         }
959
960         return 0;
961 }
962
963 static void server_destroy(RemoteServer *s) {
964         size_t i;
965         MHDDaemonWrapper *d;
966
967         while ((d = hashmap_steal_first(s->daemons))) {
968                 MHD_stop_daemon(d->daemon);
969                 sd_event_source_unref(d->event);
970                 free(d);
971         }
972
973         hashmap_free(s->daemons);
974
975         assert(s->sources_size == 0 || s->sources);
976         for (i = 0; i < s->sources_size; i++)
977                 remove_source(s, i);
978         free(s->sources);
979
980         writer_unref(s->_single_writer);
981         hashmap_free(s->writers);
982
983         sd_event_source_unref(s->sigterm_event);
984         sd_event_source_unref(s->sigint_event);
985         sd_event_source_unref(s->listen_event);
986         sd_event_unref(s->events);
987
988         /* fds that we're listening on remain open... */
989 }
990
991 /**********************************************************************
992  **********************************************************************
993  **********************************************************************/
994
995 static int dispatch_raw_source_event(sd_event_source *event,
996                                      int fd,
997                                      uint32_t revents,
998                                      void *userdata) {
999
1000         RemoteServer *s = userdata;
1001         RemoteSource *source;
1002         int r;
1003
1004         assert(fd >= 0 && fd < (ssize_t) s->sources_size);
1005         source = s->sources[fd];
1006         assert(source->fd == fd);
1007
1008         r = process_source(source, arg_compress, arg_seal);
1009         if (source->state == STATE_EOF) {
1010                 size_t remaining;
1011
1012                 log_info("EOF reached with source fd:%d (%s)",
1013                          source->fd, source->name);
1014
1015                 remaining = source_non_empty(source);
1016                 if (remaining > 0)
1017                         log_warning("Premature EOF. %zu bytes lost.", remaining);
1018                 remove_source(s, source->fd);
1019                 log_info("%zd active sources remaining", s->active);
1020                 return 0;
1021         } else if (r == -E2BIG) {
1022                 log_error("Entry too big, skipped");
1023                 return 1;
1024         } else if (r == -EAGAIN) {
1025                 return 0;
1026         } else if (r < 0) {
1027                 log_info("Closing connection: %s", strerror(-r));
1028                 remove_source(server, fd);
1029                 return 0;
1030         } else
1031                 return 1;
1032 }
1033
1034 static int accept_connection(const char* type, int fd,
1035                              SocketAddress *addr, char **hostname) {
1036         int fd2, r;
1037
1038         log_debug("Accepting new %s connection on fd:%d", type, fd);
1039         fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC);
1040         if (fd2 < 0) {
1041                 log_error("accept() on fd:%d failed: %m", fd);
1042                 return -errno;
1043         }
1044
1045         switch(socket_address_family(addr)) {
1046         case AF_INET:
1047         case AF_INET6: {
1048                 _cleanup_free_ char *a = NULL;
1049                 char *b;
1050
1051                 r = socket_address_print(addr, &a);
1052                 if (r < 0) {
1053                         log_error("socket_address_print(): %s", strerror(-r));
1054                         close(fd2);
1055                         return r;
1056                 }
1057
1058                 r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b);
1059                 if (r < 0) {
1060                         close(fd2);
1061                         return r;
1062                 }
1063
1064                 log_info("Accepted %s %s connection from %s",
1065                          type,
1066                          socket_address_family(addr) == AF_INET ? "IP" : "IPv6",
1067                          a);
1068
1069                 *hostname = b;
1070
1071                 return fd2;
1072         };
1073         default:
1074                 log_error("Rejected %s connection with unsupported family %d",
1075                           type, socket_address_family(addr));
1076                 close(fd2);
1077
1078                 return -EINVAL;
1079         }
1080 }
1081
1082 static int dispatch_raw_connection_event(sd_event_source *event,
1083                                          int fd,
1084                                          uint32_t revents,
1085                                          void *userdata) {
1086         RemoteServer *s = userdata;
1087         int fd2, r;
1088         SocketAddress addr = {
1089                 .size = sizeof(union sockaddr_union),
1090                 .type = SOCK_STREAM,
1091         };
1092         char *hostname;
1093
1094         fd2 = accept_connection("raw", fd, &addr, &hostname);
1095         if (fd2 < 0)
1096                 return fd2;
1097
1098         r = add_source(s, fd2, hostname, true);
1099         if (r < 0)
1100                 free(hostname);
1101         return r;
1102 }
1103
1104 /**********************************************************************
1105  **********************************************************************
1106  **********************************************************************/
1107
1108 static const char* const journal_write_split_mode_table[_JOURNAL_WRITE_SPLIT_MAX] = {
1109         [JOURNAL_WRITE_SPLIT_NONE] = "none",
1110         [JOURNAL_WRITE_SPLIT_HOST] = "host",
1111 };
1112
1113 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(journal_write_split_mode, JournalWriteSplitMode);
1114 static DEFINE_CONFIG_PARSE_ENUM(config_parse_write_split_mode,
1115                                 journal_write_split_mode,
1116                                 JournalWriteSplitMode,
1117                                 "Failed to parse split mode setting");
1118
1119 static int parse_config(void) {
1120         const ConfigTableItem items[] = {
1121                 { "Remote",  "SplitMode",              config_parse_write_split_mode, 0, &arg_split_mode },
1122                 { "Remote",  "ServerKeyFile",          config_parse_path,             0, &arg_key        },
1123                 { "Remote",  "ServerCertificateFile",  config_parse_path,             0, &arg_cert       },
1124                 { "Remote",  "TrustedCertificateFile", config_parse_path,             0, &arg_trust      },
1125                 {}};
1126
1127         return config_parse(NULL, PKGSYSCONFDIR "/journal-remote.conf", NULL,
1128                             "Remote\0",
1129                             config_item_table_lookup, items,
1130                             false, false, true, NULL);
1131 }
1132
1133 static void help(void) {
1134         printf("%s [OPTIONS...] {FILE|-}...\n\n"
1135                "Write external journal events to journal file(s).\n\n"
1136                "  -h --help               Show this help\n"
1137                "     --version            Show package version\n"
1138                "     --url=URL            Read events from systemd-journal-gatewayd at URL\n"
1139                "     --getter=COMMAND     Read events from the output of COMMAND\n"
1140                "     --listen-raw=ADDR    Listen for connections at ADDR\n"
1141                "     --listen-http=ADDR   Listen for HTTP connections at ADDR\n"
1142                "     --listen-https=ADDR  Listen for HTTPS connections at ADDR\n"
1143                "  -o --output=FILE|DIR Write output to FILE or DIR/external-*.journal\n"
1144                "     --compress[=BOOL]    Use XZ-compression in the output journal (default: yes)\n"
1145                "     --seal[=BOOL]        Use Event sealing in the output journal (default: no)\n"
1146                "     --key=FILENAME       Specify key in PEM format (default:\n"
1147                "                          \"" PRIV_KEY_FILE "\")\n"
1148                "     --cert=FILENAME      Specify certificate in PEM format (default:\n"
1149                "                          \"" CERT_FILE "\")\n"
1150                "     --trust=FILENAME|all Specify CA certificate or disable checking (default:\n"
1151                "                          \"" TRUST_FILE "\")\n"
1152                "     --gnutls-log=CATEGORY...\n"
1153                "                          Specify a list of gnutls logging categories\n"
1154                "\n"
1155                "Note: file descriptors from sd_listen_fds() will be consumed, too.\n"
1156                , program_invocation_short_name);
1157 }
1158
1159 static int parse_argv(int argc, char *argv[]) {
1160         enum {
1161                 ARG_VERSION = 0x100,
1162                 ARG_URL,
1163                 ARG_LISTEN_RAW,
1164                 ARG_LISTEN_HTTP,
1165                 ARG_LISTEN_HTTPS,
1166                 ARG_GETTER,
1167                 ARG_SPLIT_MODE,
1168                 ARG_COMPRESS,
1169                 ARG_SEAL,
1170                 ARG_KEY,
1171                 ARG_CERT,
1172                 ARG_TRUST,
1173                 ARG_GNUTLS_LOG,
1174         };
1175
1176         static const struct option options[] = {
1177                 { "help",         no_argument,       NULL, 'h'              },
1178                 { "version",      no_argument,       NULL, ARG_VERSION      },
1179                 { "url",          required_argument, NULL, ARG_URL          },
1180                 { "getter",       required_argument, NULL, ARG_GETTER       },
1181                 { "listen-raw",   required_argument, NULL, ARG_LISTEN_RAW   },
1182                 { "listen-http",  required_argument, NULL, ARG_LISTEN_HTTP  },
1183                 { "listen-https", required_argument, NULL, ARG_LISTEN_HTTPS },
1184                 { "output",       required_argument, NULL, 'o'              },
1185                 { "split-mode",   required_argument, NULL, ARG_SPLIT_MODE   },
1186                 { "compress",     optional_argument, NULL, ARG_COMPRESS     },
1187                 { "seal",         optional_argument, NULL, ARG_SEAL         },
1188                 { "key",          required_argument, NULL, ARG_KEY          },
1189                 { "cert",         required_argument, NULL, ARG_CERT         },
1190                 { "trust",        required_argument, NULL, ARG_TRUST        },
1191                 { "gnutls-log",   required_argument, NULL, ARG_GNUTLS_LOG   },
1192                 {}
1193         };
1194
1195         int c, r;
1196         bool type_a, type_b;
1197
1198         assert(argc >= 0);
1199         assert(argv);
1200
1201         while ((c = getopt_long(argc, argv, "ho:", options, NULL)) >= 0)
1202                 switch(c) {
1203                 case 'h':
1204                         help();
1205                         return 0 /* done */;
1206
1207                 case ARG_VERSION:
1208                         puts(PACKAGE_STRING);
1209                         puts(SYSTEMD_FEATURES);
1210                         return 0 /* done */;
1211
1212                 case ARG_URL:
1213                         if (arg_url) {
1214                                 log_error("cannot currently set more than one --url");
1215                                 return -EINVAL;
1216                         }
1217
1218                         arg_url = optarg;
1219                         break;
1220
1221                 case ARG_GETTER:
1222                         if (arg_getter) {
1223                                 log_error("cannot currently use --getter more than once");
1224                                 return -EINVAL;
1225                         }
1226
1227                         arg_getter = optarg;
1228                         break;
1229
1230                 case ARG_LISTEN_RAW:
1231                         if (arg_listen_raw) {
1232                                 log_error("cannot currently use --listen-raw more than once");
1233                                 return -EINVAL;
1234                         }
1235
1236                         arg_listen_raw = optarg;
1237                         break;
1238
1239                 case ARG_LISTEN_HTTP:
1240                         if (arg_listen_http || http_socket >= 0) {
1241                                 log_error("cannot currently use --listen-http more than once");
1242                                 return -EINVAL;
1243                         }
1244
1245                         r = fd_fd(optarg);
1246                         if (r >= 0)
1247                                 http_socket = r;
1248                         else
1249                                 arg_listen_http = optarg;
1250                         break;
1251
1252                 case ARG_LISTEN_HTTPS:
1253                         if (arg_listen_https || https_socket >= 0) {
1254                                 log_error("cannot currently use --listen-https more than once");
1255                                 return -EINVAL;
1256                         }
1257
1258                         r = fd_fd(optarg);
1259                         if (r >= 0)
1260                                 https_socket = r;
1261                         else
1262                                 arg_listen_https = optarg;
1263
1264                         break;
1265
1266                 case ARG_KEY:
1267                         if (arg_key) {
1268                                 log_error("Key file specified twice");
1269                                 return -EINVAL;
1270                         }
1271
1272                         arg_key = strdup(optarg);
1273                         if (!arg_key)
1274                                 return log_oom();
1275
1276                         break;
1277
1278                 case ARG_CERT:
1279                         if (arg_cert) {
1280                                 log_error("Certificate file specified twice");
1281                                 return -EINVAL;
1282                         }
1283
1284                         arg_cert = strdup(optarg);
1285                         if (!arg_cert)
1286                                 return log_oom();
1287
1288                         break;
1289
1290                 case ARG_TRUST:
1291                         if (arg_trust || arg_trust_all) {
1292                                 log_error("Confusing trusted CA configuration");
1293                                 return -EINVAL;
1294                         }
1295
1296                         if (streq(optarg, "all"))
1297                                 arg_trust_all = true;
1298                         else {
1299 #ifdef HAVE_GNUTLS
1300                                 arg_trust = strdup(optarg);
1301                                 if (!arg_trust)
1302                                         return log_oom();
1303 #else
1304                                 log_error("Option --trust is not available.");
1305                                 return -EINVAL;
1306 #endif
1307                         }
1308
1309                         break;
1310
1311                 case 'o':
1312                         if (arg_output) {
1313                                 log_error("cannot use --output/-o more than once");
1314                                 return -EINVAL;
1315                         }
1316
1317                         arg_output = optarg;
1318                         break;
1319
1320                 case ARG_SPLIT_MODE:
1321                         arg_split_mode = journal_write_split_mode_from_string(optarg);
1322                         if (arg_split_mode == _JOURNAL_WRITE_SPLIT_INVALID) {
1323                                 log_error("Invalid split mode: %s", optarg);
1324                                 return -EINVAL;
1325                         }
1326                         break;
1327
1328                 case ARG_COMPRESS:
1329                         if (optarg) {
1330                                 r = parse_boolean(optarg);
1331                                 if (r < 0) {
1332                                         log_error("Failed to parse --compress= parameter.");
1333                                         return -EINVAL;
1334                                 }
1335
1336                                 arg_compress = !!r;
1337                         } else
1338                                 arg_compress = true;
1339
1340                         break;
1341
1342                 case ARG_SEAL:
1343                         if (optarg) {
1344                                 r = parse_boolean(optarg);
1345                                 if (r < 0) {
1346                                         log_error("Failed to parse --seal= parameter.");
1347                                         return -EINVAL;
1348                                 }
1349
1350                                 arg_seal = !!r;
1351                         } else
1352                                 arg_seal = true;
1353
1354                         break;
1355
1356                 case ARG_GNUTLS_LOG: {
1357 #ifdef HAVE_GNUTLS
1358                         const char *word, *state;
1359                         size_t size;
1360
1361                         FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
1362                                 char *cat;
1363
1364                                 cat = strndup(word, size);
1365                                 if (!cat)
1366                                         return log_oom();
1367
1368                                 if (strv_consume(&arg_gnutls_log, cat) < 0)
1369                                         return log_oom();
1370                         }
1371                         break;
1372 #else
1373                         log_error("Option --gnutls-log is not available.");
1374                         return -EINVAL;
1375 #endif
1376                 }
1377
1378                 case '?':
1379                         return -EINVAL;
1380
1381                 default:
1382                         assert_not_reached("Unknown option code.");
1383                 }
1384
1385         if (optind < argc)
1386                 arg_files = argv + optind;
1387
1388         type_a = arg_getter || !strv_isempty(arg_files);
1389         type_b = arg_url
1390                 || arg_listen_raw
1391                 || arg_listen_http || arg_listen_https
1392                 || sd_listen_fds(false) > 0;
1393         if (type_a && type_b) {
1394                 log_error("Cannot use file input or --getter with "
1395                           "--arg-listen-... or socket activation.");
1396                 return -EINVAL;
1397         }
1398         if (type_a) {
1399                 if (!arg_output) {
1400                         log_error("Option --output must be specified with file input or --getter.");
1401                         return -EINVAL;
1402                 }
1403
1404                 arg_split_mode = JOURNAL_WRITE_SPLIT_NONE;
1405         }
1406
1407         if (arg_split_mode == JOURNAL_WRITE_SPLIT_NONE
1408             && arg_output && is_dir(arg_output, true) > 0) {
1409                 log_error("For SplitMode=none, output must be a file.");
1410                 return -EINVAL;
1411         }
1412
1413         if (arg_split_mode == JOURNAL_WRITE_SPLIT_HOST
1414             && arg_output && is_dir(arg_output, true) <= 0) {
1415                 log_error("For SplitMode=host, output must be a directory.");
1416                 return -EINVAL;
1417         }
1418
1419         log_debug("Full config: SplitMode=%s Key=%s Cert=%s Trust=%s",
1420                   journal_write_split_mode_to_string(arg_split_mode),
1421                   strna(arg_key),
1422                   strna(arg_cert),
1423                   strna(arg_trust));
1424
1425         return 1 /* work to do */;
1426 }
1427
1428 static int load_certificates(char **key, char **cert, char **trust) {
1429         int r;
1430
1431         r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
1432         if (r < 0) {
1433                 log_error("Failed to read key from file '%s': %s",
1434                           arg_key ?: PRIV_KEY_FILE, strerror(-r));
1435                 return r;
1436         }
1437
1438         r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
1439         if (r < 0) {
1440                 log_error("Failed to read certificate from file '%s': %s",
1441                           arg_cert ?: CERT_FILE, strerror(-r));
1442                 return r;
1443         }
1444
1445         if (arg_trust_all)
1446                 log_info("Certificate checking disabled.");
1447         else {
1448                 r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
1449                 if (r < 0) {
1450                         log_error("Failed to read CA certificate file '%s': %s",
1451                                   arg_trust ?: TRUST_FILE, strerror(-r));
1452                         return r;
1453                 }
1454         }
1455
1456         return 0;
1457 }
1458
1459 static int setup_gnutls_logger(char **categories) {
1460         if (!arg_listen_http && !arg_listen_https)
1461                 return 0;
1462
1463 #ifdef HAVE_GNUTLS
1464         {
1465                 char **cat;
1466                 int r;
1467
1468                 gnutls_global_set_log_function(log_func_gnutls);
1469
1470                 if (categories)
1471                         STRV_FOREACH(cat, categories) {
1472                                 r = log_enable_gnutls_category(*cat);
1473                                 if (r < 0)
1474                                         return r;
1475                         }
1476                 else
1477                         log_reset_gnutls_level();
1478         }
1479 #endif
1480
1481         return 0;
1482 }
1483
1484 int main(int argc, char **argv) {
1485         RemoteServer s = {};
1486         int r;
1487         _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
1488
1489         log_show_color(true);
1490         log_parse_environment();
1491
1492         r = parse_config();
1493         if (r < 0)
1494                 return EXIT_FAILURE;
1495
1496         r = parse_argv(argc, argv);
1497         if (r <= 0)
1498                 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1499
1500         r = setup_gnutls_logger(arg_gnutls_log);
1501         if (r < 0)
1502                 return EXIT_FAILURE;
1503
1504         if (arg_listen_https || https_socket >= 0)
1505                 if (load_certificates(&key, &cert, &trust) < 0)
1506                         return EXIT_FAILURE;
1507
1508         if (remoteserver_init(&s, key, cert, trust) < 0)
1509                 return EXIT_FAILURE;
1510
1511         sd_event_set_watchdog(s.events, true);
1512
1513         log_debug("%s running as pid "PID_FMT,
1514                   program_invocation_short_name, getpid());
1515         sd_notify(false,
1516                   "READY=1\n"
1517                   "STATUS=Processing requests...");
1518
1519         while (s.active) {
1520                 r = sd_event_get_state(s.events);
1521                 if (r < 0)
1522                         break;
1523                 if (r == SD_EVENT_FINISHED)
1524                         break;
1525
1526                 r = sd_event_run(s.events, -1);
1527                 if (r < 0) {
1528                         log_error("Failed to run event loop: %s", strerror(-r));
1529                         break;
1530                 }
1531         }
1532
1533         sd_notifyf(false,
1534                    "STOPPING=1\n"
1535                    "STATUS=Shutting down after writing %" PRIu64 " entries...", s.event_count);
1536         log_info("Finishing after writing %" PRIu64 " entries", s.event_count);
1537
1538         server_destroy(&s);
1539
1540         free(arg_key);
1541         free(arg_cert);
1542         free(arg_trust);
1543
1544         return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1545 }