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