]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/logger.c
wall: do not use a temporary file.
[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>
b363e86d 54
633493be 55#include "all-io.h"
b363e86d 56#include "c.h"
c05a80ca 57#include "closestream.h"
7eda085c 58#include "nls.h"
52a49e9a 59#include "pathnames.h"
b363e86d 60#include "strutils.h"
4b670c01 61#include "xalloc.h"
6dbe3af9
KZ
62
63#define SYSLOG_NAMES
64#include <syslog.h>
65
ebff016a 66#ifdef HAVE_LIBSYSTEMD
d77dc29e 67# include <systemd/sd-daemon.h>
4b670c01
SK
68# include <systemd/sd-journal.h>
69#endif
70
87ee2658
ST
71#ifdef HAVE_SYS_TIMEX_H
72# include <sys/timex.h>
73#endif
74
68265d07
SK
75enum {
76 TYPE_UDP = (1 << 1),
77 TYPE_TCP = (1 << 2),
78 ALL_TYPES = TYPE_UDP | TYPE_TCP
79};
80
d77dc29e
SK
81enum {
82 AF_UNIX_ERRORS_OFF = 0,
83 AF_UNIX_ERRORS_ON,
84 AF_UNIX_ERRORS_AUTO
85};
86
98920f80 87enum {
4b670c01 88 OPT_PRIO_PREFIX = CHAR_MAX + 1,
4de2e8a0
SK
89 OPT_JOURNALD,
90 OPT_RFC3164,
d77dc29e 91 OPT_RFC5424,
3f51c10b 92 OPT_SOCKET_ERRORS,
55f5bc66 93 OPT_MSGID,
fd343a05 94 OPT_NOACT,
3f51c10b 95 OPT_ID
98920f80
DJ
96};
97
cfa77d26
SK
98struct logger_ctl {
99 int fd;
cfa77d26 100 int pri;
59c6ac0b 101 pid_t pid; /* zero when unwanted */
2b3f40c5 102 char *hdr; /* the syslog header (based on protocol) */
cfa77d26 103 char *tag;
55f5bc66 104 char *msgid;
c95d3209 105 char *unix_socket; /* -u <path> or default to _PATH_DEVLOG */
c68a1cb4
SK
106 char *server;
107 char *port;
108 int socket_type;
f68b8aa7 109 size_t max_message_size;
2b3f40c5 110 void (*syslogfp)(struct logger_ctl *ctl);
4de2e8a0 111 unsigned int
d77dc29e 112 unix_socket_errors:1, /* whether to report or not errors */
fd343a05 113 noact:1, /* do not write to sockets */
d77dc29e
SK
114 prio_prefix:1, /* read priority from intput */
115 stderr_printout:1, /* output message to stderr */
116 rfc5424_time:1, /* include time stamp */
117 rfc5424_tq:1, /* include time quality markup */
ae6846b8
RG
118 rfc5424_host:1, /* include hostname */
119 skip_empty_lines:1; /* do not send empty lines when processing files */
cfa77d26
SK
120};
121
ef5fb280
KZ
122/*
123 * For tests we want to be able to control datetime outputs
124 */
125#ifdef TEST_LOGGER
126static inline int logger_gettimeofday(struct timeval *tv, struct timezone *tz)
127{
128 char *str = getenv("LOGGER_TEST_TIMEOFDAY");
129 uintmax_t sec, usec;
130
131 if (str && sscanf(str, "%ju.%ju", &sec, &usec) == 2) {
132 tv->tv_sec = sec;
133 tv->tv_usec = usec;
98e90a49 134 return tv->tv_sec >= 0 && tv->tv_usec >= 0 ? 0 : -EINVAL;
ef5fb280
KZ
135 }
136
137 return gettimeofday(tv, tz);
138}
139
140static inline char *logger_xgethostname(void)
141{
142 char *str = getenv("LOGGER_TEST_HOSTNAME");
143 return str ? xstrdup(str) : xgethostname();
144}
145
146static inline pid_t logger_getpid(void)
147{
148 char *str = getenv("LOGGER_TEST_GETPID");
149 unsigned int pid;
150
151 if (str && sscanf(str, "%u", &pid) == 1)
152 return pid;
153 return getpid();
154}
155
156
157#undef HAVE_NTP_GETTIME /* force to default non-NTP */
158
159#else /* !TEST_LOGGER */
160# define logger_gettimeofday(x, y) gettimeofday(x, y)
161# define logger_xgethostname xgethostname
162# define logger_getpid getpid
163#endif
164
165
46ee14df 166static int decode(const char *name, CODE *codetab)
82054b1d
DR
167{
168 register CODE *c;
169
4d7d1af6
SK
170 if (name == NULL || *name == '\0')
171 return -1;
172 if (isdigit(*name)) {
173 int num;
174 char *end = NULL;
175
8d341322 176 errno = 0;
4d7d1af6
SK
177 num = strtol(name, &end, 10);
178 if (errno || name == end || (end && *end))
179 return -1;
180 for (c = codetab; c->c_name; c++)
181 if (num == c->c_val)
182 return num;
183 return -1;
184 }
82054b1d
DR
185 for (c = codetab; c->c_name; c++)
186 if (!strcasecmp(name, c->c_name))
187 return (c->c_val);
188
189 return -1;
190}
191
192static int pencode(char *s)
193{
2e0fd22d
SK
194 int facility, level;
195 char *separator;
196
197 separator = strchr(s, '.');
198 if (separator) {
199 *separator = '\0';
200 facility = decode(s, facilitynames);
201 if (facility < 0)
202 errx(EXIT_FAILURE, _("unknown facility name: %s"), s);
203 s = ++separator;
204 } else
205 facility = LOG_USER;
206 level = decode(s, prioritynames);
207 if (level < 0)
208 errx(EXIT_FAILURE, _("unknown priority name: %s"), s);
9a13f968
SK
209 if (facility == LOG_KERN)
210 facility = LOG_USER; /* kern is forbidden */
2e0fd22d 211 return ((level & LOG_PRIMASK) | (facility & LOG_FACMASK));
82054b1d
DR
212}
213
d77dc29e 214static int unix_socket(struct logger_ctl *ctl, const char *path, const int socket_type)
fe6999da 215{
68265d07 216 int fd, i;
fe6999da 217 static struct sockaddr_un s_addr; /* AF_UNIX address of local logger */
7eda085c 218
68265d07
SK
219 if (strlen(path) >= sizeof(s_addr.sun_path))
220 errx(EXIT_FAILURE, _("openlog %s: pathname too long"), path);
7eda085c 221
fe6999da 222 s_addr.sun_family = AF_UNIX;
68265d07
SK
223 strcpy(s_addr.sun_path, path);
224
225 for (i = 2; i; i--) {
226 int st = -1;
49999d6a 227
68265d07
SK
228 if (i == 2 && socket_type & TYPE_UDP)
229 st = SOCK_DGRAM;
230 if (i == 1 && socket_type & TYPE_TCP)
231 st = SOCK_STREAM;
232 if (st == -1 || (fd = socket(AF_UNIX, st, 0)) == -1)
233 continue;
fe6999da
SK
234 if (connect(fd, (struct sockaddr *)&s_addr, sizeof(s_addr)) == -1) {
235 close(fd);
68265d07 236 continue;
fe6999da 237 }
68265d07 238 break;
fe6999da 239 }
7eda085c 240
d77dc29e
SK
241 if (i == 0) {
242 if (ctl->unix_socket_errors)
243 err(EXIT_FAILURE, _("socket %s"), path);
244 else
245 /* See --socket-errors manual page entry for
246 * explanation of this strange exit. */
247 exit(EXIT_SUCCESS);
248 }
fe6999da 249 return fd;
7eda085c
KZ
250}
251
195c3603
KZ
252static int inet_socket(const char *servername, const char *port,
253 const int socket_type)
68265d07
SK
254{
255 int fd, errcode, i;
24f4db69 256 struct addrinfo hints, *res;
68265d07
SK
257 const char *p = port;
258
259 for (i = 2; i; i--) {
260 memset(&hints, 0, sizeof(hints));
261 if (i == 2 && socket_type & TYPE_UDP) {
262 hints.ai_socktype = SOCK_DGRAM;
263 if (port == NULL)
264 p = "syslog";
265 }
266 if (i == 1 && socket_type & TYPE_TCP) {
267 hints.ai_socktype = SOCK_STREAM;
268 if (port == NULL)
269 p = "syslog-conn";
270 }
271 if (hints.ai_socktype == 0)
272 continue;
273 hints.ai_family = AF_UNSPEC;
274 errcode = getaddrinfo(servername, p, &hints, &res);
275 if (errcode != 0)
4ce393f4 276 errx(EXIT_FAILURE, _("failed to resolve name %s port %s: %s"),
68265d07
SK
277 servername, p, gai_strerror(errcode));
278 if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) {
279 freeaddrinfo(res);
280 continue;
281 }
282 if (connect(fd, res->ai_addr, res->ai_addrlen) == -1) {
283 freeaddrinfo(res);
284 close(fd);
285 continue;
286 }
24f4db69 287
68265d07
SK
288 freeaddrinfo(res);
289 break;
290 }
912d6b98 291
68265d07 292 if (i == 0)
4ce393f4 293 errx(EXIT_FAILURE, _("failed to connect to %s port %s"), servername, p);
49999d6a 294
912d6b98
WJE
295 return fd;
296}
68265d07 297
ebff016a 298#ifdef HAVE_LIBSYSTEMD
fd343a05 299static int journald_entry(struct logger_ctl *ctl, FILE *fp)
4b670c01
SK
300{
301 struct iovec *iovec;
302 char *buf = NULL;
303 ssize_t sz;
fd343a05 304 int n, lines, vectors = 8, ret = 0;
4b670c01
SK
305 size_t dummy = 0;
306
307 iovec = xmalloc(vectors * sizeof(struct iovec));
308 for (lines = 0; /* nothing */ ; lines++) {
309 buf = NULL;
310 sz = getline(&buf, &dummy, fp);
311 if (sz == -1)
312 break;
313 if (0 < sz && buf[sz - 1] == '\n') {
314 sz--;
315 buf[sz] = '\0';
316 }
317 if (lines == vectors) {
318 vectors *= 2;
047e2888
SK
319 if (IOV_MAX < vectors)
320 errx(EXIT_FAILURE, _("maximum input lines (%d) exceeded"), IOV_MAX);
4b670c01
SK
321 iovec = xrealloc(iovec, vectors * sizeof(struct iovec));
322 }
323 iovec[lines].iov_base = buf;
324 iovec[lines].iov_len = sz;
325 }
fd343a05
KZ
326
327 if (!ctl->noact)
328 ret = sd_journal_sendv(iovec, lines);
329 if (ctl->stderr_printout) {
330 for (n = 0; n < lines; n++)
331 fprintf(stderr, "%s\n", (char *) iovec[n].iov_base);
332 }
4b670c01
SK
333 for (n = 0; n < lines; n++)
334 free(iovec[n].iov_base);
335 free(iovec);
336 return ret;
337}
338#endif
339
46ee14df 340static char *xgetlogin(void)
019b9702
SK
341{
342 char *cp;
343 struct passwd *pw;
344
345 if (!(cp = getlogin()) || !*cp)
346 cp = (pw = getpwuid(geteuid()))? pw->pw_name : "<someone>";
347 return cp;
348}
349
3070ca77
RG
350/* this creates a timestamp based on current time according to the
351 * fine rules of RFC3164, most importantly it ensures in a portable
352 * way that the month day is correctly written (with a SP instead
353 * of a leading 0). The function uses a static buffer which is
354 * overwritten on the next call (just like ctime() does).
355 */
9a13f968 356static const char *rfc3164_current_time(void)
3070ca77
RG
357{
358 static char time[32];
359 struct timeval tv;
360 struct tm *tm;
9a13f968
SK
361 static char *monthnames[] = {
362 "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
363 "Sep", "Oct", "Nov", "Dec"
364 };
3070ca77 365
ef5fb280 366 logger_gettimeofday(&tv, NULL);
3070ca77
RG
367 tm = localtime(&tv.tv_sec);
368 snprintf(time, sizeof(time),"%s %2d %2.2d:%2.2d:%2.2d",
369 monthnames[tm->tm_mon], tm->tm_mday,
0f1c825b 370 tm->tm_hour, tm->tm_min, tm->tm_sec);
3070ca77
RG
371 return time;
372}
373
940a14a3
RG
374/* writes generated buffer to desired destination. For TCP syslog,
375 * we use RFC6587 octet-stuffing. This is not great, but doing
376 * full blown RFC5425 (TLS) looks like it is too much for the
377 * logger utility.
378 */
2b3f40c5 379static void write_output(const struct logger_ctl *ctl, const char *const msg)
4be84306 380{
2b3f40c5
RG
381 char *buf;
382 const size_t len = xasprintf(&buf, "%s%s", ctl->hdr, msg);
fd343a05
KZ
383
384 if (!ctl->noact) {
385 if (write_all(ctl->fd, buf, len) < 0)
9a13f968 386 warn(_("write failed"));
fd343a05
KZ
387 else if (ctl->socket_type == TYPE_TCP) {
388 /* using an additional write seems like the best compromise:
389 * - writev() is not yet supported by framework
390 * - adding the \n to the buffer in formatters violates layers
391 * - adding \n after the fact requires memory copy
392 * - logger is not a high-performance app
393 */
394 if (write_all(ctl->fd, "\n", 1) < 0)
395 warn(_("write failed"));
396 }
397 }
398
4be84306
RG
399 if (ctl->stderr_printout)
400 fprintf(stderr, "%s\n", buf);
401}
402
d5f93061 403#define NILVALUE "-"
2b3f40c5 404static void syslog_rfc3164_header(struct logger_ctl *const ctl)
195c3603 405{
d5f93061 406 char pid[30], *hostname;
d8b616c2 407
77c3bd5b 408 *pid = '\0';
cfa77d26 409 if (ctl->fd < 0)
c462a8ca 410 return;
59c6ac0b
KZ
411 if (ctl->pid)
412 snprintf(pid, sizeof(pid), "[%d]", ctl->pid);
852feb72 413
ef5fb280 414 if ((hostname = logger_xgethostname())) {
d5f93061
SK
415 char *dot = strchr(hostname, '.');
416 if (dot)
417 *dot = '\0';
418 } else
419 hostname = xstrdup(NILVALUE);
852feb72 420
2b3f40c5
RG
421 xasprintf(&ctl->hdr, "<%d>%.15s %s %.200s%s: ",
422 ctl->pri, rfc3164_current_time(), hostname, ctl->tag, pid);
852feb72
KZ
423
424 free(hostname);
7eda085c
KZ
425}
426
4826184b
RG
427/* Some field mappings may be controversal, thus I give the reason
428 * why this specific mapping was used:
429 * APP-NAME <-- tag
430 * Some may argue that "logger" is a better fit, but we think
431 * this is better inline of what other implementations do. In
432 * rsyslog, for example, the TAG value is populated from APP-NAME.
433 * PROCID <-- pid
434 * This is a relatively straightforward interpretation from
435 * RFC5424, sect. 6.2.6.
55f5bc66 436 * MSGID <-- msgid (from --msgid)
4826184b
RG
437 * One may argue that the string "logger" would be better suited
438 * here so that a receiver can identify the sender process.
439 * However, this does not sound like a good match to RFC5424,
55f5bc66 440 * sect. 6.2.7.
4826184b
RG
441 * Note that appendix A.1 of RFC5424 does not provide clear guidance
442 * of how these fields should be used. This is the case because the
443 * IETF working group couldn't arrive at a clear agreement when we
444 * specified RFC5424. The rest of the field mappings should be
445 * pretty clear from RFC5424. -- Rainer Gerhards, 2015-03-10
446 */
2b3f40c5 447static void syslog_rfc5424_header(struct logger_ctl *const ctl)
4de2e8a0 448{
9a13f968
SK
449 char *time;
450 char *hostname;
451 char *const app_name = ctl->tag;
452 char *procid;
453 char *const msgid = xstrdup(ctl->msgid ? ctl->msgid : NILVALUE);
454 char *structured_data;
455
4de2e8a0
SK
456 if (ctl->fd < 0)
457 return;
852feb72 458
4de2e8a0 459 if (ctl->rfc5424_time) {
4826184b
RG
460 struct timeval tv;
461 struct tm *tm;
9a13f968 462
ef5fb280 463 logger_gettimeofday(&tv, NULL);
4de2e8a0 464 if ((tm = localtime(&tv.tv_sec)) != NULL) {
852feb72 465 char fmt[64];
2f267611 466 const size_t i = strftime(fmt, sizeof(fmt),
9a13f968 467 "%Y-%m-%dT%H:%M:%S.%%06u%z ", tm);
2f267611 468 /* patch TZ info to comply with RFC3339 (we left SP at end) */
9a13f968
SK
469 fmt[i - 1] = fmt[i - 2];
470 fmt[i - 2] = fmt[i - 3];
471 fmt[i - 3] = ':';
4826184b 472 xasprintf(&time, fmt, tv.tv_usec);
4de2e8a0
SK
473 } else
474 err(EXIT_FAILURE, _("localtime() failed"));
4826184b 475 } else
7ff6948e 476 time = xstrdup(NILVALUE);
852feb72 477
4de2e8a0 478 if (ctl->rfc5424_host) {
ef5fb280 479 if (!(hostname = logger_xgethostname()))
d5f93061 480 hostname = xstrdup(NILVALUE);
4de2e8a0
SK
481 /* Arbitrary looking 'if (var < strlen()) checks originate from
482 * RFC 5424 - 6 Syslog Message Format definition. */
483 if (255 < strlen(hostname))
484 errx(EXIT_FAILURE, _("hostname '%s' is too long"),
485 hostname);
4826184b 486 } else
7ff6948e 487 hostname = xstrdup(NILVALUE);
852feb72 488
2b3f40c5
RG
489 if (48 < strlen(ctl->tag))
490 errx(EXIT_FAILURE, _("tag '%s' is too long"), ctl->tag);
852feb72 491
59c6ac0b 492 if (ctl->pid)
4826184b
RG
493 xasprintf(&procid, "%d", ctl->pid);
494 else
7ff6948e 495 procid = xstrdup(NILVALUE);
4826184b 496
4de2e8a0 497 if (ctl->rfc5424_tq) {
7d3a07d8
KZ
498#ifdef HAVE_NTP_GETTIME
499 struct ntptimeval ntptv;
9a13f968 500
4de2e8a0 501 if (ntp_gettime(&ntptv) == TIME_OK)
4826184b
RG
502 xasprintf(&structured_data,
503 "[timeQuality tzKnown=\"1\" isSynced=\"1\" syncAccuracy=\"%ld\"]",
4de2e8a0
SK
504 ntptv.maxerror);
505 else
87ee2658 506#endif
4826184b
RG
507 xasprintf(&structured_data,
508 "[timeQuality tzKnown=\"1\" isSynced=\"0\"]");
773df0fa
KZ
509 } else
510 structured_data = xstrdup(NILVALUE);
852feb72 511
4826184b
RG
512 xasprintf(&ctl->hdr, "<%d>1 %s %s %s %s %s %s ",
513 ctl->pri,
514 time,
515 hostname,
516 app_name,
517 procid,
518 msgid,
519 structured_data);
852feb72 520
4826184b 521 free(time);
852feb72 522 free(hostname);
4826184b
RG
523 /* app_name points to ctl->tag, do NOT free! */
524 free(procid);
525 free(msgid);
526 free(structured_data);
4de2e8a0
SK
527}
528
529static void parse_rfc5424_flags(struct logger_ctl *ctl, char *optarg)
530{
531 char *in, *tok;
532
533 in = optarg;
534 while ((tok = strtok(in, ","))) {
535 in = NULL;
536 if (!strcmp(tok, "notime")) {
537 ctl->rfc5424_time = 0;
538 ctl->rfc5424_tq = 0;
539 } else if (!strcmp(tok, "notq"))
540 ctl->rfc5424_tq = 0;
541 else if (!strcmp(tok, "nohost"))
542 ctl->rfc5424_host = 0;
543 else
544 warnx(_("ignoring unknown option argument: %s"), tok);
545 }
546}
547
d77dc29e
SK
548static int parse_unix_socket_errors_flags(char *optarg)
549{
550 if (!strcmp(optarg, "off"))
551 return AF_UNIX_ERRORS_OFF;
552 if (!strcmp(optarg, "on"))
553 return AF_UNIX_ERRORS_ON;
554 if (!strcmp(optarg, "auto"))
555 return AF_UNIX_ERRORS_AUTO;
556 warnx(_("invalid argument: %s: using automatic errors"), optarg);
557 return AF_UNIX_ERRORS_AUTO;
558}
559
2b3f40c5 560static void syslog_local_header(struct logger_ctl *const ctl)
cfa77d26 561{
3070ca77 562 char pid[32];
1d575033 563
59c6ac0b
KZ
564 if (ctl->pid)
565 snprintf(pid, sizeof(pid), "[%d]", ctl->pid);
1d575033
SK
566 else
567 pid[0] = '\0';
568
2b3f40c5
RG
569 xasprintf(&ctl->hdr, "<%d>%s %s%s: ", ctl->pri, rfc3164_current_time(),
570 ctl->tag, pid);
571}
572
573static void generate_syslog_header(struct logger_ctl *const ctl)
574{
575 free(ctl->hdr);
576 ctl->syslogfp(ctl);
cfa77d26
SK
577}
578
c68a1cb4
SK
579static void logger_open(struct logger_ctl *ctl)
580{
581 if (ctl->server) {
582 ctl->fd = inet_socket(ctl->server, ctl->port, ctl->socket_type);
4de2e8a0 583 if (!ctl->syslogfp)
2b3f40c5 584 ctl->syslogfp = syslog_rfc5424_header;
c68a1cb4
SK
585 return;
586 }
7dc20804
RG
587 if (!ctl->unix_socket)
588 ctl->unix_socket = _PATH_DEVLOG;
589
590 ctl->fd = unix_socket(ctl, ctl->unix_socket, ctl->socket_type);
591 if (!ctl->syslogfp)
2b3f40c5 592 ctl->syslogfp = syslog_local_header;
9a13f968
SK
593 if (!ctl->tag)
594 ctl->tag = xgetlogin();
2b3f40c5 595 generate_syslog_header(ctl);
c68a1cb4
SK
596}
597
46ee14df 598static void logger_command_line(const struct logger_ctl *ctl, char **argv)
c68a1cb4 599{
2b3f40c5
RG
600 /* note: we never re-generate the syslog header here, even if we
601 * generate multiple messages. If so, we think it is the right thing
602 * to do to report them with the same timestamp, as the user actually
603 * intended to send a single message.
604 */
f68b8aa7 605 char *const buf = xmalloc(ctl->max_message_size + 1);
c68a1cb4 606 char *p = buf;
f68b8aa7 607 const char *endp = buf + ctl->max_message_size - 1;
c68a1cb4
SK
608 size_t len;
609
610 while (*argv) {
611 len = strlen(*argv);
612 if (endp < p + len && p != buf) {
2b3f40c5 613 write_output(ctl, buf);
c68a1cb4
SK
614 p = buf;
615 }
f68b8aa7
RG
616 if (ctl->max_message_size < len) {
617 (*argv)[ctl->max_message_size] = '\0'; /* truncate */
2b3f40c5 618 write_output(ctl, *argv++);
c68a1cb4
SK
619 continue;
620 }
621 if (p != buf)
622 *p++ = ' ';
623 memmove(p, *argv++, len);
624 *(p += len) = '\0';
625 }
626 if (p != buf)
2b3f40c5 627 write_output(ctl, buf);
c68a1cb4
SK
628}
629
630static void logger_stdin(struct logger_ctl *ctl)
631{
c68a1cb4 632 int default_priority = ctl->pri;
b9ef27f5
RG
633 int last_pri = default_priority;
634 size_t max_usrmsg_size = ctl->max_message_size - strlen(ctl->hdr);
635 char *const buf = xmalloc(max_usrmsg_size + 2 + 2);
636 int pri;
637 int c;
638 size_t i;
639
640 c = getchar();
641 while (c != EOF) {
642 i = 0;
643 if (ctl->prio_prefix) {
644 if (c == '<') {
645 pri = 0;
646 buf[i++] = c;
9a13f968
SK
647 while (isdigit(c = getchar()) && pri <= 191) {
648 buf[i++] = c;
649 pri = pri * 10 + c - '0';
b9ef27f5
RG
650 }
651 if (c != EOF && c != '\n')
652 buf[i++] = c;
653 if (c == '>' && 0 <= pri && pri <= 191) { /* valid RFC PRI values */
654 i = 0;
655 if (pri < 8)
656 pri |= 8; /* kern facility is forbidden */
657 ctl->pri = pri;
658 } else
659 ctl->pri = default_priority;
660
661 if (ctl->pri != last_pri) {
662 generate_syslog_header(ctl);
663 max_usrmsg_size = ctl->max_message_size - strlen(ctl->hdr);
664 last_pri = ctl->pri;
665 }
666 if (c != EOF && c != '\n')
667 c = getchar();
668 }
669 }
670
671 while (c != EOF && c != '\n' && i < max_usrmsg_size) {
672 buf[i++] = c;
673 c = getchar();
674 }
675 buf[i] = '\0';
676
9a13f968 677 if (i > 0 || !ctl->skip_empty_lines)
ae6846b8 678 write_output(ctl, buf);
b9ef27f5 679
9a13f968 680 if (c == '\n') /* discard line terminator */
b9ef27f5 681 c = getchar();
c68a1cb4
SK
682 }
683}
684
46ee14df 685static void logger_close(const struct logger_ctl *ctl)
c68a1cb4 686{
1d575033
SK
687 if (close(ctl->fd) != 0)
688 err(EXIT_FAILURE, _("close failed"));
2b3f40c5 689 free(ctl->hdr);
c68a1cb4
SK
690}
691
b363e86d
SK
692static void __attribute__ ((__noreturn__)) usage(FILE *out)
693{
925aa9e8 694 fputs(USAGE_HEADER, out);
4ce393f4 695 fprintf(out, _(" %s [options] [<message>]\n"), program_invocation_short_name);
2da49186 696
451dbcfa
BS
697 fputs(USAGE_SEPARATOR, out);
698 fputs(_("Enter messages into the system log.\n"), out);
699
925aa9e8 700 fputs(USAGE_OPTIONS, out);
3f51c10b
SK
701 fputs(_(" -i log the logger command's PID\n"), out);
702 fputs(_(" --id[=<id>] log the given <id>, or otherwise the PID\n"), out);
d0e875ff 703 fputs(_(" -f, --file <file> log the contents of this file\n"), out);
ae6846b8 704 fputs(_(" -e, --skip-empty do not log empty lines when processing files\n"), out);
fd343a05 705 fputs(_(" --no-act do everything except the write the log\n"), out);
d0e875ff
KZ
706 fputs(_(" -p, --priority <prio> mark given message with this priority\n"), out);
707 fputs(_(" --prio-prefix look for a prefix on every line read from stdin\n"), out);
708 fputs(_(" -s, --stderr output message to standard error as well\n"), out);
f68b8aa7 709 fputs(_(" -S, --size <size> maximum size for a single message\n"), out);
d0e875ff
KZ
710 fputs(_(" -t, --tag <tag> mark every line with this tag\n"), out);
711 fputs(_(" -n, --server <name> write to this remote syslog server\n"), out);
712 fputs(_(" -P, --port <number> use this UDP port\n"), out);
713 fputs(_(" -T, --tcp use TCP only\n"), out);
714 fputs(_(" -d, --udp use UDP only\n"), out);
715 fputs(_(" --rfc3164 use the obsolete BSD syslog protocol\n"), out);
2cb40465 716 fputs(_(" --rfc5424[=<snip>] use the syslog protocol (the default for remote);\n"
d0e875ff 717 " <snip> can be notime, or notq, and/or nohost\n"), out);
8fce3924 718 fputs(_(" --msgid <msgid> set rfc5424 message id field\n"), out);
d0e875ff 719 fputs(_(" -u, --socket <socket> write to this Unix socket\n"), out);
d77dc29e
SK
720 fputs(_(" --socket-errors[=<on|off|auto>]\n"
721 " print connection errors when using Unix sockets\n"), out);
ebff016a 722#ifdef HAVE_LIBSYSTEMD
4b670c01
SK
723 fputs(_(" --journald[=<file>] write journald entry\n"), out);
724#endif
925aa9e8
KZ
725
726 fputs(USAGE_SEPARATOR, out);
727 fputs(USAGE_HELP, out);
728 fputs(USAGE_VERSION, out);
729 fprintf(out, USAGE_MAN_TAIL("logger(1)"));
b363e86d
SK
730
731 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
732}
733
6dbe3af9
KZ
734/*
735 * logger -- read and log utility
736 *
737 * Reads from an input and arranges to write the result on the system
738 * log.
739 */
195c3603
KZ
740int main(int argc, char **argv)
741{
cfa77d26
SK
742 struct logger_ctl ctl = {
743 .fd = -1,
59c6ac0b 744 .pid = 0,
d0b6c4bf 745 .pri = LOG_USER | LOG_NOTICE,
c68a1cb4 746 .prio_prefix = 0,
cfa77d26 747 .tag = NULL,
c68a1cb4 748 .unix_socket = NULL,
d77dc29e 749 .unix_socket_errors = 0,
c68a1cb4
SK
750 .server = NULL,
751 .port = NULL,
2b3f40c5 752 .hdr = NULL,
55f5bc66 753 .msgid = NULL,
4de2e8a0 754 .socket_type = ALL_TYPES,
f68b8aa7 755 .max_message_size = 1024,
4de2e8a0
SK
756 .rfc5424_time = 1,
757 .rfc5424_tq = 1,
758 .rfc5424_host = 1,
ae6846b8 759 .skip_empty_lines = 0
cfa77d26 760 };
c68a1cb4 761 int ch;
3d9f4b1d 762 int stdout_reopened = 0;
d77dc29e 763 int unix_socket_errors_mode = AF_UNIX_ERRORS_AUTO;
ebff016a 764#ifdef HAVE_LIBSYSTEMD
4b670c01
SK
765 FILE *jfd = NULL;
766#endif
b363e86d 767 static const struct option longopts[] = {
9a13f968
SK
768 { "id", optional_argument, 0, OPT_ID },
769 { "stderr", no_argument, 0, 's' },
770 { "file", required_argument, 0, 'f' },
fd343a05 771 { "no-act", no_argument, 0, OPT_NOACT, },
9a13f968
SK
772 { "priority", required_argument, 0, 'p' },
773 { "tag", required_argument, 0, 't' },
774 { "socket", required_argument, 0, 'u' },
d77dc29e 775 { "socket-errors", required_argument, 0, OPT_SOCKET_ERRORS },
9a13f968
SK
776 { "udp", no_argument, 0, 'd' },
777 { "tcp", no_argument, 0, 'T' },
778 { "server", required_argument, 0, 'n' },
779 { "port", required_argument, 0, 'P' },
780 { "version", no_argument, 0, 'V' },
781 { "help", no_argument, 0, 'h' },
782 { "prio-prefix", no_argument, 0, OPT_PRIO_PREFIX },
783 { "rfc3164", no_argument, 0, OPT_RFC3164 },
784 { "rfc5424", optional_argument, 0, OPT_RFC5424 },
785 { "size", required_argument, 0, 'S' },
786 { "msgid", required_argument, 0, OPT_MSGID },
787 { "skip-empty", no_argument, 0, 'e' },
ebff016a 788#ifdef HAVE_LIBSYSTEMD
9a13f968 789 { "journald", optional_argument, 0, OPT_JOURNALD },
4b670c01 790#endif
9a13f968 791 { NULL, 0, 0, 0 }
b363e86d 792 };
7eda085c
KZ
793
794 setlocale(LC_ALL, "");
795 bindtextdomain(PACKAGE, LOCALEDIR);
796 textdomain(PACKAGE);
c05a80ca 797 atexit(close_stdout);
6dbe3af9 798
ae6846b8 799 while ((ch = getopt_long(argc, argv, "ef:ip:S:st:u:dTn:P:Vh",
49999d6a 800 longopts, NULL)) != -1) {
98920f80 801 switch (ch) {
6dbe3af9 802 case 'f': /* file to log */
49999d6a 803 if (freopen(optarg, "r", stdin) == NULL)
3d9f4b1d
SK
804 err(EXIT_FAILURE, _("file %s"), optarg);
805 stdout_reopened = 1;
6dbe3af9 806 break;
ae6846b8
RG
807 case 'e':
808 ctl.skip_empty_lines = 1;
809 break;
6dbe3af9 810 case 'i': /* log process id also */
ef5fb280 811 ctl.pid = logger_getpid();
3f51c10b
SK
812 break;
813 case OPT_ID:
aab5b444 814 if (optarg) {
e598686d
KZ
815 const char *p = optarg;
816
817 if (*p == '=')
818 p++;
59c6ac0b
KZ
819 ctl.pid = strtoul_or_err(optarg, _("failed to parse id"));
820 } else
ef5fb280 821 ctl.pid = logger_getpid();
6dbe3af9
KZ
822 break;
823 case 'p': /* priority */
cfa77d26 824 ctl.pri = pencode(optarg);
6dbe3af9
KZ
825 break;
826 case 's': /* log to standard error */
35d36197 827 ctl.stderr_printout = 1;
6dbe3af9
KZ
828 break;
829 case 't': /* tag */
cfa77d26 830 ctl.tag = optarg;
6dbe3af9 831 break;
7eda085c 832 case 'u': /* unix socket */
c68a1cb4 833 ctl.unix_socket = optarg;
7eda085c 834 break;
f68b8aa7
RG
835 case 'S': /* max message size */
836 ctl.max_message_size = strtosize_or_err(optarg,
837 _("failed to parse message size"));
838 break;
66ee8158 839 case 'd':
c68a1cb4 840 ctl.socket_type = TYPE_UDP;
68265d07
SK
841 break;
842 case 'T':
c68a1cb4 843 ctl.socket_type = TYPE_TCP;
66ee8158 844 break;
68265d07 845 case 'n':
c68a1cb4 846 ctl.server = optarg;
912d6b98 847 break;
68265d07 848 case 'P':
c68a1cb4 849 ctl.port = optarg;
912d6b98 850 break;
b363e86d 851 case 'V':
e421313d 852 printf(UTIL_LINUX_VERSION);
b363e86d
SK
853 exit(EXIT_SUCCESS);
854 case 'h':
855 usage(stdout);
98920f80 856 case OPT_PRIO_PREFIX:
c68a1cb4 857 ctl.prio_prefix = 1;
98920f80 858 break;
4de2e8a0 859 case OPT_RFC3164:
2b3f40c5 860 ctl.syslogfp = syslog_rfc3164_header;
4de2e8a0
SK
861 break;
862 case OPT_RFC5424:
2b3f40c5 863 ctl.syslogfp = syslog_rfc5424_header;
4de2e8a0
SK
864 if (optarg)
865 parse_rfc5424_flags(&ctl, optarg);
866 break;
55f5bc66 867 case OPT_MSGID:
9a13f968 868 if (strchr(optarg, ' '))
8fce3924 869 errx(EXIT_FAILURE, _("--msgid cannot contain space"));
55f5bc66
RG
870 ctl.msgid = optarg;
871 break;
ebff016a 872#ifdef HAVE_LIBSYSTEMD
4b670c01
SK
873 case OPT_JOURNALD:
874 if (optarg) {
875 jfd = fopen(optarg, "r");
876 if (!jfd)
877 err(EXIT_FAILURE, _("cannot open %s"),
878 optarg);
879 } else
880 jfd = stdin;
881 break;
882#endif
d77dc29e
SK
883 case OPT_SOCKET_ERRORS:
884 unix_socket_errors_mode = parse_unix_socket_errors_flags(optarg);
885 break;
fd343a05
KZ
886 case OPT_NOACT:
887 ctl.noact = 1;
888 break;
6dbe3af9
KZ
889 case '?':
890 default:
b363e86d 891 usage(stderr);
6dbe3af9 892 }
49999d6a 893 }
6dbe3af9
KZ
894 argc -= optind;
895 argv += optind;
3d9f4b1d
SK
896 if (stdout_reopened && argc)
897 warnx(_("--file <file> and <message> are mutually exclusive, message is ignored"));
ebff016a 898#ifdef HAVE_LIBSYSTEMD
4b670c01 899 if (jfd) {
fd343a05 900 int ret = journald_entry(&ctl, jfd);
4b670c01
SK
901 if (stdin != jfd)
902 fclose(jfd);
047e2888 903 if (ret)
54fefa07 904 errx(EXIT_FAILURE, _("journald entry could not be written"));
047e2888 905 return EXIT_SUCCESS;
4b670c01
SK
906 }
907#endif
d77dc29e
SK
908 switch (unix_socket_errors_mode) {
909 case AF_UNIX_ERRORS_OFF:
910 ctl.unix_socket_errors = 0;
911 break;
912 case AF_UNIX_ERRORS_ON:
913 ctl.unix_socket_errors = 1;
914 break;
915 case AF_UNIX_ERRORS_AUTO:
916#ifdef HAVE_LIBSYSTEMD
917 ctl.unix_socket_errors = sd_booted();
918#else
919 ctl.unix_socket_errors = 0;
920#endif
921 break;
922 default:
923 abort();
924 }
c68a1cb4
SK
925 logger_open(&ctl);
926 if (0 < argc)
927 logger_command_line(&ctl, argv);
7eda085c 928 else
c68a1cb4
SK
929 /* Note. --file <arg> reopens stdin making the below
930 * function to be used for file inputs. */
931 logger_stdin(&ctl);
932 logger_close(&ctl);
49999d6a 933 return EXIT_SUCCESS;
6dbe3af9 934}