]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/logger.c
Merge branch 'ldadd-cflags-warnings' of https://github.com/rudimeier/util-linux
[thirdparty/util-linux.git] / misc-utils / logger.c
CommitLineData
6dbe3af9
KZ
1/*
2 * Copyright (c) 1983, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
7eda085c 32 *
b50945d4 33 * 1999-02-22 Arkadiusz Miƛkiewicz <misiek@pld.ORG.PL>
7eda085c
KZ
34 * - added Native Language Support
35 * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
36 * - fixed strerr(errno) in gettext calls
6dbe3af9
KZ
37 */
38
6dbe3af9 39#include <errno.h>
047e2888 40#include <limits.h>
6dbe3af9
KZ
41#include <unistd.h>
42#include <stdlib.h>
5ec85227 43#include <sys/time.h>
6dbe3af9
KZ
44#include <stdio.h>
45#include <ctype.h>
46#include <string.h>
7eda085c
KZ
47#include <sys/types.h>
48#include <sys/socket.h>
66ee8158 49#include <sys/un.h>
912d6b98
WJE
50#include <arpa/inet.h>
51#include <netdb.h>
b363e86d 52#include <getopt.h>
019b9702 53#include <pwd.h>
27a9eb53
KZ
54#include <sys/types.h>
55#include <signal.h>
b363e86d 56
633493be 57#include "all-io.h"
b363e86d 58#include "c.h"
c05a80ca 59#include "closestream.h"
7eda085c 60#include "nls.h"
52a49e9a 61#include "pathnames.h"
b363e86d 62#include "strutils.h"
4b670c01 63#include "xalloc.h"
4299ed1c
KZ
64#include "strv.h"
65#include "list.h"
6dbe3af9
KZ
66
67#define SYSLOG_NAMES
68#include <syslog.h>
69
ebff016a 70#ifdef HAVE_LIBSYSTEMD
d77dc29e 71# include <systemd/sd-daemon.h>
4b670c01
SK
72# include <systemd/sd-journal.h>
73#endif
74
87ee2658
ST
75#ifdef HAVE_SYS_TIMEX_H
76# include <sys/timex.h>
77#endif
78
68265d07
SK
79enum {
80 TYPE_UDP = (1 << 1),
81 TYPE_TCP = (1 << 2),
82 ALL_TYPES = TYPE_UDP | TYPE_TCP
83};
84
d77dc29e
SK
85enum {
86 AF_UNIX_ERRORS_OFF = 0,
87 AF_UNIX_ERRORS_ON,
88 AF_UNIX_ERRORS_AUTO
89};
90
98920f80 91enum {
4b670c01 92 OPT_PRIO_PREFIX = CHAR_MAX + 1,
4de2e8a0
SK
93 OPT_JOURNALD,
94 OPT_RFC3164,
d77dc29e 95 OPT_RFC5424,
3f51c10b 96 OPT_SOCKET_ERRORS,
55f5bc66 97 OPT_MSGID,
fd343a05 98 OPT_NOACT,
b6b67955 99 OPT_ID,
4299ed1c
KZ
100 OPT_STRUCTURED_DATA_ID,
101 OPT_STRUCTURED_DATA_PARAM,
b6b67955 102 OPT_OCTET_COUNT
98920f80
DJ
103};
104
4299ed1c
KZ
105/* rfc5424 structured data */
106struct structured_data {
107 char *id; /* SD-ID */
108 char **params; /* array with SD-PARAMs */
109
110 struct list_head sds;
111};
112
cfa77d26
SK
113struct logger_ctl {
114 int fd;
cfa77d26 115 int pri;
59c6ac0b 116 pid_t pid; /* zero when unwanted */
2b3f40c5 117 char *hdr; /* the syslog header (based on protocol) */
cfa77d26 118 char *tag;
55f5bc66 119 char *msgid;
c95d3209 120 char *unix_socket; /* -u <path> or default to _PATH_DEVLOG */
c68a1cb4
SK
121 char *server;
122 char *port;
123 int socket_type;
f68b8aa7 124 size_t max_message_size;
4299ed1c
KZ
125 struct list_head user_sds; /* user defined rfc5424 structured data */
126 struct list_head reserved_sds; /* standard rfc5424 structured data */
127
2b3f40c5 128 void (*syslogfp)(struct logger_ctl *ctl);
4299ed1c 129
4de2e8a0 130 unsigned int
d77dc29e 131 unix_socket_errors:1, /* whether to report or not errors */
fd343a05 132 noact:1, /* do not write to sockets */
d77dc29e
SK
133 prio_prefix:1, /* read priority from intput */
134 stderr_printout:1, /* output message to stderr */
135 rfc5424_time:1, /* include time stamp */
136 rfc5424_tq:1, /* include time quality markup */
ae6846b8 137 rfc5424_host:1, /* include hostname */
b6b67955
AB
138 skip_empty_lines:1, /* do not send empty lines when processing files */
139 octet_count:1; /* use RFC6587 octet counting */
cfa77d26
SK
140};
141
ef5fb280
KZ
142/*
143 * For tests we want to be able to control datetime outputs
144 */
145#ifdef TEST_LOGGER
146static inline int logger_gettimeofday(struct timeval *tv, struct timezone *tz)
147{
148 char *str = getenv("LOGGER_TEST_TIMEOFDAY");
149 uintmax_t sec, usec;
150
151 if (str && sscanf(str, "%ju.%ju", &sec, &usec) == 2) {
152 tv->tv_sec = sec;
153 tv->tv_usec = usec;
98e90a49 154 return tv->tv_sec >= 0 && tv->tv_usec >= 0 ? 0 : -EINVAL;
ef5fb280
KZ
155 }
156
157 return gettimeofday(tv, tz);
158}
159
160static inline char *logger_xgethostname(void)
161{
162 char *str = getenv("LOGGER_TEST_HOSTNAME");
163 return str ? xstrdup(str) : xgethostname();
164}
165
166static inline pid_t logger_getpid(void)
167{
168 char *str = getenv("LOGGER_TEST_GETPID");
169 unsigned int pid;
170
171 if (str && sscanf(str, "%u", &pid) == 1)
172 return pid;
173 return getpid();
174}
175
176
177#undef HAVE_NTP_GETTIME /* force to default non-NTP */
178
179#else /* !TEST_LOGGER */
180# define logger_gettimeofday(x, y) gettimeofday(x, y)
181# define logger_xgethostname xgethostname
182# define logger_getpid getpid
183#endif
184
185
46ee14df 186static int decode(const char *name, CODE *codetab)
82054b1d
DR
187{
188 register CODE *c;
189
4d7d1af6
SK
190 if (name == NULL || *name == '\0')
191 return -1;
192 if (isdigit(*name)) {
193 int num;
194 char *end = NULL;
195
8d341322 196 errno = 0;
4d7d1af6
SK
197 num = strtol(name, &end, 10);
198 if (errno || name == end || (end && *end))
199 return -1;
200 for (c = codetab; c->c_name; c++)
201 if (num == c->c_val)
202 return num;
203 return -1;
204 }
82054b1d
DR
205 for (c = codetab; c->c_name; c++)
206 if (!strcasecmp(name, c->c_name))
207 return (c->c_val);
208
209 return -1;
210}
211
212static int pencode(char *s)
213{
2e0fd22d
SK
214 int facility, level;
215 char *separator;
216
217 separator = strchr(s, '.');
218 if (separator) {
219 *separator = '\0';
220 facility = decode(s, facilitynames);
221 if (facility < 0)
222 errx(EXIT_FAILURE, _("unknown facility name: %s"), s);
223 s = ++separator;
224 } else
225 facility = LOG_USER;
226 level = decode(s, prioritynames);
227 if (level < 0)
228 errx(EXIT_FAILURE, _("unknown priority name: %s"), s);
9a13f968
SK
229 if (facility == LOG_KERN)
230 facility = LOG_USER; /* kern is forbidden */
2e0fd22d 231 return ((level & LOG_PRIMASK) | (facility & LOG_FACMASK));
82054b1d
DR
232}
233
fc20393c 234static int unix_socket(struct logger_ctl *ctl, const char *path, int *socket_type)
fe6999da 235{
fc20393c 236 int fd, i, type = -1;
fe6999da 237 static struct sockaddr_un s_addr; /* AF_UNIX address of local logger */
7eda085c 238
68265d07
SK
239 if (strlen(path) >= sizeof(s_addr.sun_path))
240 errx(EXIT_FAILURE, _("openlog %s: pathname too long"), path);
7eda085c 241
fe6999da 242 s_addr.sun_family = AF_UNIX;
68265d07
SK
243 strcpy(s_addr.sun_path, path);
244
245 for (i = 2; i; i--) {
246 int st = -1;
49999d6a 247
fc20393c 248 if (i == 2 && *socket_type & TYPE_UDP) {
68265d07 249 st = SOCK_DGRAM;
fc20393c
KZ
250 type = TYPE_UDP;
251 }
252 if (i == 1 && *socket_type & TYPE_TCP) {
68265d07 253 st = SOCK_STREAM;
fc20393c
KZ
254 type = TYPE_TCP;
255 }
68265d07
SK
256 if (st == -1 || (fd = socket(AF_UNIX, st, 0)) == -1)
257 continue;
fe6999da
SK
258 if (connect(fd, (struct sockaddr *)&s_addr, sizeof(s_addr)) == -1) {
259 close(fd);
68265d07 260 continue;
fe6999da 261 }
68265d07 262 break;
fe6999da 263 }
7eda085c 264
d77dc29e
SK
265 if (i == 0) {
266 if (ctl->unix_socket_errors)
267 err(EXIT_FAILURE, _("socket %s"), path);
28bad822
KZ
268
269 /* openlog(3) compatibility, socket errors are
270 * not reported, but ignored silently */
271 ctl->noact = 1;
272 return -1;
d77dc29e 273 }
fc20393c
KZ
274
275 /* replace ALL_TYPES with the real TYPE_* */
276 if (type > 0 && type != *socket_type)
277 *socket_type = type;
fe6999da 278 return fd;
7eda085c
KZ
279}
280
fc20393c 281static int inet_socket(const char *servername, const char *port, int *socket_type)
68265d07 282{
fc20393c 283 int fd, errcode, i, type = -1;
24f4db69 284 struct addrinfo hints, *res;
68265d07
SK
285 const char *p = port;
286
287 for (i = 2; i; i--) {
288 memset(&hints, 0, sizeof(hints));
fc20393c 289 if (i == 2 && *socket_type & TYPE_UDP) {
68265d07 290 hints.ai_socktype = SOCK_DGRAM;
fc20393c 291 type = TYPE_UDP;
68265d07
SK
292 if (port == NULL)
293 p = "syslog";
294 }
fc20393c 295 if (i == 1 && *socket_type & TYPE_TCP) {
68265d07 296 hints.ai_socktype = SOCK_STREAM;
fc20393c 297 type = TYPE_TCP;
68265d07
SK
298 if (port == NULL)
299 p = "syslog-conn";
300 }
301 if (hints.ai_socktype == 0)
302 continue;
303 hints.ai_family = AF_UNSPEC;
304 errcode = getaddrinfo(servername, p, &hints, &res);
305 if (errcode != 0)
4ce393f4 306 errx(EXIT_FAILURE, _("failed to resolve name %s port %s: %s"),
68265d07
SK
307 servername, p, gai_strerror(errcode));
308 if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) {
309 freeaddrinfo(res);
310 continue;
311 }
312 if (connect(fd, res->ai_addr, res->ai_addrlen) == -1) {
313 freeaddrinfo(res);
314 close(fd);
315 continue;
316 }
24f4db69 317
68265d07
SK
318 freeaddrinfo(res);
319 break;
320 }
912d6b98 321
68265d07 322 if (i == 0)
4ce393f4 323 errx(EXIT_FAILURE, _("failed to connect to %s port %s"), servername, p);
49999d6a 324
fc20393c
KZ
325 /* replace ALL_TYPES with the real TYPE_* */
326 if (type > 0 && type != *socket_type)
327 *socket_type = type;
912d6b98
WJE
328 return fd;
329}
68265d07 330
ebff016a 331#ifdef HAVE_LIBSYSTEMD
fd343a05 332static int journald_entry(struct logger_ctl *ctl, FILE *fp)
4b670c01
SK
333{
334 struct iovec *iovec;
335 char *buf = NULL;
336 ssize_t sz;
fd343a05 337 int n, lines, vectors = 8, ret = 0;
4b670c01
SK
338 size_t dummy = 0;
339
340 iovec = xmalloc(vectors * sizeof(struct iovec));
341 for (lines = 0; /* nothing */ ; lines++) {
342 buf = NULL;
343 sz = getline(&buf, &dummy, fp);
344 if (sz == -1)
345 break;
346 if (0 < sz && buf[sz - 1] == '\n') {
347 sz--;
348 buf[sz] = '\0';
349 }
350 if (lines == vectors) {
351 vectors *= 2;
047e2888
SK
352 if (IOV_MAX < vectors)
353 errx(EXIT_FAILURE, _("maximum input lines (%d) exceeded"), IOV_MAX);
4b670c01
SK
354 iovec = xrealloc(iovec, vectors * sizeof(struct iovec));
355 }
356 iovec[lines].iov_base = buf;
357 iovec[lines].iov_len = sz;
358 }
fd343a05
KZ
359
360 if (!ctl->noact)
361 ret = sd_journal_sendv(iovec, lines);
362 if (ctl->stderr_printout) {
363 for (n = 0; n < lines; n++)
364 fprintf(stderr, "%s\n", (char *) iovec[n].iov_base);
365 }
4b670c01
SK
366 for (n = 0; n < lines; n++)
367 free(iovec[n].iov_base);
368 free(iovec);
369 return ret;
370}
371#endif
372
46ee14df 373static char *xgetlogin(void)
019b9702
SK
374{
375 char *cp;
376 struct passwd *pw;
377
378 if (!(cp = getlogin()) || !*cp)
379 cp = (pw = getpwuid(geteuid()))? pw->pw_name : "<someone>";
380 return cp;
381}
382
3070ca77
RG
383/* this creates a timestamp based on current time according to the
384 * fine rules of RFC3164, most importantly it ensures in a portable
385 * way that the month day is correctly written (with a SP instead
386 * of a leading 0). The function uses a static buffer which is
387 * overwritten on the next call (just like ctime() does).
388 */
9a13f968 389static const char *rfc3164_current_time(void)
3070ca77
RG
390{
391 static char time[32];
392 struct timeval tv;
393 struct tm *tm;
9a13f968
SK
394 static char *monthnames[] = {
395 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
396 "Sep", "Oct", "Nov", "Dec"
397 };
3070ca77 398
ef5fb280 399 logger_gettimeofday(&tv, NULL);
3070ca77
RG
400 tm = localtime(&tv.tv_sec);
401 snprintf(time, sizeof(time),"%s %2d %2.2d:%2.2d:%2.2d",
402 monthnames[tm->tm_mon], tm->tm_mday,
0f1c825b 403 tm->tm_hour, tm->tm_min, tm->tm_sec);
3070ca77
RG
404 return time;
405}
406
17c8aa1d 407#define next_iovec(ary, idx) __extension__ ({ \
b9710f1f 408 assert(ARRAY_SIZE(ary) > (size_t)idx); \
17c8aa1d
KZ
409 assert(idx >= 0); \
410 &ary[idx++]; \
411})
412
413#define iovec_add_string(ary, idx, str, len) \
414 do { \
415 struct iovec *v = next_iovec(ary, idx); \
416 v->iov_base = (void *) str; \
417 v->iov_len = len ? len : strlen(str); \
418 } while (0)
419
420#define iovec_memcmp(ary, idx, str, len) \
421 memcmp((ary)[(idx) - 1].iov_base, str, len)
422
940a14a3 423/* writes generated buffer to desired destination. For TCP syslog,
b6b67955
AB
424 * we use RFC6587 octet-stuffing (unless octet-counting is selected).
425 * This is not great, but doing full blown RFC5425 (TLS) looks like
426 * it is too much for the logger utility. If octet-counting is
427 * selected, we use that.
940a14a3 428 */
2b3f40c5 429static void write_output(const struct logger_ctl *ctl, const char *const msg)
4be84306 430{
17c8aa1d
KZ
431 struct iovec iov[4];
432 int iovlen = 0;
433 char *octet = NULL;
434
435 /* 1) octen count */
436 if (ctl->octet_count) {
437 size_t len = xasprintf(&octet, "%zu ", strlen(ctl->hdr) + strlen(msg));
438 iovec_add_string(iov, iovlen, octet, len);
439 }
440
441 /* 2) header */
442 iovec_add_string(iov, iovlen, ctl->hdr, 0);
443
444 /* 3) message */
445 iovec_add_string(iov, iovlen, msg, 0);
fd343a05
KZ
446
447 if (!ctl->noact) {
2ec100a1 448 struct msghdr message = { 0 };
27a9eb53
KZ
449 struct cmsghdr *cmhp;
450 struct ucred *cred;
451 union {
452 struct cmsghdr cmh;
453 char control[CMSG_SPACE(sizeof(struct ucred))];
454 } cbuf;
17c8aa1d
KZ
455
456 /* 4) add extra \n to make sure message is terminated */
457 if ((ctl->socket_type == TYPE_TCP) && !ctl->octet_count)
458 iovec_add_string(iov, iovlen, "\n", 1);
94a28496 459
2ec100a1
SK
460 message.msg_iov = iov;
461 message.msg_iovlen = iovlen;
94a28496 462
27a9eb53
KZ
463 /* syslog/journald may follow local socket credentials rather
464 * than in the message PID. If we use --id as root than we can
465 * force kernel to accept another valid PID than the real logger(1)
466 * PID.
467 */
468 if (ctl->pid && !ctl->server && ctl->pid != getpid()
469 && geteuid() == 0 && kill(ctl->pid, 0) == 0) {
470
2ec100a1
SK
471 message.msg_control = cbuf.control;
472 message.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
27a9eb53 473
2ec100a1 474 cmhp = CMSG_FIRSTHDR(&message);
27a9eb53
KZ
475 cmhp->cmsg_len = CMSG_LEN(sizeof(struct ucred));
476 cmhp->cmsg_level = SOL_SOCKET;
477 cmhp->cmsg_type = SCM_CREDENTIALS;
478 cred = (struct ucred *) CMSG_DATA(cmhp);
479
480 cred->pid = ctl->pid;
481 }
482
2ec100a1 483 if (sendmsg(ctl->fd, &message, 0) < 0)
94a28496 484 warn(_("send message failed"));
fd343a05 485 }
17c8aa1d
KZ
486
487 if (ctl->stderr_printout) {
488 /* make sure it's terminated for stderr */
489 if (iovec_memcmp(iov, iovlen, "\n", 1) != 0)
490 iovec_add_string(iov, iovlen, "\n", 1);
491
492 ignore_result( writev(STDERR_FILENO, iov, iovlen) );
493 }
494
495 free(octet);
4be84306
RG
496}
497
d5f93061 498#define NILVALUE "-"
2b3f40c5 499static void syslog_rfc3164_header(struct logger_ctl *const ctl)
195c3603 500{
d5f93061 501 char pid[30], *hostname;
d8b616c2 502
77c3bd5b 503 *pid = '\0';
cfa77d26 504 if (ctl->fd < 0)
c462a8ca 505 return;
59c6ac0b
KZ
506 if (ctl->pid)
507 snprintf(pid, sizeof(pid), "[%d]", ctl->pid);
852feb72 508
ef5fb280 509 if ((hostname = logger_xgethostname())) {
d5f93061
SK
510 char *dot = strchr(hostname, '.');
511 if (dot)
512 *dot = '\0';
513 } else
514 hostname = xstrdup(NILVALUE);
852feb72 515
2b3f40c5
RG
516 xasprintf(&ctl->hdr, "<%d>%.15s %s %.200s%s: ",
517 ctl->pri, rfc3164_current_time(), hostname, ctl->tag, pid);
852feb72
KZ
518
519 free(hostname);
7eda085c
KZ
520}
521
4299ed1c
KZ
522static inline struct list_head *get_user_structured_data(struct logger_ctl *ctl)
523{
524 return &ctl->user_sds;
525}
526
527static inline struct list_head *get_reserved_structured_data(struct logger_ctl *ctl)
528{
529 return &ctl->reserved_sds;
530}
531
532static int has_structured_data_id(struct list_head *ls, const char *id)
533{
534 struct list_head *p;
535
536 if (!ls || list_empty(ls))
537 return 0;
538
539 list_for_each(p, ls) {
540 struct structured_data *sd = list_entry(p, struct structured_data, sds);
541 if (sd->id && strcmp(sd->id, id) == 0)
542 return 1;
543 }
544
545 return 0;
546}
547
548static void add_structured_data_id(struct list_head *ls, const char *id)
549{
550 struct structured_data *sd;
551
552 assert(id);
553
554 if (has_structured_data_id(ls, id))
555 errx(EXIT_FAILURE, _("structured data ID '%s' is not unique"), id);
556
557 sd = xcalloc(1, sizeof(*sd));
558 INIT_LIST_HEAD(&sd->sds);
559 sd->id = xstrdup(id);
560
561 list_add_tail(&sd->sds, ls);
562}
563
564static void add_structured_data_param(struct list_head *ls, const char *param)
565{
566 struct structured_data *sd;
567
568 if (list_empty(ls))
569 errx(EXIT_FAILURE, _("--sd-id no specified for --sd-param %s"), param);
570
571 assert(param);
572
573 sd = list_last_entry(ls, struct structured_data, sds);
574
575 if (strv_extend(&sd->params, param))
576 err_oom();
577}
578
579static void add_structured_data_paramf(struct list_head *ls, const char *fmt, ...)
580{
581 struct structured_data *sd;
582 va_list ap;
583 int x;
584
585 assert(!list_empty(ls));
586 assert(fmt);
587
588 sd = list_last_entry(ls, struct structured_data, sds);
589 va_start(ap, fmt);
590 x = strv_extendv(&sd->params, fmt, ap);
591 va_end(ap);
592
593 if (x)
594 err_oom();
595}
596
597static char *strdup_structured_data(struct structured_data *sd)
598{
599 char *res, *tmp;
600
601 if (strv_isempty(sd->params))
602 return NULL;
603
604 xasprintf(&res, "[%s %s]", sd->id,
605 (tmp = strv_join(sd->params, " ")));
606 free(tmp);
607 return res;
608}
609
610static char *strdup_structured_data_list(struct list_head *ls)
611{
612 struct list_head *p;
613 char *res = NULL;
614
615 list_for_each(p, ls) {
616 struct structured_data *sd = list_entry(p, struct structured_data, sds);
617 char *one = strdup_structured_data(sd);
618 char *tmp = res;
619
620 if (!one)
621 continue;
622 res = strappend(tmp, one);
623 free(tmp);
624 free(one);
625 }
626
627 return res;
628}
629
630static char *get_structured_data_string(struct logger_ctl *ctl)
631{
632 char *sys = NULL, *usr = NULL, *res;
633
634 if (!list_empty(&ctl->reserved_sds))
635 sys = strdup_structured_data_list(&ctl->reserved_sds);
636 if (!list_empty(&ctl->user_sds))
637 usr = strdup_structured_data_list(&ctl->user_sds);
638
639 if (sys && usr) {
640 res = strappend(sys, usr);
641 free(sys);
642 free(usr);
643 } else
644 res = sys ? sys : usr;
645
646 return res;
647}
648
649static int valid_structured_data_param(const char *str)
650{
651 char *eq = strchr(str, '='),
652 *qm1 = strchr(str, '"'),
653 *qm2 = qm1 ? strchr(qm1 + 1, '"') : NULL;
654
655 if (!eq || !qm1 || !qm2) /* something is missing */
656 return 0;
657
658 /* foo="bar" */
659 return eq > str && eq < qm1 && eq + 1 == qm1 && qm1 < qm2 && *(qm2 + 1) == '\0';
660}
661
662/* SD-ID format:
663 * name@<private enterprise number>, e.g., "ourSDID@32473"
664 */
665static int valid_structured_data_id(const char *str)
666{
667 char *at = strchr(str, '@');
668 const char *p;
669
670 /* standardized IDs without @<digits> */
671 if (!at && (strcmp(str, "timeQuality") == 0 ||
672 strcmp(str, "origin") == 0 ||
673 strcmp(str, "meta") == 0))
674 return 1;
675
676 if (!at || at == str || !*(at + 1))
677 return 0;
678 if (!isdigit_string(at + 1))
679 return 0;
680
681 /* check for forbidden chars in the <name> */
682 for (p = str; p < at; p++) {
683 if (*p == '[' || *p == '=' || *p == '"' || *p == '@')
684 return 0;
685 if (isblank((unsigned int) *p) || iscntrl((unsigned int) *p))
686 return 0;
687 }
688 return 1;
689}
690
691
692/* Some field mappings may be controversial, thus I give the reason
4826184b
RG
693 * why this specific mapping was used:
694 * APP-NAME <-- tag
695 * Some may argue that "logger" is a better fit, but we think
696 * this is better inline of what other implementations do. In
697 * rsyslog, for example, the TAG value is populated from APP-NAME.
698 * PROCID <-- pid
699 * This is a relatively straightforward interpretation from
700 * RFC5424, sect. 6.2.6.
55f5bc66 701 * MSGID <-- msgid (from --msgid)
4826184b
RG
702 * One may argue that the string "logger" would be better suited
703 * here so that a receiver can identify the sender process.
704 * However, this does not sound like a good match to RFC5424,
55f5bc66 705 * sect. 6.2.7.
4826184b
RG
706 * Note that appendix A.1 of RFC5424 does not provide clear guidance
707 * of how these fields should be used. This is the case because the
708 * IETF working group couldn't arrive at a clear agreement when we
709 * specified RFC5424. The rest of the field mappings should be
710 * pretty clear from RFC5424. -- Rainer Gerhards, 2015-03-10
711 */
2b3f40c5 712static void syslog_rfc5424_header(struct logger_ctl *const ctl)
4de2e8a0 713{
9a13f968
SK
714 char *time;
715 char *hostname;
716 char *const app_name = ctl->tag;
717 char *procid;
718 char *const msgid = xstrdup(ctl->msgid ? ctl->msgid : NILVALUE);
4299ed1c
KZ
719 char *structured = NULL;
720 struct list_head *sd;
9a13f968 721
4de2e8a0
SK
722 if (ctl->fd < 0)
723 return;
852feb72 724
4de2e8a0 725 if (ctl->rfc5424_time) {
4826184b
RG
726 struct timeval tv;
727 struct tm *tm;
9a13f968 728
ef5fb280 729 logger_gettimeofday(&tv, NULL);
4de2e8a0 730 if ((tm = localtime(&tv.tv_sec)) != NULL) {
852feb72 731 char fmt[64];
2f267611 732 const size_t i = strftime(fmt, sizeof(fmt),
9a13f968 733 "%Y-%m-%dT%H:%M:%S.%%06u%z ", tm);
2f267611 734 /* patch TZ info to comply with RFC3339 (we left SP at end) */
9a13f968
SK
735 fmt[i - 1] = fmt[i - 2];
736 fmt[i - 2] = fmt[i - 3];
737 fmt[i - 3] = ':';
4826184b 738 xasprintf(&time, fmt, tv.tv_usec);
4de2e8a0
SK
739 } else
740 err(EXIT_FAILURE, _("localtime() failed"));
4826184b 741 } else
7ff6948e 742 time = xstrdup(NILVALUE);
852feb72 743
4de2e8a0 744 if (ctl->rfc5424_host) {
ef5fb280 745 if (!(hostname = logger_xgethostname()))
d5f93061 746 hostname = xstrdup(NILVALUE);
4de2e8a0
SK
747 /* Arbitrary looking 'if (var < strlen()) checks originate from
748 * RFC 5424 - 6 Syslog Message Format definition. */
749 if (255 < strlen(hostname))
750 errx(EXIT_FAILURE, _("hostname '%s' is too long"),
751 hostname);
4826184b 752 } else
7ff6948e 753 hostname = xstrdup(NILVALUE);
852feb72 754
2b3f40c5
RG
755 if (48 < strlen(ctl->tag))
756 errx(EXIT_FAILURE, _("tag '%s' is too long"), ctl->tag);
852feb72 757
59c6ac0b 758 if (ctl->pid)
4826184b
RG
759 xasprintf(&procid, "%d", ctl->pid);
760 else
7ff6948e 761 procid = xstrdup(NILVALUE);
4826184b 762
4299ed1c
KZ
763 sd = get_reserved_structured_data(ctl);
764
765 /* time quality structured data (maybe overwriten by --sd-id timeQuality) */
766 if (ctl->rfc5424_tq && !has_structured_data_id(sd, "timeQuality")) {
767
768 add_structured_data_id(sd, "timeQuality");
769 add_structured_data_param(sd, "tzKnown=\"1\"");
770
7d3a07d8
KZ
771#ifdef HAVE_NTP_GETTIME
772 struct ntptimeval ntptv;
9a13f968 773
4299ed1c
KZ
774 if (ntp_gettime(&ntptv) == TIME_OK) {
775 add_structured_data_param(sd, "isSynced=\"1\"");
776 add_structured_data_paramf(sd, "syncAccuracy=\"%ld\"", ntptv.maxerror);
777 } else
87ee2658 778#endif
4299ed1c
KZ
779 add_structured_data_paramf(sd, "isSynced=\"0\"");
780 }
781
782 /* convert all structured data to string */
783 structured = get_structured_data_string(ctl);
784 if (!structured)
785 structured = xstrdup(NILVALUE);
852feb72 786
4826184b
RG
787 xasprintf(&ctl->hdr, "<%d>1 %s %s %s %s %s %s ",
788 ctl->pri,
789 time,
790 hostname,
791 app_name,
792 procid,
793 msgid,
4299ed1c 794 structured);
852feb72 795
4826184b 796 free(time);
852feb72 797 free(hostname);
4826184b
RG
798 /* app_name points to ctl->tag, do NOT free! */
799 free(procid);
800 free(msgid);
4299ed1c 801 free(structured);
4de2e8a0
SK
802}
803
804static void parse_rfc5424_flags(struct logger_ctl *ctl, char *optarg)
805{
806 char *in, *tok;
807
808 in = optarg;
809 while ((tok = strtok(in, ","))) {
810 in = NULL;
811 if (!strcmp(tok, "notime")) {
812 ctl->rfc5424_time = 0;
813 ctl->rfc5424_tq = 0;
814 } else if (!strcmp(tok, "notq"))
815 ctl->rfc5424_tq = 0;
816 else if (!strcmp(tok, "nohost"))
817 ctl->rfc5424_host = 0;
818 else
819 warnx(_("ignoring unknown option argument: %s"), tok);
820 }
821}
822
d77dc29e
SK
823static int parse_unix_socket_errors_flags(char *optarg)
824{
825 if (!strcmp(optarg, "off"))
826 return AF_UNIX_ERRORS_OFF;
827 if (!strcmp(optarg, "on"))
828 return AF_UNIX_ERRORS_ON;
829 if (!strcmp(optarg, "auto"))
830 return AF_UNIX_ERRORS_AUTO;
831 warnx(_("invalid argument: %s: using automatic errors"), optarg);
832 return AF_UNIX_ERRORS_AUTO;
833}
834
2b3f40c5 835static void syslog_local_header(struct logger_ctl *const ctl)
cfa77d26 836{
3070ca77 837 char pid[32];
1d575033 838
59c6ac0b
KZ
839 if (ctl->pid)
840 snprintf(pid, sizeof(pid), "[%d]", ctl->pid);
1d575033
SK
841 else
842 pid[0] = '\0';
843
2b3f40c5
RG
844 xasprintf(&ctl->hdr, "<%d>%s %s%s: ", ctl->pri, rfc3164_current_time(),
845 ctl->tag, pid);
846}
847
848static void generate_syslog_header(struct logger_ctl *const ctl)
849{
850 free(ctl->hdr);
851 ctl->syslogfp(ctl);
cfa77d26
SK
852}
853
c68a1cb4
SK
854static void logger_open(struct logger_ctl *ctl)
855{
856 if (ctl->server) {
fc20393c 857 ctl->fd = inet_socket(ctl->server, ctl->port, &ctl->socket_type);
4de2e8a0 858 if (!ctl->syslogfp)
2b3f40c5 859 ctl->syslogfp = syslog_rfc5424_header;
4a8919a4
PP
860 } else {
861 if (!ctl->unix_socket)
862 ctl->unix_socket = _PATH_DEVLOG;
7dc20804 863
fc20393c 864 ctl->fd = unix_socket(ctl, ctl->unix_socket, &ctl->socket_type);
4a8919a4
PP
865 if (!ctl->syslogfp)
866 ctl->syslogfp = syslog_local_header;
867 }
9a13f968
SK
868 if (!ctl->tag)
869 ctl->tag = xgetlogin();
2b3f40c5 870 generate_syslog_header(ctl);
c68a1cb4
SK
871}
872
46ee14df 873static void logger_command_line(const struct logger_ctl *ctl, char **argv)
c68a1cb4 874{
2b3f40c5
RG
875 /* note: we never re-generate the syslog header here, even if we
876 * generate multiple messages. If so, we think it is the right thing
877 * to do to report them with the same timestamp, as the user actually
878 * intended to send a single message.
879 */
f68b8aa7 880 char *const buf = xmalloc(ctl->max_message_size + 1);
c68a1cb4 881 char *p = buf;
f68b8aa7 882 const char *endp = buf + ctl->max_message_size - 1;
c68a1cb4
SK
883 size_t len;
884
885 while (*argv) {
886 len = strlen(*argv);
887 if (endp < p + len && p != buf) {
2b3f40c5 888 write_output(ctl, buf);
c68a1cb4
SK
889 p = buf;
890 }
f68b8aa7
RG
891 if (ctl->max_message_size < len) {
892 (*argv)[ctl->max_message_size] = '\0'; /* truncate */
2b3f40c5 893 write_output(ctl, *argv++);
c68a1cb4
SK
894 continue;
895 }
896 if (p != buf)
897 *p++ = ' ';
898 memmove(p, *argv++, len);
899 *(p += len) = '\0';
900 }
901 if (p != buf)
2b3f40c5 902 write_output(ctl, buf);
c3dd2ecd 903 free(buf);
c68a1cb4
SK
904}
905
906static void logger_stdin(struct logger_ctl *ctl)
907{
7db029e5
KZ
908 /* note: we re-generate the the syslog header for each log message to
909 * update header timestamps and to reflect possible priority changes.
910 * The initial header is generated by logger_open().
911 */
912 int has_header = 1;
c68a1cb4 913 int default_priority = ctl->pri;
b9ef27f5
RG
914 int last_pri = default_priority;
915 size_t max_usrmsg_size = ctl->max_message_size - strlen(ctl->hdr);
916 char *const buf = xmalloc(max_usrmsg_size + 2 + 2);
917 int pri;
918 int c;
919 size_t i;
920
921 c = getchar();
922 while (c != EOF) {
923 i = 0;
924 if (ctl->prio_prefix) {
925 if (c == '<') {
926 pri = 0;
927 buf[i++] = c;
9a13f968
SK
928 while (isdigit(c = getchar()) && pri <= 191) {
929 buf[i++] = c;
930 pri = pri * 10 + c - '0';
b9ef27f5
RG
931 }
932 if (c != EOF && c != '\n')
933 buf[i++] = c;
934 if (c == '>' && 0 <= pri && pri <= 191) { /* valid RFC PRI values */
935 i = 0;
936 if (pri < 8)
937 pri |= 8; /* kern facility is forbidden */
938 ctl->pri = pri;
939 } else
940 ctl->pri = default_priority;
941
942 if (ctl->pri != last_pri) {
7db029e5 943 has_header = 0;
b9ef27f5
RG
944 max_usrmsg_size = ctl->max_message_size - strlen(ctl->hdr);
945 last_pri = ctl->pri;
946 }
947 if (c != EOF && c != '\n')
948 c = getchar();
949 }
950 }
951
952 while (c != EOF && c != '\n' && i < max_usrmsg_size) {
953 buf[i++] = c;
954 c = getchar();
955 }
956 buf[i] = '\0';
957
7db029e5
KZ
958 if (i > 0 || !ctl->skip_empty_lines) {
959 if (!has_header)
960 generate_syslog_header(ctl);
ae6846b8 961 write_output(ctl, buf);
7db029e5
KZ
962 has_header = 0;
963 }
b9ef27f5 964
9a13f968 965 if (c == '\n') /* discard line terminator */
b9ef27f5 966 c = getchar();
c68a1cb4
SK
967 }
968}
969
46ee14df 970static void logger_close(const struct logger_ctl *ctl)
c68a1cb4 971{
9b83e7a4 972 if (ctl->fd != -1 && close(ctl->fd) != 0)
1d575033 973 err(EXIT_FAILURE, _("close failed"));
2b3f40c5 974 free(ctl->hdr);
c68a1cb4
SK
975}
976
b363e86d
SK
977static void __attribute__ ((__noreturn__)) usage(FILE *out)
978{
925aa9e8 979 fputs(USAGE_HEADER, out);
4ce393f4 980 fprintf(out, _(" %s [options] [<message>]\n"), program_invocation_short_name);
2da49186 981
451dbcfa
BS
982 fputs(USAGE_SEPARATOR, out);
983 fputs(_("Enter messages into the system log.\n"), out);
984
925aa9e8 985 fputs(USAGE_OPTIONS, out);
3f51c10b
SK
986 fputs(_(" -i log the logger command's PID\n"), out);
987 fputs(_(" --id[=<id>] log the given <id>, or otherwise the PID\n"), out);
d0e875ff 988 fputs(_(" -f, --file <file> log the contents of this file\n"), out);
ae6846b8 989 fputs(_(" -e, --skip-empty do not log empty lines when processing files\n"), out);
fd343a05 990 fputs(_(" --no-act do everything except the write the log\n"), out);
d0e875ff 991 fputs(_(" -p, --priority <prio> mark given message with this priority\n"), out);
b6b67955 992 fputs(_(" --octet-count use rfc6587 octet counting\n"), out);
d0e875ff
KZ
993 fputs(_(" --prio-prefix look for a prefix on every line read from stdin\n"), out);
994 fputs(_(" -s, --stderr output message to standard error as well\n"), out);
f68b8aa7 995 fputs(_(" -S, --size <size> maximum size for a single message\n"), out);
d0e875ff
KZ
996 fputs(_(" -t, --tag <tag> mark every line with this tag\n"), out);
997 fputs(_(" -n, --server <name> write to this remote syslog server\n"), out);
998 fputs(_(" -P, --port <number> use this UDP port\n"), out);
999 fputs(_(" -T, --tcp use TCP only\n"), out);
1000 fputs(_(" -d, --udp use UDP only\n"), out);
1001 fputs(_(" --rfc3164 use the obsolete BSD syslog protocol\n"), out);
2cb40465 1002 fputs(_(" --rfc5424[=<snip>] use the syslog protocol (the default for remote);\n"
d0e875ff 1003 " <snip> can be notime, or notq, and/or nohost\n"), out);
4299ed1c
KZ
1004 fputs(_(" --sd-id <id> rfc5424 structured data ID\n"), out);
1005 fputs(_(" --sd-param <data> rfc5424 structured data name=value\n"), out);
8fce3924 1006 fputs(_(" --msgid <msgid> set rfc5424 message id field\n"), out);
d0e875ff 1007 fputs(_(" -u, --socket <socket> write to this Unix socket\n"), out);
d77dc29e
SK
1008 fputs(_(" --socket-errors[=<on|off|auto>]\n"
1009 " print connection errors when using Unix sockets\n"), out);
ebff016a 1010#ifdef HAVE_LIBSYSTEMD
4b670c01
SK
1011 fputs(_(" --journald[=<file>] write journald entry\n"), out);
1012#endif
925aa9e8
KZ
1013
1014 fputs(USAGE_SEPARATOR, out);
1015 fputs(USAGE_HELP, out);
1016 fputs(USAGE_VERSION, out);
1017 fprintf(out, USAGE_MAN_TAIL("logger(1)"));
b363e86d
SK
1018
1019 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
1020}
1021
6dbe3af9
KZ
1022/*
1023 * logger -- read and log utility
1024 *
1025 * Reads from an input and arranges to write the result on the system
1026 * log.
1027 */
195c3603
KZ
1028int main(int argc, char **argv)
1029{
cfa77d26
SK
1030 struct logger_ctl ctl = {
1031 .fd = -1,
59c6ac0b 1032 .pid = 0,
d0b6c4bf 1033 .pri = LOG_USER | LOG_NOTICE,
c68a1cb4 1034 .prio_prefix = 0,
cfa77d26 1035 .tag = NULL,
c68a1cb4 1036 .unix_socket = NULL,
d77dc29e 1037 .unix_socket_errors = 0,
c68a1cb4
SK
1038 .server = NULL,
1039 .port = NULL,
2b3f40c5 1040 .hdr = NULL,
55f5bc66 1041 .msgid = NULL,
4de2e8a0 1042 .socket_type = ALL_TYPES,
f68b8aa7 1043 .max_message_size = 1024,
4de2e8a0
SK
1044 .rfc5424_time = 1,
1045 .rfc5424_tq = 1,
1046 .rfc5424_host = 1,
ae6846b8 1047 .skip_empty_lines = 0
cfa77d26 1048 };
c68a1cb4 1049 int ch;
3d9f4b1d 1050 int stdout_reopened = 0;
d77dc29e 1051 int unix_socket_errors_mode = AF_UNIX_ERRORS_AUTO;
ebff016a 1052#ifdef HAVE_LIBSYSTEMD
4b670c01
SK
1053 FILE *jfd = NULL;
1054#endif
b363e86d 1055 static const struct option longopts[] = {
9a13f968
SK
1056 { "id", optional_argument, 0, OPT_ID },
1057 { "stderr", no_argument, 0, 's' },
1058 { "file", required_argument, 0, 'f' },
fd343a05 1059 { "no-act", no_argument, 0, OPT_NOACT, },
9a13f968
SK
1060 { "priority", required_argument, 0, 'p' },
1061 { "tag", required_argument, 0, 't' },
1062 { "socket", required_argument, 0, 'u' },
d77dc29e 1063 { "socket-errors", required_argument, 0, OPT_SOCKET_ERRORS },
9a13f968
SK
1064 { "udp", no_argument, 0, 'd' },
1065 { "tcp", no_argument, 0, 'T' },
1066 { "server", required_argument, 0, 'n' },
1067 { "port", required_argument, 0, 'P' },
1068 { "version", no_argument, 0, 'V' },
1069 { "help", no_argument, 0, 'h' },
b6b67955 1070 { "octet-count", no_argument, 0, OPT_OCTET_COUNT },
9a13f968
SK
1071 { "prio-prefix", no_argument, 0, OPT_PRIO_PREFIX },
1072 { "rfc3164", no_argument, 0, OPT_RFC3164 },
1073 { "rfc5424", optional_argument, 0, OPT_RFC5424 },
1074 { "size", required_argument, 0, 'S' },
1075 { "msgid", required_argument, 0, OPT_MSGID },
1076 { "skip-empty", no_argument, 0, 'e' },
4299ed1c
KZ
1077 { "sd-id", required_argument, 0, OPT_STRUCTURED_DATA_ID },
1078 { "sd-param", required_argument, 0, OPT_STRUCTURED_DATA_PARAM },
ebff016a 1079#ifdef HAVE_LIBSYSTEMD
9a13f968 1080 { "journald", optional_argument, 0, OPT_JOURNALD },
4b670c01 1081#endif
9a13f968 1082 { NULL, 0, 0, 0 }
b363e86d 1083 };
7eda085c
KZ
1084
1085 setlocale(LC_ALL, "");
1086 bindtextdomain(PACKAGE, LOCALEDIR);
1087 textdomain(PACKAGE);
c05a80ca 1088 atexit(close_stdout);
6dbe3af9 1089
4299ed1c
KZ
1090 INIT_LIST_HEAD(&ctl.user_sds);
1091 INIT_LIST_HEAD(&ctl.reserved_sds);
1092
ae6846b8 1093 while ((ch = getopt_long(argc, argv, "ef:ip:S:st:u:dTn:P:Vh",
49999d6a 1094 longopts, NULL)) != -1) {
98920f80 1095 switch (ch) {
6dbe3af9 1096 case 'f': /* file to log */
49999d6a 1097 if (freopen(optarg, "r", stdin) == NULL)
3d9f4b1d
SK
1098 err(EXIT_FAILURE, _("file %s"), optarg);
1099 stdout_reopened = 1;
6dbe3af9 1100 break;
ae6846b8
RG
1101 case 'e':
1102 ctl.skip_empty_lines = 1;
1103 break;
6dbe3af9 1104 case 'i': /* log process id also */
ef5fb280 1105 ctl.pid = logger_getpid();
3f51c10b
SK
1106 break;
1107 case OPT_ID:
aab5b444 1108 if (optarg) {
e598686d
KZ
1109 const char *p = optarg;
1110
1111 if (*p == '=')
1112 p++;
59c6ac0b
KZ
1113 ctl.pid = strtoul_or_err(optarg, _("failed to parse id"));
1114 } else
ef5fb280 1115 ctl.pid = logger_getpid();
6dbe3af9
KZ
1116 break;
1117 case 'p': /* priority */
cfa77d26 1118 ctl.pri = pencode(optarg);
6dbe3af9
KZ
1119 break;
1120 case 's': /* log to standard error */
35d36197 1121 ctl.stderr_printout = 1;
6dbe3af9
KZ
1122 break;
1123 case 't': /* tag */
cfa77d26 1124 ctl.tag = optarg;
6dbe3af9 1125 break;
7eda085c 1126 case 'u': /* unix socket */
c68a1cb4 1127 ctl.unix_socket = optarg;
7eda085c 1128 break;
f68b8aa7
RG
1129 case 'S': /* max message size */
1130 ctl.max_message_size = strtosize_or_err(optarg,
1131 _("failed to parse message size"));
1132 break;
66ee8158 1133 case 'd':
c68a1cb4 1134 ctl.socket_type = TYPE_UDP;
68265d07
SK
1135 break;
1136 case 'T':
c68a1cb4 1137 ctl.socket_type = TYPE_TCP;
66ee8158 1138 break;
68265d07 1139 case 'n':
c68a1cb4 1140 ctl.server = optarg;
912d6b98 1141 break;
68265d07 1142 case 'P':
c68a1cb4 1143 ctl.port = optarg;
912d6b98 1144 break;
b363e86d 1145 case 'V':
e421313d 1146 printf(UTIL_LINUX_VERSION);
b363e86d
SK
1147 exit(EXIT_SUCCESS);
1148 case 'h':
1149 usage(stdout);
b6b67955
AB
1150 case OPT_OCTET_COUNT:
1151 ctl.octet_count = 1;
1152 break;
98920f80 1153 case OPT_PRIO_PREFIX:
c68a1cb4 1154 ctl.prio_prefix = 1;
98920f80 1155 break;
4de2e8a0 1156 case OPT_RFC3164:
2b3f40c5 1157 ctl.syslogfp = syslog_rfc3164_header;
4de2e8a0
SK
1158 break;
1159 case OPT_RFC5424:
2b3f40c5 1160 ctl.syslogfp = syslog_rfc5424_header;
4de2e8a0
SK
1161 if (optarg)
1162 parse_rfc5424_flags(&ctl, optarg);
1163 break;
55f5bc66 1164 case OPT_MSGID:
9a13f968 1165 if (strchr(optarg, ' '))
8fce3924 1166 errx(EXIT_FAILURE, _("--msgid cannot contain space"));
55f5bc66
RG
1167 ctl.msgid = optarg;
1168 break;
ebff016a 1169#ifdef HAVE_LIBSYSTEMD
4b670c01
SK
1170 case OPT_JOURNALD:
1171 if (optarg) {
1172 jfd = fopen(optarg, "r");
1173 if (!jfd)
1174 err(EXIT_FAILURE, _("cannot open %s"),
1175 optarg);
1176 } else
1177 jfd = stdin;
1178 break;
1179#endif
d77dc29e
SK
1180 case OPT_SOCKET_ERRORS:
1181 unix_socket_errors_mode = parse_unix_socket_errors_flags(optarg);
1182 break;
fd343a05
KZ
1183 case OPT_NOACT:
1184 ctl.noact = 1;
1185 break;
4299ed1c
KZ
1186 case OPT_STRUCTURED_DATA_ID:
1187 if (!valid_structured_data_id(optarg))
1188 errx(EXIT_FAILURE, _("invalid structured data ID: '%s'"), optarg);
1189 add_structured_data_id(get_user_structured_data(&ctl), optarg);
1190 break;
1191 case OPT_STRUCTURED_DATA_PARAM:
1192 if (!valid_structured_data_param(optarg))
1193 errx(EXIT_FAILURE, _("invalid structured data parameter: '%s'"), optarg);
1194 add_structured_data_param(get_user_structured_data(&ctl), optarg);
1195 break;
6dbe3af9
KZ
1196 case '?':
1197 default:
b363e86d 1198 usage(stderr);
6dbe3af9 1199 }
49999d6a 1200 }
6dbe3af9
KZ
1201 argc -= optind;
1202 argv += optind;
3d9f4b1d
SK
1203 if (stdout_reopened && argc)
1204 warnx(_("--file <file> and <message> are mutually exclusive, message is ignored"));
ebff016a 1205#ifdef HAVE_LIBSYSTEMD
4b670c01 1206 if (jfd) {
fd343a05 1207 int ret = journald_entry(&ctl, jfd);
4b670c01
SK
1208 if (stdin != jfd)
1209 fclose(jfd);
047e2888 1210 if (ret)
54fefa07 1211 errx(EXIT_FAILURE, _("journald entry could not be written"));
047e2888 1212 return EXIT_SUCCESS;
4b670c01
SK
1213 }
1214#endif
4299ed1c
KZ
1215
1216 /* user overwrites build-in SD-ELEMENT */
1217 if (has_structured_data_id(get_user_structured_data(&ctl), "timeQuality"))
1218 ctl.rfc5424_tq = 0;
1219
d77dc29e
SK
1220 switch (unix_socket_errors_mode) {
1221 case AF_UNIX_ERRORS_OFF:
1222 ctl.unix_socket_errors = 0;
1223 break;
1224 case AF_UNIX_ERRORS_ON:
1225 ctl.unix_socket_errors = 1;
1226 break;
1227 case AF_UNIX_ERRORS_AUTO:
bcf7e149 1228 ctl.unix_socket_errors = ctl.noact || ctl.stderr_printout;
d77dc29e 1229#ifdef HAVE_LIBSYSTEMD
bcf7e149 1230 ctl.unix_socket_errors |= !!sd_booted();
d77dc29e
SK
1231#endif
1232 break;
1233 default:
1234 abort();
1235 }
c68a1cb4
SK
1236 logger_open(&ctl);
1237 if (0 < argc)
1238 logger_command_line(&ctl, argv);
7eda085c 1239 else
c68a1cb4
SK
1240 /* Note. --file <arg> reopens stdin making the below
1241 * function to be used for file inputs. */
1242 logger_stdin(&ctl);
1243 logger_close(&ctl);
49999d6a 1244 return EXIT_SUCCESS;
6dbe3af9 1245}