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